diff --git a/.travis.yml b/.travis.yml index 7c52d413f6..21f77350e9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,7 @@ language: php php: - - 5.4 - - 5.5 + - 5.5.9 - 5.6 - 7.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7c35e5e55c..3c5b53c2ac 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,3 +19,29 @@ PLEASE READ THE FOLLOWING BEFORE POSTING A BUG. **POST A GITHUB ISSUE / SUBMIT PR** > Make sure to put as many details as possible. If you are referring to a discussion on the Forum, add the link. The more info you give, the more easily we can reproduce the bug, the quicker we can fix it. > If you are submitting a Pull Request, please sign the [Contributor License Agreement](https://pydio.com/en/community/contribute/contributor-license-agreement-cla). + + +#### Setting up your dev environment + +Pydio 7 requires **PHP5.6** and upper. + +The web root of the application is located in ***core/src/***. Create a virtual host to point to this folder, set up your webserver to use index.php as default page. This is generally done by default. + +Pydio uses Composer and NPM to manage dependencies respectively in PHP and JS. It uses Grunt to build javascript sources. In order to start Pydio locally after a fresh `git clone`, you will first have to run these tools in both the core and in many plugins. + + - First install Composer (see https://getcomposer.org) and NPM (https://docs.npmjs.com/getting-started/installing-node) + - Install Grunt globally by running `npm install -g grunt-cli`` + - Inside the core folder (under webroot, i.e. core/src/core/ from root of git repository), run `composer install` + - For each plugin that contains a composer.json file, run `composer install` as well. + - For each plugin tat contains a package.json file, run + - `npm install` + - `grunt` + +On a unix-based machine, this can be achieved by the following command (from the webroot directory): +``` +find . -maxdepth 5 -name Gruntfile.js -execdir bash -c "npm install && grunt" \; +find . -maxdepth 5 -name composer.json -execdir composer install \; +``` + +You should be good to go. When modifying JS files that require transpilation, there is generally a `grunt watch` task available to automatically run grunt on each file change. + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..dba13ed2dd --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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 +. diff --git a/README.md b/README.md index 88a54dfea7..d5f85c6287 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,15 @@ [Homepage](https://pydio.com/) | [GitHub-Repository](https://github.com/pydio/pydio-core) | -[Issue-Tracker](https://github.com/pydio/pydio-core/issues) | - [![Codacy Badge](https://api.codacy.com/project/badge/3b5cafea44e949e789d1928687e04032)](https://www.codacy.com/app/charles_3085/pydio-core) | - [![Build Status](https://travis-ci.org/pydio/pydio-core.svg)](https://travis-ci.org/pydio/pydio-core) +[Issue-Tracker](https://github.com/pydio/pydio-core/issues) +| ![Latest Stable](https://img.shields.io/badge/stable-6.4.2-brightgreen.svg) +| ![License Badge](https://img.shields.io/badge/License-AGPL%203%2B-blue.svg) +| [![Codacy Badge](https://api.codacy.com/project/badge/3b5cafea44e949e789d1928687e04032)](https://www.codacy.com/app/charles_3085/pydio-core) +| [![Build Status](https://travis-ci.org/pydio/pydio-core.svg)](https://travis-ci.org/pydio/pydio-core) This is the main source code repository of Pydio (formerly AjaXplorer), containing all the PHP server and HTML5 Web GUI. -* Latest Stable release : 6.4.1 +* Latest Stable release : 6.4.2 * Latest Dev release : 6.3.1 (was RC for Pydio 6.4) * License: [AGPLv3](https://www.gnu.org/licenses/agpl.html) * Lead developer : Charles du Jeu (cdujeu): [Github](https://github.com/cdujeu) | [Twitter](https://twitter.com/Pydio) @@ -20,6 +22,31 @@ Please DO NOT send emails to Charles, but use the forum located on https://pydio ### How to contribute / Developer Resources +#### Setting up your dev environment + +Pydio 7 requires **PHP5.5.9** and upper. + +The web root of the application is located in ***core/src/***. Create a virtual host to point to this folder, set up your webserver to use index.php as default page. This is generally done by default. + +Pydio uses Composer and NPM to manage dependencies respectively in PHP and JS. It uses Grunt to build javascript sources. In order to start Pydio locally after a fresh `git clone`, you will first have to run these tools in both the core and in many plugins. + + - First install Composer (see https://getcomposer.org) and NPM (https://docs.npmjs.com/getting-started/installing-node) + - Install Grunt globally by running `npm install -g grunt-cli`` + - Inside the core folder (under webroot, i.e. core/src/core/ from root of git repository), run `composer install` + - For each plugin that contains a composer.json file, run `composer install` as well. + - For each plugin tat contains a package.json file, run + - `npm install` + - `grunt` + +On a unix-based machine, this can be achieved by the following command (from the webroot directory): +``` +find . -maxdepth 5 -name Gruntfile.js -execdir bash -c "npm install && grunt" \; +find . -maxdepth 5 -name composer.json -execdir composer install \; +``` + +You should be good to go. When modifying JS files that require transpilation, there is generally a `grunt watch` task available to automatically run grunt on each file change. + + #### Coding guidelines To enforce some coding standards, please run scripts in diff --git a/core/src/.gitignore b/core/src/.gitignore index 97c57dd2ad..a806073d94 100644 --- a/core/src/.gitignore +++ b/core/src/.gitignore @@ -1,7 +1,3 @@ -/plugins/access.ajxp_home/res/build -/plugins/access.inbox/res/build -/plugins/core.mailer/js/build -/plugins/gui.ajax/res/js/ui/reactjs/build/ .idea/ data/cache/* data/files/* @@ -10,9 +6,17 @@ data/personal/* data/plugins/* data/public/* data/tmp -node_modules/ +node_modules .DS_Store .jshintignore .jshintrc -/plugins/gui.ajax/res/mui/mui-sources -.validate.json \ No newline at end of file +.validate.json +composer.lock +/core/vendor/ +/plugins/*/vendor/ +/plugins/access.ajxp_user/build +/plugins/access.ajxp_home/res/build +/plugins/access.inbox/res/build +/plugins/core.mailer/js/build +/plugins/uploader.html/js/build +/plugins/core.tasks/js/build diff --git a/core/src/.htaccess b/core/src/.htaccess index b1cae48ce6..2a032edc5d 100644 --- a/core/src/.htaccess +++ b/core/src/.htaccess @@ -8,14 +8,6 @@ RewriteEngine on RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d -RewriteRule ^shares ./dav.php [L] -RewriteRule ^api ./rest.php [L] -RewriteRule ^ocs/ ./opencollab.php [L] -RewriteRule ^ocs-provider/ ./opencollab.php [L] -RewriteRule ^user ./index.php?get_action=user_access_point [L] -RewriteCond %{REQUEST_URI} !^/index -RewriteCond %{REQUEST_URI} !^/plugins -RewriteCond %{REQUEST_URI} ^/dashboard|^/settings|^/welcome|^/ws- RewriteRule (.*) index.php [L] #Following lines seem to be necessary if PHP is working @@ -29,8 +21,8 @@ RewriteRule (.*) index.php [L] # to make sure that authorization is transmitted. # Just remove the # at the beginning of the line -#SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 +SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 -AddType application/json .json \ No newline at end of file +AddType application/json .json diff --git a/core/src/base.conf.php b/core/src/base.conf.php index 7e0c651065..eb24a818bc 100644 --- a/core/src/base.conf.php +++ b/core/src/base.conf.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . * * This is the main configuration file for configuring the core of the application. * In a standard usage, you should not have to change any variables. diff --git a/core/src/cmd.php b/core/src/cmd.php index d48ff8bd4f..bdfdae9e2f 100644 --- a/core/src/cmd.php +++ b/core/src/cmd.php @@ -1,6 +1,6 @@ + * Copyright 2007-2016 Charles du Jeu - Abstrium SAS * This file is part of Pydio. * * Pydio is free software: you can redistribute it and/or modify @@ -16,235 +16,24 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . * * Description : Command line access of the framework. */ if (php_sapi_name() !== "cli") { die("This is the command line version of the framework, you are not allowed to access this page"); } - -include_once("base.conf.php"); - -header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); -header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); -header("Cache-Control: no-cache, must-revalidate"); -header("Pragma: no-cache"); -//set_error_handler(array("AJXP_XMLWriter", "catchError"), E_ALL & ~E_NOTICE ); -//set_exception_handler(array("AJXP_XMLWriter", "catchException")); +include_once ("base.conf.php"); +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\AuthService; +use Symfony\Component\Console\Application; +use Pydio\Core\Http\Cli\Command; ConfService::init(); ConfService::start(); -$confStorageDriver = ConfService::getConfStorageImpl(); -require_once($confStorageDriver->getUserClassFileName()); -//session_name("AjaXplorer"); -//session_start(); - - -$optArgs = array(); -$options = array(); -$regex = '/^-(-?)([a-zA-z0-9_]*)=(.*)/'; -foreach ($argv as $key => $argument) { - //echo("$key => $argument \n"); - if (preg_match($regex, $argument, $matches)) { - if ($matches[1] == "-") { - $optArgs[trim($matches[2])] = SystemTextEncoding::toUTF8(trim($matches[3])); - } else { - $options[trim($matches[2])] = SystemTextEncoding::toUTF8(trim($matches[3])); - } - } -} - -$optUser = $options["u"]; -if (!empty($optUser)) { - - if (isSet($options["p"])) { - $optPass = $options["p"]; - } else { - // Consider "u" is a crypted version of u:p - $optToken = $options["t"]; - $cKey = ConfService::getCoreConf("AJXP_CLI_SECRET_KEY", "conf"); - if(empty($cKey)) $cKey = "\1CDAFx¨op#"; - $optUser = trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($optToken.$cKey), base64_decode($optUser), MCRYPT_MODE_ECB), "\0"); - $env = getenv("AJXP_SAFE_CREDENTIALS"); - if(!empty($env)){ - $array = AJXP_Safe::getCredentialsFromEncodedString($env); - if(isSet($array["user"]) && $array["user"] == $optUser){ - unset($optToken); - $optPass = $array["password"]; - } - } - } - if (strpos($optUser,",") !== false) { - $originalOptUser = $optUser; - $nextUsers = explode(",", $optUser); - $optUser = array_shift($nextUsers); - $nextUsers = implode(",",$nextUsers); - } else if (strpos($optUser, "queue:") === 0) { - $optUserQueue = substr($optUser, strlen("queue:")); - $optUser = false; - //echo("QUEUE : ".$optUserQueue); - if (is_file($optUserQueue)) { - $lines = file($optUserQueue); - if (count($lines) && !empty($lines[0])) { - $allUsers = explode(",", $lines[0]); - $optUser = array_shift($allUsers); - file_put_contents($optUserQueue, implode(",", $allUsers)); - } - } - if ($optUser === false) { - if (is_file($optUserQueue)) { - unlink($optUserQueue); - } - die("No more users inside queue"); - } - } -} - - -$optStatusFile = $options["s"] OR false; -$optAction = $options["a"]; -$optRepoId = $options["r"] OR false; -if (strpos($optRepoId,",") !== false) { - $nextRepositories = explode(",", $optRepoId); - $optRepoId = array_shift($nextRepositories); - $nextRepositories = implode(",", $nextRepositories); -} - -//echo("REPOSITORY : ".$optRepoId." USER : ".$optUser."\n"); - -$optDetectUser = $options["detect_user"] OR false; -$detectedUser = false; - -if ($optRepoId !== false) { - $repository = ConfService::getRepositoryById($optRepoId); - if ($repository == null) { - $repository = ConfService::getRepositoryByAlias($optRepoId); - if ($repository != null) { - $optRepoId =($repository->isWriteable()?$repository->getUniqueId():$repository->getId()); - } - } - if ($optDetectUser != false) { - $path = $repository->getOption("PATH", true); - if (strpos($path, "AJXP_USER") !== false) { - $path = str_replace( - array("AJXP_INSTALL_PATH", "AJXP_DATA_PATH", "/"), - array(AJXP_INSTALL_PATH, AJXP_DATA_PATH, DIRECTORY_SEPARATOR), - $path - ); - $parts = explode("AJXP_USER", $path); - if(count($parts) == 1) $parts[1] = ""; - $first = str_replace("\\", "\\\\", $parts[0]); - $last = str_replace("\\", "\\\\", $parts[1]); - if (preg_match("/$first(.*)$last.*/", $optDetectUser, $matches)) { - $detectedUser = $matches[1]; - } - } - } - try{ - ConfService::switchRootDir($optRepoId, true); - }catch(AJXP_Exception $e){} -} else { - if ($optStatusFile) { - file_put_contents($optStatusFile, "ERROR:You must pass a -r argument specifying either a repository id or alias"); - } - die("You must pass a -r argument specifying either a repository id or alias"); -} - -if (AuthService::usersEnabled() && !empty($optUser)) { - $seed = AuthService::generateSeed(); - if ($seed != -1) { - $optPass = md5(md5($optPass).$seed); - } - $loggingResult = AuthService::logUser($optUser, $optPass, isSet($optToken), false, $seed); - // Check that current user can access current repository, try to switch otherwise. - $loggedUser = AuthService::getLoggedUser(); - if ($loggedUser != null && $detectedUser !== false && $loggedUser->isAdmin()) { - AuthService::disconnect(); - AuthService::logUser($detectedUser, "empty", true, false, ""); - $loggedUser = AuthService::getLoggedUser(); - } - - if ($loggedUser != null) { - ConfService::switchRootDir($optRepoId, true); - /* - $res = ConfService::switchUserToActiveRepository($loggedUser, $optRepoId); - if (!$res) { - AuthService::disconnect(); - $requireAuth = true; - } - */ - } - if (isset($loggingResult) && $loggingResult != 1) { - AJXP_XMLWriter::header(); - AJXP_XMLWriter::loggingResult($loggingResult, false, false, ""); - AJXP_XMLWriter::close(); - if ($optStatusFile) { - file_put_contents($optStatusFile, "ERROR:No user logged"); - } - } -} else { - AJXP_Logger::debug(ConfService::getCurrentRepositoryId()); -} - -//Set language -$loggedUser = AuthService::getLoggedUser(); -if($loggedUser != null && $loggedUser->getPref("lang") != "") ConfService::setLanguage($loggedUser->getPref("lang")); -else if(isSet($_COOKIE["AJXP_lang"])) ConfService::setLanguage($_COOKIE["AJXP_lang"]); -$mess = ConfService::getMessages(); - -// THIS FIRST DRIVERS DO NOT NEED ID CHECK -//$ajxpDriver = AJXP_PluginsService::findPlugin("gui", "ajax"); -$authDriver = ConfService::getAuthDriverImpl(); -// DRIVERS BELOW NEED IDENTIFICATION CHECK -if (!AuthService::usersEnabled() || ConfService::getCoreConf("ALLOW_GUEST_BROWSING", "auth") || AuthService::getLoggedUser()!=null) { - $confDriver = ConfService::getConfStorageImpl(); - $loadRepo = ConfService::getRepository(); - $Driver = ConfService::loadDriverForRepository($loadRepo); -} -AJXP_PluginsService::getInstance()->initActivePlugins(); -require_once(AJXP_BIN_FOLDER."/class.AJXP_Controller.php"); -$xmlResult = AJXP_Controller::findActionAndApply($optAction, $optArgs, array()); -if ($xmlResult !== false && $xmlResult != "") { - AJXP_XMLWriter::header(); - print($xmlResult); - AJXP_XMLWriter::close(); -} else if (isset($requireAuth) && AJXP_Controller::$lastActionNeedsAuth) { - AJXP_XMLWriter::header(); - AJXP_XMLWriter::requireAuth(); - AJXP_XMLWriter::close(); -} -//echo("NEXT REPO ".$nextRepositories." (".$options["r"].")\n"); -//echo("NEXT USERS ".$nextUsers." ( ".$originalOptUser." )\n"); -if (!empty($nextUsers) || !empty($nextRepositories) || !empty($optUserQueue) ) { - - if (!empty($nextUsers)) { - sleep(1); - $process = AJXP_Controller::applyActionInBackground($options["r"], $optAction, $optArgs, $nextUsers, $optStatusFile); - if ($process != null && is_a($process, "UnixProcess") && isSet($optStatusFile)) { - file_put_contents($optStatusFile, "RUNNING:".$process->getPid()); - } - } - if (!empty($optUserQueue)) { - sleep(1); - //echo("Should go to next with $optUserQueue"); - $process = AJXP_Controller::applyActionInBackground($options["r"], $optAction, $optArgs, "queue:".$optUserQueue, $optStatusFile); - if ($process != null && is_a($process, "UnixProcess") && isSet($optStatusFile)) { - file_put_contents($optStatusFile, "RUNNING:".$process->getPid()); - } - } - if (!empty($nextRepositories)) { - sleep(1); - $process = AJXP_Controller::applyActionInBackground($nextRepositories, $optAction, $optArgs, $originalOptUser, $optStatusFile); - if ($process != null && is_a($process, "UnixProcess") && isSet($optStatusFile)) { - file_put_contents($optStatusFile, "RUNNING:".$process->getPid()); - } - } - -} else if (isSet($optStatusFile)) { - - $status = explode(":", file_get_contents($optStatusFile)); - file_put_contents($optStatusFile, "FINISHED".(in_array("QUEUED", $status)?":QUEUED":"")); - -} +$input = new \Pydio\Core\Http\Cli\FreeArgvOptions(); +$application = new Application(); +$application->add(new Command()); +$application->setDefaultCommand("pydio"); +$application->run($input); \ No newline at end of file diff --git a/core/src/conf/RELEASE_NOTE b/core/src/conf/RELEASE_NOTE index 7fefd8f132..e9e226041d 100644 --- a/core/src/conf/RELEASE_NOTE +++ b/core/src/conf/RELEASE_NOTE @@ -1,5 +1,17 @@ Pydio ##VERSION_NUMBER## Release Note +TODO : This is a temporary package for testing upgrade from v6 to v7 + +Second beta for Pydio 7 + +---- +Pydio Core 6.5.1 + +Very first beta for Pydio 7 + +---- +Pydio Core 6.4.2 + Pydio Core 6.4.2 fixes vulnerabilities discovered in remote download and some specific path filtering on Windows server OS. Installation is done via the "Stable" update channel in-app, or via Linux packages manager depending on your installation mode. [Most noticeable changes] @@ -140,8 +152,8 @@ Do not encodeURIComponent for oldSchoolMailer - Should fix #1138 Fix #1180: Unpatch the path for zip files before we check against the… … Force overriding conf/extensions.conf.php during upgrade, as output f… … Init new release note. -FIx JSPacker static function call. - +Fix JSPacker static function call. +Fix FormManager when setting default value inside group-based selectors ------- Pydio Core 6.4.1 - Bug Fixes for 6.4 diff --git a/core/src/conf/bootstrap_conf.php b/core/src/conf/bootstrap_conf.php index ea5ca2d17c..be488a84ac 100644 --- a/core/src/conf/bootstrap_conf.php +++ b/core/src/conf/bootstrap_conf.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . * * These configuration must be set at the very root loading of the framework */ diff --git a/core/src/conf/bootstrap_context.php b/core/src/conf/bootstrap_context.php index dad91ece71..952a1887f8 100644 --- a/core/src/conf/bootstrap_context.php +++ b/core/src/conf/bootstrap_context.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . * * This is the main configuration file for configuring the core of the application. * In a standard usage, you should not have to change any variables. @@ -53,12 +53,15 @@ define("AJXP_PLUGINS_MESSAGES_FILE", AJXP_CACHE_DIR."/plugins_messages.ser"); define("AJXP_SERVER_ACCESS", "index.php"); define("AJXP_PLUGINS_FOLDER", "plugins"); -define("AJXP_BIN_FOLDER_REL", "core/classes"); -define("AJXP_BIN_FOLDER", AJXP_INSTALL_PATH."/core/classes"); +define("AJXP_BIN_FOLDER_REL", "core/src"); +define("AJXP_VENDOR_FOLDER_REL", "core/vendor"); +define("AJXP_BIN_FOLDER", AJXP_INSTALL_PATH."/core/src"); +define("AJXP_VENDOR_FOLDER", AJXP_INSTALL_PATH."/core/vendor"); define("AJXP_DOCS_FOLDER", "core/doc"); define("AJXP_COREI18N_FOLDER", AJXP_INSTALL_PATH."/plugins/core.ajaxplorer/i18n"); -define("TESTS_RESULT_FILE", AJXP_CACHE_DIR."/diag_result.php"); -define("AJXP_TESTS_FOLDER", AJXP_INSTALL_PATH."/core/tests"); +define("TESTS_RESULT_FILE", AJXP_DATA_PATH."/plugins/boot.conf/diag_result.php"); +define("TESTS_RESULT_FILE_LEGACY", AJXP_CACHE_DIR."/diag_result.php"); +define("AJXP_TESTS_FOLDER", AJXP_BIN_FOLDER."/pydio/Tests"); define("INITIAL_ADMIN_PASSWORD", "admin"); // Startup admin password (used at first creation). Once // The admin password is created and his password is changed, @@ -70,10 +73,6 @@ // example in log.serial. Do not forget the trailing slash // define("AJXP_FORCE_LOGPATH", "/var/log/ajaxplorer/"); -// KEY-VALUE-CACHE -define("AJXP_KVCACHE_PREFIX", "pydio-unique-id"); -define("AJXP_KVCACHE_IGNORE", true); - // DEBUG OPTIONS define("AJXP_CLIENT_DEBUG" , false); define("AJXP_SERVER_DEBUG" , false); @@ -92,44 +91,52 @@ define("HASH_SALT_INDEX", 2); define("HASH_PBKDF2_INDEX", 3); +// Used to identify the booster admin tasks +define("PYDIO_BOOSTER_TASK_IDENTIFIER", "pydio-booster"); + // CAN BE SWITCHED TO TRUE TO MAKE THE SECURE TOKEN MORE SAFE // MAKE SURE YOU HAVE PHP.5.3, OPENSSL, AND THAT IT DOES NOT DEGRADE PERFORMANCES define("USE_OPENSSL_RANDOM", false); -function AjaXplorer_autoload($className) +require_once (AJXP_VENDOR_FOLDER . "/autoload.php"); +$corePlugAutoloads = glob(AJXP_INSTALL_PATH."/".AJXP_PLUGINS_FOLDER."/core.*/vendor/autoload.php", GLOB_NOSORT); +if ($corePlugAutoloads !== false && count($corePlugAutoloads)) { + foreach($corePlugAutoloads as $autoloader){ + require_once ($autoloader); + } +} + +/** + * Used as autoloader + * @param $className + */ +function pydioAutoloader($className) { + // Temp : super dummy autoloader, take only class name + $parts = explode("\\", $className); + $className = array_pop($parts); + if($className == "dibi"){ - require_once(AJXP_BIN_FOLDER."/dibi/dibi.php"); + require_once(AJXP_BIN_FOLDER."/lib/dibi/dibi.php"); } - $fileName = AJXP_BIN_FOLDER."/"."class.".$className.".php"; - if (file_exists($fileName)) { - require_once($fileName); - return; - } - $fileName = AJXP_BIN_FOLDER."/"."interface.".$className.".php"; - if (file_exists($fileName)) { - require_once($fileName); - return; - } - $corePlugClass = glob(AJXP_INSTALL_PATH."/".AJXP_PLUGINS_FOLDER."/core.*/class.".$className.".php", GLOB_NOSORT); + $corePlugClass = glob(AJXP_INSTALL_PATH."/".AJXP_PLUGINS_FOLDER."/core.*/".$className.".php", GLOB_NOSORT); if ($corePlugClass !== false && count($corePlugClass)) { require_once($corePlugClass[0]); return; } - $corePlugInterface = glob(AJXP_INSTALL_PATH."/".AJXP_PLUGINS_FOLDER."/core.*/interface.".$className.".php", GLOB_NOSORT); - if ($corePlugInterface !== false && count($corePlugInterface)) { - require_once($corePlugInterface[0]); - return; - } } -spl_autoload_register('AjaXplorer_autoload'); +spl_autoload_register('pydioAutoloader'); + +include_once(AJXP_INSTALL_PATH . "/core/compat.php"); + +use Pydio\Core\Services\ApplicationState; -AJXP_Utils::safeIniSet("session.cookie_httponly", 1); +ApplicationState::safeIniSet("session.cookie_httponly", 1); if (is_file(AJXP_CONF_PATH."/bootstrap_conf.php")) { include(AJXP_CONF_PATH."/bootstrap_conf.php"); if (isSet($AJXP_INISET)) { - foreach($AJXP_INISET as $key => $value) AJXP_Utils::safeIniSet($key, $value); + foreach($AJXP_INISET as $key => $value) ApplicationState::safeIniSet($key, $value); } if (defined('AJXP_LOCALE')) { setlocale(LC_CTYPE, AJXP_LOCALE); diff --git a/core/src/conf/bootstrap_repositories.php b/core/src/conf/bootstrap_repositories.php index b56d664e47..1ed4e4e8d1 100644 --- a/core/src/conf/bootstrap_repositories.php +++ b/core/src/conf/bootstrap_repositories.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . * * Description : configuration file * BASIC REPOSITORY CONFIGURATION. diff --git a/core/src/conf/extensions.conf.php b/core/src/conf/extensions.conf.php index 478a17c4c6..bbf03c4f90 100644 --- a/core/src/conf/extensions.conf.php +++ b/core/src/conf/extensions.conf.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . * * Core extensions and icons supported. You can add a line in this file to support * more extensions. diff --git a/core/src/conf/templates/bootstrap_plugins.bridges.php b/core/src/conf/templates/bootstrap_plugins.bridges.php deleted file mode 100644 index c727824f9c..0000000000 --- a/core/src/conf/templates/bootstrap_plugins.bridges.php +++ /dev/null @@ -1,105 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - * - * This is the main configuration file for configuring the basic plugins the application - * needs to run properly : an Authentication plugin, a Configuration plugin, and a Logger plugin. - */ - -defined('AJXP_EXEC') or die( 'Access not allowed'); -/******************************************** - * CUSTOM VARIABLES HOOK - ********************************************/ -/** - * This is a sample "hard" hook, directly included. See directly the PluginSkeleton class - * for more explanation. - */ -//require_once AJXP_INSTALL_PATH."/plugins/action.skeleton/class.PluginSkeleton.php"; -//AJXP_Controller::registerIncludeHook("vars.filter", array("PluginSkeleton", "filterVars")); - -/********************************************************* - * PLUGINS DEFINITIONS - * Drivers will define how the application will work. For - * each type of operation, there are multiple implementation - * possible. Check the content of the plugins folder. - * CONF = users and repositories definition, - * AUTH = users authentication mechanism, - * LOG = logs of the application. - * - * This template shows how to configure the auth.remote plugin - * for joomla or Drupal - * -*********************************************************/ -$PLUGINS = array( - "CONF_DRIVER" => array( - "NAME" => "serial", - "OPTIONS" => array( - "REPOSITORIES_FILEPATH" => "AJXP_DATA_PATH/plugins/conf.serial/repo.ser", - "ROLES_FILEPATH" => "AJXP_DATA_PATH/plugins/auth.serial/roles.ser", - "USERS_DIRPATH" => "AJXP_DATA_PATH/plugins/auth.serial", - "FAST_CHECKS" => false, - "CUSTOM_DATA" => array( - "email" => "Email", - "country" => "Country", - "USER_QUOTA"=> "Quota" - ) - ) - ), - "AUTH_DRIVER" => array( - "NAME" => "remote", - "OPTIONS" => array( - "SLAVE_MODE" => true, - "USERS_FILEPATH" => "AJXP_DATA_PATH/plugins/auth.serial/users.ser", - "MASTER_AUTH_FUNCTION" => "joomla_remote_auth", - "MASTER_HOST" => "localhost", - "MASTER_URI" => "/joomla/", - "LOGIN_URL" => "/joomla/", // The URL to redirect (or call) upon login (typically if one of your user type: http://yourserver/path/to/ajxp, he will get redirected to this url to login into your frontend - "LOGOUT_URL" => "/joomla/", // The URL to redirect upon login out (see above) - "SECRET" => "myprivatesecret",// the same as the one you put in the WP plugin option. - "TRANSMIT_CLEAR_PASS" => true // Don't touch this. It's unsafe (and useless here) to transmit clear password. - ) - ), - /* - // Same for Drupal 7.X - "AUTH_DRIVER" => array( - "NAME" => "remote", - "OPTIONS" => array( - "SLAVE_MODE" => true, - "USERS_FILEPATH" => "AJXP_INSTALL_PATH/plugins/auth.serial/users.ser", - "LOGIN_URL" => "/drupal/", - "LOGOUT_URL" => "/drupal/?q=user/logout", - "MASTER_AUTH_FUNCTION" => "drupal_remote_auth", - "MASTER_HOST" => "192.168.0.10", - "MASTER_URI" => "/drupal/", - "MASTER_AUTH_FORM_ID" => "user-login-form", - "SECRET" => "my_own_private_Drupal_key", - "TRANSMIT_CLEAR_PASS" => true - ) - ), - */ - "LOG_DRIVER" => array( - "NAME" => "text", - "OPTIONS" => array( - "LOG_PATH" => (defined("AJXP_FORCE_LOGPATH")?AJXP_FORCE_LOGPATH:"AJXP_INSTALL_PATH/data/logs/"), - "LOG_FILE_NAME" => 'log_' . date('m-d-y') . '.txt', - "LOG_CHMOD" => 0770 - ) - ), - -); diff --git a/core/src/conf/templates/bootstrap_plugins.multi.php b/core/src/conf/templates/bootstrap_plugins.multi.php deleted file mode 100644 index 6e84c31eb1..0000000000 --- a/core/src/conf/templates/bootstrap_plugins.multi.php +++ /dev/null @@ -1,148 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - * - * This is the main configuration file for configuring the basic plugins the application - * needs to run properly : an Authentication plugin, a Configuration plugin, and a Logger plugin. - */ - -defined('AJXP_EXEC') or die( 'Access not allowed'); -/******************************************** - * CUSTOM VARIABLES HOOK - ********************************************/ -/** - * This is a sample "hard" hook, directly included. See directly the PluginSkeleton class - * for more explanation. - */ -//require_once AJXP_INSTALL_PATH."/plugins/action.skeleton/class.PluginSkeleton.php"; -//AJXP_Controller::registerIncludeHook("vars.filter", array("PluginSkeleton", "filterVars")); - -/********************************************************* - * PLUGINS DEFINITIONS - * Drivers will define how the application will work. For - * each type of operation, there are multiple implementation - * possible. Check the content of the plugins folder. - * CONF = users and repositories definition, - * AUTH = users authentication mechanism, - * LOG = logs of the application. - * - * This template shows how to configure the auth.remote plugin - * for joomla or Drupal - * -*********************************************************/ -$PLUGINS = array( - "CONF_DRIVER" => array( - "NAME" => "serial", - "OPTIONS" => array( - "REPOSITORIES_FILEPATH" => "AJXP_DATA_PATH/plugins/conf.serial/repo.ser", - "ROLES_FILEPATH" => "AJXP_DATA_PATH/plugins/auth.serial/roles.ser", - "USERS_DIRPATH" => "AJXP_DATA_PATH/plugins/auth.serial", - "FAST_CHECKS" => false, - "CUSTOM_DATA" => array( - "email" => "Email", - "country" => "Country", - "USER_QUOTA"=> "Quota" - ) - ) - ), - - /** - * THIS CONFIGURATION WILL USE AN LDAP SERVER AS MASTER, AND A LOCAL BASE FOR CREATING TMP USERS - */ - "AUTH_DRIVER" => array( - "NAME" => "multi", - "OPTIONS" => array( - "MODE" => "MASTER_SLAVE", - "MASTER_DRIVER" => "ldap", - "USER_BASE_DRIVER" => "serial", - "TRANSMIT_CLEAR_PASS" => true, - "DRIVERS" => array( - "ldap" => array( - "NAME" => "ldap", - "OPTIONS" => array( - "LDAP_URL" => "SERVER_HOST", - "LDAP_PORT" => 389, - "LDAP_USER" => "cn=admin,dc=domain,dc=ext", - "LDAP_PASSWORD" => "SERVER_PASSWORD", - "LDAP_DN" => "ou=People,dc=domain,dc=ext", - "LDAP_FILTER" => "(objectClass=account)", - "LDAP_USERATTR" => "uid" - ) - ), - "serial" => array( - "NAME" => "serial", - "OPTIONS" => array( - "LOGIN_REDIRECT" => false, - "USERS_FILEPATH" => "AJXP_DATA_PATH/plugins/auth.serial/users.ser", - "AUTOCREATE_AJXPUSER" => false, - "FAST_CHECKS" => false - ) - ) - ) - ) - ), - - /* - * HERE, WOULD ALLOW TO LOG FROM THE LOCAL SERIAL FILES, OR AUTHENTICATING AGAINST A PREDEFINED FTP SERVER. - * THE REPOSITORY "dynamic_ftp" SHOULD BE DEFINED INSIDE bootstrap_repositories.php - * WITH THE CORRECT FTP CONNEXION DATA, AND THE CORE APPLICATION CONFIG "Set Credentials in Session" - * SHOULD BE SET TO TRUE. - "AUTH_DRIVER" => array( - "NAME" => "multi", - "OPTIONS" => array( - "MASTER_DRIVER" => "serial", - "TRANSMIT_CLEAR_PASS" => true, - "USER_ID_SEPARATOR" => "_-_", - "DRIVERS" => array( - "serial" => array( - "LABEL" => "Local", - "NAME" => "serial", - "OPTIONS" => array( - "LOGIN_REDIRECT" => false, - "USERS_FILEPATH" => "AJXP_DATA_PATH/plugins/auth.serial/users.ser", - "AUTOCREATE_AJXPUSER" => false, - "TRANSMIT_CLEAR_PASS" => false ) - ), - "ftp" => array( - "LABEL" => "Remote FTP", - "NAME" => "ftp", - "OPTIONS" => array( - "LOGIN_REDIRECT" => false, - "REPOSITORY_ID" => "dynamic_ftp", - "ADMIN_USER" => "admin", - "FTP_LOGIN_SCREEN" => false, - "AUTOCREATE_AJXPUSER" => true, - "TRANSMIT_CLEAR_PASS" => true, - ) - ) - ) - ) - ), - */ - - "LOG_DRIVER" => array( - "NAME" => "text", - "OPTIONS" => array( - "LOG_PATH" => (defined("AJXP_FORCE_LOGPATH")?AJXP_FORCE_LOGPATH:"AJXP_INSTALL_PATH/data/logs/"), - "LOG_FILE_NAME" => 'log_' . date('m-d-y') . '.txt', - "LOG_CHMOD" => 0770 - ) - ), - -); diff --git a/core/src/conf/templates/bootstrap_plugins.sql.php b/core/src/conf/templates/bootstrap_plugins.sql.php deleted file mode 100644 index 1c12515aba..0000000000 --- a/core/src/conf/templates/bootstrap_plugins.sql.php +++ /dev/null @@ -1,78 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - * - * This is the main configuration file for configuring the basic plugins the application - * needs to run properly : an Authentication plugin, a Configuration plugin, and a Logger plugin. - */ - -defined('AJXP_EXEC') or die( 'Access not allowed'); -/******************************************** - * CUSTOM VARIABLES HOOK - ********************************************/ -/** - * This is a sample "hard" hook, directly included. See directly the PluginSkeleton class - * for more explanation. - */ -//require_once AJXP_INSTALL_PATH."/plugins/action.skeleton/class.PluginSkeleton.php"; -//AJXP_Controller::registerIncludeHook("vars.filter", array("PluginSkeleton", "filterVars")); - -/*********************************************************/ -/* PLUGINS DEFINITIONS -/* Drivers will define how the application will work. For -/* each type of operation, there are multiple implementation -/* possible. Check the content of the plugins folder. -/* CONF = users and repositories definition, -/* AUTH = users authentication mechanism, -/* LOG = logs of the application. -/* -/* This template shows how to configure the three plugins -/* using SQL database. It is based on the dibiphp.com -/* implementation and thus can be stored in various db types. -*/ -/*********************************************************/ -$sqlDriver = array( - "driver" => "mysql|sqlite|etc..", - "host" => "YOUR_HOST", - "database" => "DATABASE_NAME", - "user" => "DB_USER", - "password" => "DB_PASSWORD", -); - -$PLUGINS = array( - "AUTH_DRIVER" => array( - "NAME" => "sql", - "OPTIONS" => array( - "SQL_DRIVER" => $sqlDriver, - ) - ), - "CONF_DRIVER" => array( - "NAME" => "sql", - "OPTIONS" => array( - "SQL_DRIVER" => $sqlDriver, - ) - ), - "LOG_DRIVER" => array( - "NAME" => "sql", - "OPTIONS" => array( - "SQL_DRIVER" => $sqlDriver - ) - ) - -); diff --git a/core/src/content.php b/core/src/content.php deleted file mode 100644 index 38933ae363..0000000000 --- a/core/src/content.php +++ /dev/null @@ -1,24 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - * - * Description : duplication of index.php to ensure backward compatibility. - * Could be fixed by URL rewritting also. - */ -include("index.php"); diff --git a/core/src/core/classes/class.AJXP_AbstractMetaSource.php b/core/src/core/classes/class.AJXP_AbstractMetaSource.php deleted file mode 100644 index 9c74e24f2a..0000000000 --- a/core/src/core/classes/class.AJXP_AbstractMetaSource.php +++ /dev/null @@ -1,54 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Class AJXP_AbstractMetaSource - * Abstract class from which all meta.* plugins must extend. - */ -abstract class AJXP_AbstractMetaSource extends AJXP_Plugin { - - /** - * @var AbstractAccessDriver - */ - protected $accessDriver; - - /** - * - * @param AbstractAccessDriver $accessDriver - */ - public function initMeta($accessDriver){ - - $this->accessDriver = $accessDriver; - // Override options with parent META SOURCE options - // Could be refined ? - if($this->accessDriver->repository->hasParent()){ - $parentRepo = ConfService::getRepositoryById($this->accessDriver->repository->getParentId()); - if($parentRepo != null){ - $sources = $parentRepo->getOption("META_SOURCES"); - $qParent = $sources["meta.quota"]; - if(is_array($qParent)) $this->options = array_merge($this->options, $qParent); - } - } - - } - -} \ No newline at end of file diff --git a/core/src/core/classes/class.AJXP_Cache.php b/core/src/core/classes/class.AJXP_Cache.php deleted file mode 100644 index e5d71c26f2..0000000000 --- a/core/src/core/classes/class.AJXP_Cache.php +++ /dev/null @@ -1,176 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -/** - * Generic caching system that can be used by the plugins. Use the static factory getItem() to generate - * a actual cached instance. - * @package Pydio - * @subpackage Core - */ -class AJXP_Cache -{ - protected $cacheDir; - protected $cacheId; - protected $masterFile; - protected $dataCallback; - protected $idComputerCallback; - - /** - * Create an AJXP_Cache instance - * @param string $pluginId - * @param string $filepath - * @param callable $dataCallback A function to generate the data cache. If no callback provided, will simply use the content of the master item as the cache data - * @param string $idComputerCallback A function to generate the ID of the cache. If not provided, will generate a random hash - * @return AJXP_Cache - */ - public static function getItem($pluginId, $filepath, $dataCallback=null, $idComputerCallback = null) - { - if ($dataCallback == null) { - $dataCallback = array("AJXP_Cache", "simpleCopy"); - } - return new AJXP_Cache($pluginId,$filepath, $dataCallback, $idComputerCallback); - } - - /** - * The default dataCallback - * @static - * @param string $master - * @param string $target - * @return void - */ - public static function simpleCopy($master, $target) - { - file_put_contents($target, file_get_contents($master)); - } - - /** - * Clear a cache item associated with the master filepath - * @static - * @param String $pluginId - * @param String $filepath - * @return void - */ - public static function clearItem($pluginId, $filepath) - { - $inst = new AJXP_Cache($pluginId,$filepath, false); - if (file_exists($inst->getId())) { - @unlink($inst->getId()); - } - } - - /** - * Actual Cache object. Should not be used directly, but via the factory static method getItem() - * @param $pluginId - * @param $filepath - * @param $dataCallback - * @param null $idComputerCallback - * @return void - */ - function __construct($pluginId, $filepath, $dataCallback, $idComputerCallback = NULL) { - $this->cacheDir = (defined('AJXP_SHARED_CACHE_DIR')?AJXP_SHARED_CACHE_DIR:AJXP_CACHE_DIR); - $this->masterFile = $filepath; - $this->dataCallback = $dataCallback; - if ($idComputerCallback != null) { - $this->idComputerCallback = $idComputerCallback; - } - $this->cacheId = $this->buildCacheId($pluginId, $filepath); - } - - /** - * Load the actual data, either from the cache or from the master, and save it in the cache if necessary. - * @return string - */ - public function getData() - { - if (!$this->hasCachedVersion()) { - $result = call_user_func($this->dataCallback, $this->masterFile, $this->cacheId); - if ($result !== false) { - $this->touch(); - } - } else { - - } - return file_get_contents($this->cacheId); - } - - /** - * Check if the cache dir is writeable - * @return bool - */ - public function writeable() - { - return is_dir($this->cacheDir) && is_writeable($this->cacheDir); - } - - /** - * The unique ID of the item - * @return string - */ - public function getId() - { - return $this->cacheId; - } - - /** - * Check whether a cached version of the master file exists or not - * @return bool - */ - public function hasCachedVersion() - { - $modifTime = filemtime($this->masterFile); - if (file_exists($this->cacheId) && filemtime($this->cacheId) >= $modifTime) { - return true; - } - return false; - } - - /** - * Refresh the cached version modif date to the master modif date - * @return void - */ - public function touch() - { - @touch($this->cacheId, filemtime($this->masterFile)); - } - - /** - * Generate an ID for the cached file, either using the idComputerCallback, or a simple hash function. - * @param $pluginId - * @param $filePath - * @return string - */ - protected function buildCacheId($pluginId, $filePath) - { - if (!is_dir($this->cacheDir."/".$pluginId)) { - mkdir($this->cacheDir."/".$pluginId, 0755); - } - $root = $this->cacheDir ."/".$pluginId."/"; - if (isSet($this->idComputerCallback)) { - $hash = call_user_func($this->idComputerCallback, $filePath); - } else { - $info = pathinfo($filePath); - $hash = md5($filePath).(!empty($info["extension"])?".".$info["extension"]:""); - } - return $root.$hash; - } - - -} diff --git a/core/src/core/classes/class.AJXP_Controller.php b/core/src/core/classes/class.AJXP_Controller.php deleted file mode 100755 index 7add27601e..0000000000 --- a/core/src/core/classes/class.AJXP_Controller.php +++ /dev/null @@ -1,586 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -/** - * Core controller for dispatching the actions. - * It uses the XML Registry (simple version, not extended) to search all the tags and find the action. - * @package Pydio - * @subpackage Core - */ -class AJXP_Controller -{ - /** - * @var DOMXPath - */ - private static $xPath; - /** - * @var bool - */ - public static $lastActionNeedsAuth = false; - /** - * @var array - */ - private static $includeHooks = array(); - - private static $hooksCache = array(); - - /** - * Initialize the queryable xPath object - * @static - * @param bool $useCache Whether to cache the registry version in a memory cache. - * @return DOMXPath - */ - private static function initXPath($useCache = false) - { - if (!isSet(self::$xPath)) { - $registry = ConfService::getFilteredXMLRegistry(false, false, $useCache); - self::$xPath = new DOMXPath($registry); - } - return self::$xPath; - } - - public static function registryReset(){ - self::$xPath = null; - self::$hooksCache = array(); - } - - /** - * @param $actionName - * @param $path - * @return bool - */ - public static function findRestActionAndApply($actionName, $path) - { - $xPath = self::initXPath(true); - $actions = $xPath->query("actions/action[@name='$actionName']"); - if (!$actions->length) { - self::$lastActionNeedsAuth = true; - return false; - } - $action = $actions->item(0); - $restPathList = $xPath->query("processing/serverCallback/@restParams", $action); - if (!$restPathList->length) { - self::$lastActionNeedsAuth = true; - return false; - } - $restPath = $restPathList->item(0)->nodeValue; - $paramNames = explode("/", trim($restPath, "/")); - $exploded = explode("?", $path); - $path = array_shift($exploded); - $paramValues = array_map("urldecode", explode("/", trim($path, "/"), count($paramNames))); - foreach ($paramNames as $i => $pName) { - if (strpos($pName, "+") !== false) { - $paramNames[$i] = str_replace("+", "", $pName); - $paramValues[$i] = "/" . $paramValues[$i]; - } - } - if (count($paramValues) < count($paramNames)) { - $paramNames = array_slice($paramNames, 0, count($paramValues)); - } - $paramValues = array_map(array("SystemTextEncoding", "toUTF8"), $paramValues); - $httpVars = array_merge($_GET, $_POST, array_combine($paramNames, $paramValues)); - return self::findActionAndApply($actionName, $httpVars, $_FILES, $action); - - } - - /** - * @static - * @param Array $parameters - * @param DOMNode $callbackNode - * @param DOMXPath $xPath - * @throws Exception - */ - public static function checkParams(&$parameters, $callbackNode, $xPath) - { - if (!$callbackNode->attributes->getNamedItem('checkParams') || $callbackNode->attributes->getNamedItem('checkParams')->nodeValue != "true") { - return; - } - $inputParams = $xPath->query("input_param", $callbackNode); - $declaredParams = array(); - foreach ($inputParams as $param) { - $name = $param->attributes->getNamedItem("name")->nodeValue; - $type = $param->attributes->getNamedItem("type")->nodeValue; - $defaultNode = $param->attributes->getNamedItem("default"); - $mandatory = ($param->attributes->getNamedItem("mandatory")->nodeValue == "true"); - if ($mandatory && !isSet($parameters[$name])) { - throw new Exception("Missing parameter '".$name."' of type '$type'"); - } - if ($defaultNode != null && !isSet($parameters[$name])) { - $parameters[$name] = $defaultNode->nodeValue; - } - $declaredParams[] = $name; - } - foreach ($parameters as $k => $n) { - if(!in_array($k, $declaredParams)) unset($parameters[$k]); - } - } - - /** - * Main method for querying the XML registry, find an action and all its associated processors, - * and apply all the callbacks. - * @static - * @param String $actionName - * @param array $httpVars - * @param array $fileVars - * @param DOMNode $action - * @return mixed - */ - public static function findActionAndApply($actionName, $httpVars, $fileVars, &$action = null) - { - $actionName = AJXP_Utils::sanitize($actionName, AJXP_SANITIZE_EMAILCHARS); - if ($actionName == "cross_copy") { - $pService = AJXP_PluginsService::getInstance(); - $actives = $pService->getActivePlugins(); - $accessPlug = $pService->getPluginsByType("access"); - if (count($accessPlug)) { - foreach ($accessPlug as $key=>$objbect) { - if ($actives[$objbect->getId()] === true) { - call_user_func(array($pService->getPluginById($objbect->getId()), "crossRepositoryCopy"), $httpVars); - break; - } - } - } - self::$lastActionNeedsAuth = true; - return null; - } - $xPath = self::initXPath(true); - if ($action == null) { - $actions = $xPath->query("actions/action[@name='$actionName']"); - if (!$actions->length) { - self::$lastActionNeedsAuth = true; - return false; - } - $action = $actions->item(0); - } - //Check Rights - if (AuthService::usersEnabled()) { - $loggedUser = AuthService::getLoggedUser(); - if( $actionName != "logout" && AJXP_Controller::actionNeedsRight($action, $xPath, "userLogged", "only") && $loggedUser == null){ - AJXP_XMLWriter::header(); - AJXP_XMLWriter::requireAuth(); - AJXP_XMLWriter::close(); - exit(1); - } - if( AJXP_Controller::actionNeedsRight($action, $xPath, "adminOnly") && - ($loggedUser == null || !$loggedUser->isAdmin())){ - $mess = ConfService::getMessages(); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage(null, $mess[207]); - AJXP_XMLWriter::requireAuth(); - AJXP_XMLWriter::close(); - exit(1); - } - if( AJXP_Controller::actionNeedsRight($action, $xPath, "read") && - ($loggedUser == null || !$loggedUser->canRead(ConfService::getCurrentRepositoryId().""))){ - AJXP_XMLWriter::header(); - if($actionName == "ls" & $loggedUser!=null - && $loggedUser->canWrite(ConfService::getCurrentRepositoryId()."")){ - // Special case of "write only" right : return empty listing, no auth error. - AJXP_XMLWriter::close(); - exit(1); - } - $mess = ConfService::getMessages(); - AJXP_XMLWriter::sendMessage(null, $mess[208]); - AJXP_XMLWriter::requireAuth(); - AJXP_XMLWriter::close(); - exit(1); - } - if( AJXP_Controller::actionNeedsRight($action, $xPath, "write") && - ($loggedUser == null || !$loggedUser->canWrite(ConfService::getCurrentRepositoryId().""))){ - $mess = ConfService::getMessages(); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage(null, $mess[207]); - AJXP_XMLWriter::requireAuth(); - AJXP_XMLWriter::close(); - exit(1); - } - } - - $preCalls = self::getCallbackNode($xPath, $action, 'pre_processing/serverCallback', $actionName, $httpVars, $fileVars, true); - $postCalls = self::getCallbackNode($xPath, $action, 'post_processing/serverCallback[not(@capture="true")]', $actionName, $httpVars, $fileVars, true); - $captureCalls = self::getCallbackNode($xPath, $action, 'post_processing/serverCallback[@capture="true"]', $actionName, $httpVars, $fileVars, true); - $mainCall = self::getCallbackNode($xPath, $action, "processing/serverCallback",$actionName, $httpVars, $fileVars, false); - if ($mainCall != null) { - self::checkParams($httpVars, $mainCall, $xPath); - } - - if ($captureCalls !== false) { - // Make sure the ShutdownScheduler has its own OB started BEFORE, as it will presumabily be - // executed AFTER the end of this one. - AJXP_ShutdownScheduler::getInstance(); - ob_start(); - $params = array("pre_processor_results" => array(), "post_processor_results" => array()); - } - if ($preCalls !== false) { - foreach ($preCalls as $preCall) { - // A Preprocessing callback can modify its input arguments (passed by ref) - $preResult = self::applyCallback($preCall, $actionName, $httpVars, $fileVars); - if (isSet($params)) { - $params["pre_processor_results"][$preCall->getAttribute("pluginId")] = $preResult; - } - } - } - if ($mainCall) { - $result = self::applyCallback($mainCall, $actionName, $httpVars, $fileVars); - if (isSet($params)) { - $params["processor_result"] = $result; - } - } - if ($postCalls !== false) { - foreach ($postCalls as $postCall) { - // A Preprocessing callback can modify its input arguments (passed by ref) - $postResult = self::applyCallback($postCall, $actionName, $httpVars, $fileVars); - if (isSet($params)) { - $params["post_processor_results"][$postCall->getAttribute("pluginId")] = $postResult; - } - } - } - if ($captureCalls !== false) { - $params["ob_output"] = ob_get_contents(); - ob_end_clean(); - foreach ($captureCalls as $captureCall) { - self::applyCallback($captureCall, $actionName, $httpVars, $params); - } - } else { - if(isSet($result)) return $result; - } - return null; - } - - /** - * Launch a command-line version of the framework by passing the actionName & parameters as arguments. - * @static - * @param String $currentRepositoryId - * @param String $actionName - * @param Array $parameters - * @param string $user - * @param string $statusFile - * @return null|UnixProcess - */ - public static function applyActionInBackground($currentRepositoryId, $actionName, $parameters, $user ="", $statusFile = "") - { - $token = md5(time()); - $logDir = AJXP_CACHE_DIR."/cmd_outputs"; - if(!is_dir($logDir)) mkdir($logDir, 0755); - $logFile = $logDir."/".$token.".out"; - if (empty($user)) { - if(AuthService::usersEnabled() && AuthService::getLoggedUser() !== null) $user = AuthService::getLoggedUser()->getId(); - else $user = "shared"; - } - if (AuthService::usersEnabled()) { - $cKey = ConfService::getCoreConf("AJXP_CLI_SECRET_KEY", "conf"); - if(empty($cKey)){ - $cKey = "\1CDAFx¨op#"; - } - $user = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($token.$cKey), $user, MCRYPT_MODE_ECB)); - } - $robustInstallPath = str_replace("/", DIRECTORY_SEPARATOR, AJXP_INSTALL_PATH); - $cmd = ConfService::getCoreConf("CLI_PHP")." ".$robustInstallPath.DIRECTORY_SEPARATOR."cmd.php -u=$user -t=$token -a=$actionName -r=$currentRepositoryId"; - /* Inserted next 3 lines to quote the command if in windows - rmeske*/ - if (PHP_OS == "WIN32" || PHP_OS == "WINNT" || PHP_OS == "Windows") { - $cmd = ConfService::getCoreConf("CLI_PHP")." ".chr(34).$robustInstallPath.DIRECTORY_SEPARATOR."cmd.php".chr(34)." -u=$user -t=$token -a=$actionName -r=$currentRepositoryId"; - } - if ($statusFile != "") { - $cmd .= " -s=".$statusFile; - } - foreach ($parameters as $key=>$value) { - if($key == "action" || $key == "get_action") continue; - if(is_array($value)){ - $index = 0; - foreach($value as $v){ - $cmd .= " --file_".$index."=".escapeshellarg($v); - $index++; - } - }else{ - $cmd .= " --$key=".escapeshellarg($value); - } - } - - $repoObject = ConfService::getRepository(); - $clearEnv = false; - if($repoObject->getOption("USE_SESSION_CREDENTIALS")){ - $encodedCreds = AJXP_Safe::getEncodedCredentialString(); - if(!empty($encodedCreds)){ - putenv("AJXP_SAFE_CREDENTIALS=".$encodedCreds); - $clearEnv = "AJXP_SAFE_CREDENTIALS"; - } - } - - $res = self::runCommandInBackground($cmd, $logFile); - if(!empty($clearEnv)){ - putenv($clearEnv); - } - return $res; - } - - /** - * @param $cmd - * @param $logFile - * @return UnixProcess|null - */ - public static function runCommandInBackground($cmd, $logFile) - { - if (PHP_OS == "WIN32" || PHP_OS == "WINNT" || PHP_OS == "Windows") { - if(AJXP_SERVER_DEBUG) $cmd .= " > ".$logFile; - if (class_exists("COM") && ConfService::getCoreConf("CLI_USE_COM")) { - $WshShell = new COM("WScript.Shell"); - $oExec = $WshShell->Run("cmd /C $cmd", 0, false); - } else { - $basePath = str_replace("/", DIRECTORY_SEPARATOR, AJXP_INSTALL_PATH); - $tmpBat = implode(DIRECTORY_SEPARATOR, array( $basePath, "data","tmp", md5(time()).".bat")); - $cmd = "@chcp 1252 > nul \r\n".$cmd; - $cmd .= "\n DEL ".chr(34).$tmpBat.chr(34); - AJXP_Logger::debug("Writing file $cmd to $tmpBat"); - file_put_contents($tmpBat, $cmd); - pclose(popen('start /b "CLI" "'.$tmpBat.'"', 'r')); - } - return null; - } else { - $process = new UnixProcess($cmd, (AJXP_SERVER_DEBUG?$logFile:null)); - AJXP_Logger::debug("Starting process and sending output dev null"); - return $process; - } - } - - /** - * Find a callback node by its xpath query, filtering with the applyCondition if the xml attribute exists. - * @static - * @param DOMXPath $xPath - * @param DOMNode $actionNode - * @param string $query - * @param string $actionName - * @param array $httpVars - * @param array $fileVars - * @param bool $multiple - * @return DOMElement|bool|DOMElement[] - */ - private static function getCallbackNode($xPath, $actionNode, $query ,$actionName, $httpVars, $fileVars, $multiple = true) - { - $callbacks = $xPath->query($query, $actionNode); - if(!$callbacks->length) return false; - if ($multiple) { - $cbArray = array(); - foreach ($callbacks as $callback) { - if (self::appliesCondition($callback, $actionName, $httpVars, $fileVars)) { - $cbArray[] = $callback; - } - } - if(!count($cbArray)) return false; - return $cbArray; - } else { - $callback=$callbacks->item(0); - if(!self::appliesCondition($callback, $actionName, $httpVars, $fileVars)) return false; - return $callback; - } - } - - /** - * Check in the callback node if an applyCondition XML attribute exists, and eval its content. - * The content must set an $apply boolean as result - * @static - * @param DOMElement|DOMNode $callback - * @param string $actionName - * @param array $httpVars - * @param array $fileVars - * @return bool - */ - private static function appliesCondition($callback, $actionName, $httpVars, $fileVars) - { - if ($callback->getAttribute("applyCondition")!="") { - $apply = false; - eval($callback->getAttribute("applyCondition")); - if(!$apply) return false; - } - return true; - } - - /** - * Applies a callback node - * @static - * @param DOMElement|Array $callback The DOM Node or directly an array of attributes - * @param String $actionName - * @param Array $httpVars - * @param Array $fileVars - * @param null $variableArgs - * @param bool $defer - * @throws AJXP_Exception* @internal param \DOMXPath $xPath - * @return mixed - */ - private static function applyCallback($callback, &$actionName, &$httpVars, &$fileVars, &$variableArgs = null, $defer = false) - { - //Processing - if(is_array($callback)){ - $plugId = $callback["pluginId"]; - $methodName = $callback["methodName"]; - }else{ - $plugId = $callback->getAttribute("pluginId"); - $methodName = $callback->getAttribute("methodName"); - } - $plugInstance = AJXP_PluginsService::findPluginById($plugId); - //return call_user_func(array($plugInstance, $methodName), $actionName, $httpVars, $fileVars); - // Do not use call_user_func, it cannot pass parameters by reference. - if (method_exists($plugInstance, $methodName)) { - if ($variableArgs == null) { - return $plugInstance->$methodName($actionName, $httpVars, $fileVars); - } else { - if ($defer == true) { - AJXP_ShutdownScheduler::getInstance()->registerShutdownEventArray(array($plugInstance, $methodName), $variableArgs); - } else { - call_user_func_array(array($plugInstance, $methodName), $variableArgs); - } - } - } else { - throw new AJXP_Exception("Cannot find method $methodName for plugin $plugId!"); - } - return null; - } - - /** - * Find all callbacks registered for a given hook and apply them - * @static - * @param string $hookName - * @param array $args - * @param bool $forceNonDefer - * @return void - */ - public static function applyHook($hookName, $args, $forceNonDefer = false) - { - if(isSet(self::$hooksCache[$hookName])){ - $hooks = self::$hooksCache[$hookName]; - foreach($hooks as $hook){ - if (isSet($hook["applyCondition"]) && $hook["applyCondition"]!="") { - $apply = false; - eval($hook["applyCondition"]); - if(!$apply) continue; - } - $defer = $hook["defer"]; - if($defer && $forceNonDefer) $defer = false; - self::applyCallback($hook, $fake1, $fake2, $fake3, $args, $defer); - } - return; - } - $xPath = self::initXPath(true); - $callbacks = $xPath->query("hooks/serverCallback[@hookName='$hookName']"); - if(!$callbacks->length) return ; - self::$hooksCache[$hookName] = array(); - /** - * @var $callback DOMElement - */ - foreach ($callbacks as $callback) { - $defer = ($callback->getAttribute("defer") === "true"); - $applyCondition = $callback->getAttribute("applyCondition"); - $plugId = $callback->getAttribute("pluginId"); - $methodName = $callback->getAttribute("methodName"); - $dontBreakOnExceptionAtt = $callback->getAttribute("dontBreakOnException"); - $dontBreakOnException = !empty($dontBreakOnExceptionAtt) && $dontBreakOnExceptionAtt == "true"; - $hookCallback = array( - "defer" => $defer, - "applyCondition" => $applyCondition, - "pluginId" => $plugId, - "methodName" => $methodName - ); - self::$hooksCache[$hookName][] = $hookCallback; - if (!empty($applyCondition)) { - $apply = false; - eval($applyCondition); - if(!$apply) continue; - } - if($defer && $forceNonDefer) $defer = false; - if($dontBreakOnException){ - try{ - self::applyCallback($hookCallback, $fake1, $fake2, $fake3, $args, $defer); - }catch(Exception $e){ - AJXP_Logger::error("[Hook $hookName]", "[Callback ".$plugId.".".$methodName."]", $e->getMessage()); - } - }else{ - self::applyCallback($hookCallback, $fake1, $fake2, $fake3, $args, $defer); - } - } - } - - /** - * Find the statically defined callbacks for a given hook and apply them - * @static - * @param $hookName - * @param $args - * @return void - */ - public static function applyIncludeHook($hookName, &$args) - { - if(!isSet(self::$includeHooks[$hookName])) return; - foreach (self::$includeHooks[$hookName] as $callback) { - call_user_func_array($callback, $args); - } - } - - /** - * Register a hook statically when it must be defined before the XML registry construction. - * @static - * @param $hookName - * @param $callback - * @return void - */ - public static function registerIncludeHook($hookName, $callback) - { - if (!isSet(self::$includeHooks[$hookName])) { - self::$includeHooks[$hookName] = array(); - } - self::$includeHooks[$hookName][] = $callback; - } - - /** - * Check the rightsContext node of an action. - * @static - * @param DOMNode $actionNode - * @param DOMXPath $xPath - * @param string $right - * @return bool - */ - public static function actionNeedsRight($actionNode, $xPath, $right, $expectedValue="true") - { - $rights = $xPath->query("rightsContext", $actionNode); - if(!$rights->length) return false; - $rightNode = $rights->item(0); - $rightAttr = $xPath->query("@".$right, $rightNode); - if ($rightAttr->length && $rightAttr->item(0)->value == $expectedValue) { - self::$lastActionNeedsAuth = true; - return true; - } - return false; - } - - /** - * Utilitary used by the postprocesors to forward previously computed data - * @static - * @param array $postProcessData - * @return void - */ - public static function passProcessDataThrough($postProcessData) - { - if (isSet($postProcessData["pre_processor_results"]) && is_array($postProcessData["pre_processor_results"])) { - print(implode("", $postProcessData["pre_processor_results"])); - } - if (isSet($postProcessData["processor_result"])) { - print($postProcessData["processor_result"]); - } - if(isSet($postProcessData["ob_output"])) print($postProcessData["ob_output"]); - } -} diff --git a/core/src/core/classes/class.AJXP_Exception.php b/core/src/core/classes/class.AJXP_Exception.php deleted file mode 100644 index d561eaa7b5..0000000000 --- a/core/src/core/classes/class.AJXP_Exception.php +++ /dev/null @@ -1,50 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -/** - * Custom exception (legacy from php4 when there were no exceptions) - * @package Pydio - * @subpackage Core - */ -class AJXP_Exception extends Exception -{ - public function __construct($messageString, $messageId = false) - { - if ($messageId !== false && class_exists("ConfService")) { - $messages = ConfService::getMessages(); - if (array_key_exists($messageId, $messages)) { - $messageString = $messages[$messageId]; - } else { - $messageString = $messageId; - } - } - parent::__construct($messageString); - } - - public function errorToXml($mixed) - { - if (is_a($mixed, "Exception")) { - throw $this; - } else { - throw new AJXP_Exception($mixed); - } - } -} diff --git a/core/src/core/classes/class.AJXP_JSPacker.php b/core/src/core/classes/class.AJXP_JSPacker.php deleted file mode 100644 index 095aa5770d..0000000000 --- a/core/src/core/classes/class.AJXP_JSPacker.php +++ /dev/null @@ -1,127 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Encapsulation of the javascript/css packing library - * @package Pydio - * @subpackage Core - */ -class AJXP_JSPacker -{ - /** - * Static function for packing all js and css into big files - * Auto detect /js/*_list.txt files and /css/*_list.txt files and pack them. - */ - public static function pack() - { - // Make sure that the gui.* plugin is loaded - AJXP_PluginsService::getInstance()->getPluginsByType("gui"); - - $sList = glob(CLIENT_RESOURCES_FOLDER."/js/*_list.txt"); - foreach ($sList as $list) { - $scriptName = str_replace("_list.txt", ".js", $list); - AJXP_JSPacker::concatListAndPack($list, - $scriptName, - "Normal"); - if(isSet($_GET["separate"])){ - self::compactEach($list, "Normal"); - } - } - $sList = glob(AJXP_THEME_FOLDER."/css/*_list.txt"); - foreach ($sList as $list) { - $scriptName = str_replace("_list.txt", ".css", $list); - AJXP_JSPacker::concatListAndPack($list, - $scriptName, - "None"); - } - } - - /** - * Perform actual compression - * @param $src - * @param $out - * @param $mode - * @return bool - */ - public static function concatListAndPack($src, $out, $mode) - { - if (!is_file($src) || !is_readable($src)) { - return false; - } - - // Concat List into one big string - $jscode = '' ; - $noMiniCode = ''; - $lines = file($src); - foreach($lines as $jsline){ - if(trim($jsline) == '') continue; - $noMini = false; - if(strpos($jsline, "#NO_MINI") !== FALSE){ - $jsline = str_replace("#NO_MINI", "", $jsline); - $noMini = true; - } - $code = file_get_contents(AJXP_INSTALL_PATH."/".CLIENT_RESOURCES_FOLDER."/".rtrim($jsline,"\n\r")) ; - if ($code) { - if($noMini) $noMiniCode .= $code; - else $jscode .= $code ; - } - - } - - // Pack and write to file - require_once("packer/class.JavaScriptPacker.php"); - $packer = new JavaScriptPacker($jscode, $mode , true, false); - $packed = $packer->pack(); - if ($mode == "None") { // css case, hack for I.E. - $packed = str_replace("solid#", "solid #", $packed); - } - if(!empty($noMiniCode)){ - $packed.="\n".$noMiniCode; - } - @file_put_contents($out, $packed); - - return true; - } - - public function compactEach($list, $mode){ - $lines = file($list); - require_once("packer/class.JavaScriptPacker.php"); - $fullcode = ''; - foreach($lines as $line){ - $in = AJXP_INSTALL_PATH."/".CLIENT_RESOURCES_FOLDER."/".rtrim($line,"\n\r"); - $out = str_replace("/js/", "/js/min/", $in); - $outfull = str_replace(".js", ".full.js", $out); - $jscode = file_get_contents($in); - $fullcode .= $jscode; - // Pack and write to file - $packer = new JavaScriptPacker($jscode, $mode , true, false); - $packed = $packer->pack(); - file_put_contents($out, $packed); - - // Pack and write to file - $packer = new JavaScriptPacker($fullcode, $mode , true, false); - $packed = $packer->pack(); - file_put_contents($outfull, $packed); - } - } - -} diff --git a/core/src/core/classes/class.AJXP_KeyValueCache.php b/core/src/core/classes/class.AJXP_KeyValueCache.php deleted file mode 100644 index 9c36886aec..0000000000 --- a/core/src/core/classes/class.AJXP_KeyValueCache.php +++ /dev/null @@ -1,107 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - - -/** - * Class AJXP_KeyValueCache - * - * Simple Key/Value cache stored in memory, independant of the implementation. - * Currently APC only, we should replace with the Doctrine/cache library (see https://github.com/doctrine/cache ) - * We do implement their Cache Interface for future migration. - */ -class AJXP_KeyValueCache { - - protected function makeId($id){ - if(defined('AJXP_KVCACHE_PREFIX')){ - return AJXP_KVCACHE_PREFIX . " - ".$id; - } - return $id; - } - - /** - * Fetches an entry from the cache. - * - * @param string $id The id of the cache entry to fetch. - * - * @return mixed The cached data or FALSE, if no cache entry exists for the given id. - */ - public function fetch($id){ - if(!function_exists('apc_fetch')) return FALSE; - if(defined('AJXP_KVCACHE_IGNORE') && AJXP_KVCACHE_IGNORE) return FALSE; - $result = apc_fetch($this->makeId($id), $success); - if($success) return $result; - else return false; - } - /** - * Tests if an entry exists in the cache. - * - * @param string $id The cache id of the entry to check for. - * - * @return boolean TRUE if a cache entry exists for the given cache id, FALSE otherwise. - */ - public function contains($id){ - if(!function_exists('apc_fetch')) return FALSE; - if(defined('AJXP_KVCACHE_IGNORE') && AJXP_KVCACHE_IGNORE) return FALSE; - apc_fetch($this->makeId($id), $success); - return $success; - } - /** - * Puts data into the cache. - * - * @param string $id The cache id. - * @param mixed $data The cache entry/data. - * @param int $lifeTime The cache lifetime. - * If != 0, sets a specific lifetime for this cache entry (0 => infinite lifeTime). - * - * @return boolean TRUE if the entry was successfully stored in the cache, FALSE otherwise. - */ - public function save($id, $data, $lifeTime = 0){ - if(!function_exists('apc_store')) return false; - if(defined("AJXP_KVCACHE_IGNORE") && AJXP_KVCACHE_IGNORE) return false; - $res = apc_store($this->makeId($id), $data, $lifeTime); - if($res !== false) return true; - return false; - } - - /** - * Deletes a cache entry. - * - * @param string $id The cache id. - * - * @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise. - */ - public function delete($id){ - if(!function_exists('apc_delete')) return true; - return apc_delete($this->makeId($id)); - } - - /** - * Flush a whole cache - */ - public function deleteAll(){ - if(function_exists('apc_clear_cache')){ - apc_clear_cache('user'); - } - } - - -} \ No newline at end of file diff --git a/core/src/core/classes/class.AJXP_MetaStreamWrapper.php b/core/src/core/classes/class.AJXP_MetaStreamWrapper.php deleted file mode 100644 index 4b6a591e71..0000000000 --- a/core/src/core/classes/class.AJXP_MetaStreamWrapper.php +++ /dev/null @@ -1,595 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die('Access not allowed'); - -/** - * Class AJXP_MetaStreamWrapper - * - * Global streamWrapper that encapsulates all wrappers to access a driver's resource. - * Registered under the "pydio" protocol, it should replace all the old ajxp.XX direct calls. - * The static "appendMetaWrapper" method allows to add additional wrapper that will be sequentially called until - * reaching the driver ajxp.XX wrapper. - * - * @package Pydio - * @subpackage Core - */ -class AJXP_MetaStreamWrapper implements AjxpWrapper -{ - /** - * @var resource - */ - protected $handle; - - /** - * @var string - */ - protected $currentDirPath; - - protected static $metaWrappers = [ - 'core' => [ - 'pydio' => 'AJXP_MetaStreamWrapper' - ] - ]; - - protected static $cachedRepositoriesWrappers = array(); - - protected $currentUniquePath; - - /** - * Register the stack of protocols/wrappers. - * @param null $registered_wrappers - */ - public static function register($registered_wrappers = null){ - if($registered_wrappers == null){ - $registered_wrappers = stream_get_wrappers(); - } - $it = new RecursiveIteratorIterator(new RecursiveArrayIterator(self::$metaWrappers)); - foreach($it as $protocol => $className){ - if(!in_array($protocol, $registered_wrappers)){ - stream_wrapper_register($protocol, $className); - } - } - } - - /** - * Register an addition protocol/wrapper in the stack - * @param $name string - * @param $className string - */ - public static function appendMetaWrapper($name, $className, $parent = "core"){ - self::$metaWrappers[$parent][$name] = $className; - self::register(); - } - - protected static function getMetaWrappers($scheme) { - return array_merge((array) self::$metaWrappers['core'], (array) self::$metaWrappers[$scheme]); - } - - protected static function getNextScheme($url, $context='core'){ - $parts = parse_url($url); - $metaWrapperKeys = array_keys(self::getMetaWrappers($context)); - $key = array_search($parts["scheme"], $metaWrapperKeys); - if($key < count($metaWrapperKeys) - 1){ - // Return next registered meta wrapper - return $metaWrapperKeys[$key + 1]; - }else{ - // Otherwise return repository wrapper - $data = self::actualRepositoryWrapperData($parts["host"]); - return $data["protocol"]; - } - } - - /** - * @param string $url - * @param AJXP_MetaStreamWrapper $crtInstance - * @return string - * @throws Exception - */ - protected static function translateScheme($url, $crtInstance = null){ - $parts=parse_url($url); - $currentScheme = $parts['scheme']; - $context = self::actualRepositoryWrapperProtocol($parts['host']); - $newScheme = self::getNextScheme($url, $context); - $repository = ConfService::getRepositoryById(parse_url($url, PHP_URL_HOST)); - if($currentScheme == "pydio" && $repository->hasContentFilter()){ - - $contentFilter = $repository->getContentFilter(); - - if ($contentFilter instanceof ContentFilter) { - $baseDir = $contentFilter->getBaseDir(); - - if ($crtInstance != null) { - $crtInstance->currentUniquePath = $contentFilter->getUniquePath(); - } - - if (!empty($baseDir) || $baseDir != "/") { - $crtPath = parse_url($url, PHP_URL_PATH); - $crtBase = basename($crtPath); - if (!empty($crtPath) && $crtPath != "/" && fsAccessWrapper::unPatchPathForBaseDir($crtBase) != $contentFilter->getUniquePath() && $crtBase != ".ajxp_meta") { - throw new Exception("Cannot find file " . $crtBase); - } - // Prepend baseDir in path - $url = str_replace($currentScheme . "://" . $repository->getId() . $crtPath, $currentScheme . "://" . $repository->getId() . rtrim($baseDir . $crtPath, "/"), $url); - } - } - } - - $newUrl = str_replace($currentScheme."://", $newScheme."://", $url); - - self::applyInitPathHook($newUrl, $context); - - return $newUrl; - } - - protected static function findWrapperClassName($scheme, $context = "core"){ - - $metaWrappers = self::getMetaWrappers($context); - - if(isSet($metaWrappers[$scheme])){ - $wrapper = $metaWrappers[$scheme]; - }else{ - $wrapper = AJXP_PluginsService::getInstance()->getWrapperClassName($scheme); - } - if(empty($wrapper)) { - throw new Exception("Cannot find any wrapper for the scheme " . $scheme . " in context " . $context); - } - return $wrapper; - } - - protected static function findSubWrapperClassName($url){ - $repositoryId = parse_url($url, PHP_URL_HOST); - $context = self::actualRepositoryWrapperProtocol($repositoryId); - $nextScheme = self::getNextScheme($url, $context); - - return self::findWrapperClassName($nextScheme, $context); - } - - protected static function actualRepositoryWrapperData($repositoryId){ - if(isSet(self::$cachedRepositoriesWrappers[$repositoryId])){ - return self::$cachedRepositoriesWrappers[$repositoryId]; - } - $repository = ConfService::getRepositoryById($repositoryId); - if(!is_a($repository, "Repository")){ - throw new Exception("Cannot find repository with this id!"); - } - if($repository->detectStreamWrapper(false)){ - self::$cachedRepositoriesWrappers[$repositoryId] = $repository->streamData; - return $repository->streamData; - }else{ - throw new Exception("Repository does not provide a stream wrapper!"); - } - } - - /** - * Return the final ajxp.XX wrapper class name. - * @param $repositoryId - * @return string mixed - * @throws Exception - */ - public static function actualRepositoryWrapperClass($repositoryId){ - $data = self::actualRepositoryWrapperData($repositoryId); - return $data["classname"]; - } - - /** - * Return the final ajxp.XX wrapper protocol. - * @param $repositoryId - * @return string mixed - * @throws Exception - */ - public static function actualRepositoryWrapperProtocol($repositoryId){ - $data = self::actualRepositoryWrapperData($repositoryId); - return $data["protocol"]; - } - - - - /** - * Call Init function for a translated Path if defined - * - * @param string $path - */ - public static function applyInitPathHook($path, $context = 'core') { - $currentScheme = parse_url($path, PHP_URL_SCHEME); - $wrapper = self::findWrapperClassName($currentScheme, $context); - - if (is_callable(array($wrapper, "applyInitPathHook"))){ - call_user_func(array($wrapper, "applyInitPathHook"), $path); - } - } - - /** - * Get a "usable" reference to a file : the real file or a tmp copy. - * - * @param string $path - * @param bool $persistent - * @return string - * @throws Exception - */ - public static function getRealFSReference($path, $persistent = false) - { - $wrapper = self::findSubWrapperClassName($path); - return call_user_func(array($wrapper, "getRealFSReference"), self::translateScheme($path), $persistent); - } - - /** - * Read a file (by chunks) and copy the data directly inside the given stream. - * - * @param string $path - * @param resource $stream - */ - public static function copyFileInStream($path, $stream) - { - $wrapper = self::findSubWrapperClassName($path); - call_user_func(array($wrapper, "copyFileInStream"), self::translateScheme($path), $stream); - $meta = stream_get_meta_data($stream); - if(isSet($meta["uri"]) && $meta["uri"] === "php://output" && intval(ini_get("output_buffering")) > 0){ - ob_end_flush(); - } - } - - /** - * Chmod implementation for this type of access. - * - * @param string $path - * @param number $chmodValue - */ - public static function changeMode($path, $chmodValue) - { - $wrapper = self::findSubWrapperClassName($path); - call_user_func(array($wrapper, "changeMode"), self::translateScheme($path), $chmodValue); - } - - /** - * Describe whether the current wrapper operates on a remote server or not. - * @static - * @return boolean - * @throws Exception - */ - public static function isRemote() - { - throw new Exception("Do not call this method directly, but AJXP_MetaStreamWrapper::wrapperIsRemote() instead"); - } - - /** - * Describe whether the current wrapper operates on a remote server or not. - * @param String $url Url of the resource - * @static - * @return boolean - * @throws Exception - */ - public static function isSeekable($url) - { - throw new Exception("Do not call this method directly, but AJXP_MetaStreamWrapper::wrapperIsSeekable() instead"); - } - - /** - * @param string $url - * @return boolean - */ - public static function wrapperIsRemote($url){ - $repositoryId = parse_url($url, PHP_URL_HOST); - return call_user_func(array(self::actualRepositoryWrapperClass($repositoryId), "isRemote")); - } - - /** - * @param string $url - * @return boolean - */ - public static function wrapperIsSeekable($url){ - $repositoryId = parse_url($url, PHP_URL_HOST); - return call_user_func(array(self::actualRepositoryWrapperClass($repositoryId), "isSeekable"), $url); - } - - public static function nodesUseSameWrappers($url1, $url2){ - $w1 = self::actualRepositoryWrapperClass(parse_url($url1, PHP_URL_HOST)); - $w2 = self::actualRepositoryWrapperClass(parse_url($url2, PHP_URL_HOST)); - return $w1 == $w2; - } - - /** - * - * - * @return bool - */ - public function dir_closedir() - { - if(isSet($this->handle) && is_resource($this->handle)){ - closedir($this->handle); - } - } - - /** - * Enter description here... - * - * @param string $path - * @param int $options - * @return bool - */ - public function dir_opendir($path, $options) - { - $newPath = self::translateScheme($path, $this); - $this->handle = opendir($newPath); - if($this->handle !== false){ - $this->currentDirPath = parse_url($path, PHP_URL_PATH); - return true; - }else{ - return false; - } - } - - /** - * Standard readdir() implementation - * - * @return string - */ - public function dir_readdir() - { - if(isSet($this->handle) && is_resource($this->handle)){ - if($this->currentUniquePath != null){ - return $this->innerReadDirFiltered($this->handle); - }else{ - return readdir($this->handle); - } - } - return false; - } - - /** - * Skip values until correct one is found - * @param Resource $resource - * @return string - */ - protected function innerReadDirFiltered($resource){ - $test = readdir($resource); - if($test === false || $test == "." || $test == ".."){ - return $test; - } - if($this->currentUniquePath == $test) { - return $test; - } - // Return next one - return $this->innerReadDirFiltered($resource); - } - - - /** - * Enter description here... - * - * @return bool - */ - public function dir_rewinddir() - { - $this->currentDirPath = null; - if(isSet($this->handle) && is_resource($this->handle)){ - return rewind($this->handle); - } - return false; - } - - /** - * Enter description here... - * - * @param string $path - * @param int $mode - * @param int $options - * @return bool - */ - public function mkdir($path, $mode, $options) - { - return mkdir($this->translateScheme($path), $mode, $options); - } - - /** - * Enter description here... - * - * @param string $path_from - * @param string $path_to - * @return bool - */ - public function rename($path_from, $path_to) - { - return rename($this->translateScheme($path_from), $this->translateScheme($path_to)); - } - - /** - * Enter description here... - * - * @param string $path - * @param int $options - * @return bool - */ - public function rmdir($path, $options) - { - if(is_resource($options)){ - return rmdir(AJXP_MetaStreamWrapper::translateScheme($path), $options); - }else{ - return rmdir(AJXP_MetaStreamWrapper::translateScheme($path)); - } - } - - /** - * Enter description here... - * - */ - public function stream_close() - { - if(isSet($this->handle) && is_resource($this->handle)){ - return fclose($this->handle); - } - return false; - } - - /** - * Enter description here... - * - * @return bool - */ - public function stream_eof() - { - if(isSet($this->handle) && is_resource($this->handle)){ - return feof($this->handle); - } - return false; - } - - /** - * Enter description here... - * - * @return bool - */ - public function stream_flush() - { - if(isSet($this->handle) && is_resource($this->handle)){ - return fflush($this->handle); - } - return false; - } - - /** - * Enter description here... - * - * @param string $path - * @param string $mode - * @param int $options - * @param string &$context - * @return bool - */ - public function stream_open($path, $mode, $options, &$context) - { - if(is_resource($context)){ - $this->handle = fopen($this->translateScheme($path), $mode, $options, $context); - }else{ - $this->handle = fopen($this->translateScheme($path), $mode, $options); - } - return ($this->handle !== false); - } - - /** - * Enter description here... - * - * @param int $count - * @return string - */ - public function stream_read($count) - { - if(isSet($this->handle) && is_resource($this->handle)){ - return fread($this->handle, $count); - } - return null; - } - - /** - * Enter description here... - * - * @param int $offset - * @param int $whence = SEEK_SET - * @return bool - */ - public function stream_seek($offset, $whence = SEEK_SET) - { - if(isSet($this->handle) && is_resource($this->handle)){ - return fseek($this->handle, $offset, $whence); - } - return false; - } - - /** - * Enter description here... - * - * @return array - */ - public function stream_stat() - { - if(isSet($this->handle) && is_resource($this->handle)){ - return fstat($this->handle); - } - return false; - } - - /** - * Enter description here... - * - * @return int - */ - public function stream_tell() - { - if(isSet($this->handle) && is_resource($this->handle)){ - return ftell($this->handle); - } - return false; - } - - /** - * Enter description here... - * - * @param string $data - * @return int - */ - public function stream_write($data) - { - if(isSet($this->handle) && is_resource($this->handle)){ - return fwrite($this->handle, $data); - } - return false; - } - - /** - * Enter description here... - * - * @param string $path - * @return bool - */ - public function unlink($path) - { - return unlink($this->translateScheme($path)); - } - - /** - * Enter description here... - * - * @param string $path - * @param int $flags - * @return array - */ - public function url_stat($path, $flags) - { - $stat = @stat($this->translateScheme($path)); - if($stat === false){ - return null; - } - $bytesize = $stat["size"]; - $wrapper = self::actualRepositoryWrapperClass(parse_url($path, PHP_URL_HOST)); - if(method_exists($wrapper, "getLastRealSize")){ - $custom = call_user_func(array($wrapper, "getLastRealSize")); - if ($custom !== false) { - $bytesize = $custom; - } - } - if ($bytesize < 0) { - $bytesize = sprintf("%u", $bytesize); - } - $stat["size"] = $stat[7] = $bytesize; - - return $stat; - } -} diff --git a/core/src/core/classes/class.AJXP_Node.php b/core/src/core/classes/class.AJXP_Node.php deleted file mode 100644 index f0c3cfd29e..0000000000 --- a/core/src/core/classes/class.AJXP_Node.php +++ /dev/null @@ -1,605 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Atomic representation of a data. This the basic node of the hierarchical data. - * Encapsulates the path and url, the nature (leaf or not) and the metadata of the node. - * @package Pydio - * @subpackage Core - */ -class AJXP_Node -{ - /** - * @var string URL of the node in the form ajxp.protocol://repository_id/path/to/node - */ - protected $_url; - /** - * @var array The node metadata - */ - protected $_metadata = array(); - /** - * @var string Associated wrapper - */ - protected $_wrapperClassName; - /** - * @var array Parsed url fragments - */ - protected $urlParts = array(); - /** - * @var string A local representation of a real file, if possible - */ - protected $realFilePointer; - /** - * @var bool Whether the core information of the node is already loaded or not - */ - protected $nodeInfoLoaded = false; - /** - * @var string Details level passed to nodeLoadInfo() - */ - protected $nodeInfoLevel = "minimal"; - /** - * @var Repository - */ - private $_repository; - /** - * @var AbstractAccessDriver - */ - private $_accessDriver; - /** - * @var MetaStoreProvider - */ - private $_metaStore; - - /** - * @var String - */ - private $_user; - - /** - * @var array - */ - private $_indexableMetaKeys = array(); - - /** - * @param string $url URL of the node in the form ajxp.protocol://repository_id/path/to/node - * @param array $metadata Node metadata - */ - public function __construct($url, $metadata = array()) - { - $this->setUrl($url); - $this->_metadata = $metadata; - } - - public function __sleep() - { - $t = array_diff(array_keys(get_class_vars("AJXP_Node")), array("_accessDriver", "_repository", "_metaStore")); - return $t; - } - - /** - * @param String $url of the node in the form ajxp.protocol://repository_id/path/to/node - * @return void - */ - public function setUrl($url) - { - $this->_url = $url; - // Clean url - $testExp = explode("//", $url); - if (count($testExp) > 1) { - $this->_url = array_shift($testExp)."//"; - $this->_url .= implode("/", $testExp); - } - $this->parseUrl(); - } - - public function getRepository() - { - if (!isSet($this->_repository)) { - $this->_repository = ConfService::getRepositoryById($this->urlParts["host"]); - } - return $this->_repository; - } - - /** - * @return AbstractAccessDriver - */ - public function getDriver() - { - if (!isSet($this->_accessDriver)) { - $repo = $this->getRepository(); - if ($repo != null) { - $this->_accessDriver = ConfService::loadDriverForRepository($repo); - } - } - return $this->_accessDriver; - } - - /** - * @param AbstractAccessDriver - */ - public function setDriver($accessDriver) - { - $this->_accessDriver = $accessDriver; - } - - /** - * @return MetaStoreProvider - */ - protected function getMetaStore() - { - if (!isSet($this->_metaStore)) { - $this->getDriver(); - $this->_metaStore = AJXP_PluginsService::getInstance()->getUniqueActivePluginForType("metastore"); - } - return $this->_metaStore; - } - - public function hasMetaStore() - { - return ($this->getMetaStore() != false); - } - - /** - * @param $nameSpace - * @param $metaData - * @param bool $private - * @param int $scope - * @param bool $indexable - */ - public function setMetadata($nameSpace, $metaData, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY, $indexable = false) - { - $metaStore = $this->getMetaStore(); - if ($metaStore !== false) { - $metaStore->setMetadata($this, $nameSpace, $metaData, $private, $scope); - //$this->mergeMetadata($metaData); - if ($indexable) { - if(!isSet($this->_indexableMetaKeys[$private ? "user":"shared"]))$this->_indexableMetaKeys[$private ? "user":"shared"] = array(); - $this->_indexableMetaKeys[$private ? "user":"shared"][$nameSpace] = $nameSpace; - } - AJXP_Controller::applyHook("node.meta_change", array(&$this)); - } - } - - /** - * @param String $nameSpace - * @param bool $private - * @param int $scope - * @param bool $indexable - */ - public function removeMetadata($nameSpace, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY, $indexable = false) - { - $metaStore = $this->getMetaStore(); - if ($metaStore !== false) { - $metaStore->removeMetadata($this, $nameSpace, $private, $scope); - if ($indexable && isSet($this->_indexableMetaKeys[$private ? "user":"shared"]) && isset($this->_indexableMetaKeys[$private ? "user":"shared"][$nameSpace])) { - unset($this->_indexableMetaKeys[$private ? "user":"shared"][$nameSpace]); - } - AJXP_Controller::applyHook("node.meta_change", array(&$this)); - } - } - - /** - * @param String $nameSpace - * @param bool $private - * @param int $scope - * @param bool $indexable - * @return array - */ - public function retrieveMetadata($nameSpace, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY, $indexable = false) - { - $metaStore = $this->getMetaStore(); - if ($metaStore !== false) { - $data = $metaStore->retrieveMetadata($this, $nameSpace, $private, $scope); - if (!empty($data) && $indexable) { - if(!isSet($this->_indexableMetaKeys[$private ? "user":"shared"]))$this->_indexableMetaKeys[$private ? "user":"shared"] = array(); - $this->_indexableMetaKeys[$private ? "user":"shared"][$nameSpace] = $nameSpace; - } - return $data; - } - return array(); - } - - /** - * @param AJXP_Node $originalNode - * @param string $nameSpace - * @param string $operation - * @param bool $private - * @param int $scope - * @param bool $indexable - * @return array() - */ - public function copyOrMoveMetadataFromNode($originalNode, $nameSpace, $operation="move", $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY, $indexable = false){ - - if($this->getMetaStore() == false || $this->getMetaStore()->inherentMetaMove()){ - return array(); - } - $metaData = $originalNode->retrieveMetadata($nameSpace, $private, $scope, $indexable); - if(isSet($metaData) && !empty($metaData)){ - $this->setMetadata($nameSpace, $metaData, $private, $scope, $indexable); - if($operation == "move"){ - $originalNode->removeMetadata($nameSpace, $private, $scope, $indexable); - } - return $metaData; - } - return array(); - - } - - /** - * @return AJXP_Node|null - */ - public function getParent(){ - - if(empty($this->urlParts["path"]) || $this->urlParts["path"] == "/"){ - return null; - } - $parent = new AJXP_Node(dirname($this->_url)); - $parent->setDriver($this->_accessDriver); - return $parent; - - } - - public function findMetadataInParent($nameSpace, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY, $indexable = false){ - - $metadata = false; - $parentNode = $this->getParent(); - if($parentNode != null){ - $metadata = $parentNode->retrieveMetadata($nameSpace, $private, $scope,$indexable); - if($metadata == false){ - $metadata = $parentNode->findMetadataInParent($nameSpace, $private, $scope, $indexable); - }else{ - $metadata["SOURCE_NODE"] = $parentNode; - } - } - return $metadata; - - } - - /** - * @param String $nameSpace - * @param bool $private - * @param int $scope - * @param bool $indexable - * @param array $collect - */ - public function collectMetadataInParents($nameSpace, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY, $indexable = false, &$collect=array()){ - - $parentNode = $this->getParent(); - if($parentNode != null){ - $metadata = $parentNode->retrieveMetadata($nameSpace, $private, $scope,$indexable); - if($metadata != false){ - $metadata["SOURCE_NODE"] = $parentNode; - $collect[] = $metadata; - } - $parentNode->collectMetadataInParents($nameSpace, $private, $scope, $indexable, $collect); - } - - } - - /** - * @param array $nameSpaces - * @param bool $private - * @param int $scope - * @param bool $indexable - * @param array $collect - */ - public function collectMetadatasInParents($nameSpaces, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY, $indexable = false, &$collect=array()){ - - $parentNode = $this->getParent(); - if($parentNode != null){ - $nodeMeta = array(); - foreach($nameSpaces as $nameSpace){ - $metadata = $parentNode->retrieveMetadata($nameSpace, $private, $scope,$indexable); - if($metadata != false){ - $nodeMeta[$nameSpace] = $metadata; - } - } - if(count($nodeMeta)){ - $nodeMeta["SOURCE_NODE"] = $parentNode; - $collect[] = $nodeMeta; - } - $parentNode->collectMetadatasInParents($nameSpaces, $private, $scope, $indexable, $collect); - } - - } - - public function collectRepositoryMetadatasInChildren($nameSpace, $userScope){ - $result = array(); - $metaStore = $this->getMetaStore(); - if($metaStore !== false && method_exists($metaStore, "collectChildrenWithRepositoryMeta")){ - $result = $metaStore->collectChildrenWithRepositoryMeta($this, $nameSpace, $userScope); - } - return $result; - } - - /** - * @param bool $boolean Leaf or Collection? - * @return void - */ - public function setLeaf($boolean) - { - $this->_metadata["is_file"] = $boolean; - } - - /** - * @return bool - */ - public function isLeaf() - { - return isSet($this->_metadata["is_file"])?$this->_metadata["is_file"]:true; - } - - /** - * @param $label String Main label, will set the metadata "text" key. - * @return void - */ - public function setLabel($label) - { - $this->_metadata["text"] = $label; - } - - /** - * @return string Try to get the metadata "text" key, or the basename of the node path. - */ - public function getLabel() - { - return isSet($this->_metadata["text"])? $this->_metadata["text"] : basename($this->urlParts["path"]); - } - - /** - * @return string - */ - public function getExtension(){ - return strtolower(pathinfo($this->urlParts["path"], PATHINFO_EXTENSION)); - } - - /** - * @param $string - * @return bool - */ - public function hasExtension($string){ - return strcasecmp($string, $this->getExtension()) == 0; - } - - /** - * List all set metadata keys - * @return array - */ - public function listMetaKeys() - { - return array_keys($this->_metadata); - } - - /** - * Applies the "node.info" hook, thus going through the plugins that have registered this node, and loading - * all metadata at once. - * @param bool $forceRefresh - * @param bool $contextNode The parent node, if it can be useful for the hooks callbacks - * @param mixed $details A specification of expected metadata fields, or minimal - * @return void - */ - public function loadNodeInfo($forceRefresh = false, $contextNode = false, $details = false) - { - if($this->nodeInfoLoaded && $this->nodeInfoLevel != $details){ - $forceRefresh = true; - } - if($this->nodeInfoLoaded && !$forceRefresh){ - return; - } - if (!empty($this->_wrapperClassName)) { - $registered = AJXP_PluginsService::getInstance()->getRegisteredWrappers(); - if (!isSet($registered[$this->getScheme()])) { - $driver = $this->getDriver(); - if(is_object($driver)) $driver->detectStreamWrapper(true); - } - } - AJXP_Controller::applyHook("node.info.start", array(&$this, $contextNode, $details)); - if($this->nodeInfoLoaded && !$forceRefresh){ - return; - } - AJXP_Controller::applyHook("node.info", array(&$this, $contextNode, $details)); - AJXP_Controller::applyHook("node.info.end", array(&$this, $contextNode, $details)); - $this->nodeInfoLoaded = true; - $this->nodeInfoLevel = $details; - } - - /** - * Get a real reference to the filesystem. Remote wrappers will copy the file locally. - * This will last the time of the script and will be removed afterward. - * @return string - */ - public function getRealFile() - { - if (!isset($this->realFilePointer)) { - $this->realFilePointer = AJXP_MetaStreamWrapper::getRealFSReference($this->_url, true); - $isRemote = AJXP_MetaStreamWrapper::wrapperIsRemote($this->_url); - if ($isRemote) { - register_shutdown_function(array("AJXP_Utils", "silentUnlink"), $this->realFilePointer); - } - } - return $this->realFilePointer; - } - - /** - * @return string URL of the node in the form ajxp.protocol://repository_id/path/to/node - */ - public function getUrl() - { - return $this->_url; - } - - /** - * @return string The path from the root of the repository - */ - public function getPath() - { - return $this->urlParts["path"]; - } - - public function isRoot() - { - return !isset($this->urlParts["path"]) || $this->urlParts["path"] == "/"; - } - - /** - * @return string The scheme part of the url - */ - public function getScheme() - { - return $this->urlParts["scheme"]; - } - - /** - * @return string A username - */ - public function getUser(){ - return $this->_user; - } - - /** - * @param string $userId A username - */ - public function setUser($userId){ - $this->_user = $userId; - $this->urlParts["user"] = $userId; - // Update url with a user@workspaceID - $crt = $this->getScheme()."://".$this->getRepositoryId(); - $this->_url = str_replace($crt, $this->getScheme()."://".$this->_user."@".$this->getRepositoryId(), $this->_url); - } - - /** - * @return string A username passed through url - */ - public function hasUser(){ - return isSet($this->_user); - } - - /** - * @return string The repository identifer - */ - public function getRepositoryId() - { - return $this->urlParts["host"]; - } - - /** - * Pass an array of metadata and merge its content with the current metadata. - * @param array $metadata - * @param bool $mergeValues - * @return void - */ - public function mergeMetadata($metadata, $mergeValues = false) - { - if ($mergeValues) { - foreach ($metadata as $key => $value) { - if (isSet($this->_metadata[$key])) { - $existingValue = explode(",", $this->_metadata[$key]); - if (!in_array($value, $existingValue)) { - array_push($existingValue, $value); - $this->_metadata[$key] = implode(",", $existingValue); - } - } else { - $this->_metadata[$key] = $value; - } - } - } else { - $this->_metadata = array_merge($this->_metadata, $metadata); - } - } - - /** - * Return all metadata loaded during node.info, mainly used for caching. - * @return array - */ - public function getNodeInfoMeta(){ - return $this->_metadata; - } - - /** - * Set nodeInfoLoaded to true from external. - * @param string $level - */ - public function setInfoLoaded($level){ - $this->nodeInfoLoaded = true; - $this->nodeInfoLevel = $level; - } - - /** - * Magic getter for metadata - * @param $varName - * @return array|null|string - */ - public function __get($varName) - { - if(strtolower($varName) == "wrapperclassname") return $this->_wrapperClassName; - if(strtolower($varName) == "url") return $this->_url; - if(strtolower($varName) == "metadata") return $this->_metadata; - if(strtolower($varName) == "indexablemetakeys") return $this->_indexableMetaKeys; - - if (isSet($this->_metadata[$varName])) { - return $this->_metadata[$varName]; - } else { - return null; - } - } - - /** - * Magic setter for metadata - * @param $metaName - * @param $metaValue - * @return void - */ - public function __set($metaName, $metaValue) - { - if (strtolower($metaName) == "metadata") { - $this->_metadata = $metaValue; - return; - } - if($metaValue == null) unset($this->_metadata[$metaName]); - else $this->_metadata[$metaName] = $metaValue; - } - - /** - * Safe parseUrl implementation - * @return void - */ - protected function parseUrl() - { - if (strstr($this->_url, "#") !== false) { - $url = str_replace("#", "__HASH__", $this->_url); - $this->urlParts = parse_url($url); - foreach ($this->urlParts as $partKey => $partValue) { - $this->urlParts[$partKey] = str_replace("__HASH__", "#", $partValue); - } - } else { - $this->urlParts = parse_url($this->_url); - } - - if (strstr($this->urlParts["scheme"], "ajxp.")!==false) { - $pServ = AJXP_PluginsService::getInstance(); - $this->_wrapperClassName = $pServ->getWrapperClassName($this->urlParts["scheme"]); - }else if($this->urlParts["scheme"] == "pydio"){ - $this->_wrapperClassName = "AJXP_MetaStreamWrapper"; - } - } - -} diff --git a/core/src/core/classes/class.AJXP_Permission.php b/core/src/core/classes/class.AJXP_Permission.php deleted file mode 100755 index 29008293bb..0000000000 --- a/core/src/core/classes/class.AJXP_Permission.php +++ /dev/null @@ -1,204 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die('Access not allowed'); - -/** - * Class AJXP_Permission - * - * Atomic permission associated to a folder path. Can be initialized either with an integer - * value (use MASK constants) or with a string value like "r", "rw", etc. - * - * @package Pydio - * @subpackage Core - */ -class AJXP_Permission implements JsonSerializable -{ - /** - * Use an integer number to store permission - * |r|w|d|t|....|.. - * |0|1|0|0|....|.. - */ - - const MASK = 15; // 0000..001111 means use 4 first bits for encoding permission. The rest will be cut off. - const READ = 1; // 0001 - const WRITE = 2; // 0010 - const DENY = 4; // 0100 - const TRAVEL = 8; //1000 - - private $value = 0; - - /** - * @param array|null $value - */ - function __construct($value = null){ - if($value != null){ - if(is_array($value)) $this->value = $value; - elseif(is_integer($value)) { - $this->value = $value & self::MASK; - } - elseif(is_string($value)){ - if(strpos($value, "r") !== false) $this->setRead(); - if(strpos($value, "w") !== false) $this->setWrite(); - if(strpos($value, "d") !== false) $this->setDeny(); - } - } - } - - /** - * Replicate this permission. - * @return AJXP_Permission - */ - function getCopy(){ - return new AJXP_Permission($this->value); - } - - /** - * Test if the permission is readable - * @return bool - */ - function canRead(){ - return ($this->value & self::READ) === self::READ; - } - - /** - * Test if the permission is writeable - * @return bool - */ - function canWrite(){ - return ($this->value & self::WRITE) === self::WRITE; - } - - /** - * Test if the permission denies access, whatever happens. - * @return bool - */ - function denies(){ - if ($this->value === self::DENY) return true; - if ($this->value === 0) return true; - return false; - } - - /** - * Test if the permission is just empty - * @return bool - */ - function isEmpty(){ - return $this->value === 0; - } - - /** - * Test permission against an integer value - * @param int $numPerm - * @return bool - * @throws Exception - */ - function testPermission($numPerm){ - if(is_integer($numPerm) && ($numPerm < self::MASK)){ - $numPerm = $numPerm & self::MASK; - if (($this->value !== 0) && $numPerm === 0) return false; - if (($this->value === 0) && $numPerm === self::DENY) return true; - if ( ($this->value & self::DENY ) === self::DENY) return false; - return (($this->value & $numPerm) === $numPerm); - } - else{ - throw new Exception("Unimplemented permission : " . $numPerm); - } - } - - /** - * Set this permission as readable - * @param bool|true $value - */ - function setRead($value = true){ - if($value) - $this->value = $this->value | self::READ; - else{ - $this->value = $this->value & (self::READ ^ self::MASK); - } - } - - /** - * Set this permission as writeable - * @param bool|true $value - */ - function setWrite($value = true){ - if($value) - $this->value = $this->value | self::WRITE; - else{ - $this->value = $this->value & (self::WRITE ^ self::MASK); - } - } - - /** - * Set this permission as denied - * @param bool|true $value - */ - function setDeny($value = true){ - if($value) - $this->value = self::DENY; - else{ - $this->value = $this->value & (self::DENY ^ self::MASK); - } - } - - /** - * Override this permission value with the one passed in parameter - * @param AJXP_Permission $perm - * @return AJXP_Permission - */ - function override($perm){ - $newPerm = $perm->getCopy(); - if($this->denies()){ - $newPerm->setDeny(); - }else{ - if($this->canRead()) - $newPerm->setRead(); - if($this->canWrite()) - $newPerm->setWrite(); - } - return $newPerm; - } - - function __toString(){ - if($this->denies()) { - return "DENY"; - }else if($this->canRead() && !$this->canWrite()) { - return "READONLY"; - }else if(!$this->canRead() && $this->canWrite()) { - return "WRITEONLY"; - }else{ - return "READ WRITE"; - } - } - - /** - * Specify data which should be serialized to JSON - * @link http://php.net/manual/en/jsonserializable.jsonserialize.php - * @return mixed data which can be serialized by json_encode, - * which is a value of any type other than a resource. - * @since 5.4.0 - */ - function jsonSerialize() - { - return array("read" => $this->canRead(), "write" => $this->canWrite(), "deny" => $this->denies()); - } -} \ No newline at end of file diff --git a/core/src/core/classes/class.AJXP_PermissionMask.php b/core/src/core/classes/class.AJXP_PermissionMask.php deleted file mode 100755 index 1358bfd43e..0000000000 --- a/core/src/core/classes/class.AJXP_PermissionMask.php +++ /dev/null @@ -1,315 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die('Access not allowed'); - -/** - * Class AJXP_PermissionMask - * - * Stores a mapping of path => AJXP_Permission that can be used as a mask on an existing folder structure. - * - * @package Pydio - * @subpackage Core - * - */ -class AJXP_PermissionMask implements JsonSerializable, Serializable -{ - /** - * @var array - */ - private $permissionTree = array(); - - /** - * Initialize an empty mask, or from a serializedForm. - * @param array|null $serializedForm - */ - function __construct($serializedForm = null){ - if($serializedForm != null){ - foreach($serializedForm as $path => $permissionValue){ - $path = AJXP_Utils::sanitize(AJXP_Utils::securePath($path), AJXP_SANITIZE_DIRNAME); - if(!is_array($permissionValue) || $permissionValue["children"]) continue; - $perm = new AJXP_Permission(); - if($permissionValue["read"]) $perm->setRead(); - if($permissionValue["write"]) $perm->setWrite(); - if($permissionValue["deny"]) $perm->setDeny(); - if($perm->isEmpty()) continue; - $this->updateBranch($path, $perm); - } - } - } - - /** - * Returns the whole permission tree - * @return array - */ - function getTree(){ - if($this->permissionTree == null) return array(); - return $this->permissionTree; - } - - /** - * Set the permision tree at once - * @param array $tree - * @return AJXP_PermissionMask; - */ - function updateTree($tree){ - $this->permissionTree = $tree; - return $this; - } - - /** - * Add a branch in the permissions tree - * @param string $path - * @param AJXP_Permission $permission - * @return AJXP_PermissionMask - */ - function updateBranch($path, $permission){ - // Update the tree - $this->permissionTree = $this->mergeTrees($this->permissionTree, $this->pathToBranch($path, $permission)); - return $this; - } - - /** - * Delete a branch from the permission tree - * @param string $path - * @return AJXP_PermissionMask - */ - function deleteBranch($path){ - // Update the tree - return $this; - } - - /** - * Merge the current permission tree with the one passed in parameter. The latter takes precedence on the first. - * @param AJXP_PermissionMask $mask - * @return AJXP_PermissionMask - */ - function override($mask){ - // Return a new mask - $newMask = new AJXP_PermissionMask(); - $newMask->updateTree($this->mergeTrees($this->permissionTree, $mask->getTree())); - return $newMask; - } - - /** - * Copy the full tree from the one passed in parameter - * @param AJXP_PermissionMask $mask - * @return AJXP_PermissionMask - */ - - function copyMask($mask){ - $this->updateTree($mask->getTree()); - } - - /** - * Test if a given path does have the given permission according to this mask permission tree. - * @param string $test - * @param string $permission - * @return bool - */ - function match($test, $permission){ - // Check if a path has the given permission - $pathes = $this->flattenTree(); - - if(empty($test) || $test == "/" || $test == "/." || $test == "/..") { - if(!count($pathes)) return true; - if(isSet($pathes["/"])) { - $permObject = $pathes["/"]; - // If not read or write, must be read at least for root - if($permObject->denies()) $permObject->setRead(true); - return $permObject->testPermission($permission); - } - if($permission == AJXP_Permission::READ) return true; - else if($permission == AJXP_Permission::WRITE) return false; - return true; - } - - foreach($pathes as $path => $permObject){ - if(strpos($test, rtrim($path, "/")."/") === 0 || $test === $path){ - return $permObject->testPermission($permission); - } - } - // test is not under a defined permission, check if it needs traversal - foreach($pathes as $path => $permObject){ - if(strpos($path, $test) === 0 && !$permObject->denies()){ - if($permission == AJXP_Permission::READ) return true; - else if($permission == AJXP_Permission::WRITE) return false; - return $permObject->testPermission($permission); - } - } - - // Not in any path - return false; - - } - - /** - * Merge two trees - * @param array $t1 - * @param array $t2 - * @return array - */ - private function mergeTrees($t1, $t2){ - $result = $t1; - foreach($t2 as $key => $value2){ - if(!isset($t1[$key])){ - // Add branch - $result[$key] = $value2; - continue; - } - $value1 = $t1[$key]; - if(is_a($value1, "AJXP_Permission") && is_a($value2, "AJXP_Permission")){ - /** - * @var AJXP_Permission $value2 - */ - $result[$key] = $value2->override($value1); - }else if(is_a($value1, "AJXP_Permission")){ - $result[$key] = $value2; - }else if(is_a($value2, "AJXP_Permission")){ - /** - * @var AJXP_Permission $value2 - */ - //if($value2->denies()) { - $result[$key] = $value2; - //} else{ - // do nothing, keep original branch - //} - }else{ - // Two arrays - $result[$key] = $this->mergeTrees($value1, $value2); - } - } - return $result; - } - - /** - * Translate a path=> AJXP_Permission to an array-based branch. - * @param string $path - * @param AJXP_Permission $permission - * @return array - */ - private function pathToBranch($path, $permission){ - $parts = explode("/", trim($path, "/")); - $l = count($parts); - $br = array(); - $current = &$br; - for($i=0;$i<$l; $i++){ - if($i < $l - 1){ - $current[$parts[$i]] = array(); - $current = &$current[$parts[$i]]; - }else{ - $current[$parts[$i]] = $permission; - } - } - return $br; - } - - /** - * Transform the permission tree into a flat structure of pathes => permissions. - * @param array|null $tree - * @param array|null $pathes - * @param string $currentRoot - * @return AJXP_Permission[] - */ - public function flattenTree($tree = null, &$pathes = null, $currentRoot=""){ - if($tree == null) $tree = $this->getTree(); - if($pathes == null) $pathes = array(); - if(!is_array($tree) || $tree == null) $tree = array(); - foreach($tree as $pathPart => $value){ - if(is_a($value, "AJXP_Permission")){ - $pathes[$currentRoot."/".$pathPart] = $value; - }else{ - $this->flattenTree($value, $pathes, $currentRoot."/".$pathPart); - } - } - - return $pathes; - } - - - /** - * Print the tree as a string (for debug puprpose). - * @param $permissionTree - * @param $level - */ - public function toStr($permissionTree, $level) - { - $level = $level + 1; - foreach ($permissionTree as $key => $node) { - - $this->printSpace($level); - echo "[" . $key . "]"; - if ($node instanceof AJXP_Permission) echo "(".$node.")"; - - echo "\n"; - if (is_array($node) && count($node) > 0) { - $permissionTree = $node; - $this->toStr($permissionTree, $level); - } - - } - $level = $level - 1; - //echo "\n"; - } - - public function printSpace($number){ - for ($i = 0; $i < $number; $i++){ - echo "--"; - } - } - - /** - * Specify data which should be serialized to JSON - * @link http://php.net/manual/en/jsonserializable.jsonserialize.php - * @return mixed data which can be serialized by json_encode, - * which is a value of any type other than a resource. - * @since 5.4.0 - */ - function jsonSerialize() - { - return $this->flattenTree(); - } - - /** - * String representation of object - * @link http://php.net/manual/en/serializable.serialize.php - * @return string the string representation of the object or null - * @since 5.1.0 - */ - public function serialize() - { - return serialize($this->permissionTree); - } - - /** - * Constructs the object - * @link http://php.net/manual/en/serializable.unserialize.php - * @param string $serialized

- * The string representation of the object. - *

- * @return void - * @since 5.1.0 - */ - public function unserialize($serialized) - { - $this->permissionTree = unserialize($serialized); - } -} \ No newline at end of file diff --git a/core/src/core/classes/class.AJXP_Plugin.php b/core/src/core/classes/class.AJXP_Plugin.php deleted file mode 100644 index de78d5ce39..0000000000 --- a/core/src/core/classes/class.AJXP_Plugin.php +++ /dev/null @@ -1,1048 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -if (!defined('LOG_LEVEL_DEBUG')) { - define("LOG_LEVEL_DEBUG", "Debug"); - define("LOG_LEVEL_INFO", "Info"); - define("LOG_LEVEL_NOTICE", "Notice"); - define("LOG_LEVEL_WARNING", "Warning"); - define("LOG_LEVEL_ERROR", "Error"); -} - -/** - * The basic concept of plugin. Only needs a manifest.xml file. - * @package Pydio - * @subpackage Core - */ -class AJXP_Plugin implements Serializable -{ - protected $baseDir; - protected $id; - protected $name; - protected $type; - /** - * XPath query - * - * @var DOMXPath - */ - private $xPath; - protected $manifestLoaded = false; - protected $externalFilesAppended = false; - protected $enabled; - protected $registryContributions = array(); - protected $contributionsLoaded = false; - protected $options; // can be passed at init time - protected $pluginConf; // can be passed at load time - protected $pluginConfDefinition; - protected $dependencies; - protected $extensionsDependencies; - protected $streamData; - protected $mixins = array(); - protected $cachedXPathResults = array(); - public $loadingState = ""; - /** - * The manifest.xml loaded - * - * @var DOMDocument - */ - protected $manifestDoc; - - /** - * Internally store XML during serialization state. - * - * @var string - */ - private $manifestXML; - - private $serializableAttributes = array( - "baseDir", - "id", - "name", - "type", - "manifestLoaded", - "externalFilesAppended", - "enabled", - "registryContributions", - "contributionsLoaded", - "mixins", - "streamData", - "options", "pluginConf", "pluginConfDefinition", "dependencies", "extensionsDependencies", "loadingState", "manifestXML", "cachedXPathResults"); - - /** - * Construction method - * - * @param string $id - * @param string $baseDir - */ - public function __construct($id, $baseDir) - { - $this->baseDir = $baseDir; - $this->id = $id; - $split = explode(".", $id); - $this->type = $split[0]; - $this->name = $split[1]; - $this->dependencies = array(); - $this->extensionsDependencies = array(); - } - - public static function getWorkDirForPluginId($pluginId) { - return AJXP_DATA_PATH.DIRECTORY_SEPARATOR."plugins".DIRECTORY_SEPARATOR.$pluginId; - } - - protected function getPluginWorkDir($check = false) - { - $d = self::getWorkDirForPluginId($this->getId()); - if(!$check) return $d; - if (!is_dir($d)) { - $res = @mkdir($d, 0755, true); - if(!$res) throw new Exception("Error while creating plugin directory for ".$this->getId()); - } - return $d; - } - - protected function getPluginCacheDir($shared = false, $check = false) - { - $d = ($shared ? AJXP_SHARED_CACHE_DIR : AJXP_CACHE_DIR).DIRECTORY_SEPARATOR."plugins".DIRECTORY_SEPARATOR.$this->getId(); - if(!$check) return $d; - if (!is_dir($d)) { - $res = @mkdir($d, 0755, true); - if(!$res) throw new Exception("Error while creating plugin cache directory for ".$this->getId()); - } - return $d; - } - - /** - * @return DOMXPath - */ - protected function getXPath(){ - $this->unserializeManifest(); - return $this->xPath; - } - - /** - * @param $options - * @return void - */ - public function init($options) - { - $this->options = array_merge($this->loadOptionsDefaults(), $options); - } - - /** - * @param string $optionName - * @param string|Repository $repositoryScope - * @param null|AbstractAjxpUser $userObject - * @return mixed|null - */ - protected function getFilteredOption($optionName, $repositoryScope = AJXP_REPO_SCOPE_ALL, $userObject = null) - { - if(!is_array($this->options)) $this->options = array(); - $merged = $this->options; - if(is_array($this->pluginConf)) $merged = array_merge($merged, $this->pluginConf); - $loggedUser = $userObject; - if($loggedUser == null){ - $loggedUser = AuthService::getLoggedUser(); - } - if ($loggedUser != null) { - if ($repositoryScope === AJXP_REPO_SCOPE_ALL) { - $repo = ConfService::getRepository(); - if($repo != null) $repositoryScope = $repo->getId(); - }else if(is_a($repositoryScope, "Repository")){ - $repo = $repositoryScope; - $repositoryScope = $repo->getId(); - } - $test = $loggedUser->mergedRole->filterParameterValue( - $this->getId(), - $optionName, - $repositoryScope, - isSet($merged[$optionName]) ? $merged[$optionName] : null - ); - if (isSet($repo) && $repo != null && $repo->hasParent()) { - $retest = $loggedUser->mergedRole->filterParameterValue( - $this->getId(), - $optionName, - $repo->getParentId(), - null - ); - if($retest !== null) { - $test = $retest; - }else if($repo->hasOwner()){ - // Test shared scope - $retest = $loggedUser->mergedRole->filterParameterValue( - $this->getId(), - $optionName, - AJXP_REPO_SCOPE_SHARED, - null - ); - if($retest !== null) { - $test = $retest; - } - } - } - return $test; - } else { - return isSet($merged[$optionName]) ? $merged[$optionName] : null; - } - } - /** - * Perform initialization checks, and throw exception if problems found. - * @throws Exception - */ - public function performChecks() - { - } - /** - * @return bool - */ - public function isEnabled() - { - if(isSet($this->enabled)) return $this->enabled; - $this->enabled = true; - if(isSet($this->cachedXPathResults["@enabled"])){ - $value = $this->cachedXPathResults["@enabled"]; - if($value === "false"){ - $this->enabled = false; - } - return $this->enabled; - } - if ($this->manifestLoaded) { - if($this->manifestXML != null) $this->unserializeManifest(); - $l = $this->xPath->query("@enabled", $this->manifestDoc->documentElement); - if ($l->length && $l->item(0)->nodeValue === "false") { - $this->enabled = false; - } - } - return $this->enabled; - } - - /** - * Main function for loading all the nodes under registry_contributions. - * @param bool $dry - */ - protected function loadRegistryContributions($dry = false) - { - if($this->manifestXML != null) $this->unserializeManifest(); - $regNodes = $this->xPath->query("registry_contributions/*"); - for ($i=0;$i<$regNodes->length;$i++) { - $regNode = $regNodes->item($i); - if($regNode->nodeType != XML_ELEMENT_NODE) continue; - if ($regNode->nodeName == "external_file" && !$this->externalFilesAppended) { - $data = $this->nodeAttrToHash($regNode); - $filename = $data["filename"] OR ""; - $include = $data["include"] OR "*"; - $exclude = $data["exclude"] OR ""; - if(!is_file(AJXP_INSTALL_PATH."/".$filename)) continue; - if ($include != "*") { - $include = explode(",", $include); - } else { - $include = array("*"); - } - if ($exclude != "") { - $exclude = explode(",", $exclude); - } else { - $exclude = array(); - } - $this->initXmlContributionFile($filename, $include, $exclude, $dry); - } else { - if (!$dry) { - $this->registryContributions[]=$regNode; - $this->parseSpecificContributions($regNode); - } - } - } - // add manifest as a "plugins" (remove parsed contrib) - $pluginContrib = new DOMDocument(); - $pluginContrib->loadXML(""); - $manifestNode = $pluginContrib->importNode($this->manifestDoc->documentElement, true); - $pluginContrib->documentElement->appendChild($manifestNode); - $xP=new DOMXPath($pluginContrib); - $regNodeParent = $xP->query("registry_contributions", $manifestNode); - if ($regNodeParent->length) { - $manifestNode->removeChild($regNodeParent->item(0)); - } - if (!$dry) { - $this->registryContributions[]=$pluginContrib->documentElement; - $this->parseSpecificContributions($pluginContrib->documentElement); - $this->contributionsLoaded = true; - } - } - - /** - * Load an external XML file and include/exclude its nodes as contributions. - * @param string $xmlFile Path to the file from the base install path - * @param array $include XPath query for XML Nodes to include - * @param array $exclude XPath query for XML Nodes to exclude from the included ones. - * @param bool $dry Dry-run of the inclusion - */ - protected function initXmlContributionFile($xmlFile, $include=array("*"), $exclude=array(), $dry = false) - { - $contribDoc = new DOMDocument(); - $contribDoc->load(AJXP_INSTALL_PATH."/".$xmlFile); - if (!is_array($include) && !is_array($exclude)) { - if (!$dry) { - $this->registryContributions[] = $contribDoc->documentElement; - $this->parseSpecificContributions($contribDoc->documentElement); - } - return; - } - $xPath = new DOMXPath($contribDoc); - $excluded = array(); - foreach ($exclude as $excludePath) { - $children = $xPath->query($excludePath); - foreach ($children as $child) { - $excluded[] = $child; - } - } - $selected = array(); - foreach ($include as $includePath) { - $incChildren = $xPath->query($includePath); - if(!$incChildren->length) continue; - $parentNode = $incChildren->item(0)->parentNode; - if (!isSet($selected[$parentNode->nodeName])) { - $selected[$parentNode->nodeName]=array("parent"=>$parentNode, "nodes"=>array()); - } - foreach ($incChildren as $incChild) { - $foundEx = false; - foreach ($excluded as $exChild) { - if ($this->nodesEqual($exChild, $incChild)) { - $foundEx = true;break; - } - } - if($foundEx) continue; - $selected[$parentNode->nodeName]["nodes"][] = $incChild; - } - if (!count($selected[$parentNode->nodeName]["nodes"])) { - unset($selected[$parentNode->nodeName]); - } - } - if(!count($selected)) return; - $originalRegContrib = $this->xPath->query("registry_contributions")->item(0); - $localRegParent = null; - foreach ($selected as $parentNodeName => $data) { - $node = $data["parent"]->cloneNode(false); - //$newNode = $originalRegContrib->ownerDocument->importNode($node, false); - if ($dry) { - $localRegParent = $this->xPath->query("registry_contributions/".$parentNodeName); - if ($localRegParent->length) { - $localRegParent = $localRegParent->item(0); - } else { - $localRegParent = $originalRegContrib->ownerDocument->createElement($parentNodeName); - $originalRegContrib->appendChild($localRegParent); - } - } - foreach ($data["nodes"] as $childNode) { - $node->appendChild($childNode); - if($dry) $localRegParent->appendChild($localRegParent->ownerDocument->importNode($childNode, true)); - } - if (!$dry) { - $this->registryContributions[] = $node; - $this->parseSpecificContributions($node); - } else { - $this->reloadXPath(); - } - } - } - /** - * Dynamically modify some registry contributions nodes. Can be easily derivated to enable/disable - * some features dynamically during plugin initialization. - * @param DOMNode $contribNode - * @return void - */ - protected function parseSpecificContributions(&$contribNode) - { - //Append plugin id to callback tags - $callbacks = $contribNode->getElementsByTagName("serverCallback"); - foreach ($callbacks as $callback) { - $attr = $callback->ownerDocument->createAttribute("pluginId"); - $attr->value = $this->id; - $callback->appendChild($attr); - } - } - - /** - * Load the main manifest.xml file of the plugni - * @throws Exception - * @return void - */ - public function loadManifest() - { - $file = $this->baseDir."/manifest.xml"; - if (!is_file($file)) { - return; - } - $this->manifestDoc = new DOMDocument(); - try { - $this->manifestDoc->load($file); - } catch (Exception $e) { - throw $e; - } - $id = $this->manifestDoc->documentElement->getAttribute("id"); - if (empty($id) || $id != $this->getId()) { - $this->manifestDoc->documentElement->setAttribute("id", $this->getId()); - } - $this->xPath = new DOMXPath($this->manifestDoc); - $this->loadMixins(); - $this->detectStreamWrapper(); - $this->manifestLoaded = true; - $this->loadDependencies(); - } - - /** - * Get the plugin label as defined in the manifest file (label attribute) - * @return mixed|string - */ - public function getManifestLabel() - { - if($this->manifestXML != null) $this->unserializeManifest(); - $l = $this->xPath->query("@label", $this->manifestDoc->documentElement); - if($l->length) return AJXP_XMLWriter::replaceAjxpXmlKeywords($l->item(0)->nodeValue); - else return $this->id; - } - /** - * Get the plugin description as defined in the manifest file (description attribute) - * @return mixed|string - */ - public function getManifestDescription() - { - if($this->manifestXML != null) $this->unserializeManifest(); - $l = $this->xPath->query("@description", $this->manifestDoc->documentElement); - if($l->length) return AJXP_XMLWriter::replaceAjxpXmlKeywords($l->item(0)->nodeValue); - else return ""; - } - - /** - * Serialized all declared attributes and return a serialized representation of this plugin. - * The XML Manifest is base64 encoded before serialization. - * @return string - */ - public function serialize() - { - if ($this->manifestDoc != null) { - $this->manifestXML = serialize(base64_encode($this->manifestDoc->saveXML())); - } - $serialArray = array(); - foreach ($this->serializableAttributes as $attr) { - $serialArray[$attr] = $this->$attr; - } - return serialize($serialArray); - } - - /** - * Load this plugin from its serialized reprensation. The manifest XML is base64 decoded. - * @param $string - * @return void - */ - public function unserialize($string) - { - $serialArray = unserialize($string); - foreach ($serialArray as $key => $value) { - $this->$key = $value; - } - /* - if ($this->manifestXML != NULL) { - $this->manifestDoc = new DOMDocument(1.0, "UTF-8"); - $this->manifestDoc->loadXML(base64_decode(unserialize($this->manifestXML))); - $this->reloadXPath(); - unset($this->manifestXML); - } - */ - } - - /** - * Load DOMDocument from serialized value. Must be called after checking - * that property $this->manifestXML is not null. - */ - protected function unserializeManifest(){ - if($this->manifestXML != null){ - $this->manifestDoc = new DOMDocument(1.0, "UTF-8"); - $this->manifestDoc->loadXML(base64_decode(unserialize($this->manifestXML))); - $this->reloadXPath(); - unset($this->manifestXML); - } - } - - /** - * Legacy function, should better be used to return XML Nodes than string. Perform a query - * on the manifest. - * @param string $xmlNodeName - * @param string $format - * @param bool $externalFiles - * @return DOMElement|DOMNodeList|string - */ - public function getManifestRawContent($xmlNodeName = "", $format = "string", $externalFiles = false) - { - if ($externalFiles && !$this->externalFilesAppended) { - $this->loadRegistryContributions(true); - $this->externalFilesAppended = true; - } - if($this->manifestXML != null) $this->unserializeManifest(); - if ($xmlNodeName == "") { - if ($format == "string") { - return $this->manifestDoc->saveXML($this->manifestDoc->documentElement); - } else { - return $this->manifestDoc->documentElement; - } - } else { - $nodes = $this->xPath->query($xmlNodeName); - if ($format == "string") { - $buffer = ""; - foreach ($nodes as $node) { - $buffer .= $this->manifestDoc->saveXML($node); - } - return $buffer; - } else { - return $nodes; - } - } - } - /** - * Return the registry contributions. The parameter can be used by subclasses to optimize the size of the XML returned : - * the extended version is called when sending to the client, whereas the "small" version is loaded to find and apply actions. - * @param bool $extendedVersion Can be used by subclasses to optimize the size of the XML returned. - * @return array - */ - public function getRegistryContributions($extendedVersion = true) - { - if (!$this->contributionsLoaded) { - $this->loadRegistryContributions(); - } - return $this->registryContributions; - } - /** - * Load the declared dependant plugins - * @return void - */ - protected function loadDependencies() - { - $depPaths = "dependencies/*/@pluginName"; - $nodes = $this->xPath->query($depPaths); - foreach ($nodes as $attr) { - $value = $attr->value; - $this->dependencies = array_merge($this->dependencies, explode("|", $value)); - } - $extPaths = "dependencies/phpExtension/@name"; - $nodes = $this->xPath->query($extPaths); - foreach ($nodes as $attr) { - $this->extensionsDependencies[] = $attr->value; - } - $this->cachedNodesFromManifest("dependencies/activePlugin"); - $this->cachedNodesFromManifest("//server_settings/param[@default]"); - $this->cachedNodesFromManifest("//server_settings/global_param|//server_settings/param"); - $l = $this->xPath->query("@enabled", $this->manifestDoc->documentElement); - if ($l->length) { - $this->cachedXPathResults["@enabled"] = $l->item(0)->nodeValue; - }else{ - $this->cachedXPathResults["@enabled"] = "not_set"; - } - - } - /** - * Update dependencies dynamically - * @param AJXP_PluginsService $pluginService - * @return void - */ - public function updateDependencies($pluginService) - { - $append = false; - foreach ($this->dependencies as $index => $dependency) { - if ($dependency == "access.AJXP_STREAM_PROVIDER") { - unset($this->dependencies[$index]); - $append = true; - } - } - if ($append) { - $this->dependencies = array_merge($this->dependencies, $pluginService->getStreamWrapperPlugins()); - } - } - /** - * Check if this plugin depends on another one. - * @param string $pluginName - * @return bool - */ - public function dependsOn($pluginName) - { - return (in_array($pluginName, $this->dependencies) - || in_array(substr($pluginName, 0, strpos($pluginName, "."))."+", $this->dependencies)); - } - - protected function cachedNodesFromManifest($query){ - if(isSet($this->cachedXPathResults[$query])){ - return $this->cachedXPathResults[$query]; - } - if(!$this->manifestLoaded) return array(); - if($this->manifestXML != null) $this->unserializeManifest(); - $nodes = $this->xPath->query($query); - $res = array(); - foreach($nodes as $xmlNode){ - $arrayNode = $this->nodeAttrToHash($xmlNode); - $arrayNode["__NODE_NAME__"] = $xmlNode->nodeName; - $res[] = $arrayNode; - } - $this->cachedXPathResults[$query] = $res; - return $res; - } - - /** - * Get dependencies - * - * @param AJXP_PluginsService $pluginService - * @return array - */ - public function getActiveDependencies($pluginService) - { - $deps = array(); - $nodes = $this->cachedNodesFromManifest("dependencies/activePlugin"); - foreach ($nodes as $arrayNode) { - $value = $arrayNode["pluginName"]; - $parts = explode("|", $value); - foreach ($parts as $depName) { - if ($depName == "access.AJXP_STREAM_PROVIDER") { - $deps = array_merge($deps, $pluginService->getStreamWrapperPlugins()); - } else if (strpos($depName, "+") !== false) { - $typed = $pluginService->getPluginsByType(substr($depName, 0, strlen($depName)-1)); - foreach($typed as $typPlug) $deps[] = $typPlug->getId(); - } else { - $deps[] = $depName;// array_merge($deps, explode("|", $value)); - } - } - } - return $deps; - } - /** - * Load the global parameters for this plugin - * @return void - */ - protected function loadConfigsDefinitions() - { - $params = $this->cachedNodesFromManifest("//server_settings/global_param|//server_settings/param"); - $this->pluginConf = array(); - foreach ($params as $paramNode) { - $global = ($paramNode['__NODE_NAME__'] == "global_param"); - $this->pluginConfDefinition[$paramNode["name"]] = $paramNode; - if ($global && isset($paramNode["default"])) { - if ($paramNode["type"] == "boolean") { - $paramNode["default"] = ($paramNode["default"] === "true" ? true: false); - } else if ($paramNode["type"] == "integer") { - $paramNode["default"] = intval($paramNode["default"]); - } - $this->pluginConf[$paramNode["name"]] = $paramNode["default"]; - } - } - } - /** - * Load the default values for this plugin options - * @return array - */ - protected function loadOptionsDefaults() - { - $optionsDefaults = array(); - $params = $this->cachedNodesFromManifest("//server_settings/param[@default]"); - foreach ($params as $node) { - $default = $node["default"]; - if ($node["type"] == "boolean") { - $default = ($default === "true" ? true: false); - } else if ($node["type"] == "integer") { - $default = intval($default); - } - $optionsDefaults[$node["name"]] = $default; - } - return $optionsDefaults; - } - /** - * @return array - */ - public function getConfigsDefinitions() - { - return $this->pluginConfDefinition; - } - /** - * Load the configs passed as parameter. This method will - * + Parse the config definitions and load the default values - * + Merge these values with the $configData parameter - * + Publish their value in the manifest if the global_param is "exposed" to the client. - * @param array $configData - * @return void - */ - public function loadConfigs($configData) - { - // PARSE DEFINITIONS AND LOAD DEFAULT VALUES - if (!isSet($this->pluginConf)) { - $this->loadConfigsDefinitions(); - } - // MERGE WITH PASSED CONFIGS - $this->pluginConf = array_merge($this->pluginConf, $configData); - - // PUBLISH IF NECESSARY - foreach ($this->pluginConf as $key => $value) { - if (isSet($this->pluginConfDefinition[$key]) && isSet($this->pluginConfDefinition[$key]["expose"]) && $this->pluginConfDefinition[$key]["expose"] == "true") { - $this->exposeConfigInManifest($key, $value); - } - } - - // ASSIGN SPECIFIC OPTIONS TO PLUGIN KEY - if (isSet($this->pluginConf["AJXP_PLUGIN_ENABLED"])) { - $this->enabled = $this->pluginConf["AJXP_PLUGIN_ENABLED"]; - } - } - /** - * Return this plugin configs, merged with its associated "core" configs. - * @return array - */ - public function getConfigs() - { - $core = AJXP_PluginsService::getInstance()->findPlugin("core", $this->type); - if (!empty($core)) { - $coreConfs = $core->getConfigs(); - return array_merge($coreConfs, $this->pluginConf); - } else { - return $this->pluginConf; - } - } - /** - * Return the file path of the specific class to load - * @return array|bool - */ - public function getClassFile() - { - if($this->manifestXML != null) $this->unserializeManifest(); - $files = $this->xPath->query("class_definition"); - if(!$files->length) return false; - return $this->nodeAttrToHash($files->item(0)); - } - - public function missingExtensions(){ - $missing = array(); - if(count($this->extensionsDependencies)){ - foreach($this->extensionsDependencies as $ext){ - if (!extension_loaded($ext)) { - $missing[] = $ext; - } - } - } - return $missing; - } - - public function hasMissingExtensions(){ - return count($this->missingExtensions()) > 0; - } - - /** - * @return bool - */ - public function manifestLoaded() - { - return $this->manifestLoaded; - } - /** - * @return string - */ - public function getId() - { - return $this->id; - } - /** - * @return string - */ - public function getName() - { - return $this->name; - } - /** - * @return string - */ - public function getType() - { - return $this->type; - } - /** - * @return string - */ - public function getBaseDir() - { - return $this->baseDir; - } - /** - * @return array - */ - public function getDependencies() - { - return $this->dependencies; - } - /** - * Write a debug log with the plugin id as source - * @param string $prefix A quick description or action - * @param array|string $message Variable number of message args (string or array) - * @return void - */ - public function logDebug($prefix, $message = "") - { - if(!class_exists("ConfService")) return ; - if(!ConfService::getConf("SERVER_DEBUG")) return ; - $args = func_get_args(); - array_shift($args); - AJXP_Logger::log2(LOG_LEVEL_DEBUG, $this->getId(), $prefix, $args); - } - - /** - * Write an info log with the plugin id as source - * @param string $prefix A quick description or action - * @param array|string $message Variable number of message args (string or array) - * @return void - */ - public function logInfo($prefix, $message) - { - $args = func_get_args(); - array_shift($args); - AJXP_Logger::log2(LOG_LEVEL_INFO, $this->getId(), $prefix, $args); - } - - /** - * Write a notice log with the plugin id as source - * @param string $prefix A quick description or action - * @param array|string $message Variable number of message args (string or array) - * @return void - */ - public function logNotice($prefix, $message) - { - $args = func_get_args(); - array_shift($args); - AJXP_Logger::log2(LOG_LEVEL_NOTICE, $this->getId(), $prefix, $args); - } - - /** - * Write a warning log with the plugin id as source - * @param string $prefix A quick description or action - * @param array|string $message Variable number of message args (string or array) - * @return void - */ - public function logWarning($prefix, $message) - { - $args = func_get_args(); - array_shift($args); - AJXP_Logger::log2(LOG_LEVEL_WARNING, $this->getId(), $prefix, $args); - } - - /** - * Write an error log with the plugin id as source - * @param string $prefix A quick description or action - * @param array|string $message Variable number of message args (string or array) - * @return void - */ - public function logError($prefix, $message) - { - $args = func_get_args(); - array_shift($args); - AJXP_Logger::log2(LOG_LEVEL_ERROR, $this->getId(), $prefix, $args); - } - - /** - * Detect if this plugin declares a StreamWrapper, and if yes loads it and register the stream. - * @param bool $register - * @return array|bool - */ - public function detectStreamWrapper($register = false) - { - if (isSet($this->streamData)) { - if($this->streamData === false) return false; - $streamData = $this->streamData; - // include wrapper, no other checks needed. - include_once(AJXP_INSTALL_PATH."/".$streamData["filename"]); - } else { - if($this->manifestXML != null) $this->unserializeManifest(); - $files = $this->xPath->query("class_stream_wrapper"); - if (!$files->length) { - $this->streamData = false; - return false; - } - $streamData = $this->nodeAttrToHash($files->item(0)); - if (!is_file(AJXP_INSTALL_PATH."/".$streamData["filename"])) { - $this->streamData = false; - return false; - } - include_once(AJXP_INSTALL_PATH."/".$streamData["filename"]); - if (!class_exists($streamData["classname"])) { - $this->streamData = false; - return false; - } - $this->streamData = $streamData; - } - if ($register) { - $pServ = AJXP_PluginsService::getInstance(); - $wrappers = stream_get_wrappers(); - if (!in_array($streamData["protocol"], $wrappers)) { - stream_wrapper_register($streamData["protocol"], $streamData["classname"]); - $pServ->registerWrapperClass($streamData["protocol"], $streamData["classname"]); - } - AJXP_MetaStreamWrapper::register($wrappers); - } - return $streamData; - } - /** - * Add a name/value pair in the manifest to be published to the world. - * @param $configName - * @param $configValue - * @return void - */ - protected function exposeConfigInManifest($configName, $configValue) - { - if($this->manifestXML != null) $this->unserializeManifest(); - $confBranch = $this->xPath->query("plugin_configs"); - if (!$confBranch->length) { - $configNode = $this->manifestDoc->importNode(new DOMElement("plugin_configs", "")); - $this->manifestDoc->documentElement->appendChild($configNode); - } else { - $configNode = $confBranch->item(0); - } - $prop = $this->manifestDoc->createElement("property"); - $propValue = $this->manifestDoc->createCDATASection(json_encode($configValue)); - $prop->appendChild($propValue); - $attName = $this->manifestDoc->createAttribute("name"); - $attValue = $this->manifestDoc->createTextNode($configName); - $attName->appendChild($attValue); - $prop->appendChild($attName); - $configNode->appendChild($prop); - $this->reloadXPath(); - } - - public function getPluginInformation() - { - $info = array( - "plugin_author" => "", - "plugin_uri" => "", - "core_packaged" => true, - "plugin_version"=> "follow", - "core_version" => AJXP_VERSION, - ); - if($this->manifestXML != null) $this->unserializeManifest(); - $infoBranch = $this->xPath->query("plugin_info"); - if ($infoBranch->length) { - foreach ($infoBranch->item(0)->childNodes as $child) { - if($child->nodeType != 1) continue; - if ($child->nodeName != "core_relation") { - $info[$child->nodeName] = $child->nodeValue; - } else { - if($child->getAttribute("packaged") == "false") $info["core_packaged"] = false; - $coreV = $child->getAttribute("tested_version"); - if($coreV == "follow") $coreV = AJXP_VERSION; - $info["core_version"] = $coreV; - } - } - } - return $info; - } - - public function getPluginInformationHTML($defaultAuthor, $defaultUriBase) - { - $pInfo = $this->getPluginInformation(); - $pInfoString = ""; - if (empty($pInfo["plugin_uri"])) { - if($pInfo["core_packaged"]) $pInfo["plugin_uri"] = $defaultUriBase.$this->getType()."/".$this->getName()."/"; - else $pInfo["plugin_uri"] = "N/A"; - } - if ($pInfo["plugin_uri"] != "N/A") { - $pInfo["plugin_uri"] = "".$pInfo["plugin_uri"].""; - } - if ($pInfo["core_packaged"]) { - unset($pInfo["plugin_version"]); - unset($pInfo["core_version"]); - } - $pInfo["core_packaged"] = ($pInfo["core_packaged"] === true ? "Yes": "No"); - $humanKeys = array( - "plugin_author" => "Author", - "plugin_version" => "Version", - "plugin_uri" => "Url", - "core_packaged" => "Core distrib.", - "core_version" => "Core version" - ); - if(empty($pInfo["plugin_author"])) $pInfo["plugin_author"] = $defaultAuthor; - foreach ($pInfo as $k => $v) { - $pInfoString .= "
  • ".$humanKeys[$k]."$v
  • "; - } - return "
      $pInfoString
    "; - } - - /** - * @return void - */ - public function reloadXPath() - { - // Relaunch xpath - $this->xPath = new DOMXPath($this->manifestDoc); - } - /** - * @param $mixinName - * @return bool - */ - public function hasMixin($mixinName) - { - return (in_array($mixinName, $this->mixins)); - } - /** - * Check if the plugin declares mixins, and load them using AJXP_PluginsService::patchPluginWithMixin method - * @return void - */ - protected function loadMixins() - { - $attr = $this->manifestDoc->documentElement->getAttribute("mixins"); - if ($attr != "") { - $this->mixins = explode(",", $attr); - foreach ($this->mixins as $mixin) { - AJXP_PluginsService::getInstance()->patchPluginWithMixin($this, $this->manifestDoc, $mixin); - } - } - } - - /** - * Transform a simple node and its attributes to a HashTable - * - * @param DOMNode $node - * @return array - */ - protected function nodeAttrToHash($node) - { - $hash = array(); - $attributes = $node->attributes; - if ($attributes!=null) { - foreach ($attributes as $domAttr) { - $hash[$domAttr->name] = $domAttr->value; - } - } - return $hash; - } - /** - * Compare two nodes at first level (nodename and attributes) - * - * @param DOMNode $node1 - * @param DOMNode $node2 - * @return bool - */ - protected function nodesEqual($node1, $node2) - { - if($node1->nodeName != $node2->nodeName) return false; - $hash1 = $this->nodeAttrToHash($node1); - $hash2 = $this->nodeAttrToHash($node2); - foreach ($hash1 as $name=>$value) { - if(!isSet($hash2[$name]) || $hash2[$name] != $value) return false; - } - return true; - } -} diff --git a/core/src/core/classes/class.AJXP_PluginsService.php b/core/src/core/classes/class.AJXP_PluginsService.php deleted file mode 100644 index 2e4e5c53e8..0000000000 --- a/core/src/core/classes/class.AJXP_PluginsService.php +++ /dev/null @@ -1,929 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Core parser for loading / serving plugins - * @package Pydio - * @subpackage Core - */ -class AJXP_PluginsService -{ - private static $instance; - private $registry = array(); - private $required_files = array(); - private $activePlugins = array(); - private $streamWrapperPlugins = array(); - private $registeredWrappers = array(); - private $xmlRegistry; - private $registryVersion; - private $tmpDeferRegistryBuild = false; - /** - * @var AbstractConfDriver - */ - private $confStorage; - /** - * @var AbstractCacheDriver - */ - private $cacheStorage; - private $mixinsDoc; - private $mixinsXPath; - - - /** - * @return bool - */ - private function _loadRegistryFromCache(){ - - if((!defined("AJXP_SKIP_CACHE") || AJXP_SKIP_CACHE === false)){ - $reqs = AJXP_Utils::loadSerialFile(AJXP_PLUGINS_REQUIRES_FILE); - if (count($reqs)) { - foreach ($reqs as $fileName) { - if (!is_file($fileName)) { - // Cache is out of sync - return false; - } - require_once($fileName); - } - - $res = null; - - // Retrieving Registry from Server Cache - if ($this->cacheStorage) { - $res = $this->cacheStorage->fetch(AJXP_CACHE_SERVICE_NS_SHARED, 'plugins_registry'); - - $this->registry=$res; - } - - // Retrieving Registry from files cache - if (empty($res)) { - $res = AJXP_Utils::loadSerialFile(AJXP_PLUGINS_CACHE_FILE); - $this->registry=$res; - $this->savePluginsRegistryToCache(); - } - - // Refresh streamWrapperPlugins - foreach ($this->registry as $plugs) { - foreach ($plugs as $plugin) { - if (method_exists($plugin, "detectStreamWrapper") && $plugin->detectStreamWrapper(false) !== false) { - $this->streamWrapperPlugins[] = $plugin->getId(); - } - } - } - - return true; - }else{ - return false; - } - }else{ - return false; - } - - } - - /** - * Loads the full registry, from the cache only - * - */ - public function loadPluginsRegistryFromCache($cacheStorage = null) { - - $this->cacheStorage = $cacheStorage; - - if(!empty($this->registry)){ - return true; - } - if($this->_loadRegistryFromCache()){ - return true; - } - } - - /** - * Save plugin registry to cache - * - */ - public function savePluginsRegistryToCache() { - if (!empty ($this->cacheStorage)) { - $this->cacheStorage->save(AJXP_CACHE_SERVICE_NS_SHARED, "plugins_registry", $this->registry); - } - } - - /** - * Loads the full registry, from the cache or not - * @param String $pluginFolder - * @param AbstractConfDriver $confStorage - * @param bool $rewriteCache Force a cache rewriting - */ - public function loadPluginsRegistry($pluginFolder, $confStorage, $rewriteCache = false) - { - if(!$rewriteCache){ - if ($this->loadPluginsRegistryFromCache()) return; - } - - if (is_string($pluginFolder)) { - $pluginFolder = array($pluginFolder); - } - - $this->confStorage = $confStorage; - $pluginsPool = array(); - - foreach ($pluginFolder as $sourceFolder) { - $handler = @opendir($sourceFolder); - if ($handler) { - while ( ($item = readdir($handler)) !==false) { - if($item == "." || $item == ".." || !@is_dir($sourceFolder."/".$item) || strstr($item,".")===false) continue ; - $plugin = new AJXP_Plugin($item, $sourceFolder."/".$item); - $plugin->loadManifest(); - if ($plugin->manifestLoaded()) { - $pluginsPool[$plugin->getId()] = $plugin; - if (method_exists($plugin, "detectStreamWrapper") && $plugin->detectStreamWrapper(false) !== false) { - $this->streamWrapperPlugins[] = $plugin->getId(); - } - } - } - closedir($handler); - } - } - if (count($pluginsPool)) { - $this->checkDependencies($pluginsPool); - foreach ($pluginsPool as $plugin) { - $this->recursiveLoadPlugin($plugin, $pluginsPool); - } - } - - if (!defined("AJXP_SKIP_CACHE") || AJXP_SKIP_CACHE === false) { - AJXP_Utils::saveSerialFile(AJXP_PLUGINS_REQUIRES_FILE, $this->required_files, false, false); - AJXP_Utils::saveSerialFile(AJXP_PLUGINS_CACHE_FILE, $this->registry, false, false); - if (is_file(AJXP_PLUGINS_QUERIES_CACHE)) { - @unlink(AJXP_PLUGINS_QUERIES_CACHE); - } - - $this->savePluginsRegistryToCache(); - } - } - - /** - * Load plugin class with dependencies first - * - * @param AJXP_Plugin $plugin - * @param array $pluginsPool - */ - private function recursiveLoadPlugin($plugin, $pluginsPool) - { - if ($plugin->loadingState!="") { - return ; - } - $dependencies = $plugin->getDependencies(); - $plugin->loadingState = "lock"; - foreach ($dependencies as $dependencyId) { - if (isSet($pluginsPool[$dependencyId])) { - $this->recursiveLoadPlugin($pluginsPool[$dependencyId], $pluginsPool); - } else if (strpos($dependencyId, "+") !== false) { - foreach (array_keys($pluginsPool) as $pId) { - if (strpos($pId, str_replace("+", "", $dependencyId)) === 0) { - $this->recursiveLoadPlugin($pluginsPool[$pId], $pluginsPool); - } - } - } - } - $plugType = $plugin->getType(); - if (!isSet($this->registry[$plugType])) { - $this->registry[$plugType] = array(); - } - $options = $this->confStorage->loadPluginConfig($plugType, $plugin->getName()); - if($plugin->isEnabled() || (isSet($options["AJXP_PLUGIN_ENABLED"]) && $options["AJXP_PLUGIN_ENABLED"] === true)){ - $plugin = $this->instanciatePluginClass($plugin); - } - $plugin->loadConfigs($options); - $this->registry[$plugType][$plugin->getName()] = $plugin; - $plugin->loadingState = "loaded"; - } - - public function loadFromPluginQueriesCache($key) - { - if(AJXP_SKIP_CACHE) return null; - $test = AJXP_Utils::loadSerialFile(AJXP_PLUGINS_QUERIES_CACHE); - if (!empty($test) && is_array($test) && isset($test[$key])) { - return $test[$key]; - } - return null; - } - - public function storeToPluginQueriesCache($key, $value) - { - if(AJXP_SKIP_CACHE) return; - $test = AJXP_Utils::loadSerialFile(AJXP_PLUGINS_QUERIES_CACHE); - if(!is_array($test)) $test = array(); - $test[$key] = $value; - AJXP_Utils::saveSerialFile(AJXP_PLUGINS_QUERIES_CACHE, $test); - } - - public static function clearPluginsCache(){ - @unlink(AJXP_PLUGINS_CACHE_FILE); - @unlink(AJXP_PLUGINS_REQUIRES_FILE); - @unlink(AJXP_PLUGINS_QUERIES_CACHE); - @unlink(AJXP_PLUGINS_BOOTSTRAP_CACHE); - if(@unlink(AJXP_PLUGINS_REPOSITORIES_CACHE)){ - $content = "registry) && isSet($this->registry[$type][$name])) { - /** - * @var AJXP_Plugin $plugin - */ - $plugin = $this->registry[$type][$name]; - $plugin->init($pluginOptions); - return clone $plugin; - } - - - $plugin = new AJXP_Plugin($pluginId, AJXP_INSTALL_PATH."/".AJXP_PLUGINS_FOLDER."/".$pluginId); - $plugin->loadManifest(); - $plugin = $this->instanciatePluginClass($plugin); - $plugin->loadConfigs(array()); // Load default - $plugin->init($pluginOptions); - return $plugin; - } - - /** - * Find a PHP class and instanciate it to replace the empty AJXP_Plugin - * - * @param AJXP_Plugin $plugin - * @return AJXP_Plugin - */ - private function instanciatePluginClass($plugin) - { - $definition = $plugin->getClassFile(); - if(!$definition) return $plugin; - $filename = AJXP_INSTALL_PATH."/".$definition["filename"]; - $className = $definition["classname"]; - if (is_file($filename)) { - /** - * @var AJXP_Plugin $newPlugin - */ - require_once($filename); - $newPlugin = new $className($plugin->getId(), $plugin->getBaseDir()); - $newPlugin->loadManifest(); - $this->required_files[$filename] = $filename; - return $newPlugin; - } else { - return $plugin; - } - } - - /** - * Check that a plugin dependencies are loaded, disable it otherwise. - * @param AJXP_Plugin[] $arrayToSort - */ - private function checkDependencies(&$arrayToSort) - { - // First make sure that the given dependencies are present - foreach ($arrayToSort as $plugId => $plugObject) { - $plugObject->updateDependencies($this); - if($plugObject->hasMissingExtensions()){ - unset($arrayToSort[$plugId]); - continue; - } - $dependencies = $plugObject->getDependencies(); - if(!count($dependencies)) continue;// return ; - $found = false; - foreach ($dependencies as $requiredPlugId) { - if ( strpos($requiredPlugId, "+") !== false || isSet($arrayToSort[$requiredPlugId])) { - $found = true; break; - } - } - if (!$found) { - unset($arrayToSort[$plugId]); - } - } - } - - /** - * User function for sorting - * - * @param $pluginIdA - * @param $pluginIdB - * @internal param String $pluginA - * @internal param String $pluginB - * @return integer - */ - public static function sortByDependencyIds($pluginIdA, $pluginIdB) - { - if($pluginIdA == $pluginIdB) return 0; - $pluginA = self::findPluginById($pluginIdA); - $pluginB = self::findPluginById($pluginIdB); - if($pluginA == null || $pluginB == null) return 0; - if ($pluginA->dependsOn($pluginIdB)) { - return 1; - } - if ($pluginB->dependsOn($pluginIdA)) { - return -1; - } - return 0; - } - - public function getOrderByDependency($plugins, $withStatus = true) - { - $keys = array(); - $unkowns = array(); - if ($withStatus) { - foreach ($plugins as $pid => $status) { - if($status) $keys[] = $pid; - } - } else { - $keys = array_keys($plugins); - } - $result = array(); - while (count($keys) > 0) { - $test = array_shift($keys); - $testObject = $this->getPluginById($test); - $deps = $testObject->getActiveDependencies(self::getInstance()); - if (!count($deps)) { - $result[] = $test; - continue; - } - $found = false; - $inOriginalPlugins = false; - foreach ($deps as $depId) { - if (in_array($depId, $result)) { - $found = true; - break; - } - if (!$inOriginalPlugins && array_key_exists($depId, $plugins) && (!$withStatus || $plugins[$depId] == true)) { - $inOriginalPlugins = true; - } - } - if ($found) { - $result[] = $test; - } else { - if($inOriginalPlugins) $keys[] = $test; - else { - unset($plugins[$test]); - $unkowns[] = $test; - } - } - } - return array_merge($result, $unkowns); - } - - - - - /** - * All the plugins of a given type - * @param string $type - * @return AJXP_Plugin[] - */ - public function getPluginsByType($type) - { - if(isSet($this->registry[$type])) return $this->registry[$type]; - else return array(); - } - - /** - * Get a plugin instance - * - * @param string $pluginId - * @return AJXP_Plugin - */ - public function getPluginById($pluginId) - { - $split = explode(".", $pluginId); - return $this->getPluginByTypeName($split[0], $split[1]); - } - - /** - * Remove a plugin - * @param string $pluginId - * @return void - */ - public function removePluginById($pluginId) - { - $split = explode(".", $pluginId); - if (isSet($this->registry[$split[0]]) && isSet($this->registry[$split[0]][$split[1]])) { - unset($this->registry[$split[0]][$split[1]]); - } - } - - /** - * Load the plugins list, and set active the plugins automatically, - * except for the specific types that declare a "core.*" plugin. In that case, - * either the core class has an AUTO_LOAD_TYPE property and all plugins are activated, - * or it's the task of the core class to load the necessary plugin(s) of this type. - * @return void - */ - public function initActivePlugins() - { - /** - * @var AJXP_Plugin $pObject - */ - $detected = $this->getDetectedPlugins(); - $toActivate = array(); - foreach ($detected as $pType => $pObjects) { - $coreP = $this->findPlugin("core", $pType); - if($coreP !== false && !isSet($coreP->AUTO_LOAD_TYPE)) continue; - foreach ($pObjects as $pName => $pObject) { - $toActivate[$pObject->getId()] = $pObject ; - } - } - $o = $this->getOrderByDependency($toActivate, false); - foreach ($o as $id) { - $pObject = $toActivate[$id]; - $pObject->init(array()); - try { - $pObject->performChecks(); - if(!$pObject->isEnabled() || $pObject->hasMissingExtensions()) continue; - $this->setPluginActiveInst($pObject->getType(), $pObject->getName(), true); - } catch (Exception $e) { - //$this->errors[$pName] = "[$pName] ".$e->getMessage(); - } - - } - } - - /** - * Add a plugin to the list of active plugins - * @static - * @param string $type - * @param string $name - * @param bool $active - * @param AJXP_Plugin $updateInstance - * @return void - */ - public static function setPluginActive($type, $name, $active=true, $updateInstance = null) - { - self::getInstance()->setPluginActiveInst($type, $name, $active, $updateInstance); - } - /** - * Instance implementation of the setPluginActive - * @param $type - * @param $name - * @param bool $active - * @param AJXP_Plugin $updateInstance - * @return void - */ - public function setPluginActiveInst($type, $name, $active=true, $updateInstance = null) - { - if ($active) { - // Check active plugin dependencies - $plug = $this->getPluginById($type.".".$name); - if(!$plug || !$plug->isEnabled()) return; - $deps = $plug->getActiveDependencies($this); - if (count($deps)) { - $found = false; - foreach ($deps as $dep) { - if (isSet($this->activePlugins[$dep]) && $this->activePlugins[$dep] !== false) { - $found = true; break; - } - } - if (!$found) { - $this->activePlugins[$type.".".$name] = false; - return ; - } - } - } - if(isSet($this->activePlugins[$type.".".$name])){ - unset($this->activePlugins[$type.".".$name]); - } - $this->activePlugins[$type.".".$name] = $active; - if (isSet($updateInstance) && isSet($this->registry[$type][$name])) { - $this->registry[$type][$name] = $updateInstance; - } - if (isSet($this->xmlRegistry) && !$this->tmpDeferRegistryBuild) { - $this->buildXmlRegistry(($this->registryVersion == "extended")); - } - } - - public static function deferBuildingRegistry(){ - self::getInstance()->tmpDeferRegistryBuild = true; - } - - public static function flushDeferredRegistryBuilding(){ - $t = self::getInstance(); - $t->tmpDeferRegistryBuild = false; - if (isSet($t->xmlRegistry)) { - $t->buildXmlRegistry(($t->registryVersion == "extended")); - } - } - - /** - * Some type require only one active plugin at a time - * @param $type - * @param $name - * @param AJXP_Plugin $updateInstance - * @return void - */ - public function setPluginUniqueActiveForType($type, $name, $updateInstance = null) - { - $typePlugs = $this->getPluginsByType($type); - $originalValue = $this->tmpDeferRegistryBuild; - $this->tmpDeferRegistryBuild = true; - foreach ($typePlugs as $plugName => $plugObject) { - $this->setPluginActiveInst($type, $plugName, false); - } - $this->tmpDeferRegistryBuild = $originalValue; - $this->setPluginActiveInst($type, $name, true, $updateInstance); - } - /** - * Retrieve the whole active plugins list - * @return array - */ - public function getActivePlugins() - { - return $this->activePlugins; - } - /** - * Retrieve an array of active plugins for type - * @param string $type - * @param bool $unique - * @return AJXP_Plugin[] - */ - public function getActivePluginsForType($type, $unique = false) - { - $acts = array(); - foreach ($this->activePlugins as $plugId => $active) { - if(!$active) continue; - list($pT,$pN) = explode(".", $plugId); - if ($pT == $type && isset($this->registry[$pT][$pN])) { - if ($unique) { - return $this->registry[$pT][$pN]; - break; - } - $acts[$pN] = $this->registry[$pT][$pN]; - } - } - if($unique && !count($acts)) return false; - return $acts; - } - - /** - * Return only one of getActivePluginsForType - * @param $type - * @return array|bool - */ - public function getUniqueActivePluginForType($type) - { - return $this->getActivePluginsForType($type, true); - } - /** - * All the plugins registry, active or not - * @return array - */ - public function getDetectedPlugins() - { - return $this->registry; - } - /** - * All the plugins that declare a stream wrapper - * @return array - */ - public function getStreamWrapperPlugins() - { - return $this->streamWrapperPlugins; - } - /** - * Add the $protocol/$wrapper to an internal cache - * @param string $protocol - * @param string $wrapperClassName - * @return void - */ - public function registerWrapperClass($protocol, $wrapperClassName) - { - $this->registeredWrappers[$protocol] = $wrapperClassName; - } - - /** - * Find a classname for a given protocol - * @param $protocol - * @return - */ - public function getWrapperClassName($protocol) - { - return $this->registeredWrappers[$protocol]; - } - /** - * The protocol/classnames table - * @return array - */ - public function getRegisteredWrappers() - { - return $this->registeredWrappers; - } - /** - * Go through all plugins and call their getRegistryContributions() method. - * Add all these contributions to the main XML ajxp_registry document. - * @param bool $extendedVersion Will be passed to the plugin, for optimization purpose. - * @return void - */ - public function buildXmlRegistry($extendedVersion = true) - { - $actives = $this->getActivePlugins(); - $reg = new DOMDocument(); - $reg->loadXML(""); - foreach ($actives as $activeName=>$status) { - if($status === false) continue; - $plug = $this->getPluginById($activeName); - $contribs = $plug->getRegistryContributions($extendedVersion); - foreach ($contribs as $contrib) { - $parent = $contrib->nodeName; - $nodes = $contrib->childNodes; - if(!$nodes->length) continue; - $uuidAttr = $contrib->getAttribute("uuidAttr"); - if($uuidAttr == "") $uuidAttr = "name"; - $this->mergeNodes($reg, $parent, $uuidAttr, $nodes); - } - } - $this->xmlRegistry = $reg; - } - - /** - * Build the XML Registry if not already built, and return it. - * @static - * @param bool $extendedVersion - * @return DOMDocument The registry - */ - public static function getXmlRegistry($extendedVersion = true) - { - $self = self::getInstance(); - if (!isSet($self->xmlRegistry) || ($self->registryVersion == "light" && $extendedVersion)) { - $self->buildXmlRegistry( $extendedVersion ); - $self->registryVersion = ($extendedVersion ? "extended":"light"); - } - return $self->xmlRegistry; - } - - /** - * Replace the current xml registry - * @static - * @param $registry - * @param bool $extendedVersion - */ - public static function updateXmlRegistry($registry, $extendedVersion = true) - { - $self = self::getInstance(); - $self->xmlRegistry = $registry; - $self->registryVersion = ($extendedVersion? "extended" : "light"); - } - - /** - * Append some predefined XML to a plugin instance - * @param AJXP_Plugin $plugin - * @param DOMDocument $manifestDoc - * @param String $mixinName - */ - public function patchPluginWithMixin(&$plugin, &$manifestDoc, $mixinName) - { - // Load behaviours if not already - if (!isSet($this->mixinsDoc)) { - $this->mixinsDoc = new DOMDocument(); - $this->mixinsDoc->load(AJXP_INSTALL_PATH."/".AJXP_PLUGINS_FOLDER."/core.ajaxplorer/ajxp_mixins.xml"); - $this->mixinsXPath = new DOMXPath($this->mixinsDoc); - } - // Merge into manifestDoc - $nodeList = $this->mixinsXPath->query($mixinName); - if(!$nodeList->length) return; - $mixinNode = $nodeList->item(0); - foreach ($mixinNode->childNodes as $child) { - if($child->nodeType != XML_ELEMENT_NODE) continue; - $uuidAttr = $child->getAttribute("uuidAttr") OR "name"; - $this->mergeNodes($manifestDoc, $child->nodeName, $uuidAttr, $child->childNodes, true); - } - - // Reload plugin XPath - $plugin->reloadXPath(); - } - - /** - * Search all plugins manifest with an XPath query, and return either the Nodes, or directly an XML string. - * @param string $query - * @param string $stringOrNodeFormat - * @param boolean $limitToActivePlugins Whether to search only in active plugins or in all plugins - * @param bool $limitToEnabledPlugins - * @param bool $loadExternalFiles - * @return DOMNode[] - */ - public static function searchAllManifests($query, $stringOrNodeFormat = "string", $limitToActivePlugins = false, $limitToEnabledPlugins = false, $loadExternalFiles = false) - { - $buffer = ""; - $nodes = array(); - $self = self::getInstance(); - foreach ($self->registry as $plugType) { - foreach ($plugType as $plugName => $plugObject) { - if ($limitToActivePlugins) { - $plugId = $plugObject->getId(); - if ($limitToActivePlugins && (!isSet($self->activePlugins[$plugId]) || $self->activePlugins[$plugId] === false)) { - continue; - } - } - if ($limitToEnabledPlugins) { - if(!$plugObject->isEnabled()) continue; - } - $res = $plugObject->getManifestRawContent($query, $stringOrNodeFormat, $loadExternalFiles); - if ($stringOrNodeFormat == "string") { - $buffer .= $res; - } else { - foreach ($res as $node) { - $nodes[] = $node; - } - } - } - } - if($stringOrNodeFormat == "string") return $buffer; - else return $nodes; - } - - /** - * Central function of the registry construction, merges some nodes into the existing registry. - * @param DOMDocument $original - * @param $parentName - * @param $uuidAttr - * @param $childrenNodes - * @param bool $doNotOverrideChildren - * @return void - */ - protected function mergeNodes(&$original, $parentName, $uuidAttr, $childrenNodes, $doNotOverrideChildren = false) - { - // find or create parent - $parentSelection = $original->getElementsByTagName($parentName); - if ($parentSelection->length) { - $parentNode = $parentSelection->item(0); - $xPath = new DOMXPath($original); - foreach ($childrenNodes as $child) { - if($child->nodeType != XML_ELEMENT_NODE) continue; - if ($child->getAttribute($uuidAttr) == "*") { - $query = $parentName.'/'.$child->nodeName; - } else { - $query = $parentName.'/'.$child->nodeName.'[@'.$uuidAttr.' = "'.$child->getAttribute($uuidAttr).'"]'; - } - $childrenSel = $xPath->query($query); - if ($childrenSel->length) { - if($doNotOverrideChildren) continue; - foreach ($childrenSel as $existingNode) { - if($existingNode->getAttribute("forbidOverride") == "true"){ - continue; - } - // Clone as many as needed - $clone = $original->importNode($child, true); - $this->mergeChildByTagName($clone, $existingNode); - } - } else { - $clone = $original->importNode($child, true); - $parentNode->appendChild($clone); - } - } - } else { - //create parentNode and append children - if ($childrenNodes->length) { - $parentNode = $original->importNode($childrenNodes->item(0)->parentNode, true); - $original->documentElement->appendChild($parentNode); - } else { - $parentNode = $original->createElement($parentName); - $original->documentElement->appendChild($parentNode); - } - } - } - - /** - * Utilitary function - * @param $new - * @param $old - */ - protected function mergeChildByTagName($new, &$old) - { - if (!$this->hasElementChild($new) || !$this->hasElementChild($old)) { - $old->parentNode->replaceChild($new, $old); - return; - } - foreach ($new->childNodes as $newChild) { - if($newChild->nodeType != XML_ELEMENT_NODE) continue; - - $found = null; - foreach ($old->childNodes as $oldChild) { - if($oldChild->nodeType != XML_ELEMENT_NODE) continue; - if ($oldChild->nodeName == $newChild->nodeName) { - $found = $oldChild; - } - } - if ($found != null) { - if ($newChild->nodeName == "post_processing" || $newChild->nodeName == "pre_processing") { - $old->appendChild($newChild->cloneNode(true)); - } else { - if($found->getAttribute("forbidOverride") == "true") { - continue; - } - $this->mergeChildByTagName($newChild->cloneNode(true), $found); - } - } else { - // CloneNode or it's messing with the current foreach loop. - $old->appendChild($newChild->cloneNode(true)); - } - } - } - - /** - * Utilitary - * @param $node - * @return bool - */ - private function hasElementChild($node) - { - if(!$node->hasChildNodes()) return false; - foreach ($node->childNodes as $child) { - if($child->nodeType == XML_ELEMENT_NODE) return true; - } - return false; - } - - /** - * @param string $plugType - * @param string $plugName - * @return AJXP_Plugin - */ - public function getPluginByTypeName($plugType, $plugName) - { - if (isSet($this->registry[$plugType]) && isSet($this->registry[$plugType][$plugName])) { - return $this->registry[$plugType][$plugName]; - } else { - return false; - } - } - - /** - * - * @param string $type - * @param string $name - * @return AJXP_Plugin - */ - public static function findPlugin($type, $name) - { - $instance = self::getInstance(); - return $instance->getPluginByTypeName($type, $name); - } - - /** - * Simply find a plugin by its id (type.name) - * @static - * @param $id - * @return AJXP_Plugin - */ - public static function findPluginById($id) - { - return self::getInstance()->getPluginById($id); - } - - private function __construct() - { - } - /** - * Singleton method - * - * @return AJXP_PluginsService the service instance - */ - public static function getInstance() - { - if (!isSet(self::$instance)) { - $c = __CLASS__; - self::$instance = new $c; - } - return self::$instance; - } - public function __clone() - { - trigger_error("Cannot clone me, i'm a singleton!", E_USER_ERROR); - } -} diff --git a/core/src/core/classes/class.AJXP_ProgressBarCLI.php b/core/src/core/classes/class.AJXP_ProgressBarCLI.php deleted file mode 100644 index 6b22a87655..0000000000 --- a/core/src/core/classes/class.AJXP_ProgressBarCLI.php +++ /dev/null @@ -1,173 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Class AJXP_ProgressBarCLI - * Simple object to print a progress bar on the command-line. - * - * @package Pydio - * @subpackage Core - */ -class AJXP_ProgressBarCLI { - private $strProgress = ""; - private $strLength = 25; // 50 '=' - private $strName = ""; - private $strNameLengthMax = 20; - private $total = 0; - private $isFinish = false; - private $lastPercent = -1; - private $barSign = "="; - private $startPoint ; - private $lastsecond = 0; - - public function init($currentValue = 0, $total, $name){ - if(!(php_sapi_name() == "cli")) return; - $this->total = $total; - $this->strName = $name; - $this->startPoint = time(); - $this->barSign = "="; - //echo "\nobjects: ". $this->total."\n"; - $str = ''; - $str .= $this->convertNumToStrBlank($this->strNameLengthMax)."[=="; - $str .= "Total: ".$total." objects"; - $str .= $this->convertNumToStr($this->strLength - strlen($str) + 21); - $str .= "]"; - echo "\n"; - echo $str; - echo "\n"; - } - - public function update($currentValue = 0){ - if(!(php_sapi_name() == "cli")) return; - if($this->isFinish) return; - $percentage = $this->convertToPercent($currentValue, $this->total); - if (($percentage != $this->lastPercent) || ((time() - $this->lastsecond) > 0.9)){ - $this->lastPercent = $percentage; - $this->lastsecond = time(); - } - else{ - return; - } - $strDone = $this->convertNumToStr((int) $percentage/4); - $this->strProgress .= $strDone; - $this->strProgress .= $this->convertNumToStrBlank($this->strLength - strlen($strDone)); - $this->addStartString(); - $this->strProgress = $this->formatName($this->strName).$this->strProgress; - $this->addEndString(); - $this->strProgress .= " "; - - if($percentage < 10){ - $this->strProgress .= " "; - }elseif($percentage > 10 && $percentage < 100){ - $this->strProgress .= " "; - } - $this->strProgress .= "".$percentage." % " . $this->ago($this->startPoint); - $this->clearLine(strlen($this->strProgress) + 2); - echo $this->strProgress; - $this->strProgress = ""; - if($percentage == 100){ - echo "\n"; - $this->isFinish = true; - return ; - } - } - - private function addStartString(){ - $this->strProgress = "[".$this->strProgress; - } - - private function addEndString(){ - $this->strProgress = $this->strProgress."]"; - } - - private function convertToPercent($currentValue = 0, $maxValue){ - if($maxValue > 0 && $currentValue > 0){ - $percent = ( int ) ((100*$currentValue) / $maxValue); - return $percent; - } - elseif($maxValue === $currentValue){ - return 100; - } - return 0; - } - - private function convertNumToStr($num){ - if ($num >= 0 && $num <= $this->strLength){ - $str = ""; - for($i = 0; $i < $num; $i++){ - $str .= $this->barSign; - } - return $str; - }else{ - return ""; - } - } - - private function formatName($name){ - if(strlen($name) > $this->strNameLengthMax){ - return substr($name, 0, $this->strNameLengthMax); - }else{ - $str = $name . $this->convertNumToStrBlank($this->strNameLengthMax - strlen($name)); - return $str; - } - } - - public function convertNumToStrBlank($num){ - if($num > 0){ - $str = ""; - for($i = 0; $i < $num; $i++){ - $str .= " "; - } - return $str; - } - return ""; - } - - public function clearLine($lineLength = 80){ - for($i = 0; $i < $lineLength ;$i ++){ - echo "\010"; - } - } - - public function setName($name){ - $this->strName = $name; - } - - public function ago($time) { - $timediff=time()-$time; - - $days=intval($timediff/86400); - $remain=$timediff%86400; - $hours=intval($remain/3600); - $remain=$remain%3600; - $mins=intval($remain/60); - $secs=$remain%60; - $timestring = "-"; - - if ($secs>=0) $timestring = "0m".$secs."s"; - if ($mins>0) $timestring = $mins."m".$secs."s"; - if ($hours>0) $timestring = $hours."u".$mins."m"; - if ($days>0) $timestring = $days."d".$hours."u"; - - return $timestring; - } -} \ No newline at end of file diff --git a/core/src/core/classes/class.AJXP_PromptException.php b/core/src/core/classes/class.AJXP_PromptException.php deleted file mode 100644 index 8cfb485281..0000000000 --- a/core/src/core/classes/class.AJXP_PromptException.php +++ /dev/null @@ -1,104 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -define('AJXP_PROMPT_EXCEPTION_PROMPT', 'AJXP_PROMPT_EXCEPTION_PROMPT'); -define('AJXP_PROMPT_EXCEPTION_CONFIRM', 'AJXP_PROMPT_EXCEPTION_CONFIRM'); -define('AJXP_PROMPT_EXCEPTION_ALERT', 'AJXP_PROMPT_EXCEPTION_ALERT'); - -/** - * Class AJXP_PromptException - * Specific exception that triggers a prompt in the UI instead of displaying an error message. - * - * @package Pydio - * @subpackage Core - */ -class AJXP_PromptException extends AJXP_Exception{ - - private $promptType = "prompt"; - /** - * @var array - */ - private $promptData = array(); - - /** - * @return array - */ - public function getPromptData() - { - return $this->promptData; - } - - /** - * @return string - */ - public function getPromptType() - { - return $this->promptType; - } - - /** - * @param $promptType - * @param array $data - * @param String $messageString - * @param string|bool $messageId - */ - public function __construct($promptType, $data, $messageString, $messageId = false) - { - $this->promptType = $promptType; - $this->promptData = $data; - parent::__construct($messageString, $messageId); - } - - /** - * Prompt user for credentials - * @param $sessionVariable - * @param $switchToRepositoryId - * @throws AJXP_PromptException - */ - public static function testOrPromptForCredentials($sessionVariable, $switchToRepositoryId){ - if(isSet($_GET["prompt_passed_data"]) && isSet($_GET["variable_name"]) && $_GET["variable_name"] == $sessionVariable){ - $_SESSION[$sessionVariable] = true; - } - if(!isSet($_SESSION[$sessionVariable])){ - throw new AJXP_PromptException( - "confirm", - array( - "DIALOG" => "Please enter your credentials for this workspace - - - - - ", - "OK" => array( - "GET_FIELDS" => array("get_action", "repository_id", "prompt_passed_data", "variable_name"), - "EVAL" => "ajaxplorer.loadXmlRegistry();" - ), - "CANCEL" => array( - "EVAL" => "ajaxplorer.loadXmlRegistry();" - ) - ), - "Credentials Needed"); - } - - } - -} \ No newline at end of file diff --git a/core/src/core/classes/class.AJXP_Role.php b/core/src/core/classes/class.AJXP_Role.php deleted file mode 100644 index a5e128184f..0000000000 --- a/core/src/core/classes/class.AJXP_Role.php +++ /dev/null @@ -1,589 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die('Access not allowed'); - -define('AJXP_VALUE_CLEAR', "AJXP_VALUE_CLEAR"); -define('AJXP_REPO_SCOPE_ALL',"AJXP_REPO_SCOPE_ALL"); -define('AJXP_REPO_SCOPE_SHARED',"AJXP_REPO_SCOPE_SHARED"); -define('AJXP_PLUGINS_SCOPE_ALL',"plugin_all"); - -/** - * @package Pydio - * @subpackage Core - */ -class AJXP_Role implements AjxpGroupPathProvider -{ - - /** - * @var String path of this role, default is root path - */ - protected $groupPath; - /** - * @var String Role identifier - */ - protected $roleId; - - /** - * @var array List of access rights for each workspaces (wsId => "r", "w", "rw", "d") - */ - protected $acls = array(); - /** - * @var array List of plugins parameters values, (SCOPE => PLUGIN NAME => PARAM NAME => value) - */ - protected $parameters = array(); - /** - * @var array List of plugin actions that can be disabled/enabled (SCOPE => PLUGIN NAME => ACTION NAME => status) - */ - protected $actions = array(); - /** - * @var array Automatically applies to a given list of profiles - */ - protected $autoApplies = array(); - /** - * @var AJXP_PermissionMask[] - */ - protected $masks = array(); - - /** - * @var integer - */ - protected $lastUpdated = 0; - - static $cypheredPassPrefix = '$pydio_password$'; - - public function __construct($id) - { - $this->roleId = $id; - } - - public function migrateDeprectated($repositoriesList, AjxpRole $oldRole) - { - $repositoriesList["ajxp.all"] = ""; - foreach ($repositoriesList as $repoId => $repoObject) { - $right = $oldRole->getRight($repoId); - if(!empty($right)) $this->setAcl($repoId, $right); - $actions = $oldRole->getSpecificActionsRights($repoId); - if (count($actions)) { - foreach ($actions as $act => $status) { - if ($repoId == "ajxp.all") { - $this->setActionState(AJXP_PLUGINS_SCOPE_ALL, $act, AJXP_REPO_SCOPE_ALL, $status); - } else { - $this->setActionState(AJXP_PLUGINS_SCOPE_ALL, $act, $repoId, $status); - } - } - } - } - $this->setGroupPath($oldRole->getGroupPath()); - if ($oldRole->isDefault()) { - $this->setAutoApplies(array("all")); - } - } - - public function isGroupRole() - { - return strpos($this->roleId, "AJXP_GRP_") === 0; - } - public function isUserRole() - { - return strpos($this->roleId, "AJXP_USER_") === 0; - } - - - /** - * Whether this role can read the given repo - * @param string $repositoryId Repository ID - * @return bool - */ - public function canRead($repositoryId) - { - $right = $this->getAcl($repositoryId); - if($right == "rw" || $right == "r") return true; - return false; - } - - /** - * Whether this role can write the given repo - * @param string $repositoryId Repository ID - * @return bool - */ - public function canWrite($repositoryId) - { - $right = $this->getAcl($repositoryId); - if($right == "rw" || $right == "w") return true; - return false; - } - - - /** - * @param string $repositoryId - * @param string $rightString - * @return void - */ - public function setAcl($repositoryId, $rightString) - { - if (empty($rightString)) { - if(isSet($this->acls[$repositoryId])) unset($this->acls[$repositoryId]); - } else { - $this->acls[$repositoryId] = $rightString; - } - return; - } - /** - * @param string $repositoryId - * @return string - */ - public function getAcl($repositoryId) - { - if (isSet($this->acls[$repositoryId])) { - return $this->acls[$repositoryId]; - } - return ""; - } - - /** - * @param bool $accessibleOnly If set to true, return only r, w, or rw. - * @return array Associative array[REPO_ID] => RIGHT_STRING (r / w / rw / AJXP_VALUE_CLEAR) - */ - public function listAcls($accessibleOnly = false) - { - if(!$accessibleOnly){ - return $this->acls; - } - $output = array(); - foreach ($this->acls as $id => $acl) { - if(empty($acl) || $acl == AJXP_VALUE_CLEAR) continue; - $output[$id] = $acl; - } - return $output; - } - - public function clearAcls() - { - $this->acls = array(); - } - - /** - * @param String $repositoryId - * @param AJXP_PermissionMask $mask - */ - public function setMask($repositoryId, $mask){ - $this->masks[$repositoryId] = $mask; - } - - /** - * @param string $repositoryId - */ - public function clearMask($repositoryId){ - if(isSet($this->masks[$repositoryId])){ - unset($this->masks[$repositoryId]); - } - } - - /** - * @param string $repositoryId - * @return bool - */ - public function hasMask($repositoryId){ - return isSet($this->masks[$repositoryId]); - } - - /** - * @param $repositoryId - * @return AJXP_PermissionMask|null - */ - public function getMask($repositoryId){ - return (isSet($this->masks[$repositoryId]) ? $this->masks[$repositoryId] : null); - } - - /** - * @return AJXP_PermissionMask[] - */ - public function listMasks(){ - return $this->masks; - } - - /** - * Send all role informations as an associative array - * @param bool $blurPasswords - * @return array - */ - public function getDataArray($blurPasswords = false) - { - $roleData = array(); - $roleData["ACL"] = $this->listAcls(); - $roleData["MASKS"] = $this->listMasks(); - $roleData["ACTIONS"] = $this->listActionsStates(); - $roleData["PARAMETERS"] = $this->listParameters(false, $blurPasswords); - $roleData["APPLIES"] = $this->listAutoApplies(); - return $roleData; - } - - /** - * Update the role information from an associative array - * @see getDataArray() - * @param array $roleData - */ - public function bunchUpdate($roleData) - { - $this->acls = $roleData["ACL"]; - $this->actions = $roleData["ACTIONS"]; - $this->parameters = $roleData["PARAMETERS"]; - $this->autoApplies = $roleData["APPLIES"]; - if(isSet($roleData["MASKS"])){ - $this->masks = $roleData["MASKS"]; - } - - } - - - /** - * @param string $pluginId - * @param string $parameterName - * @param mixed $parameterValue can be AJXP_VALUE_CLEAR (force clear previous), or empty string for clearing value (apply previous). - * @param string|null $repositoryId - */ - public function setParameterValue($pluginId, $parameterName, $parameterValue, $repositoryId = null) - { - if($repositoryId === null) $repositoryId = AJXP_REPO_SCOPE_ALL; - if (empty($parameterValue) && $parameterValue !== false && $parameterValue !== "0") { - if (isSet($this->parameters[$repositoryId][$pluginId][$parameterName])) { - unset($this->parameters[$repositoryId][$pluginId][$parameterName]); - if(!count($this->parameters[$repositoryId][$pluginId])) unset($this->parameters[$repositoryId][$pluginId]); - if(!count($this->parameters[$repositoryId])) unset($this->parameters[$repositoryId]); - } - } else { - $this->parameters = $this->setArrayValue($this->parameters, $repositoryId, $pluginId, $parameterName, $parameterValue); - } - return; - } - - /** - * @param string $pluginId - * @param string $parameterName - * @param string $repositoryId - * @param mixed $parameterValue - * @return mixed - */ - public function filterParameterValue($pluginId, $parameterName, $repositoryId, $parameterValue) - { - if (isSet($this->parameters[$repositoryId][$pluginId][$parameterName])) { - $v = $this->parameters[$repositoryId][$pluginId][$parameterName]; - if($v === AJXP_VALUE_CLEAR) return ""; - else return $this->filterCypheredPasswordValue($v); - } - if (isSet($this->parameters[AJXP_REPO_SCOPE_ALL][$pluginId][$parameterName])) { - $v = $this->parameters[AJXP_REPO_SCOPE_ALL][$pluginId][$parameterName]; - if($v === AJXP_VALUE_CLEAR) return ""; - else return $this->filterCypheredPasswordValue($v); - } - return $parameterValue; - } - - /** - * @param bool $preserveCypheredPasswords - * @param bool $blurCypheredPasswords - * @return array Associative array of parameters : array[REPO_ID][PLUGIN_ID][PARAMETER_NAME] = PARAMETER_VALUE - */ - public function listParameters($preserveCypheredPasswords = false, $blurCypheredPasswords = false) - { - if($preserveCypheredPasswords) return $this->parameters; - - $copy = $this->parameters; - foreach($copy as $repo => &$plugs){ - foreach($plugs as $plugName => &$plugData){ - foreach($plugData as $paramName => &$paramValue){ - $testValue = $this->filterCypheredPasswordValue($paramValue); - if($testValue != $paramValue){ - if($blurCypheredPasswords) $paramValue = "__AJXP_VALUE_SET__"; - else $paramValue = $testValue; - } - } - } - } - return $copy; - } - - public function listAutoApplies() - { - return $this->autoApplies; - } - - /** - * @param String $value - * @return String - */ - private function filterCypheredPasswordValue($value){ - if(is_string($value) && strpos($value, self::$cypheredPassPrefix) === 0) return str_replace(self::$cypheredPassPrefix, "", $value); - return $value; - } - - /** - * @param string $pluginId - * @param string $actionName - * @param string|null $repositoryId - * @param string $state - */ - public function setActionState($pluginId, $actionName, $repositoryId = null, $state = "disabled") - { - $this->actions = $this->setArrayValue($this->actions, $repositoryId, $pluginId, $actionName, $state); - return; - } - - public function listActionsStates() - { - return $this->actions; - } - - /** - * @param Repository $repository - * @return array - */ - public function listActionsStatesFor($repository) - { - $actions = array(); - if (isSet($this->actions[AJXP_REPO_SCOPE_ALL])) { - $actions = $this->actions[AJXP_REPO_SCOPE_ALL]; - } - if ($repository != null && isSet($this->actions[AJXP_REPO_SCOPE_SHARED]) && $repository->hasParent()) { - $actions = $this->array_merge_recursive2($actions, $this->actions[AJXP_REPO_SCOPE_SHARED]); - } - if ($repository != null && isSet($this->actions[$repository->getId()])) { - $actions = $this->array_merge_recursive2($actions, $this->actions[$repository->getId()]); - } - return $actions; - } - - /** - * @param string $pluginId - * @param string $actionName - * @param string $repositoryId - * @param boolean $inputState - * @return boolean - */ - public function actionEnabled($pluginId, $actionName, $repositoryId, $inputState) - { - if (isSet($this->actions[AJXP_REPO_SCOPE_ALL][$pluginId][$actionName])) { - return $this->actions[AJXP_REPO_SCOPE_ALL][$pluginId][$actionName] == "enabled" ? true : false ; - } - if (isSet($this->actions[$repositoryId][$pluginId][$actionName])) { - return $this->actions[$repositoryId][$pluginId][$actionName] == "enabled" ? true : false ; - } - return $inputState; - } - - /** - * @return array - */ - public function listAllActionsStates() - { - return $this->actions; - } - - /** - * @param AJXP_Role $role - * @return AJXP_Role - */ - public function override(AJXP_Role $role) - { - $newRole = new AJXP_Role($role->getId()); - - $roleAcl = $role->listAcls(); - $newAcls = $this->array_merge_recursive2($roleAcl, $this->listAcls()); - foreach ($newAcls as $repoId => $rightString) { - //if($rightString == AJXP_VALUE_CLEAR) continue; - if(empty($rightString) && !empty($roleAcl[$repoId])){ - $rightString = $roleAcl[$repoId]; - } - $newRole->setAcl($repoId, $rightString); - } - - $roleParameters = $role->listParameters(true); - $newParams = $this->array_merge_recursive2($roleParameters, $this->listParameters(true)); - foreach ($newParams as $repoId => $data) { - foreach ($data as $pluginId => $param) { - foreach ($param as $parameterName => $parameterValue) { - if ($parameterValue === true || $parameterValue === false) { - $newRole->setParameterValue($pluginId, $parameterName, $parameterValue, $repoId); - continue; - } - if($parameterValue == AJXP_VALUE_CLEAR) continue; - if($parameterValue === "" && !empty($roleParameters[$repoId][$pluginId][$parameterName])){ - $parameterValue = $newParams[$repoId][$pluginId][$parameterName]; - } - $newRole->setParameterValue($pluginId, $parameterName, $parameterValue, $repoId); - } - } - } - - $newActions = $this->array_merge_recursive2($role->listActionsStates(), $this->listActionsStates()); - foreach ($newActions as $repoId => $data) { - foreach ($data as $pluginId => $action) { - foreach ($action as $actionName => $actionState) { - $newRole->setActionState($pluginId, $actionName, $repoId, $actionState); - } - } - } - - $roleMasks = $role->listMasks(); - $allKeys = array_merge(array_keys($this->masks), array_keys($roleMasks)); - foreach($allKeys as $repoId){ - if(isSet($roleMasks[$repoId]) && isSet($this->masks[$repoId])){ - $newRole->setMask($repoId, $roleMasks[$repoId]->override($this->masks[$repoId])); - }else if(isSet($roleMasks[$repoId])){ - $newRole->setMask($repoId, $roleMasks[$repoId]); - }else{ - $newRole->setMask($repoId, $this->masks[$repoId]); - } - } - - return $newRole; - } - - /** - * @param array - * @param key1 - * @param key2 - * @param key3... - * @param value - */ - public function setArrayValue() - { - $args = func_get_args(); - $arr = $args[0]; //array_shift($args); - $argMaxIndex = count($args)-1; - $value = $args[$argMaxIndex]; //array_pop($args); - $current = &$arr; - foreach ($args as $index => $key) { - if($index == 0) continue; - if ($index < $argMaxIndex -1) { - if(!isset($current[$key])) $current[$key] = array(); - $current = &$current[$key]; - } else { - $current[$key] = $value; - break; - } - } - return $arr; - } - - /** - * @param array $array1 - * @param array $array2 - * @return array - */ - public function array_merge_recursive2($array1, $array2) - { - $arrays = func_get_args(); - $narrays = count($arrays); - - // check arguments - // comment out if more performance is necessary (in this case the foreach loop will trigger a warning if the argument is not an array) - for ($i = 0; $i < $narrays; $i ++) { - if (!is_array($arrays[$i])) { - // also array_merge_recursive returns nothing in this case - trigger_error('Argument #' . ($i+1) . ' is not an array - trying to merge array with scalar! Returning null!', E_USER_WARNING); - return null; - } - } - - // the first array is in the output set in every case - $ret = $arrays[0]; - - // merege $ret with the remaining arrays - for ($i = 1; $i < $narrays; $i ++) { - foreach ($arrays[$i] as $key => $value) { - //if (((string) $key) === ((string) intval($key))) { // integer or string as integer key - append - // $ret[] = $value; - //} - //{ // string key - megre - if (is_array($value) && isset($ret[$key])) { - // if $ret[$key] is not an array you try to merge an scalar value with an array - the result is not defined (incompatible arrays) - // in this case the call will trigger an E_USER_WARNING and the $ret[$key] will be null. - $ret[$key] = $this->array_merge_recursive2($ret[$key], $value); - } else { - $ret[$key] = $value; - } - // } - } - } - - return $ret; - } - - public function setGroupPath($groupPath) - { - $this->groupPath = $groupPath; - } - - public function getGroupPath() - { - return $this->groupPath; - } - - public function getId() - { - return $this->roleId; - } - - public function setLabel($roleLabel) - { - $this->setParameterValue("core.conf", "ROLE_DISPLAY_NAME", $roleLabel); - } - - public function getLabel() - { - $test = $this->filterParameterValue("core.conf", "ROLE_DISPLAY_NAME", AJXP_REPO_SCOPE_ALL, $this->roleId); - if(!empty($test)) return $test; - return $this->roleId; - } - - public function alwaysOverrides() - { - return $this->filterParameterValue("core.conf", "ROLE_FORCE_OVERRIDE", AJXP_REPO_SCOPE_ALL, false); - } - - /** - * @param array $specificRights - */ - public function setAutoApplies($specificRights) - { - $this->autoApplies = $specificRights; - } - - /** - * @param string $specificRight - * @return boolean - */ - public function autoAppliesTo($specificRight) - { - return in_array($specificRight, $this->autoApplies); - } - - public function getLastUpdated(){ - return $this->lastUpdated; - } - - public function setLastUpdated($time){ - $this->lastUpdated = $time; - } - -} diff --git a/core/src/core/classes/class.AJXP_Safe.php b/core/src/core/classes/class.AJXP_Safe.php deleted file mode 100644 index a0e84c0d39..0000000000 --- a/core/src/core/classes/class.AJXP_Safe.php +++ /dev/null @@ -1,287 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Credential keeper that can be stored in the session, the credentials are kept crypted. - * @package Pydio - * @subpackage Core - */ -class AJXP_Safe -{ - private static $instance; - - private $user; - private $encodedPassword; - private $secretKey; - private $separator = "__SAFE_SEPARATOR__"; - private $forceSessionCredentials = false; - /** - * Instance constructor - */ - public function __construct() - { - if (defined('AJXP_SAFE_SECRET_KEY')) { - $this->secretKey = AJXP_SAFE_SECRET_KEY; - } else { - $this->secretKey = "\1CDAFx¨op#"; - } - } - /** - * Store the user/password pair. Password will be encoded - * @param string $user - * @param string $password - * @return void - */ - public function setCredentials($user, $password) - { - $this->user = $user; - $this->encodedPassword = $this->_encodePassword($password, $user); - } - /** - * Return the user/password pair, or false if cannot find it. - * @return array|bool - */ - public function getCredentials() - { - if (isSet($this->user) && isSet($this->encodedPassword)) { - $decoded = $this->_decodePassword($this->encodedPassword, $this->user); - return array( - "user" => $this->user, - "password" => $decoded, - 0 => $this->user, - 1 => $decoded - ); - } else { - return false; - } - } - /** - * Use mcrypt function to encode the password - * @param $password - * @param $user - * @return string - */ - private function _encodePassword($password, $user) - { - if (function_exists('mcrypt_encrypt')) { - // We encode as base64 so if we need to store the result in a database, it can be stored in text column - $password = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($user.$this->secretKey), $password, MCRYPT_MODE_ECB)); - } - return $password; - } - /** - * Use mcrypt functions to decode the password - * @param $encoded - * @param $user - * @return string - */ - private function _decodePassword($encoded, $user) - { - if (function_exists('mcrypt_decrypt')) { - // We have encoded as base64 so if we need to store the result in a database, it can be stored in text column - $encoded = trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($user.$this->secretKey), base64_decode($encoded), MCRYPT_MODE_ECB), "\0"); - } - return $encoded; - } - /** - * Store the password credentials in the session - * @return void - */ - public function store() - { - $_SESSION["AJXP_SAFE_CREDENTIALS"] = base64_encode($this->user.$this->separator.$this->encodedPassword); - } - - /** - * Load the credentials from session - * @param string $encodedString - * @return void - */ - public function load($encodedString = "") - { - if ($encodedString == "" && !empty($_SESSION["AJXP_SAFE_CREDENTIALS"])) { - $encodedString = $_SESSION["AJXP_SAFE_CREDENTIALS"]; - } - if(empty($encodedString)) return; - $sessData = base64_decode($encodedString); - $parts = explode($this->separator, $sessData); - $this->user = $parts[0]; - $this->encodedPassword = $parts[1]; - } - /** - * Remove the credentials from session - * @return void - */ - public function clear() - { - unset($_SESSION["AJXP_SAFE_CREDENTIALS"]); - $this->user = null; - $this->encodedPassword = null; - } - /** - * For the session credentials to override other credentials set via config - * @return void - */ - public function forceSessionCredentialsUsage() - { - $this->forceSessionCredentials = true; - } - - - - /** - * Creates the singleton instance - * @return AJXP_Safe - */ - public static function getInstance() - { - if (empty(self::$instance)) { - self::$instance = new AJXP_Safe(); - } - return self::$instance; - } - /** - * Store the user/pass key pair - * @static - * @param string $user - * @param string $password - * @return void - */ - public static function storeCredentials($user, $password) - { - $inst = AJXP_Safe::getInstance(); - $inst->setCredentials($user, $password); - $inst->store(); - } - /** - * Remove the user/pass encoded from the session - * @static - * @return void - */ - public static function clearCredentials() - { - $inst = AJXP_Safe::getInstance(); - $inst->clear(); - } - /** - * Retrieve the user/pass from the session - * @static - * @return array|bool - */ - public static function loadCredentials() - { - $inst = AJXP_Safe::getInstance(); - $inst->load(); - return $inst->getCredentials(); - } - - public static function getEncodedCredentialString() - { - return $_SESSION["AJXP_SAFE_CREDENTIALS"]; - } - - public static function getCredentialsFromEncodedString($encoded) - { - $tmpInstance = new AJXP_Safe(); - $tmpInstance->load($encoded); - return $tmpInstance->getCredentials(); - } - - /** - * Will try to get the credentials for a given repository as follow : - * + Try to get the credentials from the url parsing - * + Try to get them from the user "Wallet" (personal data) - * + Try to get them from the repository configuration - * + Try to get them from the AJXP_Safe. - * - * @param array $parsedUrl - * @param Repository $repository - * @return array - */ - public static function tryLoadingCredentialsFromSources($parsedUrl, $repository) - { - $user = $password = ""; - $optionsPrefix = ""; - if ($repository->getAccessType() == "ftp") { - $optionsPrefix = "FTP_"; - } - // Get USER/PASS - // 1. Try from URL - if (isSet($parsedUrl["user"]) && isset($parsedUrl["pass"])) { - $user = rawurldecode($parsedUrl["user"]); - $password = rawurldecode($parsedUrl["pass"]); - } - // 2. Try from user wallet - if ($user=="") { - $loggedUser = AuthService::getLoggedUser(); - if ($loggedUser != null) { - $wallet = $loggedUser->getPref("AJXP_WALLET"); - if (is_array($wallet) && isSet($wallet[$repository->getId()][$optionsPrefix."USER"])) { - $user = $wallet[$repository->getId()][$optionsPrefix."USER"]; - $password = AJXP_Utils::decypherStandardFormPassword($loggedUser->getId(), $wallet[$repository->getId()][$optionsPrefix."PASS"]); - } - } - } - // 2bis. Wallet is now a custom parameter - if ($user =="") { - $loggedUser = AuthService::getLoggedUser(); - if ($loggedUser != null) { - $u = $loggedUser->mergedRole->filterParameterValue("access.".$repository->getAccessType(), $optionsPrefix."USER", $repository->getId(), ""); - $p = $loggedUser->mergedRole->filterParameterValue("access.".$repository->getAccessType(), $optionsPrefix."PASS", $repository->getId(), ""); - if (!empty($u) && !empty($p)) { - $user = $u; - $password = AJXP_Utils::decypherStandardFormPassword($loggedUser->getId(), $p); - } - } - } - // 3. Try from repository config - if ($user=="") { - $user = $repository->getOption($optionsPrefix."USER"); - $password = $repository->getOption($optionsPrefix."PASS"); - } - // 4. Test if there are encoded credentials available - if ($user == "" && $repository->getOption("ENCODED_CREDENTIALS") != "") { - list($user, $password) = AJXP_Safe::getCredentialsFromEncodedString($repository->getOption("ENCODED_CREDENTIALS")); - } - // 5. Try from session - $storeCreds = false; - if ($repository->getOption("META_SOURCES")) { - $options["META_SOURCES"] = $repository->getOption("META_SOURCES"); - foreach ($options["META_SOURCES"] as $metaSource) { - if (isSet($metaSource["USE_SESSION_CREDENTIALS"]) && $metaSource["USE_SESSION_CREDENTIALS"] === true) { - $storeCreds = true; - break; - } - } - } - if ($user=="" && ( $repository->getOption("USE_SESSION_CREDENTIALS") || $storeCreds || self::getInstance()->forceSessionCredentials )) { - $safeCred = AJXP_Safe::loadCredentials(); - if ($safeCred !== false) { - $user = $safeCred["user"]; - $password = $safeCred["password"]; - } - } - return array("user" => $user, "password" => $password); - - } - -} diff --git a/core/src/core/classes/class.AJXP_SchemeTranslatorWrapper.php b/core/src/core/classes/class.AJXP_SchemeTranslatorWrapper.php deleted file mode 100644 index 7d4d09715f..0000000000 --- a/core/src/core/classes/class.AJXP_SchemeTranslatorWrapper.php +++ /dev/null @@ -1,341 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die('Access not allowed'); - -/** - * Class AJXP_SchemeTranslatorWrapper - * Simple Wrapper that justs converts a given protocol to another one. - * - * @package Pydio - * @subpackage Core - */ -class AJXP_SchemeTranslatorWrapper extends AJXP_MetaStreamWrapper implements AjxpWrapper -{ - /** - * @var resource - */ - protected $handle; - - /** - * @var string - */ - protected $currentDirPath; - - - /* - * Initialise the path for a file - */ - public static function applyInitPathHook($path, $context = 'core') { - //Do nothing - } - - /** - * Get a "usable" reference to a file : the real file or a tmp copy. - * - * @param string $path - * @param bool $persistent - * @return string - * @throws Exception - */ - public static function getRealFSReference($path, $persistent = false) - { - $wrapper = AJXP_MetaStreamWrapper::findSubWrapperClassName($path); - return call_user_func(array($wrapper, "getRealFSReference"), AJXP_MetaStreamWrapper::translateScheme($path), $persistent); - } - - /** - * Read a file (by chunks) and copy the data directly inside the given stream. - * - * @param string $path - * @param resource $stream - */ - public static function copyFileInStream($path, $stream) - { - $wrapper = AJXP_MetaStreamWrapper::findSubWrapperClassName($path); - call_user_func(array($wrapper, "copyFileInStream"), AJXP_MetaStreamWrapper::translateScheme($path), $stream); - } - - /** - * Chmod implementation for this type of access. - * - * @param string $path - * @param number $chmodValue - */ - public static function changeMode($path, $chmodValue) - { - $wrapper = AJXP_MetaStreamWrapper::findSubWrapperClassName($path); - call_user_func(array($wrapper, "changeMode"), AJXP_MetaStreamWrapper::translateScheme($path), $chmodValue); - } - - /** - * - * - * @return bool - */ - public function dir_closedir() - { - $this->currentDirPath = null; - if(isSet($this->handle) && is_resource($this->handle)){ - closedir($this->handle); - } - } - - /** - * Enter description here... - * - * @param string $path - * @param int $options - * @return bool - */ - public function dir_opendir($path, $options) - { - $newPath = AJXP_MetaStreamWrapper::translateScheme($path); - $this->handle = opendir($newPath); - if($this->handle !== false){ - $this->currentDirPath = $path; - return true; - }else{ - return false; - } - } - - /** - * Enter description here... - * - * @return string - */ - public function dir_readdir() - { - if(isSet($this->handle) && is_resource($this->handle)){ - return readdir($this->handle); - } - return false; - } - - /** - * Enter description here... - * - * @return bool - */ - public function dir_rewinddir() - { - //$this->currentDirPath = null; - if(isSet($this->handle) && is_resource($this->handle)){ - return rewind($this->handle); - } - return false; - } - - /** - * Enter description here... - * - * @param string $path - * @param int $mode - * @param int $options - * @return bool - */ - public function mkdir($path, $mode, $options) - { - return mkdir(AJXP_MetaStreamWrapper::translateScheme($path), $mode, $options); - } - - /** - * Enter description here... - * - * @param string $path_from - * @param string $path_to - * @return bool - */ - public function rename($path_from, $path_to) - { - return rename(AJXP_MetaStreamWrapper::translateScheme($path_from), AJXP_MetaStreamWrapper::translateScheme($path_to)); - } - - /** - * Enter description here... - * - * @param string $path - * @param int $options - * @return bool - */ - public function rmdir($path, $options) - { - if(is_resource($options)){ - return rmdir(AJXP_MetaStreamWrapper::translateScheme($path), $options); - }else{ - return rmdir(AJXP_MetaStreamWrapper::translateScheme($path)); - } - } - - /** - * Enter description here... - * - */ - public function stream_close() - { - if(isSet($this->handle) && is_resource($this->handle)){ - return fclose($this->handle); - } - return false; - } - - /** - * Enter description here... - * - * @return bool - */ - public function stream_eof() - { - if(isSet($this->handle) && is_resource($this->handle)){ - return feof($this->handle); - } - return false; - } - - /** - * Enter description here... - * - * @return bool - */ - public function stream_flush() - { - if(isSet($this->handle) && is_resource($this->handle)){ - return fflush($this->handle); - } - return false; - } - - /** - * Enter description here... - * - * @param string $path - * @param string $mode - * @param int $options - * @param string &$context - * @return bool - */ - public function stream_open($path, $mode, $options, &$context) - { - if(is_resource($context)){ - $this->handle = fopen(AJXP_MetaStreamWrapper::translateScheme($path), $mode, $options, $context); - }else{ - $this->handle = fopen(AJXP_MetaStreamWrapper::translateScheme($path), $mode, $options); - } - return ($this->handle !== false); - } - - /** - * Enter description here... - * - * @param int $count - * @return string - */ - public function stream_read($count) - { - if(isSet($this->handle) && is_resource($this->handle)){ - return fread($this->handle, $count); - } - return null; - } - - /** - * Enter description here... - * - * @param int $offset - * @param int $whence = SEEK_SET - * @return bool - */ - public function stream_seek($offset, $whence = SEEK_SET) - { - if(isSet($this->handle) && is_resource($this->handle)){ - return fseek($this->handle, $offset, $whence); - } - return false; - } - - /** - * Enter description here... - * - * @return array - */ - public function stream_stat() - { - if(isSet($this->handle) && is_resource($this->handle)){ - return fstat($this->handle); - } - return false; - } - - /** - * Enter description here... - * - * @return int - */ - public function stream_tell() - { - if(isSet($this->handle) && is_resource($this->handle)){ - return ftell($this->handle); - } - return false; - } - - /** - * Enter description here... - * - * @param string $data - * @return int - */ - public function stream_write($data) - { - if(isSet($this->handle) && is_resource($this->handle)){ - return fwrite($this->handle, $data); - } - return false; - } - - /** - * Enter description here... - * - * @param string $path - * @return bool - */ - public function unlink($path) - { - return unlink(AJXP_MetaStreamWrapper::translateScheme($path)); - } - - /** - * Enter description here... - * - * @param string $path - * @param int $flags - * @return array - */ - public function url_stat($path, $flags) - { - $stat = @stat(AJXP_MetaStreamWrapper::translateScheme($path)); - if($stat === false){ - return null; - } - return $stat; - } -} diff --git a/core/src/core/classes/class.AJXP_ShutdownScheduler.php b/core/src/core/classes/class.AJXP_ShutdownScheduler.php deleted file mode 100644 index 8d9c575b45..0000000000 --- a/core/src/core/classes/class.AJXP_ShutdownScheduler.php +++ /dev/null @@ -1,113 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die('Access not allowed'); -/** - * - * Registry for callbacks that must be triggered after the script is finished. - * - * @package Pydio - * @subpackage Core - * - */ -class AJXP_ShutdownScheduler -{ - private static $instance; - - private $callbacks; // array to store user callbacks - - /** - * @static - * @return AJXP_ShutdownScheduler - */ - public static function getInstance() - { - if(self::$instance == null) self::$instance = new AJXP_ShutdownScheduler(); - return self::$instance; - } - - public function __construct() - { - $this->callbacks = array(); - register_shutdown_function(array($this, 'callRegisteredShutdown')); - ob_start(); - } - public function registerShutdownEventArray() - { - $callback = func_get_args(); - - if (empty($callback)) { - throw new Exception('No callback passed to '.__FUNCTION__.' method'); - } - if (!is_callable($callback[0])) { - throw new Exception('Invalid callback ('.$callback[0].') passed to the '.__FUNCTION__.' method'); - } - $flattenArray = array(); - $flattenArray[0] = $callback[0]; - if (is_array($callback[1])) { - foreach($callback[1] as $argument) $flattenArray[] = $argument; - } - $this->callbacks[] = $flattenArray; - return true; - } - public function registerShutdownEvent() - { - $callback = func_get_args(); - - if (empty($callback)) { - throw new Exception('No callback passed to '.__FUNCTION__.' method'); - } - if (!is_callable($callback[0])) { - throw new Exception('Invalid callback ('.$callback[0].') passed to the '.__FUNCTION__.' method'); - } - $this->callbacks[] = $callback; - return true; - } - - public function callRegisteredShutdown() - { - session_write_close(); - if (!headers_sent()) { - $size = ob_get_length(); - header("Connection: close\r\n"); - //header("Content-Encoding: none\r\n"); - header("Content-Length: $size"); - } - ob_end_flush(); - flush(); - $index = 0; - while (count($this->callbacks)) { - $arguments = array_shift($this->callbacks); - $callback = array_shift($arguments); - try { - call_user_func_array($callback, $arguments); - } catch (\Exception $e) { - AJXP_Logger::error(__CLASS__, __FUNCTION__, array("context" => "Applying hook " . get_class($callback[0]) . "::" . $callback[1], "message" => $e->getMessage())); - } - $index++; - if($index > 200) { - AJXP_Logger::error(__CLASS__, __FUNCTION__, "Breaking ShutdownScheduler loop, seems too big (200)"); - break; - } - } - } - -} diff --git a/core/src/core/classes/class.AJXP_UserAlertException.php b/core/src/core/classes/class.AJXP_UserAlertException.php deleted file mode 100644 index e3d869d1a7..0000000000 --- a/core/src/core/classes/class.AJXP_UserAlertException.php +++ /dev/null @@ -1,26 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die('Access not allowed'); - -class AJXP_UserAlertException extends AJXP_Exception{ - -} \ No newline at end of file diff --git a/core/src/core/classes/class.AJXP_Utils.php b/core/src/core/classes/class.AJXP_Utils.php deleted file mode 100644 index 3f42d2e1bc..0000000000 --- a/core/src/core/classes/class.AJXP_Utils.php +++ /dev/null @@ -1,2132 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die('Access not allowed'); - -define('AJXP_SANITIZE_HTML', 1); -define('AJXP_SANITIZE_HTML_STRICT', 2); -define('AJXP_SANITIZE_ALPHANUM', 3); -define('AJXP_SANITIZE_EMAILCHARS', 4); -define('AJXP_SANITIZE_FILENAME', 5); -define('AJXP_SANITIZE_DIRNAME', 6); - -// THESE ARE DEFINED IN bootstrap_context.php -// REPEAT HERE FOR BACKWARD COMPATIBILITY. -if (!defined('PBKDF2_HASH_ALGORITHM')) { - - define("PBKDF2_HASH_ALGORITHM", "sha256"); - define("PBKDF2_ITERATIONS", 1000); - define("PBKDF2_SALT_BYTE_SIZE", 24); - define("PBKDF2_HASH_BYTE_SIZE", 24); - - define("HASH_SECTIONS", 4); - define("HASH_ALGORITHM_INDEX", 0); - define("HASH_ITERATION_INDEX", 1); - define("HASH_SALT_INDEX", 2); - define("HASH_PBKDF2_INDEX", 3); - - define("USE_OPENSSL_RANDOM", false); - -} - - -/** - * Various functions used everywhere, static library - * @package Pydio - * @subpackage Core - */ -class AJXP_Utils -{ - - /** - * Performs a natural sort on the array keys. - * Behaves the same as ksort() with natural sorting added. - * - * @param Array $array The array to sort - * @return boolean - */ - public static function natksort(&$array) - { - uksort($array, 'strnatcasecmp'); - return true; - } - - /** - * Performs a reverse natural sort on the array keys - * Behaves the same as krsort() with natural sorting added. - * - * @param Array $array The array to sort - * @return boolean - */ - public static function natkrsort(&$array) - { - AJXP_Utils::natksort($array); - $array = array_reverse($array, TRUE); - return true; - } - - /** - * Remove all "../../" tentatives, replace double slashes - * @static - * @param string $path - * @return string - */ - public static function securePath($path) - { - if ($path == null) return ""; - // - // REMOVE ALL "../" TENTATIVES - // - $path = str_replace(chr(0), "", $path); - $dirs = self::safeExplode($path); - for ($i = 0; $i < count($dirs); $i++) { - if ($dirs[$i] == '.' or $dirs[$i] == '..') { - $dirs[$i] = ''; - } - } - // rebuild safe directory string - $path = implode('/', $dirs); - - // - // REPLACE DOUBLE SLASHES - // - while (preg_match('/\/\//', $path)) { - $path = str_replace('//', '/', $path); - } - return $path; - } - - public static function safeExplode($path) { - return (DIRECTORY_SEPARATOR === "\\" ? preg_split('/(\\\|\\/)/', $path) : explode('/', $path)); - } - - public static function safeDirname($path) { - return (DIRECTORY_SEPARATOR === "\\" ? str_replace("\\", "/", dirname($path)): dirname($path)); - } - - public static function safeBasename($path) { - return (DIRECTORY_SEPARATOR === "\\" ? str_replace("\\", "/", basename($path)): basename($path)); - } - - public static function clearHexaCallback($array){ - return chr(hexdec($array[1])); - } - - /** - * Given a string, this function will determine if it potentially an - * XSS attack and return boolean. - * - * @param string $string - * The string to run XSS detection logic on - * @return boolean - * True if the given `$string` contains XSS, false otherwise. - */ - public static function detectXSS($string) { - $contains_xss = FALSE; - - // Skip any null or non string values - if(is_null($string) || !is_string($string)) { - return $contains_xss; - } - - // Keep a copy of the original string before cleaning up - $orig = $string; - - // Set the patterns we'll test against - $patterns = array( - // Match any attribute starting with "on" or xmlns - '#(<[^>]+[\x00-\x20\"\'\/])(on|xmlns)[^>]*>?#iUu', - - // Match javascript:, livescript:, vbscript: and mocha: protocols - '!((java|live|vb)script|mocha|feed|data):(\w)*!iUu', - '#-moz-binding[\x00-\x20]*:#u', - - // Match style attributes - '#(<[^>]+[\x00-\x20\"\'\/])style=[^>]*>?#iUu', - - // Match unneeded tags - '#]*>?#i' - ); - - foreach($patterns as $pattern) { - // Test both the original string and clean string - if(preg_match($pattern, $string) || preg_match($pattern, $orig)){ - $contains_xss = TRUE; - } - if ($contains_xss === TRUE) return TRUE; - } - - return FALSE; - } - - - /** - * Function to clean a string from specific characters - * - * @static - * @param string $s - * @param int $level Can be AJXP_SANITIZE_ALPHANUM, AJXP_SANITIZE_EMAILCHARS, AJXP_SANITIZE_HTML, AJXP_SANITIZE_HTML_STRICT - * @param string $expand - * @return mixed|string - */ - public static function sanitize($s, $level = AJXP_SANITIZE_HTML, $expand = 'script|style|noframes|select|option') - { - if ($level == AJXP_SANITIZE_ALPHANUM) { - return preg_replace("/[^a-zA-Z0-9_\-\.]/", "", $s); - } else if ($level == AJXP_SANITIZE_EMAILCHARS) { - return preg_replace("/[^a-zA-Z0-9_\-\.@!%\+=|~\?]/", "", $s); - } else if ($level == AJXP_SANITIZE_FILENAME || $level == AJXP_SANITIZE_DIRNAME) { - // Convert Hexadecimals - $s = preg_replace_callback('!(&#|\\\)[xX]([0-9a-fA-F]+);?!', array('AJXP_Utils', 'clearHexaCallback'), $s); - // Clean up entities - $s = preg_replace('!(�+[0-9]+)!','$1;',$s); - // Decode entities - $s = html_entity_decode($s, ENT_NOQUOTES, 'UTF-8'); - // Strip whitespace characters - $s = ltrim($s); - $s = str_replace(chr(0), "", $s); - if($level == AJXP_SANITIZE_FILENAME) $s = preg_replace("/[\"\/\|\?\\\]/", "", $s); - else $s = preg_replace("/[\"\|\?\\\]/", "", $s); - if(self::detectXSS($s)){ - if(strpos($s, "/") === 0) $s = "/XSS Detected - Rename Me"; - else $s = "XSS Detected - Rename Me"; - } - return $s; - } - - /**/ //prep the string - $s = ' ' . $s; - - //begin removal - /**/ //remove comment blocks - while (stripos($s, '', $pos[1]); - $len[1] = $pos[2] - $pos[1] + 3; - $x = substr($s, $pos[1], $len[1]); - $s = str_replace($x, '', $s); - } - - /**/ //remove tags with content between them - if (strlen($expand) > 0) { - $e = explode('|', $expand); - for ($i = 0; $i < count($e); $i++) { - while (stripos($s, '<' . $e[$i]) > 0) { - $len[1] = strlen('<' . $e[$i]); - $pos[1] = stripos($s, '<' . $e[$i]); - $pos[2] = stripos($s, $e[$i] . '>', $pos[1] + $len[1]); - $len[2] = $pos[2] - $pos[1] + $len[1]; - $x = substr($s, $pos[1], $len[2]); - $s = str_replace($x, '', $s); - } - } - } - - $s = strip_tags($s); - if ($level == AJXP_SANITIZE_HTML_STRICT) { - $s = preg_replace("/[\",;\/`<>:\*\|\?!\^\\\]/", "", $s); - } else { - $s = str_replace(array("<", ">"), array("<", ">"), $s); - } - return ltrim($s); - } - - /** - * Perform standard urldecode, sanitization and securepath - * @static - * @param $data - * @param int $sanitizeLevel - * @return string - */ - public static function decodeSecureMagic($data, $sanitizeLevel = AJXP_SANITIZE_HTML) - { - return SystemTextEncoding::fromUTF8(AJXP_Utils::sanitize(AJXP_Utils::securePath($data), $sanitizeLevel)); - } - /** - * Try to load the tmp dir from the CoreConf AJXP_TMP_DIR, or the constant AJXP_TMP_DIR, - * or the sys_get_temp_dir - * @static - * @return mixed|null|string - */ - public static function getAjxpTmpDir() - { - $conf = ConfService::getCoreConf("AJXP_TMP_DIR"); - if (!empty($conf)) { - return $conf; - } - if (defined("AJXP_TMP_DIR") && AJXP_TMP_DIR != "") { - return AJXP_TMP_DIR; - } - return realpath(sys_get_temp_dir()); - } - - public static function detectApplicationFirstRun() - { - return !file_exists(AJXP_CACHE_DIR."/first_run_passed"); - } - - public static function setApplicationFirstRunPassed() - { - @file_put_contents(AJXP_CACHE_DIR."/first_run_passed", "true"); - } - - public static function forwardSlashDirname($path) - { - return (DIRECTORY_SEPARATOR === "\\" ? str_replace("\\", "/", dirname($path)): dirname($path)); - } - - public static function forwardSlashBasename($path) - { - return (DIRECTORY_SEPARATOR === "\\" ? str_replace("\\", "/", basename($path)): basename($path)); - } - - - /** - * Parse a Comma-Separated-Line value - * @static - * @param $string - * @param bool $hash - * @return array - */ - public static function parseCSL($string, $hash = false) - { - $exp = array_map("trim", explode(",", $string)); - if (!$hash) return $exp; - $assoc = array(); - foreach ($exp as $explVal) { - $reExp = explode("|", $explVal); - if (count($reExp) == 1) $assoc[$reExp[0]] = $reExp[0]; - else $assoc[$reExp[0]] = $reExp[1]; - } - return $assoc; - } - - /** - * Parse the $fileVars[] PHP errors - * @static - * @param $boxData - * @param bool $throwException - * @return array|null - * @throws Exception - */ - public static function parseFileDataErrors($boxData, $throwException=false) - { - $mess = ConfService::getMessages(); - $userfile_error = $boxData["error"]; - $userfile_tmp_name = $boxData["tmp_name"]; - $userfile_size = $boxData["size"]; - if ($userfile_error != UPLOAD_ERR_OK) { - $errorsArray = array(); - $errorsArray[UPLOAD_ERR_FORM_SIZE] = $errorsArray[UPLOAD_ERR_INI_SIZE] = array(409, str_replace("%i", ini_get("upload_max_filesize"), $mess["537"])); - $errorsArray[UPLOAD_ERR_NO_FILE] = array(410, $mess[538]); - $errorsArray[UPLOAD_ERR_PARTIAL] = array(410, $mess[539]); - $errorsArray[UPLOAD_ERR_NO_TMP_DIR] = array(410, $mess[540]); - $errorsArray[UPLOAD_ERR_CANT_WRITE] = array(411, $mess[541]); - $errorsArray[UPLOAD_ERR_EXTENSION] = array(410, $mess[542]); - if ($userfile_error == UPLOAD_ERR_NO_FILE) { - // OPERA HACK, do not display "no file found error" - if (strpos($_SERVER['HTTP_USER_AGENT'], 'Opera') === false) { - $data = $errorsArray[$userfile_error]; - if($throwException) throw new Exception($data[1], $data[0]); - return $data; - } - } else { - $data = $errorsArray[$userfile_error]; - if($throwException) throw new Exception($data[1], $data[0]); - return $data; - } - } - if ($userfile_tmp_name == "none" || $userfile_size == 0) { - if($throwException) throw new Exception($mess[31], 410); - return array(410, $mess[31]); - } - return null; - } - - /** - * Utilitary to pass some parameters directly at startup : - * + repository_id / folder - * + compile & skipDebug - * + update_i18n, extract, create - * + external_selector_type - * + skipIOS - * + gui - * @static - * @param $parameters - * @param $output - * @param $session - * @return void - */ - public static function parseApplicationGetParameters($parameters, &$output, &$session) - { - $output["EXT_REP"] = "/"; - - if (isSet($parameters["repository_id"]) && isSet($parameters["folder"]) || isSet($parameters["goto"])) { - if (isSet($parameters["goto"])) { - $explode = explode("/", ltrim($parameters["goto"], "/")); - $repoId = array_shift($explode); - $parameters["folder"] = str_replace($repoId, "", ltrim($parameters["goto"], "/")); - } else { - $repoId = $parameters["repository_id"]; - } - $repository = ConfService::getRepositoryById($repoId); - if ($repository == null) { - $repository = ConfService::getRepositoryByAlias($repoId); - if ($repository != null) { - $parameters["repository_id"] = $repository->getId(); - } - } else { - $parameters["repository_id"] = $repository->getId(); - } - require_once(AJXP_BIN_FOLDER . "/class.SystemTextEncoding.php"); - if (AuthService::usersEnabled()) { - $loggedUser = AuthService::getLoggedUser(); - if ($loggedUser != null && $loggedUser->canSwitchTo($parameters["repository_id"])) { - $output["FORCE_REGISTRY_RELOAD"] = true; - $output["EXT_REP"] = SystemTextEncoding::toUTF8(urldecode($parameters["folder"])); - $loggedUser->setArrayPref("history", "last_repository", $parameters["repository_id"]); - $loggedUser->setPref("pending_folder", SystemTextEncoding::toUTF8(AJXP_Utils::decodeSecureMagic($parameters["folder"]))); - AuthService::updateUser($loggedUser); - } else { - $session["PENDING_REPOSITORY_ID"] = $parameters["repository_id"]; - $session["PENDING_FOLDER"] = SystemTextEncoding::toUTF8(AJXP_Utils::decodeSecureMagic($parameters["folder"])); - } - } else { - ConfService::switchRootDir($parameters["repository_id"]); - $output["EXT_REP"] = SystemTextEncoding::toUTF8(urldecode($parameters["folder"])); - } - } - - - if (isSet($parameters["skipDebug"])) { - ConfService::setConf("JS_DEBUG", false); - } - if (ConfService::getConf("JS_DEBUG") && isSet($parameters["compile"])) { - require_once(AJXP_BIN_FOLDER . "/class.AJXP_JSPacker.php"); - AJXP_JSPacker::pack(); - } - if (ConfService::getConf("JS_DEBUG") && isSet($parameters["update_i18n"])) { - if (isSet($parameters["extract"])) { - self::extractConfStringsFromManifests(); - } - self::updateAllI18nLibraries((isSet($parameters["create"]) ? $parameters["create"] : "")); - } - if (ConfService::getConf("JS_DEBUG") && isSet($parameters["clear_plugins_cache"])) { - @unlink(AJXP_PLUGINS_CACHE_FILE); - @unlink(AJXP_PLUGINS_REQUIRES_FILE); - } - if (AJXP_SERVER_DEBUG && isSet($parameters["extract_application_hooks"])) { - self::extractHooksToDoc(); - } - - if (isSet($parameters["external_selector_type"])) { - $output["SELECTOR_DATA"] = array("type" => $parameters["external_selector_type"], "data" => $parameters); - } - - if (isSet($parameters["skipIOS"])) { - setcookie("SKIP_IOS", "true"); - } - if (isSet($parameters["skipANDROID"])) { - setcookie("SKIP_ANDROID", "true"); - } - if (isSet($parameters["gui"])) { - setcookie("AJXP_GUI", $parameters["gui"]); - if ($parameters["gui"] == "light") $session["USE_EXISTING_TOKEN_IF_EXISTS"] = true; - } else { - if (isSet($session["USE_EXISTING_TOKEN_IF_EXISTS"])) { - unset($session["USE_EXISTING_TOKEN_IF_EXISTS"]); - } - setcookie("AJXP_GUI", null); - } - if (isSet($session["OVERRIDE_GUI_START_PARAMETERS"])) { - $output = array_merge($output, $session["OVERRIDE_GUI_START_PARAMETERS"]); - } - } - - /** - * Remove windows carriage return - * @static - * @param $fileContent - * @return mixed - */ - public static function removeWinReturn($fileContent) - { - $fileContent = str_replace(chr(10), "", $fileContent); - $fileContent = str_replace(chr(13), "", $fileContent); - return $fileContent; - } - - /** - * Get the filename extensions using ConfService::getRegisteredExtensions() - * @static - * @param string $fileName - * @param string $mode "image" or "text" - * @param bool $isDir - * @return string Returns the icon name ("image") or the mime label ("text") - */ - public static function mimetype($fileName, $mode, $isDir) - { - $mess = ConfService::getMessages(); - $fileName = strtolower($fileName); - $EXTENSIONS = ConfService::getRegisteredExtensions(); - if ($isDir) { - $mime = $EXTENSIONS["ajxp_folder"]; - } else { - foreach ($EXTENSIONS as $ext) { - if (preg_match("/\.$ext[0]$/", $fileName)) { - $mime = $ext; - break; - } - } - } - if (!isSet($mime)) { - $mime = $EXTENSIONS["ajxp_empty"]; - } - if (is_numeric($mime[3]) || array_key_exists($mime[3], $mess)) { - $mime[3] = $mess[$mime[3]]; - } - return (($mode == "image" ? $mime[1] : $mime[3])); - } - - public static $registeredExtensions; - public static function mimeData($fileName, $isDir) - { - $fileName = strtolower($fileName); - if (self::$registeredExtensions == null) { - self::$registeredExtensions = ConfService::getRegisteredExtensions(); - } - if ($isDir) { - $mime = self::$registeredExtensions["ajxp_folder"]; - } else { - $pos = strrpos($fileName, "."); - if ($pos !== false) { - $fileExt = substr($fileName, $pos + 1); - if (!empty($fileExt) && array_key_exists($fileExt, self::$registeredExtensions) && $fileExt != "ajxp_folder" && $fileExt != "ajxp_empty") { - $mime = self::$registeredExtensions[$fileExt]; - } - } - } - if (!isSet($mime)) { - $mime = self::$registeredExtensions["ajxp_empty"]; - } - return array($mime[3], $mime[1], $mime[2]); - - } - - - /** - * Gather a list of mime that must be treated specially. Used for dynamic replacement in XML mainly. - * @static - * @param string $keyword "editable", "image", "audio", "zip" - * @return string - */ - public static function getAjxpMimes($keyword) - { - if ($keyword == "editable") { - // Gather editors! - $pServ = AJXP_PluginsService::getInstance(); - $plugs = $pServ->getPluginsByType("editor"); - //$plugin = new AJXP_Plugin(); - $mimes = array(); - foreach ($plugs as $plugin) { - $node = $plugin->getManifestRawContent("/editor/@mimes", "node"); - $openable = $plugin->getManifestRawContent("/editor/@openable", "node"); - if ($openable->item(0) && $openable->item(0)->value == "true" && $node->item(0)) { - $mimestring = $node->item(0)->value; - $mimesplit = explode(",", $mimestring); - foreach ($mimesplit as $value) { - $mimes[$value] = $value; - } - } - } - return implode(",", array_values($mimes)); - } else if ($keyword == "image") { - return "png,bmp,jpg,jpeg,gif"; - } else if ($keyword == "audio") { - return "mp3"; - } else if ($keyword == "zip") { - if (ConfService::zipBrowsingEnabled()) { - return "zip,ajxp_browsable_archive"; - } else { - return "none_allowed"; - } - } - return ""; - } - /** - * Whether a file is to be considered as an image or not - * @static - * @param $fileName - * @return bool - */ - public static function is_image($fileName) - { - if (preg_match("/\.png$|\.bmp$|\.jpg$|\.jpeg$|\.gif$/i", $fileName)) { - return 1; - } - return 0; - } - /** - * Whether a file is to be considered as an mp3... Should be DEPRECATED - * @static - * @param string $fileName - * @return bool - * @deprecated - */ - public static function is_mp3($fileName) - { - if (preg_match("/\.mp3$/i", $fileName)) return 1; - return 0; - } - /** - * Static image mime type headers - * @static - * @param $fileName - * @return string - */ - public static function getImageMimeType($fileName) - { - if (preg_match("/\.jpg$|\.jpeg$/i", $fileName)) { - return "image/jpeg"; - } else if (preg_match("/\.png$/i", $fileName)) { - return "image/png"; - } else if (preg_match("/\.bmp$/i", $fileName)) { - return "image/bmp"; - } else if (preg_match("/\.gif$/i", $fileName)) { - return "image/gif"; - } - return ""; - } - /** - * Headers to send when streaming - * @static - * @param $fileName - * @return bool|string - */ - public static function getStreamingMimeType($fileName) - { - if (preg_match("/\.mp3$/i", $fileName)) { - return "audio/mp3"; - } else if (preg_match("/\.wav$/i", $fileName)) { - return "audio/wav"; - } else if (preg_match("/\.aac$/i", $fileName)) { - return "audio/aac"; - } else if (preg_match("/\.m4a$/i", $fileName)) { - return "audio/m4a"; - } else if (preg_match("/\.aiff$/i", $fileName)) { - return "audio/aiff"; - } else if (preg_match("/\.mp4$/i", $fileName)) { - return "video/mp4"; - } else if (preg_match("/\.mov$/i", $fileName)) { - return "video/quicktime"; - } else if (preg_match("/\.m4v$/i", $fileName)) { - return "video/x-m4v"; - } else if (preg_match("/\.3gp$/i", $fileName)) { - return "video/3gpp"; - } else if (preg_match("/\.3g2$/i", $fileName)) { - return "video/3gpp2"; - } else return false; - } - - public static $sizeUnit; - /** - * Display a human readable string for a bytesize (1MB, 2,3Go, etc) - * @static - * @param $filesize - * @param bool $phpConfig - * @return string - */ - public static function roundSize($filesize, $phpConfig = false) - { - if (self::$sizeUnit == null) { - $mess = ConfService::getMessages(); - self::$sizeUnit = $mess["byte_unit_symbol"]; - } - if ($filesize < 0) { - $filesize = sprintf("%u", $filesize); - } - if ($filesize >= 1073741824) { - $filesize = round($filesize / 1073741824 * 100) / 100 . ($phpConfig ? "G" : " G" . self::$sizeUnit); - } elseif ($filesize >= 1048576) { - $filesize = round($filesize / 1048576 * 100) / 100 . ($phpConfig ? "M" : " M" . self::$sizeUnit); - } elseif ($filesize >= 1024) { - $filesize = round($filesize / 1024 * 100) / 100 . ($phpConfig ? "K" : " K" . self::$sizeUnit); - } else { - $filesize = $filesize . " " . self::$sizeUnit; - } - if ($filesize == 0) { - $filesize = "-"; - } - return $filesize; - } - - /** - * Hidden files start with dot - * @static - * @param string $fileName - * @return bool - */ - public static function isHidden($fileName) - { - return (substr($fileName, 0, 1) == "."); - } - - /** - * Whether a file is a browsable archive - * @static - * @param string $fileName - * @return int - */ - public static function isBrowsableArchive($fileName) - { - return preg_match("/\.zip$/i", $fileName); - } - - /** - * Convert a shorthand byte value from a PHP configuration directive to an integer value - * @param string $value - * @return int - */ - public static function convertBytes($value) - { - if (is_numeric($value)) { - return intval($value); - } else { - $value_length = strlen($value); - $value = str_replace(",",".", $value); - $qty = floatval(substr($value, 0, $value_length - 1)); - $unit = strtolower(substr($value, $value_length - 1)); - switch ($unit) { - case 'k': - $qty *= 1024; - break; - case 'm': - $qty *= 1048576; - break; - case 'g': - $qty *= 1073741824; - break; - } - return $qty; - } - } - - - //Relative Date Function - - public static function relativeDate($time, $messages, $shortestForm = false) - { - $crtYear = date('Y'); - $today = strtotime(date('M j, Y')); - $reldays = ($time - $today)/86400; - $relTime = date($messages['date_relative_time_format'], $time); - - if ($reldays >= 0 && $reldays < 1) { - return str_replace("TIME", $relTime, $messages['date_relative_today']); - } else if ($reldays >= 1 && $reldays < 2) { - return str_replace("TIME", $relTime, $messages['date_relative_tomorrow']); - } else if ($reldays >= -1 && $reldays < 0) { - return str_replace("TIME", $relTime, $messages['date_relative_yesterday']); - } - - if (abs($reldays) < 7) { - - if ($reldays > 0) { - $reldays = floor($reldays); - return str_replace("%s", $reldays, $messages['date_relative_days_ahead']); - //return 'In ' . $reldays . ' day' . ($reldays != 1 ? 's' : ''); - } else { - $reldays = abs(floor($reldays)); - return str_replace("%s", $reldays, $messages['date_relative_days_ago']); - //return $reldays . ' day' . ($reldays != 1 ? 's' : '') . ' ago'; - } - - } - $finalDate = date($messages["date_relative_date_format"], $time ? $time : time()); - if(strpos($messages["date_relative_date_format"], "F") !== false && isSet($messages["date_intl_locale"]) && extension_loaded("intl")){ - $intl = IntlDateFormatter::create($messages["date_intl_locale"], IntlDateFormatter::FULL, IntlDateFormatter::FULL, null, null, "MMMM"); - $localizedMonth = $intl->format($time ? $time : time()); - $dateFuncMonth = date("F", $time ? $time : time()); - $finalDate = str_replace($dateFuncMonth, $localizedMonth, $finalDate); - } - if(!$shortestForm || strpos($finalDate, $crtYear) !== false){ - $finalDate = str_replace($crtYear, '', $finalDate); - return str_replace("DATE", $finalDate, $messages["date_relative_date"]); - }else{ - return $finalDate = date("M Y", $time ? $time : time()); - } - - } - - - /** - * Replace specific chars by their XML Entities, for use inside attributes value - * @static - * @param $string - * @param bool $toUtf8 - * @return mixed|string - */ - public static function xmlEntities($string, $toUtf8 = false) - { - $xmlSafe = str_replace(array("&", "<", ">", "\"", "\n", "\r"), array("&", "<", ">", """, " ", " "), $string); - if ($toUtf8 && SystemTextEncoding::getEncoding() != "UTF-8") { - return SystemTextEncoding::toUTF8($xmlSafe); - } else { - return $xmlSafe; - } - } - - - /** - * Replace specific chars by their XML Entities, for use inside attributes value - * @static - * @param $string - * @param bool $toUtf8 - * @return mixed|string - */ - public static function xmlContentEntities($string, $toUtf8 = false) - { - $xmlSafe = str_replace(array("&", "<", ">", "\""), array("&", "<", ">", """), $string); - if ($toUtf8) { - return SystemTextEncoding::toUTF8($xmlSafe); - } else { - return $xmlSafe; - } - } - - /** - * Search include path for a given file - * @static - * @param string $file - * @return bool - */ - public static function searchIncludePath($file) - { - $ps = explode(PATH_SEPARATOR, ini_get('include_path')); - foreach ($ps as $path) { - if (@file_exists($path . DIRECTORY_SEPARATOR . $file)) return true; - } - if (@file_exists($file)) return true; - return false; - } - - /** - * @static - * @param $from - * @param $to - * @return string - */ - public static function getTravelPath($from, $to) - { - $from = explode('/', $from); - $to = explode('/', $to); - $relPath = $to; - - foreach ($from as $depth => $dir) { - // find first non-matching dir - if ($dir === $to[$depth]) { - // ignore this directory - array_shift($relPath); - } else { - // get number of remaining dirs to $from - $remaining = count($from) - $depth; - if ($remaining > 1) { - // add traversals up to first matching dir - $padLength = (count($relPath) + $remaining - 1) * -1; - $relPath = array_pad($relPath, $padLength, '..'); - break; - } else { - $relPath[0] = './' . $relPath[0]; - } - } - } - return implode('/', $relPath); - } - - - /** - * Build the current server URL - * @param bool $withURI - * @static - * @return string - */ - public static function detectServerURL($withURI = false) - { - $setUrl = ConfService::getCoreConf("SERVER_URL"); - if (!empty($setUrl)) { - return $setUrl; - } - if (php_sapi_name() == "cli") { - AJXP_Logger::debug("WARNING, THE SERVER_URL IS NOT SET, WE CANNOT BUILD THE MAIL ADRESS WHEN WORKING IN CLI"); - } - $protocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http'); - $port = (($protocol === 'http' && $_SERVER['SERVER_PORT'] == 80 || $protocol === 'https' && $_SERVER['SERVER_PORT'] == 443) - ? "" : ":" . $_SERVER['SERVER_PORT']); - $name = $_SERVER["SERVER_NAME"]; - if (!$withURI) { - return "$protocol://$name$port"; - } else { - $uri = dirname($_SERVER["REQUEST_URI"]); - $api = ConfService::currentContextIsRestAPI(); - if(!empty($api)){ - // Keep only before api base - $explode = explode("/".$api."/", $uri); - $uri = array_shift($explode); - } - return "$protocol://$name$port".$uri; - } - } - - /** - * @param Repository $repository - * @return string - */ - public static function getWorkspaceShortcutURL($repository){ - if(empty($repository)){ - return ""; - } - $repoSlug = $repository->getSlug(); - $skipHistory = ConfService::getCoreConf("SKIP_USER_HISTORY", "conf"); - if($skipHistory){ - $prefix = "/ws-"; - }else{ - $prefix = "?goto="; - } - return trim(self::detectServerURL(true), "/").$prefix.$repoSlug; - } - - /** - * Modifies a string to remove all non ASCII characters and spaces. - * @param string $text - * @return string - */ - public static function slugify($text) - { - if (empty($text)) return ""; - // replace non letter or digits by - - $text = preg_replace('~[^\\pL\d]+~u', '-', $text); - - // trim - $text = trim($text, '-'); - - // transliterate - if (function_exists('iconv')) { - $text = iconv('utf-8', 'us-ascii//TRANSLIT', $text); - } - - // lowercase - $text = strtolower($text); - - // remove unwanted characters - $text = preg_replace('~[^-\w]+~', '', $text); - - if (empty($text)) { - return 'n-a'; - } - - return $text; - } - - public static function getHooksFile() - { - return AJXP_INSTALL_PATH."/".AJXP_DOCS_FOLDER."/hooks.json"; - } - - public static function extractHooksToDoc() - { - $docFile = self::getHooksFile(); - if (is_file($docFile)) { - copy($docFile, $docFile.".bak"); - $existingHooks = json_decode(file_get_contents($docFile), true); - } else { - $existingHooks = array(); - } - $allPhpFiles1 = self::glob_recursive(AJXP_INSTALL_PATH."/core/classes/*.php"); - $allPhpFiles2= self::glob_recursive(AJXP_INSTALL_PATH."/plugins/*.php"); - $allPhpFiles3= self::glob_recursive(AJXP_INSTALL_PATH."/conf/*.php"); - $allPhpFiles = array_merge(array_merge($allPhpFiles1, $allPhpFiles2), $allPhpFiles3); - $hooks = array(); - foreach ($allPhpFiles as $phpFile) { - $fileContent = file($phpFile); - foreach ($fileContent as $lineNumber => $line) { - if (preg_match_all('/AJXP_Controller::applyHook\("([^"]+)", (.*)\)/', $line, $matches)) { - $names = $matches[1]; - $params = $matches[2]; - foreach ($names as $index => $hookName) { - if(!isSet($hooks[$hookName])) $hooks[$hookName] = array("TRIGGERS" => array(), "LISTENERS" => array()); - $filename = substr($phpFile, strlen(AJXP_INSTALL_PATH)); - if(strpos($filename, "/plugins") === 0) { - $source = explode("/", $filename)[2]; - } else { - $source = str_replace(array("class.", ".php"), "", array_pop(explode("/", $filename))); - } - if(!isSet($hooks[$hookName]["TRIGGERS"][$source])){ - $hooks[$hookName]["TRIGGERS"][$source] = array(); - } - $hooks[$hookName]["TRIGGERS"][$source][] = array( - "FILE" => $filename, - "LINE" => $lineNumber - ); - $hooks[$hookName]["PARAMETER_SAMPLE"] = $params[$index]; - } - } - - } - } - $registryHooks = AJXP_PluginsService::getInstance()->searchAllManifests("//hooks/serverCallback", "xml", false, false, true); - $regHooks = array(); - foreach ($registryHooks as $xmlHook) { - $name = $xmlHook->getAttribute("hookName"); - $method = $xmlHook->getAttribute("methodName"); - $pluginId = $xmlHook->getAttribute("pluginId"); - $deferred = $xmlHook->getAttribute("defer") === "true"; - if($pluginId == "") $pluginId = $xmlHook->parentNode->parentNode->parentNode->getAttribute("id"); - if(!isSet($regHooks[$name])) $regHooks[$name] = array(); - $data = array("PLUGIN_ID" => $pluginId, "METHOD" => $method); - if($deferred) $data["DEFERRED"] = true; - $regHooks[$name][] = $data; - } - - foreach ($hooks as $h => $data) { - - if (isSet($regHooks[$h])) { - $data["LISTENERS"] = $regHooks[$h]; - } - if (isSet($existingHooks[$h])) { - $existingHooks[$h]["TRIGGERS"] = $data["TRIGGERS"]; - $existingHooks[$h]["LISTENERS"] = $data["LISTENERS"]; - $existingHooks[$h]["PARAMETER_SAMPLE"] = $data["PARAMETER_SAMPLE"]; - } else { - $existingHooks[$h] = $data; - } - } - file_put_contents($docFile, self::prettyPrintJSON(json_encode($existingHooks))); - - } - - /** - * Indents a flat JSON string to make it more human-readable. - * - * @param string $json The original JSON string to process. - * - * @return string Indented version of the original JSON string. - */ - public static function prettyPrintJSON($json) - { - $result = ''; - $pos = 0; - $strLen = strlen($json); - $indentStr = ' '; - $newLine = "\n"; - $prevChar = ''; - $outOfQuotes = true; - - for ($i=0; $i<=$strLen; $i++) { - - // Grab the next character in the string. - $char = substr($json, $i, 1); - - // Are we inside a quoted string? - if ($char == '"' && $prevChar != '\\') { - $outOfQuotes = !$outOfQuotes; - - // If this character is the end of an element, - // output a new line and indent the next line. - } else if (($char == '}' || $char == ']') && $outOfQuotes) { - $result .= $newLine; - $pos --; - for ($j=0; $j<$pos; $j++) { - $result .= $indentStr; - } - } - - // Add the character to the result string. - $result .= $char; - - // If the last character was the beginning of an element, - // output a new line and indent the next line. - if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) { - $result .= $newLine; - if ($char == '{' || $char == '[') { - $pos ++; - } - - for ($j = 0; $j < $pos; $j++) { - $result .= $indentStr; - } - } - - $prevChar = $char; - } - - return $result; - } - - /** - * i18n utilitary for extracting the CONF_MESSAGE[] strings out of the XML files - * @static - * @return void - */ - public static function extractConfStringsFromManifests() - { - $plugins = AJXP_PluginsService::getInstance()->getDetectedPlugins(); - /** - * @var AJXP_Plugin $plug - */ - foreach ($plugins as $pType => $plugs) { - foreach ($plugs as $plug) { - $lib = $plug->getManifestRawContent("//i18n", "nodes"); - if (!$lib->length) continue; - $library = $lib->item(0); - $namespace = $library->getAttribute("namespace"); - $path = $library->getAttribute("path"); - $xml = $plug->getManifestRawContent(); - // for core, also load mixins - $refFile = AJXP_INSTALL_PATH . "/" . $path . "/conf/en.php"; - $reference = array(); - if (preg_match_all("/CONF_MESSAGE(\[.*?\])/", $xml, $matches, PREG_SET_ORDER)) { - foreach ($matches as $match) { - $match[1] = str_replace(array("[", "]"), "", $match[1]); - $reference[$match[1]] = $match[1]; - } - } - if ($namespace == "") { - $mixXml = file_get_contents(AJXP_INSTALL_PATH . "/" . AJXP_PLUGINS_FOLDER . "/core.ajaxplorer/ajxp_mixins.xml"); - if (preg_match_all("/MIXIN_MESSAGE(\[.*?\])/", $mixXml, $matches, PREG_SET_ORDER)) { - foreach ($matches as $match) { - $match[1] = str_replace(array("[", "]"), "", $match[1]); - $reference[$match[1]] = $match[1]; - } - } - } - if (count($reference)) { - self::updateI18nFromRef($refFile, $reference); - } - } - } - } - - /** - * Browse the i18n libraries and update the languages with the strings missing - * @static - * @param string $createLanguage - * @return void - */ - public static function updateAllI18nLibraries($createLanguage = "") - { - // UPDATE EN => OTHER LANGUAGES - $nodes = AJXP_PluginsService::getInstance()->searchAllManifests("//i18n", "nodes"); - foreach ($nodes as $node) { - $nameSpace = $node->getAttribute("namespace"); - $path = AJXP_INSTALL_PATH . "/" . $node->getAttribute("path"); - if ($nameSpace == "") { - self::updateI18nFiles($path, false, $createLanguage); - self::updateI18nFiles($path . "/conf", true, $createLanguage); - } else { - self::updateI18nFiles($path . "/conf", true, $createLanguage); - if($nameSpace == "user_home") continue; - self::updateI18nFiles($path, true, $createLanguage); - } - } - } - - /** - * Patch the languages files of an i18n library with the references strings from the "en" file. - * @static - * @param $baseDir - * @param bool $detectLanguages - * @param string $createLanguage - */ - public static function updateI18nFiles($baseDir, $detectLanguages = true, $createLanguage = "") - { - if (!is_dir($baseDir) || !is_file($baseDir . "/en.php")) return; - if ($createLanguage != "" && !is_file($baseDir . "/$createLanguage.php")) { - @copy(AJXP_INSTALL_PATH . "/plugins/core.ajaxplorer/i18n-template.php", $baseDir . "/$createLanguage.php"); - } - if (!$detectLanguages) { - $languages = ConfService::listAvailableLanguages(); - $filenames = array(); - foreach ($languages as $key => $value) { - $filenames[] = $baseDir . "/" . $key . ".php"; - } - } else { - $filenames = glob($baseDir . "/*.php"); - } - - $mess = array(); - include($baseDir . "/en.php"); - $reference = $mess; - - foreach ($filenames as $filename) { - self::updateI18nFromRef($filename, $reference); - } - } - - /** - * i18n Utilitary - * @static - * @param $filename - * @param $reference - */ - public static function updateI18nFromRef($filename, $reference) - { - if (!is_file($filename)) return; - $mess = array(); - include($filename); - $missing = array(); - foreach ($reference as $messKey => $message) { - if (!array_key_exists($messKey, $mess)) { - $missing[] = "\"$messKey\" => \"$message\","; - } - } - //print_r($missing); - if (count($missing)) { - $header = array(); - $currentMessages = array(); - $footer = array(); - $fileLines = file($filename); - $insideArray = false; - foreach ($fileLines as $line) { - if (strstr($line, "\"") !== false) { - $currentMessages[] = trim($line); - $insideArray = true; - } else { - if (!$insideArray && strstr($line, ");") !== false) $insideArray = true; - if (!$insideArray) { - $header[] = trim($line); - } else { - $footer[] = trim($line); - } - } - } - $currentMessages = array_merge($header, $currentMessages, $missing, $footer); - file_put_contents($filename, join("\n", $currentMessages)); - } - } - - /** - * Generate an HTML table for the tests results. We should use a template somewhere... - * @static - * @param $outputArray - * @param $testedParams - * @param bool $showSkipLink - * @return void - */ - public static function testResultsToTable($outputArray, $testedParams, $showSkipLink = true) - { - $dumpRows = ""; - $passedRows = array(); - $warnRows = ""; - $errRows = ""; - $errs = $warns = 0; - $ALL_ROWS = array( - "error" => array(), - "warning" => array(), - "dump" => array(), - "passed" => array(), - ); - $TITLES = array( - "error" => "Failed Tests", - "warning" => "Warnings", - "dump" => "Server Information", - "passed" => "Other tests passed", - ); - foreach ($outputArray as $item) { - - // A test is output only if it hasn't succeeded (doText returned FALSE) - $result = $item["result"] ? "passed" : ($item["level"] == "info" ? "dump" : ($item["level"] == "warning" - ? "warning" : "error")); - $success = $result == "passed"; - if($result == "dump") $result = "passed"; - $ALL_ROWS[$result][$item["name"]] = $item["info"]; - } - - include(AJXP_INSTALL_PATH."/core/tests/startup.phtml"); - } - - /** - * @static - * @param $outputArray - * @param $testedParams - * @return bool - */ - public static function runTests(&$outputArray, &$testedParams) - { - // At first, list folder in the tests subfolder - chdir(AJXP_TESTS_FOLDER); - $files = glob('*.php'); - - $outputArray = array(); - $testedParams = array(); - $passed = true; - foreach ($files as $file) { - require_once($file); - // Then create the test class - $testName = str_replace(".php", "", substr($file, 5)); - if(!class_exists($testName)) continue; - $class = new $testName(); - - $result = $class->doTest(); - if (!$result && $class->failedLevel != "info") $passed = false; - $outputArray[] = array( - "name" => $class->name, - "result" => $result, - "level" => $class->failedLevel, - "info" => $class->failedInfo); - if (count($class->testedParams)) { - $testedParams = array_merge($testedParams, $class->testedParams); - } - } - // PREPARE REPOSITORY LISTS - $repoList = array(); - $REPOSITORIES = array(); - require_once("../classes/class.ConfService.php"); - require_once("../classes/class.Repository.php"); - include(AJXP_CONF_PATH . "/bootstrap_repositories.php"); - foreach ($REPOSITORIES as $index => $repo) { - $repoList[] = ConfService::createRepositoryFromArray($index, $repo); - } - // Try with the serialized repositories - if (is_file(AJXP_DATA_PATH . "/plugins/conf.serial/repo.ser")) { - $fileLines = file(AJXP_DATA_PATH . "/plugins/conf.serial/repo.ser"); - $repos = unserialize($fileLines[0]); - $repoList = array_merge($repoList, $repos); - } - - // NOW TRY THE PLUGIN TESTS - chdir(AJXP_INSTALL_PATH . "/" . AJXP_PLUGINS_FOLDER); - $files = glob('access.*/test.*.php'); - foreach ($files as $file) { - require_once($file); - // Then create the test class - list($accessFolder, $testFileName) = explode("/", $file); - $testName = str_replace(".php", "", substr($testFileName, 5) . "Test"); - $class = new $testName(); - foreach ($repoList as $repository) { - if($repository->isTemplate || $repository->getParentId() != null) continue; - $result = $class->doRepositoryTest($repository); - if ($result === false || $result === true) { - if (!$result && $class->failedLevel != "info") { - $passed = false; - } - $outputArray[] = array( - "name" => $class->name . "\n Testing repository : " . $repository->getDisplay(), - "result" => $result, - "level" => $class->failedLevel, - "info" => $class->failedInfo); - if (count($class->testedParams)) { - $testedParams = array_merge($testedParams, $class->testedParams); - } - } - } - } - - return $passed; - } - - /** - * @static - * @param $outputArray - * @param $testedParams - * @return void - */ - public static function testResultsToFile($outputArray, $testedParams) - { - ob_start(); - echo '$diagResults = '; - var_export($testedParams); - echo ';'; - echo '$outputArray = '; - var_export($outputArray); - echo ';'; - $content = ''; - ob_end_clean(); - //print_r($content); - file_put_contents(TESTS_RESULT_FILE, $content); - } - - public static function isStream($path) - { - $wrappers = stream_get_wrappers(); - $wrappers_re = '(' . join('|', $wrappers) . ')'; - return preg_match( "!^$wrappers_re://!", $path ) === 1; - } - - /** - * Load an array stored serialized inside a file. - * - * @param String $filePath Full path to the file - * @param Boolean $skipCheck do not test for file existence before opening - * @param string $format - * @return Array - */ - public static function loadSerialFile($filePath, $skipCheck = false, $format="ser") - { - $filePath = AJXP_VarsFilter::filter($filePath); - $result = array(); - if ($skipCheck) { - $fileLines = @file($filePath); - if ($fileLines !== false) { - if($format == "ser") $result = unserialize(implode("", $fileLines)); - else if($format == "json") $result = json_decode(implode("", $fileLines), true); - } - return $result; - } - if (is_file($filePath)) { - $fileLines = file($filePath); - if($format == "ser") $result = unserialize(implode("", $fileLines)); - else if($format == "json") $result = json_decode(implode("", $fileLines), true); - } - return $result; - } - - /** - * Stores an Array as a serialized string inside a file. - * - * @param String $filePath Full path to the file - * @param array|Object $value The value to store - * @param Boolean $createDir Whether to create the parent folder or not, if it does not exist. - * @param bool $silent Silently write the file, are throw an exception on problem. - * @param string $format "ser" or "json" - * @param bool $jsonPrettyPrint If json, use pretty printing - * @throws Exception - */ - public static function saveSerialFile($filePath, $value, $createDir = true, $silent = false, $format="ser", $jsonPrettyPrint = false) - { - if(!in_array($format, array("ser", "json"))){ - throw new Exception("Unsupported serialization format: ".$format); - } - $filePath = AJXP_VarsFilter::filter($filePath); - if ($createDir && !is_dir(dirname($filePath))) { - @mkdir(dirname($filePath), 0755, true); - if (!is_dir(dirname($filePath))) { - // Creation failed - if($silent) return; - else throw new Exception("[AJXP_Utils::saveSerialFile] Cannot write into " . dirname(dirname($filePath))); - } - } - try { - $fp = fopen($filePath, "w"); - if($format == "ser") { - $content = serialize($value); - } else { - $content = json_encode($value); - if($jsonPrettyPrint) $content = self::prettyPrintJSON($content); - } - fwrite($fp, $content); - fclose($fp); - } catch (Exception $e) { - if ($silent) return; - else throw $e; - } - } - - /** - * Detect mobile browsers - * @static - * @return bool - */ - public static function userAgentIsMobile() - { - $op = strtolower($_SERVER['HTTP_X_OPERAMINI_PHONE'] OR ""); - $ua = strtolower($_SERVER['HTTP_USER_AGENT']); - $ac = strtolower($_SERVER['HTTP_ACCEPT']); - $isMobile = strpos($ac, 'application/vnd.wap.xhtml+xml') !== false - || $op != '' - || strpos($ua, 'sony') !== false - || strpos($ua, 'symbian') !== false - || strpos($ua, 'nokia') !== false - || strpos($ua, 'samsung') !== false - || strpos($ua, 'mobile') !== false - || strpos($ua, 'android') !== false - || strpos($ua, 'windows ce') !== false - || strpos($ua, 'epoc') !== false - || strpos($ua, 'opera mini') !== false - || strpos($ua, 'nitro') !== false - || strpos($ua, 'j2me') !== false - || strpos($ua, 'midp-') !== false - || strpos($ua, 'cldc-') !== false - || strpos($ua, 'netfront') !== false - || strpos($ua, 'mot') !== false - || strpos($ua, 'up.browser') !== false - || strpos($ua, 'up.link') !== false - || strpos($ua, 'audiovox') !== false - || strpos($ua, 'blackberry') !== false - || strpos($ua, 'ericsson,') !== false - || strpos($ua, 'panasonic') !== false - || strpos($ua, 'philips') !== false - || strpos($ua, 'sanyo') !== false - || strpos($ua, 'sharp') !== false - || strpos($ua, 'sie-') !== false - || strpos($ua, 'portalmmm') !== false - || strpos($ua, 'blazer') !== false - || strpos($ua, 'avantgo') !== false - || strpos($ua, 'danger') !== false - || strpos($ua, 'palm') !== false - || strpos($ua, 'series60') !== false - || strpos($ua, 'palmsource') !== false - || strpos($ua, 'pocketpc') !== false - || strpos($ua, 'smartphone') !== false - || strpos($ua, 'rover') !== false - || strpos($ua, 'ipaq') !== false - || strpos($ua, 'au-mic,') !== false - || strpos($ua, 'alcatel') !== false - || strpos($ua, 'ericy') !== false - || strpos($ua, 'up.link') !== false - || strpos($ua, 'vodafone/') !== false - || strpos($ua, 'wap1.') !== false - || strpos($ua, 'wap2.') !== false; - return $isMobile; - } - /** - * Detect iOS browser - * @static - * @return bool - */ - public static function userAgentIsIOS() - { - if (stripos($_SERVER["HTTP_USER_AGENT"], "iphone") !== false) return true; - if (stripos($_SERVER["HTTP_USER_AGENT"], "ipad") !== false) return true; - if (stripos($_SERVER["HTTP_USER_AGENT"], "ipod") !== false) return true; - return false; - } - /** - * Detect Windows Phone - * @static - * @return bool - */ - public static function userAgentIsWindowsPhone() - { - if (stripos($_SERVER["HTTP_USER_AGENT"], "IEMobile") !== false) return true; - return false; - } - /** - * Detect Android UA - * @static - * @return bool - */ - public static function userAgentIsAndroid() - { - return (stripos($_SERVER["HTTP_USER_AGENT"], "android") !== false); - } - - public static function userAgentIsNativePydioApp(){ - - return (stripos($_SERVER["HTTP_USER_AGENT"], "ajaxplorer-ios-client") !== false - || stripos($_SERVER["HTTP_USER_AGENT"], "Apache-HttpClient") !== false - || stripos($_SERVER["HTTP_USER_AGENT"], "python-requests") !== false - ); - } - - public static function osFromUserAgent($useragent = null) { - - $osList = array - ( - 'Windows 10' => 'windows nt 10.0', - 'Windows 8.1' => 'windows nt 6.3', - 'Windows 8' => 'windows nt 6.2', - 'Windows 7' => 'windows nt 6.1', - 'Windows Vista' => 'windows nt 6.0', - 'Windows Server 2003' => 'windows nt 5.2', - 'Windows XP' => 'windows nt 5.1', - 'Windows 2000 sp1' => 'windows nt 5.01', - 'Windows 2000' => 'windows nt 5.0', - 'Windows NT 4.0' => 'windows nt 4.0', - 'Windows Me' => 'win 9x 4.9', - 'Windows 98' => 'windows 98', - 'Windows 95' => 'windows 95', - 'Windows CE' => 'windows ce', - 'Windows (version unknown)' => 'windows', - 'OpenBSD' => 'openbsd', - 'SunOS' => 'sunos', - 'Ubuntu' => 'ubuntu', - 'Linux' => '(linux)|(x11)', - 'Mac OSX Beta (Kodiak)' => 'mac os x beta', - 'Mac OSX Cheetah' => 'mac os x 10.0', - 'Mac OSX Jaguar' => 'mac os x 10.2', - 'Mac OSX Panther' => 'mac os x 10.3', - 'Mac OSX Tiger' => 'mac os x 10.4', - 'Mac OSX Leopard' => 'mac os x 10.5', - 'Mac OSX Snow Leopard' => 'mac os x 10.6', - 'Mac OSX Lion' => 'mac os x 10.7', - 'Mac OSX Mountain Lion' => 'mac os x 10.8', - 'Mac OSX Mavericks' => 'mac os x 10.9', - 'Mac OSX Yosemite' => 'mac os x 10.10', - 'Mac OSX El Capitan' => 'mac os x 10.11', - 'Mac OSX Puma' => 'mac os x 10.1', - 'Mac OS (classic)' => '(mac_powerpc)|(macintosh)', - 'QNX' => 'QNX', - 'BeOS' => 'beos', - 'Apple iPad' => 'iPad', - 'Apple iPhone' => 'iPhone', - 'OS2' => 'os\/2', - 'SearchBot'=>'(nuhk)|(googlebot)|(yammybot)|(openbot)|(slurp)|(msnbot)|(ask jeeves\/teoma)|(ia_archiver)', - 'Pydio iOS Native Application' => 'ajaxplorer-ios', - 'Pydio Android Native Application' => 'Apache-HttpClient', - 'Pydio Sync Client' => 'python-requests' - ); - - if($useragent == null){ - $useragent = $_SERVER['HTTP_USER_AGENT']; - $useragent = strtolower($useragent); - } - - $found = "Not automatically detected.$useragent"; - foreach($osList as $os=>$match) { - if (preg_match('/' . $match . '/i', $useragent)) { - $found = $os; - break; - } - } - - return $found; - - - } - - - /** - * Try to remove a file without errors - * @static - * @param $file - * @return void - */ - public static function silentUnlink($file) - { - @unlink($file); - } - - /** - * Try to set an ini config, without errors - * @static - * @param string $paramName - * @param string $paramValue - * @return void - */ - public static function safeIniSet($paramName, $paramValue) - { - $current = ini_get($paramName); - if ($current == $paramValue) return; - @ini_set($paramName, $paramValue); - } - - /** - * Parse URL ignoring # and ? - * @param $path - * @return array - */ - public static function safeParseUrl($path) - { - $parts = parse_url(str_replace(array("#", "?"), array("__AJXP_FRAGMENT__", "__AJXP_MARK__"), $path)); - $parts["path"]= str_replace(array("__AJXP_FRAGMENT__", "__AJXP_MARK__"), array("#", "?"), $parts["path"]); - return $parts; - } - - /** - * Sanitize a URL removin all unwanted / trailing slashes - * - * @param array url_parts - * return string url - */ - public function getSanitizedUrl($arr) { - $credentials = join(':', array_filter([$arr['user'], $arr['pass']])); - $hostname = join(':', array_filter([$arr['host'], $arr['port']])); - $domain = join('@', array_filter([$credentials, $hostname])); - return $arr['scheme'] . '://' . join('/', array_filter([$domain, $arr['path']])); - } - - /** - * @static - * @param string $url - * @return bool|mixed|string - */ - public static function getRemoteContent($url) - { - if (ini_get("allow_url_fopen")) { - return file_get_contents($url); - } else if (function_exists("curl_init")) { - $ch = curl_init(); - $timeout = 30; // set to zero for no timeout - curl_setopt ($ch, CURLOPT_URL, $url); - curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, $timeout); - $return = curl_exec($ch); - curl_close($ch); - return $return; - } else { - $i = parse_url($url); - $httpClient = new HttpClient($i["host"]); - $httpClient->timeout = 30; - return $httpClient->quickGet($url); - } - } - - public static function decypherStandardFormPassword($userId, $password){ - if (function_exists('mcrypt_decrypt')) { - // We have encoded as base64 so if we need to store the result in a database, it can be stored in text column - $password = trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($userId."\1CDAFx¨op#"), base64_decode($password), MCRYPT_MODE_ECB), "\0"); - } - return $password; - } - - public static function filterFormElementsFromMeta($metadata, &$nestedData, $userId=null, $binariesContext=null, $cypheredPassPrefix=""){ - foreach($metadata as $key => $level){ - if(!array_key_exists($key, $nestedData)) continue; - if(!is_array($level)) continue; - if(isSet($level["ajxp_form_element"])){ - // filter now - $type = $level["type"]; - if($type == "binary" && $binariesContext != null){ - $value = $nestedData[$key]; - if ($value == "ajxp-remove-original") { - if (!empty($level["original_binary"])) { - ConfService::getConfStorageImpl()->deleteBinary($binariesContext, $level["original_binary"]); - } - $value = ""; - } else { - $file = AJXP_Utils::getAjxpTmpDir()."/".$value; - if (file_exists($file)) { - $id= !empty($level["original_binary"]) ? $level["original_binary"] : null; - $id=ConfService::getConfStorageImpl()->saveBinary($binariesContext, $file, $id); - $value = $id; - } - } - $nestedData[$key] = $value; - } - }else{ - self::filterFormElementsFromMeta($level, $nestedData[$key], $userId, $binariesContext, $cypheredPassPrefix); - } - } - } - - public static function parseStandardFormParameters(&$repDef, &$options, $userId = null, $prefix = "DRIVER_OPTION_", $binariesContext = null, $cypheredPassPrefix = "") - { - if ($binariesContext === null) { - $binariesContext = array("USER" => (AuthService::getLoggedUser()!= null)?AuthService::getLoggedUser()->getId():"shared"); - } - $replicationGroups = array(); - $switchesGroups = array(); - foreach ($repDef as $key => $value) { - if( ( ( !empty($prefix) && strpos($key, $prefix)!== false && strpos($key, $prefix)==0 ) || empty($prefix) ) - && strpos($key, "ajxptype") === false - && strpos($key, "_original_binary") === false - && strpos($key, "_replication") === false - && strpos($key, "_checkbox") === false){ - if (isSet($repDef[$key."_ajxptype"])) { - $type = $repDef[$key."_ajxptype"]; - if ($type == "boolean") { - $value = ($value == "true"?true:false); - } else if ($type == "integer") { - $value = intval($value); - } else if ($type == "array") { - $value = explode(",", $value); - } else if ($type == "password" && $userId!=null) { - if (trim($value) != "" && $value != "__AJXP_VALUE_SET__" && function_exists('mcrypt_encrypt')) { - // We encode as base64 so if we need to store the result in a database, it can be stored in text column - $value = $cypheredPassPrefix . base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($userId."\1CDAFx¨op#"), $value, MCRYPT_MODE_ECB)); - } - } else if ($type == "binary" && $binariesContext !== null) { - if (!empty($value)) { - if ($value == "ajxp-remove-original") { - if (!empty($repDef[$key."_original_binary"])) { - ConfService::getConfStorageImpl()->deleteBinary($binariesContext, $repDef[$key."_original_binary"]); - } - $value = ""; - } else { - $file = AJXP_Utils::getAjxpTmpDir()."/".$value; - if (file_exists($file)) { - $id= !empty($repDef[$key."_original_binary"]) ? $repDef[$key."_original_binary"] : null; - $id=ConfService::getConfStorageImpl()->saveBinary($binariesContext, $file, $id); - $value = $id; - } - } - } else if (!empty($repDef[$key."_original_binary"])) { - $value = $repDef[$key."_original_binary"]; - } - } else if (strpos($type,"group_switch:") === 0) { - $tmp = explode(":", $type); - $gSwitchName = $tmp[1]; - $switchesGroups[substr($key, strlen($prefix))] = $gSwitchName; - } else if ($type == "text/json") { - $value = json_decode($value, true); - } - if (!in_array($type, array("textarea", "boolean", "text/json"))) { - $value = AJXP_Utils::sanitize($value, AJXP_SANITIZE_HTML); - } - unset($repDef[$key."_ajxptype"]); - } - if (isSet($repDef[$key."_checkbox"])) { - $checked = $repDef[$key."_checkbox"] == "checked"; - unset($repDef[$key."_checkbox"]); - if(!$checked) continue; - } - if (isSet($repDef[$key."_replication"])) { - $repKey = $repDef[$key."_replication"]; - if(!is_array($replicationGroups[$repKey])) $replicationGroups[$repKey] = array(); - $replicationGroups[$repKey][] = $key; - } - $options[substr($key, strlen($prefix))] = $value; - unset($repDef[$key]); - } else { - $repDef[$key] = $value; - } - } - // DO SOMETHING WITH REPLICATED PARAMETERS? - if (count($switchesGroups)) { - $gValues = array(); - foreach ($switchesGroups as $fieldName => $groupName) { - if (isSet($options[$fieldName])) { - $gValues = array(); - $radic = $groupName."_".$options[$fieldName]."_"; - foreach ($options as $optN => $optV) { - if (strpos($optN, $radic) === 0) { - $newName = substr($optN, strlen($radic)); - $gValues[$newName] = $optV; - } - } - } - $options[$fieldName."_group_switch"] = $options[$fieldName]; - $options[$fieldName] = $gValues; - } - } - - } - - private static $_dibiParamClean = array(); - public static function cleanDibiDriverParameters($params) - { - if(!is_array($params)) return $params; - $value = $params["group_switch_value"]; - if (isSet($value)) { - if(isSet(self::$_dibiParamClean[$value])){ - return self::$_dibiParamClean[$value]; - } - if ($value == "core") { - $bootStorage = ConfService::getBootConfStorageImpl(); - $configs = $bootStorage->loadPluginConfig("core", "conf"); - $params = $configs["DIBI_PRECONFIGURATION"]; - if (!is_array($params)) { - throw new Exception("Empty SQL default connexion, there is something wrong with your setup! You may have switch to an SQL-based plugin without defining a connexion."); - } - } else { - unset($params["group_switch_value"]); - } - foreach ($params as $k => $v) { - $explode = explode("_", $k, 2); - $params[array_pop($explode)] = AJXP_VarsFilter::filter($v); - unset($params[$k]); - } - } - switch ($params["driver"]) { - case "sqlite": - case "sqlite3": - $params["formatDateTime"] = "'Y-m-d H:i:s'"; - $params["formatDate"] = "'Y-m-d'"; - break; - } - if(isSet($value)){ - self::$_dibiParamClean[$value] = $params; - } - return $params; - } - - public static function runCreateTablesQuery($p, $file) - { - - switch ($p["driver"]) { - case "sqlite": - case "sqlite3": - if (!file_exists(dirname($p["database"]))) { - @mkdir(dirname($p["database"]), 0755, true); - } - $ext = ".sqlite"; - break; - case "mysql": - $ext = ".mysql"; - break; - case "postgre": - $ext = ".pgsql"; - break; - default: - return "ERROR!, DB driver ". $p["driver"] ." not supported yet in __FUNCTION__"; - } - - $result = array(); - $file = dirname($file) ."/". str_replace(".sql", $ext, basename($file) ); - $sql = file_get_contents($file); - $separators = explode("/** SEPARATOR **/", $sql); - - $allParts = array(); - - foreach($separators as $sep){ - $explode = explode("\n", trim($sep)); - $firstLine = array_shift($explode); - if($firstLine == "/** BLOCK **/"){ - $allParts[] = $sep; - }else{ - $parts = explode(";", $sep); - $remove = array(); - for ($i = 0 ; $i < count($parts); $i++) { - $part = $parts[$i]; - if (strpos($part, "BEGIN") && isSet($parts[$i+1])) { - $parts[$i] .= ';'.$parts[$i+1]; - $remove[] = $i+1; - } - } - foreach($remove as $rk) unset($parts[$rk]); - $allParts = array_merge($allParts, $parts); - } - } - dibi::connect($p); - dibi::begin(); - foreach ($allParts as $createPart) { - $sqlPart = trim($createPart); - if (empty($sqlPart)) continue; - try { - dibi::nativeQuery($sqlPart); - $resKey = str_replace("\n", "", substr($sqlPart, 0, 50))."..."; - $result[] = "OK: $resKey executed successfully"; - } catch (DibiException $e) { - $result[] = "ERROR! $sqlPart failed"; - } - } - dibi::commit(); - dibi::disconnect(); - $message = implode("\n", $result); - if (strpos($message, "ERROR!")) return $message; - else return "SUCCESS:".$message; - } - - - /* - * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt - * $algorithm - The hash algorithm to use. Recommended: SHA256 - * $password - The password. - * $salt - A salt that is unique to the password. - * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000. - * $key_length - The length of the derived key in bytes. - * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise. - * Returns: A $key_length-byte key derived from the password and salt. - * - * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt - * - * This implementation of PBKDF2 was originally created by https://defuse.ca - * With improvements by http://www.variations-of-shadow.com - */ - public static function pbkdf2_apply($algorithm, $password, $salt, $count, $key_length, $raw_output = false) - { - $algorithm = strtolower($algorithm); - - if(!in_array($algorithm, hash_algos(), true)) - die('PBKDF2 ERROR: Invalid hash algorithm.'); - if($count <= 0 || $key_length <= 0) - die('PBKDF2 ERROR: Invalid parameters.'); - - $hash_length = strlen(hash($algorithm, "", true)); - $block_count = ceil($key_length / $hash_length); - - $output = ""; - - for ($i = 1; $i <= $block_count; $i++) { - // $i encoded as 4 bytes, big endian. - $last = $salt . pack("N", $i); - // first iteration - $last = $xorsum = hash_hmac($algorithm, $last, $password, true); - - // perform the other $count - 1 iterations - for ($j = 1; $j < $count; $j++) { - $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true)); - } - - $output .= $xorsum; - } - - if($raw_output) - return substr($output, 0, $key_length); - else - return bin2hex(substr($output, 0, $key_length)); - } - - - // Compares two strings $a and $b in length-constant time. - public static function pbkdf2_slow_equals($a, $b) - { - $diff = strlen($a) ^ strlen($b); - for ($i = 0; $i < strlen($a) && $i < strlen($b); $i++) { - $diff |= ord($a[$i]) ^ ord($b[$i]); - } - - return $diff === 0; - } - - public static function pbkdf2_validate_password($password, $correct_hash) - { - $params = explode(":", $correct_hash); - - if (count($params) < HASH_SECTIONS) { - if (strlen($correct_hash) == 32 && count($params) == 1) { - return md5($password) == $correct_hash; - } - return false; - } - - $pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]); - return self::pbkdf2_slow_equals( - $pbkdf2, - self::pbkdf2_apply( - $params[HASH_ALGORITHM_INDEX], - $password, - $params[HASH_SALT_INDEX], - (int) $params[HASH_ITERATION_INDEX], - strlen($pbkdf2), - true - ) - ); - } - - - public static function pbkdf2_create_hash($password) - { - // format: algorithm:iterations:salt:hash - $salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTE_SIZE, MCRYPT_DEV_URANDOM)); - return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" . $salt . ":" . - base64_encode(self::pbkdf2_apply( - PBKDF2_HASH_ALGORITHM, - $password, - $salt, - PBKDF2_ITERATIONS, - PBKDF2_HASH_BYTE_SIZE, - true - )); - } - - /** - * generates a random password, uses base64: 0-9a-zA-Z - * @param int [optional] $length length of password, default 24 (144 Bit) - * @param bool $complexChars - * @return string password - */ - public static function generateRandomString($length = 24, $complexChars = false) - { - if (function_exists('openssl_random_pseudo_bytes') && USE_OPENSSL_RANDOM && !$complexChars) { - $password = base64_encode(openssl_random_pseudo_bytes($length, $strong)); - if($strong == TRUE) - return substr(str_replace(array("/","+","="), "", $password), 0, $length); //base64 is about 33% longer, so we need to truncate the result - } - - //fallback to mt_rand if php < 5.3 or no openssl available - $characters = '0123456789'; - $characters .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; - if($complexChars){ - $characters .= "!@#$%&*?"; - } - $charactersLength = strlen($characters)-1; - $password = ''; - - //select some random characters - for ($i = 0; $i < $length; $i++) { - $password .= $characters[mt_rand(0, $charactersLength)]; - } - - return $password; - } - - // Does not support flag GLOB_BRACE - public static function glob_recursive($pattern, $flags = 0) - { - $files = glob($pattern, $flags); - foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir) { - $files = array_merge($files, self::glob_recursive($dir.'/'.basename($pattern), $flags)); - } - return $files; - } - - public static function regexpToLike($regexp) - { - $regexp = trim($regexp, '/'); - $left = "~"; - $right = "~"; - if ($regexp[0]=="^") { - $left = ""; - } - if ($regexp[strlen($regexp)-1] == "$") { - $right = ""; - } - if ($left == "" && $right == "") { - return "= %s"; - } - return "LIKE %".$left."like".$right; - } - - public static function cleanRegexp($regexp) - { - $regexp = str_replace("\/", "/", trim($regexp, '/')); - return ltrim(rtrim($regexp, "$"), "^"); - } - - public static function likeToLike($regexp) - { - $left = ""; - $right = ""; - if ($regexp[0]=="%") { - $left = "~"; - } - if ($regexp[strlen($regexp)-1] == "%") { - $right = "~"; - } - if ($left == "" && $right == "") { - return "= %s"; - } - return "LIKE %".$left."like".$right; - } - - public static function cleanLike($regexp) - { - return ltrim(rtrim($regexp, "%"), "%"); - } - - public static function regexpToLdap($regexp) - { - if(empty($regexp)) - return null; - - $left = "*"; - $right = "*"; - if ($regexp[0]=="^") { - $regexp = ltrim($regexp, "^"); - $left = ""; - } - if ($regexp[strlen($regexp)-1] == "$") { - $regexp = rtrim($regexp, "$"); - $right = ""; - } - return $left.$regexp.$right; - } - - /** - * Hide file or folder for Windows OS - * @static - * @param $file - */ - public static function winSetHidden($file) - { - @shell_exec("attrib +H " . escapeshellarg($file)); - } -} diff --git a/core/src/core/classes/class.AJXP_VarsFilter.php b/core/src/core/classes/class.AJXP_VarsFilter.php deleted file mode 100644 index a40ae32474..0000000000 --- a/core/src/core/classes/class.AJXP_VarsFilter.php +++ /dev/null @@ -1,107 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Standard values filtering used in the core. - * @static - * @package Pydio - * @subpackage Core - */ -class AJXP_VarsFilter -{ - /** - * Filter the very basic keywords from the XML : AJXP_USER, AJXP_INSTALL_PATH, AJXP_DATA_PATH - * Calls the vars.filter hooks. - * @static - * @param $value - * @param AbstractAjxpUser|String $resolveUser - * @return mixed|string - */ - public static function filter($value, $resolveUser = null) - { - if (is_string($value) && strpos($value, "AJXP_USER")!==false) { - if (AuthService::usersEnabled()) { - if($resolveUser != null){ - if(is_string($resolveUser)){ - $resolveUserId = $resolveUser; - } else { - $resolveUserId = $resolveUser->getId(); - } - $value = str_replace("AJXP_USER", $resolveUserId, $value); - }else{ - $loggedUser = AuthService::getLoggedUser(); - if ($loggedUser != null) { - if ($loggedUser->hasParent() && $loggedUser->getResolveAsParent()) { - $loggedUserId = $loggedUser->getParent(); - } else { - $loggedUserId = $loggedUser->getId(); - } - $value = str_replace("AJXP_USER", $loggedUserId, $value); - } else { - return ""; - } - } - } else { - $value = str_replace("AJXP_USER", "shared", $value); - } - } - if (is_string($value) && strpos($value, "AJXP_GROUP_PATH")!==false) { - if (AuthService::usersEnabled()) { - if($resolveUser != null){ - if(is_string($resolveUser) && AuthService::userExists($resolveUser)){ - $loggedUser = ConfService::getConfStorageImpl()->createUserObject($resolveUser); - }else{ - $loggedUser = $resolveUser; - } - }else{ - $loggedUser = AuthService::getLoggedUser(); - } - if ($loggedUser != null) { - $gPath = $loggedUser->getGroupPath(); - $value = str_replace("AJXP_GROUP_PATH_FLAT", str_replace("/", "_", trim($gPath, "/")), $value); - $value = str_replace("AJXP_GROUP_PATH", $gPath, $value); - } else { - return ""; - } - } else { - $value = str_replace(array("AJXP_GROUP_PATH", "AJXP_GROUP_PATH_FLAT"), "shared", $value); - } - } - if (is_string($value) && strpos($value, "AJXP_INSTALL_PATH") !== false) { - $value = str_replace("AJXP_INSTALL_PATH", AJXP_INSTALL_PATH, $value); - } - if (is_string($value) && strpos($value, "AJXP_DATA_PATH") !== false) { - $value = str_replace("AJXP_DATA_PATH", AJXP_DATA_PATH, $value); - } - $tab = array(&$value); - AJXP_Controller::applyIncludeHook("vars.filter", $tab); - return $value; - } - - public static function filterI18nStrings(&$array){ - if(!is_array($array)) return; - $appTitle = ConfService::getCoreConf("APPLICATION_TITLE"); - foreach($array as &$value){ - $value = str_replace("APPLICATION_TITLE", $appTitle, $value); - } - } -} diff --git a/core/src/core/classes/class.AJXP_XMLWriter.php b/core/src/core/classes/class.AJXP_XMLWriter.php deleted file mode 100644 index c5e2024d7b..0000000000 --- a/core/src/core/classes/class.AJXP_XMLWriter.php +++ /dev/null @@ -1,874 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * XML output Generator - * @package Pydio - * @subpackage Core - */ -class AJXP_XMLWriter -{ - public static $headerSent = false; - - /** - * Output Headers, XML tag and a root node - * @static - * @param string $docNode - * @param array $attributes - */ - public static function header($docNode="tree", $attributes=array()) - { - if(self::$headerSent !== false && self::$headerSent == $docNode) return ; - header('Content-Type: text/xml; charset=UTF-8'); - header('Cache-Control: no-cache'); - print(''); - $attString = ""; - if (count($attributes)) { - foreach ($attributes as $name=>$value) { - $attString.="$name=\"$value\" "; - } - } - self::$headerSent = $docNode; - print("<$docNode $attString>"); - - } - /** - * Outputs a closing root not () - * @static - * @param string $docNode - * @return void - */ - public static function close($docNode="tree") - { - print(""); - } - - /** - * @static - * @param string $data - * @param bool $print - * @return string - */ - public static function write($data, $print) - { - if ($print) { - print($data); - return ""; - } else { - return $data; - } - } - - /** - * Ouput the tag - * @static - * @param integer $count - * @param integer $currentPage - * @param integer $totalPages - * @param integer $dirsCount - * @param null $remoteSortAttributes - */ - public static function renderPaginationData($count, $currentPage, $totalPages, $dirsCount = -1, $remoteSortAttributes = null) - { - $remoteSortString = ""; - if (is_array($remoteSortAttributes)) { - foreach($remoteSortAttributes as $k => $v) $remoteSortString .= " $k='$v'"; - } - $string = ''; - AJXP_XMLWriter::write($string, true); - } - - /** - * Convert an arbitrary list of data (array of associative array) as standard XML list. - * @param $data - * @param $rootPath - * @param $idKey - * @param $labelKey - */ - public static function renderSimpleListAsNodes($data, $rootPath, $idKey, $labelKey){ - - self::header(); - - foreach($data as $item){ - - $nodeName = rtrim($rootPath, "/")."/".$item[$idKey]; - $nodeLabel = $item[$labelKey]; - $isLeaf = true; - $metaData = $item; - self::renderNode($nodeName, $nodeLabel, $isLeaf, $metaData); - - } - - self::close(); - } - - /** - * Prints out the XML headers and preamble, then an open node - * @static - * @param $nodeName - * @param $nodeLabel - * @param $isLeaf - * @param array $metaData - * @return void - */ - public static function renderHeaderNode($nodeName, $nodeLabel, $isLeaf, $metaData = array()) - { - header('Content-Type: text/xml; charset=UTF-8'); - header('Cache-Control: no-cache'); - print(''); - self::$headerSent = "tree"; - AJXP_XMLWriter::renderNode($nodeName, $nodeLabel, $isLeaf, $metaData, false); - } - - /** - * @static - * @param AJXP_Node $ajxpNode - * @return void - */ - public static function renderAjxpHeaderNode($ajxpNode) - { - header('Content-Type: text/xml; charset=UTF-8'); - header('Cache-Control: no-cache'); - print(''); - self::$headerSent = "tree"; - self::renderAjxpNode($ajxpNode, false); - } - - /** - * The basic node - * @static - * @param string $nodeName - * @param string $nodeLabel - * @param bool $isLeaf - * @param array $metaData - * @param bool $close - * @param bool $print - * @return void|string - */ - public static function renderNode($nodeName, $nodeLabel, $isLeaf, $metaData = array(), $close=true, $print = true) - { - $string = " $value) { - if(AJXP_Utils::detectXSS($value)) $value = "XSS Detected!"; - $value = AJXP_Utils::xmlEntities($value, true); - $string .= " $key=\"$value\""; - } - if ($close) { - $string .= "/>"; - } else { - $string .= ">"; - } - return AJXP_XMLWriter::write($string, $print); - } - - /** - * @static - * @param AJXP_Node $ajxpNode - * @param bool $close - * @param bool $print - * @return void|string - */ - public static function renderAjxpNode($ajxpNode, $close = true, $print = true) - { - return AJXP_XMLWriter::renderNode( - $ajxpNode->getPath(), - $ajxpNode->getLabel(), - $ajxpNode->isLeaf(), - $ajxpNode->metadata, - $close, - $print); - } - - /** - * Render a node with arguments passed as array - * @static - * @param $array - * @return void - */ - public static function renderNodeArray($array) - { - self::renderNode($array[0],$array[1],$array[2],$array[3]); - } - - /** - * Error Catcher for PHP errors. Depending on the SERVER_DEBUG config - * shows the file/line info or not. - * @static - * @param $code - * @param $message - * @param $fichier - * @param $ligne - * @param $context - */ - public static function catchError($code, $message, $fichier, $ligne, $context) - { - if(error_reporting() == 0) { - return ; - } - AJXP_Logger::error(basename($fichier), "error l.$ligne", array("message" => $message)); - if (ConfService::getConf("SERVER_DEBUG")) { - $stack = debug_backtrace(); - $stackLen = count($stack); - for ($i = 1; $i < $stackLen; $i++) { - $entry = $stack[$i]; - - $func = $entry['function'] . '('; - $argsLen = count($entry['args']); - for ($j = 0; $j < $argsLen; $j++) { - $s = $entry['args'][$j]; - if(is_string($s)){ - $func .= $s; - }else if (is_object($s)){ - $func .= get_class($s); - } - if ($j < $argsLen - 1) $func .= ', '; - } - $func .= ')'; - - $message .= "\n". str_replace(dirname(__FILE__), '', $entry['file']) . ':' . $entry['line'] . ' - ' . $func . PHP_EOL; - } - } - if(!headers_sent()) AJXP_XMLWriter::header(); - if(!empty($context) && is_object($context) && is_a($context, "AJXP_PromptException")){ - AJXP_XMLWriter::write("getPromptType()."\">".$message."getPromptData())."]]>", true); - }else{ - AJXP_XMLWriter::sendMessage(null, SystemTextEncoding::toUTF8($message), true); - } - AJXP_XMLWriter::close(); - exit(1); - } - - /** - * Catch exceptions, @see catchError - * @param Exception $exception - */ - public static function catchException($exception) - { - try { - AJXP_XMLWriter::catchError($exception->getCode(), SystemTextEncoding::fromUTF8($exception->getMessage()), $exception->getFile(), $exception->getLine(), $exception); - } catch (Exception $innerEx) { - error_log(get_class($innerEx)." thrown within the exception handler!"); - error_log("Original exception was: ".$innerEx->getMessage()." in ".$innerEx->getFile()." on line ".$innerEx->getLine()); - error_log("New exception is: ".$innerEx->getMessage()." in ".$innerEx->getFile()." on line ".$innerEx->getLine()." ".$innerEx->getTraceAsString()); - print("Error"); - } - } - /** - * Dynamically replace XML keywords with their live values. - * AJXP_SERVER_ACCESS, AJXP_MIMES_*,AJXP_ALL_MESSAGES, etc. - * @static - * @param string $xml - * @param bool $stripSpaces - * @return mixed - */ - public static function replaceAjxpXmlKeywords($xml, $stripSpaces = false) - { - $messages = ConfService::getMessages(); - $confMessages = ConfService::getMessagesConf(); - $matches = array(); - if (isSet($_SESSION["AJXP_SERVER_PREFIX_URI"])) { - //$xml = str_replace("AJXP_THEME_FOLDER", $_SESSION["AJXP_SERVER_PREFIX_URI"].AJXP_THEME_FOLDER, $xml); - $xml = str_replace("AJXP_SERVER_ACCESS", $_SESSION["AJXP_SERVER_PREFIX_URI"].AJXP_SERVER_ACCESS, $xml); - } else { - //$xml = str_replace("AJXP_THEME_FOLDER", AJXP_THEME_FOLDER, $xml); - $xml = str_replace("AJXP_SERVER_ACCESS", AJXP_SERVER_ACCESS, $xml); - } - $xml = str_replace("AJXP_APPLICATION_TITLE", ConfService::getCoreConf("APPLICATION_TITLE"), $xml); - $xml = str_replace("AJXP_MIMES_EDITABLE", AJXP_Utils::getAjxpMimes("editable"), $xml); - $xml = str_replace("AJXP_MIMES_IMAGE", AJXP_Utils::getAjxpMimes("image"), $xml); - $xml = str_replace("AJXP_MIMES_AUDIO", AJXP_Utils::getAjxpMimes("audio"), $xml); - $xml = str_replace("AJXP_MIMES_ZIP", AJXP_Utils::getAjxpMimes("zip"), $xml); - $authDriver = ConfService::getAuthDriverImpl(); - if ($authDriver != NULL) { - $loginRedirect = $authDriver->getLoginRedirect(); - $xml = str_replace("AJXP_LOGIN_REDIRECT", ($loginRedirect!==false?"'".$loginRedirect."'":"false"), $xml); - } - $xml = str_replace("AJXP_REMOTE_AUTH", "false", $xml); - $xml = str_replace("AJXP_NOT_REMOTE_AUTH", "true", $xml); - $xml = str_replace("AJXP_ALL_MESSAGES", "MessageHash=".json_encode(ConfService::getMessages()).";", $xml); - - if (preg_match_all("/AJXP_MESSAGE(\[.*?\])/", $xml, $matches, PREG_SET_ORDER)) { - foreach ($matches as $match) { - $messId = str_replace("]", "", str_replace("[", "", $match[1])); - $xml = str_replace("AJXP_MESSAGE[$messId]", $messages[$messId], $xml); - } - } - if (preg_match_all("/CONF_MESSAGE(\[.*?\])/", $xml, $matches, PREG_SET_ORDER)) { - foreach ($matches as $match) { - $messId = str_replace(array("[", "]"), "", $match[1]); - $message = $messId; - if (array_key_exists($messId, $confMessages)) { - $message = $confMessages[$messId]; - } - $xml = str_replace("CONF_MESSAGE[$messId]", AJXP_Utils::xmlEntities($message), $xml); - } - } - if (preg_match_all("/MIXIN_MESSAGE(\[.*?\])/", $xml, $matches, PREG_SET_ORDER)) { - foreach ($matches as $match) { - $messId = str_replace(array("[", "]"), "", $match[1]); - $message = $messId; - if (array_key_exists($messId, $confMessages)) { - $message = $confMessages[$messId]; - } - $xml = str_replace("MIXIN_MESSAGE[$messId]", AJXP_Utils::xmlEntities($message), $xml); - } - } - if ($stripSpaces) { - $xml = preg_replace("/[\n\r]?/", "", $xml); - $xml = preg_replace("/\t/", " ", $xml); - } - $xml = str_replace(array('xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"','xsi:noNamespaceSchemaLocation="file:../core.ajaxplorer/ajxp_registry.xsd"'), "", $xml); - $tab = array(&$xml); - AJXP_Controller::applyIncludeHook("xml.filter", $tab); - return $xml; - } - /** - * Send a XML instruction for refreshing the list - * @static - * @param string $nodePath - * @param string $pendingSelection - * @param bool $print - * @return string - */ - public static function reloadDataNode($nodePath="", $pendingSelection="", $print = true) - { - $nodePath = AJXP_Utils::xmlEntities($nodePath, true); - $pendingSelection = AJXP_Utils::xmlEntities($pendingSelection, true); - return AJXP_XMLWriter::write("", $print); - } - - - /** - * Send a XML instruction for refreshing the list - * @static - * @param $diffNodes - * @param bool $print - * @return string - */ - public static function writeNodesDiff($diffNodes, $print = false) - { - /** - * @var $ajxpNode AJXP_Node - */ - $mess = ConfService::getMessages(); - $buffer = ""; - if (isSet($diffNodes["REMOVE"]) && count($diffNodes["REMOVE"])) { - $buffer .= ""; - foreach ($diffNodes["REMOVE"] as $nodePath) { - $nodePath = AJXP_Utils::xmlEntities($nodePath, true); - $buffer .= ""; - } - $buffer .= ""; - } - if (isSet($diffNodes["ADD"]) && count($diffNodes["ADD"])) { - $buffer .= ""; - foreach ($diffNodes["ADD"] as $ajxpNode) { - $ajxpNode->loadNodeInfo(false, false, "all"); - if (!empty($ajxpNode->metaData["mimestring_id"]) && array_key_exists($ajxpNode->metaData["mimestring_id"], $mess)) { - $ajxpNode->mergeMetadata(array("mimestring" => $mess[$ajxpNode->metaData["mimestring_id"]])); - } - $buffer .= self::renderAjxpNode($ajxpNode, true, false); - } - $buffer .= ""; - } - if (isSet($diffNodes["UPDATE"]) && count($diffNodes["UPDATE"])) { - $buffer .= ""; - foreach ($diffNodes["UPDATE"] as $originalPath => $ajxpNode) { - $ajxpNode->loadNodeInfo(false, false, "all"); - if (!empty($ajxpNode->metaData["mimestring_id"]) && array_key_exists($ajxpNode->metaData["mimestring_id"], $mess)) { - $ajxpNode->mergeMetadata(array("mimestring" => $mess[$ajxpNode->metaData["mimestring_id"]])); - } - $ajxpNode->original_path = $originalPath; - $buffer .= self::renderAjxpNode($ajxpNode, true, false); - } - $buffer .= ""; - } - $buffer .= ""; - return AJXP_XMLWriter::write($buffer, $print); - - /* - $nodePath = AJXP_Utils::xmlEntities($nodePath, true); - $pendingSelection = AJXP_Utils::xmlEntities($pendingSelection, true); - return AJXP_XMLWriter::write("", $print); - */ - } - - - /** - * Send a XML instruction for refreshing the repositories list - * @static - * @param bool $print - * @return string - */ - public static function reloadRepositoryList($print = true) - { - return AJXP_XMLWriter::write("", $print); - } - /** - * Outputs a tag - * @static - * @param bool $print - * @return string - */ - public static function requireAuth($print = true) - { - return AJXP_XMLWriter::write("", $print); - } - /** - * Triggers a background action client side - * @static - * @param $actionName - * @param $parameters - * @param $messageId - * @param bool $print - * @param int $delay - * @return string - */ - public static function triggerBgAction($actionName, $parameters, $messageId, $print=true, $delay = 0) - { - $messageId = AJXP_Utils::xmlEntities($messageId); - $data = AJXP_XMLWriter::write("", $print); - foreach ($parameters as $paramName=>$paramValue) { - $paramValue = AJXP_Utils::xmlEntities($paramValue); - $data .= AJXP_XMLWriter::write("", $print); - } - $data .= AJXP_XMLWriter::write("", $print); - return $data; - } - - public static function triggerBgJSAction($jsCode, $messageId, $print=true, $delay = 0) - { - $data = AJXP_XMLWriter::write("", $print); - $data .= AJXP_XMLWriter::write("", $print); - $data .= AJXP_XMLWriter::write("", $print); - return $data; - } - - /** - * List all bookmmarks as XML - * @static - * @param $allBookmarks - * @param bool $print - * @param string $format legacy|node_list - * @return string - */ - public static function writeBookmarks($allBookmarks, $print = true, $format = "legacy") - { - $driver = false; - if ($format == "node_list") { - $driver = ConfService::loadRepositoryDriver(); - if (!is_a($driver, "AjxpWrapperProvider")) { - $driver = false; - } - } - $buffer = ""; - foreach ($allBookmarks as $bookmark) { - $path = ""; $title = ""; - if (is_array($bookmark)) { - $path = $bookmark["PATH"]; - $title = $bookmark["TITLE"]; - } else if (is_string($bookmark)) { - $path = $bookmark; - $title = basename($bookmark); - } - if ($format == "node_list") { - if ($driver) { - $node = new AJXP_Node($driver->getResourceUrl($path)); - $buffer .= AJXP_XMLWriter::renderAjxpNode($node, true, false); - } else { - $buffer .= AJXP_XMLWriter::renderNode($path, $title, false, array('icon' => "mime_empty.png"), true, false); - } - } else { - $buffer .= ""; - } - } - if($print) { - print $buffer; - return null; - } else { - return $buffer; - } - } - /** - * Utilitary for generating a tag for the FilesList component - * @static - * @param $config - * @return void - */ - public static function sendFilesListComponentConfig($config) - { - if (is_string($config)) { - print("$config"); - } - } - /** - * Send a success or error message to the client. - * @static - * @param $logMessage - * @param $errorMessage - * @param bool $print - * @return string - */ - public static function sendMessage($logMessage, $errorMessage, $print = true) - { - if ($errorMessage == null) { - $messageType = "SUCCESS"; - $message = AJXP_Utils::xmlContentEntities($logMessage); - } else { - $messageType = "ERROR"; - $message = AJXP_Utils::xmlContentEntities($errorMessage); - } - return AJXP_XMLWriter::write("".$message."", $print); - } - - /** - * Extract all the user data and put it in XML - * @static - * @param null $userObject * @internal param bool $details - * @return string - */ - public static function getUserXML($userObject = null) - { - $buffer = ""; - $loggedUser = AuthService::getLoggedUser(); - $confDriver = ConfService::getConfStorageImpl(); - if($userObject != null) $loggedUser = $userObject; - if (!AuthService::usersEnabled()) { - $buffer.=""; - $buffer.=""; - $buffer.= AJXP_XMLWriter::writeRepositoriesData(null); - $buffer.=""; - } else if ($loggedUser != null) { - $lock = $loggedUser->getLock(); - $buffer.="id."\">"; - $buffer.="canWrite(ConfService::getCurrentRepositoryId())?"1":"0")."\" read=\"".($loggedUser->canRead(ConfService::getCurrentRepositoryId())?"1":"0")."\"/>"; - $buffer.= AJXP_XMLWriter::writeRepositoriesData($loggedUser); - $buffer.=""; - $preferences = $confDriver->getExposedPreferences($loggedUser); - foreach ($preferences as $prefName => $prefData) { - $atts = ""; - if (isSet($prefData["exposed"]) && $prefData["exposed"] == true) { - foreach ($prefData as $k => $v) { - if($k=="name") continue; - if($k == "value") $k = "default"; - $atts .= "$k='$v' "; - } - } - if (isset($prefData["pluginId"])) { - $atts .= "pluginId='".$prefData["pluginId"]."' "; - } - if ($prefData["type"] == "string") { - $buffer.=""; - } else if ($prefData["type"] == "json") { - $buffer.=""; - } - } - $buffer.=""; - $buffer.="isAdmin()?"1":"0")."\" ".($lock!==false?"lock=\"$lock\"":"")."/>"; - /* - $bMarks = $loggedUser->getBookmarks(); - if (count($bMarks)) { - $buffer.= "".AJXP_XMLWriter::writeBookmarks($bMarks, false).""; - } - */ - $buffer.=""; - } - return $buffer; - } - - /** - * Write the repositories access rights in XML format - * @static - * @param AbstractAjxpUser|null $loggedUser * @internal param bool $details - * @return string - */ - public static function writeRepositoriesData($loggedUser) - { - $st = ""; - $streams = ConfService::detectRepositoryStreams(false); - - $exposed = array(); - $cacheHasExposed = AJXP_PluginsService::getInstance()->loadFromPluginQueriesCache("//server_settings/param[contains(@scope,'repository') and @expose='true']"); - if ($cacheHasExposed !== null && is_array($cacheHasExposed)) { - $exposed = $cacheHasExposed; - } else { - $exposed_props = AJXP_PluginsService::searchAllManifests("//server_settings/param[contains(@scope,'repository') and @expose='true']", "node", false, false, true); - foreach($exposed_props as $exposed_prop){ - $pluginId = $exposed_prop->parentNode->parentNode->getAttribute("id"); - $paramName = $exposed_prop->getAttribute("name"); - $paramDefault = $exposed_prop->getAttribute("default"); - $exposed[] = array("PLUGIN_ID" => $pluginId, "NAME" => $paramName, "DEFAULT" => $paramDefault); - } - AJXP_PluginsService::getInstance()->storeToPluginQueriesCache("//server_settings/param[contains(@scope,'repository') and @expose='true']", $exposed); - } - - $accessible = ConfService::getAccessibleRepositories($loggedUser, false, false); - - $inboxStatus = 0; - foreach($accessible as $repoId => $repoObject){ - if(!$repoObject->hasContentFilter()) { - continue; - } - $accessStatus = $repoObject->getAccessStatus(); - if(empty($accessStatus) && $loggedUser != null){ - $lastConnected = $loggedUser->getArrayPref("repository_last_connected", $repoId); - if(empty($lastConnected)){ - $accessStatus = 1; - } - } - if(!empty($accessStatus)){ - $inboxStatus ++; - } - } - - foreach ($accessible as $repoId => $repoObject) { - if(!isSet($_SESSION["CURRENT_MINISITE"]) && $repoObject->hasContentFilter()){ - continue; - } - $accessStatus = ''; - if($repoObject->getAccessType() == "inbox"){ - $accessStatus = $inboxStatus; - } - $xmlString = self::repositoryToXML($repoId, $repoObject, $exposed, $streams, $loggedUser, $accessStatus); - $st .= $xmlString; - } - - $st .= ""; - return $st; - } - - /** - * @param string $repoId - * @param Repository $repoObject - * @param array $exposed - * @param array $streams - * @param AbstractAjxpUser $loggedUser - * @param string $accessStatus - * @return string - * @throws Exception - */ - public static function repositoryToXML($repoId, $repoObject, $exposed, $streams, $loggedUser, $accessStatus = ""){ - - - $statusString = " repository_type=\"".$repoObject->getRepositoryType()."\""; - if(empty($accessStatus)){ - $accessStatus = $repoObject->getAccessStatus(); - } - if(!empty($accessStatus)){ - $statusString .= " access_status=\"$accessStatus\" "; - }else if($loggedUser != null){ - $lastConnected = $loggedUser->getArrayPref("repository_last_connected", $repoId); - if(!empty($lastConnected)) $statusString .= " last_connection=\"$lastConnected\" "; - } - - $streamString = ""; - if (in_array($repoObject->accessType, $streams)) { - $streamString = "allowCrossRepositoryCopy=\"true\""; - } - if ($repoObject->getUniqueUser()) { - $streamString .= " user_editable_repository=\"true\" "; - } - if ($repoObject->hasContentFilter()){ - $streamString .= " hasContentFilter=\"true\""; - } - $slugString = ""; - $slug = $repoObject->getSlug(); - if (!empty($slug)) { - $slugString = "repositorySlug=\"$slug\""; - } - $isSharedString = ""; - $currentUserIsOwner = false; - $ownerLabel = null; - if ($repoObject->hasOwner()) { - $uId = $repoObject->getOwner(); - if(AuthService::usersEnabled() && AuthService::getLoggedUser()->getId() == $uId){ - $currentUserIsOwner = true; - } - $label = ConfService::getUserPersonalParameter("USER_DISPLAY_NAME", $uId, "core.conf", $uId); - $ownerLabel = $label; - $isSharedString = 'owner="'.AJXP_Utils::xmlEntities($label).'"'; - } - if ($repoObject->securityScope() == "USER" || $currentUserIsOwner){ - $streamString .= " userScope=\"true\""; - } - - $descTag = ""; - $public = false; - if(!empty($_SESSION["CURRENT_MINISITE"])) $public = true; - $description = $repoObject->getDescription($public, $ownerLabel); - if (!empty($description)) { - $descTag = ''.AJXP_Utils::xmlEntities($description, true).''; - } - $roleString=""; - if($loggedUser != null){ - $merged = $loggedUser->mergedRole; - $params = array(); - foreach($exposed as $exposed_prop){ - $metaOptions = $repoObject->getOption("META_SOURCES"); - if(!isSet($metaOptions[$exposed_prop["PLUGIN_ID"]])){ - continue; - } - $value = $exposed_prop["DEFAULT"]; - if(isSet($metaOptions[$exposed_prop["PLUGIN_ID"]][$exposed_prop["NAME"]])){ - $value = $metaOptions[$exposed_prop["PLUGIN_ID"]][$exposed_prop["NAME"]]; - } - $value = $merged->filterParameterValue($exposed_prop["PLUGIN_ID"], $exposed_prop["NAME"], $repoId, $value); - if($value !== null){ - if($value === true || $value === false) $value = ($value === true ?"true":"false"); - $params[] = ''; - $roleString .= str_replace(".", "_",$exposed_prop["PLUGIN_ID"])."_".$exposed_prop["NAME"].'="'.AJXP_Utils::xmlEntities($value).'" '; - } - } - $roleString.='acl="'.$merged->getAcl($repoId).'"'; - if($merged->hasMask($repoId)){ - $roleString.= ' hasMask="true" '; - } - } - return "accessType."\" id=\"".$repoId."\"$statusString $streamString $slugString $isSharedString $roleString>".$descTag.$repoObject->getClientSettings().""; - - } - - /** - * Writes a tag - * @static - * @param integer $result - * @param string $rememberLogin - * @param string $rememberPass - * @param string $secureToken - * @return void - */ - public static function loggingResult($result, $rememberLogin="", $rememberPass = "", $secureToken="") - { - $remString = ""; - if ($rememberPass != "" && $rememberLogin!= "") { - $remString = " remember_login=\"$rememberLogin\" remember_pass=\"$rememberPass\""; - } - if ($secureToken != "") { - $remString .= " secure_token=\"$secureToken\""; - } - print(""); - } - - /** - * Create plain PHP associative array from XML. - * - * Example usage: - * $xmlNode = simplexml_load_file('example.xml'); - * $arrayData = xmlToArray($xmlNode); - * echo json_encode($arrayData); - * - * @param \DOMNode $domXml The dom node to load - * @param array $options Associative array of options - * @return array - * @link http://outlandishideas.co.uk/blog/2012/08/xml-to-json/ More info - * @author Tamlyn Rhodes - * @license http://creativecommons.org/publicdomain/mark/1.0/ Public Domain - */ - public static function xmlToArray($domXml, $options = array()) { - $xml = simplexml_import_dom($domXml); - $defaults = array( - 'namespaceSeparator' => ':',//you may want this to be something other than a colon - 'attributePrefix' => '@', //to distinguish between attributes and nodes with the same name - 'alwaysArray' => array(), //array of xml tag names which should always become arrays - 'autoArray' => true, //only create arrays for tags which appear more than once - 'textContent' => '$', //key used for the text content of elements - 'autoText' => true, //skip textContent key if node has no attributes or child nodes - 'keySearch' => false, //optional search and replace on tag and attribute names - 'keyReplace' => false //replace values for above search values (as passed to str_replace()) - ); - $options = array_merge($defaults, $options); - $namespaces = $xml->getDocNamespaces(); - $namespaces[''] = null; //add base (empty) namespace - - //get attributes from all namespaces - $attributesArray = array(); - foreach ($namespaces as $prefix => $namespace) { - foreach ($xml->attributes($namespace) as $attributeName => $attribute) { - //replace characters in attribute name - if ($options['keySearch']) $attributeName = - str_replace($options['keySearch'], $options['keyReplace'], $attributeName); - $attributeKey = $options['attributePrefix'] - . ($prefix ? $prefix . $options['namespaceSeparator'] : '') - . $attributeName; - $attributesArray[$attributeKey] = (string)$attribute; - } - } - - //get child nodes from all namespaces - $tagsArray = array(); - foreach ($namespaces as $prefix => $namespace) { - foreach ($xml->children($namespace) as $childXml) { - //recurse into child nodes - $childArray = self::xmlToArray($childXml, $options); - list($childTagName, $childProperties) = each($childArray); - - //replace characters in tag name - if ($options['keySearch']) $childTagName = - str_replace($options['keySearch'], $options['keyReplace'], $childTagName); - //add namespace prefix, if any - if ($prefix) $childTagName = $prefix . $options['namespaceSeparator'] . $childTagName; - - if (!isset($tagsArray[$childTagName])) { - //only entry with this key - //test if tags of this type should always be arrays, no matter the element count - $tagsArray[$childTagName] = - in_array($childTagName, $options['alwaysArray']) || !$options['autoArray'] - ? array($childProperties) : $childProperties; - } elseif ( - is_array($tagsArray[$childTagName]) && array_keys($tagsArray[$childTagName]) - === range(0, count($tagsArray[$childTagName]) - 1) - ) { - //key already exists and is integer indexed array - $tagsArray[$childTagName][] = $childProperties; - } else { - //key exists so convert to integer indexed array with previous value in position 0 - $tagsArray[$childTagName] = array($tagsArray[$childTagName], $childProperties); - } - } - } - - //get text content of node - $textContentArray = array(); - $plainText = trim((string)$xml); - if ($plainText !== '') $textContentArray[$options['textContent']] = $plainText; - - //stick it all together - $propertiesArray = !$options['autoText'] || $attributesArray || $tagsArray || ($plainText === '') - ? array_merge($attributesArray, $tagsArray, $textContentArray) : $plainText; - - //return node as array - return array( - $xml->getName() => $propertiesArray - ); - } - -} diff --git a/core/src/core/classes/class.AbstractTest.php b/core/src/core/classes/class.AbstractTest.php deleted file mode 100644 index d248508d0b..0000000000 --- a/core/src/core/classes/class.AbstractTest.php +++ /dev/null @@ -1,99 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -global $MAIN_testsArray; -/** - * Abstract test class - * Abstract class for diagnostic tests. These tests are run at the first application start up, and their - * results are displayed in the Diagnostic page. It's possible to re-run the full diagnostic by calling - * the runTests.php script (first line needs to be commented first). - * @package Pydio - * @subpackage Core - */ -class AbstractTest -{ - /** The test name */ - public $name; - /** The test information when failed */ - public $failedInfo; - /** The test results output (used for report) */ - public $resultOutput; - /** Tested params - When used as a diagnostic tool, can store variables used by the test*/ - public $testedParas; - /** The test level when failed (warning, info or error, default to error) */ - public $failedLevel; - /** The test parameters */ - public $params; - - public function __construct($name, $failedInfo, $params = NULL) - { - $this->name = $name; - $this->failedInfo = $failedInfo; - $this->params = $params; - $this->failedLevel = "error"; - $this->testedParams = array(); - global $MAIN_testsArray; - $MAIN_testsArray[] = $this; - } - - /** - * Perform the test, should be overwritten in concrete classes - * @abstract - * @return Boolean - */ - public function doTest() { return FALSE; } - - /** - * Perform the test on a given repository object, should be overwritten in concrete classes - * @param Repository $repository - * @return Boolean - */ - public function doRepositoryTest($repository) { return FALSE; } - - /** - * Utilitary to convert php config to numerical values. - * - * @param String $val - * @return Integer - */ - public function returnBytes($val) - { - $val = trim($val); - $last = strtolower($val[strlen($val)-1]); - switch ($last) { - // Le modifieur 'G' est disponible depuis PHP 5.1.0 - case 'g': - $val *= 1024 * 1024 * 1024; - break; - case 'm': - $val *= 1024 * 1024; - break; - case 'k': - $val *= 1024; - break; - default: - break; - } - - return $val; - } -}; diff --git a/core/src/core/classes/class.AjxpRole.php b/core/src/core/classes/class.AjxpRole.php deleted file mode 100644 index 15a859f818..0000000000 --- a/core/src/core/classes/class.AjxpRole.php +++ /dev/null @@ -1,181 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Authentication "role" concept : set of permissions that can be applied to - * one or more users, plus set of actions to be disabled. - * @package Pydio - * @subpackage Core - * @deprecated use AJXP_Role instead. Still present in package for automatic migration. - */ -class AjxpRole implements AjxpGroupPathProvider -{ - private $id; - private $rights = array(); - private $default = false; - /** - * @var String - */ - protected $groupPath; - /** - * Constructor - * @param string $id - * @return void - */ - public function __construct($id) - { - $this->id = $id; - } - /** - * @param $id - * @return void - */ - public function setId($id) - { - $this->id = $id; - } - /** - * @return string - */ - public function getId() - { - return $this->id; - } - /** - * Whether this role can read the given repo - * @param string $rootDirId Repository ID - * @return bool - */ - public function canRead($rootDirId) - { - $right = $this->getRight($rootDirId); - if($right == "rw" || $right == "r") return true; - return false; - } - - /** - * Whether this role can write the given repo - * @param string $rootDirId Repository ID - * @return bool - */ - public function canWrite($rootDirId) - { - $right = $this->getRight($rootDirId); - if($right == "rw" || $right == "w") return true; - return false; - } - - /** - * Current definitioon (r, rw, w, empty string) for the given repo - * @param string $rootDirId Repository ID - * @return string - */ - public function getRight($rootDirId) - { - if(isSet($this->rights[$rootDirId])) return $this->rights[$rootDirId]; - return ""; - } - - /** - * Set the right - * @param string $rootDirId Repo id - * @param string $rightString ("r", "rw", "w", "") - * @return void - */ - public function setRight($rootDirId, $rightString) - { - $this->rights[$rootDirId] = $rightString; - } - /** - * Remove a right entry for the repository - * @param string $rootDirId - * @return void - */ - public function removeRights($rootDirId) - { - if(isSet($this->rights[$rootDirId])) unset($this->rights[$rootDirId]); - } - /** - * Remove all rights - * @return void - */ - public function clearRights() - { - $this->rights = array(); - } - /** - * Get the specific actions rights (see setSpecificActionsRights) - * @param $rootDirId - * @return array - */ - public function getSpecificActionsRights($rootDirId) - { - $res = array(); - if ($rootDirId."" != "ajxp.all") { - $res = $this->getSpecificActionsRights("ajxp.all"); - } - if (isSet($this->rights["ajxp.actions"]) && isSet($this->rights["ajxp.actions"][$rootDirId])) { - $res = array_merge($res, $this->rights["ajxp.actions"][$rootDirId]); - } - return $res; - } - /** - * This method allows to specifically disable some actions for a given role for one or more repository. - * @param string $rootDirId Repository id or "ajxp.all" for all repositories - * @param string $actionName - * @param bool $allowed - * @return void - */ - public function setSpecificActionRight($rootDirId, $actionName, $allowed) - { - if(!isSet($this->rights["ajxp.actions"])) $this->rights["ajxp.actions"] = array(); - if(!isset($this->rights["ajxp.actions"][$rootDirId])) $this->rights["ajxp.actions"][$rootDirId] = array(); - $this->rights["ajxp.actions"][$rootDirId][$actionName] = $allowed; - } - - public function setDefault($default) - { - $this->default = $default; - } - - public function isDefault() - { - return $this->default; - } - - /** - * @param String $groupPath - */ - public function setGroupPath($groupPath) - { - $this->groupPath = $groupPath; - } - - /** - * @return String - */ - public function getGroupPath() - { - return $this->groupPath; - } - -} diff --git a/core/src/core/classes/class.AuthService.php b/core/src/core/classes/class.AuthService.php deleted file mode 100644 index d84d4a9fdc..0000000000 --- a/core/src/core/classes/class.AuthService.php +++ /dev/null @@ -1,1331 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Static access to the authentication mechanism. Encapsulates the authDriver implementation - * @package Pydio - * @subpackage Core - */ -class AuthService -{ - public static $cacheRoles = false; - public static $roles; - public static $useSession = true; - private static $currentUser; - public static $bufferedMessage = null; - /** - * Whether the whole users management system is enabled or not. - * @static - * @return bool - */ - public static function usersEnabled() - { - return ConfService::getCoreConf("ENABLE_USERS", "auth"); - } - /** - * Whether the current auth driver supports password update or not - * @static - * @return bool - */ - public static function changePasswordEnabled() - { - $authDriver = ConfService::getAuthDriverImpl(); - return $authDriver->passwordsEditable(); - } - /** - * Get a unique seed from the current auth driver - * @static - * @return int|string - */ - public static function generateSeed() - { - $authDriver = ConfService::getAuthDriverImpl(); - return $authDriver->getSeed(true); - } - - /** - * Put a secure token in the session - * @static - * @return string - */ - public static function generateSecureToken() - { - if(!isSet($_SESSION["SECURE_TOKENS"])){ - $_SESSION["SECURE_TOKENS"] = array(); - } - if(isSet($_SESSION["FORCE_SECURE_TOKEN"])){ - $_SESSION["SECURE_TOKENS"][] = $_SESSION["FORCE_SECURE_TOKEN"]; - return $_SESSION["FORCE_SECURE_TOKEN"]; - } - $newToken = AJXP_Utils::generateRandomString(32); //md5(time()); - $_SESSION["SECURE_TOKENS"][] = $newToken; - return $newToken; - } - /** - * Get the secure token from the session - * @static - * @return string|bool - */ - public static function getSecureToken() - { - if(isSet($_SESSION["SECURE_TOKENS"]) && count($_SESSION["SECURE_TOKENS"])){ - return true; - } - return false; - //return (isSet($_SESSION["SECURE_TOKENS"])?$_SESSION["SECURE_TOKEN"]:FALSE); - } - /** - * Verify a secure token value from the session - * @static - * @param string $token - * @return bool - */ - public static function checkSecureToken($token) - { - if (isSet($_SESSION["SECURE_TOKENS"]) && in_array($token, $_SESSION["SECURE_TOKENS"])) { - return true; - } - return false; - } - - /** - * Get the currently logged user object - * @return AbstractAjxpUser - */ - public static function getLoggedUser() - { - if (self::$useSession && isSet($_SESSION["AJXP_USER"])) { - if (is_a($_SESSION["AJXP_USER"], "__PHP_Incomplete_Class")) { - session_unset(); - return null; - } - return $_SESSION["AJXP_USER"]; - } - if(!self::$useSession && isSet(self::$currentUser)) return self::$currentUser; - return null; - } - /** - * Call the preLogUser() functino on the auth driver implementation - * @static - * @param Array $httpVars - * @return void - */ - public static function preLogUser($httpVars) - { - if(self::getLoggedUser() != null && self::getLoggedUser()->getId() != "guest") return ; - - $frontends = AJXP_PluginsService::getInstance()->getActivePluginsForType("authfront"); - $index = 0; - /** - * @var AbstractAuthFrontend $frontendPlugin - */ - foreach($frontends as $frontendPlugin){ - if(!$frontendPlugin->isEnabled()) continue; - if(!method_exists($frontendPlugin, "tryToLogUser")){ - AJXP_Logger::error(__CLASS__, __FUNCTION__, "Trying to use an authfront plugin without tryToLogUser method. Wrongly initialized?"); - continue; - } - $res = $frontendPlugin->tryToLogUser($httpVars, ($index == count($frontends)-1)); - $index ++; - if($res) break; - } - // Keep old-fashioned test, should be removed - $authDriver = ConfService::getAuthDriverImpl(); - $authDriver->preLogUser((isSet($httpVars["remote_session"])?$httpVars["remote_session"]:"")); - - return ; - } - /** - * The array is located in the AjxpTmpDir/failedAJXP.log - * @static - * @return array - */ - public static function getBruteForceLoginArray() - { - $failedLog = AJXP_Utils::getAjxpTmpDir()."/failedAJXP.log"; - $loginAttempt = @file_get_contents($failedLog); - $loginArray = unserialize($loginAttempt); - $ret = array(); - $curTime = time(); - if (is_array($loginArray)) { - // Filter the array (all old time are cleaned) - foreach ($loginArray as $key => $login) { - if (($curTime - $login["time"]) <= 60 * 60 * 24) $ret[$key] = $login; - } - } - return $ret; - } - /** - * Store the array - * @static - * @param $loginArray - * @return void - */ - public static function setBruteForceLoginArray($loginArray) - { - $failedLog = AJXP_Utils::getAjxpTmpDir()."/failedAJXP.log"; - @file_put_contents($failedLog, serialize($loginArray)); - } - /** - * Determines whether the user is try to make many attemps - * @static - * @param $loginArray - * @return bool - */ - public static function checkBruteForceLogin(&$loginArray) - { - if (isSet($_SERVER['REMOTE_ADDR'])) { - $serverAddress = $_SERVER['REMOTE_ADDR']; - } else if(isSet($_SERVER['SERVER_ADDR'])) { - $serverAddress = $_SERVER['SERVER_ADDR']; - } else { - return TRUE; - } - $login = null; - if (isSet($loginArray[$serverAddress])) { - $login = $loginArray[$serverAddress]; - } - if (is_array($login)) { - $login["count"]++; - } else $login = array("count"=>1, "time"=>time()); - $loginArray[$serverAddress] = $login; - if ($login["count"] > 3) { - if (AJXP_SERVER_DEBUG || ConfService::getCoreConf("DISABLE_BRUTE_FORCE_CHECK", "auth") === true) { - AJXP_Logger::debug("Warning: failed login 3 time from address $serverAddress, but ignored because captcha is disabled."); - return true; - } - return FALSE; - } - return TRUE; - } - /** - * Is there a brute force login attempt? - * @static - * @return bool - */ - public static function suspectBruteForceLogin() - { - $loginAttempt = self::getBruteForceLoginArray(); - return !self::checkBruteForceLogin($loginAttempt); - } - - public static function filterUserSensitivity($user) - { - if (!ConfService::getCoreConf("CASE_SENSITIVE", "auth")) { - return strtolower($user); - } else { - return $user; - } - } - - public static function ignoreUserCase() - { - return !ConfService::getCoreConf("CASE_SENSITIVE", "auth"); - } - - /** - * @static - * @param AbstractAjxpUser $user - */ - public static function refreshRememberCookie($user) - { - $current = $_COOKIE["AjaXplorer-remember"]; - if (!empty($current)) { - $user->invalidateCookieString(substr($current, strpos($current, ":")+1)); - } - $rememberPass = $user->getCookieString(); - setcookie("AjaXplorer-remember", $user->id.":".$rememberPass, time()+3600*24*10, null, null, (isSet($_SERVER["HTTPS"]) && strtolower($_SERVER["HTTPS"]) == "on"), true); - } - - /** - * @static - * @return bool - */ - public static function hasRememberCookie() - { - return (isSet($_COOKIE["AjaXplorer-remember"]) && !empty($_COOKIE["AjaXplorer-remember"])); - } - - /** - * @static - * Warning, must be called before sending other headers! - */ - public static function clearRememberCookie() - { - $current = $_COOKIE["AjaXplorer-remember"]; - $user = self::getLoggedUser(); - if (!empty($current) && $user != null) { - $user->invalidateCookieString(substr($current, strpos($current, ":")+1)); - } - setcookie("AjaXplorer-remember", "", time()-3600, null, null, (isSet($_SERVER["HTTPS"]) && strtolower($_SERVER["HTTPS"]) == "on"), true); - } - - public static function logTemporaryUser($parentUserId, $temporaryUserId) - { - $parentUserId = self::filterUserSensitivity($parentUserId); - $temporaryUserId = self::filterUserSensitivity($temporaryUserId); - $confDriver = ConfService::getConfStorageImpl(); - $parentUser = $confDriver->createUserObject($parentUserId); - $temporaryUser = $confDriver->createUserObject($temporaryUserId); - $temporaryUser->mergedRole = $parentUser->mergedRole; - $temporaryUser->rights = $parentUser->rights; - $temporaryUser->setGroupPath($parentUser->getGroupPath()); - $temporaryUser->setParent($parentUserId); - $temporaryUser->setResolveAsParent(true); - AJXP_Logger::info(__CLASS__,"Log in", array("temporary user" => $temporaryUserId, "owner" => $parentUserId)); - self::updateUser($temporaryUser); - } - - public static function clearTemporaryUser($temporaryUserId) - { - AJXP_Logger::info(__CLASS__,"Log out", array("temporary user" => $temporaryUserId)); - if (isSet($_SESSION["AJXP_USER"]) || isSet(self::$currentUser)) { - AJXP_Logger::info(__CLASS__, "Log Out", ""); - unset($_SESSION["AJXP_USER"]); - if(isSet(self::$currentUser)) unset(self::$currentUser); - if (ConfService::getCoreConf("SESSION_SET_CREDENTIALS", "auth")) { - AJXP_Safe::clearCredentials(); - } - } - } - - /** - * Log the user from its credentials - * @static - * @param string $user_id The user id - * @param string $pwd The password - * @param bool $bypass_pwd Ignore password or not - * @param bool $cookieLogin Is it a logging from the remember me cookie? - * @param string $returnSeed The unique seed - * @return int - */ - public static function logUser($user_id, $pwd, $bypass_pwd = false, $cookieLogin = false, $returnSeed="") - { - $user_id = self::filterUserSensitivity($user_id); - if ($cookieLogin && !isSet($_COOKIE["AjaXplorer-remember"])) { - return -5; // SILENT IGNORE - } - if ($cookieLogin) { - list($user_id, $pwd) = explode(":", $_COOKIE["AjaXplorer-remember"]); - } - $confDriver = ConfService::getConfStorageImpl(); - if ($user_id == null) { - if (self::$useSession) { - if(isSet($_SESSION["AJXP_USER"]) && is_object($_SESSION["AJXP_USER"])) { - /** - * @var AbstractAjxpUser $u - */ - $u = $_SESSION["AJXP_USER"]; - if($u->reloadRolesIfRequired()){ - ConfService::getInstance()->invalidateLoadedRepositories(); - self::$bufferedMessage = AJXP_XMLWriter::reloadRepositoryList(false); - $_SESSION["AJXP_USER"] = $u; - } - return 1; - } - } else { - if(isSet(self::$currentUser) && is_object(self::$currentUser)) return 1; - } - if (ConfService::getCoreConf("ALLOW_GUEST_BROWSING", "auth") && !isSet($_SESSION["CURRENT_MINISITE"])) { - $authDriver = ConfService::getAuthDriverImpl(); - if (!$authDriver->userExists("guest")) { - self::createUser("guest", ""); - $guest = $confDriver->createUserObject("guest"); - $guest->save("superuser"); - } - self::logUser("guest", null); - return 1; - } - return -1; - } - $authDriver = ConfService::getAuthDriverImpl(); - // CHECK USER PASSWORD HERE! - $loginAttempt = self::getBruteForceLoginArray(); - $bruteForceLogin = self::checkBruteForceLogin($loginAttempt); - self::setBruteForceLoginArray($loginAttempt); - - if (!$authDriver->userExists($user_id)) { - AJXP_Logger::warning(__CLASS__, "Login failed", array("user" => AJXP_Utils::sanitize($user_id, AJXP_SANITIZE_EMAILCHARS), "error" => "Invalid user")); - if ($bruteForceLogin === FALSE) { - return -4; - } else { - return -1; - } - } - if (!$bypass_pwd) { - if (!self::checkPassword($user_id, $pwd, $cookieLogin, $returnSeed)) { - AJXP_Logger::warning(__CLASS__, "Login failed", array("user" => AJXP_Utils::sanitize($user_id, AJXP_SANITIZE_EMAILCHARS), "error" => "Invalid password")); - if ($bruteForceLogin === FALSE) { - return -4; - } else { - if($cookieLogin) return -5; - return -1; - } - } - } - // Successful login attempt - unset($loginAttempt[$_SERVER["REMOTE_ADDR"]]); - self::setBruteForceLoginArray($loginAttempt); - - // Setting session credentials if asked in config - if (ConfService::getCoreConf("SESSION_SET_CREDENTIALS", "auth")) { - list($authId, $authPwd) = $authDriver->filterCredentials($user_id, $pwd); - AJXP_Safe::storeCredentials($authId, $authPwd); - } - - $user = $confDriver->createUserObject($user_id); - if ($user->getLock() == "logout") { - AJXP_Logger::warning(__CLASS__, "Login failed", array("user" => AJXP_Utils::sanitize($user_id, AJXP_SANITIZE_EMAILCHARS), "error" => "Locked user")); - return -1; - } - - if(AuthService::$useSession && ConfService::getCoreConf("ALLOW_GUEST_BROWSING", "auth")){ - ConfService::getInstance()->invalidateLoadedRepositories(); - } - - if ($authDriver->isAjxpAdmin($user_id)) { - $user->setAdmin(true); - } - if(self::$useSession) $_SESSION["AJXP_USER"] = $user; - else self::$currentUser = $user; - - if ($user->isAdmin()) { - $user = self::updateAdminRights($user); - self::updateUser($user); - } - - if ($authDriver->autoCreateUser() && !$user->storageExists()) { - $user->save("superuser"); // make sure update rights now - } - AJXP_Logger::info(__CLASS__, "Log In", array("context"=>self::$useSession?"WebUI":"API")); - return 1; - } - /** - * Store the object in the session - * @static - * @param $userObject - * @return void - */ - public static function updateUser($userObject) - { - if(self::$useSession) $_SESSION["AJXP_USER"] = $userObject; - else self::$currentUser = $userObject; - } - /** - * Clear the session - * @static - * @return void - */ - public static function disconnect() - { - if (isSet($_SESSION["AJXP_USER"]) || isSet(self::$currentUser)) { - $user = isSet($_SESSION["AJXP_USER"]) ? $_SESSION["AJXP_USER"] : self::$currentUser; - $userId = $user->id; - AJXP_Controller::applyHook("user.before_disconnect", array($user)); - self::clearRememberCookie(); - AJXP_Logger::info(__CLASS__, "Log Out", ""); - unset($_SESSION["AJXP_USER"]); - if(isSet(self::$currentUser)) unset(self::$currentUser); - if (ConfService::getCoreConf("SESSION_SET_CREDENTIALS", "auth")) { - AJXP_Safe::clearCredentials(); - } - AJXP_Controller::applyHook("user.after_disconnect", array($userId)); - } - } - /** - * Specific operations to perform at boot time - * @static - * @param array $START_PARAMETERS A HashTable of parameters to send back to the client - * @return void - */ - public static function bootSequence(&$START_PARAMETERS) - { - if(AJXP_Utils::detectApplicationFirstRun()) return; - if(file_exists(AJXP_CACHE_DIR."/admin_counted")) return; - $rootRole = self::getRole("AJXP_GRP_/", false); - if ($rootRole === false) { - $rootRole = new AJXP_Role("AJXP_GRP_/"); - $rootRole->setLabel("Root Group"); - //$rootRole->setAutoApplies(array("standard", "admin")); - //$dashId = ""; - $allRepos = ConfService::getRepositoriesList("all", false); - foreach ($allRepos as $repositoryId => $repoObject) { - if($repoObject->isTemplate) continue; - //if($repoObject->getAccessType() == "ajxp_user") $dashId = $repositoryId; - $gp = $repoObject->getGroupPath(); - if (empty($gp) || $gp == "/") { - if ($repoObject->getDefaultRight() != "") { - $rootRole->setAcl($repositoryId, $repoObject->getDefaultRight()); - } - } - } - //if(!empty($dashId)) $rootRole->setParameterValue("core.conf", "DEFAULT_START_REPOSITORY", $dashId); - $paramNodes = AJXP_PluginsService::searchAllManifests("//server_settings/param[@scope]", "node", false, false, true); - if (is_array($paramNodes) && count($paramNodes)) { - foreach ($paramNodes as $xmlNode) { - $default = $xmlNode->getAttribute("default"); - if(empty($default)) continue; - $parentNode = $xmlNode->parentNode->parentNode; - $pluginId = $parentNode->getAttribute("id"); - if (empty($pluginId)) { - $pluginId = $parentNode->nodeName.".".$parentNode->getAttribute("name"); - } - $rootRole->setParameterValue($pluginId, $xmlNode->getAttribute("name"), $default); - } - } - self::updateRole($rootRole); - } - $miniRole = self::getRole("MINISITE", false); - if ($miniRole === false) { - $rootRole = new AJXP_Role("MINISITE"); - $rootRole->setLabel("Minisite Users"); - $actions = array( - "access.fs" => array("ajxp_link", "chmod", "purge"), - "meta.watch" => array("toggle_watch"), - "conf.serial"=> array("get_bookmarks"), - "conf.sql"=> array("get_bookmarks"), - "index.lucene" => array("index"), - "action.share" => array("share", "share-edit-shared", "share-folder-workspace", "share-file-minisite", "share-selection-minisite", "share-folder-minisite-public"), - "gui.ajax" => array("bookmark"), - "auth.serial" => array("pass_change"), - "auth.sql" => array("pass_change"), - ); - foreach ($actions as $pluginId => $acts) { - foreach ($acts as $act) { - $rootRole->setActionState($pluginId, $act, AJXP_REPO_SCOPE_SHARED, false); - } - } - self::updateRole($rootRole); - } - $miniRole = self::getRole("MINISITE_NODOWNLOAD", false); - if ($miniRole === false) { - $rootRole = new AJXP_Role("MINISITE_NODOWNLOAD"); - $rootRole->setLabel("Minisite Users - No Download"); - $actions = array( - "access.fs" => array("download", "download_chunk", "prepare_chunk_dl", "download_all") - ); - foreach ($actions as $pluginId => $acts) { - foreach ($acts as $act) { - $rootRole->setActionState($pluginId, $act, AJXP_REPO_SCOPE_SHARED, false); - } - } - self::updateRole($rootRole); - } - $miniRole = self::getRole("GUEST", false); - if ($miniRole === false) { - $rootRole = new AJXP_Role("GUEST"); - $rootRole->setLabel("Guest user role"); - $actions = array( - "access.fs" => array("purge"), - "meta.watch" => array("toggle_watch"), - "index.lucene" => array("index"), - ); - $rootRole->setAutoApplies(array("guest")); - foreach ($actions as $pluginId => $acts) { - foreach ($acts as $act) { - $rootRole->setActionState($pluginId, $act, AJXP_REPO_SCOPE_ALL); - } - } - self::updateRole($rootRole); - } - $adminCount = self::countAdminUsers(); - if ($adminCount == 0) { - $authDriver = ConfService::getAuthDriverImpl(); - $adminPass = ADMIN_PASSWORD; - if (!$authDriver->getOptionAsBool("TRANSMIT_CLEAR_PASS")) { - $adminPass = md5(ADMIN_PASSWORD); - } - self::createUser("admin", $adminPass, true); - if (ADMIN_PASSWORD == INITIAL_ADMIN_PASSWORD) { - $userObject = ConfService::getConfStorageImpl()->createUserObject("admin"); - $userObject->setAdmin(true); - self::updateAdminRights($userObject); - if (self::changePasswordEnabled()) { - $userObject->setLock("pass_change"); - } - $userObject->save("superuser"); - $START_PARAMETERS["ALERT"] .= "Warning! User 'admin' was created with the initial password '". INITIAL_ADMIN_PASSWORD ."'. \\nPlease log in as admin and change the password now!"; - self::updateUser($userObject); - } - } else if ($adminCount == -1) { - // Here we may come from a previous version! Check the "admin" user and set its right as admin. - $confStorage = ConfService::getConfStorageImpl(); - $adminUser = $confStorage->createUserObject("admin"); - $adminUser->setAdmin(true); - $adminUser->save("superuser"); - $START_PARAMETERS["ALERT"] .= "There is an admin user, but without admin right. Now any user can have the administration rights, \\n your 'admin' user was set with the admin rights. Please check that this suits your security configuration."; - } - file_put_contents(AJXP_CACHE_DIR."/admin_counted", "true"); - - } - /** - * If the auth driver implementatino has a logout redirect URL, clear session and return it. - * @static - * @param bool $logUserOut - * @return bool - */ - public static function getLogoutAddress($logUserOut = true) - { - $authDriver = ConfService::getAuthDriverImpl(); - $logout = $authDriver->getLogoutRedirect(); - if ($logUserOut) { - self::disconnect(); - } - return $logout; - } - /** - * Compute the default repository id to log the current user - * @static - * @return int|string - */ - public static function getDefaultRootId() - { - $loggedUser = self::getLoggedUser(); - if($loggedUser == null) return 0; - $acls = $loggedUser->mergedRole->listAcls(true); - foreach($acls as $key => $right){ - if (!empty($right) && ConfService::getRepositoryById($key) != null) return $key; - } - return 0; - } - - /** - * Update a user with admin rights and return it - * @param AbstractAjxpUser $adminUser - * @return AbstractAjxpUser - */ - public static function updateAdminRights($adminUser) - { - if($adminUser->personalRole->getAcl('ajxp_conf') != "rw"){ - $adminUser->personalRole->setAcl('ajxp_conf', 'rw'); - $adminUser->recomputeMergedRole(); - $adminUser->save("superuser"); - } - return $adminUser; - } - - /** - * Update a user object with the default repositories rights - * - * @param AbstractAjxpUser $userObject - */ - public static function updateDefaultRights(&$userObject) - { - if (!$userObject->hasParent()) { - /* - $changes = false; - $repoList = ConfService::getRepositoriesList("all"); - foreach ($repoList as $repositoryId => $repoObject) { - if(!self::allowedForCurrentGroup($repoObject, $userObject)) continue; - if($repoObject->isTemplate) continue; - if ($repoObject->getDefaultRight() != "") { - $changes = true; - $userObject->personalRole->setAcl($repositoryId, $repoObject->getDefaultRight()); - } - } - if ($changes) { - $userObject->recomputeMergedRole(); - } - */ - $rolesList = self::getRolesList(array(), true); - foreach ($rolesList as $roleId => $roleObject) { - if(!self::allowedForCurrentGroup($roleObject, $userObject)) continue; - if ($userObject->getProfile() == "shared" && $roleObject->autoAppliesTo("shared")) { - $userObject->addRole($roleObject); - } else if ($roleObject->autoAppliesTo("standard")) { - $userObject->addRole($roleObject); - } - } - } - } - - /** - * @static - * @param AbstractAjxpUser $userObject - */ - public static function updateAutoApplyRole(&$userObject) - { - $roles = self::getRolesList(array(), true); - foreach ($roles as $roleObject) { - if(!self::allowedForCurrentGroup($roleObject, $userObject)) continue; - if ($roleObject->autoAppliesTo($userObject->getProfile()) || $roleObject->autoAppliesTo("all")) { - $userObject->addRole($roleObject); - } - } - } - - public static function updateAuthProvidedData(&$userObject) - { - ConfService::getAuthDriverImpl()->updateUserObject($userObject); - } - - /** - * Use driver implementation to check whether the user exists or not. - * @static - * @param String $userId - * @param String $mode "r" or "w" - * @return bool - */ - public static function userExists($userId, $mode = "r") - { - if ($userId == "guest" && !ConfService::getCoreConf("ALLOW_GUEST_BROWSING", "auth")) { - return false; - } - $userId = self::filterUserSensitivity($userId); - $authDriver = ConfService::getAuthDriverImpl(); - if ($mode == "w") { - return $authDriver->userExistsWrite($userId); - } - return $authDriver->userExists($userId); - } - - /** - * Make sure a user id is not reserved for low-level tasks (currently "guest" and "shared"). - * @static - * @param String $username - * @return bool - */ - public static function isReservedUserId($username) - { - $username = self::filterUserSensitivity($username); - return in_array($username, array("guest", "shared")); - } - - /** - * Simple password encoding, should be deported in a more complex/configurable function - * @static - * @param $pass - * @return string - */ - public static function encodePassword($pass) - { - return md5($pass); - } - /** - * Check a password - * @static - * @param $userId - * @param $userPass - * @param bool $cookieString - * @param string $returnSeed - * @return bool|void - */ - public static function checkPassword($userId, $userPass, $cookieString = false, $returnSeed = "") - { - if(ConfService::getCoreConf("ALLOW_GUEST_BROWSING", "auth") && $userId == "guest") return true; - $userId = self::filterUserSensitivity($userId); - $authDriver = ConfService::getAuthDriverImpl(); - if ($cookieString) { - $confDriver = ConfService::getConfStorageImpl(); - $userObject = $confDriver->createUserObject($userId); - $res = $userObject->checkCookieString($userPass); - return $res; - } - if(!$authDriver->getOptionAsBool("TRANSMIT_CLEAR_PASS")){ - if($authDriver->getSeed(false) != $returnSeed) return false; - } - return $authDriver->checkPassword($userId, $userPass, $returnSeed); - } - /** - * Update the password in the auth driver implementation. - * @static - * @throws Exception - * @param $userId - * @param $userPass - * @return bool - */ - public static function updatePassword($userId, $userPass) - { - if (strlen($userPass) < ConfService::getCoreConf("PASSWORD_MINLENGTH", "auth")) { - $messages = ConfService::getMessages(); - throw new Exception($messages[378]); - } - $userId = self::filterUserSensitivity($userId); - $authDriver = ConfService::getAuthDriverImpl(); - AJXP_Controller::applyHook("user.before_password_change", array($userId)); - $authDriver->changePassword($userId, $userPass); - AJXP_Controller::applyHook("user.after_password_change", array($userId)); - if ($authDriver->getOptionAsBool("TRANSMIT_CLEAR_PASS")) { - // We can directly update the HA1 version of the WEBDAV Digest - $realm = ConfService::getCoreConf("WEBDAV_DIGESTREALM"); - $ha1 = md5("{$userId}:{$realm}:{$userPass}"); - $zObj = ConfService::getConfStorageImpl()->createUserObject($userId); - $wData = $zObj->getPref("AJXP_WEBDAV_DATA"); - if(!is_array($wData)) $wData = array(); - $wData["HA1"] = $ha1; - $zObj->setPref("AJXP_WEBDAV_DATA", $wData); - $zObj->save(); - } - AJXP_Logger::info(__CLASS__,"Update Password", array("user_id"=>$userId)); - return true; - } - /** - * Create a user - * @static - * @throws Exception - * @param $userId - * @param $userPass - * @param bool $isAdmin - * @param bool $isHidden - * @return null - * @todo the minlength check is probably causing problem with the bridges - */ - public static function createUser($userId, $userPass, $isAdmin=false, $isHidden=false) - { - $userId = self::filterUserSensitivity($userId); - AJXP_Controller::applyHook("user.before_create", array($userId, $userPass, $isAdmin, $isHidden)); - if (!ConfService::getCoreConf("ALLOW_GUEST_BROWSING", "auth") && $userId == "guest") { - throw new Exception("Reserved user id"); - } - /* - if (strlen($userPass) < ConfService::getCoreConf("PASSWORD_MINLENGTH", "auth") && $userId != "guest") { - $messages = ConfService::getMessages(); - throw new Exception($messages[378]); - } - */ - $authDriver = ConfService::getAuthDriverImpl(); - $confDriver = ConfService::getConfStorageImpl(); - $authDriver->createUser($userId, $userPass); - $user = $confDriver->createUserObject($userId); - if ($isAdmin) { - $user->setAdmin(true); - $user->save("superuser"); - } - if($isHidden){ - $user->setHidden(true); - $user->save("superuser"); - } - if ($authDriver->getOptionAsBool("TRANSMIT_CLEAR_PASS")) { - $realm = ConfService::getCoreConf("WEBDAV_DIGESTREALM"); - $ha1 = md5("{$userId}:{$realm}:{$userPass}"); - $wData = $user->getPref("AJXP_WEBDAV_DATA"); - if(!is_array($wData)) $wData = array(); - $wData["HA1"] = $ha1; - $user->setPref("AJXP_WEBDAV_DATA", $wData); - $user->save(); - } - AJXP_Controller::applyHook("user.after_create", array($user)); - AJXP_Logger::info(__CLASS__,"Create User", array("user_id"=>$userId)); - return null; - } - /** - * Detect the number of admin users - * @static - * @return int|void - */ - public static function countAdminUsers() - { - $confDriver = ConfService::getConfStorageImpl(); - $auth = ConfService::getAuthDriverImpl(); - $count = $confDriver->countAdminUsers(); - if (!$count && $auth->userExists("admin") && $confDriver->getName() == "serial") { - return -1; - } - return $count; - } - - /** - * Delete a user in the auth/conf driver impl - * @static - * @param $userId - * @return bool - */ - public static function deleteUser($userId) - { - AJXP_Controller::applyHook("user.before_delete", array($userId)); - $userId = self::filterUserSensitivity($userId); - $authDriver = ConfService::getAuthDriverImpl(); - $authDriver->deleteUser($userId); - $subUsers = array(); - ConfService::getConfStorageImpl()->deleteUser($userId, $subUsers); - foreach ($subUsers as $deletedUser) { - $authDriver->deleteUser($deletedUser); - } - AJXP_Controller::applyHook("user.after_delete", array($userId)); - AJXP_Logger::info(__CLASS__,"Delete User", array("user_id"=>$userId, "sub_user" => implode(",", $subUsers))); - return true; - } - - private static $groupFiltering = true; - - /** - * @param boolean $boolean - */ - public static function setGroupFiltering($boolean){ - self::$groupFiltering = $boolean; - } - - /** - * Automatically set the group to the current user base - * @param $baseGroup - * @return string - */ - public static function filterBaseGroup($baseGroup) - { - if(!self::$groupFiltering) { - return $baseGroup; - } - - $u = self::getLoggedUser(); - // make sure it starts with a slash. - $baseGroup = "/".ltrim($baseGroup, "/"); - if($u == null) return $baseGroup; - if ($u->getGroupPath() != "/") { - if($baseGroup == "/") return $u->getGroupPath(); - else return $u->getGroupPath().$baseGroup; - } else { - return $baseGroup; - } - } - - /** - * List children groups of current base - * @param string $baseGroup - * @return string[] - */ - public static function listChildrenGroups($baseGroup = "/") - { - return ConfService::getAuthDriverImpl()->listChildrenGroups(self::filterBaseGroup($baseGroup)); - - } - - /** - * Create a new group at the given path - * - * @param $baseGroup - * @param $groupName - * @param $groupLabel - * @throws Exception - */ - public static function createGroup($baseGroup, $groupName, $groupLabel) - { - if(empty($groupName)) throw new Exception("Please provide a name for this new group!"); - $fullGroupPath = rtrim(self::filterBaseGroup($baseGroup), "/")."/".$groupName; - $exists = ConfService::getConfStorageImpl()->groupExists($fullGroupPath); - if($exists){ - throw new Exception("Group with this name already exists, please pick another name!"); - } - if(empty($groupLabel)) $groupLabel = $groupName; - ConfService::getConfStorageImpl()->createGroup(rtrim(self::filterBaseGroup($baseGroup), "/")."/".$groupName, $groupLabel); - } - - /** - * Delete group by name - * @param $baseGroup - * @param $groupName - */ - public static function deleteGroup($baseGroup, $groupName) - { - ConfService::getConfStorageImpl()->deleteGroup(rtrim(self::filterBaseGroup($baseGroup), "/")."/".$groupName); - } - - /** - * Count the number of children a given user has already created - * @param $parentUserId - * @return AbstractAjxpUser[] - */ - public static function getChildrenUsers($parentUserId) - { - return ConfService::getConfStorageImpl()->getUserChildren($parentUserId); - } - - /** - * Retrieve the current users who have either read or write access to a repository - * @param $repositoryId - * @return array - */ - public static function getUsersForRepository($repositoryId) - { - return ConfService::getConfStorageImpl()->getUsersForRepository($repositoryId); - } - - /** - * Retrieve the current users who have either read or write access to a repository - * @param $repositoryId - * @param string $rolePrefix - * @param bool $splitByType - * @return array - */ - public static function getRolesForRepository($repositoryId, $rolePrefix = '', $splitByType = false) - { - return ConfService::getConfStorageImpl()->getRolesForRepository($repositoryId, $rolePrefix, $splitByType); - } - - /** - * Count the number of users who have either read or write access to a repository - * @param $repositoryId - * @param bool $details - * @param bool $admin True if called in an admin context - * @return Array|int - */ - public static function countUsersForRepository($repositoryId, $details = false, $admin = false) - { - return ConfService::getConfStorageImpl()->countUsersForRepository($repositoryId, $details, $admin); - } - - /** - * @static - * @param string $baseGroup - * @param null $regexp - * @param $offset - * @param $limit - * @param bool $cleanLosts - * @param bool $recursive - * @param null $countCallback - * @param null $loopCallback - * @return AbstractAjxpUser[] - */ - public static function listUsers($baseGroup = "/", $regexp = null, $offset = -1, $limit = -1, $cleanLosts = true, $recursive = true, $countCallback = null, $loopCallback = null) - { - $baseGroup = self::filterBaseGroup($baseGroup); - $authDriver = ConfService::getAuthDriverImpl(); - $confDriver = ConfService::getConfStorageImpl(); - /** - * @var $allUsers AbstractAjxpUser[] - */ - $allUsers = array(); - $paginated = false; - if (($regexp != null || $offset != -1 || $limit != -1) && $authDriver->supportsUsersPagination()) { - $users = $authDriver->listUsersPaginated($baseGroup, $regexp, $offset, $limit, $recursive); - $paginated = ($offset != -1 || $limit != -1); - } else { - $users = $authDriver->listUsers($baseGroup); - } - $index = 0; - - // Callback func for display progression on cli mode - if($countCallback != null){ - call_user_func($countCallback, $index, count($users), "Update users"); - } - - self::$cacheRoles = true; - self::$roles = null; - foreach (array_keys($users) as $userId) { - if(($userId == "guest" && !ConfService::getCoreConf("ALLOW_GUEST_BROWSING", "auth")) || $userId == "ajxp.admin.users" || $userId == "") continue; - if($regexp != null && !$authDriver->supportsUsersPagination() && !preg_match("/$regexp/i", $userId)) continue; - $allUsers[$userId] = $confDriver->createUserObject($userId); - $index++; - - // Callback func for display progression on cli mode - if($countCallback != null){ - call_user_func($loopCallback, $index); - } - - if (empty($regexp) && $paginated) { - // Make sure to reload all children objects - foreach ($confDriver->getUserChildren($userId) as $childObject) { - $allUsers[$childObject->getId()] = $childObject; - } - } - } - self::$cacheRoles = false; - - if (empty($regexp) && $paginated && $cleanLosts) { - // Remove 'lost' items (children without parents). - foreach ($allUsers as $id => $object) { - if ($object->hasParent() && !array_key_exists($object->getParent(), $allUsers)) { - unset($allUsers[$id]); - } - } - } - return $allUsers; - } - - /** - * Depending on the plugin, tried to compute the actual page where a given user can be located - * - * @param $baseGroup - * @param $userLogin - * @param $usersPerPage - * @param int $offset - * @return int - */ - public static function findUserPage($baseGroup, $userLogin, $usersPerPage, $offset = 0){ - if(ConfService::getAuthDriverImpl()->supportsUsersPagination()){ - return ConfService::getAuthDriverImpl()->findUserPage($baseGroup, $userLogin, $usersPerPage, $offset); - }else{ - return -1; - } - } - - /** - * Whether the current auth driver supports paginated listing - * - * @return bool - */ - public static function authSupportsPagination() - { - $authDriver = ConfService::getAuthDriverImpl(); - return $authDriver->supportsUsersPagination(); - } - - - /** - * Count the total number of users inside a group (recursive). - * Regexp can be used to limit the users IDs with a specific expression - * Property can be used for basic filtering, either on "parent" or "admin". - * - * @param string $baseGroup - * @param string $regexp - * @param null $filterProperty Can be "parent" or "admin" - * @param null $filterValue Can be a string, or constants AJXP_FILTER_EMPTY / AJXP_FILTER_NOT_EMPTY - * @param bool $recursive - * @return int - */ - public static function authCountUsers($baseGroup="/", $regexp="", $filterProperty = null, $filterValue = null, $recursive = true) - { - $authDriver = ConfService::getAuthDriverImpl(); - return $authDriver->getUsersCount(self::filterBaseGroup($baseGroup), $regexp, $filterProperty, $filterValue, $recursive); - } - - /** - * Makes a correspondance between a user and its auth scheme, for multi auth - * @param $userName - * @return String - */ - public static function getAuthScheme($userName) - { - $authDriver = ConfService::getAuthDriverImpl(); - return $authDriver->getAuthScheme($userName); - } - - /** - * Check if auth implementation supports schemes detection - * @return bool - */ - public static function driverSupportsAuthSchemes() - { - $authDriver = ConfService::getAuthDriverImpl(); - return $authDriver->supportsAuthSchemes(); - } - - /** - * Get Role by Id - * - * @param string $roleId - * @param boolean $createIfNotExists - * @return AJXP_Role - */ - public static function getRole($roleId, $createIfNotExists = false) - { - $roles = self::getRolesList(array($roleId)); - if(isSet($roles[$roleId])) return $roles[$roleId]; - if ($createIfNotExists) { - $role = new AJXP_Role($roleId); - if (self::getLoggedUser()!=null && self::getLoggedUser()->getGroupPath()!=null) { - $role->setGroupPath(self::getLoggedUser()->getGroupPath()); - } - self::updateRole($role); - return $role; - } - return false; - } - - /** - * Create or update role - * - * @param AJXP_Role $roleObject - * @param null $userObject - */ - public static function updateRole($roleObject, $userObject = null) - { - ConfService::getConfStorageImpl()->updateRole($roleObject, $userObject); - //CacheService::deleteAll(AJXP_CACHE_SERVICE_NS_SHARED); - ConfService::getInstance()->invalidateLoadedRepositories(); - } - /** - * Delete a role by its id - * @static - * @param string $roleId - * @return void - */ - public static function deleteRole($roleId) - { - ConfService::getConfStorageImpl()->deleteRole($roleId); - //CacheService::deleteAll(AJXP_CACHE_SERVICE_NS_SHARED); - ConfService::getInstance()->invalidateLoadedRepositories(); - } - - public static function filterPluginParameters($pluginId, $params, $repoId = null) - { - $logged = self::getLoggedUser(); - if($logged == null) return $params; - if ($repoId == null) { - $repo = ConfService::getRepository(); - if($repo!=null) $repoId = $repo->getId(); - } - if($logged == null || $logged->mergedRole == null) return $params; - $roleParams = $logged->mergedRole->listParameters(); - if (iSSet($roleParams[AJXP_REPO_SCOPE_ALL][$pluginId])) { - $params = array_merge($params, $roleParams[AJXP_REPO_SCOPE_ALL][$pluginId]); - } - if ($repoId != null && isSet($roleParams[$repoId][$pluginId])) { - $params = array_merge($params, $roleParams[$repoId][$pluginId]); - } - return $params; - } - - /** - * @param String $pluginId - * @param Repository $repository - * @param String $optionName - * @param bool $safe - * @return Mixed - */ - public static function getFilteredRepositoryOption($pluginId, $repository, $optionName, $safe = false){ - $logged = self::getLoggedUser(); - $test = null; - if($logged != null){ - $test = $logged->mergedRole->filterParameterValue($pluginId, $optionName, $repository->getId(), null); - if(!empty($test) && !$safe) $test = AJXP_VarsFilter::filter($test); - } - if(empty($test)){ - return $repository->getOption($optionName, $safe); - }else{ - return $test; - } - } - - /** - * @param string $parentUserId - * @return AJXP_Role - */ - public static function limitedRoleFromParent($parentUserId) - { - $parentRole = self::getRole("AJXP_USR_/".$parentUserId); - if($parentRole === false) return null; - - // Inherit actions - $inheritActions = array(); - $cacheInherit = AJXP_PluginsService::getInstance()->loadFromPluginQueriesCache("//server_settings/param[@inherit='true']"); - if ($cacheInherit !== null && is_array($cacheInherit)) { - $inheritActions = $cacheInherit; - } else { - $paramNodes = AJXP_PluginsService::searchAllManifests("//server_settings/param[@inherit='true']", "node", false, false, true); - if (is_array($paramNodes) && count($paramNodes)) { - foreach ($paramNodes as $node) { - $paramName = $node->getAttribute("name"); - $pluginId = $node->parentNode->parentNode->getAttribute("id"); - if(isSet($inheritActions[$pluginId])) $inheritActions[$pluginId] = array(); - $inheritActions[$pluginId][] = $paramName; - } - } - AJXP_PluginsService::getInstance()->storeToPluginQueriesCache("//server_settings/param[@inherit='true']", $inheritActions); - } - - // Clear ACL, Keep disabled actions, keep 'inherit' parameters. - $childRole = new AJXP_Role("AJXP_PARENT_USR_/"); - $childRole->bunchUpdate(array( - "ACL" => array(), - "ACTIONS" => $parentRole->listAllActionsStates(), - "APPLIES" => array(), - "PARAMETERS"=> array())); - $params = $parentRole->listParameters(); - - foreach ($params as $scope => $plugData) { - foreach ($plugData as $pId => $paramData) { - if(!isSet($inheritActions[$pId])) continue; - foreach ($paramData as $pName => $pValue) { - $childRole->setParameterValue($pId, $pName, $pValue, $scope); - } - } - } - - return $childRole; - } - - /** - * Get all defined roles - * @static - * @param array $roleIds - * @param boolean $excludeReserved, - * @return AJXP_Role[] - */ - public static function getRolesList($roleIds = array(), $excludeReserved = false) - { - if(self::$cacheRoles && !count($roleIds) && $excludeReserved == true && self::$roles != null) { - return self::$roles; - } - $confDriver = ConfService::getConfStorageImpl(); - $roles = $confDriver->listRoles($roleIds, $excludeReserved); - $repoList = null; - foreach ($roles as $roleId => $roleObject) { - if (is_a($roleObject, "AjxpRole")) { - if($repoList == null) $repoList = ConfService::getRepositoriesList("all"); - $newRole = new AJXP_Role($roleId); - $newRole->migrateDeprectated($repoList, $roleObject); - $roles[$roleId] = $newRole; - self::updateRole($newRole); - } - } - if(self::$cacheRoles && !count($roleIds) && $excludeReserved == true) { - self::$roles = $roles; - } - return $roles; - } - - /** - * Check if the current user is allowed to see the GroupPathProvider object - * @param AjxpGroupPathProvider $provider - * @param AbstractAjxpUser $userObject - * @return bool - */ - public static function allowedForCurrentGroup(AjxpGroupPathProvider $provider, $userObject = null) - { - $l = ($userObject == null ? self::getLoggedUser() : $userObject); - $pGP = $provider->getGroupPath(); - if(empty($pGP)) $pGP = "/"; - if($l == null || $l->getGroupPath() == null || $pGP == null) return true; - return (strpos($l->getGroupPath(), $pGP, 0) === 0); - } - - /** - * Check if the current user can administrate the GroupPathProvider object - * @param AjxpGroupPathProvider $provider - * @param AbstractAjxpUser $userObject - * @return bool - */ - public static function canAdministrate(AjxpGroupPathProvider $provider, $userObject = null) - { - $l = ($userObject == null ? self::getLoggedUser() : $userObject); - $pGP = $provider->getGroupPath(); - if(empty($pGP)) $pGP = "/"; - if($l == null || $l->getGroupPath() == null || $pGP == null) return true; - return (strpos($pGP, $l->getGroupPath(), 0) === 0); - } - - /** - * Check if the current user can assign administration for the GroupPathProvider object - * @param AjxpGroupPathProvider $provider - * @param AbstractAjxpUser $userObject - * @return bool - */ - public static function canAssign(AjxpGroupPathProvider $provider, $userObject = null) - { - $l = ($userObject == null ? self::getLoggedUser() : $userObject); - $pGP = $provider->getGroupPath(); - if(empty($pGP)) $pGP = "/"; - if($l == null || $l->getGroupPath() == null || $pGP == null) return true; - return (strpos($l->getGroupPath(), $pGP, 0) === 0); - } - -} diff --git a/core/src/core/classes/class.CacheService.php b/core/src/core/classes/class.CacheService.php deleted file mode 100644 index 8c51807ce9..0000000000 --- a/core/src/core/classes/class.CacheService.php +++ /dev/null @@ -1,106 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Static access to the caching mechanism. Encapsulates the cacheDriver implementation - * @package Pydio - * @subpackage Core - */ -class CacheService -{ - /** - * @param $namespace - * @param $id - * @return bool - */ - public static function contains($namespace, $id) { - $cacheDriver = ConfService::getCacheDriverImpl(); - - if ($cacheDriver) { - return $cacheDriver->contains($namespace, $id); - } - - return false; - } - - /** - * @param $namespace - * @param $id - * @param $object - * @param int $timelimit - * @return bool - */ - public static function save($namespace, $id, $object, $timelimit = 0) { - $cacheDriver = ConfService::getCacheDriverImpl(); - - if ($cacheDriver) { - return $cacheDriver->save($namespace, $id, $object, $timelimit); - } - - return false; - } - - /** - * @param $namespace - * @param $id - * @return bool|mixed - */ - public static function fetch($namespace, $id) { - $cacheDriver = ConfService::getCacheDriverImpl(); - - if ($cacheDriver) { - $data = $cacheDriver->fetch($namespace, $id); - return $data; - } - - return false; - } - - /** - * @param $namespace - * @param $id - * @return bool - */ - public static function delete($namespace, $id) { - $cacheDriver = ConfService::getCacheDriverImpl(); - - if ($cacheDriver) { - return $cacheDriver->delete($namespace, $id); - } - - return false; - } - - /** - * @param $namespace - * @return bool - */ - public static function deleteAll($namespace) { - $cacheDriver = ConfService::getCacheDriverImpl(); - - if ($cacheDriver) { - return $cacheDriver->deleteAll($namespace); - } - - return false; - } -} \ No newline at end of file diff --git a/core/src/core/classes/class.CaptchaProvider.php b/core/src/core/classes/class.CaptchaProvider.php deleted file mode 100644 index 1fa8d57f23..0000000000 --- a/core/src/core/classes/class.CaptchaProvider.php +++ /dev/null @@ -1,87 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -include_once(AJXP_BIN_FOLDER."/securimage/securimage.php"); - -/** - * Encapsulation of the securimage external library, to generate a Captcha Image on brute force login attempt. - * @package Pydio - * @subpackage Core - */ -class CaptchaProvider -{ - /** - * Print out a Captcha image - * @static - * @return void - */ - public static function sendCaptcha() - { - $libPath = AJXP_BIN_FOLDER."/securimage"; - - $img = new Securimage(); - $img->wordlist_file = $libPath."/words/words.txt"; - $img->gd_font_file = $libPath."/gdfonts/automatic.gdf"; - $img->signature_font = $img->ttf_file = $libPath."/AHGBold.ttf"; - - $img->image_height = 80; - $img->image_width = 170; - $img->perturbation = 0.85; - $img->image_bg_color = new Securimage_Color("#f6f6f6"); - $img->multi_text_color = array(new Securimage_Color("#3399ff"), - new Securimage_Color("#3300cc"), - new Securimage_Color("#3333cc"), - new Securimage_Color("#6666ff"), - new Securimage_Color("#99cccc") - ); - $img->use_multi_text = true; - $img->text_angle_minimum = -5; - $img->text_angle_maximum = 5; - $img->use_transparent_text = true; - $img->text_transparency_percentage = 30; // 100 = completely transparent - $img->num_lines = 5; - $img->line_color = new Securimage_Color("#eaeaea"); - $img->signature_color = new Securimage_Color(rand(0, 64), rand(64, 128), rand(128, 255)); - $img->use_wordlist = true; - if (!function_exists('imagettftext')) { - $img->use_gd_font = true; - $img->use_transparent_text = false; - $img->use_multi_text = false; - } - //$img->show($libPath."/backgrounds/bg3.jpg"); - $img->show(); - } - - /** - * Verify the code against the current image. - * @static - * @param $code - * @return bool - */ - public static function checkCaptchaResult($code) - { - $img = new Securimage(); - return $img->check($code); - - } - -} diff --git a/core/src/core/classes/class.ConfService.php b/core/src/core/classes/class.ConfService.php deleted file mode 100644 index 2536a79e28..0000000000 --- a/core/src/core/classes/class.ConfService.php +++ /dev/null @@ -1,1812 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Configuration holder. Singleton class accessed statically, encapsulates the confDriver implementation. - * @package Pydio - * @subpackage Core - */ -class ConfService -{ - private static $instance; - public static $useSession = true; - - private $booter; - private $confPlugin; - /** - * @var AbstractCacheDriver - */ - private $cachePlugin; - private $errors = array(); - private $configs = array(); - - private $contextRepositoryId; - private $contextCharset; - - /** - * @return AbstractConfDriver - */ - public function confPluginSoftLoad() - { - $this->booter = AJXP_PluginsService::getInstance()->softLoad("boot.conf", array()); - $coreConfigs = $this->booter->loadPluginConfig("core", "conf"); - $corePlug = AJXP_PluginsService::getInstance()->softLoad("core.conf", array()); - $corePlug->loadConfigs($coreConfigs); - return $corePlug->getConfImpl(); - - } - - /** - * @return AbstractCacheDriver - */ - public function cachePluginSoftLoad() - { - $coreConfigs = array(); - $corePlug = AJXP_PluginsService::getInstance()->softLoad("core.cache", array()); - CoreConfLoader::loadBootstrapConfForPlugin("core.cache", $coreConfigs); - if (!empty($coreConfigs)) $corePlug->loadConfigs($coreConfigs); - return $corePlug->getCacheImpl(); - } - - /** - * @return AbstractConfDriver - */ - public static function getBootConfStorageImpl() - { - $inst = AJXP_PluginsService::getInstance()->findPluginById("boot.conf"); - if (empty($inst)) { - $inst = AJXP_PluginsService::getInstance()->softLoad("boot.conf", array()); - } - return $inst; - } - - /** - * Initialize singleton - * @static - * @return void - */ - public static function init($installPath=AJXP_INSTALL_PATH, $pluginDir="plugins") - { - $inst = self::getInstance(); - $inst->initInst($installPath.DIRECTORY_SEPARATOR.$pluginDir); - } - - /** - * Load the boostrap_* files and their configs - * @return void - */ - public function initInst($pluginDirPath) - { - // INIT AS GLOBAL - $this->configs["AVAILABLE_LANG"] = self::listAvailableLanguages(); - if (isSet($_SERVER["HTTPS"]) && strtolower($_SERVER["HTTPS"]) == "on") { - $this->configs["USE_HTTPS"] = true; - } - if (isSet($this->configs["USE_HTTPS"])) { - AJXP_Utils::safeIniSet("session.cookie_secure", true); - } - $this->configs["JS_DEBUG"] = AJXP_CLIENT_DEBUG; - $this->configs["SERVER_DEBUG"] = AJXP_SERVER_DEBUG; - - if (is_file(AJXP_CONF_PATH."/bootstrap_repositories.php")) { - $REPOSITORIES = array(); - include(AJXP_CONF_PATH."/bootstrap_repositories.php"); - $this->configs["DEFAULT_REPOSITORIES"] = $REPOSITORIES; - } else { - $this->configs["DEFAULT_REPOSITORIES"] = array(); - } - - // Try to load instance from cache first - $this->cachePlugin = $this->cachePluginSoftLoad(); - if (AJXP_PluginsService::getInstance()->loadPluginsRegistryFromCache($this->cachePlugin)) { - return; - } - - $this->booter = AJXP_PluginsService::getInstance()->softLoad("boot.conf", array()); - $this->confPlugin = $this->confPluginSoftLoad(); - - // Loading the registry - try { - AJXP_PluginsService::getInstance()->loadPluginsRegistry($pluginDirPath, $this->confPlugin); - } catch (Exception $e) { - die("Severe error while loading plugins registry : ".$e->getMessage()); - } - } - - /** - * Start the singleton - * @static - * @return void - */ - public static function start() - { - $inst = self::getInstance(); - $inst->startInst(); - } - /** - * Init CONF, AUTH drivers - * Init Repositories - * @return void - */ - public function startInst() - { - AJXP_PluginsService::getInstance()->setPluginUniqueActiveForType("conf", self::getConfStorageImpl()->getName()); - } - /** - * Get errors generated by the boot sequence (init/start) - * @static - * @return array - */ - public static function getErrors() - { - return self::getInstance()->errors; - } - - public static function getContextCharset(){ - if(self::$useSession) { - if(isSet($_SESSION["AJXP_CHARSET"])) return $_SESSION["AJXP_CHARSET"]; - else return null; - }else { - return self::getInstance()->contextCharset; - } - } - - public static function setContextCharset($value){ - if(self::$useSession){ - $_SESSION["AJXP_CHARSET"] = $value; - }else{ - self::getInstance()->contextCharset = $value; - } - } - - public static function clearContextCharset(){ - if(self::$useSession && isSet($_SESSION["AJXP_CHARSET"])){ - unset($_SESSION["AJXP_CHARSET"]); - }else{ - self::getInstance()->contextCharset = null; - } - } - - public function getContextRepositoryId(){ - return self::$useSession ? $_SESSION["REPO_ID"] : $this->contextRepositoryId; - } - - public static function clearAllCaches(){ - AJXP_PluginsService::clearPluginsCache(); - self::clearMessagesCache(); - CacheService::deleteAll(AJXP_CACHE_SERVICE_NS_SHARED); - if(function_exists('opcache_reset')){ - opcache_reset(); - } - } - - /** - * @static - * @param $globalsArray - * @param string $interfaceCheck - * @return AJXP_Plugin|null - */ - public static function instanciatePluginFromGlobalParams($globalsArray, $interfaceCheck = "") - { - $plugin = false; - - if (is_string($globalsArray)) { - $globalsArray = array("instance_name" => $globalsArray); - } - - if (isSet($globalsArray["instance_name"])) { - $pName = $globalsArray["instance_name"]; - unset($globalsArray["instance_name"]); - - $plugin = AJXP_PluginsService::getInstance()->softLoad($pName, $globalsArray); - $plugin->performChecks(); - } - - if ($plugin != false && !empty($interfaceCheck)) { - if (!is_a($plugin, $interfaceCheck)) { - $plugin = false; - } - } - if ($plugin !== false) { - AJXP_PluginsService::getInstance()->setPluginActive($plugin->getType(), $plugin->getName(), true, $plugin); - } - return $plugin; - - } - - /** - * Check if the STDIN constant is defined - * @static - * @return bool - */ - public static function currentContextIsCommandLine() - { - return php_sapi_name() === "cli"; - } - - protected static $restAPIContext; - - /** - * Set or get if we are currently running REST - * @static - * @param string $restBase - * @return bool - */ - public static function currentContextIsRestAPI($restBase = '') - { - if(!empty($restBase)){ - self::$restAPIContext = $restBase; - return $restBase; - }else{ - return self::$restAPIContext; - } - } - - /** - * Check the presence of mcrypt and option CMDLINE_ACTIVE - * @static - * @return bool - */ - public static function backgroundActionsSupported() - { - return function_exists("mcrypt_create_iv") && ConfService::getCoreConf("CMDLINE_ACTIVE"); - } - - /** - * @var AbstractConfDriver - */ - private static $tmpConfStorageImpl; - /** - * @var AbstractAuthDriver - */ - private static $tmpAuthStorageImpl; - /** - * @var AbstractCacheDriver - */ - private static $tmpCacheStorageImpl; - - /** - * @param $confStorage AbstractConfDriver - * @param $authStorage AbstractAuthDriver - * @param $cacheStorage AbstractCacheDriver - */ - public static function setTmpStorageImplementations($confStorage, $authStorage, $cacheStorage) - { - self::$tmpConfStorageImpl = $confStorage; - self::$tmpAuthStorageImpl = $authStorage; - self::$tmpCacheStorageImpl = $cacheStorage; - } - - /** - * Get conf driver implementation - * - * @return AbstractConfDriver - */ - public static function getConfStorageImpl() - { - if(isSet(self::$tmpConfStorageImpl)) return self::$tmpConfStorageImpl; - return AJXP_PluginsService::getInstance()->getPluginById("core.conf")->getConfImpl(); - } - - /** - * Get auth driver implementation - * - * @return AbstractAuthDriver - */ - public static function getAuthDriverImpl() - { - if(isSet(self::$tmpAuthStorageImpl)) return self::$tmpAuthStorageImpl; - return AJXP_PluginsService::getInstance()->getPluginById("core.auth")->getAuthImpl(); - } - - /** - * Return info about auth plugins - * @return string - */ - public static function getInfo(){ - return "&a=".self::getAuthDriverImpl()->getStats(); - } - - /** - * Get auth driver implementation - * - * @return AbstractCacheDriver - */ - public static function getCacheDriverImpl() - { - if(isSet(self::$tmpCacheStorageImpl)) return self::$tmpCacheStorageImpl; - return AJXP_PluginsService::getInstance()->getPluginById("core.cache")->getCacheImpl(); - } - - public static function getFilteredXMLRegistry($extendedVersion = true, $clone = false, $useCache = false){ - - if($useCache){ - $cacheKey = self::getRegistryCacheKey($extendedVersion); - $cachedXml = CacheService::fetch(AJXP_CACHE_SERVICE_NS_SHARED, $cacheKey); - if($cachedXml !== false){ - $registry = new DOMDocument("1.0", "utf-8"); - $registry->loadXML($cachedXml); - AJXP_PluginsService::updateXmlRegistry($registry, $extendedVersion); - if($clone){ - return $registry->cloneNode(true); - }else{ - return $registry; - } - } - } - - $registry = AJXP_PluginsService::getXmlRegistry($extendedVersion); - $changes = self::filterRegistryFromRole($registry); - if($changes){ - AJXP_PluginsService::updateXmlRegistry($registry, $extendedVersion); - } - - if($useCache && isSet($cacheKey)){ - CacheService::save(AJXP_CACHE_SERVICE_NS_SHARED, $cacheKey, $registry->saveXML()); - } - - if($clone){ - $cloneDoc = $registry->cloneNode(true); - $registry = $cloneDoc; - } - return $registry; - - } - - private static function getRegistryCacheKey($extendedVersion = true){ - - $logged = AuthService::getLoggedUser(); - $u = $logged == null ? "shared" : $logged->getId(); - $a = "norepository"; - $r = ConfService::getRepository(); - if($r !== null){ - $a = $r->getSlug(); - } - $v = $extendedVersion ? "extended":"light"; - return "xml_registry:".$v.":".$u.":".$a; - - } - - /** - * Check the current user "specificActionsRights" and filter the full registry actions with these. - * @static - * @param DOMDocument $registry - * @return bool - */ - public static function filterRegistryFromRole(&$registry) - { - if(!AuthService::usersEnabled()) return false ; - $loggedUser = AuthService::getLoggedUser(); - if($loggedUser == null) return false; - $crtRepo = ConfService::getRepository(); - $crtRepoId = AJXP_REPO_SCOPE_ALL; // "ajxp.all"; - if ($crtRepo != null && is_a($crtRepo, "Repository")) { - $crtRepoId = $crtRepo->getId(); - } - $actionRights = $loggedUser->mergedRole->listActionsStatesFor($crtRepo); - $changes = false; - $xPath = new DOMXPath($registry); - foreach ($actionRights as $pluginName => $actions) { - foreach ($actions as $actionName => $enabled) { - if($enabled !== false) continue; - $actions = $xPath->query("actions/action[@name='$actionName']"); - if (!$actions->length) { - continue; - } - $action = $actions->item(0); - $action->parentNode->removeChild($action); - $changes = true; - } - } - $parameters = $loggedUser->mergedRole->listParameters(); - foreach ($parameters as $scope => $paramsPlugs) { - if ($scope === AJXP_REPO_SCOPE_ALL || $scope === $crtRepoId || ($crtRepo!=null && $crtRepo->hasParent() && $scope === AJXP_REPO_SCOPE_SHARED)) { - foreach ($paramsPlugs as $plugId => $params) { - foreach ($params as $name => $value) { - // Search exposed plugin_configs, replace if necessary. - $searchparams = $xPath->query("plugins/*[@id='$plugId']/plugin_configs/property[@name='$name']"); - if(!$searchparams->length) continue; - $param = $searchparams->item(0); - $newCdata = $registry->createCDATASection(json_encode($value)); - $param->removeChild($param->firstChild); - $param->appendChild($newCdata); - } - } - } - } - return $changes; - } - - - /** - * @param AbstractAjxpUser $loggedUser - * @param String|int $parameterId - * @return bool - */ - public static function switchUserToActiveRepository($loggedUser, $parameterId = -1) - { - if (isSet($_SESSION["PENDING_REPOSITORY_ID"]) && isSet($_SESSION["PENDING_FOLDER"])) { - $loggedUser->setArrayPref("history", "last_repository", $_SESSION["PENDING_REPOSITORY_ID"]); - $loggedUser->setPref("pending_folder", $_SESSION["PENDING_FOLDER"]); - AuthService::updateUser($loggedUser); - unset($_SESSION["PENDING_REPOSITORY_ID"]); - unset($_SESSION["PENDING_FOLDER"]); - } - $currentRepoId = ConfService::getCurrentRepositoryId(); - $lastRepoId = $loggedUser->getArrayPref("history", "last_repository"); - $defaultRepoId = AuthService::getDefaultRootId(); - if ($defaultRepoId == -1) { - return false; - } else { - if ($lastRepoId !== "" && $lastRepoId!==$currentRepoId && $parameterId == -1 && $loggedUser->canSwitchTo($lastRepoId)) { - ConfService::switchRootDir($lastRepoId); - } else if ($parameterId != -1 && $loggedUser->canSwitchTo($parameterId)) { - ConfService::switchRootDir($parameterId); - } else if (!$loggedUser->canSwitchTo($currentRepoId)) { - ConfService::switchRootDir($defaultRepoId); - } - } - return true; - } - - - /** - * See instance method - * @static - * @param $rootDirIndex - * @param bool $temporary - * @return void - */ - public static function switchRootDir($rootDirIndex = -1, $temporary = false) - { - self::getInstance()->switchRootDirInst($rootDirIndex, $temporary); - } - /** - * Switch the current repository - * @param $rootDirIndex - * @param bool $temporary - * @return void - */ - public function switchRootDirInst($rootDirIndex=-1, $temporary=false) - { - if ($rootDirIndex == -1) { - $ok = false; - if (isSet($_SESSION['REPO_ID']) || $this->contextRepositoryId != null) { - $sessionId = self::$useSession ? $_SESSION['REPO_ID'] : $this->contextRepositoryId; - $object = self::getRepositoryById($sessionId); - if($object != null && self::repositoryIsAccessible($sessionId, $object)){ - $this->configs["REPOSITORY"] = $object; - $ok = true; - } - } - if(!$ok) { - $currentRepos = $this->getLoadedRepositories(); - $keys = array_keys($currentRepos); - $this->configs["REPOSITORY"] = $currentRepos[$keys[0]]; - if(self::$useSession){ - $_SESSION['REPO_ID'] = $keys[0]; - }else{ - $this->contextRepositoryId = $keys[0]; - } - } - } else { - $object = self::getRepositoryById($rootDirIndex); - if($temporary && ($object == null || !self::repositoryIsAccessible($rootDirIndex, $object))) { - throw new AJXP_Exception("Trying to switch to an unauthorized repository"); - } - if ($temporary && (isSet($_SESSION['REPO_ID']) || $this->contextRepositoryId != null)) { - $crtId = self::$useSession ? $_SESSION['REPO_ID'] : $this->contextRepositoryId; - if ($crtId != $rootDirIndex && !isSet($_SESSION['SWITCH_BACK_REPO_ID'])) { - $_SESSION['SWITCH_BACK_REPO_ID'] = $crtId; - //AJXP_Logger::debug("switching to $rootDirIndex, registering $crtId"); - } - } else { - $crtId = self::$useSession ? $_SESSION['REPO_ID'] : $this->contextRepositoryId; - $_SESSION['PREVIOUS_REPO_ID'] = $crtId; - //AJXP_Logger::debug("switching back to $rootDirIndex"); - } - if (isSet($this->configs["REPOSITORIES"]) && isSet($this->configs["REPOSITORIES"][$rootDirIndex])) { - $this->configs["REPOSITORY"] = $this->configs["REPOSITORIES"][$rootDirIndex]; - } else { - $this->configs["REPOSITORY"] = ConfService::getRepositoryById($rootDirIndex); - } - if(self::$useSession){ - $_SESSION['REPO_ID'] = $rootDirIndex; - }else{ - $this->contextRepositoryId = $rootDirIndex; - } - if(isSet($this->configs["ACCESS_DRIVER"])) unset($this->configs["ACCESS_DRIVER"]); - } - - if (isSet($this->configs["REPOSITORY"]) && $this->configs["REPOSITORY"]->getOption("CHARSET")!="") { - self::setContextCharset($this->configs["REPOSITORY"]->getOption("CHARSET")); - } else { - self::clearContextCharset(); - } - - - if ($rootDirIndex!=-1 && AuthService::usersEnabled() && AuthService::getLoggedUser()!=null) { - $loggedUser = AuthService::getLoggedUser(); - $loggedUser->setArrayPref("history", "last_repository", $rootDirIndex); - } - - } - - /** - * See instance method - * @static - * @param String $scope "user" or "all" - * @param bool $includeShared - * @return Repository[] - */ - public static function getRepositoriesList($scope = "user", $includeShared = true) - { - if ($scope == "user") { - return self::getInstance()->getLoadedRepositories(); - } else { - return self::getInstance()->initRepositoriesListInst("all", $includeShared); - } - } - - public static function detectSessionCorrupted($logMessage){ - if(isSet($_SESSION["REPOSITORIES"])){ - $sessionNotCorrupted = array_reduce($_SESSION["REPOSITORIES"], function($carry, $item){ return $carry && is_a($item, "Repository"); }, true); - if(!$sessionNotCorrupted){ - error_log("[session corrupted] ".$logMessage." ".$_GET["get_action"]."".$_POST["get_action"]); - } - } - } - - /** - * @return Repository[] - */ - private function getLoadedRepositories() - { - if (!isSet($this->configs["REPOSITORIES"]) && isSet($_SESSION["REPOSITORIES"]) && is_array($_SESSION["REPOSITORIES"])){ - $sessionNotCorrupted = array_reduce($_SESSION["REPOSITORIES"], function($carry, $item){ return $carry && is_a($item, "Repository"); }, true); - if($sessionNotCorrupted){ - $this->configs["REPOSITORIES"] = $_SESSION["REPOSITORIES"]; - return $_SESSION["REPOSITORIES"]; - }else{ - unset($_SESSION["REPOSITORIES"]); - } - } - if (isSet($this->configs["REPOSITORIES"])) { - $configsNotCorrupted = array_reduce($this->configs["REPOSITORIES"], function($carry, $item){ return $carry && is_a($item, "Repository"); }, true); - if($configsNotCorrupted){ - return $this->configs["REPOSITORIES"]; - }else{ - unset($this->configs["REPOSITORIES"]); - } - } - $this->configs["REPOSITORIES"] = $this->initRepositoriesListInst(); - $_SESSION["REPOSITORIES"] = $this->configs["REPOSITORIES"]; - return $this->configs["REPOSITORIES"]; - } - - public function invalidateLoadedRepositories() - { - if(isSet($_SESSION["REPOSITORIES"])) unset($_SESSION["REPOSITORIES"]); - $this->configs["REPOSITORIES"] = null; - CacheService::delete(AJXP_CACHE_SERVICE_NS_SHARED, $this->getRegistryCacheKey(true)); - CacheService::delete(AJXP_CACHE_SERVICE_NS_SHARED, $this->getRegistryCacheKey(false)); - } - - private function cacheRepository($repoId, $repository){ - if(!is_array($this->configs["REPOSITORIES"])) return; - $this->configs["REPOSITORIES"][$repoId] = $repository; - $_SESSION["REPOSITORIES"] = $this->configs["REPOSITORIES"]; - } - - private function removeRepositoryFromCache($repositoryId){ - if(!is_array($this->configs["REPOSITORIES"]) || !isSet($this->configs["REPOSITORIES"][$repositoryId])) return; - unset($this->configs["REPOSITORIES"][$repositoryId]); - $_SESSION["REPOSITORIES"] = $this->configs["REPOSITORIES"]; - } - - /** - * @static - * @param AbstractAjxpUser $userObject - * @param bool $details - * @param bool $labelOnly - * @param bool $includeShared - * @return Repository[] - */ - public static function getAccessibleRepositories($userObject=null, $details=false, $labelOnly = false, $includeShared = true) - { - $result = array(); - $allReps = ConfService::getRepositoriesList("user"); - foreach ($allReps as $repositoryId => $repositoryObject) { - if (!ConfService::repositoryIsAccessible($repositoryId, $repositoryObject, $userObject, $details, $includeShared)) { - continue; - } - - if ($labelOnly) { - $result[$repositoryId] = $repositoryObject->getDisplay(); - } else { - $result[$repositoryId] = $repositoryObject; - } - - } - return $result; - } - - /** - * @param String $repositoryId - * @param Repository $repositoryObject - * @param AbstractAjxpUser $userObject - * @param bool $details - * @param bool $includeShared - * - * @return bool - */ - public static function repositoryIsAccessible($repositoryId, $repositoryObject, $userObject = null, $details=false, $includeShared=true) - { - if($userObject == null) $userObject = AuthService::getLoggedUser(); - if ($userObject == null && AuthService::usersEnabled()) { - return false; - } - if (!AuthService::canAssign($repositoryObject, $userObject)) { - return false; - } - if ($repositoryObject->isTemplate) { - return false; - } - if (($repositoryObject->getAccessType()=="ajxp_conf" || $repositoryObject->getAccessType()=="ajxp_admin") && $userObject != null) { - if (AuthService::usersEnabled() && !$userObject->isAdmin()) { - return false; - } - } - if ($repositoryObject->getAccessType()=="ajxp_user" && $userObject != null) { - return ($userObject->canRead($repositoryId) || $userObject->canWrite($repositoryId)) ; - } - if ($repositoryObject->getAccessType() == "ajxp_shared" && !AuthService::usersEnabled()) { - return false; - } - if ($repositoryObject->getUniqueUser() && (!AuthService::usersEnabled() || $userObject == null || $userObject->getId() == "shared" || $userObject->getId() != $repositoryObject->getUniqueUser() )) { - return false; - } - if ( $userObject != null && !($userObject->canRead($repositoryId) || $userObject->canWrite($repositoryId)) && !$details) { - return false; - } - if ($userObject == null || $userObject->canRead($repositoryId) || $userObject->canWrite($repositoryId) || $details) { - // Do not display standard repositories even in details mode for "sub"users - if ($userObject != null && $userObject->hasParent() && !($userObject->canRead($repositoryId) || $userObject->canWrite($repositoryId) )) { - return false; - } - // Do not display shared repositories otherwise. - if ($repositoryObject->hasOwner() && !$includeShared && ($userObject == null || $userObject->getParent() != $repositoryObject->getOwner())) { - return false; - } - if ($userObject != null && $repositoryObject->hasOwner() && !$userObject->hasParent()) { - // Display the repositories if allow_crossusers is ok - if(ConfService::getCoreConf("ALLOW_CROSSUSERS_SHARING", "conf") === false - || ConfService::getCoreConf("ALLOW_CROSSUSERS_SHARING", "conf") === 0) { - return false; - } - // But still do not display its own shared repositories! - if ($repositoryObject->getOwner() == $userObject->getId()) { - return false; - } - } - if ($repositoryObject->hasOwner() && $userObject != null && $details && !($userObject->canRead($repositoryId) || $userObject->canWrite($repositoryId) ) ) { - return false; - } - } - $res = null; - $args = array($repositoryId, $repositoryObject, $userObject, &$res); - AJXP_Controller::applyIncludeHook("repository.test_access", $args); - if($res === false){ - return false; - } - return true; - } - - /** - * Return the full list of repositories, as id => objects - * @return array - */ - public function getRepositoriesListInst() - { - return $this->getLoadedRepositories(); - } - - /** - * See instance method - * @static - * @return string - */ - public static function getCurrentRepositoryId() - { - return self::getInstance()->getCurrentRepositoryIdInst(); - } - /** - * Get the current repository ID; - * @return string - */ - public function getCurrentRepositoryIdInst() - { - $ctxId = $this->getContextRepositoryId(); - if(!empty($ctxId) || $ctxId."" === "0"){ - $object = self::getRepositoryById($ctxId); - if($object != null && self::repositoryIsAccessible($ctxId, $object)){ - return $ctxId; - } - } - $currentRepos = $this->getLoadedRepositories(); - $keys = array_keys($currentRepos); - return array_shift($keys); - } - /** - * Get the current repo label - * @static - * @return string - */ - public static function getCurrentRootDirDisplay() - { - return self::getInstance()->getCurrentRootDirDisplayInst(); - } - /** - * @return string - */ - public function getCurrentRootDirDisplayInst() - { - $currentRepos = $this->getLoadedRepositories(); - $ctxId = $this->getContextRepositoryId(); - if (isSet($currentRepos[$ctxId])) { - $repo = $currentRepos[$ctxId]; - return $repo->getDisplay(); - } - return ""; - } - - /** - * @param Repository[] $repoList - * @param array $criteria - * @return Repository[] array - */ - public static function filterRepositoryListWithCriteria($repoList, $criteria){ - $repositories = array(); - $searchableKeys = array("uuid", "parent_uuid", "owner_user_id", "display", "accessType", "isTemplate", "slug", "groupPath"); - foreach ($repoList as $repoId => $repoObject) { - $failOneCriteria = false; - foreach($criteria as $key => $value){ - if(!in_array($key, $searchableKeys)) continue; - $criteriumOk = false; - $comp = null; - if($key == "uuid") $comp = $repoObject->getUniqueId(); - else if($key == "parent_uuid") $comp = $repoObject->getParentId(); - else if($key == "owner_user_id") $comp = $repoObject->getUniqueUser(); - else if($key == "display") $comp = $repoObject->getDisplay(); - else if($key == "accessType") $comp = $repoObject->getAccessType(); - else if($key == "isTemplate") $comp = $repoObject->isTemplate; - else if($key == "slug") $comp = $repoObject->getSlug(); - else if($key == "groupPath") $comp = $repoObject->getGroupPath(); - if(is_array($value) && in_array($comp, $value)){ - //$repositories[$repoId] = $repoObject; - $criteriumOk = true; - }else if($value == AJXP_FILTER_EMPTY && empty($comp)){ - //$repositories[$repoId] = $repoObject; - $criteriumOk = true; - }else if($value == AJXP_FILTER_NOT_EMPTY && !empty($comp)){ - //$repositories[$repoId] = $repoObject; - $criteriumOk = true; - }else if(is_string($value) && strpos($value, "regexp:")===0 && preg_match(str_replace("regexp:", "", $value), $comp)){ - //$repositories[$repoId] = $repoObject; - $criteriumOk = true; - }else if($value == $comp){ - //$repositories[$repoId] = $repoObject; - $criteriumOk = true; - } - if(!$criteriumOk) { - $failOneCriteria = true; - break; - } - } - if(!$failOneCriteria){ - $repositories[$repoId] = $repoObject; - } - } - return $repositories; - } - - /** - * @param array $criteria - * @param $count - * @return Repository[] - */ - public static function listRepositoriesWithCriteria($criteria, &$count){ - - $statics = array(); - foreach (self::getInstance()->configs["DEFAULT_REPOSITORIES"] as $index=>$repository) { - $repo = self::createRepositoryFromArray($index, $repository); - $repo->setWriteable(false); - $statics[$repo->getId()] = $repo; - } - $statics = self::filterRepositoryListWithCriteria($statics, $criteria); - $dyna = self::getInstance()->getConfStorageImpl()->listRepositoriesWithCriteria($criteria, $count); - $count += count($statics); - return array_merge($statics, $dyna); - - } - - /** - * @param $scope String "user", "all" - * @param bool $includeShared - * @return array - */ - protected function initRepositoriesListInst($scope = "user", $includeShared = true) - { - // APPEND CONF FILE REPOSITORIES - $loggedUser = AuthService::getLoggedUser(); - $objList = array(); - if($loggedUser != null){ - $l = $loggedUser->getLock(); - if( !empty($l)) return $objList; - } - foreach ($this->configs["DEFAULT_REPOSITORIES"] as $index=>$repository) { - $repo = self::createRepositoryFromArray($index, $repository); - if($scope == "user" && $loggedUser != null && !self::repositoryIsAccessible($index, $repo, $loggedUser)){ - continue; - } - $repo->setWriteable(false); - $objList["".$repo->getId()] = $repo; - } - // LOAD FROM DRIVER - $confDriver = self::getConfStorageImpl(); - if($scope == "user"){ - $acls = array(); - if(AuthService::getLoggedUser() != null){ - $acls = AuthService::getLoggedUser()->mergedRole->listAcls(true); - } - if(!count($acls)) { - $drvList = array(); - }else{ - $criteria = array( - "uuid" => array_keys($acls) - ); - $drvList = $confDriver->listRepositoriesWithCriteria($criteria); - } - }else{ - if($includeShared){ - $drvList = $confDriver->listRepositories(); - }else{ - $drvList = $confDriver->listRepositoriesWithCriteria(array( - "owner_user_id" => AJXP_FILTER_EMPTY - )); - } - } - if (is_array($drvList)) { - /** - * @var $drvList Repository[] - */ - foreach ($drvList as $repoId=>$repoObject) { - $driver = AJXP_PluginsService::getInstance()->getPluginByTypeName("access", $repoObject->getAccessType()); - if (!is_object($driver) || !$driver->isEnabled()) { - unset($drvList[$repoId]); - } else { - $repoObject->setId($repoId); - $drvList[$repoId] = $repoObject; - } - if($repoObject->hasParent() && !ConfService::findRepositoryByIdOrAlias($repoObject->getParentId())){ - AJXP_Logger::error(__CLASS__, __FUNCTION__, "Disabling repository ".$repoObject->getSlug()." as parent cannot be correctly loaded."); - unset($drvList[$repoId]); - } - } - foreach($drvList as $key => $value){ - $objList[$key] = $value; - } - } - $args = array(&$objList, $scope, $includeShared); - AJXP_Controller::applyIncludeHook("repository.list", $args); - return $objList; - } - /** - * See instance method - * @static - * @param bool $register - * @return array - */ - public static function detectRepositoryStreams($register = false) - { - return self::getInstance()->detectRepositoryStreamsInst($register); - } - /** - * Call the detectStreamWrapper method - * @param bool $register - * @return array - */ - public function detectRepositoryStreamsInst($register = false) - { - $streams = array(); - $currentRepos = $this->getLoadedRepositories(); - foreach ($currentRepos as $repository) { - $repository->detectStreamWrapper($register, $streams); - } - return $streams; - } - - /** - * Create a repository object from a config options array - * - * @param integer $index - * @param Array $repository - * @return Repository - */ - public static function createRepositoryFromArray($index, $repository) - { - return self::getInstance()->createRepositoryFromArrayInst($index, $repository); - } - /** - * See static method - * @param string $index - * @param array $repository - * @return Repository - */ - public function createRepositoryFromArrayInst($index, $repository) - { - $repo = new Repository($index, $repository["DISPLAY"], $repository["DRIVER"]); - if (isSet($repository["DISPLAY_ID"])) { - $repo->setDisplayStringId($repository["DISPLAY_ID"]); - } - if (isSet($repository["DESCRIPTION_ID"])) { - $repo->setDescription($repository["DESCRIPTION_ID"]); - } - if (isSet($repository["AJXP_SLUG"])) { - $repo->setSlug($repository["AJXP_SLUG"]); - } - if (isSet($repository["IS_TEMPLATE"]) && $repository["IS_TEMPLATE"]) { - $repo->isTemplate = true; - $repo->uuid = $index; - } - if (array_key_exists("DRIVER_OPTIONS", $repository) && is_array($repository["DRIVER_OPTIONS"])) { - foreach ($repository["DRIVER_OPTIONS"] as $oName=>$oValue) { - $repo->addOption($oName, $oValue); - } - } - // BACKWARD COMPATIBILITY! - if (array_key_exists("PATH", $repository)) { - $repo->addOption("PATH", $repository["PATH"]); - $repo->addOption("CREATE", intval($repository["CREATE"])); - $repo->addOption("RECYCLE_BIN", $repository["RECYCLE_BIN"]); - } - return $repo; - } - - /** - * Add dynamically created repository - * - * @param Repository $oRepository - * @return -1|null if error - */ - public static function addRepository($oRepository) - { - return self::getInstance()->addRepositoryInst($oRepository); - } - /** - * @param Repository $oRepository - * @return -1|null on error - */ - public function addRepositoryInst($oRepository) - { - AJXP_Controller::applyHook("workspace.before_create", array($oRepository)); - $confStorage = self::getConfStorageImpl(); - $res = $confStorage->saveRepository($oRepository); - if ($res == -1) { - return $res; - } - AJXP_Controller::applyHook("workspace.after_create", array($oRepository)); - AJXP_Logger::info(__CLASS__,"Create Repository", array("repo_name"=>$oRepository->getDisplay())); - CacheService::save(AJXP_CACHE_SERVICE_NS_SHARED, "repository:".$oRepository->getId(), $oRepository); - $this->invalidateLoadedRepositories(); - return null; - } - - /** - * @param $idOrAlias - * @return null|Repository - */ - public static function findRepositoryByIdOrAlias($idOrAlias) - { - $repository = ConfService::getRepositoryById($idOrAlias); - if($repository != null ) return $repository; - $repository = ConfService::getRepositoryByAlias($idOrAlias); - if($repository != null) return $repository; - return null; - } - - /** - * Get the reserved slugs used for config defined repositories - * @return array - */ - public static function reservedSlugsFromConfig(){ - $inst = self::getInstance(); - $slugs = array(); - if(isSet($inst->configs["DEFAULT_REPOSITORIES"])){ - foreach($inst->configs["DEFAULT_REPOSITORIES"] as $repo){ - if(isSet($repo["AJXP_SLUG"])){ - $slugs[] = $repo["AJXP_SLUG"]; - } - } - } - return $slugs; - } - - /** - * Retrieve a repository object - * - * @param String $repoId - * @return Repository - */ - public static function getRepositoryById($repoId) - { - return self::getInstance()->getRepositoryByIdInst($repoId); - } - /** - * See static method - * @param $repoId - * @return Repository|null - */ - public function getRepositoryByIdInst($repoId) - { - if (isSet($this->configs["REPOSITORIES"]) && isSet($this->configs["REPOSITORIES"][$repoId])) { - return $this->configs["REPOSITORIES"][$repoId]; - } - if (iSset($this->configs["REPOSITORY"]) && $this->configs["REPOSITORY"]->getId()."" == $repoId) { - return $this->configs["REPOSITORY"]; - } - $test = CacheService::fetch(AJXP_CACHE_SERVICE_NS_SHARED, "repository:" . $repoId); - if($test !== false){ - return $test; - } - $test = $this->getConfStorageImpl()->getRepositoryById($repoId); - if($test != null) { - CacheService::save(AJXP_CACHE_SERVICE_NS_SHARED, "repository:" . $repoId, $test); - return $test; - } - // Finally try to search in default repositories - if (isSet($this->configs["DEFAULT_REPOSITORIES"]) && isSet($this->configs["DEFAULT_REPOSITORIES"][$repoId])) { - $repo = self::createRepositoryFromArray($repoId, $this->configs["DEFAULT_REPOSITORIES"][$repoId]); - $repo->setWriteable(false); - CacheService::save(AJXP_CACHE_SERVICE_NS_SHARED, "repository:" . $repoId, $repo); - return $repo; - } - $hookedRepo = null; - $args = array($repoId, &$hookedRepo); - AJXP_Controller::applyIncludeHook("repository.search", $args); - if($hookedRepo !== null){ - return $hookedRepo; - } - return null; - } - - /** - * Retrieve a repository object by its slug - * - * @param String $repoAlias - * @return Repository - */ - public static function getRepositoryByAlias($repoAlias) - { - $repo = self::getConfStorageImpl()->getRepositoryByAlias($repoAlias); - if($repo !== null) return $repo; - // check default repositories - return self::getInstance()->getRepositoryByAliasInstDefaults($repoAlias); - } - /** - * See static method - * @param $repoAlias - * @return Repository|null - */ - public function getRepositoryByAliasInstDefaults($repoAlias) - { - $conf = $this->configs["DEFAULT_REPOSITORIES"]; - foreach ($conf as $repoId => $repoDef) { - if ($repoDef["AJXP_SLUG"] == $repoAlias) { - $repo = self::createRepositoryFromArray($repoId, $repoDef); - $repo->setWriteable(false); - return $repo; - } - } - return null; - } - - - /** - * Replace a repository by an update one. - * - * @param String $oldId - * @param Repository $oRepositoryObject - * @return mixed - */ - public static function replaceRepository($oldId, $oRepositoryObject) - { - return self::getInstance()->replaceRepositoryInst($oldId, $oRepositoryObject); - } - /** - * See static method - * @param string $oldId - * @param Repository $oRepositoryObject - * @return int - */ - public function replaceRepositoryInst($oldId, $oRepositoryObject) - { - AJXP_Controller::applyHook("workspace.before_update", array($oRepositoryObject)); - $confStorage = self::getConfStorageImpl(); - $res = $confStorage->saveRepository($oRepositoryObject, true); - if ($res == -1) { - return -1; - } - AJXP_Controller::applyHook("workspace.after_update", array($oRepositoryObject)); - AJXP_Logger::info(__CLASS__,"Edit Repository", array("repo_name"=>$oRepositoryObject->getDisplay())); - $this->invalidateLoadedRepositories(); - CacheService::save(AJXP_CACHE_SERVICE_NS_SHARED, "repository:" . $oRepositoryObject->getId(), $oRepositoryObject); - return 0; - } - /** - * Set a temp repository id but not in the session - * @static - * @param $repositoryObject - * @return void - */ - public static function tmpReplaceRepository($repositoryObject) - { - $inst = self::getInstance(); - if (isSet($inst->configs["REPOSITORIES"][$repositoryObject->getUniqueId()])) { - $inst->configs["REPOSITORIES"][$repositoryObject->getUniqueId()] = $repositoryObject; - } - } - /** - * Remove a repository using the conf driver implementation - * @static - * @param $repoId - * @return int - */ - public static function deleteRepository($repoId) - { - return self::getInstance()->deleteRepositoryInst($repoId); - } - /** - * See static method - * @param $repoId - * @return int - */ - public function deleteRepositoryInst($repoId) - { - AJXP_Controller::applyHook("workspace.before_delete", array($repoId)); - $confStorage = self::getConfStorageImpl(); - $shares = $confStorage->listRepositoriesWithCriteria(array("parent_uuid" => $repoId)); - $toDelete = array(); - foreach($shares as $share){ - $toDelete[] = $share->getId(); - } - $res = $confStorage->deleteRepository($repoId); - if ($res == -1) { - return $res; - } - foreach($toDelete as $deleteId){ - $this->deleteRepositoryInst($deleteId); - } - AJXP_Controller::applyHook("workspace.after_delete", array($repoId)); - AJXP_Logger::info(__CLASS__,"Delete Repository", array("repo_id"=>$repoId)); - $this->invalidateLoadedRepositories(); - CacheService::delete(AJXP_CACHE_SERVICE_NS_SHARED, "repository:".$repoId); - return 0; - } - - /** - * Check if the gzopen function exists - * @static - * @return bool - */ - public static function zipEnabled() - { - return (function_exists("gzopen") || function_exists("gzopen64")); - } - - /** - * Check if users are allowed to browse ZIP content - * @static - * @return bool - */ - public static function zipBrowsingEnabled() - { - if(!self::zipEnabled()) return false; - return !ConfService::getCoreConf("DISABLE_ZIP_BROWSING"); - } - - /** - * Check if users are allowed to create ZIP archive - * @static - * @return bool - */ - public static function zipCreationEnabled() - { - if(!self::zipEnabled()) return false; - return ConfService::getCoreConf("ZIP_CREATION"); - } - - - /** - * Get the list of all "conf" messages - * @static - * @param bool $forceRefresh Refresh the list - * @return - */ - public static function getMessagesConf($forceRefresh = false) - { - return self::getInstance()->getMessagesInstConf($forceRefresh); - } - /** - * See static method - * @param bool $forceRefresh - * @return - */ - public function getMessagesInstConf($forceRefresh = false) - { - // make sure they are loaded - $this->getMessagesInst($forceRefresh); - return $this->configs["CONF_MESSAGES"]; - } - - /** - * Get all i18n message - * @static - * @param bool $forceRefresh - * @return - */ - public static function getMessages($forceRefresh = false) - { - return self::getInstance()->getMessagesInst($forceRefresh); - } - /** - * Get i18n messages - * @param bool $forceRefresh - * @return - */ - public function getMessagesInst($forceRefresh = false) - { - $crtLang = self::getLanguage(); - $messageCacheDir = dirname(AJXP_PLUGINS_MESSAGES_FILE)."/i18n"; - $messageFile = $messageCacheDir."/".$crtLang."_".basename(AJXP_PLUGINS_MESSAGES_FILE); - if (isSet($this->configs["MESSAGES"]) && !$forceRefresh) { - return $this->configs["MESSAGES"]; - } - if (!isset($this->configs["MESSAGES"]) && is_file($messageFile)) { - include($messageFile); - if (isSet($MESSAGES)) { - $this->configs["MESSAGES"] = $MESSAGES; - } - if (isSet($CONF_MESSAGES)) { - $this->configs["CONF_MESSAGES"] = $CONF_MESSAGES; - } - } else { - $this->configs["MESSAGES"] = array(); - $this->configs["CONF_MESSAGES"] = array(); - $nodes = AJXP_PluginsService::getInstance()->searchAllManifests("//i18n", "nodes"); - foreach ($nodes as $node) { - $nameSpace = $node->getAttribute("namespace"); - $path = AJXP_INSTALL_PATH."/".$node->getAttribute("path"); - $lang = $crtLang; - if (!is_file($path."/".$crtLang.".php")) { - $lang = "en"; // Default language, minimum required. - } - if (is_file($path."/".$lang.".php")) { - require($path."/".$lang.".php"); - if (isSet($mess)) { - foreach ($mess as $key => $message) { - $this->configs["MESSAGES"][(empty($nameSpace)?"":$nameSpace.".").$key] = $message; - } - } - } - $lang = $crtLang; - if (!is_file($path."/conf/".$crtLang.".php")) { - $lang = "en"; - } - if (is_file($path."/conf/".$lang.".php")) { - $mess = array(); - require($path."/conf/".$lang.".php"); - $this->configs["CONF_MESSAGES"] = array_merge($this->configs["CONF_MESSAGES"], $mess); - } - } - if(!is_dir($messageCacheDir)) mkdir($messageCacheDir); - AJXP_VarsFilter::filterI18nStrings($this->configs["MESSAGES"]); - AJXP_VarsFilter::filterI18nStrings($this->configs["CONF_MESSAGES"]); - @file_put_contents($messageFile, "configs["MESSAGES"], true) ." ; \$CONF_MESSAGES = ".var_export($this->configs["CONF_MESSAGES"], true) ." ; "); - } - - return $this->configs["MESSAGES"]; - } - - /** - * Clear the messages cache - */ - public static function clearMessagesCache(){ - $i18nFiles = glob(dirname(AJXP_PLUGINS_MESSAGES_FILE)."/i18n/*.ser"); - if (is_array($i18nFiles)) { - foreach ($i18nFiles as $file) { - @unlink($file); - } - } - } - - /** - * Get all registered extensions, from both the conf/extensions.conf.php and from the plugins - * @static - * @return - */ - public static function getRegisteredExtensions() - { - return self::getInstance()->getRegisteredExtensionsInst(); - } - /** - * See static method - * @return - */ - public function getRegisteredExtensionsInst() - { - if (!isSet($this->configs["EXTENSIONS"])) { - $EXTENSIONS = array(); - $RESERVED_EXTENSIONS = array(); - include_once(AJXP_CONF_PATH."/extensions.conf.php"); - $EXTENSIONS = array_merge($RESERVED_EXTENSIONS, $EXTENSIONS); - foreach ($EXTENSIONS as $key => $value) { - unset($EXTENSIONS[$key]); - $EXTENSIONS[$value[0]] = $value; - } - $nodes = AJXP_PluginsService::getInstance()->searchAllManifests("//extensions/extension", "nodes", true); - $res = array(); - foreach ($nodes as $node) { - $res[$node->getAttribute("mime")] = array( - $node->getAttribute("mime"), - $node->getAttribute("icon"), - $node->getAttribute("font"), - $node->getAttribute("messageId") - ); - } - if (count($res)) { - $EXTENSIONS = array_merge($EXTENSIONS, $res); - } - $this->configs["EXTENSIONS"] = $EXTENSIONS; - } - return $this->configs["EXTENSIONS"]; - } - /** - * Get the actions that declare to skip the secure token in the plugins - * @static - * @return array - */ - public static function getDeclaredUnsecureActions() - { - $test = AJXP_PluginsService::getInstance()->loadFromPluginQueriesCache("//action[@skipSecureToken]"); - if (!empty($test) && is_array($test)) { - return $test; - } else { - $nodes = AJXP_PluginsService::getInstance()->searchAllManifests("//action[@skipSecureToken]", "nodes", false, false, true); - $res = array(); - foreach ($nodes as $node) { - $res[] = $node->getAttribute("name"); - } - AJXP_PluginsService::getInstance()->storeToPluginQueriesCache("//action[@skipSecureToken]", $res); - return $res; - } - - } - /** - * Detect available languages from the core i18n library - * @static - * @return array - */ - public static function listAvailableLanguages() - { - // Cache in session! - if (isSet($_SESSION["AJXP_LANGUAGES"]) && !isSet($_GET["refresh_langs"])) { - return $_SESSION["AJXP_LANGUAGES"]; - } - $langDir = AJXP_COREI18N_FOLDER; - $languages = array(); - if (($dh = opendir($langDir))!==FALSE) { - while (($file = readdir($dh)) !== false) { - $matches = array(); - if (preg_match("/(.*)\.php/", $file, $matches) == 1) { - $fRadical = $matches[1]; - include($langDir."/".$fRadical.".php"); - $langName = isSet($mess["languageLabel"])?$mess["languageLabel"]:"Not Found"; - $languages[$fRadical] = $langName; - } - } - closedir($dh); - } - if (count($languages)) { - $_SESSION["AJXP_LANGUAGES"] = $languages; - } - return $languages; - } - - /** - * Get a config by its name - * @static - * @param string $varName - * @return mixed - */ - public static function getConf($varName) - { - return self::getInstance()->getConfInst($varName); - } - /** - * Set a config by its name - * @static - * @param string $varName - * @param mixed $varValue - * @return void - */ - public static function setConf($varName, $varValue) - { - self::getInstance()->setConfInst($varName, $varValue); - } - /** - * See static method - * @param $varName - * @return mixed - */ - public function getConfInst($varName) - { - if (isSet($this->configs[$varName])) { - return $this->configs[$varName]; - } - if (defined("AJXP_".$varName)) { - return constant("AJXP_".$varName); - } - return null; - } - /** - * See static method - * @param $varName - * @param $varValue - * @return void - */ - public function setConfInst($varName, $varValue) - { - $this->configs[$varName] = $varValue; - } - /** - * Get config from the core.$coreType plugin - * @static - * @param string $varName - * @param string $coreType - * @return mixed|null|string - */ - public static function getCoreConf($varName, $coreType = "ajaxplorer") - { - $coreP = AJXP_PluginsService::getInstance()->findPlugin("core", $coreType); - if($coreP === false) return null; - $confs = $coreP->getConfigs(); - $confs = AuthService::filterPluginParameters("core.".$coreType, $confs); - return (isSet($confs[$varName]) ? AJXP_VarsFilter::filter($confs[$varName]) : null); - } - - /** - * @var array Keep loaded labels in memory - */ - private static $usersParametersCache = array(); - - /** - * @param string $parameterName Plugin parameter name - * @param AbstractAjxpUser|string $userIdOrObject - * @param string $pluginId Plugin name, core.conf by default - * @param null $defaultValue - * @return mixed - */ - public static function getUserPersonalParameter($parameterName, $userIdOrObject, $pluginId="core.conf", $defaultValue=null){ - - $cacheId = $pluginId."-".$parameterName; - if(!isSet(self::$usersParametersCache[$cacheId])){ - self::$usersParametersCache[$cacheId] = array(); - } - // Passed an already loaded object - if(is_a($userIdOrObject, "AbstractAjxpUser")){ - $value = $userIdOrObject->personalRole->filterParameterValue($pluginId, $parameterName, AJXP_REPO_SCOPE_ALL, $defaultValue); - self::$usersParametersCache[$cacheId][$userIdOrObject->getId()] = $value; - if(empty($value) && !empty($defaultValue)) $value = $defaultValue; - return $value; - } - // Already in memory cache - if(isSet(self::$usersParametersCache[$cacheId][$userIdOrObject])){ - return self::$usersParametersCache[$cacheId][$userIdOrObject]; - } - - // Try to load personal role if it was already loaded. - $uRole = AuthService::getRole("AJXP_USR_/".$userIdOrObject); - if($uRole === false){ - $uObject = self::getConfStorageImpl()->createUserObject($userIdOrObject); - if(isSet($uObject)){ - $uRole = $uObject->personalRole; - } - } - if(empty($uRole)){ - return $defaultValue; - } - $value = $uRole->filterParameterValue($pluginId, $parameterName, AJXP_REPO_SCOPE_ALL, $defaultValue); - if(empty($value) && !empty($defaultValue)) { - $value = $userIdOrObject; - } - self::$usersParametersCache[$cacheId][$userIdOrObject] = $value; - return $value; - - } - - /** - * Set the language in the session - * @static - * @param string $lang - * @return void - */ - public static function setLanguage($lang) - { - self::getInstance()->setLanguageInst($lang); - } - /** - * See static method - * @param string $lang - * @return void - */ - public function setLanguageInst($lang) - { - if (array_key_exists($lang, $this->configs["AVAILABLE_LANG"])) { - $this->configs["LANGUE"] = $lang; - } - } - /** - * Get the language from the session - * @static - * @return string - */ - public static function getLanguage() - { - $lang = self::getInstance()->getConfInst("LANGUE"); - if ($lang == null) { - $lang = self::getInstance()->getCoreConf("DEFAULT_LANGUAGE"); - } - if(empty($lang)) return "en"; - return $lang; - } - - /** - * The current repository - * @return Repository - */ - public static function getRepository() - { - return self::getInstance()->getRepositoryInst(); - } - /** - * See static method - * @return Repository - */ - public function getRepositoryInst() - { - $ctxId = $this->getContextRepositoryId(); - if (!empty($ctxId) && isSet($this->configs["REPOSITORIES"]) && isSet($this->configs["REPOSITORIES"][$ctxId])) { - return $this->configs["REPOSITORIES"][$ctxId]; - } - return isSet($this->configs["REPOSITORY"])?$this->configs["REPOSITORY"]:null; - } - - /** - * Returns the repository access driver - * @return AJXP_Plugin - */ - public static function loadRepositoryDriver() - { - return self::getInstance()->loadRepositoryDriverInst(); - } - - /** - * @static - * @param Repository $repository - * @return AbstractAccessDriver - */ - public static function loadDriverForRepository(&$repository) - { - return self::getInstance()->loadRepositoryDriverInst($repository); - } - - /** - * See static method - * @param Repository|null $repository - * @throws AJXP_Exception|Exception - * @return AbstractAccessDriver - */ - private function loadRepositoryDriverInst(&$repository = null) - { - $rest = false; - if($repository == null){ - if (isSet($this->configs["ACCESS_DRIVER"]) && is_a($this->configs["ACCESS_DRIVER"], "AbstractAccessDriver")) { - return $this->configs["ACCESS_DRIVER"]; - } - $this->switchRootDirInst(); - $repository = $this->getRepositoryInst(); - if($repository == null){ - throw new Exception("No active repository found for user!"); - } - }else{ - $rest = true; - if (isset($repository->driverInstance)) { - return $repository->driverInstance; - } - } - /** - * @var AbstractAccessDriver $plugInstance - */ - $accessType = $repository->getAccessType(); - $pServ = AJXP_PluginsService::getInstance(); - $plugInstance = $pServ->getPluginByTypeName("access", $accessType); - - // TRIGGER BEFORE INIT META - $metaSources = $repository->getOption("META_SOURCES"); - if (isSet($metaSources) && is_array($metaSources) && count($metaSources)) { - $keys = array_keys($metaSources); - foreach ($keys as $plugId) { - if($plugId == "") continue; - $instance = $pServ->getPluginById($plugId); - if (!is_object($instance)) { - continue; - } - if (!method_exists($instance, "beforeInitMeta")) { - continue; - } - try { - $instance->init(AuthService::filterPluginParameters($plugId, $metaSources[$plugId], $repository->getId())); - $instance->beforeInitMeta($plugInstance, $repository); - } catch (Exception $e) { - AJXP_Logger::error(__CLASS__, 'Meta plugin', 'Cannot instanciate Meta plugin, reason : '.$e->getMessage()); - $this->errors[] = $e->getMessage(); - } - } - } - - // INIT MAIN DRIVER - $plugInstance->init($repository); - try { - $plugInstance->initRepository(); - $repository->driverInstance = $plugInstance; - } catch (Exception $e) { - if(!$rest){ - // Remove repositories from the lists - if(!is_a($e, "AJXP_UserAlertException")){ - $this->removeRepositoryFromCache($repository->getId()); - } - if (isSet($_SESSION["PREVIOUS_REPO_ID"]) && $_SESSION["PREVIOUS_REPO_ID"] !=$repository->getId()) { - $this->switchRootDir($_SESSION["PREVIOUS_REPO_ID"]); - } else { - $this->switchRootDir(); - } - } - throw $e; - } - - AJXP_PluginsService::deferBuildingRegistry(); - $pServ->setPluginUniqueActiveForType("access", $accessType); - - // TRIGGER INIT META - $metaSources = $repository->getOption("META_SOURCES"); - if (isSet($metaSources) && is_array($metaSources) && count($metaSources)) { - $keys = array_keys($metaSources); - foreach ($keys as $plugId) { - if($plugId == "") continue; - $split = explode(".", $plugId); - $instance = $pServ->getPluginById($plugId); - if (!is_object($instance)) { - continue; - } - try { - $instance->init(AuthService::filterPluginParameters($plugId, $metaSources[$plugId], $repository->getId())); - if(!method_exists($instance, "initMeta")) { - throw new Exception("Meta Source $plugId does not implement the initMeta method."); - } - $instance->initMeta($plugInstance); - } catch (Exception $e) { - AJXP_Logger::error(__CLASS__, 'Meta plugin', 'Cannot instanciate Meta plugin, reason : '.$e->getMessage()); - $this->errors[] = $e->getMessage(); - } - $pServ->setPluginActive($split[0], $split[1]); - } - } - AJXP_PluginsService::flushDeferredRegistryBuilding(); - if (count($this->errors)>0) { - $e = new AJXP_Exception("Error while loading repository feature : ".implode(",",$this->errors)); - if(!$rest){ - // Remove repositories from the lists - $this->removeRepositoryFromCache($repository->getId()); - if (isSet($_SESSION["PREVIOUS_REPO_ID"]) && $_SESSION["PREVIOUS_REPO_ID"] !=$repository->getId()) { - $this->switchRootDir($_SESSION["PREVIOUS_REPO_ID"]); - } else { - $this->switchRootDir(); - } - } - throw $e; - } - if($rest){ - $ctxId = $this->getContextRepositoryId(); - if ( (!empty($ctxId) || $ctxId === 0) && $ctxId == $repository->getId()) { - $this->configs["REPOSITORY"] = $repository; - $this->cacheRepository($ctxId, $repository); - } - } else { - $this->configs["ACCESS_DRIVER"] = $plugInstance; - } - return $plugInstance; - } - - /** - * Search the manifests declaring ajxpdriver as their root node. Remove ajxp_* drivers - * @static - * @param string $filterByTagName - * @param string $filterByDriverName - * @param bool $limitToEnabledPlugins - * @return string - */ - public static function availableDriversToXML($filterByTagName = "", $filterByDriverName="", $limitToEnabledPlugins = false) - { - $nodeList = AJXP_PluginsService::searchAllManifests("//ajxpdriver", "node", false, $limitToEnabledPlugins); - $xmlBuffer = ""; - foreach ($nodeList as $node) { - $dName = $node->getAttribute("name"); - if($filterByDriverName != "" && $dName != $filterByDriverName) continue; - if(strpos($dName, "ajxp_") === 0) continue; - if ($filterByTagName == "") { - $xmlBuffer .= $node->ownerDocument->saveXML($node); - continue; - } - $q = new DOMXPath($node->ownerDocument); - $cNodes = $q->query("//".$filterByTagName, $node); - $xmlBuffer .= "attributes as $attr) $xmlBuffer.= " $attr->name=\"$attr->value\" "; - $xmlBuffer .=">"; - foreach ($cNodes as $child) { - $xmlBuffer .= $child->ownerDocument->saveXML($child); - } - $xmlBuffer .= ""; - } - return $xmlBuffer; - } - - /** - * Singleton method - * - * @return ConfService the service instance - */ - public static function getInstance() - { - if (!isSet(self::$instance)) { - $c = __CLASS__; - self::$instance = new $c; - } - return self::$instance; - } - private function __construct(){} - public function __clone() - { - trigger_error("Cannot clone me, i'm a singleton!", E_USER_ERROR); - } - -} diff --git a/core/src/core/classes/class.ContentFilter.php b/core/src/core/classes/class.ContentFilter.php deleted file mode 100644 index 5132a9822b..0000000000 --- a/core/src/core/classes/class.ContentFilter.php +++ /dev/null @@ -1,133 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Class ContentFilter - */ -class ContentFilter { - - public $filters = array(); - public $virtualPaths = array(); - - /** - * @param AJXP_Node[] $nodes - */ - function __construct($nodes){ - foreach($nodes as $n){ - $virtualPath = $this->getVirtualPath($n->getPath()); - $this->filters[$n->getPath()] = $virtualPath; - } - $this->virtualPaths = array_flip($this->filters); - } - - private function getVirtualPath($path){ - return "/".substr(md5($path), 0, 10)."/".basename($path); - } - - /** - * @param UserSelection $userSelection - */ - function filterUserSelection( &$userSelection ){ - if($userSelection->isEmpty()){ - foreach($this->filters as $path => $virtual){ - $userSelection->addFile($path); - } - }else{ - $newFiles = array(); - foreach($userSelection->getFiles() as $f){ - if(isSet($this->virtualPaths[$f])){ - $newFiles[] = $this->virtualPaths[$f]; - }else{ - $testB = base64_decode($f); - if(isSet($this->virtualPaths[$testB])){ - $newFiles[] = $this->virtualPaths[$testB]; - } - } - } - $userSelection->setFiles($newFiles); - } - } - - function getBaseDir(){ - return AJXP_Utils::safeDirname(array_keys($this->filters)[0]); - } - - function getUniquePath(){ - return AJXP_Utils::safeBasename(array_keys($this->filters)[0]); - } - - /** - * @param AJXP_Node $node - * @return String - */ - function externalPath(AJXP_Node $node){ - return $this->getVirtualPath($node->getPath()); - } - - /** - * @param String $vPath - * @return String mixed - */ - function filterExternalPath($vPath){ - if(isSet($this->virtualPaths) && isSet($this->virtualPaths[$vPath])){ - return $this->virtualPaths[$vPath]; - } - return $vPath; - } - - /** - * @param String $oldPath - * @param String $newPath - * @return bool Operation result - */ - public function movePath($oldPath, $newPath){ - - if(isSet($this->filters[$oldPath])){ - $this->filters[$newPath] = $this->getVirtualPath($newPath); - unset($this->filters[$oldPath]); - $this->virtualPaths = array_flip($this->filters); - return true; - } - return false; - - } - - /** - * @return array public data as array, pre-utf8 encoded - */ - public function toArray(){ - $data = array("filters" => array(), "virtualPaths" => array()); - foreach($this->filters as $k => $v){ - $data["filters"][SystemTextEncoding::toUTF8($k)] = SystemTextEncoding::toUTF8($v); - } - foreach($this->virtualPaths as $k => $v){ - $data["virtualPaths"][SystemTextEncoding::toUTF8($k)] = SystemTextEncoding::toUTF8($v); - } - return $data; - } - - public function fromFilterArray($filters){ - $this->filters = $filters; - $this->virtualPaths = array_flip($this->filters); - } - -} \ No newline at end of file diff --git a/core/src/core/classes/class.HTMLWriter.php b/core/src/core/classes/class.HTMLWriter.php deleted file mode 100644 index d612bb63a5..0000000000 --- a/core/src/core/classes/class.HTMLWriter.php +++ /dev/null @@ -1,230 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Static functions for generating HTML. - * @package Pydio - * @subpackage Core - */ -class HTMLWriter -{ - /** - * Write an HTML block message - * @static - * @param string $logMessage - * @param string $errorMessage - * @return void - */ - public static function displayMessage($logMessage, $errorMessage) - { - $mess = ConfService::getMessages(); - echo "
    ".(isset($logMessage)?$logMessage:$errorMessage)."".$mess[98]."
    "; - echo ""; - } - - /** - * Replace the doc files keywords - * @static - * @param string $docFileName - * @return string - */ - public static function getDocFile($docFileName) - { - $realName = AJXP_DOCS_FOLDER."/".$docFileName.".txt"; - if (is_file($realName)) { - $content = implode("
    ", file($realName)); - $content = preg_replace("(http:\/\/[a-z|.|\/|\-|0-9]*)", "$0", $content); - $content = preg_replace("(\[(.*)\])", "
    $1
    ", $content); - $content = preg_replace("(\+\+ (.*) \+\+)", "
    $1
    ", $content); - $content = str_replace("__AJXP_VERSION__", AJXP_VERSION, $content); - $content = str_replace("__AJXP_VERSION_DATE__", AJXP_VERSION_DATE, $content); - return $content; - } - return "File not found : ".$docFileName; - } - /** - * Write repository data directly as javascript string - * @static - * @return mixed|string - */ - public static function repositoryDataAsJS() - { - if(AuthService::usersEnabled()) return ""; - require_once(AJXP_BIN_FOLDER."/class.SystemTextEncoding.php"); - require_once(AJXP_BIN_FOLDER."/class.AJXP_XMLWriter.php"); - return str_replace("'", "\'", AJXP_XMLWriter::writeRepositoriesData(null)); - } - /** - * Write the messages as Javascript - * @static - * @param array $mess - * @return void - */ - public static function writeI18nMessagesClass($mess) - { - echo "\n"; - } - - /** - * Send a simple Content-type header - * @static - */ - public static function internetExplorerMainDocumentHeader() - { - if (strstr($_SERVER["HTTP_USER_AGENT"], "MSIE 9.")) { - header("X-UA-Compatible: IE=9"); - } else if (strstr($_SERVER["HTTP_USER_AGENT"], "MSIE 10.")) { - header("X-UA-Compatible: IE=Edge,chrome=1"); - } - } - - /** - * Send a simple Content-type header - * @static - * @param string $type - * @param string $charset - * @return void - */ - public static function charsetHeader($type = 'text/html', $charset='UTF-8') - { - header("Content-type:$type; charset=$charset"); - } - /** - * Write a closing sequence - * @static - * @return void - */ - public static function closeBodyAndPage() - { - print(""); - } - - /** - * Write directly an error as a javascript instruction - * @static - * @param $errorType - * @param $errorMessage - */ - public static function javascriptErrorHandler($errorType, $errorMessage) - { - // Handle "@" case! - if(error_reporting() == 0) return ; - restore_error_handler(); - die(""); - } - - public static function encodeAttachmentName($name){ - if (preg_match('/ MSIE /',$_SERVER['HTTP_USER_AGENT']) - || preg_match('/ WebKit /',$_SERVER['HTTP_USER_AGENT']) - || preg_match('/ Trident/',$_SERVER['HTTP_USER_AGENT'])) { - $name = str_replace("+", " ", urlencode(SystemTextEncoding::toUTF8($name))); - } - return $name; - } - - /** - * @static - * @param string $attachmentName - * @param int $dataSize - * @param bool $isFile - * @param bool $gzip If true, make sure the $dataSize is the size of the ENCODED data. - */ - public static function generateAttachmentsHeader(&$attachmentName, $dataSize, $isFile=true, $gzip=false) - { - $attachmentName = self::encodeAttachmentName($attachmentName); - - header("Content-Type: application/force-download; name=\"".$attachmentName."\""); - header("Content-Transfer-Encoding: binary"); - if ($gzip) { - header("Content-Encoding: gzip"); - } - if ($dataSize >= 0) { - header("Content-Length: ".$dataSize); - if ($isFile && ($dataSize != 0)) { - header("Content-Range: bytes 0-" . ($dataSize- 1) . "/" . $dataSize . ";"); - } - } - header("Content-Disposition: attachment; filename=\"".$attachmentName."\""); - header("Expires: 0"); - header("Cache-Control: no-cache, must-revalidate"); - header("Pragma: no-cache"); - if (preg_match('/ MSIE /',$_SERVER['HTTP_USER_AGENT'])) { - header("Cache-Control: max_age=0"); - header("Pragma: public"); - } - - // IE8 is dumb - if (preg_match('/ MSIE /',$_SERVER['HTTP_USER_AGENT'])) { - header("Pragma: public"); - header("Expires: 0"); - header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); - header("Cache-Control: private",false); - } - - // For SSL websites there is a bug with IE see article KB 323308 - // therefore we must reset the Cache-Control and Pragma Header - if (ConfService::getConf("USE_HTTPS")==1 && preg_match('/ MSIE /',$_SERVER['HTTP_USER_AGENT'])) { - header("Cache-Control:"); - header("Pragma:"); - } - } - - public static function generateInlineHeaders($attachName, $fileSize, $mimeType) - { - $attachName = self::encodeAttachmentName($attachName); - - //Send headers - header("Content-Type: " . $mimeType . "; name=\"" . $attachName . "\""); - header("Content-Disposition: inline; filename=\"" . $attachName . "\""); - // changed header for IE 7 & 8 - if (preg_match('/ MSIE /',$_SERVER['HTTP_USER_AGENT'])) { - header("Pragma: public"); - header("Expires: 0"); - header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); - header("Cache-Control: private",false); - } else { - header("Cache-Control: public"); - } - header("Content-Length: " . $fileSize); - - // Neccessary for IE 8 and xx - if (ConfService::getConf("USE_HTTPS")==1 && preg_match('/ MSIE /',$_SERVER['HTTP_USER_AGENT'])) { - header("Cache-Control:"); - header("Pragma:"); - } - - } - -} diff --git a/core/src/core/classes/class.HttpClient.php b/core/src/core/classes/class.HttpClient.php deleted file mode 100644 index 234942b12e..0000000000 --- a/core/src/core/classes/class.HttpClient.php +++ /dev/null @@ -1,499 +0,0 @@ -cookies array ready for the next request - // Note: This currently ignores the cookie path (and time) completely. Time is not important, - // but path could possibly lead to security problems. - public $persist_referers = true; // For each request, sends path of last request as referer - public $debug = false; - public $handle_redirects = true; // Auaomtically redirect if Location or URI header is found - public $max_redirects = 5; - public $headers_only = false; // If true, stops receiving once headers have been read. - // Basic authorization variables - public $username; - public $password; - // Response vars - public $status; - public $headers = array(); - public $content = ''; - public $errormsg; - // Tracker variables - public $redirect_count = 0; - public $cookie_host = ''; - public $postFileName = "userfile"; - public $postFileData = array(); - public $postDataArray = array(); - - public $directForwarding = false; - /** - * @var bool|resource - */ - public $contentDestStream = false; - /** - * @var bool|callable - */ - public $eventListener = false; - - public $collectHeaders; - - public function __construct($host, $port=80) - { - $this->host = $host; - $this->port = $port; - } - public function get($path, $data = false) - { - $this->path = $path; - $this->method = 'GET'; - if ($data) { - $this->path .= '?'.$this->buildQueryString($data); - } - return $this->doRequest(); - } - public function post($path, $data) - { - $this->path = $path; - $this->method = 'POST'; - $this->postdata = $this->buildQueryString($data); - return $this->doRequest(); - } - public function postFile($path, $postData, $fileVarName, $fileData) - { - $this->path = $path; - $this->method = 'POST'; - $this->postFileData = $fileData; - $this->postDataArray = $postData; - $this->postFileName = $fileVarName; - $this->postdata = $this->buildQueryString($postData); - return $this->doRequest(); - } - - public function writeContentToStream($destStream) - { - $this->contentDestStream = $destStream; - } - - public function clearContentDestStream() - { - $this->contentDestStream = false; - } - - public function setEventListener($callback) - { - $this->eventListener = $callback; - } - - public function notify($eventName, $data = null) - { - if($this->eventListener == false) return; - call_user_func($this->eventListener, $eventName, $data); - } - - public function buildQueryString($data) - { - $querystring = ''; - if (is_array($data)) { - // Change data in to postable data - foreach ($data as $key => $val) { - if (is_array($val)) { - foreach ($val as $val2) { - $querystring .= urlencode($key).'='.urlencode($val2).'&'; - } - } else { - $querystring .= urlencode($key).'='.urlencode($val).'&'; - } - } - $querystring = substr($querystring, 0, -1); // Eliminate unnecessary & - } else { - $querystring = $data; - } - return $querystring; - } - public function doRequest() - { - // Performs the actual HTTP request, returning true or false depending on outcome - $this->notify("open"); - if (!$fp = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout)) { - // Set error message - switch ($errno) { - case -3: - $this->errormsg = 'Socket creation failed (-3)'; - break; - case -4: - $this->errormsg = 'DNS lookup failure (-4)'; - break; - case -5: - $this->errormsg = 'Connection refused or timed out (-5)'; - break; - default: - $this->errormsg = 'Connection failed on '.$this->host.'('.$errno.')'; - $this->errormsg .= ' '.$errstr; - $this->debug($this->errormsg); - } - $this->notify("error", $this->errormsg); - $this->notify("close"); - return false; - } - socket_set_timeout($fp, $this->timeout); - $request = $this->buildRequest(); - $this->debug('Request', $request); - @fwrite($fp, $request); - // Reset all the variables that should not persist between requests - $this->headers = array(); - $this->content = ''; - $this->errormsg = ''; - // Set a couple of flags - $inHeaders = true; - $atStart = true; - $parsedHeaders = false; - $totalReadSize = 0; - // Now start reading back the response - while (!feof($fp)) { - @set_time_limit(60); - $line = fgets($fp, 4096); - if ($atStart) { - // Deal with first line of returned data - $atStart = false; - if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/', $line, $m)) { - $this->errormsg = "Status code line invalid: ".htmlentities($line); - $this->debug($this->errormsg); - $this->notify("error", $this->errormsg); - $this->notify("close"); - return false; - } - $http_version = $m[1]; // not used - $this->status = $m[2]; - $status_string = $m[3]; // not used - $this->debug(trim($line)); - continue; - } - if ($inHeaders) { - if (trim($line) == '') { - $inHeaders = false; - $this->debug('Received Headers', $this->headers); - if (isSet($this->collectHeaders)) { - foreach ($this->headers as $hKey => $hValue) { - if (isSet($this->collectHeaders[$hKey])) { - if($hKey == "content-length" && $hValue == "0") continue; - $this->collectHeaders[$hKey] = $hValue; - AJXP_Logger::debug("Setting $hKey", $this->collectHeaders); - } - } - } - if ($this->persist_cookies && isset($this->headers['set-cookie']) && $this->host == $this->cookie_host) { - $cookies = $this->headers['set-cookie']; - if (!is_array($cookies)) { - $cookies = array($cookies); - } - foreach ($cookies as $cookie) { - if (preg_match('/([^=]+)=([^;]+);/', $cookie, $m)) { - $this->cookies[$m[1]] = $m[2]; - } - } - // Record domain of cookies for security reasons - $this->cookie_host = $this->host; - } - // If $persist_referers, set the referer ready for the next request - if ($this->persist_referers) { - //$this->debug('Persisting referer: '.$this->getRequestURL()); - $this->referer = $this->getRequestURL(); - } - // Finally, if handle_redirects and a redirect is sent, do that - if ($this->handle_redirects) { - if (++$this->redirect_count >= $this->max_redirects) { - $this->errormsg = 'Number of redirects exceeded maximum ('.$this->max_redirects.')'; - $this->debug($this->errormsg); - $this->redirect_count = 0; - return false; - } - $location = isset($this->headers['location']) ? $this->headers['location'] : ''; - $uri = isset($this->headers['uri']) ? $this->headers['uri'] : ''; - if ($location || $uri) { - $url = parse_url($location.$uri); - // This will FAIL if redirect is to a different site - $this->debug("Should redirect! ", $url); - $data = array(); - if (isSet($url['query'])) { - parse_str($url["query"], $data); - } - $this->host = $url["host"]; - fclose($fp); - if (isSet($this->collectHeaders) && isSet($this->collectHeaders["ajxp-last-redirection"])) { - $this->collectHeaders["ajxp-last-redirection"] = $location.$uri; - } - return $this->get($url['path'], (!empty($data)?$data:false)); - } - } - - if ($this->headers_only) { - break; // Skip the rest of the input - } - continue; - } - if (!preg_match('/([^:]+):\\s*(.*)/', $line, $m)) { - // Skip to the next header - continue; - } - $key = strtolower(trim($m[1])); - $val = trim($m[2]); - if ($this->directForwarding) { - header($line, true); - continue; - } - - // Deal with the possibility of multiple headers of same name - if (isset($this->headers[$key])) { - if (is_array($this->headers[$key])) { - $this->headers[$key][] = $val; - } else { - $this->headers[$key] = array($this->headers[$key], $val); - } - } else { - $this->headers[$key] = $val; - } - continue; - } - - // We're not in the headers, so append the line to the contents - if ($this->directForwarding) { - print $line; - continue; - } - if ($this->contentDestStream===false) { - $this->content .= $line; - } else { - fwrite($this->contentDestStream, $line); - } - $totalReadSize += strlen($line); - $this->notify("data_read", $totalReadSize); - } - $this->notify("close"); - fclose($fp); - if ($this->directForwarding) { - return true; - } - // If data is compressed, uncompress it - if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] == 'gzip') { - $this->debug('Content is gzip encoded, unzipping it'); - if (!$this->headers_only) { - $this->content = substr($this->content, 10); // See http://www.php.net/manual/en/function.gzencode.php - $this->content = gzinflate($this->content); - } - } - // $this->debug("CONTENT : ".htmlentities($this->content)); - return true; - } - public function buildRequest() - { - $headers = array(); - $headers[] = "{$this->method} {$this->path} HTTP/1.0"; // Using 1.1 leads to all manner of problems, such as "chunked" encoding - $headers[] = "Host: {$this->host}"; - $headers[] = "User-Agent: {$this->user_agent}"; - $headers[] = "Accept: {$this->accept}"; - if ($this->use_gzip) { - $headers[] = "Accept-encoding: {$this->accept_encoding}"; - } - $headers[] = "Accept-language: {$this->accept_language}"; - if ($this->referer) { - $headers[] = "Referer: {$this->referer}"; - } - // Cookies - if ($this->cookies) { - $cookie = 'Cookie: '; - foreach ($this->cookies as $key => $value) { - $cookie .= "$key=$value; "; - } - $headers[] = $cookie; - } - // Basic authentication - if ($this->username && $this->password) { - $headers[] = 'Authorization: BASIC '.base64_encode($this->username.':'.$this->password); - } - if (!count($this->postFileData)) { - // If this is a POST, set the content type and length - if ($this->postdata) { - $headers[] = 'Content-Type: application/x-www-form-urlencoded'; - $headers[] = 'Content-Length: '.strlen($this->postdata); - } - $request = implode("\r\n", $headers)."\r\n\r\n".$this->postdata; - } else { - srand((double) microtime()*1000000); - $boundary = "----".substr(md5(rand(0,32000)),0,10); - $headers[] = "Content-Type: multipart/form-data; boundary=$boundary"; - $data = array(); - // attach post vars - $this->postDataArray["Filename"] = $this->postFileData["name"]; - foreach ($this->postDataArray as $index => $value) { - $data[]="--$boundary"; - $data[]= "content-disposition: form-data; name=\"".$index."\""; - $data[]= "\r\n".$value.""; - } - // and attach the file - //$data[]= "--$boundary"; - $content_file = join("", file($this->postFileData["tmp_name"])); - $data[]="--$boundary"; - $data[]="content-disposition: form-data; name=\"".$this->postFileName."\"; filename=\"".$this->postFileData["name"]."\""; - $data[]= "Content-Type: ".$this->postFileData['type']."\r\n"; - $data[]= "".$content_file.""; - $data[]="--$boundary--"; - //$headers[]= "Content-Length: " . strlen(implode("",$data)); - $data = implode("\r\n", $data); - $headers[]= "Content-Length: " . strlen($data); - $headers[] = "Cache-Control: no-cache"; - $headers[] = "Connection: Keep-Alive"; - $request = implode("\r\n", $headers)."\r\n\r\n".$data; - } - return $request; - } - public function getStatus() - { - return $this->status; - } - public function getContent() - { - return $this->content; - } - public function getHeaders() - { - return $this->headers; - } - public function getHeader($header) - { - $header = strtolower($header); - if (isset($this->headers[$header])) { - return $this->headers[$header]; - } else { - return false; - } - } - public function getError() - { - return $this->errormsg; - } - public function getCookies() - { - return $this->cookies; - } - public function getRequestURL() - { - $url = 'http://'.$this->host; - if ($this->port != 80) { - $url .= ':'.$this->port; - } - $url .= $this->path; - return $url; - } - // Setter methods - public function setUserAgent($string) - { - $this->user_agent = $string; - } - public function setAuthorization($username, $password) - { - $this->username = $username; - $this->password = $password; - } - public function setCookies($array) - { - $this->cookies = $array; - } - // Option setting methods - public function useGzip($boolean) - { - $this->use_gzip = $boolean; - } - public function setPersistCookies($boolean) - { - $this->persist_cookies = $boolean; - } - public function setPersistReferers($boolean) - { - $this->persist_referers = $boolean; - } - public function setHandleRedirects($boolean) - { - $this->handle_redirects = $boolean; - } - public function setMaxRedirects($num) - { - $this->max_redirects = $num; - } - public function setHeadersOnly($boolean, &$collectHeaders = null) - { - $this->headers_only = $boolean; - if ($collectHeaders != null) { - $this->collectHeaders = $collectHeaders; - } - } - public function setDebug($boolean) - { - $this->debug = $boolean; - } - // "Quick" static methods - public function quickGet($url) - { - $bits = parse_url($url); - $host = $bits['host']; - $port = isset($bits['port']) ? $bits['port'] : 80; - $path = isset($bits['path']) ? $bits['path'] : '/'; - if (isset($bits['query'])) { - $path .= '?'.$bits['query']; - } - $client = new HttpClient($host, $port); - if (!$client->get($path)) { - return false; - } else { - return $client->getContent(); - } - } - public function quickPost($url, $data) - { - $bits = parse_url($url); - $host = $bits['host']; - $port = isset($bits['port']) ? $bits['port'] : 80; - $path = isset($bits['path']) ? $bits['path'] : '/'; - $client = new HttpClient($host, $port); - if (!$client->post($path, $data)) { - return false; - } else { - return $client->getContent(); - } - } - public function debug($msg, $object = false) - { - if ($this->debug) { - $st = '
    HttpClient Debug: '.$msg; - if ($object) { - ob_start(); - print_r($object); - $content = htmlentities(ob_get_contents()); - ob_end_clean(); - $st .= '
    '.$content.'
    '; - } - $st .= '
    '; - AJXP_Logger::debug($msg . ($object!==false?" - ".print_r($object, true):"")); - } - } -} diff --git a/core/src/core/classes/class.PydioSdkGenerator.php b/core/src/core/classes/class.PydioSdkGenerator.php deleted file mode 100644 index 99e8a1f715..0000000000 --- a/core/src/core/classes/class.PydioSdkGenerator.php +++ /dev/null @@ -1,227 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - * - * - */ - -/** - * Class PydioSdkGenerator - * Generate a Swagger file for Rest APIs documentation - * @package Pydio - * @subpackage Core - */ - -//define("JSON_DIR", AJXP_INSTALL_PATH."/core/doc/api"); -define("JSON_DIR", AJXP_INSTALL_PATH."/../api"); -define("JSON_URL", "https://pydio.com/static-docs/api"); -define("API_DOC_PAGE", "https://pydio.com/en/docs/references/pydio-api#!/"); - -class PydioSdkGenerator -{ - static $apiGroups = [ - "fs" => ["access.fs", "index.*", "meta.*", "editor.*", "action.share", "action.powerfs"], - "conf" => ["access.ajxp_conf", "action.scheduler", "action.updater"], - "lifecycle" => ["conf.*", "auth.*", "gui.*", "core.*", "action.avatar"], - "nonfs" => ["access.*"], - "misc" => ["*"] - ]; - - static $apiGroupsLabels = [ - "fs" => "Most current operations on files and folders, their metadata, and additional sharing features.", - "conf" => "Administration task : users/groups/workspaces provisionning, maintenance tasks, etc... Generally performed using /settings/ as workspace alias.", - "lifecycle" => "Application objects lifecycle, like current user access rights and preferences, authentication utils, etc. As they are generally not linked to a specific workspace, these actions can be performed using /pydio/ instead of a workspace alias.", - "nonfs" => "Non-standard drivers accessing to structured data like IMAP, MySQL, Apis, etc.", - "misc" => "Other plugins actions." - ]; - - public static function findApiGroupForPlugin($pluginId){ - list($pType, $pName) = explode(".", $pluginId); - foreach(self::$apiGroups as $groupName => $pluginPatterns){ - foreach($pluginPatterns as $pattern){ - if($pattern == "*" || $pattern == "$pType.*" || $pattern == "$pType.$pName"){ - return $groupName; - } - } - } - return "misc"; - } - - public static function analyzeRegistry($versionString) - { - if(!AJXP_SERVER_DEBUG) { - echo "Please switch the server to debug mode to use this API."; - return; - } - - $pServ = AJXP_PluginsService::getInstance(); - $nodes = $pServ->searchAllManifests('//actions/*/processing/serverCallback[@developerComment]', 'node', false, false, true); - $jsFile = AJXP_DATA_PATH."/public/sdkMethods.js"; - $swaggerJsonDir = JSON_DIR."/".$versionString; - $swaggerAPIs = array(); - $methods = array(); - $alreadyParsed = array(); - foreach ($nodes as $callbackNode) { - $params = array(); - $swaggerParams = array(); - $pluginName = $callbackNode->parentNode->parentNode->parentNode->parentNode->parentNode->getAttribute("id"); - $actionName = $callbackNode->parentNode->parentNode->getAttribute("name"); - $methodName = $callbackNode->getAttribute("sdkMethodName"); - if(empty($methodName)){ - $methodName = $actionName; - } - $outputType = 'xml'; - /* - if(in_array($actionName, $alreadyParsed)){ - continue; - } - $alreadyParsed[] = $actionName; - */ - if(!isset($swaggerAPIs[$pluginName])) $swaggerAPIs[$pluginName] = array(); - - foreach ($callbackNode->childNodes as $child) { - if($child->nodeType != XML_ELEMENT_NODE)continue; - if ($child->nodeName == "input_param") { - $params[$child->getAttribute("name")] = array( - "name" => $child->getAttribute("name"), - "type" => $child->getAttribute("type"), - "mandatory" => $child->getAttribute("mandatory") === "true", - "default" => $child->getAttribute("default"), - ); - $default = $child->getAttribute("default"); - $swaggerParams[] = array( - "name" => $child->getAttribute("name"), - "description" => $child->getAttribute("description"). "
    ".(!empty($default) ? "Default: $default" : ""), - "required" => ($child->getAttribute("mandatory") === "true"), - "allowMultiple" => (strpos($child->getAttribute("type"), "[]") !== false), - "dataType" => (strpos($child->getAttribute("type"), "[]") !== false) ? "array":$child->getAttribute("type"), - "paramType" => "query" - ); - - } else if ($child->nodeName=="output") { - $outputType = $child->getAttribute("type"); - } - } - $methods[$methodName] = array( - "action" => $actionName, - "params" => $params, - "output" => $outputType - ); - $comment = $callbackNode->getAttribute("developerComment"); - $http = $callbackNode->getAttribute("preferredHttp"); - $restParams = $callbackNode->getAttribute("restParams"); - $prefix = "/workspace_alias"; - $apiGroup = self::findApiGroupForPlugin($pluginName); - if($apiGroup == "conf"){ - $prefix = "/settings"; - }else if($apiGroup == "lifecycle"){ - $prefix = "/pydio"; - } - $api = array( - "path" => $prefix."/".$actionName . (empty($restParams) ? "" : $restParams), - "operations" => array( - array( - "method" => empty($http) ? "POST" : strtoupper($http), - "summary" => substr($comment, 0, 80) . (strlen($comment) > 80 ? "..." : ""), - "notes" => $comment, - "responseClass" => $outputType, - "nickname" => $methodName, - "parameters" => $swaggerParams - ) - ) - ); - $swaggerAPIs[$pluginName][] = $api; - } - - file_put_contents($jsFile, "window.sdkMethods = ".json_encode($methods, JSON_PRETTY_PRINT)); - - $apidocs = array( - "apiVersion" => $versionString, - "swaggerVersion" => "1.2", - "apis" => array() - ); - $allDocs = array(); - $markdowns = array(); - - foreach($swaggerAPIs as $pluginName => $apis){ - - echo("Writing file for $pluginName"); - - $swaggerJson = array( - "apiVersion" => $versionString, - "swaggerVersion" => 1.2, - "basePath" => JSON_URL."/$versionString", - "resourcePath" => "/api", - "produces" => array("application/xml"), - "apis" => $apis - ); - file_put_contents($swaggerJsonDir."/".$pluginName, json_encode($swaggerJson, JSON_PRETTY_PRINT)); - $p = $pServ->findPluginById($pluginName); - $apiGroup = self::findApiGroupForPlugin($pluginName); - if(!isset($allDocs[$apiGroup])) { - $allDocs[$apiGroup] = array(); - $markdowns[$apiGroup] = array(); - } - $markdowns[$apiGroup][] = self::makeMarkdown($p, $apis); - $allDocs[$apiGroup][] = array( - "path" => JSON_URL."/$versionString/".$pluginName, - "description" => $p->getManifestDescription() - ); - $apidocs["apis"][] = array( - "path" => JSON_URL."/$versionString/".$pluginName, - "description" => $p->getManifestDescription() - ); - - } - foreach($allDocs as $apiGroupName => $groupApis){ - $groupApiDocs = array( - "apiVersion" => $versionString, - "swaggerVersion" => "1.2", - "apis" => $groupApis - ); - file_put_contents($swaggerJsonDir."/api-docs-".$apiGroupName, json_encode($groupApiDocs, JSON_PRETTY_PRINT)); - file_put_contents($swaggerJsonDir."/api-md-".$apiGroupName, self::$apiGroupsLabels[$apiGroupName]."\n\n".implode("", $markdowns[$apiGroupName])); - } - // Store file with all apis. - file_put_contents($swaggerJsonDir."/api-docs", json_encode($apidocs, JSON_PRETTY_PRINT)); - } - - /** - * @param AJXP_Plugin $plugin - * @param array $apis - * @return string - */ - static public function makeMarkdown($plugin, $apis){ - - $md = "\n\n"; - $md .= "## ".$plugin->getManifestLabel()." "; - $md .= "\n".$plugin->getManifestDescription()."\n\n"; - $id = $plugin->getId(); - foreach($apis as $index => $api) { - $md .= "\n"; - $md .= "- **".$api["path"]."** \n"; - $md .= " ".$api["operations"][0]["notes"]." \n"; - $md .= " [Details](".API_DOC_PAGE."".$id."/".$api["operations"][0]["nickname"]."_".strtolower($api["operations"][0]["method"])."_".$index.")"; - } - - return $md; - - } - -} diff --git a/core/src/core/classes/class.RecycleBinManager.php b/core/src/core/classes/class.RecycleBinManager.php deleted file mode 100644 index ce01dfec72..0000000000 --- a/core/src/core/classes/class.RecycleBinManager.php +++ /dev/null @@ -1,197 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -/** - * Recycle bin actions manager. Utilitaries to check whether the current dir is the recycle bin, - * and to filter the actions accordingly : transform an item deletion into a move into the recycle folder, etc. - * @package Pydio - * @subpackage Core - */ -class RecycleBinManager -{ - private static $rbmRecycle; - private static $rbmRelativeRecycle; - - public static function recycleEnabled() - { - return (isSet(self::$rbmRecycle) && self::$rbmRecycle != null && is_string(self::$rbmRecycle)); - } - - /** - * Initialize manager - * @static - * @param $repositoryWrapperURL - * @param $recyclePath - * @return void - */ - public static function init($repositoryWrapperURL, $recyclePath) - { - self::$rbmRecycle = $repositoryWrapperURL.$recyclePath; - self::$rbmRelativeRecycle = $recyclePath; - } - /** - * Get the recycle bin path (repository URL included) - * @static - * @return string - */ - public static function getRecyclePath() - { - return self::$rbmRecycle ; - } - /** - * Get the recycle bin path (from the root of the repository) - * @static - * @return string - */ - public static function getRelativeRecycle() - { - return self::$rbmRelativeRecycle; - } - /** - * Is the current path the recycle? - * @static - * @param string $currentLocation PATH from the root of repo - * @return bool - */ - public static function currentLocationIsRecycle($currentLocation) - { - return ($currentLocation == self::$rbmRelativeRecycle); - } - /** - * Transform delete/restore actions into move actino - * @static - * @param string $action - * @param UserSelection $selection - * @param string $currentLocation - * @param array $httpVars - * @return array - */ - public static function filterActions($action, $selection, $currentLocation, $httpVars = array()) - { - if(!self::recycleEnabled()) return array(); - $newArgs = array(); - - // FILTER ACTION FOR DELETE - if ($action == "delete" && !self::currentLocationIsRecycle($currentLocation) && !isSet($httpVars["force_deletion"])) { - $newArgs["action"] = "move"; - $newArgs["dest"] = self::$rbmRelativeRecycle; - } - // FILTER ACTION FOR RESTORE - if ($action == "restore" && self::currentLocationIsRecycle($currentLocation)) { - $originalRep = self::getFileOrigin($selection->getUniqueFile()); - if ($originalRep != "") { - $newArgs["action"] = "move"; - $newArgs["dest"] = $originalRep; // CHECK UTF8 HANDLING HERE - } - } - return $newArgs; - - } - /** - * Get the file for caching recylce metadata - * @static - * @return string - */ - public static function getCacheFileName() - { - return ".ajxp_recycle_cache.ser"; - } - /** - * Update metadata - * @static - * @param string $originalFilePath - * @return void - */ - public static function fileToRecycle($originalFilePath) - { - $cache = self::loadCache(); - $cache[basename($originalFilePath)] = str_replace("\\", "/", dirname($originalFilePath)); - self::saveCache($cache); - } - - /** - * Update metadata - * @static - * @param $filePath - * @return void - */ - public static function deleteFromRecycle($filePath) - { - $cache = self::loadCache(); - if (array_key_exists(basename($filePath), $cache)) { - unset($cache[basename($filePath)]); - } - self::saveCache($cache); - } - /** - * Use metadata for getting original location - * @static - * @param $filePath - * @return string - */ - public static function getFileOrigin($filePath) - { - $cache = self::loadCache(); - if (is_array($cache) && array_key_exists(basename($filePath), $cache)) { - return $cache[basename($filePath)]; - } - return ""; - } - /** - * Load the metadata cache - * @static - * @return array|mixed|null - */ - public static function loadCache() - { - $result = array(); - if(!self::recycleEnabled()) return null; - $cachePath = self::getRecyclePath()."/".self::getCacheFileName(); - $fp = @fopen($cachePath, "r"); - if ($fp) { - $s = ""; - while (!feof($fp)) { - $s .= fread($fp, 4096); - } - fclose($fp); - $result = unserialize($s); - } - return $result; - } - /** - * Save the metadata cache - * @static - * @param $value - * @return null - */ - public static function saveCache($value) - { - if(!self::recycleEnabled()) return null; - $cachePath = self::getRecyclePath()."/".self::getCacheFileName(); - $fp = fopen($cachePath, "w"); - if ($fp) { - fwrite($fp, serialize($value)); - fflush($fp); - fclose($fp); - } - } - -} diff --git a/core/src/core/classes/class.Repository.php b/core/src/core/classes/class.Repository.php deleted file mode 100644 index a9820e4699..0000000000 --- a/core/src/core/classes/class.Repository.php +++ /dev/null @@ -1,734 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -define('AJXP_REPOSITORY_TYPE_LOCAL', 'local'); -/** - * The basic abstraction of a data store. Can map a FileSystem, but can also map data from a totally - * different source, like the application configurations, a mailbox, etc. - * @package Pydio - * @subpackage Core - */ -class Repository implements AjxpGroupPathProvider -{ - /** - * @var string - */ - public $uuid; - /** - * @var string - */ - public $id; - /** - * @var string - */ - public $path; - /** - * @var string - */ - public $display; - /** - * @var string - */ - public $displayStringId; - /** - * @var string - */ - public $accessType = "fs"; - /** - * @var string - */ - public $recycle = ""; - /** - * @var bool - */ - public $create = true; - /** - * @var bool - */ - public $writeable = true; - /** - * @var bool - */ - public $enabled = true; - /** - * @var array - */ - public $options = array(); - /** - * @var string - */ - public $slug; - /** - * @var bool - */ - public $isTemplate = false; - - /** - * @var string local or remote - */ - protected $repositoryType = AJXP_REPOSITORY_TYPE_LOCAL; - - /** - * @var string status - */ - protected $accessStatus; - - /** - * @var string - */ - private $owner; - /** - * @var string - */ - private $parentId; - /** - * @var string - */ - private $uniqueUser; - /** - * @var bool - */ - private $inferOptionsFromParent; - /** - * @var Repository - */ - private $parentTemplateObject; - /** - * @var array - */ - public $streamData; - - /** - * @var String the groupPath of the administrator who created that repository. - */ - protected $groupPath; - - - /** - * @var AbstractAccessDriver - */ - public $driverInstance; - - /** - * @var ContentFilter - */ - protected $contentFilter; - - /** - * @var array Store filters as array variable to avoid serialization problems - */ - private $contentFilterData; - - /** - * @param \ContentFilter $contentFilter - */ - public function setContentFilter($contentFilter) - { - $this->contentFilter = $contentFilter; - $this->contentFilterData = $contentFilter->filters; - } - - /** - * Fix unserialization issues by reloading object from data array - */ - private function checkCFilterData(){ - if(!is_object($this->contentFilter) && is_array($this->contentFilterData)){ - $cf = new ContentFilter(array()); - $cf->fromFilterArray($this->contentFilterData); - $this->contentFilter = $cf; - } - } - - /** - * Check if a ContentFilter is set or not - * @return bool - */ - public function hasContentFilter(){ - $this->checkCFilterData(); - return isSet($this->contentFilter); - } - - /** - * @return \ContentFilter - */ - public function getContentFilter() - { - $this->checkCFilterData(); - return $this->contentFilter; - } - - /** - * @param string $id - * @param string $display - * @param string $driver - */ - public function __construct($id, $display, $driver) - { - $this->setAccessType($driver); - $this->setDisplay($display); - $this->setId($id); - $this->uuid = md5(microtime()); - $this->slug = AJXP_Utils::slugify($display); - $this->inferOptionsFromParent = false; - $this->options["CREATION_TIME"] = time(); - if (AuthService::usersEnabled() && AuthService::getLoggedUser() != null) { - $this->options["CREATION_USER"] = AuthService::getLoggedUser()->getId(); - } - } - - /** - * Create a shared version of this repository - * @param string $newLabel - * @param array $newOptions - * @param string $parentId - * @param string $owner - * @param string $uniqueUser - * @return Repository - */ - public function createSharedChild($newLabel, $newOptions, $parentId = null, $owner = null, $uniqueUser = null) - { - $repo = new Repository(0, $newLabel, $this->accessType); - $newOptions = array_merge($this->options, $newOptions); - $newOptions["CREATION_TIME"] = time(); - if (AuthService::usersEnabled() && AuthService::getLoggedUser() != null) { - $newOptions["CREATION_USER"] = AuthService::getLoggedUser()->getId(); - } - $repo->options = $newOptions; - if ($parentId === null) { - $parentId = $this->getId(); - } - $repo->setInferOptionsFromParent(true); - $repo->setOwnerData($parentId, $owner, $uniqueUser); - return $repo; - } - /** - * Create a child from this repository if it's a template - * @param string $newLabel - * @param array $newOptions - * @param string $owner - * @param string $uniqueUser - * @return Repository - */ - public function createTemplateChild($newLabel, $newOptions, $owner = null, $uniqueUser = null) - { - $repo = new Repository(0, $newLabel, $this->accessType); - $newOptions["CREATION_TIME"] = time(); - if (AuthService::usersEnabled() && AuthService::getLoggedUser() != null) { - $newOptions["CREATION_USER"] = AuthService::getLoggedUser()->getId(); - } - $repo->options = $newOptions; - $repo->setOwnerData($this->getId(), $owner, $uniqueUser); - $repo->setInferOptionsFromParent(true); - return $repo; - } - /** - * Recompute uuid - * @return bool - */ - public function upgradeId() - { - if (!isSet($this->uuid)) { - $this->uuid = md5(serialize($this)); - //$this->uuid = md5(time()); - return true; - } - return false; - } - /** - * Get a uuid - * @param bool $serial - * @return string - */ - public function getUniqueId($serial=false) - { - if ($serial) { - return md5(serialize($this)); - } - return $this->uuid; - } - /** - * Alias for this repository - * @return string - */ - public function getSlug() - { - return $this->slug; - } - /** - * Use the slugify function to generate an alias from the label - * @param string $slug - * @return void - */ - public function setSlug($slug = null) - { - if ($slug === null) { - $this->slug = AJXP_Utils::slugify($this->display); - } else { - $this->slug = $slug; - } - } - /** - * Get the content of the manifest.xml - * @return DOMElement|DOMNodeList|string - */ - public function getClientSettings() - { - $plugin = AJXP_PluginsService::findPlugin("access", $this->accessType); - if(!$plugin) return ""; - if (isSet($this->parentId)) { - $parentObject = ConfService::getRepositoryById($this->parentId); - if ($parentObject != null && $parentObject->isTemplate) { - $ic = $parentObject->getOption("TPL_ICON_SMALL"); - $settings = $plugin->getManifestRawContent("//client_settings", "node"); - if (!empty($ic) && $settings->length) { - $newAttr = $settings->item(0)->ownerDocument->createAttribute("icon_tpl_id"); - $newAttr->nodeValue = $this->parentId; - $settings->item(0)->appendChild($newAttr); - return $settings->item(0)->ownerDocument->saveXML($settings->item(0)); - } - } - } - return $plugin->getManifestRawContent("//client_settings", "string"); - } - /** - * Find the streamWrapper declared by the access driver - * @param bool $register - * @param array $streams - * @return bool - */ - public function detectStreamWrapper($register = false, &$streams=null) - { - if(isSet($this->driverInstance) && is_a($this->driverInstance, "AJXP_Plugin")){ - $plugin = $this->driverInstance; - }else{ - $plugin = AJXP_PluginsService::findPlugin("access", $this->accessType); - $this->driverInstance = $plugin; - } - if(!$plugin) return(false); - $streamData = $plugin->detectStreamWrapper($register); - if (!$register && $streamData !== false && is_array($streams)) { - $streams[$this->accessType] = $this->accessType; - } - if($streamData !== false) $this->streamData = $streamData; - return ($streamData !== false); - } - - /** - * Add options - * @param $oName - * @param $oValue - * @return void - */ - public function addOption($oName, $oValue) - { - if (strpos($oName, "PATH") !== false) { - $oValue = str_replace("\\", "/", $oValue); - } - $this->options[$oName] = $oValue; - } - - /** - * Get the repository options, filtered in various maners - * @param string $oName - * @param bool $safe Do not filter - * @param AbstractAjxpUser $resolveUser - * @return mixed|string - * @throws Exception - */ - public function getOption($oName, $safe=false, $resolveUser = null) - { - if(isSet($this->inferOptionsFromParent) && isSet($this->parentId)){ - $parentTemplateObject = ConfService::getRepositoryById($this->parentId); - if(empty($parentTemplateObject) || !is_a($parentTemplateObject, "Repository")) { - throw new Exception("Option should be loaded from parent repository, but it was not found"); - } - } - if (!$safe && $this->inferOptionsFromParent) { - if (isSet($parentTemplateObject)) { - $pvalue = $parentTemplateObject->getOption($oName, $safe); - $pathChanged = false; - if (is_string($pvalue) && strstr($pvalue, "AJXP_WORKSPACE_UUID") !== false) { - $pvalue = rtrim(str_replace("AJXP_WORKSPACE_UUID", $this->getUniqueId(), $pvalue), "/"); - $pathChanged = true; - } - if (is_string($pvalue) && strstr($pvalue, "AJXP_WORKSPACE_SLUG") !== false) { - $pvalue = rtrim(str_replace("AJXP_WORKSPACE_SLUG", $this->getSlug(), $pvalue), "/"); - $pathChanged = true; - } - if (is_string($pvalue) && strstr($pvalue, "AJXP_ALLOW_SUB_PATH") !== false) { - $pvalue = rtrim(str_replace("AJXP_ALLOW_SUB_PATH", "", $pvalue), "/")."/".$this->options[$oName]; - $pathChanged = true; - } - if ($pathChanged) { - return AJXP_Utils::securePath($pvalue); - } - } - } - if (isSet($this->options[$oName])) { - $value = $this->options[$oName]; - if(!$safe) $value = AJXP_VarsFilter::filter($value, $resolveUser); - return $value; - } - if ($this->inferOptionsFromParent) { - if (!isset($parentTemplateObject)) { - $parentTemplateObject = ConfService::getRepositoryById($this->parentId); - } - if (isSet($parentTemplateObject)) { - return $parentTemplateObject->getOption($oName, $safe); - } - } - return ""; - } - - public function resolveVirtualRoots($path) - { - // Gathered from the current role - $roots = $this->listVirtualRoots(); - if(!count($roots)) return $path; - foreach ($roots as $rootKey => $rootValue) { - if (strpos($path, "/".ltrim($rootKey, "/")) === 0) { - return preg_replace("/^\/{$rootKey}/", $rootValue["path"], $path, 1); - } - } - return $path; - - } - - public function listVirtualRoots() - { - return array(); - /* TEST STUB - $roots = array( - "root1" => array( - "right" => "rw", - "path" => "/Test"), - "root2" => array( - "right" => "r", - "path" => "/Retoto/sub" - )); - return $roots; - */ - } - - /** - * Get the options that already have a value - * @return array - */ - public function getOptionsDefined() - { - //return array_keys($this->options); - $keys = array(); - foreach ($this->options as $key => $value) { - if(is_string($value) && strstr($value, "AJXP_ALLOW_SUB_PATH") !== false) continue; - $keys[] = $key; - } - return $keys; - } - - /** - * Get the DEFAULT_RIGHTS option - * @return string - */ - public function getDefaultRight() - { - $opt = $this->getOption("DEFAULT_RIGHTS"); - return (isSet($opt)?$opt:""); - } - - - /** - * The the access driver type - * @return String - */ - public function getAccessType() - { - return $this->accessType; - } - - /** - * The label of this repository - * @return String - */ - public function getDisplay() - { - if (isSet($this->displayStringId)) { - $mess = ConfService::getMessages(); - if (isSet($mess[$this->displayStringId])) { - return $mess[$this->displayStringId]; - } - } - return AJXP_VarsFilter::filter($this->display); - } - - /** - * @return string - */ - public function getId() - { - if($this->isWriteable() || $this->id === null) return $this->getUniqueId(); - return $this->id; - } - - /** - * @return boolean - */ - public function getCreate() - { - return (bool) $this->getOption("CREATE"); - } - - /** - * @param boolean $create - */ - public function setCreate($create) - { - $this->options["CREATE"] = (bool) $create; - } - - - /** - * @param String $accessType - */ - public function setAccessType($accessType) - { - $this->accessType = $accessType; - } - - /** - * @param String $display - */ - public function setDisplay($display) - { - $this->display = $display; - } - - /** - * @param int $id - */ - public function setId($id) - { - $this->id = $id; - } - - public function isWriteable() - { - return (bool) $this->writeable; - } - - public function setWriteable($w) - { - $this->writeable = (bool) $w; - } - - public function isEnabled() - { - return (bool) $this->enabled; - } - - public function setEnabled($e) - { - $this->enabled = (bool) $e; - } - - public function setDisplayStringId($id) - { - $this->displayStringId = $id; - } - - public function setOwnerData($repoParentId, $ownerUserId = null, $childUserId = null) - { - $this->owner = $ownerUserId; - $this->uniqueUser = $childUserId; - $this->parentId = $repoParentId; - } - - public function getOwner() - { - return $this->owner; - } - - public function getParentId() - { - return $this->parentId; - } - - public function getUniqueUser() - { - return $this->uniqueUser; - } - - public function hasOwner() - { - return isSet($this->owner); - } - - public function hasParent() - { - return isSet($this->parentId); - } - - public function setInferOptionsFromParent($bool) - { - $this->inferOptionsFromParent = (bool) $bool; - } - - public function getInferOptionsFromParent() - { - return (bool) $this->inferOptionsFromParent; - } - - /** - * @param String $groupPath - */ - public function setGroupPath($groupPath) - { - if(strlen($groupPath) > 1) $groupPath = rtrim($groupPath, "/"); - $this->groupPath = $groupPath; - } - - /** - * @return String - */ - public function getGroupPath() - { - return $this->groupPath; - } - - /** - * @param String $descriptionText - */ - public function setDescription( $descriptionText ) - { - $this->options["USER_DESCRIPTION"] = $descriptionText; - } - - /** - * @return string - */ - public function getAccessStatus() - { - return $this->accessStatus; - } - - /** - * @param string $accessStatus - */ - public function setAccessStatus($accessStatus) - { - $this->accessStatus = $accessStatus; - } - - /** - * @return string - */ - public function getRepositoryType() - { - return $this->repositoryType; - } - - /** - * @param string $repositoryType - */ - public function setRepositoryType($repositoryType) - { - $this->repositoryType = $repositoryType; - } - - /** - * @param bool $public - * @param null $ownerLabel - * @return String - */ - public function getDescription( $public = false, $ownerLabel = null ) - { - $m = ConfService::getMessages(); - if (isset($this->options["USER_DESCRIPTION"]) && !empty($this->options["USER_DESCRIPTION"])) { - if (isSet($m[$this->options["USER_DESCRIPTION"]])) { - return $m[$this->options["USER_DESCRIPTION"]]; - } else { - return $this->options["USER_DESCRIPTION"]; - } - } - if (isSet($this->parentId) && isset($this->owner)) { - if (isSet($this->options["CREATION_TIME"])) { - $date = AJXP_Utils::relativeDate($this->options["CREATION_TIME"], $m); - return str_replace( - array("%date", "%user"), - array($date, $ownerLabel!= null ? $ownerLabel : $this->owner), - $public?$m["470"]:$m["473"]); - } else { - if($public) return $m["474"]; - else return str_replace( - array("%user"), - array($ownerLabel!= null ? $ownerLabel : $this->owner), - $m["472"]); - } - } else if ($this->isWriteable() && isSet($this->options["CREATION_TIME"])) { - $date = AJXP_Utils::relativeDate($this->options["CREATION_TIME"], $m); - if (isSet($this->options["CREATION_USER"])) { - return str_replace(array("%date", "%user"), array($date, $this->options["CREATION_USER"]), $m["471"]); - } else { - return str_replace(array("%date"), array($date), $m["470"]); - } - } else { - return $m["474"]; - } - } - - /** - * Infer a security scope for this repository. Will determine to whome the messages - * will be broadcasted. - * @return bool|string - */ - public function securityScope() - { - if($this->hasParent()){ - $parentRepo = ConfService::getRepositoryById($this->getParentId()); - if(!empty($parentRepo) && $parentRepo->isTemplate){ - $path = $parentRepo->getOption("PATH", true); - $container = $parentRepo->getOption("CONTAINER", true); - // If path is set in the template, compute identifier from the template path. - if(!empty($path) || !empty($container)) return $parentRepo->securityScope(); - } - } - $path = $this->getOption("CONTAINER", true); - if(!empty($path)){ - if(strpos($path, "AJXP_USER") !== false) return "USER"; - if(strpos($path, "AJXP_GROUP_PATH") !== false) return "GROUP"; - if(strpos($path, "AJXP_GROUP_PATH_FLAT") !== false) return "GROUP"; - } - $path = $this->getOption("PATH", true); - if($this->accessType == "ajxp_conf" || $this->accessType == "ajxp_admin" || $this->accessType == "inbox") return "USER"; - if(empty($path)) return false; - if(strpos($path, "AJXP_USER") !== false) return "USER"; - if(strpos($path, "AJXP_GROUP_PATH") !== false) return "GROUP"; - if(strpos($path, "AJXP_GROUP_PATH_FLAT") !== false) return "GROUP"; - return false; - } - - public function __sleep() - { - $this->driverInstance = null; - return array_keys(get_object_vars($this)); - } - -} diff --git a/core/src/core/classes/class.SystemTextEncoding.php b/core/src/core/classes/class.SystemTextEncoding.php deleted file mode 100644 index 07d82b451f..0000000000 --- a/core/src/core/classes/class.SystemTextEncoding.php +++ /dev/null @@ -1,196 +0,0 @@ -, Cyril Russo - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -/** - * Static utilitaries to encode/decode charset to/from utf8 - * @package Pydio - * @subpackage Core - */ -class SystemTextEncoding -{ - /** - * Change the charset of a string from input to output - * @static - * @param string $inputCharset - * @param string $outputCharset - * @param string $text - * @return string - */ - public static function changeCharset($inputCharset, $outputCharset, $text) - { - if ($inputCharset == $outputCharset) return $text; - // Due to iconv bug when dealing with text with non ASCII encoding for last char, we use this workaround http://fr.php.net/manual/fr/function.iconv.php#81494 - if (function_exists("iconv")) { - - return iconv($inputCharset, $outputCharset, $text); - } else { - $content = @htmlentities($text, ENT_QUOTES, $inputCharset); - return @html_entity_decode($content, ENT_QUOTES , $outputCharset); - } - } - - public static $currentCharsetValue; - /** - * Detect the current charset from the current locale - * @static - * @param string $locale - * @return string - */ - public static function parseCharset($locale) - { - $test = explode("@", $locale); - $locale = $test[0]; - $encoding = substr(strrchr($locale, "."), 1); - if (is_numeric($encoding)) { - if (substr($encoding, 0, 2) == "12") // CP12xx are changed to Windows-12xx to allow PHP4 conversion - $encoding = "windows-".$encoding; - else $encoding = "CP".$encoding; // In other cases, PHP4 won't work anyway, so use CPxxxx encoding (that iconv supports) - } else if ($locale == "C") { // Locale not set correctly, most probable error cause is /etc/init.d/apache having "LANG=C" defined - // In any case, "C" is ASCII-7 bit so it's safe to use the extra bit as if it was UTF-8 - $encoding = "UTF-8"; - } - if (!strlen($encoding)) $encoding = "UTF-8"; - return $encoding; - } - /** - * Try to detect the current encoding (cached in session) - * @static - * @return string - */ - public static function getEncoding() - { - if (self::$currentCharsetValue == null) { - $charset = ConfService::getContextCharset(); - if (!empty($charset)) { - // Check if the session get an assigned charset encoding (it's the case for remote SSH for example) - self::$currentCharsetValue = $charset; - } else { - // Get the current locale (expecting the filesystem is in the same locale, as the standard says) - self::$currentCharsetValue = self::parseCharset(setlocale(LC_CTYPE, 0)); - } - } - return self::$currentCharsetValue; - } - /** - * Decode a string from UTF8 to current Charset - * @static - * @param string $filesystemElement - * @param bool $test Try to detect if it's really utf8 or not - * @return string - */ - public static function fromUTF8($filesystemElement, $test = false) - { - if ($test && !SystemTextEncoding::isUtf8($filesystemElement)) { - return $filesystemElement; - } - $enc = SystemTextEncoding::getEncoding(); - return SystemTextEncoding::changeCharset("UTF-8", $enc, $filesystemElement); - } - - /** - * This function is used when the server's PHP configuration is using magic quote - * @param string $text - * @return string - */ - public static function magicDequote($text) - { - // If the PHP server enables magic quotes, remove them - if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) - return stripslashes($text); - return $text; - } - - /** - * call fromUTF8 - * @static - * @param string $filesystemElement - * @return string - */ - public static function fromPostedFileName($filesystemElement) - { - return SystemTextEncoding::fromUTF8(SystemTextEncoding::magicDequote($filesystemElement)); - } - - /** - * Transform a string from current charset to utf8 - * @static - * @param string $filesystemElement - * @param bool $test Test if it's already UTF8 or not, to avoid double-encoding - * @return string - */ - public static function toUTF8($filesystemElement, $test = true) - { - if ($test && SystemTextEncoding::isUtf8($filesystemElement)) { - return $filesystemElement; - } - $enc = SystemTextEncoding::getEncoding(); - return SystemTextEncoding::changeCharset($enc, "UTF-8", $filesystemElement); - } - /** - * Test if a string seem to be already UTF8-encoded - * @static - * @param string $string - * @return bool - */ - public static function isUtf8($string) - { - return preg_match('%^(?: - [\x09\x0A\x0D\x20-\x7E] # ASCII - | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte - | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs - | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte - | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates - | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 - | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 - | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 - )*$%xs', $string); - } - /** - * Transform a string from current Storage charset to utf8 - * @static - * @param string $filesystemElement - * @param bool $test Test if it's already UTF8 or not, to avoid double-encoding - * @return string - */ - public static function fromStorageEncoding($filesystemElement, $test = true) - { - if ($test && SystemTextEncoding::isUtf8($filesystemElement)) { - return $filesystemElement; - } - $enc = SystemTextEncoding::getEncoding(); - return SystemTextEncoding::changeCharset($enc, "UTF-8", $filesystemElement); - } - /** - * Decode a string from UTF8 to current Storage Charset - * @static - * @param string $filesystemElement - * @param bool $test Try to detect if it's really utf8 or not - * @return string - */ - public static function toStorageEncoding($filesystemElement, $test = false) - { - if ($test && !SystemTextEncoding::isUtf8($filesystemElement)) { - return $filesystemElement; - } - $enc = SystemTextEncoding::getEncoding(); - return SystemTextEncoding::changeCharset("UTF-8", $enc, $filesystemElement); - } -} diff --git a/core/src/core/classes/class.UnixProcess.php b/core/src/core/classes/class.UnixProcess.php deleted file mode 100644 index 3e1921db00..0000000000 --- a/core/src/core/classes/class.UnixProcess.php +++ /dev/null @@ -1,119 +0,0 @@ -, Cyril Russo - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -/** - * Utilitary to launch a process and keep track of it - * @package Pydio - * @subpackage Core - */ -class UnixProcess -{ - /** - * @var string - */ - private $pid; - /** - * @var string - */ - private $command; - /** - * @var string - */ - private $output; - - /** - * @param bool|string $cl Command to execute - * @param bool|string $output A file in which to redirect the output. Send to /dev/null if false. - */ - public function __construct($cl=false, $output=false) - { - if ($output != false) { - $this->output = $output; - } else { - $this->output = "/dev/null"; - } - if ($cl != false) { - $this->command = $cl; - $this->runCom(); - } - } - /** - * Run the command - * @return void - */ - private function runCom() - { - $command = $this->command.' > '.$this->output.' 2>&1 & echo $!'; - exec($command ,$op); - $this->pid = (int) $op[0]; - $this->command = $command; - } - /** - * Processid setter - * @param $pid - * @return void - */ - public function setPid($pid) - { - $this->pid = $pid; - } - - /** - * Processid getter - * @return string - */ - public function getPid() - { - return $this->pid; - } - - /** - * Try to get status from command line by running "ps -p PID" - * @return bool - */ - public function status() - { - $command = 'ps -p '.$this->pid; - exec($command,$op); - if (!isset($op[1]))return false; - else return true; - } - /** - * Start the command - * @return bool - */ - public function start() - { - if ($this->command != '') $this->runCom(); - return true; - } - /** - * Try to kill the process via command line. - * @return bool - */ - public function stop() - { - $command = 'kill '.$this->pid; - exec($command); - if ($this->status() == false)return true; - else return false; - } -} diff --git a/core/src/core/classes/class.UserSelection.php b/core/src/core/classes/class.UserSelection.php deleted file mode 100644 index 444e526283..0000000000 --- a/core/src/core/classes/class.UserSelection.php +++ /dev/null @@ -1,334 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -/** - * Abstraction of a user selection passed via http parameters. - * @package Pydio - * @subpackage Core - */ -class UserSelection -{ - public $files; - /** - * @var AJXP_Node[] - */ - private $nodes; - public $varPrefix = "file"; - public $dirPrefix = "dir"; - public $isUnique = true; - public $dir; - - public $inZip = false; - public $zipFile; - public $localZipPath; - - /** - * @var Repository - */ - private $repository; - - /** - * Construction selector - * @param Repository|null $repository - * @param array|null $httpVars - */ - public function __construct($repository = null, $httpVars = null) - { - $this->files = array(); - if(isSet($repository)){ - $this->repository = $repository; - } - if(iSset($httpVars)){ - $this->initFromHttpVars($httpVars); - } - } - /** - * Init the selection from the query vars - * @param array $passedArray - * @return void - */ - public function initFromHttpVars($passedArray=null) - { - if ($passedArray != null) { - if (isSet($passedArray["selection_nodes"]) && is_array($passedArray["selection_nodes"])) { - $this->initFromNodes($passedArray["selection_nodes"]); - } else { - $this->initFromArray($passedArray); - } - } else { - $this->initFromArray($_GET); - $this->initFromArray($_POST); - } - } - - /** - * @param AJXP_Node[] $nodes - */ - public function initFromNodes($nodes) - { - $this->nodes = $nodes; - $this->isUnique = (count($nodes) == 1); - $this->dir = '/'; - $this->files = array(); - $this->inZip = false; - foreach ($nodes as $n) { - $this->addFile($n->getPath()); - } - - } - - /** - * - * Init from a simple array - * @param $array - */ - public function initFromArray($array) - { - if (!is_array($array)) { - return ; - } - if (isSet($array[$this->varPrefix]) && $array[$this->varPrefix] != "") { - $v = $array[$this->varPrefix]; - if(strpos($v, "base64encoded:") === 0){ - $v = base64_decode(array_pop(explode(':', $v, 2))); - } - $this->addFile(AJXP_Utils::decodeSecureMagic($v)); - $this->isUnique = true; - //return ; - } - if (isSet($array[$this->varPrefix."_0"])) { - $index = 0; - while (isSet($array[$this->varPrefix."_".$index])) { - $v = $array[$this->varPrefix."_".$index]; - if(strpos($v, "base64encoded:") === 0){ - $v = base64_decode(array_pop(explode(':', $v, 2))); - } - $this->addFile(AJXP_Utils::decodeSecureMagic($v)); - $index ++; - } - $this->isUnique = false; - if (count($this->files) == 1) { - $this->isUnique = true; - } - //return ; - } - if (isSet($array["nodes"]) && is_array($array["nodes"])) { - $this->files = array(); - foreach($array["nodes"] as $value){ - $this->addFile(AJXP_Utils::decodeSecureMagic($value)); - } - $this->isUnique = count($this->files) == 1; - } - if (isSet($array[$this->dirPrefix])) { - $this->dir = AJXP_Utils::securePath($array[$this->dirPrefix]); - if ($test = $this->detectZip($this->dir)) { - $this->inZip = true; - $this->zipFile = $test[0]; - $this->localZipPath = $test[1]; - } - } else if (!$this->isEmpty() && $this->isUnique()) { - if ($test = $this->detectZip(AJXP_Utils::safeDirname($this->files[0]))) { - $this->inZip = true; - $this->zipFile = $test[0]; - $this->localZipPath = $test[1]; - } - } - } - - /** - * Add file to selection - * @param string $filePath - */ - public function addFile($filePath){ - if(!in_array($filePath, $this->files)){ - $this->files[] = $filePath; - } - } - - - /** - * Does the selection have one or more items - * @return bool - */ - public function isUnique() - { - return (count($this->files) == 1); - } - /** - * Are we currently inside a zip? - * @return bool - */ - public function inZip() - { - return $this->inZip; - } - /** - * Returns UTF8 encoded path - * @param bool $decode - * @return String - */ - public function getZipPath($decode = false) - { - if($decode) return AJXP_Utils::decodeSecureMagic($this->zipFile); - else return $this->zipFile; - } - - /** - * Returns UTF8 encoded path - * @param bool $decode - * @return String - */ - public function getZipLocalPath($decode = false) - { - if($decode) return AJXP_Utils::decodeSecureMagic($this->localZipPath); - else return $this->localZipPath; - } - /** - * Number of selected items - * @return int - */ - public function getCount() - { - return count($this->files); - } - /** - * List of items selected - * @return string[] - */ - public function getFiles() - { - return $this->files; - } - /** - * First item of the list - * @return string - */ - public function getUniqueFile() - { - return $this->files[0]; - } - - /** - * @return AJXP_Node - * @throws Exception - */ - public function getUniqueNode() - { - if (isSet($this->nodes) && is_array($this->nodes)) { - return $this->nodes[0]; - } - if(!isSet($this->repository)){ - throw new Exception("UserSelection: cannot build nodes URL without a proper repository"); - } - $user = AuthService::getLoggedUser(); - if (!AuthService::usersEnabled() && $user!=null && !$user->canWrite($this->repository->getId())) { - throw new Exception("You have no right on this action."); - } - - $currentFile = $this->getUniqueFile(); - $urlBase = "pydio://".$this->repository->getId(); - $ajxpNode = new AJXP_Node($urlBase.$currentFile); - return $ajxpNode; - - } - - /** - * @return AJXP_Node[] - * @throws Exception - */ - public function buildNodes() - { - if (isSet($this->nodes)) { - return $this->nodes; - } - if(!isSet($this->repository)){ - throw new Exception("UserSelection: cannot build nodes URL without a proper repository"); - } - $urlBase = "pydio://".$this->repository->getId(); - $nodes = array(); - foreach ($this->files as $file) { - $nodes[] = new AJXP_Node($urlBase.$file); - } - return $nodes; - - } - - public function currentBaseUrl(){ - if(!isSet($this->repository)){ - throw new Exception("UserSelection::currentBaseUrl: cannot build nodes URL without a proper repository"); - } - return "pydio://".$this->repository->getId(); - } - - /** - * Is this selection empty? - * @return bool - */ - public function isEmpty() - { - if (count($this->files) == 0) { - return true; - } - return false; - } - /** - * Detect if there is .zip somewhere in the path - * @static - * @param string $dirPath - * @return array|bool - */ - public static function detectZip($dirPath) - { - if (preg_match("/\.zip\//i", $dirPath) || preg_match("/\.zip$/i", $dirPath)) { - $contExt = strrpos(strtolower($dirPath), ".zip"); - $zipPath = substr($dirPath, 0, $contExt+4); - $localPath = substr($dirPath, $contExt+4); - if($localPath == "") $localPath = "/"; - return array($zipPath, $localPath); - } - return false; - } - /** - * Sets the selected items - * @param array $files - * @return void - */ - public function setFiles($files) - { - $this->files = $files; - } - - public function removeFile($file){ - $newFiles = array(); - foreach($this->files as $k => $f){ - if($f != $file) $newFiles[] = $file; - } - $this->files = $newFiles; - if(isSet($this->nodes)){ - $newNodes = array(); - foreach($this->nodes as $l => $n){ - if($n->getPath() != $file) $newNodes[] = $n; - } - $this->nodes = $newNodes; - } - } - -} diff --git a/core/src/core/classes/dibi.compact.php b/core/src/core/classes/dibi.compact.php deleted file mode 100644 index 3899a822ad..0000000000 --- a/core/src/core/classes/dibi.compact.php +++ /dev/null @@ -1,2 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Autoload; - -/** - * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. - * - * $loader = new \Composer\Autoload\ClassLoader(); - * - * // register classes with namespaces - * $loader->add('Symfony\Component', __DIR__.'/component'); - * $loader->add('Symfony', __DIR__.'/framework'); - * - * // activate the autoloader - * $loader->register(); - * - * // to enable searching the include path (eg. for PEAR packages) - * $loader->setUseIncludePath(true); - * - * In this example, if you try to use a class in the Symfony\Component - * namespace or one of its children (Symfony\Component\Console for instance), - * the autoloader will first look for the class under the component/ - * directory, and it will then fallback to the framework/ directory if not - * found before giving up. - * - * This class is loosely based on the Symfony UniversalClassLoader. - * - * @author Fabien Potencier - * @author Jordi Boggiano - * @see http://www.php-fig.org/psr/psr-0/ - * @see http://www.php-fig.org/psr/psr-4/ - */ -class ClassLoader -{ - // PSR-4 - private $prefixLengthsPsr4 = array(); - private $prefixDirsPsr4 = array(); - private $fallbackDirsPsr4 = array(); - - // PSR-0 - private $prefixesPsr0 = array(); - private $fallbackDirsPsr0 = array(); - - private $useIncludePath = false; - private $classMap = array(); - - private $classMapAuthoritative = false; - - public function getPrefixes() - { - if (!empty($this->prefixesPsr0)) { - return call_user_func_array('array_merge', $this->prefixesPsr0); - } - - return array(); - } - - public function getPrefixesPsr4() - { - return $this->prefixDirsPsr4; - } - - public function getFallbackDirs() - { - return $this->fallbackDirsPsr0; - } - - public function getFallbackDirsPsr4() - { - return $this->fallbackDirsPsr4; - } - - public function getClassMap() - { - return $this->classMap; - } - - /** - * @param array $classMap Class to filename map - */ - public function addClassMap(array $classMap) - { - if ($this->classMap) { - $this->classMap = array_merge($this->classMap, $classMap); - } else { - $this->classMap = $classMap; - } - } - - /** - * Registers a set of PSR-0 directories for a given prefix, either - * appending or prepending to the ones previously set for this prefix. - * - * @param string $prefix The prefix - * @param array|string $paths The PSR-0 root directories - * @param bool $prepend Whether to prepend the directories - */ - public function add($prefix, $paths, $prepend = false) - { - if (!$prefix) { - if ($prepend) { - $this->fallbackDirsPsr0 = array_merge( - (array) $paths, - $this->fallbackDirsPsr0 - ); - } else { - $this->fallbackDirsPsr0 = array_merge( - $this->fallbackDirsPsr0, - (array) $paths - ); - } - - return; - } - - $first = $prefix[0]; - if (!isset($this->prefixesPsr0[$first][$prefix])) { - $this->prefixesPsr0[$first][$prefix] = (array) $paths; - - return; - } - if ($prepend) { - $this->prefixesPsr0[$first][$prefix] = array_merge( - (array) $paths, - $this->prefixesPsr0[$first][$prefix] - ); - } else { - $this->prefixesPsr0[$first][$prefix] = array_merge( - $this->prefixesPsr0[$first][$prefix], - (array) $paths - ); - } - } - - /** - * Registers a set of PSR-4 directories for a given namespace, either - * appending or prepending to the ones previously set for this namespace. - * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-4 base directories - * @param bool $prepend Whether to prepend the directories - * - * @throws \InvalidArgumentException - */ - public function addPsr4($prefix, $paths, $prepend = false) - { - if (!$prefix) { - // Register directories for the root namespace. - if ($prepend) { - $this->fallbackDirsPsr4 = array_merge( - (array) $paths, - $this->fallbackDirsPsr4 - ); - } else { - $this->fallbackDirsPsr4 = array_merge( - $this->fallbackDirsPsr4, - (array) $paths - ); - } - } elseif (!isset($this->prefixDirsPsr4[$prefix])) { - // Register directories for a new namespace. - $length = strlen($prefix); - if ('\\' !== $prefix[$length - 1]) { - throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); - } - $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; - $this->prefixDirsPsr4[$prefix] = (array) $paths; - } elseif ($prepend) { - // Prepend directories for an already registered namespace. - $this->prefixDirsPsr4[$prefix] = array_merge( - (array) $paths, - $this->prefixDirsPsr4[$prefix] - ); - } else { - // Append directories for an already registered namespace. - $this->prefixDirsPsr4[$prefix] = array_merge( - $this->prefixDirsPsr4[$prefix], - (array) $paths - ); - } - } - - /** - * Registers a set of PSR-0 directories for a given prefix, - * replacing any others previously set for this prefix. - * - * @param string $prefix The prefix - * @param array|string $paths The PSR-0 base directories - */ - public function set($prefix, $paths) - { - if (!$prefix) { - $this->fallbackDirsPsr0 = (array) $paths; - } else { - $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; - } - } - - /** - * Registers a set of PSR-4 directories for a given namespace, - * replacing any others previously set for this namespace. - * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-4 base directories - * - * @throws \InvalidArgumentException - */ - public function setPsr4($prefix, $paths) - { - if (!$prefix) { - $this->fallbackDirsPsr4 = (array) $paths; - } else { - $length = strlen($prefix); - if ('\\' !== $prefix[$length - 1]) { - throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); - } - $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; - $this->prefixDirsPsr4[$prefix] = (array) $paths; - } - } - - /** - * Turns on searching the include path for class files. - * - * @param bool $useIncludePath - */ - public function setUseIncludePath($useIncludePath) - { - $this->useIncludePath = $useIncludePath; - } - - /** - * Can be used to check if the autoloader uses the include path to check - * for classes. - * - * @return bool - */ - public function getUseIncludePath() - { - return $this->useIncludePath; - } - - /** - * Turns off searching the prefix and fallback directories for classes - * that have not been registered with the class map. - * - * @param bool $classMapAuthoritative - */ - public function setClassMapAuthoritative($classMapAuthoritative) - { - $this->classMapAuthoritative = $classMapAuthoritative; - } - - /** - * Should class lookup fail if not found in the current class map? - * - * @return bool - */ - public function isClassMapAuthoritative() - { - return $this->classMapAuthoritative; - } - - /** - * Registers this instance as an autoloader. - * - * @param bool $prepend Whether to prepend the autoloader or not - */ - public function register($prepend = false) - { - spl_autoload_register(array($this, 'loadClass'), true, $prepend); - } - - /** - * Unregisters this instance as an autoloader. - */ - public function unregister() - { - spl_autoload_unregister(array($this, 'loadClass')); - } - - /** - * Loads the given class or interface. - * - * @param string $class The name of the class - * @return bool|null True if loaded, null otherwise - */ - public function loadClass($class) - { - if ($file = $this->findFile($class)) { - includeFile($file); - - return true; - } - } - - /** - * Finds the path to the file where the class is defined. - * - * @param string $class The name of the class - * - * @return string|false The path if found, false otherwise - */ - public function findFile($class) - { - // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 - if ('\\' == $class[0]) { - $class = substr($class, 1); - } - - // class map lookup - if (isset($this->classMap[$class])) { - return $this->classMap[$class]; - } - if ($this->classMapAuthoritative) { - return false; - } - - $file = $this->findFileWithExtension($class, '.php'); - - // Search for Hack files if we are running on HHVM - if ($file === null && defined('HHVM_VERSION')) { - $file = $this->findFileWithExtension($class, '.hh'); - } - - if ($file === null) { - // Remember that this class does not exist. - return $this->classMap[$class] = false; - } - - return $file; - } - - private function findFileWithExtension($class, $ext) - { - // PSR-4 lookup - $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; - - $first = $class[0]; - if (isset($this->prefixLengthsPsr4[$first])) { - foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { - if (0 === strpos($class, $prefix)) { - foreach ($this->prefixDirsPsr4[$prefix] as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { - return $file; - } - } - } - } - } - - // PSR-4 fallback dirs - foreach ($this->fallbackDirsPsr4 as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { - return $file; - } - } - - // PSR-0 lookup - if (false !== $pos = strrpos($class, '\\')) { - // namespaced class name - $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) - . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); - } else { - // PEAR-like class name - $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; - } - - if (isset($this->prefixesPsr0[$first])) { - foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { - if (0 === strpos($class, $prefix)) { - foreach ($dirs as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { - return $file; - } - } - } - } - } - - // PSR-0 fallback dirs - foreach ($this->fallbackDirsPsr0 as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { - return $file; - } - } - - // PSR-0 include paths. - if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { - return $file; - } - } -} - -/** - * Scope isolated include. - * - * Prevents access to $this/self from included files. - */ -function includeFile($file) -{ - include $file; -} diff --git a/core/src/core/classes/guzzle/vendor/composer/LICENSE b/core/src/core/classes/guzzle/vendor/composer/LICENSE deleted file mode 100644 index 1a28124886..0000000000 --- a/core/src/core/classes/guzzle/vendor/composer/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ - -Copyright (c) 2016 Nils Adermann, Jordi Boggiano - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - diff --git a/core/src/core/classes/guzzle/vendor/composer/autoload_classmap.php b/core/src/core/classes/guzzle/vendor/composer/autoload_classmap.php deleted file mode 100644 index 59a0cdf083..0000000000 --- a/core/src/core/classes/guzzle/vendor/composer/autoload_classmap.php +++ /dev/null @@ -1,13 +0,0 @@ - $vendorDir . '/firebase/php-jwt/Exceptions/BeforeValidException.php', - 'ExpiredException' => $vendorDir . '/firebase/php-jwt/Exceptions/ExpiredException.php', - 'JWT' => $vendorDir . '/firebase/php-jwt/Authentication/JWT.php', - 'SignatureInvalidException' => $vendorDir . '/firebase/php-jwt/Exceptions/SignatureInvalidException.php', -); diff --git a/core/src/core/classes/guzzle/vendor/composer/autoload_files.php b/core/src/core/classes/guzzle/vendor/composer/autoload_files.php deleted file mode 100644 index 80babd7e9c..0000000000 --- a/core/src/core/classes/guzzle/vendor/composer/autoload_files.php +++ /dev/null @@ -1,10 +0,0 @@ - $vendorDir . '/react/promise/src/functions_include.php', -); diff --git a/core/src/core/classes/guzzle/vendor/composer/autoload_namespaces.php b/core/src/core/classes/guzzle/vendor/composer/autoload_namespaces.php deleted file mode 100644 index b7fc0125db..0000000000 --- a/core/src/core/classes/guzzle/vendor/composer/autoload_namespaces.php +++ /dev/null @@ -1,9 +0,0 @@ - array($vendorDir . '/symfony/yaml'), - 'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'), - 'Symfony\\Component\\Config\\' => array($vendorDir . '/symfony/config'), - 'React\\Promise\\' => array($vendorDir . '/react/promise/src'), - 'Guzzle\\Service\\Loader\\' => array($vendorDir . '/gimler/guzzle-description-loader/src'), - 'GuzzleHttp\\Stream\\' => array($vendorDir . '/guzzlehttp/streams/src'), - 'GuzzleHttp\\Ring\\' => array($vendorDir . '/guzzlehttp/ringphp/src'), - 'GuzzleHttp\\Command\\Guzzle\\' => array($vendorDir . '/guzzlehttp/guzzle-services/src'), - 'GuzzleHttp\\Command\\' => array($vendorDir . '/guzzlehttp/command/src'), - 'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'), - 'CommerceGuys\\Guzzle\\Oauth2\\' => array($vendorDir . '/commerceguys/guzzle-oauth2-plugin/src'), -); diff --git a/core/src/core/classes/guzzle/vendor/composer/autoload_real.php b/core/src/core/classes/guzzle/vendor/composer/autoload_real.php deleted file mode 100644 index b4d2ed09a1..0000000000 --- a/core/src/core/classes/guzzle/vendor/composer/autoload_real.php +++ /dev/null @@ -1,70 +0,0 @@ -= 50600 && !defined('HHVM_VERSION'); - if ($useStaticLoader) { - require_once __DIR__ . '/autoload_static.php'; - - call_user_func(\Composer\Autoload\ComposerStaticInitd34ad80d4d6e8f56f3411c9698c8a661::getInitializer($loader)); - } else { - $map = require __DIR__ . '/autoload_namespaces.php'; - foreach ($map as $namespace => $path) { - $loader->set($namespace, $path); - } - - $map = require __DIR__ . '/autoload_psr4.php'; - foreach ($map as $namespace => $path) { - $loader->setPsr4($namespace, $path); - } - - $classMap = require __DIR__ . '/autoload_classmap.php'; - if ($classMap) { - $loader->addClassMap($classMap); - } - } - - $loader->register(true); - - if ($useStaticLoader) { - $includeFiles = Composer\Autoload\ComposerStaticInitd34ad80d4d6e8f56f3411c9698c8a661::$files; - } else { - $includeFiles = require __DIR__ . '/autoload_files.php'; - } - foreach ($includeFiles as $fileIdentifier => $file) { - composerRequired34ad80d4d6e8f56f3411c9698c8a661($fileIdentifier, $file); - } - - return $loader; - } -} - -function composerRequired34ad80d4d6e8f56f3411c9698c8a661($fileIdentifier, $file) -{ - if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { - require $file; - - $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; - } -} diff --git a/core/src/core/classes/guzzle/vendor/composer/installed.json b/core/src/core/classes/guzzle/vendor/composer/installed.json deleted file mode 100644 index 1f725cb7c1..0000000000 --- a/core/src/core/classes/guzzle/vendor/composer/installed.json +++ /dev/null @@ -1,597 +0,0 @@ -[ - { - "name": "guzzlehttp/streams", - "version": "3.0.0", - "version_normalized": "3.0.0.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/streams.git", - "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/streams/zipball/47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", - "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "time": "2014-10-12 19:18:40", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "GuzzleHttp\\Stream\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Provides a simple abstraction over streams of data", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "Guzzle", - "stream" - ] - }, - { - "name": "guzzlehttp/ringphp", - "version": "1.1.0", - "version_normalized": "1.1.0.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/RingPHP.git", - "reference": "dbbb91d7f6c191e5e405e900e3102ac7f261bc0b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/RingPHP/zipball/dbbb91d7f6c191e5e405e900e3102ac7f261bc0b", - "reference": "dbbb91d7f6c191e5e405e900e3102ac7f261bc0b", - "shasum": "" - }, - "require": { - "guzzlehttp/streams": "~3.0", - "php": ">=5.4.0", - "react/promise": "~2.0" - }, - "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "ext-curl": "Guzzle will use specific adapters if cURL is present" - }, - "time": "2015-05-20 03:37:09", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "GuzzleHttp\\Ring\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function." - }, - { - "name": "guzzlehttp/command", - "version": "0.7.1", - "version_normalized": "0.7.1.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/command.git", - "reference": "2e8286ff81f75f97b00f7dcc2f51e2efafb373bd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/command/zipball/2e8286ff81f75f97b00f7dcc2f51e2efafb373bd", - "reference": "2e8286ff81f75f97b00f7dcc2f51e2efafb373bd", - "shasum": "" - }, - "require": { - "guzzlehttp/guzzle": "~5.0", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "time": "2015-01-14 18:54:57", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.7-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "GuzzleHttp\\Command\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Provides the foundation for building command based web service clients" - }, - { - "name": "guzzlehttp/guzzle-services", - "version": "0.5.0", - "version_normalized": "0.5.0.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle-services.git", - "reference": "5402867628d266748db88f05c90ede54fd1d884c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle-services/zipball/5402867628d266748db88f05c90ede54fd1d884c", - "reference": "5402867628d266748db88f05c90ede54fd1d884c", - "shasum": "" - }, - "require": { - "guzzlehttp/command": "0.7.*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "time": "2014-12-23 19:13:18", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.4-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "GuzzleHttp\\Command\\Guzzle\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Provides an implementation of the Guzzle Command library that uses Guzzle service descriptions to describe web services, serialize requests, and parse responses into easy to use model structures." - }, - { - "name": "symfony/yaml", - "version": "v3.1.2", - "version_normalized": "3.1.2.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "2884c26ce4c1d61aebf423a8b912950fe7c764de" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/2884c26ce4c1d61aebf423a8b912950fe7c764de", - "reference": "2884c26ce4c1d61aebf423a8b912950fe7c764de", - "shasum": "" - }, - "require": { - "php": ">=5.5.9" - }, - "time": "2016-06-29 05:41:56", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com" - }, - { - "name": "symfony/filesystem", - "version": "v3.1.2", - "version_normalized": "3.1.2.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "322da5f0910d8aa0b25fa65ffccaba68dbddb890" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/322da5f0910d8aa0b25fa65ffccaba68dbddb890", - "reference": "322da5f0910d8aa0b25fa65ffccaba68dbddb890", - "shasum": "" - }, - "require": { - "php": ">=5.5.9" - }, - "time": "2016-06-29 05:41:56", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Filesystem Component", - "homepage": "https://symfony.com" - }, - { - "name": "symfony/config", - "version": "v3.1.2", - "version_normalized": "3.1.2.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/config.git", - "reference": "bcf5aebabc95b56e370e13d78565f74c7d8726dc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/bcf5aebabc95b56e370e13d78565f74c7d8726dc", - "reference": "bcf5aebabc95b56e370e13d78565f74c7d8726dc", - "shasum": "" - }, - "require": { - "php": ">=5.5.9", - "symfony/filesystem": "~2.8|~3.0" - }, - "suggest": { - "symfony/yaml": "To use the yaml reference dumper" - }, - "time": "2016-06-29 05:41:56", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Component\\Config\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Config Component", - "homepage": "https://symfony.com" - }, - { - "name": "gimler/guzzle-description-loader", - "version": "v0.0.4", - "version_normalized": "0.0.4.0", - "source": { - "type": "git", - "url": "https://github.com/gimler/guzzle-description-loader.git", - "reference": "d1384a65ffb1a10a7e9e17c427e77b281ae38498" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/gimler/guzzle-description-loader/zipball/d1384a65ffb1a10a7e9e17c427e77b281ae38498", - "reference": "d1384a65ffb1a10a7e9e17c427e77b281ae38498", - "shasum": "" - }, - "require": { - "php": ">=5.5.0", - "symfony/config": "~3.0", - "symfony/yaml": "~3.0" - }, - "time": "2016-01-27 06:35:21", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "Guzzle\\Service\\Loader\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gordon Franke", - "email": "info@nevalon.de" - }, - { - "name": "Community", - "homepage": "https://github.com/gimler/guzzle-description-loader/contributors" - } - ], - "description": "Load guzzle service description from various file formats" - }, - { - "name": "react/promise", - "version": "v2.4.1", - "version_normalized": "2.4.1.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/promise.git", - "reference": "8025426794f1944de806618671d4fa476dc7626f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/8025426794f1944de806618671d4fa476dc7626f", - "reference": "8025426794f1944de806618671d4fa476dc7626f", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "time": "2016-05-03 17:50:52", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "React\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com" - } - ], - "description": "A lightweight implementation of CommonJS Promises/A for PHP" - }, - { - "name": "firebase/php-jwt", - "version": "v2.2.0", - "version_normalized": "2.2.0.0", - "source": { - "type": "git", - "url": "https://github.com/firebase/php-jwt.git", - "reference": "e0a75bfb6413f22092c99b70f310ccb2cca3efa5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/firebase/php-jwt/zipball/e0a75bfb6413f22092c99b70f310ccb2cca3efa5", - "reference": "e0a75bfb6413f22092c99b70f310ccb2cca3efa5", - "shasum": "" - }, - "require": { - "php": ">=5.2.0" - }, - "time": "2015-06-22 23:26:39", - "type": "library", - "installation-source": "dist", - "autoload": { - "classmap": [ - "Authentication/", - "Exceptions/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Neuman Vong", - "email": "neuman+pear@twilio.com", - "role": "Developer" - }, - { - "name": "Anant Narayanan", - "email": "anant@php.net", - "role": "Developer" - } - ], - "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", - "homepage": "https://github.com/firebase/php-jwt" - }, - { - "name": "guzzlehttp/guzzle", - "version": "5.3.1", - "version_normalized": "5.3.1.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "70f1fa53b71c4647bf2762c09068a95f77e12fb8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/70f1fa53b71c4647bf2762c09068a95f77e12fb8", - "reference": "70f1fa53b71c4647bf2762c09068a95f77e12fb8", - "shasum": "" - }, - "require": { - "guzzlehttp/ringphp": "^1.1", - "php": ">=5.4.0" - }, - "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "^4.0" - }, - "time": "2016-07-15 19:28:39", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "GuzzleHttp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "rest", - "web service" - ] - }, - { - "name": "commerceguys/guzzle-oauth2-plugin", - "version": "v2.1.1", - "version_normalized": "2.1.1.0", - "source": { - "type": "git", - "url": "https://github.com/commerceguys/guzzle-oauth2-plugin.git", - "reference": "f7ed19171c3c5accfb6f0b3d1209eb5815ef8148" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/commerceguys/guzzle-oauth2-plugin/zipball/f7ed19171c3c5accfb6f0b3d1209eb5815ef8148", - "reference": "f7ed19171c3c5accfb6f0b3d1209eb5815ef8148", - "shasum": "" - }, - "require": { - "firebase/php-jwt": "~2.0", - "guzzlehttp/guzzle": "~5.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.5" - }, - "time": "2015-12-12 23:27:25", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "CommerceGuys\\Guzzle\\Oauth2\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bojan Zivanovic" - }, - { - "name": "Damien Tournoud" - }, - { - "name": "Patrick Dawkins" - } - ], - "description": "An OAuth2 plugin (subscriber) for Guzzle" - } -] diff --git a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/.gitignore b/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/.gitignore deleted file mode 100644 index 3ce5adbbde..0000000000 --- a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.idea -vendor diff --git a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/.travis.yml b/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/.travis.yml deleted file mode 100644 index b9e8c39db9..0000000000 --- a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: php - -sudo: false - -matrix: - include: - - php: hhvm - - php: 5.5 - - php: 5.6 - - php: 7 - fast_finish: true - -install: composer install --dev --prefer-source -script: phpunit diff --git a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/LICENSE b/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/LICENSE deleted file mode 100644 index 1ee7e36a44..0000000000 --- a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2011 Gordon Franke, https://github.com/gimler - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/README.md b/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/README.md deleted file mode 100644 index 30305d389d..0000000000 --- a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/README.md +++ /dev/null @@ -1,65 +0,0 @@ -[![Build Status](https://secure.travis-ci.org/gimler/guzzle-description-loader.png?branch=master)](http://travis-ci.org/gimler/guzzle-description-loader) -[![Dependency Status](https://www.versioneye.com/user/projects/55f17b93d4d204001e000053/badge.png)](https://www.versioneye.com/user/projects/55f17b93d4d204001e000053) - -# Guzzle Service Description Loader - -A stand-alone Service Description loader for Guzzle 5.x. - -## Installation - -If you are using Composer, and you should, just run the following command: - -``` sh -composer require "gimler/guzzle-description-loader" -``` - -## Supported File Formats - -* Yaml -* Php -* Json - -## Usage - -``` php -use Guzzle\Service\Loader\JsonLoader; -use GuzzleHttp\Command\Guzzle\Description; -use Symfony\Component\Config\FileLocator; - -$configDirectories = array(DESCRIPTION_PATH); -$this->locator = new FileLocator($configDirectories); - -$this->jsonLoader = new JsonLoader($this->locator); - -$description = $this->jsonLoader->load($this->locator->locate('description.json')); -$description = new Description($description); -``` - -## Sample - -``` json -{ - "operations": { - "certificates.list": { - "httpMethod": "GET", - "uri": "certificates", - "description": "Lists and returns basic information about all of the management certificates associated with the specified subscription.", - "responseModel": "CertificateList" - } - }, - "models": { - "CertificateList": { - "type": "array", - "name": "certificates", - "sentAs": "SubscriptionCertificate", - "location": "xml", - "items": { - "type": "object" - } - } - }, - "imports": [ - "description_import.json" - ] -} -``` diff --git a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/composer.json b/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/composer.json deleted file mode 100644 index e93a85366c..0000000000 --- a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/composer.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "gimler/guzzle-description-loader", - "description": "Load guzzle service description from various file formats", - "require": { - "php": ">=5.5.0", - "symfony/config": "~3.0", - "symfony/yaml": "~3.0" - }, - "license": "MIT", - "authors": [ - { - "name": "Gordon Franke", - "email": "info@nevalon.de" - }, - { - "name": "Community", - "homepage": "https://github.com/gimler/guzzle-description-loader/contributors" - } - ], - "autoload": { - "psr-4": { - "Guzzle\\Service\\Loader\\": "src/" - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/composer.lock b/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/composer.lock deleted file mode 100644 index 2e9db002d9..0000000000 --- a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/composer.lock +++ /dev/null @@ -1,169 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", - "This file is @generated automatically" - ], - "hash": "950a6c7017df36346b2daebfd6a55cf7", - "content-hash": "b19a782dc303a22631422c69ca828fa0", - "packages": [ - { - "name": "symfony/config", - "version": "v3.0.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/config.git", - "reference": "58680a6516a457a6c65044fe33586c4a81fdff01" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/58680a6516a457a6c65044fe33586c4a81fdff01", - "reference": "58680a6516a457a6c65044fe33586c4a81fdff01", - "shasum": "" - }, - "require": { - "php": ">=5.5.9", - "symfony/filesystem": "~2.8|~3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Config\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Config Component", - "homepage": "https://symfony.com", - "time": "2015-12-26 13:39:53" - }, - { - "name": "symfony/filesystem", - "version": "v3.0.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "c2e59d11dccd135dc8f00ee97f34fe1de842e70c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/c2e59d11dccd135dc8f00ee97f34fe1de842e70c", - "reference": "c2e59d11dccd135dc8f00ee97f34fe1de842e70c", - "shasum": "" - }, - "require": { - "php": ">=5.5.9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Filesystem Component", - "homepage": "https://symfony.com", - "time": "2015-12-22 10:39:06" - }, - { - "name": "symfony/yaml", - "version": "v3.0.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "3df409958a646dad2bc5046c3fb671ee24a1a691" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/3df409958a646dad2bc5046c3fb671ee24a1a691", - "reference": "3df409958a646dad2bc5046c3fb671ee24a1a691", - "shasum": "" - }, - "require": { - "php": ">=5.5.9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com", - "time": "2015-12-26 13:39:53" - } - ], - "packages-dev": [], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=5.5.0" - }, - "platform-dev": [] -} diff --git a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/phpunit.xml.dist b/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/phpunit.xml.dist deleted file mode 100644 index 46c634f0f5..0000000000 --- a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/phpunit.xml.dist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - tests/suite - - - - - src - - - diff --git a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/src/FileLoader.php b/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/src/FileLoader.php deleted file mode 100644 index 6a7f2bfc84..0000000000 --- a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/src/FileLoader.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Guzzle\Service\Loader; - -use Symfony\Component\Config\Loader\FileLoader as BaseFileLoader; - -abstract class FileLoader extends BaseFileLoader -{ - - public function load($resource, $type = null) - { - if (!stream_is_local($resource)) { - throw new \Exception(sprintf('This is not a local file "%s".', $resource)); - } - if (!file_exists($resource)) { - throw new \Exception(sprintf('File "%s" not found.', $resource)); - } - - $configValues = $this->loadResource($resource); - - if (isset($configValues["imports"])) { - foreach($configValues["imports"] as $file) { - $configValues = array_merge_recursive($configValues, $this->import($this->locator->locate($file))); - } - } - - unset($configValues["imports"]); - - return $configValues; - } - - /* - * @param string $resource - * - * @return array - * - * @throws InvalidResourceException If stream content has an invalid format. - */ - abstract protected function loadResource($resource); -} \ No newline at end of file diff --git a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/src/JsonLoader.php b/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/src/JsonLoader.php deleted file mode 100644 index 5be3001cea..0000000000 --- a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/src/JsonLoader.php +++ /dev/null @@ -1,66 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Guzzle\Service\Loader; - -use Guzzle\Service\Loader\FileLoader; - -class JsonLoader extends FileLoader -{ - /** - * {@inheritdoc} - */ - public function loadResource($resource) - { - $configValues = array(); - if ($data = file_get_contents($resource)) { - $configValues = json_decode($data, true); - if (0 < $errorCode = json_last_error()) { - throw new \Exception(sprintf('Error parsing JSON - %s', $this->getJSONErrorMessage($errorCode))); - } - } - - return $configValues; - } - - /** - * Translates JSON_ERROR_* constant into meaningful message. - * - * @param int $errorCode Error code returned by json_last_error() call - * - * @return string Message string - */ - private function getJSONErrorMessage($errorCode) - { - switch ($errorCode) { - case JSON_ERROR_DEPTH: - return 'Maximum stack depth exceeded'; - case JSON_ERROR_STATE_MISMATCH: - return 'Underflow or the modes mismatch'; - case JSON_ERROR_CTRL_CHAR: - return 'Unexpected control character found'; - case JSON_ERROR_SYNTAX: - return 'Syntax error, malformed JSON'; - case JSON_ERROR_UTF8: - return 'Malformed UTF-8 characters, possibly incorrectly encoded'; - default: - return 'Unknown error'; - } - } - - public function supports($resource, $type = null) - { - return is_string($resource) && 'json' === pathinfo( - $resource, - PATHINFO_EXTENSION - ); - } -} \ No newline at end of file diff --git a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/src/PhpLoader.php b/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/src/PhpLoader.php deleted file mode 100644 index b358341c59..0000000000 --- a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/src/PhpLoader.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Guzzle\Service\Loader; - -use Guzzle\Service\Loader\FileLoader; - -class PhpLoader extends FileLoader -{ - /** - * {@inheritdoc} - */ - public function loadResource($resource) - { - return require $resource; - } - - public function supports($resource, $type = null) - { - return is_string($resource) && 'php' === pathinfo( - $resource, - PATHINFO_EXTENSION - ); - } -} \ No newline at end of file diff --git a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/src/YamlLoader.php b/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/src/YamlLoader.php deleted file mode 100644 index 2a1e49612f..0000000000 --- a/core/src/core/classes/guzzle/vendor/gimler/guzzle-description-loader/src/YamlLoader.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Guzzle\Service\Loader; - -use Guzzle\Service\Loader\FileLoader; -use Symfony\Component\Yaml\Parser as YamlParser; -use Symfony\Component\Yaml\Exception\ParseException; - -class YamlLoader extends FileLoader -{ - private $yamlParser; - - /** - * {@inheritdoc} - */ - public function loadResource($resource) - { - if (null === $this->yamlParser) { - if (!class_exists('Symfony\Component\Yaml\Parser')) { - throw new \LogicException('Loading translations from the YAML format requires the Symfony Yaml component.'); - } - $this->yamlParser = new YamlParser(); - } - - $configValues = $this->yamlParser->parse(file_get_contents($resource)); - - return $configValues; - } - - public function supports($resource, $type = null) - { - return is_string($resource) && 'yml' === pathinfo( - $resource, - PATHINFO_EXTENSION - ); - } -} \ No newline at end of file diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/LICENSE b/core/src/core/classes/guzzle/vendor/guzzlehttp/command/LICENSE deleted file mode 100644 index 581d95f920..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2015 Michael Dowling, https://github.com/mtdowling - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/composer.json b/core/src/core/classes/guzzle/vendor/guzzlehttp/command/composer.json deleted file mode 100644 index 214c6c421d..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/composer.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "guzzlehttp/command", - "description": "Provides the foundation for building command based web service clients", - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.4.0", - "guzzlehttp/guzzle": "~5.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Command\\": "src/" - } - }, - "extra": { - "branch-alias": { - "dev-master": "0.7-dev" - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/AbstractClient.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/AbstractClient.php deleted file mode 100644 index e54074afba..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/AbstractClient.php +++ /dev/null @@ -1,285 +0,0 @@ -client = $client; - - // Ensure the defaults key is an array so we can easily merge later. - if (!isset($config['defaults'])) { - $config['defaults'] = []; - } - - if (isset($config['emitter'])) { - $this->emitter = $config['emitter']; - unset($config['emitter']); - } - - $this->config = new Collection($config); - } - - public function __call($name, array $arguments) - { - return $this->execute( - $this->getCommand( - $name, - isset($arguments[0]) ? $arguments[0] : [] - ) - ); - } - - public function execute(CommandInterface $command) - { - $trans = $this->initTransaction($command); - - if ($trans->result !== null) { - return $trans->result; - } - - try { - $trans->response = $this->client->send($trans->request); - return $trans->response instanceof FutureInterface - ? $this->createFutureResult($trans) - : $trans->response; - } catch (CommandException $e) { - // Command exceptions are thrown in the command layer, so throw 'em. - throw $e; - } catch (\Exception $e) { - // Handle when a command result is set after a terminal request - // error was encountered. - if ($trans->result !== null) { - return $trans->result; - } - $trans->exception = $e; - throw $this->createCommandException($trans); - } - } - - public function executeAll($commands, array $options = []) - { - $this->createPool($commands, $options)->wait(); - } - - public function createPool($commands, array $options = []) - { - return new Pool( - $this->client, - new CommandToRequestIterator( - function (CommandInterface $command) { - $trans = $this->initTransaction($command); - return [ - 'request' => $trans->request, - 'result' => $trans->result - ]; - }, - $commands, - $options - ), - isset($options['pool_size']) ? ['pool_size' => $options['pool_size']] : [] - ); - } - - public function getHttpClient() - { - return $this->client; - } - - public function getConfig($keyOrPath = null) - { - if ($keyOrPath === null) { - return $this->config->toArray(); - } - - if (strpos($keyOrPath, '/') === false) { - return $this->config[$keyOrPath]; - } - - return $this->config->getPath($keyOrPath); - } - - public function setConfig($keyOrPath, $value) - { - $this->config->setPath($keyOrPath, $value); - } - - public function createCommandException(CommandTransaction $transaction) - { - $cn = 'GuzzleHttp\\Command\\Exception\\CommandException'; - - // Don't continuously wrap the same exceptions. - if ($transaction->exception instanceof CommandException) { - return $transaction->exception; - } - - if ($transaction->response) { - $statusCode = (string) $transaction->response->getStatusCode(); - if ($statusCode[0] == '4') { - $cn = 'GuzzleHttp\\Command\\Exception\\CommandClientException'; - } elseif ($statusCode[0] == '5') { - $cn = 'GuzzleHttp\\Command\\Exception\\CommandServerException'; - } - } - - return new $cn( - "Error executing command: " . $transaction->exception->getMessage(), - $transaction, - $transaction->exception - ); - } - - /** - * Prepares a request for the command. - * - * @param CommandTransaction $trans Command and context to serialize. - * - * @return RequestInterface - */ - abstract protected function serializeRequest(CommandTransaction $trans); - - /** - * Creates a future result for a given command transaction. - * - * This method really should beoverridden in subclasses to implement custom - * future response results. - * - * @param CommandTransaction $transaction - * - * @return FutureInterface - */ - protected function createFutureResult(CommandTransaction $transaction) - { - return new FutureValue( - $transaction->response->then(function () use ($transaction) { - return $transaction->result; - }), - // Wait function derefs the response which populates the result. - [$transaction->response, 'wait'], - [$transaction->response, 'cancel'] - ); - } - - /** - * Initialize a transaction for a command and send the prepare event. - * - * @param CommandInterface $command Command to associate with the trans. - * - * @return CommandTransaction - */ - protected function initTransaction(CommandInterface $command) - { - $trans = new CommandTransaction($this, $command); - // Throwing in the init event WILL NOT emit an error event. - $command->getEmitter()->emit('init', new InitEvent($trans)); - $trans->request = $this->serializeRequest($trans); - - if ($future = $command->getFuture()) { - $trans->request->getConfig()->set('future', $future); - } - - $trans->state = 'prepared'; - $prep = new PreparedEvent($trans); - - try { - $command->getEmitter()->emit('prepared', $prep); - } catch (\Exception $e) { - $trans->exception = $e; - $trans->exception = $this->createCommandException($trans); - } - - // If the command failed in the prepare event or was intercepted, then - // emit the process event now and skip hooking up the request. - if ($trans->exception || $prep->isPropagationStopped()) { - $this->emitProcess($trans); - return $trans; - } - - $trans->state = 'executing'; - - // When a request completes, process the request at the command - // layer. - $trans->request->getEmitter()->on( - 'end', - function (EndEvent $e) use ($trans) { - $trans->response = $e->getResponse(); - if ($trans->exception = $e->getException()) { - $trans->exception = $this->createCommandException($trans); - } - $this->emitProcess($trans); - }, RequestEvents::LATE - ); - - return $trans; - } - - /** - * Finishes the process event for the command. - */ - private function emitProcess(CommandTransaction $trans) - { - $trans->state = 'process'; - - try { - // Emit the final "process" event for the command. - $trans->command->getEmitter()->emit('process', new ProcessEvent($trans)); - } catch (\Exception $ex) { - // Override any previous exception with the most recent exception. - $trans->exception = $ex; - $trans->exception = $this->createCommandException($trans); - } - - $trans->state = 'end'; - - // If the transaction still has the exception, then throw it. - if ($trans->exception) { - throw $trans->exception; - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Command.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Command.php deleted file mode 100644 index 10c2871d28..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Command.php +++ /dev/null @@ -1,74 +0,0 @@ -name = $name; - $this->data = $args; - - if (isset($options['emitter'])) { - $this->emitter = $options['emitter']; - } - - if (isset($options['future'])) { - $this->future = $options['future']; - } - } - - /** - * Ensure that the emitter is cloned. - */ - public function __clone() - { - if ($this->emitter) { - $this->emitter = clone $this->emitter; - } - } - - public function getName() - { - return $this->name; - } - - public function hasParam($name) - { - return array_key_exists($name, $this->data); - } - - public function setFuture($useFuture) - { - $this->future = $useFuture; - } - - public function getFuture() - { - return $this->future; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/CommandInterface.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/CommandInterface.php deleted file mode 100644 index b949d1460b..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/CommandInterface.php +++ /dev/null @@ -1,54 +0,0 @@ -requestBuilder = $requestBuilder; - $this->eventListeners = $this->prepareListeners( - $options, - ['init', 'prepared', 'process'] - ); - - if ($commands instanceof \Iterator) { - $this->commands = $commands; - } elseif (is_array($commands)) { - $this->commands = new \ArrayIterator($commands); - } else { - throw new \InvalidArgumentException('Command iterators must be ' - . 'created using an \\Iterator or array or commands'); - } - } - - public function current() - { - return $this->currentRequest; - } - - public function next() - { - $this->currentRequest = null; - $this->commands->next(); - } - - public function key() - { - return $this->commands->key(); - } - - public function valid() - { - get_next: - - // Return true if this function has already been called for iteration. - if ($this->currentRequest) { - return true; - } - - // Return false if we are at the end of the provided commands iterator. - if (!$this->commands->valid()) { - return false; - } - - $command = $this->commands->current(); - - if (!($command instanceof CommandInterface)) { - throw new \RuntimeException('All commands provided to the ' . __CLASS__ - . ' must implement GuzzleHttp\\Command\\CommandInterface.' - . ' Encountered a ' . Core::describeType($command) . ' value.'); - } - - $command->setFuture('lazy'); - $this->attachListeners($command, $this->eventListeners); - - // Prevent transfer exceptions from throwing. - $command->getEmitter()->on( - 'process', - function (ProcessEvent $e) { - if ($e->getException()) { - $e->setResult(null); - } - }, - RequestEvents::LATE - ); - - $builder = $this->requestBuilder; - $result = $builder($command); - - // Skip commands that were intercepted with a result. - if (isset($result['result'])) { - $this->commands->next(); - goto get_next; - } - - $this->currentRequest = $result['request']; - - return true; - } - - public function rewind() - { - $this->currentRequest = null; - - if (!($this->commands instanceof \Generator)) { - $this->commands->rewind(); - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/CommandTransaction.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/CommandTransaction.php deleted file mode 100644 index 4e5487d481..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/CommandTransaction.php +++ /dev/null @@ -1,100 +0,0 @@ -serviceClient = $client; - $this->client = $client->getHttpClient(); - $this->command = $command; - $this->context = new Collection($context); - $this->state = 'init'; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/CommandUtils.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/CommandUtils.php deleted file mode 100644 index 69b2750736..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/CommandUtils.php +++ /dev/null @@ -1,57 +0,0 @@ -attach($command); - } - - $client->executeAll($commands, RequestEvents::convertEventArray( - $options, - ['process'], - [ - 'priority' => RequestEvents::LATE, - 'fn' => function (ProcessEvent $e) use ($hash) { - if ($e->getException()) { - $hash[$e->getCommand()] = $e->getException(); - } else { - $hash[$e->getCommand()] = $e->getResult(); - } - } - ] - )); - - return new BatchResults($hash); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Event/CommandEvent.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Event/CommandEvent.php deleted file mode 100644 index 7b5d00745b..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Event/CommandEvent.php +++ /dev/null @@ -1,69 +0,0 @@ -trans = $trans; - } - - /** - * Get the command associated with the event - * - * @return CommandInterface - */ - public function getCommand() - { - return $this->trans->command; - } - - /** - * Get the service client associated with the command transfer. - * - * @return ServiceClientInterface - */ - public function getClient() - { - return $this->trans->serviceClient; - } - - /** - * Get context associated with the command transfer. - * - * The return value is a Guzzle collection object which can be accessed and - * mutated like a PHP associative array. You can add arbitrary data to the - * context for application specific purposes. - * - * @return Collection - */ - public function getContext() - { - return $this->trans->context; - } - - /** - * Gets the transaction associated with the event - * - * @return CommandTransaction - */ - public function getTransaction() - { - return $this->trans; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Event/InitEvent.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Event/InitEvent.php deleted file mode 100644 index 83276c2158..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Event/InitEvent.php +++ /dev/null @@ -1,9 +0,0 @@ -request) { - throw new StateException('No request on the command transaction'); - } - - $this->trans = $trans; - } - - /** - * Gets the HTTP request that will be sent for the command. - * - * @return RequestInterface - */ - public function getRequest() - { - return $this->trans->request; - } - - /** - * Set a result on the command transaction to prevent the command from - * actually sending an HTTP request. - * - * Subsequent listeners ARE NOT emitted even when a result is set in the - * prepare event. - * - * @param mixed $result Result to associate with the command - */ - public function intercept($result) - { - $this->trans->exception = null; - $this->trans->result = $result; - $this->stopPropagation(); - } - - /** - * Returns the result of the command (if one is available). - * - * @return mixed|null - */ - public function getResult() - { - return $this->trans->result; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Event/ProcessEvent.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Event/ProcessEvent.php deleted file mode 100644 index 4b4f1bd3c5..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Event/ProcessEvent.php +++ /dev/null @@ -1,74 +0,0 @@ -trans->exception; - } - - /** - * Gets the HTTP request that was sent. - * - * @return RequestInterface - */ - public function getRequest() - { - return $this->trans->request; - } - - /** - * Get the response that was received for the request if one was received. - * - * It is important to remember that a response will not be available if the - * HTTP request failed with a networking error. - * - * @return ResponseInterface|null - */ - public function getResponse() - { - return $this->trans->response; - } - - /** - * Set the processed result on the event. - * - * Subsequent listeners ARE STILL emitted even when a result is set. - * Calling this method will remove any exceptions associated with the - * command. - * - * @param mixed $result Result to associate with the command - */ - public function setResult($result) - { - $this->trans->exception = null; - $this->trans->result = $result; - } - - /** - * Returns the result of the command (if one is available). - * - * @return mixed|null - */ - public function getResult() - { - return $this->trans->result; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Exception/CommandClientException.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Exception/CommandClientException.php deleted file mode 100644 index f5d555b41c..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Exception/CommandClientException.php +++ /dev/null @@ -1,7 +0,0 @@ -trans = $trans; - $request = $trans->request ?: new Request(null, null); - $response = $trans->response; - parent::__construct($message, $request, $response, $previous); - } - - /** - * Gets the service client associated with the failed command. - * - * @return ServiceClientInterface - */ - public function getClient() - { - return $this->trans->serviceClient; - } - - /** - * Gets the command that failed. - * - * @return CommandInterface - */ - public function getCommand() - { - return $this->trans->command; - } - - /** - * Gets the result of the command if a result was set. - * - * @return mixed - */ - public function getResult() - { - return $this->trans->result; - } - - /** - * Gets the context of the command as a collection. - * - * @return Collection - */ - public function getContext() - { - return $this->trans->context; - } - - /** - * Gets the transaction associated with the exception. - * - * @return CommandTransaction - */ - public function getTransaction() - { - return $this->trans; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Exception/CommandServerException.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Exception/CommandServerException.php deleted file mode 100644 index 22356b526a..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Exception/CommandServerException.php +++ /dev/null @@ -1,7 +0,0 @@ -getCommand('foo', ['baz' => 'bar'])]; - * $client->executeAll($commands); - * - * @param array|\Iterator $commands Array or iterator that contains - * CommandInterface objects to execute. - * @param array $options Associative array of options to apply. - * @see GuzzleHttp\Command\ServiceClientInterface::createCommandPool for - * a list of options. - */ - public function executeAll($commands, array $options = []); - - /** - * Creates a future object that, when dereferenced, sends commands in - * parallel using a fixed pool size. - * - * Exceptions encountered while executing the commands will not be thrown. - * Instead, callers are expected to handle errors using the event system. - * - * @param array|\Iterator $commands Array or iterator that contains - * CommandInterface objects to execute with the client. - * @param array $options Associative array of options to apply. - * - pool_size: (int) Max number of commands to send concurrently. - * When this number of concurrent requests are created, the sendAll - * function blocks until all of the futures have completed. - * - init: (callable) Receives an InitEvent from each command. - * - prepare: (callable) Receives a PrepareEvent from each command. - * - process: (callable) Receives a ProcessEvent from each command. - * - * @return FutureInterface - */ - public function createPool($commands, array $options = []); - - /** - * Get the HTTP client used to send requests for the web service client - * - * @return ClientInterface - */ - public function getHttpClient(); - - /** - * Get a client configuration value. - * - * @param string|int|null $keyOrPath The Path to a particular configuration - * value. The syntax uses a path notation that allows you to retrieve - * nested array values without throwing warnings. - * - * @return mixed - */ - public function getConfig($keyOrPath = null); - - /** - * Set a client configuration value at the specified configuration path. - * - * @param string|int $keyOrPath Path at which to change a configuration - * value. This path syntax follows the same path syntax specified in - * {@see getConfig}. - * - * @param mixed $value Value to set - */ - public function setConfig($keyOrPath, $value); - - /** - * Create an exception for a command based on a previous exception. - * - * This method is invoked when an exception occurs while executing a - * command. You may choose to use a custom exception class for exceptions - * or return the provided exception as-is. - * - * @param CommandTransaction $transaction Command transaction context - * - * @return \Exception - */ - public function createCommandException(CommandTransaction $transaction); -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Subscriber/Debug.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Subscriber/Debug.php deleted file mode 100644 index f93392fe2b..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Subscriber/Debug.php +++ /dev/null @@ -1,427 +0,0 @@ -states = new \SplObjectStorage(); - $this->output = isset($options['output']) - ? $options['output'] - : fopen('php://output', 'w'); - $this->output = Stream::factory($this->output); - $this->http = isset($options['http']) ? $options['http'] : true; - $this->adapterDebug = isset($options['adapter_debug']) - ? $options['adapter_debug'] - : true; - $this->maxStreamSize = isset($options['max_stream_size']) - ? $options['max_stream_size'] - : 10240; - } - - public function getEvents() - { - return [ - 'init' => [ - ['beforeInit', 'first'], - ['afterInit', 'last'] - ], - 'prepared' => [ - ['beforePrepared', 'first'], - ['afterPrepared', 'last'] - ], - 'process' => [ - ['beforeProcess', 'first'], - ['afterProcess', 'last'] - ] - ]; - } - - private function write($text) - { - $this->output->write(date('c') . ': ' . $text . PHP_EOL); - } - - private function hashCommand( - ServiceClientInterface $client, - CommandInterface $command, - EventInterface $event - ) { - return get_class($client) . '::' . $command->getName() - . ' (' . spl_object_hash($event) . ')'; - } - - private function startEvent( - $name, - $hash, - $command, - $request = null, - $response = null, - $result = null, - $error = null - ) { - $last = isset($this->states[$command]) - ? $this->states[$command] - : null; - $this->states[$command] = $this->eventState(func_get_args()); - $this->write(sprintf( - "Starting the %s event for %s: %s", - $name, - $hash, - $this->diffStates($last, $this->states[$command]) - )); - } - - private function endEvent( - $name, - $hash, - $command, - $request = null, - $response = null, - $result = null, - $error = null - ) { - if (!isset($this->states[$command])) { - throw new \RuntimeException('Matching start event not found'); - } - - $last = $this->states[$command]; - $this->states[$command] = $this->eventState(func_get_args()); - $this->write(sprintf( - "Done with the %s event for %s (took %f seconds): %s", - $name, - $hash, - microtime(true) - $last['time'], - $this->diffStates($last, $this->states[$command]) - )); - } - - public function beforeInit(InitEvent $e) - { - $this->proxyEvent('command:init', $e); - } - - public function afterInit(InitEvent $e) - { - $this->proxyEvent('command:init', $e); - } - - public function beforePrepared(PreparedEvent $e) - { - $this->proxyEvent('command:prepared', $e); - } - - public function afterPrepared(PreparedEvent $e) - { - $this->proxyEvent('command:prepared', $e); - $request = $e->getRequest(); - - if (!$this->http || !$request) { - return; - } - - if ($this->adapterDebug) { - $request->getConfig()->set('debug', true); - } - - // Attach listeners to request events - $before = function ($before) use ($e) { - $this->proxyReqEvent('startEvent', $e, $before); - }; - - $after = function ($after) use ($e) { - $this->proxyReqEvent('endEvent', $e, $after); - }; - - foreach (['before', 'complete', 'error', 'end'] as $event) { - $request->getEmitter()->on($event, $before, RequestEvents::EARLY); - $request->getEmitter()->on($event, $after, RequestEvents::LATE); - } - } - - public function beforeProcess(ProcessEvent $e) - { - $this->proxyEvent('command:process', $e); - } - - public function afterProcess(ProcessEvent $e) - { - $this->proxyEvent('command:process', $e); - } - - /** - * Proxies the appropriate call to start or end event - * - * @param string $name Name of the event - * @param EventInterface $e Event to proxy - */ - public function proxyEvent($name, EventInterface $e) - { - $meth = substr(debug_backtrace()[1]['function'], 0, 6) == 'before' - ? 'startEvent' - : 'endEvent'; - - call_user_func_array( - [$this, $meth], - [ - $name, - $this->hashCommand($e->getClient(), $e->getCommand(), $e), - $e->getCommand(), - method_exists($e, 'getRequest') ? $e->getRequest() : null, - method_exists($e, 'getResponse') ? $e->getResponse() : null, - method_exists($e, 'getResult') ? $e->getResult() : null, - method_exists($e, 'getException') ? $e->getException() : null - ] - ); - } - - /** - * Proxies a call to start or end event based on a request event. - * - * @param string $meth startEvent or endEvent - * @param EventInterface $cev Command event - * @param EventInterface $rev Request event - */ - private function proxyReqEvent( - $meth, - EventInterface $cev, - EventInterface $rev - ) { - call_user_func( - [$this, $meth], - $this->getEventName($rev), - $this->hashCommand($cev->getClient(), $cev->getCommand(), $rev), - $cev->getCommand(), - $rev->getRequest(), - method_exists($rev, 'getResponse') ? $rev->getResponse() : null, - method_exists($cev, 'getResult') ? $cev->getResult() : null, - method_exists($cev, 'getError') ? $cev->getError() : null - ); - } - - /** - * Gets the state of an event as a hash. - * - * @param array $args Ordered array of arguments passed to an event fn - * - * @return array - */ - private function eventState($args) - { - return [ - 'time' => microtime(true), - 'command' => $this->toArrayState($args[2]), - 'request' => $this->messageState($args[3]), - 'response' => $this->messageState($args[4]), - 'result' => $this->resultState($args[5]), - 'error' => $this->errorState($args[6]) - ]; - } - - /** - * Calculates the event name of a request event. - * - * @param EventInterface $event - * - * @return string - */ - private function getEventName(EventInterface $event) - { - $cl = get_class($event); - $name = strtolower(substr($cl, strrpos($cl, '\\') + 1)); - return 'request:' . substr($name, 0, -5); - } - - /** - * Gets the state of a stream as a hash or null. - * - * If the size of the stream is below the max threshold and the stream is - * seekable, then the contents of the stream is included in the hash. - * - * @param StreamInterface $stream - * - * @return array|null - */ - private function streamState(StreamInterface $stream = null) - { - if (!$stream) { - return null; - } - - $result = [ - 'class' => get_class($stream), - 'size' => $stream->getSize(), - 'tell' => $stream->tell() - ]; - - if ($stream->getSize() < $this->maxStreamSize && - $stream->isSeekable() - ) { - $pos = $stream->tell(); - $result['contents'] = (string) $stream; - $stream->seek($pos); - } - - return $result; - } - - /** - * Gets the state of a message as a hash. - * - * @param MessageInterface $msg - * - * @return array|null - */ - private function messageState(MessageInterface $msg = null) - { - return !$msg ? null : [ - 'start-line' => AbstractMessage::getStartLine($msg), - 'headers' => AbstractMessage::getHeadersAsString($msg), - 'body' => $this->streamState($msg->getBody()) - ]; - } - - /** - * Converts a ToArrayInterface object into a hash with streams mapped as - * needed to hash states. - * - * @param ToArrayInterface $data - * - * @return array - */ - private function toArrayState(ToArrayInterface $data) - { - $params = $data->toArray(); - array_walk_recursive($params, function (&$value) { - if ($value instanceof StreamInterface) { - $value = $this->streamState($value); - } - }); - - $result = [ - 'id' => spl_object_hash($data), - 'class' => get_class($data), - 'keys' => $params - ]; - - if ($data instanceof CommandInterface) { - $result['name'] = $data->getName(); - } - - return $result; - } - - /** - * Returns the most appropriate JSON encodable value for a result state. - * - * @param null $result - * - * @return array|null|string - */ - private function resultState($result = null) - { - if ($result === null) { - return null; - } elseif ($result instanceof ToArrayInterface) { - return $this->toArrayState($result); - } elseif ($result instanceof StreamInterface) { - return $this->streamState($result); - } else { - return json_encode($result); - } - } - - /** - * Returns the state of an exception as a hash or null. - * - * @param \Exception $e - * - * @return array|null - */ - private function errorState(\Exception $e = null) - { - return !$e ? null : [ - 'class' => get_class($e), - 'message' => $e->getMessage(), - 'line' => $e->getLine(), - 'file' => $e->getFile(), - 'code' => $e->getCode() - ]; - } - - /** - * Provides a diff between two states as a string - */ - private function diffStates($a, $b) - { - if (!$a) { - return 'State was changed to ' . print_r($b, true); - } - - unset($a['time'], $b['time']); - $diff = $this->diff($a, $b); - - return $diff ? print_r($diff, true) : 'No change'; - } - - private function diff($a, $b) { - - $result = []; - - // Check differences in previous keys - foreach ($a as $k => $v) { - if (!array_key_exists($k, $b)) { - $result[$k . ' was removed, previously'] = $v; - } elseif (is_array($v)) { - if ($diff = $this->diff($v, $b[$k])) { - $result[$k . ' has a change'] = $diff; - } - } elseif ($v !== $b[$k]) { - $result[$k . ' was changed'] = $b[$k]; - } - } - - return $result; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Subscriber/ResultMock.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Subscriber/ResultMock.php deleted file mode 100644 index 1f25e9c18f..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/command/src/Subscriber/ResultMock.php +++ /dev/null @@ -1,107 +0,0 @@ -addMultiple($results); - } - - public function getEvents() - { - return ['prepare' => ['onPrepared', 'last']]; - } - - /** - * @throws \Exception if one has been queued. - * @throws \OutOfBoundsException if the queue is empty. - */ - public function onPrepared(PreparedEvent $event) - { - if (!$result = array_shift($this->queue)) { - throw new \OutOfBoundsException('Result mock queue is empty'); - } elseif ($result instanceof \Exception) { - throw $result; - } else { - $event->intercept($result); - } - } - - public function count() - { - return count($this->queue); - } - - /** - * Add a result to the end of the queue. - * - * @param mixed $result The result of the command. - * - * @return self - */ - public function addResult($result) - { - $this->queue[] = $result; - - return $this; - } - - /** - * Add an exception to the end of the queue. - * - * @param \Exception $exception Thrown when executing. - * - * @return self - */ - public function addException(\Exception $exception) - { - $this->queue[] = $exception; - - return $this; - } - - /** - * Add multiple results/exceptions to the queue - * - * @param array $results Results to add - * - * @return self - */ - public function addMultiple(array $results) - { - foreach ($results as $result) { - if ($result instanceof \Exception) { - $this->addException($result); - } else { - $this->addResult($result); - } - } - - return $this; - } - - /** - * Clear the queue. - * - * @return self - */ - public function clearQueue() - { - $this->queue = []; - - return $this; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/.gitignore b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/.gitignore deleted file mode 100644 index 571a384ed3..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -.idea -.DS_STORE -phpunit.xml -composer.lock -vendor/ -artifacts/ diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/.travis.yml b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/.travis.yml deleted file mode 100644 index 3a02bb382a..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: php - -php: - - 5.4 - - 5.5 - - 5.6 - - hhvm - -before_script: - - composer self-update - - composer install --no-interaction --prefer-source --dev - -script: make test diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/LICENSE b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/LICENSE deleted file mode 100644 index 71d3b783cb..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2014 Michael Dowling, https://github.com/mtdowling - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/Makefile b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/Makefile deleted file mode 100644 index cfb82e4507..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -all: clean test - -test: - vendor/bin/phpunit - -coverage: - vendor/bin/phpunit --coverage-html=artifacts/coverage - -view-coverage: - open artifacts/coverage/index.html - -clean: - rm -rf artifacts/* - -.PHONY: coverage diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/README.rst b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/README.rst deleted file mode 100644 index 9f30e95bd3..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/README.rst +++ /dev/null @@ -1,63 +0,0 @@ -=============== -Guzzle Services -=============== - -Provides an implementation of the Guzzle Command library that uses Guzzle service descriptions to describe web services, serialize requests, and parse responses into easy to use model structures. - -.. code-block:: php - - use GuzzleHttp\Client; - use GuzzleHttp\Command\Guzzle\GuzzleClient; - use GuzzleHttp\Command\Guzzle\Description; - - $client = new Client(); - $description = new Description([ - 'baseUrl' => 'http://httpbin.org/', - 'operations' => [ - 'testing' => [ - 'httpMethod' => 'GET', - 'uri' => '/get/{foo}', - 'responseModel' => 'getResponse', - 'parameters' => [ - 'foo' => [ - 'type' => 'string', - 'location' => 'uri' - ], - 'bar' => [ - 'type' => 'string', - 'location' => 'query' - ] - ] - ] - ], - 'models' => [ - 'getResponse' => [ - 'type' => 'object', - 'additionalProperties' => [ - 'location' => 'json' - ] - ] - ] - ]); - - $guzzleClient = new GuzzleClient($client, $description); - - $result = $guzzleClient->testing(['foo' => 'bar']); - echo $result['args']['foo']; - // bar - -Installing -========== - -This project can be installed using Composer. Add the following to your -composer.json: - -.. code-block:: javascript - - { - "require": { - "guzzlehttp/guzzle-services": "0.4.*" - } - } - -More documentation coming soon. diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/composer.json b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/composer.json deleted file mode 100644 index 6d0ef37310..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/composer.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "guzzlehttp/guzzle-services", - "description": "Provides an implementation of the Guzzle Command library that uses Guzzle service descriptions to describe web services, serialize requests, and parse responses into easy to use model structures.", - "type": "library", - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.4.0", - "guzzlehttp/command": "0.7.*" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Command\\Guzzle\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "GuzzleHttp\\Tests\\Command\\Guzzle\\": "tests/" - } - }, - "extra": { - "branch-alias": { - "dev-master": "0.4-dev" - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/phpunit.xml.dist b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/phpunit.xml.dist deleted file mode 100644 index 994e1584ea..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/phpunit.xml.dist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - tests - - - - - src - - - diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/Description.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/Description.php deleted file mode 100644 index 3a0c749754..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/Description.php +++ /dev/null @@ -1,261 +0,0 @@ -{$key} = $config[$key]; - } - } - - // Set the baseUrl - $this->baseUrl = Url::fromString(isset($config['baseUrl']) ? $config['baseUrl'] : ''); - - // Ensure that the models and operations properties are always arrays - $this->models = (array) $this->models; - $this->operations = (array) $this->operations; - - // We want to add operations differently than adding the other properties - $defaultKeys[] = 'operations'; - - // Create operations for each operation - if (isset($config['operations'])) { - foreach ($config['operations'] as $name => $operation) { - if (!is_array($operation)) { - throw new \InvalidArgumentException('Operations must be arrays'); - } - $this->operations[$name] = $operation; - } - } - - // Get all of the additional properties of the service description and - // store them in a data array - foreach (array_diff(array_keys($config), $defaultKeys) as $key) { - $this->extraData[$key] = $config[$key]; - } - - // Configure the schema formatter - if (isset($options['formatter'])) { - $this->formatter = $options['formatter']; - } else { - static $defaultFormatter; - if (!$defaultFormatter) { - $defaultFormatter = new SchemaFormatter(); - } - $this->formatter = $defaultFormatter; - } - } - - /** - * Get the basePath/baseUrl of the description - * - * @return Url - */ - public function getBaseUrl() - { - return $this->baseUrl; - } - - /** - * Get the API operations of the service - * - * @return Operation[] Returns an array of {@see Operation} objects - */ - public function getOperations() - { - return $this->operations; - } - - /** - * Check if the service has an operation by name - * - * @param string $name Name of the operation to check - * - * @return bool - */ - public function hasOperation($name) - { - return isset($this->operations[$name]); - } - - /** - * Get an API operation by name - * - * @param string $name Name of the command - * - * @return Operation - * @throws \InvalidArgumentException if the operation is not found - */ - public function getOperation($name) - { - if (!$this->hasOperation($name)) { - throw new \InvalidArgumentException("No operation found named $name"); - } - - // Lazily create operations as they are retrieved - if (!($this->operations[$name] instanceof Operation)) { - $this->operations[$name]['name'] = $name; - $this->operations[$name] = new Operation($this->operations[$name], $this); - } - - return $this->operations[$name]; - } - - /** - * Get a shared definition structure. - * - * @param string $id ID/name of the model to retrieve - * - * @return Parameter - * @throws \InvalidArgumentException if the model is not found - */ - public function getModel($id) - { - if (!$this->hasModel($id)) { - throw new \InvalidArgumentException("No model found named $id"); - } - - // Lazily create models as they are retrieved - if (!($this->models[$id] instanceof Parameter)) { - $this->models[$id] = new Parameter( - $this->models[$id], - ['description' => $this] - ); - } - - return $this->models[$id]; - } - - /** - * Get all models of the service description. - * - * @return array - */ - public function getModels() - { - $models = []; - foreach ($this->models as $name => $model) { - $models[$name] = $this->getModel($name); - } - - return $models; - } - - /** - * Check if the service description has a model by name. - * - * @param string $id Name/ID of the model to check - * - * @return bool - */ - public function hasModel($id) - { - return isset($this->models[$id]); - } - - /** - * Get the API version of the service - * - * @return string - */ - public function getApiVersion() - { - return $this->apiVersion; - } - - /** - * Get the name of the API - * - * @return string - */ - public function getName() - { - return $this->name; - } - - /** - * Get a summary of the purpose of the API - * - * @return string - */ - public function getDescription() - { - return $this->description; - } - - /** - * Format a parameter using named formats. - * - * @param string $format Format to convert it to - * @param mixed $input Input string - * - * @return mixed - */ - public function format($format, $input) - { - return $this->formatter->format($format, $input); - } - - /** - * Get arbitrary data from the service description that is not part of the - * Guzzle service description specification. - * - * @param string $key Data key to retrieve or null to retrieve all extra - * - * @return null|mixed - */ - public function getData($key = null) - { - if ($key === null) { - return $this->extraData; - } elseif (isset($this->extraData[$key])) { - return $this->extraData[$key]; - } else { - return null; - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/DescriptionInterface.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/DescriptionInterface.php deleted file mode 100644 index 2abb1e9d32..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/DescriptionInterface.php +++ /dev/null @@ -1,107 +0,0 @@ -description = $description; - $this->processConfig($config); - } - - public function getCommand($name, array $args = []) - { - $factory = $this->commandFactory; - - // Determine if a future array should be returned. - if (!empty($args['@future'])) { - $future = !empty($args['@future']); - unset($args['@future']); - } else { - $future = false; - } - - // Merge in default command options - $args += $this->getConfig('defaults'); - - if ($command = $factory($name, $args, $this)) { - $command->setFuture($future); - return $command; - } - - throw new \InvalidArgumentException("No operation found named $name"); - } - - public function getDescription() - { - return $this->description; - } - - protected function createFutureResult(CommandTransaction $transaction) - { - return new FutureArray( - $transaction->response->then(function () use ($transaction) { - return $transaction->result; - }), - [$transaction->response, 'wait'], - [$transaction->response, 'cancel'] - ); - } - - protected function serializeRequest(CommandTransaction $trans) - { - $fn = $this->serializer; - return $fn($trans); - } - - /** - * Creates a callable function used to create command objects from a - * service description. - * - * @param DescriptionInterface $description Service description - * - * @return callable Returns a command factory - */ - public static function defaultCommandFactory(DescriptionInterface $description) - { - return function ( - $name, - array $args = [], - ServiceClientInterface $client - ) use ($description) { - $operation = null; - - if ($description->hasOperation($name)) { - $operation = $description->getOperation($name); - } else { - $name = ucfirst($name); - if ($description->hasOperation($name)) { - $operation = $description->getOperation($name); - } - } - - if (!$operation) { - return null; - } - - return new Command($name, $args, ['emitter' => clone $client->getEmitter()]); - }; - } - - /** - * Prepares the client based on the configuration settings of the client. - * - * @param array $config Constructor config as an array - */ - protected function processConfig(array $config) - { - // Use the passed in command factory or a custom factory if provided - $this->commandFactory = isset($config['command_factory']) - ? $config['command_factory'] - : self::defaultCommandFactory($this->description); - - // Add event listeners based on the configuration option - $emitter = $this->getEmitter(); - - if (!isset($config['validate']) || - $config['validate'] === true - ) { - $emitter->attach(new ValidateInput($this->description)); - } - - $this->serializer = isset($config['serializer']) - ? $config['serializer'] - : new Serializer($this->description); - - if (!isset($config['process']) || - $config['process'] === true - ) { - $emitter->attach( - new ProcessResponse( - $this->description, - isset($config['response_locations']) - ? $config['response_locations'] - : [] - ) - ); - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/Operation.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/Operation.php deleted file mode 100644 index 9ae4bf5e7b..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/Operation.php +++ /dev/null @@ -1,299 +0,0 @@ - '', - 'httpMethod' => '', - 'uri' => '', - 'responseModel' => null, - 'notes' => '', - 'summary' => '', - 'documentationUrl' => null, - 'deprecated' => false, - 'data' => [], - 'parameters' => [], - 'additionalParameters' => null, - 'errorResponses' => [] - ]; - - $this->description = $description; - - if (isset($config['extends'])) { - $config = $this->resolveExtends($config['extends'], $config); - } - - $this->config = $config + $defaults; - - // Account for the old style of using responseClass - if (isset($config['responseClass'])) { - $this->config['responseModel'] = $config['responseClass']; - } - - $this->resolveParameters(); - } - - public function toArray() - { - return $this->config; - } - - /** - * Get the service description that the operation belongs to - * - * @return Description - */ - public function getServiceDescription() - { - return $this->description; - } - - /** - * Get the params of the operation - * - * @return Parameter[] - */ - public function getParams() - { - return $this->parameters; - } - - /** - * Get additionalParameters of the operation - * - * @return Parameter|null - */ - public function getAdditionalParameters() - { - return $this->additionalParameters; - } - - /** - * Check if the operation has a specific parameter by name - * - * @param string $name Name of the param - * - * @return bool - */ - public function hasParam($name) - { - return isset($this->parameters[$name]); - } - - /** - * Get a single parameter of the operation - * - * @param string $name Parameter to retrieve by name - * - * @return Parameter|null - */ - public function getParam($name) - { - return isset($this->parameters[$name]) - ? $this->parameters[$name] - : null; - } - - /** - * Get the HTTP method of the operation - * - * @return string|null - */ - public function getHttpMethod() - { - return $this->config['httpMethod']; - } - - /** - * Get the name of the operation - * - * @return string|null - */ - public function getName() - { - return $this->config['name']; - } - - /** - * Get a short summary of what the operation does - * - * @return string|null - */ - public function getSummary() - { - return $this->config['summary']; - } - - /** - * Get a longer text field to explain the behavior of the operation - * - * @return string|null - */ - public function getNotes() - { - return $this->config['notes']; - } - - /** - * Get the documentation URL of the operation - * - * @return string|null - */ - public function getDocumentationUrl() - { - return $this->config['documentationUrl']; - } - - /** - * Get the name of the model used for processing the response. - * - * @return string - */ - public function getResponseModel() - { - return $this->config['responseModel']; - } - - /** - * Get whether or not the operation is deprecated - * - * @return bool - */ - public function getDeprecated() - { - return $this->config['deprecated']; - } - - /** - * Get the URI that will be merged into the generated request - * - * @return string - */ - public function getUri() - { - return $this->config['uri']; - } - - /** - * Get the errors that could be encountered when executing the operation - * - * @return array - */ - public function getErrorResponses() - { - return $this->config['errorResponses']; - } - - /** - * Get extra data from the operation - * - * @param string $name Name of the data point to retrieve or null to - * retrieve all of the extra data. - * - * @return mixed|null - */ - public function getData($name = null) - { - if ($name === null) { - return $this->config['data']; - } elseif (isset($this->config['data'][$name])) { - return $this->config['data'][$name]; - } else { - return null; - } - } - - private function resolveExtends($name, array $config) - { - if (!$this->description->hasOperation($name)) { - throw new \InvalidArgumentException('No operation named ' . $name); - } - - // Merge parameters together one level deep - $base = $this->description->getOperation($name)->toArray(); - $result = $config + $base; - - if (isset($base['parameters']) && isset($config['parameters'])) { - $result['parameters'] = $config['parameters'] + $base['parameters']; - } - - return $result; - } - - private function resolveParameters() - { - // Parameters need special handling when adding - foreach ($this->config['parameters'] as $name => $param) { - if (!is_array($param)) { - throw new \InvalidArgumentException( - 'Parameters must be arrays' - ); - } - $param['name'] = $name; - $this->parameters[$name] = new Parameter( - $param, - ['description' => $this->description] - ); - } - - if ($this->config['additionalParameters']) { - if (is_array($this->config['additionalParameters'])) { - $this->additionalParameters = new Parameter( - $this->config['additionalParameters'], - ['description' => $this->description] - ); - } else { - $this->additionalParameters = $this->config['additionalParameters']; - } - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/Parameter.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/Parameter.php deleted file mode 100644 index 112f09f677..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/Parameter.php +++ /dev/null @@ -1,596 +0,0 @@ -originalData = $data; - - if (isset($options['description'])) { - $this->serviceDescription = $options['description']; - if (!($this->serviceDescription instanceof DescriptionInterface)) { - throw new \InvalidArgumentException('description must be a Description'); - } - if (isset($data['$ref'])) { - if ($model = $this->serviceDescription->getModel($data['$ref'])) { - $name = isset($data['name']) ? $data['name'] : null; - $data = $model->toArray() + $data; - if ($name) { - $data['name'] = $name; - } - } - } elseif (isset($data['extends'])) { - // If this parameter extends from another parameter then start - // with the actual data union in the parent's data (e.g. actual - // supersedes parent) - if ($extends = $this->serviceDescription->getModel($data['extends'])) { - $data += $extends->toArray(); - } - } - } - - // Pull configuration data into the parameter - foreach ($data as $key => $value) { - $this->{$key} = $value; - } - - $this->required = (bool) $this->required; - $this->data = (array) $this->data; - - if ($this->filters) { - $this->setFilters((array) $this->filters); - } - - if ($this->type == 'object' && $this->additionalProperties === null) { - $this->additionalProperties = true; - } - } - - /** - * Convert the object to an array - * - * @return array - */ - public function toArray() - { - return $this->originalData; - } - - /** - * Get the default or static value of the command based on a value - * - * @param string $value Value that is currently set - * - * @return mixed Returns the value, a static value if one is present, or a default value - */ - public function getValue($value) - { - if ($this->static || ($this->default !== null && $value === null)) { - return $this->default; - } - - return $value; - } - - /** - * Run a value through the filters OR format attribute associated with the - * parameter. - * - * @param mixed $value Value to filter - * - * @return mixed Returns the filtered value - * @throws \RuntimeException when trying to format when no service - * description is available. - */ - public function filter($value) - { - // Formats are applied exclusively and supersed filters - if ($this->format) { - if (!$this->serviceDescription) { - throw new \RuntimeException('No service description was set so ' - . 'the value cannot be formatted.'); - } - return $this->serviceDescription->format($this->format, $value); - } - - // Convert Boolean values - if ($this->type == 'boolean' && !is_bool($value)) { - $value = filter_var($value, FILTER_VALIDATE_BOOLEAN); - } - - // Apply filters to the value - if ($this->filters) { - foreach ($this->filters as $filter) { - if (is_array($filter)) { - // Convert complex filters that hold value place holders - foreach ($filter['args'] as &$data) { - if ($data == '@value') { - $data = $value; - } elseif ($data == '@api') { - $data = $this; - } - } - $value = call_user_func_array( - $filter['method'], - $filter['args'] - ); - } else { - $value = call_user_func($filter, $value); - } - } - } - - return $value; - } - - /** - * Get the name of the parameter - * - * @return string - */ - public function getName() - { - return $this->name; - } - - /** - * Set the name of the parameter - * - * @param string $name Name to set - */ - public function setName($name) - { - $this->name = $name; - } - - /** - * Get the key of the parameter, where sentAs will supersede name if it is - * set. - * - * @return string - */ - public function getWireName() - { - return $this->sentAs ?: $this->name; - } - - /** - * Get the type(s) of the parameter - * - * @return string|array - */ - public function getType() - { - return $this->type; - } - - /** - * Get if the parameter is required - * - * @return bool - */ - public function getRequired() - { - return $this->required; - } - - /** - * Get the default value of the parameter - * - * @return string|null - */ - public function getDefault() - { - return $this->default; - } - - /** - * Get the description of the parameter - * - * @return string|null - */ - public function getDescription() - { - return $this->description; - } - - /** - * Get the minimum acceptable value for an integer - * - * @return int|null - */ - public function getMinimum() - { - return $this->minimum; - } - - /** - * Get the maximum acceptable value for an integer - * - * @return int|null - */ - public function getMaximum() - { - return $this->maximum; - } - - /** - * Get the minimum allowed length of a string value - * - * @return int - */ - public function getMinLength() - { - return $this->minLength; - } - - /** - * Get the maximum allowed length of a string value - * - * @return int|null - */ - public function getMaxLength() - { - return $this->maxLength; - } - - /** - * Get the maximum allowed number of items in an array value - * - * @return int|null - */ - public function getMaxItems() - { - return $this->maxItems; - } - - /** - * Get the minimum allowed number of items in an array value - * - * @return int - */ - public function getMinItems() - { - return $this->minItems; - } - - /** - * Get the location of the parameter - * - * @return string|null - */ - public function getLocation() - { - return $this->location; - } - - /** - * Get the sentAs attribute of the parameter that used with locations to - * sentAs an attribute when it is being applied to a location. - * - * @return string|null - */ - public function getSentAs() - { - return $this->sentAs; - } - - /** - * Retrieve a known property from the parameter by name or a data property - * by name. When not specific name value is specified, all data properties - * will be returned. - * - * @param string|null $name Specify a particular property name to retrieve - * - * @return array|mixed|null - */ - public function getData($name = null) - { - if (!$name) { - return $this->data; - } elseif (isset($this->data[$name])) { - return $this->data[$name]; - } elseif (isset($this->{$name})) { - return $this->{$name}; - } - - return null; - } - - /** - * Get whether or not the default value can be changed - * - * @return mixed|null - */ - public function getStatic() - { - return $this->static; - } - - /** - * Get an array of filters used by the parameter - * - * @return array - */ - public function getFilters() - { - return $this->filters ?: []; - } - - /** - * Get the properties of the parameter - * - * @return Parameter[] - */ - public function getProperties() - { - if (!$this->propertiesCache) { - $this->propertiesCache = []; - foreach (array_keys($this->properties) as $name) { - $this->propertiesCache[$name] = $this->getProperty($name); - } - } - - return $this->propertiesCache; - } - - /** - * Get a specific property from the parameter - * - * @param string $name Name of the property to retrieve - * - * @return null|Parameter - */ - public function getProperty($name) - { - if (!isset($this->properties[$name])) { - return null; - } - - if (!($this->properties[$name] instanceof self)) { - $this->properties[$name]['name'] = $name; - $this->properties[$name] = new static( - $this->properties[$name], - ['description' => $this->serviceDescription] - ); - } - - return $this->properties[$name]; - } - - /** - * Get the additionalProperties value of the parameter - * - * @return bool|Parameter|null - */ - public function getAdditionalProperties() - { - if (is_array($this->additionalProperties)) { - $this->additionalProperties = new static( - $this->additionalProperties, - ['description' => $this->serviceDescription] - ); - } - - return $this->additionalProperties; - } - - /** - * Get the item data of the parameter - * - * @return Parameter|null - */ - public function getItems() - { - if (is_array($this->items)) { - $this->items = new static( - $this->items, - ['description' => $this->serviceDescription] - ); - } - - return $this->items; - } - - /** - * Get the enum of strings that are valid for the parameter - * - * @return array|null - */ - public function getEnum() - { - return $this->enum; - } - - /** - * Get the regex pattern that must match a value when the value is a string - * - * @return string - */ - public function getPattern() - { - return $this->pattern; - } - - /** - * Get the format attribute of the schema - * - * @return string - */ - public function getFormat() - { - return $this->format; - } - - /** - * Set the array of filters used by the parameter - * - * @param array $filters Array of functions to use as filters - * - * @return self - */ - private function setFilters(array $filters) - { - $this->filters = []; - foreach ($filters as $filter) { - $this->addFilter($filter); - } - - return $this; - } - - /** - * Add a filter to the parameter - * - * @param string|array $filter Method to filter the value through - * - * @return self - * @throws \InvalidArgumentException - */ - private function addFilter($filter) - { - if (is_array($filter)) { - if (!isset($filter['method'])) { - throw new \InvalidArgumentException( - 'A [method] value must be specified for each complex filter' - ); - } - } - - if (!$this->filters) { - $this->filters = [$filter]; - } else { - $this->filters[] = $filter; - } - - return $this; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/AbstractLocation.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/AbstractLocation.php deleted file mode 100644 index a27a9abe06..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/AbstractLocation.php +++ /dev/null @@ -1,87 +0,0 @@ -locationName = $locationName; - } - - public function visit( - CommandInterface $command, - RequestInterface $request, - Parameter $param, - array $context - ) {} - - public function after( - CommandInterface $command, - RequestInterface $request, - Operation $operation, - array $context - ) {} - - /** - * Prepare (filter and set desired name for request item) the value for - * request. - * - * @param mixed $value - * @param Parameter $param - * - * @return array|mixed - */ - protected function prepareValue($value, Parameter $param) - { - return is_array($value) - ? $this->resolveRecursively($value, $param) - : $param->filter($value); - } - - /** - * Recursively prepare and filter nested values. - * - * @param array $value Value to map - * @param Parameter $param Parameter related to the current key. - * - * @return array Returns the mapped array - */ - protected function resolveRecursively(array $value, Parameter $param) - { - foreach ($value as $name => &$v) { - switch ($param->getType()) { - case 'object': - if ($subParam = $param->getProperty($name)) { - $key = $subParam->getWireName(); - $value[$key] = $this->prepareValue($v, $subParam); - if ($name != $key) { - unset($value[$name]); - } - } elseif ($param->getAdditionalProperties() instanceof Parameter) { - $v = $this->prepareValue($v, $param->getAdditionalProperties()); - } - break; - case 'array': - if ($items = $param->getItems()) { - $v = $this->prepareValue($v, $items); - } - break; - } - } - - return $param->filter($value); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/BodyLocation.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/BodyLocation.php deleted file mode 100644 index 17446b1ffd..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/BodyLocation.php +++ /dev/null @@ -1,23 +0,0 @@ -getName()]; - $request->setBody(Stream::factory($param->filter($value))); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/HeaderLocation.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/HeaderLocation.php deleted file mode 100644 index 3d31cfcb59..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/HeaderLocation.php +++ /dev/null @@ -1,39 +0,0 @@ -getName()]; - $request->setHeader($param->getWireName(), $param->filter($value)); - } - - public function after( - CommandInterface $command, - RequestInterface $request, - Operation $operation, - array $context - ) { - $additional = $operation->getAdditionalParameters(); - if ($additional && $additional->getLocation() == $this->locationName) { - foreach ($command->toArray() as $key => $value) { - if (!$operation->hasParam($key)) { - $request->setHeader($key, $additional->filter($value)); - } - } - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/JsonLocation.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/JsonLocation.php deleted file mode 100644 index da46338e19..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/JsonLocation.php +++ /dev/null @@ -1,70 +0,0 @@ -locationName = $locationName; - $this->jsonContentType = $contentType; - } - - public function visit( - CommandInterface $command, - RequestInterface $request, - Parameter $param, - array $context - ) { - $this->jsonData[$param->getWireName()] = $this->prepareValue( - $command[$param->getName()], - $param - ); - } - - public function after( - CommandInterface $command, - RequestInterface $request, - Operation $operation, - array $context - ) { - $data = $this->jsonData; - $this->jsonData = null; - - // Add additional parameters to the JSON document - $additional = $operation->getAdditionalParameters(); - if ($additional && $additional->getLocation() == $this->locationName) { - foreach ($command->toArray() as $key => $value) { - if (!$operation->hasParam($key)) { - $data[$key] = $this->prepareValue($value, $additional); - } - } - } - - // Don't overwrite the Content-Type if one is set - if ($this->jsonContentType && !$request->hasHeader('Content-Type')) { - $request->setHeader('Content-Type', $this->jsonContentType); - } - - $request->setBody(Stream::factory(json_encode($data))); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/PostFieldLocation.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/PostFieldLocation.php deleted file mode 100644 index f0a0ab3876..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/PostFieldLocation.php +++ /dev/null @@ -1,56 +0,0 @@ -getBody(); - if (!($body instanceof PostBodyInterface)) { - throw new \RuntimeException('Must be a POST body interface'); - } - - $body->setField( - $param->getWireName(), - $this->prepareValue($command[$param->getName()], $param) - ); - } - - public function after( - CommandInterface $command, - RequestInterface $request, - Operation $operation, - array $context - ) { - $additional = $operation->getAdditionalParameters(); - if ($additional && $additional->getLocation() == $this->locationName) { - - $body = $request->getBody(); - if (!($body instanceof PostBodyInterface)) { - throw new \RuntimeException('Must be a POST body interface'); - } - - foreach ($command->toArray() as $key => $value) { - if (!$operation->hasParam($key)) { - $body->setField( - $key, - $this->prepareValue($value, $additional) - ); - } - } - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/PostFileLocation.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/PostFileLocation.php deleted file mode 100644 index 94434720a9..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/PostFileLocation.php +++ /dev/null @@ -1,34 +0,0 @@ -getBody(); - if (!($body instanceof PostBodyInterface)) { - throw new \RuntimeException('Must be a POST body interface'); - } - - $value = $param->filter($command[$param->getName()]); - if (!($value instanceof PostFileInterface)) { - $value = new PostFile($param->getWireName(), $value); - } - - $body->addFile($value); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/QueryLocation.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/QueryLocation.php deleted file mode 100644 index bdcb74d583..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/QueryLocation.php +++ /dev/null @@ -1,44 +0,0 @@ -getQuery()[$param->getWireName()] = $this->prepareValue( - $command[$param->getName()], - $param - ); - } - - public function after( - CommandInterface $command, - RequestInterface $request, - Operation $operation, - array $context - ) { - $additional = $operation->getAdditionalParameters(); - if ($additional && $additional->getLocation() == $this->locationName) { - foreach ($command->toArray() as $key => $value) { - if (!$operation->hasParam($key)) { - $request->getQuery()[$key] = $this->prepareValue( - $value, - $additional - ); - } - } - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/RequestLocationInterface.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/RequestLocationInterface.php deleted file mode 100644 index 8f98228ec0..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/RequestLocation/RequestLocationInterface.php +++ /dev/null @@ -1,46 +0,0 @@ -locationName = $locationName; - $this->contentType = $contentType; - $this->data = new \SplObjectStorage(); - } - - public function visit( - CommandInterface $command, - RequestInterface $request, - Parameter $param, - array $context - ) { - // Buffer and order the parameters to visit based on if they are - // top-level attributes or child nodes. - // @link https://github.com/guzzle/guzzle/pull/494 - if ($param->getData('xmlAttribute')) { - array_unshift($this->buffered, $param); - } else { - $this->buffered[] = $param; - } - } - - public function after( - CommandInterface $command, - RequestInterface $request, - Operation $operation, - array $context - ) { - foreach ($this->buffered as $param) { - $this->visitWithValue( - $command[$param->getName()], - $param, - $operation - ); - } - - $this->buffered = []; - - $additional = $operation->getAdditionalParameters(); - if ($additional && $additional->getLocation() == $this->locationName) { - foreach ($command->toArray() as $key => $value) { - if (!$operation->hasParam($key)) { - $additional->setName($key); - $this->visitWithValue($value, $additional, $operation); - } - } - $additional->setName(null); - } - - // If data was found that needs to be serialized, then do so - $xml = null; - if ($this->writer) { - $xml = $this->finishDocument($this->writer); - } elseif ($operation->getData('xmlAllowEmpty')) { - // Check if XML should always be sent for the command - $writer = $this->createRootElement($operation); - $xml = $this->finishDocument($writer); - } - - if ($xml) { - $request->setBody(Stream::factory($xml)); - // Don't overwrite the Content-Type if one is set - if ($this->contentType && !$request->hasHeader('Content-Type')) { - $request->setHeader('Content-Type', $this->contentType); - } - } - - $this->writer = null; - } - - /** - * Create the root XML element to use with a request - * - * @param Operation $operation Operation object - * - * @return \XMLWriter - */ - protected function createRootElement(Operation $operation) - { - static $defaultRoot = ['name' => 'Request']; - // If no root element was specified, then just wrap the XML in 'Request' - $root = $operation->getData('xmlRoot') ?: $defaultRoot; - // Allow the XML declaration to be customized with xmlEncoding - $encoding = $operation->getData('xmlEncoding'); - $writer = $this->startDocument($encoding); - $writer->startElement($root['name']); - - // Create the wrapping element with no namespaces if no namespaces were present - if (!empty($root['namespaces'])) { - // Create the wrapping element with an array of one or more namespaces - foreach ((array) $root['namespaces'] as $prefix => $uri) { - $nsLabel = 'xmlns'; - if (!is_numeric($prefix)) { - $nsLabel .= ':'.$prefix; - } - $writer->writeAttribute($nsLabel, $uri); - } - } - - return $writer; - } - - /** - * Recursively build the XML body - * - * @param \XMLWriter $writer XML to modify - * @param Parameter $param API Parameter - * @param mixed $value Value to add - */ - protected function addXml(\XMLWriter $writer, Parameter $param, $value) - { - $value = $param->filter($value); - $type = $param->getType(); - $name = $param->getWireName(); - $prefix = null; - $namespace = $param->getData('xmlNamespace'); - if (false !== strpos($name, ':')) { - list($prefix, $name) = explode(':', $name, 2); - } - - if ($type == 'object' || $type == 'array') { - if (!$param->getData('xmlFlattened')) { - if ($namespace) { - $writer->startElementNS(null, $name, $namespace); - } else { - $writer->startElement($name); - } - } - if ($param->getType() == 'array') { - $this->addXmlArray($writer, $param, $value); - } elseif ($param->getType() == 'object') { - $this->addXmlObject($writer, $param, $value); - } - if (!$param->getData('xmlFlattened')) { - $writer->endElement(); - } - return; - } - if ($param->getData('xmlAttribute')) { - $this->writeAttribute($writer, $prefix, $name, $namespace, $value); - } else { - $this->writeElement($writer, $prefix, $name, $namespace, $value); - } - } - - /** - * Write an attribute with namespace if used - * - * @param \XMLWriter $writer XMLWriter instance - * @param string $prefix Namespace prefix if any - * @param string $name Attribute name - * @param string $namespace The uri of the namespace - * @param string $value The attribute content - */ - protected function writeAttribute($writer, $prefix, $name, $namespace, $value) - { - if ($namespace) { - $writer->writeAttributeNS($prefix, $name, $namespace, $value); - } else { - $writer->writeAttribute($name, $value); - } - } - - /** - * Write an element with namespace if used - * - * @param \XMLWriter $writer XML writer resource - * @param string $prefix Namespace prefix if any - * @param string $name Element name - * @param string $namespace The uri of the namespace - * @param string $value The element content - */ - protected function writeElement(\XMLWriter $writer, $prefix, $name, $namespace, $value) - { - if ($namespace) { - $writer->startElementNS($prefix, $name, $namespace); - } else { - $writer->startElement($name); - } - if (strpbrk($value, '<>&')) { - $writer->writeCData($value); - } else { - $writer->writeRaw($value); - } - $writer->endElement(); - } - - /** - * Create a new xml writer and start a document - * - * @param string $encoding document encoding - * - * @return \XMLWriter the writer resource - * @throws \RuntimeException if the document cannot be started - */ - protected function startDocument($encoding) - { - $this->writer = new \XMLWriter(); - if (!$this->writer->openMemory()) { - throw new \RuntimeException('Unable to open XML document in memory'); - } - if (!$this->writer->startDocument('1.0', $encoding)) { - throw new \RuntimeException('Unable to start XML document'); - } - - return $this->writer; - } - - /** - * End the document and return the output - * - * @param \XMLWriter $writer - * - * @return \string the writer resource - */ - protected function finishDocument($writer) - { - $writer->endDocument(); - - return $writer->outputMemory(); - } - - /** - * Add an array to the XML - */ - protected function addXmlArray(\XMLWriter $writer, Parameter $param, &$value) - { - if ($items = $param->getItems()) { - foreach ($value as $v) { - $this->addXml($writer, $items, $v); - } - } - } - - /** - * Add an object to the XML - */ - protected function addXmlObject(\XMLWriter $writer, Parameter $param, &$value) - { - $noAttributes = []; - - // add values which have attributes - foreach ($value as $name => $v) { - if ($property = $param->getProperty($name)) { - if ($property->getData('xmlAttribute')) { - $this->addXml($writer, $property, $v); - } else { - $noAttributes[] = ['value' => $v, 'property' => $property]; - } - } - } - - // now add values with no attributes - foreach ($noAttributes as $element) { - $this->addXml($writer, $element['property'], $element['value']); - } - } - - private function visitWithValue( - $value, - Parameter $param, - Operation $operation - ) { - if (!$this->writer) { - $this->createRootElement($operation); - } - - $this->addXml($this->writer, $param, $value); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/AbstractLocation.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/AbstractLocation.php deleted file mode 100644 index e985ec9aac..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/AbstractLocation.php +++ /dev/null @@ -1,46 +0,0 @@ -locationName = $locationName; - } - - public function before( - CommandInterface $command, - ResponseInterface $response, - Parameter $model, - &$result, - array $context = [] - ) {} - - public function after( - CommandInterface $command, - ResponseInterface $response, - Parameter $model, - &$result, - array $context = [] - ) {} - - public function visit( - CommandInterface $command, - ResponseInterface $response, - Parameter $param, - &$result, - array $context = [] - ) {} -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/BodyLocation.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/BodyLocation.php deleted file mode 100644 index d88888b5f0..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/BodyLocation.php +++ /dev/null @@ -1,22 +0,0 @@ -getName()] = $param->filter($response->getBody()); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/HeaderLocation.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/HeaderLocation.php deleted file mode 100644 index df230dc7fc..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/HeaderLocation.php +++ /dev/null @@ -1,26 +0,0 @@ -getName(); - if ($header = $response->getHeader($param->getWireName())) { - $result[$name] = $param->filter($header); - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/JsonLocation.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/JsonLocation.php deleted file mode 100644 index fa620417d9..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/JsonLocation.php +++ /dev/null @@ -1,140 +0,0 @@ -json = $response->json() ?: []; - // relocate named arrays, so that they have the same structure as - // arrays nested in objects and visit can work on them in the same way - if ($model->getType() == 'array' && ($name = $model->getName())) { - $this->json = [$name => $this->json]; - } - } - - public function after( - CommandInterface $command, - ResponseInterface $response, - Parameter $model, - &$result, - array $context = [] - ) { - // Handle additional, undefined properties - $additional = $model->getAdditionalProperties(); - if (!($additional instanceof Parameter)) { - return; - } - - // Use the model location as the default if one is not set on additional - $addLocation = $additional->getLocation() ?: $model->getLocation(); - if ($addLocation == $this->locationName) { - foreach ($this->json as $prop => $val) { - if (!isset($result[$prop])) { - // Only recurse if there is a type specified - $result[$prop] = $additional->getType() - ? $this->recurse($additional, $val) - : $val; - } - } - } - - $this->json = []; - } - - public function visit( - CommandInterface $command, - ResponseInterface $response, - Parameter $param, - &$result, - array $context = [] - ) { - $name = $param->getName(); - $key = $param->getWireName(); - - // Check if the result should be treated as a list - if ($param->getType() == 'array') { - // Treat as javascript array - if ($name) { - // name provided, store it under a key in the array - $result[$name] = $this->recurse($param, $this->json[$name]); - } else { - // top-level `array` or an empty name - $result = array_merge($result, $this->recurse($param, $this->json)); - } - } elseif (isset($this->json[$key])) { - $result[$name] = $this->recurse($param, $this->json[$key]); - } - } - - /** - * Recursively process a parameter while applying filters - * - * @param Parameter $param API parameter being validated - * @param mixed $value Value to process. - * @return mixed|null - */ - private function recurse(Parameter $param, $value) - { - if (!is_array($value)) { - return $param->filter($value); - } - - $result = []; - $type = $param->getType(); - - if ($type == 'array') { - $items = $param->getItems(); - foreach ($value as $val) { - $result[] = $this->recurse($items, $val); - } - } elseif ($type == 'object' && !isset($value[0])) { - // On the above line, we ensure that the array is associative and - // not numerically indexed - if ($properties = $param->getProperties()) { - foreach ($properties as $property) { - $key = $property->getWireName(); - if (isset($value[$key])) { - $result[$property->getName()] = $this->recurse( - $property, - $value[$key] - ); - // Remove from the value so that AP can later be handled - unset($value[$key]); - } - } - } - // Only check additional properties if everything wasn't already - // handled - if ($value) { - $additional = $param->getAdditionalProperties(); - if ($additional === null || $additional === true) { - // Merge the JSON under the resulting array - $result += $value; - } elseif ($additional instanceof Parameter) { - // Process all child elements according to the given schema - foreach ($value as $prop => $val) { - $result[$prop] = $this->recurse($additional, $val); - } - } - } - } - - return $param->filter($result); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/ReasonPhraseLocation.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/ReasonPhraseLocation.php deleted file mode 100644 index 9c4323bdca..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/ReasonPhraseLocation.php +++ /dev/null @@ -1,24 +0,0 @@ -getName()] = $param->filter( - $response->getReasonPhrase() - ); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/ResponseLocationInterface.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/ResponseLocationInterface.php deleted file mode 100644 index b41b94643c..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/ResponseLocationInterface.php +++ /dev/null @@ -1,70 +0,0 @@ -getName()] = $param->filter($response->getStatusCode()); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/XmlLocation.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/XmlLocation.php deleted file mode 100644 index b9a58c5b7c..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/ResponseLocation/XmlLocation.php +++ /dev/null @@ -1,269 +0,0 @@ -xml = $response->xml(); - } - - public function after( - CommandInterface $command, - ResponseInterface $response, - Parameter $model, - &$result, - array $context = [] - ) { - // Handle additional, undefined properties - $additional = $model->getAdditionalProperties(); - if ($additional instanceof Parameter && - $additional->getLocation() == $this->locationName - ) { - $result += self::xmlToArray($this->xml); - } - - $this->xml = null; - } - - public function visit( - CommandInterface $command, - ResponseInterface $response, - Parameter $param, - &$result, - array $context = [] - ) { - $sentAs = $param->getWireName(); - $ns = null; - if (strstr($sentAs, ':')) { - list($ns, $sentAs) = explode(':', $sentAs); - } - - // Process the primary property - if (count($this->xml->children($ns, true)->{$sentAs})) { - $result[$param->getName()] = $this->recursiveProcess( - $param, - $this->xml->children($ns, true)->{$sentAs} - ); - } - } - - /** - * Recursively process a parameter while applying filters - * - * @param Parameter $param API parameter being processed - * @param \SimpleXMLElement $node Node being processed - * @return array - */ - private function recursiveProcess( - Parameter $param, - \SimpleXMLElement $node - ) { - $result = []; - $type = $param->getType(); - - if ($type == 'object') { - $result = $this->processObject($param, $node); - } elseif ($type == 'array') { - $result = $this->processArray($param, $node); - } else { - // We are probably handling a flat data node (i.e. string or - // integer), so let's check if it's childless, which indicates a - // node containing plain text. - if ($node->children()->count() == 0) { - // Retrieve text from node - $result = (string) $node; - } - } - - // Filter out the value - if (isset($result)) { - $result = $param->filter($result); - } - - return $result; - } - - private function processArray(Parameter $param, \SimpleXMLElement $node) - { - // Cast to an array if the value was a string, but should be an array - $items = $param->getItems(); - $sentAs = $items->getWireName(); - $result = []; - $ns = null; - - if (strstr($sentAs, ':')) { - // Get namespace from the wire name - list($ns, $sentAs) = explode(':', $sentAs); - } else { - // Get namespace from data - $ns = $items->getData('xmlNs'); - } - - if ($sentAs === null) { - // A general collection of nodes - foreach ($node as $child) { - $result[] = $this->recursiveProcess($items, $child); - } - } else { - // A collection of named, repeating nodes - // (i.e. ) - $children = $node->children($ns, true)->{$sentAs}; - foreach ($children as $child) { - $result[] = $this->recursiveProcess($items, $child); - } - } - - return $result; - } - - /** - * Process an object - * - * @param Parameter $param API parameter being parsed - * @param \SimpleXMLElement $node Value to process - * @return array - */ - private function processObject(Parameter $param, \SimpleXMLElement $node) - { - $result = $knownProps = $knownAttributes = []; - - // Handle known properties - if ($properties = $param->getProperties()) { - foreach ($properties as $property) { - $name = $property->getName(); - $sentAs = $property->getWireName(); - $knownProps[$sentAs] = 1; - if (strpos($sentAs, ':')) { - list($ns, $sentAs) = explode(':', $sentAs); - } else { - $ns = $property->getData('xmlNs'); - } - - if ($property->getData('xmlAttribute')) { - // Handle XML attributes - $result[$name] = (string) $node->attributes($ns, true)->{$sentAs}; - $knownAttributes[$sentAs] = 1; - } elseif (count($node->children($ns, true)->{$sentAs})) { - // Found a child node matching wire name - $childNode = $node->children($ns, true)->{$sentAs}; - $result[$name] = $this->recursiveProcess( - $property, - $childNode - ); - } - } - } - - // Handle additional, undefined properties - $additional = $param->getAdditionalProperties(); - if ($additional instanceof Parameter) { - // Process all child elements according to the given schema - foreach ($node->children($additional->getData('xmlNs'), true) as $childNode) { - $sentAs = $childNode->getName(); - if (!isset($knownProps[$sentAs])) { - $result[$sentAs] = $this->recursiveProcess( - $additional, - $childNode - ); - } - } - } elseif ($additional === null || $additional === true) { - // Blindly transform the XML into an array preserving as much data - // as possible. Remove processed, aliased properties. - $array = array_diff_key(static::xmlToArray($node), $knownProps); - // Remove @attributes that were explicitly plucked from the - // attributes list. - if (isset($array['@attributes']) && $knownAttributes) { - $array['@attributes'] = array_diff_key($array['@attributes'], $knownProps); - if (!$array['@attributes']) { - unset($array['@attributes']); - } - } - - // Merge it together with the original result - $result = array_merge($array, $result); - } - - return $result; - } - - /** - * Convert an XML document to an array. - * - * @param \SimpleXMLElement $xml - * @param int $nesting - * @param null $ns - * - * @return array - */ - private static function xmlToArray( - \SimpleXMLElement $xml, - $ns = null, - $nesting = 0 - ) { - $result = []; - $children = $xml->children($ns, true); - - foreach ($children as $name => $child) { - $attributes = (array) $child->attributes($ns, true); - if (!isset($result[$name])) { - $childArray = static::xmlToArray($child, $ns, $nesting + 1); - $result[$name] = $attributes - ? array_merge($attributes, $childArray) - : $childArray; - continue; - } - // A child element with this name exists so we're assuming - // that the node contains a list of elements - if (!is_array($result[$name])) { - $result[$name] = [$result[$name]]; - } - $childArray = static::xmlToArray($child, $ns, $nesting + 1); - if ($attributes) { - $result[$name][] = array_merge($attributes, $childArray); - } else { - $result[$name][] = $childArray; - } - } - - // Extract text from node - $text = trim((string) $xml); - if ($text === '') { - $text = null; - } - - // Process attributes - $attributes = (array) $xml->attributes($ns, true); - if ($attributes) { - if ($text !== null) { - $result['value'] = $text; - } - $result = array_merge($attributes, $result); - } elseif ($text !== null) { - $result = $text; - } - - // Make sure we're always returning an array - if ($nesting == 0 && !is_array($result)) { - $result = [$result]; - } - - return $result; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/SchemaFormatter.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/SchemaFormatter.php deleted file mode 100644 index 33280a6c69..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/SchemaFormatter.php +++ /dev/null @@ -1,141 +0,0 @@ -formatDateTime($value); - case 'date-time-http': - return $this->formatDateTimeHttp($value); - case 'date': - return $this->formatDate($value); - case 'time': - return $this->formatTime($value); - case 'timestamp': - return $this->formatTimestamp($value); - case 'boolean-string': - return $this->formatBooleanAsString($value); - default: - return $value; - } - } - - /** - * Perform the actual DateTime formatting - * - * @param int|string|\DateTime $dateTime Date time value - * @param string $format Format of the result - * - * @return string - * @throws \InvalidArgumentException - */ - protected function dateFormatter($dateTime, $format) - { - if (is_numeric($dateTime)) { - return gmdate($format, (int) $dateTime); - } - - if (is_string($dateTime)) { - $dateTime = new \DateTime($dateTime); - } - - if ($dateTime instanceof \DateTime) { - static $utc; - if (!$utc) { - $utc = new \DateTimeZone('UTC'); - } - return $dateTime->setTimezone($utc)->format($format); - } - - throw new \InvalidArgumentException('Date/Time values must be either ' - . 'be a string, integer, or DateTime object'); - } - - /** - * Create a ISO 8601 (YYYY-MM-DDThh:mm:ssZ) formatted date time value in - * UTC time. - * - * @param string|integer|\DateTime $value Date time value - * - * @return string - */ - private function formatDateTime($value) - { - return $this->dateFormatter($value, 'Y-m-d\TH:i:s\Z'); - } - - /** - * Create an HTTP date (RFC 1123 / RFC 822) formatted UTC date-time string - * - * @param string|integer|\DateTime $value Date time value - * - * @return string - */ - private function formatDateTimeHttp($value) - { - return $this->dateFormatter($value, 'D, d M Y H:i:s \G\M\T'); - } - - /** - * Create a YYYY-MM-DD formatted string - * - * @param string|integer|\DateTime $value Date time value - * - * @return string - */ - private function formatDate($value) - { - return $this->dateFormatter($value, 'Y-m-d'); - } - - /** - * Create a hh:mm:ss formatted string - * - * @param string|integer|\DateTime $value Date time value - * - * @return string - */ - private function formatTime($value) - { - return $this->dateFormatter($value, 'H:i:s'); - } - - /** - * Formats a boolean value as a string - * - * @param string|integer|bool $value Value to convert to a boolean - * 'true' / 'false' value - * - * @return string - */ - private function formatBooleanAsString($value) - { - return filter_var($value, FILTER_VALIDATE_BOOLEAN) ? 'true' : 'false'; - } - - /** - * Return a UNIX timestamp in the UTC timezone - * - * @param string|integer|\DateTime $value Time value - * - * @return int - */ - private function formatTimestamp($value) - { - return (int) $this->dateFormatter($value, 'U'); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/SchemaValidator.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/SchemaValidator.php deleted file mode 100644 index 88e0420afe..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/SchemaValidator.php +++ /dev/null @@ -1,296 +0,0 @@ -castIntegerToStringType = $castIntegerToStringType; - } - - public function validate(Parameter $param, &$value) - { - $this->errors = []; - $this->recursiveProcess($param, $value); - - if (empty($this->errors)) { - return true; - } else { - sort($this->errors); - return false; - } - } - - /** - * Get the errors encountered while validating - * - * @return array - */ - public function getErrors() - { - return $this->errors ?: []; - } - - /** - * From the allowable types, determine the type that the variable matches - * - * @param string $type Parameter type - * @param mixed $value Value to determine the type - * - * @return string|bool Returns the matching type on - */ - protected function determineType($type, $value) - { - foreach ((array) $type as $t) { - if ($t == 'string' - && (is_string($value) || (is_object($value) && method_exists($value, '__toString'))) - ) { - return 'string'; - } elseif ($t == 'object' && (is_array($value) || is_object($value))) { - return 'object'; - } elseif ($t == 'array' && is_array($value)) { - return 'array'; - } elseif ($t == 'integer' && is_integer($value)) { - return 'integer'; - } elseif ($t == 'boolean' && is_bool($value)) { - return 'boolean'; - } elseif ($t == 'number' && is_numeric($value)) { - return 'number'; - } elseif ($t == 'numeric' && is_numeric($value)) { - return 'numeric'; - } elseif ($t == 'null' && !$value) { - return 'null'; - } elseif ($t == 'any') { - return 'any'; - } - } - - return false; - } - - /** - * Recursively validate a parameter - * - * @param Parameter $param API parameter being validated - * @param mixed $value Value to validate and validate. The value may - * change during this validate. - * @param string $path Current validation path (used for error reporting) - * @param int $depth Current depth in the validation validate - * - * @return bool Returns true if valid, or false if invalid - */ - protected function recursiveProcess( - Parameter $param, - &$value, - $path = '', - $depth = 0 - ) { - // Update the value by adding default or static values - $value = $param->getValue($value); - - $required = $param->getRequired(); - // if the value is null and the parameter is not required or is static, - // then skip any further recursion - if ((null === $value && !$required) || $param->getStatic()) { - return true; - } - - $type = $param->getType(); - // Attempt to limit the number of times is_array is called by tracking - // if the value is an array - $valueIsArray = is_array($value); - // If a name is set then update the path so that validation messages - // are more helpful - if ($name = $param->getName()) { - $path .= "[{$name}]"; - } - - if ($type == 'object') { - - // Determine whether or not this "value" has properties and should - // be traversed - $traverse = $temporaryValue = false; - - // Convert the value to an array - if (!$valueIsArray && $value instanceof ToArrayInterface) { - $value = $value->toArray(); - } - - if ($valueIsArray) { - // Ensure that the array is associative and not numerically - // indexed - if (isset($value[0])) { - $this->errors[] = "{$path} must be an array of properties. Got a numerically indexed array."; - return false; - } - $traverse = true; - } elseif ($value === null) { - // Attempt to let the contents be built up by default values if - // possible - $value = []; - $temporaryValue = $valueIsArray = $traverse = true; - } - - if ($traverse) { - - if ($properties = $param->getProperties()) { - // if properties were found, validate each property - foreach ($properties as $property) { - $name = $property->getName(); - if (isset($value[$name])) { - $this->recursiveProcess($property, $value[$name], $path, $depth + 1); - } else { - $current = null; - $this->recursiveProcess($property, $current, $path, $depth + 1); - // Only set the value if it was populated - if (null !== $current) { - $value[$name] = $current; - } - } - } - } - - $additional = $param->getAdditionalProperties(); - if ($additional !== true) { - // If additional properties were found, then validate each - // against the additionalProperties attr. - $keys = array_keys($value); - // Determine the keys that were specified that were not - // listed in the properties of the schema - $diff = array_diff($keys, array_keys($properties)); - if (!empty($diff)) { - // Determine which keys are not in the properties - if ($additional instanceOf Parameter) { - foreach ($diff as $key) { - $this->recursiveProcess($additional, $value[$key], "{$path}[{$key}]", $depth); - } - } else { - // if additionalProperties is set to false and there - // are additionalProperties in the values, then fail - foreach ($diff as $prop) { - $this->errors[] = sprintf('%s[%s] is not an allowed property', $path, $prop); - } - } - } - } - - // A temporary value will be used to traverse elements that - // have no corresponding input value. This allows nested - // required parameters with default values to bubble up into the - // input. Here we check if we used a temp value and nothing - // bubbled up, then we need to remote the value. - if ($temporaryValue && empty($value)) { - $value = null; - $valueIsArray = false; - } - } - - } elseif ($type == 'array' && $valueIsArray && $param->getItems()) { - foreach ($value as $i => &$item) { - // Validate each item in an array against the items attribute of the schema - $this->recursiveProcess($param->getItems(), $item, $path . "[{$i}]", $depth + 1); - } - } - - // If the value is required and the type is not null, then there is an - // error if the value is not set - if ($required && $value === null && $type != 'null') { - $message = "{$path} is " . ($param->getType() - ? ('a required ' . implode(' or ', (array) $param->getType())) - : 'required'); - if ($param->getDescription()) { - $message .= ': ' . $param->getDescription(); - } - $this->errors[] = $message; - return false; - } - - // Validate that the type is correct. If the type is string but an - // integer was passed, the class can be instructed to cast the integer - // to a string to pass validation. This is the default behavior. - if ($type && (!$type = $this->determineType($type, $value))) { - if ($this->castIntegerToStringType - && $param->getType() == 'string' - && is_integer($value) - ) { - $value = (string) $value; - } else { - $this->errors[] = "{$path} must be of type " . implode(' or ', (array) $param->getType()); - } - } - - // Perform type specific validation for strings, arrays, and integers - if ($type == 'string') { - - // Strings can have enums which are a list of predefined values - if (($enum = $param->getEnum()) && !in_array($value, $enum)) { - $this->errors[] = "{$path} must be one of " . implode(' or ', array_map(function ($s) { - return '"' . addslashes($s) . '"'; - }, $enum)); - } - // Strings can have a regex pattern that the value must match - if (($pattern = $param->getPattern()) && !preg_match($pattern, $value)) { - $this->errors[] = "{$path} must match the following regular expression: {$pattern}"; - } - - $strLen = null; - if ($min = $param->getMinLength()) { - $strLen = strlen($value); - if ($strLen < $min) { - $this->errors[] = "{$path} length must be greater than or equal to {$min}"; - } - } - if ($max = $param->getMaxLength()) { - if (($strLen ?: strlen($value)) > $max) { - $this->errors[] = "{$path} length must be less than or equal to {$max}"; - } - } - - } elseif ($type == 'array') { - - $size = null; - if ($min = $param->getMinItems()) { - $size = count($value); - if ($size < $min) { - $this->errors[] = "{$path} must contain {$min} or more elements"; - } - } - if ($max = $param->getMaxItems()) { - if (($size ?: count($value)) > $max) { - $this->errors[] = "{$path} must contain {$max} or fewer elements"; - } - } - - } elseif ($type == 'integer' || $type == 'number' || $type == 'numeric') { - if (($min = $param->getMinimum()) && $value < $min) { - $this->errors[] = "{$path} must be greater than or equal to {$min}"; - } - if (($max = $param->getMaximum()) && $value > $max) { - $this->errors[] = "{$path} must be less than or equal to {$max}"; - } - } - - return empty($this->errors); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/Serializer.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/Serializer.php deleted file mode 100644 index 7986bda819..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/Serializer.php +++ /dev/null @@ -1,171 +0,0 @@ - new BodyLocation('body'), - 'query' => new QueryLocation('query'), - 'header' => new HeaderLocation('header'), - 'json' => new JsonLocation('json'), - 'xml' => new XmlLocation('xml'), - 'postField' => new PostFieldLocation('postField'), - 'postFile' => new PostFileLocation('postFile') - ]; - } - - $this->requestLocations = $requestLocations + $defaultRequestLocations; - $this->description = $description; - } - - public function __invoke(CommandTransaction $trans) - { - $request = $this->createRequest($trans); - $this->prepareRequest($trans, $request); - - return $request; - } - - /** - * Prepares a request for sending using location visitors - * - * @param CommandTransaction $trans - * @param RequestInterface $request Request being created - * @throws \RuntimeException If a location cannot be handled - */ - protected function prepareRequest( - CommandTransaction $trans, - RequestInterface $request - ) { - $visitedLocations = []; - $context = ['client' => $trans->client, 'command' => $trans->command]; - $operation = $this->description->getOperation($trans->command->getName()); - - // Visit each actual parameter - foreach ($operation->getParams() as $name => $param) { - /* @var Parameter $param */ - $location = $param->getLocation(); - // Skip parameters that have not been set or are URI location - if ($location == 'uri' || !$trans->command->hasParam($name)) { - continue; - } - if (!isset($this->requestLocations[$location])) { - throw new \RuntimeException("No location registered for $location"); - } - $visitedLocations[$location] = true; - $this->requestLocations[$location]->visit( - $trans->command, - $request, - $param, - $context - ); - } - - // Ensure that the after() method is invoked for additionalParameters - if ($additional = $operation->getAdditionalParameters()) { - $visitedLocations[$additional->getLocation()] = true; - } - - // Call the after() method for each visited location - foreach (array_keys($visitedLocations) as $location) { - $this->requestLocations[$location]->after( - $trans->command, - $request, - $operation, - $context - ); - } - } - - /** - * Create a request for the command and operation - * - * @param CommandTransaction $trans - * - * @return RequestInterface - * @throws \RuntimeException - */ - protected function createRequest(CommandTransaction $trans) - { - $operation = $this->description->getOperation($trans->command->getName()); - - // If the command does not specify a template, then assume the base URL - // of the client - if (null === ($uri = $operation->getUri())) { - return $trans->client->createRequest( - $operation->getHttpMethod(), - $this->description->getBaseUrl(), - $trans->command['request_options'] ?: [] - ); - } - - return $this->createCommandWithUri( - $operation, $trans->command, $trans->serviceClient - ); - } - - /** - * Create a request for an operation with a uri merged onto a base URI - */ - private function createCommandWithUri( - Operation $operation, - CommandInterface $command, - ServiceClientInterface $client - ) { - // Get the path values and use the client config settings - $variables = []; - foreach ($operation->getParams() as $name => $arg) { - /* @var Parameter $arg */ - if ($arg->getLocation() == 'uri') { - if (isset($command[$name])) { - $variables[$name] = $arg->filter($command[$name]); - if (!is_array($variables[$name])) { - $variables[$name] = (string) $variables[$name]; - } - } - } - } - - // Expand the URI template. - $uri = Utils::uriTemplate($operation->getUri(), $variables); - - return $client->getHttpClient()->createRequest( - $operation->getHttpMethod(), - $this->description->getBaseUrl()->combine($uri), - $command['request_options'] ?: [] - ); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/Subscriber/ProcessResponse.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/Subscriber/ProcessResponse.php deleted file mode 100644 index 3ddf81b89b..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/Subscriber/ProcessResponse.php +++ /dev/null @@ -1,211 +0,0 @@ - new BodyLocation('body'), - 'header' => new HeaderLocation('header'), - 'reasonPhrase' => new ReasonPhraseLocation('reasonPhrase'), - 'statusCode' => new StatusCodeLocation('statusCode'), - 'xml' => new XmlLocation('xml'), - 'json' => new JsonLocation('json') - ]; - } - - $this->responseLocations = $responseLocations + $defaultResponseLocations; - $this->description = $description; - } - - public function getEvents() - { - return ['process' => ['onProcess']]; - } - - public function onProcess(ProcessEvent $event) - { - // Only add a result object if no exception was encountered. - if ($event->getException()) { - return; - } - - $command = $event->getCommand(); - - // Do not overwrite a previous result - if ($event->getResult()) { - return; - } - - $operation = $this->description->getOperation($command->getName()); - - // Add a default Model as the result if no matching schema was found. - if (!($modelName = $operation->getResponseModel())) { - $event->setResult([]); - return; - } - - $model = $operation->getServiceDescription()->getModel($modelName); - if (!$model) { - throw new \RuntimeException("Unknown model: {$modelName}"); - } - - $event->setResult($this->visit($model, $event)); - } - - protected function visit(Parameter $model, ProcessEvent $event) - { - $result = []; - $context = ['client' => $event->getClient(), 'visitors' => []]; - $command = $event->getCommand(); - $response = $event->getResponse(); - - if ($model->getType() == 'object') { - $this->visitOuterObject($model, $result, $command, $response, $context); - } elseif ($model->getType() == 'array') { - $this->visitOuterArray($model, $result, $command, $response, $context); - } else { - throw new \InvalidArgumentException('Invalid response model: ' . $model->getType()); - } - - // Call the after() method of each found visitor - foreach ($context['visitors'] as $visitor) { - $visitor->after($command, $response, $model, $result, $context); - } - - return $result; - } - - private function triggerBeforeVisitor( - $location, - Parameter $model, - array &$result, - CommandInterface $command, - ResponseInterface $response, - array &$context - ) { - if (!isset($this->responseLocations[$location])) { - throw new \RuntimeException("Unknown location: $location"); - } - - $context['visitors'][$location] = $this->responseLocations[$location]; - - $this->responseLocations[$location]->before( - $command, - $response, - $model, - $result, - $context - ); - } - - private function visitOuterObject( - Parameter $model, - array &$result, - CommandInterface $command, - ResponseInterface $response, - array &$context - ) { - $parentLocation = $model->getLocation(); - - // If top-level additionalProperties is a schema, then visit it - $additional = $model->getAdditionalProperties(); - if ($additional instanceof Parameter) { - // Use the model location if none set on additionalProperties. - $location = $additional->getLocation() ?: $parentLocation; - $this->triggerBeforeVisitor( - $location, $model, $result, $command, $response, $context - ); - } - - // Use 'location' from all individual defined properties, but fall back - // to the model location if no per-property location is set. Collect - // the properties that need to be visited into an array. - $visitProperties = []; - foreach ($model->getProperties() as $schema) { - $location = $schema->getLocation() ?: $parentLocation; - if ($location) { - $visitProperties[] = [$location, $schema]; - // Trigger the before method on each unique visitor location - if (!isset($context['visitors'][$location])) { - $this->triggerBeforeVisitor( - $location, $model, $result, $command, $response, $context - ); - } - } - } - - // Actually visit each response element - foreach ($visitProperties as $prop) { - $this->responseLocations[$prop[0]]->visit( - $command, $response, $prop[1], $result, $context - ); - } - } - - private function visitOuterArray( - Parameter $model, - array &$result, - CommandInterface $command, - ResponseInterface $response, - array &$context - ) { - // Use 'location' defined on the top of the model - if (!($location = $model->getLocation())) { - return; - } - - if (!isset($foundVisitors[$location])) { - $this->triggerBeforeVisitor( - $location, $model, $result, $command, $response, $context - ); - } - - // Visit each item in the response - $this->responseLocations[$location]->visit( - $command, $response, $model, $result, $context - ); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/Subscriber/ValidateInput.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/Subscriber/ValidateInput.php deleted file mode 100644 index 8cac3c5605..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle-services/src/Subscriber/ValidateInput.php +++ /dev/null @@ -1,76 +0,0 @@ -description = $description; - $this->validator = $schemaValidator ?: new SchemaValidator(); - } - - public function getEvents() - { - return ['init' => ['onInit']]; - } - - public function onInit(InitEvent $event) - { - $command = $event->getCommand(); - $errors = []; - $operation = $this->description->getOperation($command->getName()); - - foreach ($operation->getParams() as $name => $schema) { - $value = $command[$name]; - if (!$this->validator->validate($schema, $value)) { - $errors = array_merge($errors, $this->validator->getErrors()); - } elseif ($value !== $command[$name]) { - // Update the config value if it changed and no validation - // errors were encountered - $command[$name] = $value; - } - } - - if ($params = $operation->getAdditionalParameters()) { - foreach ($command->toArray() as $name => $value) { - // It's only additional if it isn't defined in the schema - if (!$operation->hasParam($name)) { - // Always set the name so that error messages are useful - $params->setName($name); - if (!$this->validator->validate($params, $value)) { - $errors = array_merge( - $errors, - $this->validator->getErrors() - ); - } elseif ($value !== $command[$name]) { - $command[$name] = $value; - } - } - } - } - - if ($errors) { - throw new CommandException( - 'Validation errors: ' . implode("\n", $errors), - $event->getTransaction() - ); - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/.travis.yml b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/.travis.yml deleted file mode 100644 index 55ec89253b..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/.travis.yml +++ /dev/null @@ -1,41 +0,0 @@ -language: php - -php: - - 5.4 - - 5.5 - - 5.6 - - 7.0 - - hhvm - -before_script: - - curl --version - - pear config-set php_ini ~/.phpenv/versions/`php -r 'echo phpversion();'`/etc/php.ini || echo 'Error modifying PEAR' - - pecl install uri_template || echo 'Error installing uri_template' - - composer self-update - - composer install --no-interaction --prefer-source --dev - - ~/.nvm/nvm.sh install v0.6.14 - - ~/.nvm/nvm.sh run v0.6.14 - -script: make test - -matrix: - allow_failures: - - php: hhvm - - php: 7.0 - fast_finish: true - -before_deploy: - - make package - -deploy: - provider: releases - api_key: - secure: UpypqlYgsU68QT/x40YzhHXvzWjFwCNo9d+G8KAdm7U9+blFfcWhV1aMdzugvPMl6woXgvJj7qHq5tAL4v6oswCORhpSBfLgOQVFaica5LiHsvWlAedOhxGmnJqMTwuepjBCxXhs3+I8Kof1n4oUL9gKytXjOVCX/f7XU1HiinU= - file: - - build/artifacts/guzzle.phar - - build/artifacts/guzzle.zip - on: - repo: guzzle/guzzle - tags: true - all_branches: true - php: 5.4 diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/CHANGELOG.md b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/CHANGELOG.md deleted file mode 100644 index 7bd0a720a4..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/CHANGELOG.md +++ /dev/null @@ -1,1060 +0,0 @@ -# CHANGELOG - -## 5.3.1 - 2016-07-18 - -* Address HTTP_PROXY security vulnerability, CVE-2016-5385: - https://httpoxy.org/ -* Event name fix: https://github.com/guzzle/guzzle/commit/fcae91ff31de41e312fe113ec3acbcda31b2622e -* Response header case sensitivity fix: https://github.com/guzzle/guzzle/commit/043eeadf20ee40ddc6712faee4d3957a91f2b041 - -## 5.3.0 - 2015-05-19 - -* Mock now supports `save_to` -* Marked `AbstractRequestEvent::getTransaction()` as public. -* Fixed a bug in which multiple headers using different casing would overwrite - previous headers in the associative array. -* Added `Utils::getDefaultHandler()` -* Marked `GuzzleHttp\Client::getDefaultUserAgent` as deprecated. -* URL scheme is now always lowercased. - -## 5.2.0 - 2015-01-27 - -* Added `AppliesHeadersInterface` to make applying headers to a request based - on the body more generic and not specific to `PostBodyInterface`. -* Reduced the number of stack frames needed to send requests. -* Nested futures are now resolved in the client rather than the RequestFsm -* Finishing state transitions is now handled in the RequestFsm rather than the - RingBridge. -* Added a guard in the Pool class to not use recursion for request retries. - -## 5.1.0 - 2014-12-19 - -* Pool class no longer uses recursion when a request is intercepted. -* The size of a Pool can now be dynamically adjusted using a callback. - See https://github.com/guzzle/guzzle/pull/943. -* Setting a request option to `null` when creating a request with a client will - ensure that the option is not set. This allows you to overwrite default - request options on a per-request basis. - See https://github.com/guzzle/guzzle/pull/937. -* Added the ability to limit which protocols are allowed for redirects by - specifying a `protocols` array in the `allow_redirects` request option. -* Nested futures due to retries are now resolved when waiting for synchronous - responses. See https://github.com/guzzle/guzzle/pull/947. -* `"0"` is now an allowed URI path. See - https://github.com/guzzle/guzzle/pull/935. -* `Query` no longer typehints on the `$query` argument in the constructor, - allowing for strings and arrays. -* Exceptions thrown in the `end` event are now correctly wrapped with Guzzle - specific exceptions if necessary. - -## 5.0.3 - 2014-11-03 - -This change updates query strings so that they are treated as un-encoded values -by default where the value represents an un-encoded value to send over the -wire. A Query object then encodes the value before sending over the wire. This -means that even value query string values (e.g., ":") are url encoded. This -makes the Query class match PHP's http_build_query function. However, if you -want to send requests over the wire using valid query string characters that do -not need to be encoded, then you can provide a string to Url::setQuery() and -pass true as the second argument to specify that the query string is a raw -string that should not be parsed or encoded (unless a call to getQuery() is -subsequently made, forcing the query-string to be converted into a Query -object). - -## 5.0.2 - 2014-10-30 - -* Added a trailing `\r\n` to multipart/form-data payloads. See - https://github.com/guzzle/guzzle/pull/871 -* Added a `GuzzleHttp\Pool::send()` convenience method to match the docs. -* Status codes are now returned as integers. See - https://github.com/guzzle/guzzle/issues/881 -* No longer overwriting an existing `application/x-www-form-urlencoded` header - when sending POST requests, allowing for customized headers. See - https://github.com/guzzle/guzzle/issues/877 -* Improved path URL serialization. - - * No longer double percent-encoding characters in the path or query string if - they are already encoded. - * Now properly encoding the supplied path to a URL object, instead of only - encoding ' ' and '?'. - * Note: This has been changed in 5.0.3 to now encode query string values by - default unless the `rawString` argument is provided when setting the query - string on a URL: Now allowing many more characters to be present in the - query string without being percent encoded. See http://tools.ietf.org/html/rfc3986#appendix-A - -## 5.0.1 - 2014-10-16 - -Bugfix release. - -* Fixed an issue where connection errors still returned response object in - error and end events event though the response is unusable. This has been - corrected so that a response is not returned in the `getResponse` method of - these events if the response did not complete. https://github.com/guzzle/guzzle/issues/867 -* Fixed an issue where transfer statistics were not being populated in the - RingBridge. https://github.com/guzzle/guzzle/issues/866 - -## 5.0.0 - 2014-10-12 - -Adding support for non-blocking responses and some minor API cleanup. - -### New Features - -* Added support for non-blocking responses based on `guzzlehttp/guzzle-ring`. -* Added a public API for creating a default HTTP adapter. -* Updated the redirect plugin to be non-blocking so that redirects are sent - concurrently. Other plugins like this can now be updated to be non-blocking. -* Added a "progress" event so that you can get upload and download progress - events. -* Added `GuzzleHttp\Pool` which implements FutureInterface and transfers - requests concurrently using a capped pool size as efficiently as possible. -* Added `hasListeners()` to EmitterInterface. -* Removed `GuzzleHttp\ClientInterface::sendAll` and marked - `GuzzleHttp\Client::sendAll` as deprecated (it's still there, just not the - recommended way). - -### Breaking changes - -The breaking changes in this release are relatively minor. The biggest thing to -look out for is that request and response objects no longer implement fluent -interfaces. - -* Removed the fluent interfaces (i.e., `return $this`) from requests, - responses, `GuzzleHttp\Collection`, `GuzzleHttp\Url`, - `GuzzleHttp\Query`, `GuzzleHttp\Post\PostBody`, and - `GuzzleHttp\Cookie\SetCookie`. This blog post provides a good outline of - why I did this: http://ocramius.github.io/blog/fluent-interfaces-are-evil/. - This also makes the Guzzle message interfaces compatible with the current - PSR-7 message proposal. -* Removed "functions.php", so that Guzzle is truly PSR-4 compliant. Except - for the HTTP request functions from function.php, these functions are now - implemented in `GuzzleHttp\Utils` using camelCase. `GuzzleHttp\json_decode` - moved to `GuzzleHttp\Utils::jsonDecode`. `GuzzleHttp\get_path` moved to - `GuzzleHttp\Utils::getPath`. `GuzzleHttp\set_path` moved to - `GuzzleHttp\Utils::setPath`. `GuzzleHttp\batch` should now be - `GuzzleHttp\Pool::batch`, which returns an `objectStorage`. Using functions.php - caused problems for many users: they aren't PSR-4 compliant, require an - explicit include, and needed an if-guard to ensure that the functions are not - declared multiple times. -* Rewrote adapter layer. - * Removing all classes from `GuzzleHttp\Adapter`, these are now - implemented as callables that are stored in `GuzzleHttp\Ring\Client`. - * Removed the concept of "parallel adapters". Sending requests serially or - concurrently is now handled using a single adapter. - * Moved `GuzzleHttp\Adapter\Transaction` to `GuzzleHttp\Transaction`. The - Transaction object now exposes the request, response, and client as public - properties. The getters and setters have been removed. -* Removed the "headers" event. This event was only useful for changing the - body a response once the headers of the response were known. You can implement - a similar behavior in a number of ways. One example might be to use a - FnStream that has access to the transaction being sent. For example, when the - first byte is written, you could check if the response headers match your - expectations, and if so, change the actual stream body that is being - written to. -* Removed the `asArray` parameter from - `GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header - value as an array, then use the newly added `getHeaderAsArray()` method of - `MessageInterface`. This change makes the Guzzle interfaces compatible with - the PSR-7 interfaces. -* `GuzzleHttp\Message\MessageFactory` no longer allows subclasses to add - custom request options using double-dispatch (this was an implementation - detail). Instead, you should now provide an associative array to the - constructor which is a mapping of the request option name mapping to a - function that applies the option value to a request. -* Removed the concept of "throwImmediately" from exceptions and error events. - This control mechanism was used to stop a transfer of concurrent requests - from completing. This can now be handled by throwing the exception or by - cancelling a pool of requests or each outstanding future request individually. -* Updated to "GuzzleHttp\Streams" 3.0. - * `GuzzleHttp\Stream\StreamInterface::getContents()` no longer accepts a - `maxLen` parameter. This update makes the Guzzle streams project - compatible with the current PSR-7 proposal. - * `GuzzleHttp\Stream\Stream::__construct`, - `GuzzleHttp\Stream\Stream::factory`, and - `GuzzleHttp\Stream\Utils::create` no longer accept a size in the second - argument. They now accept an associative array of options, including the - "size" key and "metadata" key which can be used to provide custom metadata. - -## 4.2.2 - 2014-09-08 - -* Fixed a memory leak in the CurlAdapter when reusing cURL handles. -* No longer using `request_fulluri` in stream adapter proxies. -* Relative redirects are now based on the last response, not the first response. - -## 4.2.1 - 2014-08-19 - -* Ensuring that the StreamAdapter does not always add a Content-Type header -* Adding automated github releases with a phar and zip - -## 4.2.0 - 2014-08-17 - -* Now merging in default options using a case-insensitive comparison. - Closes https://github.com/guzzle/guzzle/issues/767 -* Added the ability to automatically decode `Content-Encoding` response bodies - using the `decode_content` request option. This is set to `true` by default - to decode the response body if it comes over the wire with a - `Content-Encoding`. Set this value to `false` to disable decoding the - response content, and pass a string to provide a request `Accept-Encoding` - header and turn on automatic response decoding. This feature now allows you - to pass an `Accept-Encoding` header in the headers of a request but still - disable automatic response decoding. - Closes https://github.com/guzzle/guzzle/issues/764 -* Added the ability to throw an exception immediately when transferring - requests in parallel. Closes https://github.com/guzzle/guzzle/issues/760 -* Updating guzzlehttp/streams dependency to ~2.1 -* No longer utilizing the now deprecated namespaced methods from the stream - package. - -## 4.1.8 - 2014-08-14 - -* Fixed an issue in the CurlFactory that caused setting the `stream=false` - request option to throw an exception. - See: https://github.com/guzzle/guzzle/issues/769 -* TransactionIterator now calls rewind on the inner iterator. - See: https://github.com/guzzle/guzzle/pull/765 -* You can now set the `Content-Type` header to `multipart/form-data` - when creating POST requests to force multipart bodies. - See https://github.com/guzzle/guzzle/issues/768 - -## 4.1.7 - 2014-08-07 - -* Fixed an error in the HistoryPlugin that caused the same request and response - to be logged multiple times when an HTTP protocol error occurs. -* Ensuring that cURL does not add a default Content-Type when no Content-Type - has been supplied by the user. This prevents the adapter layer from modifying - the request that is sent over the wire after any listeners may have already - put the request in a desired state (e.g., signed the request). -* Throwing an exception when you attempt to send requests that have the - "stream" set to true in parallel using the MultiAdapter. -* Only calling curl_multi_select when there are active cURL handles. This was - previously changed and caused performance problems on some systems due to PHP - always selecting until the maximum select timeout. -* Fixed a bug where multipart/form-data POST fields were not correctly - aggregated (e.g., values with "&"). - -## 4.1.6 - 2014-08-03 - -* Added helper methods to make it easier to represent messages as strings, - including getting the start line and getting headers as a string. - -## 4.1.5 - 2014-08-02 - -* Automatically retrying cURL "Connection died, retrying a fresh connect" - errors when possible. -* cURL implementation cleanup -* Allowing multiple event subscriber listeners to be registered per event by - passing an array of arrays of listener configuration. - -## 4.1.4 - 2014-07-22 - -* Fixed a bug that caused multi-part POST requests with more than one field to - serialize incorrectly. -* Paths can now be set to "0" -* `ResponseInterface::xml` now accepts a `libxml_options` option and added a - missing default argument that was required when parsing XML response bodies. -* A `save_to` stream is now created lazily, which means that files are not - created on disk unless a request succeeds. - -## 4.1.3 - 2014-07-15 - -* Various fixes to multipart/form-data POST uploads -* Wrapping function.php in an if-statement to ensure Guzzle can be used - globally and in a Composer install -* Fixed an issue with generating and merging in events to an event array -* POST headers are only applied before sending a request to allow you to change - the query aggregator used before uploading -* Added much more robust query string parsing -* Fixed various parsing and normalization issues with URLs -* Fixing an issue where multi-valued headers were not being utilized correctly - in the StreamAdapter - -## 4.1.2 - 2014-06-18 - -* Added support for sending payloads with GET requests - -## 4.1.1 - 2014-06-08 - -* Fixed an issue related to using custom message factory options in subclasses -* Fixed an issue with nested form fields in a multi-part POST -* Fixed an issue with using the `json` request option for POST requests -* Added `ToArrayInterface` to `GuzzleHttp\Cookie\CookieJar` - -## 4.1.0 - 2014-05-27 - -* Added a `json` request option to easily serialize JSON payloads. -* Added a `GuzzleHttp\json_decode()` wrapper to safely parse JSON. -* Added `setPort()` and `getPort()` to `GuzzleHttp\Message\RequestInterface`. -* Added the ability to provide an emitter to a client in the client constructor. -* Added the ability to persist a cookie session using $_SESSION. -* Added a trait that can be used to add event listeners to an iterator. -* Removed request method constants from RequestInterface. -* Fixed warning when invalid request start-lines are received. -* Updated MessageFactory to work with custom request option methods. -* Updated cacert bundle to latest build. - -4.0.2 (2014-04-16) ------------------- - -* Proxy requests using the StreamAdapter now properly use request_fulluri (#632) -* Added the ability to set scalars as POST fields (#628) - -## 4.0.1 - 2014-04-04 - -* The HTTP status code of a response is now set as the exception code of - RequestException objects. -* 303 redirects will now correctly switch from POST to GET requests. -* The default parallel adapter of a client now correctly uses the MultiAdapter. -* HasDataTrait now initializes the internal data array as an empty array so - that the toArray() method always returns an array. - -## 4.0.0 - 2014-03-29 - -* For more information on the 4.0 transition, see: - http://mtdowling.com/blog/2014/03/15/guzzle-4-rc/ -* For information on changes and upgrading, see: - https://github.com/guzzle/guzzle/blob/master/UPGRADING.md#3x-to-40 -* Added `GuzzleHttp\batch()` as a convenience function for sending requests in - parallel without needing to write asynchronous code. -* Restructured how events are added to `GuzzleHttp\ClientInterface::sendAll()`. - You can now pass a callable or an array of associative arrays where each - associative array contains the "fn", "priority", and "once" keys. - -## 4.0.0.rc-2 - 2014-03-25 - -* Removed `getConfig()` and `setConfig()` from clients to avoid confusion - around whether things like base_url, message_factory, etc. should be able to - be retrieved or modified. -* Added `getDefaultOption()` and `setDefaultOption()` to ClientInterface -* functions.php functions were renamed using snake_case to match PHP idioms -* Added support for `HTTP_PROXY`, `HTTPS_PROXY`, and - `GUZZLE_CURL_SELECT_TIMEOUT` environment variables -* Added the ability to specify custom `sendAll()` event priorities -* Added the ability to specify custom stream context options to the stream - adapter. -* Added a functions.php function for `get_path()` and `set_path()` -* CurlAdapter and MultiAdapter now use a callable to generate curl resources -* MockAdapter now properly reads a body and emits a `headers` event -* Updated Url class to check if a scheme and host are set before adding ":" - and "//". This allows empty Url (e.g., "") to be serialized as "". -* Parsing invalid XML no longer emits warnings -* Curl classes now properly throw AdapterExceptions -* Various performance optimizations -* Streams are created with the faster `Stream\create()` function -* Marked deprecation_proxy() as internal -* Test server is now a collection of static methods on a class - -## 4.0.0-rc.1 - 2014-03-15 - -* See https://github.com/guzzle/guzzle/blob/master/UPGRADING.md#3x-to-40 - -## 3.8.1 - 2014-01-28 - -* Bug: Always using GET requests when redirecting from a 303 response -* Bug: CURLOPT_SSL_VERIFYHOST is now correctly set to false when setting `$certificateAuthority` to false in - `Guzzle\Http\ClientInterface::setSslVerification()` -* Bug: RedirectPlugin now uses strict RFC 3986 compliance when combining a base URL with a relative URL -* Bug: The body of a request can now be set to `"0"` -* Sending PHP stream requests no longer forces `HTTP/1.0` -* Adding more information to ExceptionCollection exceptions so that users have more context, including a stack trace of - each sub-exception -* Updated the `$ref` attribute in service descriptions to merge over any existing parameters of a schema (rather than - clobbering everything). -* Merging URLs will now use the query string object from the relative URL (thus allowing custom query aggregators) -* Query strings are now parsed in a way that they do no convert empty keys with no value to have a dangling `=`. - For example `foo&bar=baz` is now correctly parsed and recognized as `foo&bar=baz` rather than `foo=&bar=baz`. -* Now properly escaping the regular expression delimiter when matching Cookie domains. -* Network access is now disabled when loading XML documents - -## 3.8.0 - 2013-12-05 - -* Added the ability to define a POST name for a file -* JSON response parsing now properly walks additionalProperties -* cURL error code 18 is now retried automatically in the BackoffPlugin -* Fixed a cURL error when URLs contain fragments -* Fixed an issue in the BackoffPlugin retry event where it was trying to access all exceptions as if they were - CurlExceptions -* CURLOPT_PROGRESS function fix for PHP 5.5 (69fcc1e) -* Added the ability for Guzzle to work with older versions of cURL that do not support `CURLOPT_TIMEOUT_MS` -* Fixed a bug that was encountered when parsing empty header parameters -* UriTemplate now has a `setRegex()` method to match the docs -* The `debug` request parameter now checks if it is truthy rather than if it exists -* Setting the `debug` request parameter to true shows verbose cURL output instead of using the LogPlugin -* Added the ability to combine URLs using strict RFC 3986 compliance -* Command objects can now return the validation errors encountered by the command -* Various fixes to cache revalidation (#437 and 29797e5) -* Various fixes to the AsyncPlugin -* Cleaned up build scripts - -## 3.7.4 - 2013-10-02 - -* Bug fix: 0 is now an allowed value in a description parameter that has a default value (#430) -* Bug fix: SchemaFormatter now returns an integer when formatting to a Unix timestamp - (see https://github.com/aws/aws-sdk-php/issues/147) -* Bug fix: Cleaned up and fixed URL dot segment removal to properly resolve internal dots -* Minimum PHP version is now properly specified as 5.3.3 (up from 5.3.2) (#420) -* Updated the bundled cacert.pem (#419) -* OauthPlugin now supports adding authentication to headers or query string (#425) - -## 3.7.3 - 2013-09-08 - -* Added the ability to get the exception associated with a request/command when using `MultiTransferException` and - `CommandTransferException`. -* Setting `additionalParameters` of a response to false is now honored when parsing responses with a service description -* Schemas are only injected into response models when explicitly configured. -* No longer guessing Content-Type based on the path of a request. Content-Type is now only guessed based on the path of - an EntityBody. -* Bug fix: ChunkedIterator can now properly chunk a \Traversable as well as an \Iterator. -* Bug fix: FilterIterator now relies on `\Iterator` instead of `\Traversable`. -* Bug fix: Gracefully handling malformed responses in RequestMediator::writeResponseBody() -* Bug fix: Replaced call to canCache with canCacheRequest in the CallbackCanCacheStrategy of the CachePlugin -* Bug fix: Visiting XML attributes first before visiting XML children when serializing requests -* Bug fix: Properly parsing headers that contain commas contained in quotes -* Bug fix: mimetype guessing based on a filename is now case-insensitive - -## 3.7.2 - 2013-08-02 - -* Bug fix: Properly URL encoding paths when using the PHP-only version of the UriTemplate expander - See https://github.com/guzzle/guzzle/issues/371 -* Bug fix: Cookie domains are now matched correctly according to RFC 6265 - See https://github.com/guzzle/guzzle/issues/377 -* Bug fix: GET parameters are now used when calculating an OAuth signature -* Bug fix: Fixed an issue with cache revalidation where the If-None-Match header was being double quoted -* `Guzzle\Common\AbstractHasDispatcher::dispatch()` now returns the event that was dispatched -* `Guzzle\Http\QueryString::factory()` now guesses the most appropriate query aggregator to used based on the input. - See https://github.com/guzzle/guzzle/issues/379 -* Added a way to add custom domain objects to service description parsing using the `operation.parse_class` event. See - https://github.com/guzzle/guzzle/pull/380 -* cURL multi cleanup and optimizations - -## 3.7.1 - 2013-07-05 - -* Bug fix: Setting default options on a client now works -* Bug fix: Setting options on HEAD requests now works. See #352 -* Bug fix: Moving stream factory before send event to before building the stream. See #353 -* Bug fix: Cookies no longer match on IP addresses per RFC 6265 -* Bug fix: Correctly parsing header parameters that are in `<>` and quotes -* Added `cert` and `ssl_key` as request options -* `Host` header can now diverge from the host part of a URL if the header is set manually -* `Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor` was rewritten to change from using SimpleXML to XMLWriter -* OAuth parameters are only added via the plugin if they aren't already set -* Exceptions are now thrown when a URL cannot be parsed -* Returning `false` if `Guzzle\Http\EntityBody::getContentMd5()` fails -* Not setting a `Content-MD5` on a command if calculating the Content-MD5 fails via the CommandContentMd5Plugin - -## 3.7.0 - 2013-06-10 - -* See UPGRADING.md for more information on how to upgrade. -* Requests now support the ability to specify an array of $options when creating a request to more easily modify a - request. You can pass a 'request.options' configuration setting to a client to apply default request options to - every request created by a client (e.g. default query string variables, headers, curl options, etc.). -* Added a static facade class that allows you to use Guzzle with static methods and mount the class to `\Guzzle`. - See `Guzzle\Http\StaticClient::mount`. -* Added `command.request_options` to `Guzzle\Service\Command\AbstractCommand` to pass request options to requests - created by a command (e.g. custom headers, query string variables, timeout settings, etc.). -* Stream size in `Guzzle\Stream\PhpStreamRequestFactory` will now be set if Content-Length is returned in the - headers of a response -* Added `Guzzle\Common\Collection::setPath($path, $value)` to set a value into an array using a nested key - (e.g. `$collection->setPath('foo/baz/bar', 'test'); echo $collection['foo']['bar']['bar'];`) -* ServiceBuilders now support storing and retrieving arbitrary data -* CachePlugin can now purge all resources for a given URI -* CachePlugin can automatically purge matching cached items when a non-idempotent request is sent to a resource -* CachePlugin now uses the Vary header to determine if a resource is a cache hit -* `Guzzle\Http\Message\Response` now implements `\Serializable` -* Added `Guzzle\Cache\CacheAdapterFactory::fromCache()` to more easily create cache adapters -* `Guzzle\Service\ClientInterface::execute()` now accepts an array, single command, or Traversable -* Fixed a bug in `Guzzle\Http\Message\Header\Link::addLink()` -* Better handling of calculating the size of a stream in `Guzzle\Stream\Stream` using fstat() and caching the size -* `Guzzle\Common\Exception\ExceptionCollection` now creates a more readable exception message -* Fixing BC break: Added back the MonologLogAdapter implementation rather than extending from PsrLog so that older - Symfony users can still use the old version of Monolog. -* Fixing BC break: Added the implementation back in for `Guzzle\Http\Message\AbstractMessage::getTokenizedHeader()`. - Now triggering an E_USER_DEPRECATED warning when used. Use `$message->getHeader()->parseParams()`. -* Several performance improvements to `Guzzle\Common\Collection` -* Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: - createRequest, head, delete, put, patch, post, options, prepareRequest -* Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` -* Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` -* Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to - `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a - resource, string, or EntityBody into the $options parameter to specify the download location of the response. -* Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a - default `array()` -* Added `Guzzle\Stream\StreamInterface::isRepeatable` -* Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use - $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or - $client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))`. -* Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use $client->getConfig()->getPath('request.options/headers')`. -* Removed `Guzzle\Http\ClientInterface::expandTemplate()` -* Removed `Guzzle\Http\ClientInterface::setRequestFactory()` -* Removed `Guzzle\Http\ClientInterface::getCurlMulti()` -* Removed `Guzzle\Http\Message\RequestInterface::canCache` -* Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect` -* Removed `Guzzle\Http\Message\RequestInterface::isRedirect` -* Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. -* You can now enable E_USER_DEPRECATED warnings to see if you are using a deprecated method by setting - `Guzzle\Common\Version::$emitWarnings` to true. -* Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use - `$request->getResponseBody()->isRepeatable()` instead. -* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use - `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. -* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use - `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. -* Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. -* Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. -* Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated -* Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. - These will work through Guzzle 4.0 -* Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use [request.options][params]. -* Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. -* Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use $client->getConfig()->getPath('request.options/headers')`. -* Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. -* Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. -* Marked `Guzzle\Common\Collection::inject()` as deprecated. -* Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');` -* CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a - CacheStorageInterface. These two objects and interface will be removed in a future version. -* Always setting X-cache headers on cached responses -* Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin -* `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface - $request, Response $response);` -* `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` -* `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` -* Added `CacheStorageInterface::purge($url)` -* `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin - $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, - CanCacheStrategyInterface $canCache = null)` -* Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` - -## 3.6.0 - 2013-05-29 - -* ServiceDescription now implements ToArrayInterface -* Added command.hidden_params to blacklist certain headers from being treated as additionalParameters -* Guzzle can now correctly parse incomplete URLs -* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. -* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution -* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). -* Specific header implementations can be created for complex headers. When a message creates a header, it uses a - HeaderFactory which can map specific headers to specific header classes. There is now a Link header and - CacheControl header implementation. -* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate -* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() -* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in - Guzzle\Http\Curl\RequestMediator -* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. -* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface -* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() -* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() -* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). -* All response header helper functions return a string rather than mixing Header objects and strings inconsistently -* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle - directly via interfaces -* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist - but are a no-op until removed. -* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a - `Guzzle\Service\Command\ArrayCommandInterface`. -* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response - on a request while the request is still being transferred -* The ability to case-insensitively search for header values -* Guzzle\Http\Message\Header::hasExactHeader -* Guzzle\Http\Message\Header::raw. Use getAll() -* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object - instead. -* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess -* Added the ability to cast Model objects to a string to view debug information. - -## 3.5.0 - 2013-05-13 - -* Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times -* Bug: Better cleanup of one-time events across the board (when an event is meant to fire once, it will now remove - itself from the EventDispatcher) -* Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values -* Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too -* Bug: Fixed an undefined index error when parsing nested JSON responses with a sentAs parameter that reference a - non-existent key -* Bug: All __call() method arguments are now required (helps with mocking frameworks) -* Deprecating Response::getRequest() and now using a shallow clone of a request object to remove a circular reference - to help with refcount based garbage collection of resources created by sending a request -* Deprecating ZF1 cache and log adapters. These will be removed in the next major version. -* Deprecating `Response::getPreviousResponse()` (method signature still exists, but it'sdeprecated). Use the - HistoryPlugin for a history. -* Added a `responseBody` alias for the `response_body` location -* Refactored internals to no longer rely on Response::getRequest() -* HistoryPlugin can now be cast to a string -* HistoryPlugin now logs transactions rather than requests and responses to more accurately keep track of the requests - and responses that are sent over the wire -* Added `getEffectiveUrl()` and `getRedirectCount()` to Response objects - -## 3.4.3 - 2013-04-30 - -* Bug fix: Fixing bug introduced in 3.4.2 where redirect responses are duplicated on the final redirected response -* Added a check to re-extract the temp cacert bundle from the phar before sending each request - -## 3.4.2 - 2013-04-29 - -* Bug fix: Stream objects now work correctly with "a" and "a+" modes -* Bug fix: Removing `Transfer-Encoding: chunked` header when a Content-Length is present -* Bug fix: AsyncPlugin no longer forces HEAD requests -* Bug fix: DateTime timezones are now properly handled when using the service description schema formatter -* Bug fix: CachePlugin now properly handles stale-if-error directives when a request to the origin server fails -* Setting a response on a request will write to the custom request body from the response body if one is specified -* LogPlugin now writes to php://output when STDERR is undefined -* Added the ability to set multiple POST files for the same key in a single call -* application/x-www-form-urlencoded POSTs now use the utf-8 charset by default -* Added the ability to queue CurlExceptions to the MockPlugin -* Cleaned up how manual responses are queued on requests (removed "queued_response" and now using request.before_send) -* Configuration loading now allows remote files - -## 3.4.1 - 2013-04-16 - -* Large refactoring to how CurlMulti handles work. There is now a proxy that sits in front of a pool of CurlMulti - handles. This greatly simplifies the implementation, fixes a couple bugs, and provides a small performance boost. -* Exceptions are now properly grouped when sending requests in parallel -* Redirects are now properly aggregated when a multi transaction fails -* Redirects now set the response on the original object even in the event of a failure -* Bug fix: Model names are now properly set even when using $refs -* Added support for PHP 5.5's CurlFile to prevent warnings with the deprecated @ syntax -* Added support for oauth_callback in OAuth signatures -* Added support for oauth_verifier in OAuth signatures -* Added support to attempt to retrieve a command first literally, then ucfirst, the with inflection - -## 3.4.0 - 2013-04-11 - -* Bug fix: URLs are now resolved correctly based on http://tools.ietf.org/html/rfc3986#section-5.2. #289 -* Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289 -* Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263 -* Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264. -* Bug fix: Added `number` type to service descriptions. -* Bug fix: empty parameters are removed from an OAuth signature -* Bug fix: Revalidating a cache entry prefers the Last-Modified over the Date header -* Bug fix: Fixed "array to string" error when validating a union of types in a service description -* Bug fix: Removed code that attempted to determine the size of a stream when data is written to the stream -* Bug fix: Not including an `oauth_token` if the value is null in the OauthPlugin. -* Bug fix: Now correctly aggregating successful requests and failed requests in CurlMulti when a redirect occurs. -* The new default CURLOPT_TIMEOUT setting has been increased to 150 seconds so that Guzzle works on poor connections. -* Added a feature to EntityEnclosingRequest::setBody() that will automatically set the Content-Type of the request if - the Content-Type can be determined based on the entity body or the path of the request. -* Added the ability to overwrite configuration settings in a client when grabbing a throwaway client from a builder. -* Added support for a PSR-3 LogAdapter. -* Added a `command.after_prepare` event -* Added `oauth_callback` parameter to the OauthPlugin -* Added the ability to create a custom stream class when using a stream factory -* Added a CachingEntityBody decorator -* Added support for `additionalParameters` in service descriptions to define how custom parameters are serialized. -* The bundled SSL certificate is now provided in the phar file and extracted when running Guzzle from a phar. -* You can now send any EntityEnclosingRequest with POST fields or POST files and cURL will handle creating bodies -* POST requests using a custom entity body are now treated exactly like PUT requests but with a custom cURL method. This - means that the redirect behavior of POST requests with custom bodies will not be the same as POST requests that use - POST fields or files (the latter is only used when emulating a form POST in the browser). -* Lots of cleanup to CurlHandle::factory and RequestFactory::createRequest - -## 3.3.1 - 2013-03-10 - -* Added the ability to create PHP streaming responses from HTTP requests -* Bug fix: Running any filters when parsing response headers with service descriptions -* Bug fix: OauthPlugin fixes to allow for multi-dimensional array signing, and sorting parameters before signing -* Bug fix: Removed the adding of default empty arrays and false Booleans to responses in order to be consistent across - response location visitors. -* Bug fix: Removed the possibility of creating configuration files with circular dependencies -* RequestFactory::create() now uses the key of a POST file when setting the POST file name -* Added xmlAllowEmpty to serialize an XML body even if no XML specific parameters are set - -## 3.3.0 - 2013-03-03 - -* A large number of performance optimizations have been made -* Bug fix: Added 'wb' as a valid write mode for streams -* Bug fix: `Guzzle\Http\Message\Response::json()` now allows scalar values to be returned -* Bug fix: Fixed bug in `Guzzle\Http\Message\Response` where wrapping quotes were stripped from `getEtag()` -* BC: Removed `Guzzle\Http\Utils` class -* BC: Setting a service description on a client will no longer modify the client's command factories. -* BC: Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using - the 'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' -* BC: `Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to - lowercase -* Operation parameter objects are now lazy loaded internally -* Added ErrorResponsePlugin that can throw errors for responses defined in service description operations' errorResponses -* Added support for instantiating responseType=class responseClass classes. Classes must implement - `Guzzle\Service\Command\ResponseClassInterface` -* Added support for additionalProperties for top-level parameters in responseType=model responseClasses. These - additional properties also support locations and can be used to parse JSON responses where the outermost part of the - JSON is an array -* Added support for nested renaming of JSON models (rename sentAs to name) -* CachePlugin - * Added support for stale-if-error so that the CachePlugin can now serve stale content from the cache on error - * Debug headers can now added to cached response in the CachePlugin - -## 3.2.0 - 2013-02-14 - -* CurlMulti is no longer reused globally. A new multi object is created per-client. This helps to isolate clients. -* URLs with no path no longer contain a "/" by default -* Guzzle\Http\QueryString does no longer manages the leading "?". This is now handled in Guzzle\Http\Url. -* BadResponseException no longer includes the full request and response message -* Adding setData() to Guzzle\Service\Description\ServiceDescriptionInterface -* Adding getResponseBody() to Guzzle\Http\Message\RequestInterface -* Various updates to classes to use ServiceDescriptionInterface type hints rather than ServiceDescription -* Header values can now be normalized into distinct values when multiple headers are combined with a comma separated list -* xmlEncoding can now be customized for the XML declaration of a XML service description operation -* Guzzle\Http\QueryString now uses Guzzle\Http\QueryAggregator\QueryAggregatorInterface objects to add custom value - aggregation and no longer uses callbacks -* The URL encoding implementation of Guzzle\Http\QueryString can now be customized -* Bug fix: Filters were not always invoked for array service description parameters -* Bug fix: Redirects now use a target response body rather than a temporary response body -* Bug fix: The default exponential backoff BackoffPlugin was not giving when the request threshold was exceeded -* Bug fix: Guzzle now takes the first found value when grabbing Cache-Control directives - -## 3.1.2 - 2013-01-27 - -* Refactored how operation responses are parsed. Visitors now include a before() method responsible for parsing the - response body. For example, the XmlVisitor now parses the XML response into an array in the before() method. -* Fixed an issue where cURL would not automatically decompress responses when the Accept-Encoding header was sent -* CURLOPT_SSL_VERIFYHOST is never set to 1 because it is deprecated (see 5e0ff2ef20f839e19d1eeb298f90ba3598784444) -* Fixed a bug where redirect responses were not chained correctly using getPreviousResponse() -* Setting default headers on a client after setting the user-agent will not erase the user-agent setting - -## 3.1.1 - 2013-01-20 - -* Adding wildcard support to Guzzle\Common\Collection::getPath() -* Adding alias support to ServiceBuilder configs -* Adding Guzzle\Service\Resource\CompositeResourceIteratorFactory and cleaning up factory interface - -## 3.1.0 - 2013-01-12 - -* BC: CurlException now extends from RequestException rather than BadResponseException -* BC: Renamed Guzzle\Plugin\Cache\CanCacheStrategyInterface::canCache() to canCacheRequest() and added CanCacheResponse() -* Added getData to ServiceDescriptionInterface -* Added context array to RequestInterface::setState() -* Bug: Removing hard dependency on the BackoffPlugin from Guzzle\Http -* Bug: Adding required content-type when JSON request visitor adds JSON to a command -* Bug: Fixing the serialization of a service description with custom data -* Made it easier to deal with exceptions thrown when transferring commands or requests in parallel by providing - an array of successful and failed responses -* Moved getPath from Guzzle\Service\Resource\Model to Guzzle\Common\Collection -* Added Guzzle\Http\IoEmittingEntityBody -* Moved command filtration from validators to location visitors -* Added `extends` attributes to service description parameters -* Added getModels to ServiceDescriptionInterface - -## 3.0.7 - 2012-12-19 - -* Fixing phar detection when forcing a cacert to system if null or true -* Allowing filename to be passed to `Guzzle\Http\Message\Request::setResponseBody()` -* Cleaning up `Guzzle\Common\Collection::inject` method -* Adding a response_body location to service descriptions - -## 3.0.6 - 2012-12-09 - -* CurlMulti performance improvements -* Adding setErrorResponses() to Operation -* composer.json tweaks - -## 3.0.5 - 2012-11-18 - -* Bug: Fixing an infinite recursion bug caused from revalidating with the CachePlugin -* Bug: Response body can now be a string containing "0" -* Bug: Using Guzzle inside of a phar uses system by default but now allows for a custom cacert -* Bug: QueryString::fromString now properly parses query string parameters that contain equal signs -* Added support for XML attributes in service description responses -* DefaultRequestSerializer now supports array URI parameter values for URI template expansion -* Added better mimetype guessing to requests and post files - -## 3.0.4 - 2012-11-11 - -* Bug: Fixed a bug when adding multiple cookies to a request to use the correct glue value -* Bug: Cookies can now be added that have a name, domain, or value set to "0" -* Bug: Using the system cacert bundle when using the Phar -* Added json and xml methods to Response to make it easier to parse JSON and XML response data into data structures -* Enhanced cookie jar de-duplication -* Added the ability to enable strict cookie jars that throw exceptions when invalid cookies are added -* Added setStream to StreamInterface to actually make it possible to implement custom rewind behavior for entity bodies -* Added the ability to create any sort of hash for a stream rather than just an MD5 hash - -## 3.0.3 - 2012-11-04 - -* Implementing redirects in PHP rather than cURL -* Added PECL URI template extension and using as default parser if available -* Bug: Fixed Content-Length parsing of Response factory -* Adding rewind() method to entity bodies and streams. Allows for custom rewinding of non-repeatable streams. -* Adding ToArrayInterface throughout library -* Fixing OauthPlugin to create unique nonce values per request - -## 3.0.2 - 2012-10-25 - -* Magic methods are enabled by default on clients -* Magic methods return the result of a command -* Service clients no longer require a base_url option in the factory -* Bug: Fixed an issue with URI templates where null template variables were being expanded - -## 3.0.1 - 2012-10-22 - -* Models can now be used like regular collection objects by calling filter, map, etc. -* Models no longer require a Parameter structure or initial data in the constructor -* Added a custom AppendIterator to get around a PHP bug with the `\AppendIterator` - -## 3.0.0 - 2012-10-15 - -* Rewrote service description format to be based on Swagger - * Now based on JSON schema - * Added nested input structures and nested response models - * Support for JSON and XML input and output models - * Renamed `commands` to `operations` - * Removed dot class notation - * Removed custom types -* Broke the project into smaller top-level namespaces to be more component friendly -* Removed support for XML configs and descriptions. Use arrays or JSON files. -* Removed the Validation component and Inspector -* Moved all cookie code to Guzzle\Plugin\Cookie -* Magic methods on a Guzzle\Service\Client now return the command un-executed. -* Calling getResult() or getResponse() on a command will lazily execute the command if needed. -* Now shipping with cURL's CA certs and using it by default -* Added previousResponse() method to response objects -* No longer sending Accept and Accept-Encoding headers on every request -* Only sending an Expect header by default when a payload is greater than 1MB -* Added/moved client options: - * curl.blacklist to curl.option.blacklist - * Added ssl.certificate_authority -* Added a Guzzle\Iterator component -* Moved plugins from Guzzle\Http\Plugin to Guzzle\Plugin -* Added a more robust backoff retry strategy (replaced the ExponentialBackoffPlugin) -* Added a more robust caching plugin -* Added setBody to response objects -* Updating LogPlugin to use a more flexible MessageFormatter -* Added a completely revamped build process -* Cleaning up Collection class and removing default values from the get method -* Fixed ZF2 cache adapters - -## 2.8.8 - 2012-10-15 - -* Bug: Fixed a cookie issue that caused dot prefixed domains to not match where popular browsers did - -## 2.8.7 - 2012-09-30 - -* Bug: Fixed config file aliases for JSON includes -* Bug: Fixed cookie bug on a request object by using CookieParser to parse cookies on requests -* Bug: Removing the path to a file when sending a Content-Disposition header on a POST upload -* Bug: Hardening request and response parsing to account for missing parts -* Bug: Fixed PEAR packaging -* Bug: Fixed Request::getInfo -* Bug: Fixed cases where CURLM_CALL_MULTI_PERFORM return codes were causing curl transactions to fail -* Adding the ability for the namespace Iterator factory to look in multiple directories -* Added more getters/setters/removers from service descriptions -* Added the ability to remove POST fields from OAuth signatures -* OAuth plugin now supports 2-legged OAuth - -## 2.8.6 - 2012-09-05 - -* Added the ability to modify and build service descriptions -* Added the use of visitors to apply parameters to locations in service descriptions using the dynamic command -* Added a `json` parameter location -* Now allowing dot notation for classes in the CacheAdapterFactory -* Using the union of two arrays rather than an array_merge when extending service builder services and service params -* Ensuring that a service is a string before doing strpos() checks on it when substituting services for references - in service builder config files. -* Services defined in two different config files that include one another will by default replace the previously - defined service, but you can now create services that extend themselves and merge their settings over the previous -* The JsonLoader now supports aliasing filenames with different filenames. This allows you to alias something like - '_default' with a default JSON configuration file. - -## 2.8.5 - 2012-08-29 - -* Bug: Suppressed empty arrays from URI templates -* Bug: Added the missing $options argument from ServiceDescription::factory to enable caching -* Added support for HTTP responses that do not contain a reason phrase in the start-line -* AbstractCommand commands are now invokable -* Added a way to get the data used when signing an Oauth request before a request is sent - -## 2.8.4 - 2012-08-15 - -* Bug: Custom delay time calculations are no longer ignored in the ExponentialBackoffPlugin -* Added the ability to transfer entity bodies as a string rather than streamed. This gets around curl error 65. Set `body_as_string` in a request's curl options to enable. -* Added a StreamInterface, EntityBodyInterface, and added ftell() to Guzzle\Common\Stream -* Added an AbstractEntityBodyDecorator and a ReadLimitEntityBody decorator to transfer only a subset of a decorated stream -* Stream and EntityBody objects will now return the file position to the previous position after a read required operation (e.g. getContentMd5()) -* Added additional response status codes -* Removed SSL information from the default User-Agent header -* DELETE requests can now send an entity body -* Added an EventDispatcher to the ExponentialBackoffPlugin and added an ExponentialBackoffLogger to log backoff retries -* Added the ability of the MockPlugin to consume mocked request bodies -* LogPlugin now exposes request and response objects in the extras array - -## 2.8.3 - 2012-07-30 - -* Bug: Fixed a case where empty POST requests were sent as GET requests -* Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body -* Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new -* Added multiple inheritance to service description commands -* Added an ApiCommandInterface and added `getParamNames()` and `hasParam()` -* Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything -* Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles - -## 2.8.2 - 2012-07-24 - -* Bug: Query string values set to 0 are no longer dropped from the query string -* Bug: A Collection object is no longer created each time a call is made to `Guzzle\Service\Command\AbstractCommand::getRequestHeaders()` -* Bug: `+` is now treated as an encoded space when parsing query strings -* QueryString and Collection performance improvements -* Allowing dot notation for class paths in filters attribute of a service descriptions - -## 2.8.1 - 2012-07-16 - -* Loosening Event Dispatcher dependency -* POST redirects can now be customized using CURLOPT_POSTREDIR - -## 2.8.0 - 2012-07-15 - -* BC: Guzzle\Http\Query - * Query strings with empty variables will always show an equal sign unless the variable is set to QueryString::BLANK (e.g. ?acl= vs ?acl) - * Changed isEncodingValues() and isEncodingFields() to isUrlEncoding() - * Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool) - * Changed the aggregation functions of QueryString to be static methods - * Can now use fromString() with querystrings that have a leading ? -* cURL configuration values can be specified in service descriptions using `curl.` prefixed parameters -* Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body -* Cookies are no longer URL decoded by default -* Bug: URI template variables set to null are no longer expanded - -## 2.7.2 - 2012-07-02 - -* BC: Moving things to get ready for subtree splits. Moving Inflection into Common. Moving Guzzle\Http\Parser to Guzzle\Parser. -* BC: Removing Guzzle\Common\Batch\Batch::count() and replacing it with isEmpty() -* CachePlugin now allows for a custom request parameter function to check if a request can be cached -* Bug fix: CachePlugin now only caches GET and HEAD requests by default -* Bug fix: Using header glue when transferring headers over the wire -* Allowing deeply nested arrays for composite variables in URI templates -* Batch divisors can now return iterators or arrays - -## 2.7.1 - 2012-06-26 - -* Minor patch to update version number in UA string -* Updating build process - -## 2.7.0 - 2012-06-25 - -* BC: Inflection classes moved to Guzzle\Inflection. No longer static methods. Can now inject custom inflectors into classes. -* BC: Removed magic setX methods from commands -* BC: Magic methods mapped to service description commands are now inflected in the command factory rather than the client __call() method -* Verbose cURL options are no longer enabled by default. Set curl.debug to true on a client to enable. -* Bug: Now allowing colons in a response start-line (e.g. HTTP/1.1 503 Service Unavailable: Back-end server is at capacity) -* Guzzle\Service\Resource\ResourceIteratorApplyBatched now internally uses the Guzzle\Common\Batch namespace -* Added Guzzle\Service\Plugin namespace and a PluginCollectionPlugin -* Added the ability to set POST fields and files in a service description -* Guzzle\Http\EntityBody::factory() now accepts objects with a __toString() method -* Adding a command.before_prepare event to clients -* Added BatchClosureTransfer and BatchClosureDivisor -* BatchTransferException now includes references to the batch divisor and transfer strategies -* Fixed some tests so that they pass more reliably -* Added Guzzle\Common\Log\ArrayLogAdapter - -## 2.6.6 - 2012-06-10 - -* BC: Removing Guzzle\Http\Plugin\BatchQueuePlugin -* BC: Removing Guzzle\Service\Command\CommandSet -* Adding generic batching system (replaces the batch queue plugin and command set) -* Updating ZF cache and log adapters and now using ZF's composer repository -* Bug: Setting the name of each ApiParam when creating through an ApiCommand -* Adding result_type, result_doc, deprecated, and doc_url to service descriptions -* Bug: Changed the default cookie header casing back to 'Cookie' - -## 2.6.5 - 2012-06-03 - -* BC: Renaming Guzzle\Http\Message\RequestInterface::getResourceUri() to getResource() -* BC: Removing unused AUTH_BASIC and AUTH_DIGEST constants from -* BC: Guzzle\Http\Cookie is now used to manage Set-Cookie data, not Cookie data -* BC: Renaming methods in the CookieJarInterface -* Moving almost all cookie logic out of the CookiePlugin and into the Cookie or CookieJar implementations -* Making the default glue for HTTP headers ';' instead of ',' -* Adding a removeValue to Guzzle\Http\Message\Header -* Adding getCookies() to request interface. -* Making it easier to add event subscribers to HasDispatcherInterface classes. Can now directly call addSubscriber() - -## 2.6.4 - 2012-05-30 - -* BC: Cleaning up how POST files are stored in EntityEnclosingRequest objects. Adding PostFile class. -* BC: Moving ApiCommand specific functionality from the Inspector and on to the ApiCommand -* Bug: Fixing magic method command calls on clients -* Bug: Email constraint only validates strings -* Bug: Aggregate POST fields when POST files are present in curl handle -* Bug: Fixing default User-Agent header -* Bug: Only appending or prepending parameters in commands if they are specified -* Bug: Not requiring response reason phrases or status codes to match a predefined list of codes -* Allowing the use of dot notation for class namespaces when using instance_of constraint -* Added any_match validation constraint -* Added an AsyncPlugin -* Passing request object to the calculateWait method of the ExponentialBackoffPlugin -* Allowing the result of a command object to be changed -* Parsing location and type sub values when instantiating a service description rather than over and over at runtime - -## 2.6.3 - 2012-05-23 - -* [BC] Guzzle\Common\FromConfigInterface no longer requires any config options. -* [BC] Refactoring how POST files are stored on an EntityEnclosingRequest. They are now separate from POST fields. -* You can now use an array of data when creating PUT request bodies in the request factory. -* Removing the requirement that HTTPS requests needed a Cache-Control: public directive to be cacheable. -* [Http] Adding support for Content-Type in multipart POST uploads per upload -* [Http] Added support for uploading multiple files using the same name (foo[0], foo[1]) -* Adding more POST data operations for easier manipulation of POST data. -* You can now set empty POST fields. -* The body of a request is only shown on EntityEnclosingRequest objects that do not use POST files. -* Split the Guzzle\Service\Inspector::validateConfig method into two methods. One to initialize when a command is created, and one to validate. -* CS updates - -## 2.6.2 - 2012-05-19 - -* [Http] Better handling of nested scope requests in CurlMulti. Requests are now always prepares in the send() method rather than the addRequest() method. - -## 2.6.1 - 2012-05-19 - -* [BC] Removing 'path' support in service descriptions. Use 'uri'. -* [BC] Guzzle\Service\Inspector::parseDocBlock is now protected. Adding getApiParamsForClass() with cache. -* [BC] Removing Guzzle\Common\NullObject. Use https://github.com/mtdowling/NullObject if you need it. -* [BC] Removing Guzzle\Common\XmlElement. -* All commands, both dynamic and concrete, have ApiCommand objects. -* Adding a fix for CurlMulti so that if all of the connections encounter some sort of curl error, then the loop exits. -* Adding checks to EntityEnclosingRequest so that empty POST files and fields are ignored. -* Making the method signature of Guzzle\Service\Builder\ServiceBuilder::factory more flexible. - -## 2.6.0 - 2012-05-15 - -* [BC] Moving Guzzle\Service\Builder to Guzzle\Service\Builder\ServiceBuilder -* [BC] Executing a Command returns the result of the command rather than the command -* [BC] Moving all HTTP parsing logic to Guzzle\Http\Parsers. Allows for faster C implementations if needed. -* [BC] Changing the Guzzle\Http\Message\Response::setProtocol() method to accept a protocol and version in separate args. -* [BC] Moving ResourceIterator* to Guzzle\Service\Resource -* [BC] Completely refactored ResourceIterators to iterate over a cloned command object -* [BC] Moved Guzzle\Http\UriTemplate to Guzzle\Http\Parser\UriTemplate\UriTemplate -* [BC] Guzzle\Guzzle is now deprecated -* Moving Guzzle\Common\Guzzle::inject to Guzzle\Common\Collection::inject -* Adding Guzzle\Version class to give version information about Guzzle -* Adding Guzzle\Http\Utils class to provide getDefaultUserAgent() and getHttpDate() -* Adding Guzzle\Curl\CurlVersion to manage caching curl_version() data -* ServiceDescription and ServiceBuilder are now cacheable using similar configs -* Changing the format of XML and JSON service builder configs. Backwards compatible. -* Cleaned up Cookie parsing -* Trimming the default Guzzle User-Agent header -* Adding a setOnComplete() method to Commands that is called when a command completes -* Keeping track of requests that were mocked in the MockPlugin -* Fixed a caching bug in the CacheAdapterFactory -* Inspector objects can be injected into a Command object -* Refactoring a lot of code and tests to be case insensitive when dealing with headers -* Adding Guzzle\Http\Message\HeaderComparison for easy comparison of HTTP headers using a DSL -* Adding the ability to set global option overrides to service builder configs -* Adding the ability to include other service builder config files from within XML and JSON files -* Moving the parseQuery method out of Url and on to QueryString::fromString() as a static factory method. - -## 2.5.0 - 2012-05-08 - -* Major performance improvements -* [BC] Simplifying Guzzle\Common\Collection. Please check to see if you are using features that are now deprecated. -* [BC] Using a custom validation system that allows a flyweight implementation for much faster validation. No longer using Symfony2 Validation component. -* [BC] No longer supporting "{{ }}" for injecting into command or UriTemplates. Use "{}" -* Added the ability to passed parameters to all requests created by a client -* Added callback functionality to the ExponentialBackoffPlugin -* Using microtime in ExponentialBackoffPlugin to allow more granular backoff strategies. -* Rewinding request stream bodies when retrying requests -* Exception is thrown when JSON response body cannot be decoded -* Added configurable magic method calls to clients and commands. This is off by default. -* Fixed a defect that added a hash to every parsed URL part -* Fixed duplicate none generation for OauthPlugin. -* Emitting an event each time a client is generated by a ServiceBuilder -* Using an ApiParams object instead of a Collection for parameters of an ApiCommand -* cache.* request parameters should be renamed to params.cache.* -* Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc.). See CurlHandle. -* Added the ability to disable type validation of service descriptions -* ServiceDescriptions and ServiceBuilders are now Serializable diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/LICENSE b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/LICENSE deleted file mode 100644 index 9af9fba682..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2011-2015 Michael Dowling, https://github.com/mtdowling - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/README.md b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/README.md deleted file mode 100644 index d41e7e7588..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/README.md +++ /dev/null @@ -1,70 +0,0 @@ -Guzzle, PHP HTTP client and webservice framework -================================================ - -[![Build Status](https://secure.travis-ci.org/guzzle/guzzle.svg?branch=master)](http://travis-ci.org/guzzle/guzzle) - -Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and -trivial to integrate with web services. - -- Manages things like persistent connections, represents query strings as - collections, simplifies sending streaming POST requests with fields and - files, and abstracts away the underlying HTTP transport layer. -- Can send both synchronous and asynchronous requests using the same interface - without requiring a dependency on a specific event loop. -- Pluggable HTTP adapters allows Guzzle to integrate with any method you choose - for sending HTTP requests over the wire (e.g., cURL, sockets, PHP's stream - wrapper, non-blocking event loops like ReactPHP. -- Guzzle makes it so that you no longer need to fool around with cURL options, - stream contexts, or sockets. - -```php -$client = new GuzzleHttp\Client(); -$response = $client->get('http://guzzlephp.org'); -$res = $client->get('https://api.github.com/user', ['auth' => ['user', 'pass']]); -echo $res->getStatusCode(); -// "200" -echo $res->getHeader('content-type'); -// 'application/json; charset=utf8' -echo $res->getBody(); -// {"type":"User"...' -var_export($res->json()); -// Outputs the JSON decoded data - -// Send an asynchronous request. -$req = $client->createRequest('GET', 'http://httpbin.org', ['future' => true]); -$client->send($req)->then(function ($response) { - echo 'I completed! ' . $response; -}); -``` - -Get more information and answers with the -[Documentation](http://guzzlephp.org/), -[Forums](https://groups.google.com/forum/?hl=en#!forum/guzzle), -and [Gitter](https://gitter.im/guzzle/guzzle). - -### Installing via Composer - -The recommended way to install Guzzle is through -[Composer](http://getcomposer.org). - -```bash -# Install Composer -curl -sS https://getcomposer.org/installer | php -``` - -Next, run the Composer command to install the latest stable version of Guzzle: - -```bash -composer.phar require guzzlehttp/guzzle -``` - -After installing, you need to require Composer's autoloader: - -```php -require 'vendor/autoload.php'; -``` - -### Documentation - -More information can be found in the online documentation at -http://guzzlephp.org/. diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/UPGRADING.md b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/UPGRADING.md deleted file mode 100644 index 2b3877fa87..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/UPGRADING.md +++ /dev/null @@ -1,1050 +0,0 @@ -Guzzle Upgrade Guide -==================== - -4.x to 5.0 ----------- - -## Rewritten Adapter Layer - -Guzzle now uses [RingPHP](http://ringphp.readthedocs.org/en/latest) to send -HTTP requests. The `adapter` option in a `GuzzleHttp\Client` constructor -is still supported, but it has now been renamed to `handler`. Instead of -passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP -`callable` that follows the RingPHP specification. - -## Removed Fluent Interfaces - -[Fluent interfaces were removed](http://ocramius.github.io/blog/fluent-interfaces-are-evil) -from the following classes: - -- `GuzzleHttp\Collection` -- `GuzzleHttp\Url` -- `GuzzleHttp\Query` -- `GuzzleHttp\Post\PostBody` -- `GuzzleHttp\Cookie\SetCookie` - -## Removed functions.php - -Removed "functions.php", so that Guzzle is truly PSR-4 compliant. The following -functions can be used as replacements. - -- `GuzzleHttp\json_decode` -> `GuzzleHttp\Utils::jsonDecode` -- `GuzzleHttp\get_path` -> `GuzzleHttp\Utils::getPath` -- `GuzzleHttp\Utils::setPath` -> `GuzzleHttp\set_path` -- `GuzzleHttp\Pool::batch` -> `GuzzleHttp\batch`. This function is, however, - deprecated in favor of using `GuzzleHttp\Pool::batch()`. - -The "procedural" global client has been removed with no replacement (e.g., -`GuzzleHttp\get()`, `GuzzleHttp\post()`, etc.). Use a `GuzzleHttp\Client` -object as a replacement. - -## `throwImmediately` has been removed - -The concept of "throwImmediately" has been removed from exceptions and error -events. This control mechanism was used to stop a transfer of concurrent -requests from completing. This can now be handled by throwing the exception or -by cancelling a pool of requests or each outstanding future request -individually. - -## headers event has been removed - -Removed the "headers" event. This event was only useful for changing the -body a response once the headers of the response were known. You can implement -a similar behavior in a number of ways. One example might be to use a -FnStream that has access to the transaction being sent. For example, when the -first byte is written, you could check if the response headers match your -expectations, and if so, change the actual stream body that is being -written to. - -## Updates to HTTP Messages - -Removed the `asArray` parameter from -`GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header -value as an array, then use the newly added `getHeaderAsArray()` method of -`MessageInterface`. This change makes the Guzzle interfaces compatible with -the PSR-7 interfaces. - -3.x to 4.0 ----------- - -## Overarching changes: - -- Now requires PHP 5.4 or greater. -- No longer requires cURL to send requests. -- Guzzle no longer wraps every exception it throws. Only exceptions that are - recoverable are now wrapped by Guzzle. -- Various namespaces have been removed or renamed. -- No longer requiring the Symfony EventDispatcher. A custom event dispatcher - based on the Symfony EventDispatcher is - now utilized in `GuzzleHttp\Event\EmitterInterface` (resulting in significant - speed and functionality improvements). - -Changes per Guzzle 3.x namespace are described below. - -## Batch - -The `Guzzle\Batch` namespace has been removed. This is best left to -third-parties to implement on top of Guzzle's core HTTP library. - -## Cache - -The `Guzzle\Cache` namespace has been removed. (Todo: No suitable replacement -has been implemented yet, but hoping to utilize a PSR cache interface). - -## Common - -- Removed all of the wrapped exceptions. It's better to use the standard PHP - library for unrecoverable exceptions. -- `FromConfigInterface` has been removed. -- `Guzzle\Common\Version` has been removed. The VERSION constant can be found - at `GuzzleHttp\ClientInterface::VERSION`. - -### Collection - -- `getAll` has been removed. Use `toArray` to convert a collection to an array. -- `inject` has been removed. -- `keySearch` has been removed. -- `getPath` no longer supports wildcard expressions. Use something better like - JMESPath for this. -- `setPath` now supports appending to an existing array via the `[]` notation. - -### Events - -Guzzle no longer requires Symfony's EventDispatcher component. Guzzle now uses -`GuzzleHttp\Event\Emitter`. - -- `Symfony\Component\EventDispatcher\EventDispatcherInterface` is replaced by - `GuzzleHttp\Event\EmitterInterface`. -- `Symfony\Component\EventDispatcher\EventDispatcher` is replaced by - `GuzzleHttp\Event\Emitter`. -- `Symfony\Component\EventDispatcher\Event` is replaced by - `GuzzleHttp\Event\Event`, and Guzzle now has an EventInterface in - `GuzzleHttp\Event\EventInterface`. -- `AbstractHasDispatcher` has moved to a trait, `HasEmitterTrait`, and - `HasDispatcherInterface` has moved to `HasEmitterInterface`. Retrieving the - event emitter of a request, client, etc. now uses the `getEmitter` method - rather than the `getDispatcher` method. - -#### Emitter - -- Use the `once()` method to add a listener that automatically removes itself - the first time it is invoked. -- Use the `listeners()` method to retrieve a list of event listeners rather than - the `getListeners()` method. -- Use `emit()` instead of `dispatch()` to emit an event from an emitter. -- Use `attach()` instead of `addSubscriber()` and `detach()` instead of - `removeSubscriber()`. - -```php -$mock = new Mock(); -// 3.x -$request->getEventDispatcher()->addSubscriber($mock); -$request->getEventDispatcher()->removeSubscriber($mock); -// 4.x -$request->getEmitter()->attach($mock); -$request->getEmitter()->detach($mock); -``` - -Use the `on()` method to add a listener rather than the `addListener()` method. - -```php -// 3.x -$request->getEventDispatcher()->addListener('foo', function (Event $event) { /* ... */ } ); -// 4.x -$request->getEmitter()->on('foo', function (Event $event, $name) { /* ... */ } ); -``` - -## Http - -### General changes - -- The cacert.pem certificate has been moved to `src/cacert.pem`. -- Added the concept of adapters that are used to transfer requests over the - wire. -- Simplified the event system. -- Sending requests in parallel is still possible, but batching is no longer a - concept of the HTTP layer. Instead, you must use the `complete` and `error` - events to asynchronously manage parallel request transfers. -- `Guzzle\Http\Url` has moved to `GuzzleHttp\Url`. -- `Guzzle\Http\QueryString` has moved to `GuzzleHttp\Query`. -- QueryAggregators have been rewritten so that they are simply callable - functions. -- `GuzzleHttp\StaticClient` has been removed. Use the functions provided in - `functions.php` for an easy to use static client instance. -- Exceptions in `GuzzleHttp\Exception` have been updated to all extend from - `GuzzleHttp\Exception\TransferException`. - -### Client - -Calling methods like `get()`, `post()`, `head()`, etc. no longer create and -return a request, but rather creates a request, sends the request, and returns -the response. - -```php -// 3.0 -$request = $client->get('/'); -$response = $request->send(); - -// 4.0 -$response = $client->get('/'); - -// or, to mirror the previous behavior -$request = $client->createRequest('GET', '/'); -$response = $client->send($request); -``` - -`GuzzleHttp\ClientInterface` has changed. - -- The `send` method no longer accepts more than one request. Use `sendAll` to - send multiple requests in parallel. -- `setUserAgent()` has been removed. Use a default request option instead. You - could, for example, do something like: - `$client->setConfig('defaults/headers/User-Agent', 'Foo/Bar ' . $client::getDefaultUserAgent())`. -- `setSslVerification()` has been removed. Use default request options instead, - like `$client->setConfig('defaults/verify', true)`. - -`GuzzleHttp\Client` has changed. - -- The constructor now accepts only an associative array. You can include a - `base_url` string or array to use a URI template as the base URL of a client. - You can also specify a `defaults` key that is an associative array of default - request options. You can pass an `adapter` to use a custom adapter, - `batch_adapter` to use a custom adapter for sending requests in parallel, or - a `message_factory` to change the factory used to create HTTP requests and - responses. -- The client no longer emits a `client.create_request` event. -- Creating requests with a client no longer automatically utilize a URI - template. You must pass an array into a creational method (e.g., - `createRequest`, `get`, `put`, etc.) in order to expand a URI template. - -### Messages - -Messages no longer have references to their counterparts (i.e., a request no -longer has a reference to it's response, and a response no loger has a -reference to its request). This association is now managed through a -`GuzzleHttp\Adapter\TransactionInterface` object. You can get references to -these transaction objects using request events that are emitted over the -lifecycle of a request. - -#### Requests with a body - -- `GuzzleHttp\Message\EntityEnclosingRequest` and - `GuzzleHttp\Message\EntityEnclosingRequestInterface` have been removed. The - separation between requests that contain a body and requests that do not - contain a body has been removed, and now `GuzzleHttp\Message\RequestInterface` - handles both use cases. -- Any method that previously accepts a `GuzzleHttp\Response` object now accept a - `GuzzleHttp\Message\ResponseInterface`. -- `GuzzleHttp\Message\RequestFactoryInterface` has been renamed to - `GuzzleHttp\Message\MessageFactoryInterface`. This interface is used to create - both requests and responses and is implemented in - `GuzzleHttp\Message\MessageFactory`. -- POST field and file methods have been removed from the request object. You - must now use the methods made available to `GuzzleHttp\Post\PostBodyInterface` - to control the format of a POST body. Requests that are created using a - standard `GuzzleHttp\Message\MessageFactoryInterface` will automatically use - a `GuzzleHttp\Post\PostBody` body if the body was passed as an array or if - the method is POST and no body is provided. - -```php -$request = $client->createRequest('POST', '/'); -$request->getBody()->setField('foo', 'bar'); -$request->getBody()->addFile(new PostFile('file_key', fopen('/path/to/content', 'r'))); -``` - -#### Headers - -- `GuzzleHttp\Message\Header` has been removed. Header values are now simply - represented by an array of values or as a string. Header values are returned - as a string by default when retrieving a header value from a message. You can - pass an optional argument of `true` to retrieve a header value as an array - of strings instead of a single concatenated string. -- `GuzzleHttp\PostFile` and `GuzzleHttp\PostFileInterface` have been moved to - `GuzzleHttp\Post`. This interface has been simplified and now allows the - addition of arbitrary headers. -- Custom headers like `GuzzleHttp\Message\Header\Link` have been removed. Most - of the custom headers are now handled separately in specific - subscribers/plugins, and `GuzzleHttp\Message\HeaderValues::parseParams()` has - been updated to properly handle headers that contain parameters (like the - `Link` header). - -#### Responses - -- `GuzzleHttp\Message\Response::getInfo()` and - `GuzzleHttp\Message\Response::setInfo()` have been removed. Use the event - system to retrieve this type of information. -- `GuzzleHttp\Message\Response::getRawHeaders()` has been removed. -- `GuzzleHttp\Message\Response::getMessage()` has been removed. -- `GuzzleHttp\Message\Response::calculateAge()` and other cache specific - methods have moved to the CacheSubscriber. -- Header specific helper functions like `getContentMd5()` have been removed. - Just use `getHeader('Content-MD5')` instead. -- `GuzzleHttp\Message\Response::setRequest()` and - `GuzzleHttp\Message\Response::getRequest()` have been removed. Use the event - system to work with request and response objects as a transaction. -- `GuzzleHttp\Message\Response::getRedirectCount()` has been removed. Use the - Redirect subscriber instead. -- `GuzzleHttp\Message\Response::isSuccessful()` and other related methods have - been removed. Use `getStatusCode()` instead. - -#### Streaming responses - -Streaming requests can now be created by a client directly, returning a -`GuzzleHttp\Message\ResponseInterface` object that contains a body stream -referencing an open PHP HTTP stream. - -```php -// 3.0 -use Guzzle\Stream\PhpStreamRequestFactory; -$request = $client->get('/'); -$factory = new PhpStreamRequestFactory(); -$stream = $factory->fromRequest($request); -$data = $stream->read(1024); - -// 4.0 -$response = $client->get('/', ['stream' => true]); -// Read some data off of the stream in the response body -$data = $response->getBody()->read(1024); -``` - -#### Redirects - -The `configureRedirects()` method has been removed in favor of a -`allow_redirects` request option. - -```php -// Standard redirects with a default of a max of 5 redirects -$request = $client->createRequest('GET', '/', ['allow_redirects' => true]); - -// Strict redirects with a custom number of redirects -$request = $client->createRequest('GET', '/', [ - 'allow_redirects' => ['max' => 5, 'strict' => true] -]); -``` - -#### EntityBody - -EntityBody interfaces and classes have been removed or moved to -`GuzzleHttp\Stream`. All classes and interfaces that once required -`GuzzleHttp\EntityBodyInterface` now require -`GuzzleHttp\Stream\StreamInterface`. Creating a new body for a request no -longer uses `GuzzleHttp\EntityBody::factory` but now uses -`GuzzleHttp\Stream\Stream::factory` or even better: -`GuzzleHttp\Stream\create()`. - -- `Guzzle\Http\EntityBodyInterface` is now `GuzzleHttp\Stream\StreamInterface` -- `Guzzle\Http\EntityBody` is now `GuzzleHttp\Stream\Stream` -- `Guzzle\Http\CachingEntityBody` is now `GuzzleHttp\Stream\CachingStream` -- `Guzzle\Http\ReadLimitEntityBody` is now `GuzzleHttp\Stream\LimitStream` -- `Guzzle\Http\IoEmittyinEntityBody` has been removed. - -#### Request lifecycle events - -Requests previously submitted a large number of requests. The number of events -emitted over the lifecycle of a request has been significantly reduced to make -it easier to understand how to extend the behavior of a request. All events -emitted during the lifecycle of a request now emit a custom -`GuzzleHttp\Event\EventInterface` object that contains context providing -methods and a way in which to modify the transaction at that specific point in -time (e.g., intercept the request and set a response on the transaction). - -- `request.before_send` has been renamed to `before` and now emits a - `GuzzleHttp\Event\BeforeEvent` -- `request.complete` has been renamed to `complete` and now emits a - `GuzzleHttp\Event\CompleteEvent`. -- `request.sent` has been removed. Use `complete`. -- `request.success` has been removed. Use `complete`. -- `error` is now an event that emits a `GuzzleHttp\Event\ErrorEvent`. -- `request.exception` has been removed. Use `error`. -- `request.receive.status_line` has been removed. -- `curl.callback.progress` has been removed. Use a custom `StreamInterface` to - maintain a status update. -- `curl.callback.write` has been removed. Use a custom `StreamInterface` to - intercept writes. -- `curl.callback.read` has been removed. Use a custom `StreamInterface` to - intercept reads. - -`headers` is a new event that is emitted after the response headers of a -request have been received before the body of the response is downloaded. This -event emits a `GuzzleHttp\Event\HeadersEvent`. - -You can intercept a request and inject a response using the `intercept()` event -of a `GuzzleHttp\Event\BeforeEvent`, `GuzzleHttp\Event\CompleteEvent`, and -`GuzzleHttp\Event\ErrorEvent` event. - -See: http://docs.guzzlephp.org/en/latest/events.html - -## Inflection - -The `Guzzle\Inflection` namespace has been removed. This is not a core concern -of Guzzle. - -## Iterator - -The `Guzzle\Iterator` namespace has been removed. - -- `Guzzle\Iterator\AppendIterator`, `Guzzle\Iterator\ChunkedIterator`, and - `Guzzle\Iterator\MethodProxyIterator` are nice, but not a core requirement of - Guzzle itself. -- `Guzzle\Iterator\FilterIterator` is no longer needed because an equivalent - class is shipped with PHP 5.4. -- `Guzzle\Iterator\MapIterator` is not really needed when using PHP 5.5 because - it's easier to just wrap an iterator in a generator that maps values. - -For a replacement of these iterators, see https://github.com/nikic/iter - -## Log - -The LogPlugin has moved to https://github.com/guzzle/log-subscriber. The -`Guzzle\Log` namespace has been removed. Guzzle now relies on -`Psr\Log\LoggerInterface` for all logging. The MessageFormatter class has been -moved to `GuzzleHttp\Subscriber\Log\Formatter`. - -## Parser - -The `Guzzle\Parser` namespace has been removed. This was previously used to -make it possible to plug in custom parsers for cookies, messages, URI -templates, and URLs; however, this level of complexity is not needed in Guzzle -so it has been removed. - -- Cookie: Cookie parsing logic has been moved to - `GuzzleHttp\Cookie\SetCookie::fromString`. -- Message: Message parsing logic for both requests and responses has been moved - to `GuzzleHttp\Message\MessageFactory::fromMessage`. Message parsing is only - used in debugging or deserializing messages, so it doesn't make sense for - Guzzle as a library to add this level of complexity to parsing messages. -- UriTemplate: URI template parsing has been moved to - `GuzzleHttp\UriTemplate`. The Guzzle library will automatically use the PECL - URI template library if it is installed. -- Url: URL parsing is now performed in `GuzzleHttp\Url::fromString` (previously - it was `Guzzle\Http\Url::factory()`). If custom URL parsing is necessary, - then developers are free to subclass `GuzzleHttp\Url`. - -## Plugin - -The `Guzzle\Plugin` namespace has been renamed to `GuzzleHttp\Subscriber`. -Several plugins are shipping with the core Guzzle library under this namespace. - -- `GuzzleHttp\Subscriber\Cookie`: Replaces the old CookiePlugin. Cookie jar - code has moved to `GuzzleHttp\Cookie`. -- `GuzzleHttp\Subscriber\History`: Replaces the old HistoryPlugin. -- `GuzzleHttp\Subscriber\HttpError`: Throws errors when a bad HTTP response is - received. -- `GuzzleHttp\Subscriber\Mock`: Replaces the old MockPlugin. -- `GuzzleHttp\Subscriber\Prepare`: Prepares the body of a request just before - sending. This subscriber is attached to all requests by default. -- `GuzzleHttp\Subscriber\Redirect`: Replaces the RedirectPlugin. - -The following plugins have been removed (third-parties are free to re-implement -these if needed): - -- `GuzzleHttp\Plugin\Async` has been removed. -- `GuzzleHttp\Plugin\CurlAuth` has been removed. -- `GuzzleHttp\Plugin\ErrorResponse\ErrorResponsePlugin` has been removed. This - functionality should instead be implemented with event listeners that occur - after normal response parsing occurs in the guzzle/command package. - -The following plugins are not part of the core Guzzle package, but are provided -in separate repositories: - -- `Guzzle\Http\Plugin\BackoffPlugin` has been rewritten to be muchs simpler - to build custom retry policies using simple functions rather than various - chained classes. See: https://github.com/guzzle/retry-subscriber -- `Guzzle\Http\Plugin\Cache\CachePlugin` has moved to - https://github.com/guzzle/cache-subscriber -- `Guzzle\Http\Plugin\Log\LogPlugin` has moved to - https://github.com/guzzle/log-subscriber -- `Guzzle\Http\Plugin\Md5\Md5Plugin` has moved to - https://github.com/guzzle/message-integrity-subscriber -- `Guzzle\Http\Plugin\Mock\MockPlugin` has moved to - `GuzzleHttp\Subscriber\MockSubscriber`. -- `Guzzle\Http\Plugin\Oauth\OauthPlugin` has moved to - https://github.com/guzzle/oauth-subscriber - -## Service - -The service description layer of Guzzle has moved into two separate packages: - -- http://github.com/guzzle/command Provides a high level abstraction over web - services by representing web service operations using commands. -- http://github.com/guzzle/guzzle-services Provides an implementation of - guzzle/command that provides request serialization and response parsing using - Guzzle service descriptions. - -## Stream - -Stream have moved to a separate package available at -https://github.com/guzzle/streams. - -`Guzzle\Stream\StreamInterface` has been given a large update to cleanly take -on the responsibilities of `Guzzle\Http\EntityBody` and -`Guzzle\Http\EntityBodyInterface` now that they have been removed. The number -of methods implemented by the `StreamInterface` has been drastically reduced to -allow developers to more easily extend and decorate stream behavior. - -## Removed methods from StreamInterface - -- `getStream` and `setStream` have been removed to better encapsulate streams. -- `getMetadata` and `setMetadata` have been removed in favor of - `GuzzleHttp\Stream\MetadataStreamInterface`. -- `getWrapper`, `getWrapperData`, `getStreamType`, and `getUri` have all been - removed. This data is accessible when - using streams that implement `GuzzleHttp\Stream\MetadataStreamInterface`. -- `rewind` has been removed. Use `seek(0)` for a similar behavior. - -## Renamed methods - -- `detachStream` has been renamed to `detach`. -- `feof` has been renamed to `eof`. -- `ftell` has been renamed to `tell`. -- `readLine` has moved from an instance method to a static class method of - `GuzzleHttp\Stream\Stream`. - -## Metadata streams - -`GuzzleHttp\Stream\MetadataStreamInterface` has been added to denote streams -that contain additional metadata accessible via `getMetadata()`. -`GuzzleHttp\Stream\StreamInterface::getMetadata` and -`GuzzleHttp\Stream\StreamInterface::setMetadata` have been removed. - -## StreamRequestFactory - -The entire concept of the StreamRequestFactory has been removed. The way this -was used in Guzzle 3 broke the actual interface of sending streaming requests -(instead of getting back a Response, you got a StreamInterface). Streeaming -PHP requests are now implemented throught the `GuzzleHttp\Adapter\StreamAdapter`. - -3.6 to 3.7 ----------- - -### Deprecations - -- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.: - -```php -\Guzzle\Common\Version::$emitWarnings = true; -``` - -The following APIs and options have been marked as deprecated: - -- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead. -- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. -- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. -- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. -- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. -- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated -- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. -- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. -- Marked `Guzzle\Common\Collection::inject()` as deprecated. -- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use - `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or - `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` - -3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational -request methods. When paired with a client's configuration settings, these options allow you to specify default settings -for various aspects of a request. Because these options make other previous configuration options redundant, several -configuration options and methods of a client and AbstractCommand have been deprecated. - -- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`. -- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`. -- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')` -- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0 - - $command = $client->getCommand('foo', array( - 'command.headers' => array('Test' => '123'), - 'command.response_body' => '/path/to/file' - )); - - // Should be changed to: - - $command = $client->getCommand('foo', array( - 'command.request_options' => array( - 'headers' => array('Test' => '123'), - 'save_as' => '/path/to/file' - ) - )); - -### Interface changes - -Additions and changes (you will need to update any implementations or subclasses you may have created): - -- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: - createRequest, head, delete, put, patch, post, options, prepareRequest -- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` -- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` -- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to - `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a - resource, string, or EntityBody into the $options parameter to specify the download location of the response. -- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a - default `array()` -- Added `Guzzle\Stream\StreamInterface::isRepeatable` -- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. - -The following methods were removed from interfaces. All of these methods are still available in the concrete classes -that implement them, but you should update your code to use alternative methods: - -- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use - `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or - `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or - `$client->setDefaultOption('headers/{header_name}', 'value')`. or - `$client->setDefaultOption('headers', array('header_name' => 'value'))`. -- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`. -- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail. -- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail. -- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail. -- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin. -- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin. -- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin. - -### Cache plugin breaking changes - -- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a - CacheStorageInterface. These two objects and interface will be removed in a future version. -- Always setting X-cache headers on cached responses -- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin -- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface - $request, Response $response);` -- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` -- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` -- Added `CacheStorageInterface::purge($url)` -- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin - $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, - CanCacheStrategyInterface $canCache = null)` -- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` - -3.5 to 3.6 ----------- - -* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. -* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution -* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). - For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader(). - Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request. -* Specific header implementations can be created for complex headers. When a message creates a header, it uses a - HeaderFactory which can map specific headers to specific header classes. There is now a Link header and - CacheControl header implementation. -* Moved getLinks() from Response to just be used on a Link header object. - -If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the -HeaderInterface (e.g. toArray(), getAll(), etc.). - -### Interface changes - -* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate -* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() -* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in - Guzzle\Http\Curl\RequestMediator -* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. -* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface -* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() - -### Removed deprecated functions - -* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() -* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). - -### Deprecations - -* The ability to case-insensitively search for header values -* Guzzle\Http\Message\Header::hasExactHeader -* Guzzle\Http\Message\Header::raw. Use getAll() -* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object - instead. - -### Other changes - -* All response header helper functions return a string rather than mixing Header objects and strings inconsistently -* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle - directly via interfaces -* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist - but are a no-op until removed. -* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a - `Guzzle\Service\Command\ArrayCommandInterface`. -* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response - on a request while the request is still being transferred -* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess - -3.3 to 3.4 ----------- - -Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs. - -3.2 to 3.3 ----------- - -### Response::getEtag() quote stripping removed - -`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header - -### Removed `Guzzle\Http\Utils` - -The `Guzzle\Http\Utils` class was removed. This class was only used for testing. - -### Stream wrapper and type - -`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getStreamType()` are no longer converted to lowercase. - -### curl.emit_io became emit_io - -Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the -'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' - -3.1 to 3.2 ----------- - -### CurlMulti is no longer reused globally - -Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added -to a single client can pollute requests dispatched from other clients. - -If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the -ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is -created. - -```php -$multi = new Guzzle\Http\Curl\CurlMulti(); -$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json'); -$builder->addListener('service_builder.create_client', function ($event) use ($multi) { - $event['client']->setCurlMulti($multi); -} -}); -``` - -### No default path - -URLs no longer have a default path value of '/' if no path was specified. - -Before: - -```php -$request = $client->get('http://www.foo.com'); -echo $request->getUrl(); -// >> http://www.foo.com/ -``` - -After: - -```php -$request = $client->get('http://www.foo.com'); -echo $request->getUrl(); -// >> http://www.foo.com -``` - -### Less verbose BadResponseException - -The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and -response information. You can, however, get access to the request and response object by calling `getRequest()` or -`getResponse()` on the exception object. - -### Query parameter aggregation - -Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a -setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is -responsible for handling the aggregation of multi-valued query string variables into a flattened hash. - -2.8 to 3.x ----------- - -### Guzzle\Service\Inspector - -Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig` - -**Before** - -```php -use Guzzle\Service\Inspector; - -class YourClient extends \Guzzle\Service\Client -{ - public static function factory($config = array()) - { - $default = array(); - $required = array('base_url', 'username', 'api_key'); - $config = Inspector::fromConfig($config, $default, $required); - - $client = new self( - $config->get('base_url'), - $config->get('username'), - $config->get('api_key') - ); - $client->setConfig($config); - - $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); - - return $client; - } -``` - -**After** - -```php -use Guzzle\Common\Collection; - -class YourClient extends \Guzzle\Service\Client -{ - public static function factory($config = array()) - { - $default = array(); - $required = array('base_url', 'username', 'api_key'); - $config = Collection::fromConfig($config, $default, $required); - - $client = new self( - $config->get('base_url'), - $config->get('username'), - $config->get('api_key') - ); - $client->setConfig($config); - - $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); - - return $client; - } -``` - -### Convert XML Service Descriptions to JSON - -**Before** - -```xml - - - - - - Get a list of groups - - - Uses a search query to get a list of groups - - - - Create a group - - - - - Delete a group by ID - - - - - - - Update a group - - - - - - -``` - -**After** - -```json -{ - "name": "Zendesk REST API v2", - "apiVersion": "2012-12-31", - "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users", - "operations": { - "list_groups": { - "httpMethod":"GET", - "uri": "groups.json", - "summary": "Get a list of groups" - }, - "search_groups":{ - "httpMethod":"GET", - "uri": "search.json?query=\"{query} type:group\"", - "summary": "Uses a search query to get a list of groups", - "parameters":{ - "query":{ - "location": "uri", - "description":"Zendesk Search Query", - "type": "string", - "required": true - } - } - }, - "create_group": { - "httpMethod":"POST", - "uri": "groups.json", - "summary": "Create a group", - "parameters":{ - "data": { - "type": "array", - "location": "body", - "description":"Group JSON", - "filters": "json_encode", - "required": true - }, - "Content-Type":{ - "type": "string", - "location":"header", - "static": "application/json" - } - } - }, - "delete_group": { - "httpMethod":"DELETE", - "uri": "groups/{id}.json", - "summary": "Delete a group", - "parameters":{ - "id":{ - "location": "uri", - "description":"Group to delete by ID", - "type": "integer", - "required": true - } - } - }, - "get_group": { - "httpMethod":"GET", - "uri": "groups/{id}.json", - "summary": "Get a ticket", - "parameters":{ - "id":{ - "location": "uri", - "description":"Group to get by ID", - "type": "integer", - "required": true - } - } - }, - "update_group": { - "httpMethod":"PUT", - "uri": "groups/{id}.json", - "summary": "Update a group", - "parameters":{ - "id": { - "location": "uri", - "description":"Group to update by ID", - "type": "integer", - "required": true - }, - "data": { - "type": "array", - "location": "body", - "description":"Group JSON", - "filters": "json_encode", - "required": true - }, - "Content-Type":{ - "type": "string", - "location":"header", - "static": "application/json" - } - } - } -} -``` - -### Guzzle\Service\Description\ServiceDescription - -Commands are now called Operations - -**Before** - -```php -use Guzzle\Service\Description\ServiceDescription; - -$sd = new ServiceDescription(); -$sd->getCommands(); // @returns ApiCommandInterface[] -$sd->hasCommand($name); -$sd->getCommand($name); // @returns ApiCommandInterface|null -$sd->addCommand($command); // @param ApiCommandInterface $command -``` - -**After** - -```php -use Guzzle\Service\Description\ServiceDescription; - -$sd = new ServiceDescription(); -$sd->getOperations(); // @returns OperationInterface[] -$sd->hasOperation($name); -$sd->getOperation($name); // @returns OperationInterface|null -$sd->addOperation($operation); // @param OperationInterface $operation -``` - -### Guzzle\Common\Inflection\Inflector - -Namespace is now `Guzzle\Inflection\Inflector` - -### Guzzle\Http\Plugin - -Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below. - -### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log - -Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively. - -**Before** - -```php -use Guzzle\Common\Log\ClosureLogAdapter; -use Guzzle\Http\Plugin\LogPlugin; - -/** @var \Guzzle\Http\Client */ -$client; - -// $verbosity is an integer indicating desired message verbosity level -$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE); -``` - -**After** - -```php -use Guzzle\Log\ClosureLogAdapter; -use Guzzle\Log\MessageFormatter; -use Guzzle\Plugin\Log\LogPlugin; - -/** @var \Guzzle\Http\Client */ -$client; - -// $format is a string indicating desired message format -- @see MessageFormatter -$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT); -``` - -### Guzzle\Http\Plugin\CurlAuthPlugin - -Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`. - -### Guzzle\Http\Plugin\ExponentialBackoffPlugin - -Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes. - -**Before** - -```php -use Guzzle\Http\Plugin\ExponentialBackoffPlugin; - -$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge( - ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429) - )); - -$client->addSubscriber($backoffPlugin); -``` - -**After** - -```php -use Guzzle\Plugin\Backoff\BackoffPlugin; -use Guzzle\Plugin\Backoff\HttpBackoffStrategy; - -// Use convenient factory method instead -- see implementation for ideas of what -// you can do with chaining backoff strategies -$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge( - HttpBackoffStrategy::getDefaultFailureCodes(), array(429) - )); -$client->addSubscriber($backoffPlugin); -``` - -### Known Issues - -#### [BUG] Accept-Encoding header behavior changed unintentionally. - -(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e) - -In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to -properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen. -See issue #217 for a workaround, or use a version containing the fix. diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/composer.json b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/composer.json deleted file mode 100644 index 717b3be4b3..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/composer.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "guzzlehttp/guzzle", - "type": "library", - "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", - "keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"], - "homepage": "http://guzzlephp.org/", - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.4.0", - "guzzlehttp/ringphp": "^1.1" - }, - "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "^4.0" - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "GuzzleHttp\\Tests\\": "tests/" - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/BatchResults.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/BatchResults.php deleted file mode 100644 index e5af433ddf..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/BatchResults.php +++ /dev/null @@ -1,148 +0,0 @@ -hash = $hash; - } - - /** - * Get the keys that are available on the batch result. - * - * @return array - */ - public function getKeys() - { - return iterator_to_array($this->hash); - } - - /** - * Gets a result from the container for the given object. When getting - * results for a batch of requests, provide the request object. - * - * @param object $forObject Object to retrieve the result for. - * - * @return mixed|null - */ - public function getResult($forObject) - { - return isset($this->hash[$forObject]) ? $this->hash[$forObject] : null; - } - - /** - * Get an array of successful results. - * - * @return array - */ - public function getSuccessful() - { - $results = []; - foreach ($this->hash as $key) { - if (!($this->hash[$key] instanceof \Exception)) { - $results[] = $this->hash[$key]; - } - } - - return $results; - } - - /** - * Get an array of failed results. - * - * @return array - */ - public function getFailures() - { - $results = []; - foreach ($this->hash as $key) { - if ($this->hash[$key] instanceof \Exception) { - $results[] = $this->hash[$key]; - } - } - - return $results; - } - - /** - * Allows iteration over all batch result values. - * - * @return \ArrayIterator - */ - public function getIterator() - { - $results = []; - foreach ($this->hash as $key) { - $results[] = $this->hash[$key]; - } - - return new \ArrayIterator($results); - } - - /** - * Counts the number of elements in the batch result. - * - * @return int - */ - public function count() - { - return count($this->hash); - } - - /** - * Checks if the batch contains a specific numerical array index. - * - * @param int $key Index to access - * - * @return bool - */ - public function offsetExists($key) - { - return $key < count($this->hash); - } - - /** - * Allows access of the batch using a numerical array index. - * - * @param int $key Index to access. - * - * @return mixed|null - */ - public function offsetGet($key) - { - $i = -1; - foreach ($this->hash as $obj) { - if ($key === ++$i) { - return $this->hash[$obj]; - } - } - - return null; - } - - public function offsetUnset($key) - { - throw new \RuntimeException('Not implemented'); - } - - public function offsetSet($key, $value) - { - throw new \RuntimeException('Not implemented'); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Client.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Client.php deleted file mode 100644 index fa1038dad5..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Client.php +++ /dev/null @@ -1,355 +0,0 @@ - [ - * 'http://www.foo.com/{version}/', - * ['version' => '123'] - * ], - * 'defaults' => [ - * 'timeout' => 10, - * 'allow_redirects' => false, - * 'proxy' => '192.168.16.1:10' - * ] - * ]); - * - * @param array $config Client configuration settings - * - base_url: Base URL of the client that is merged into relative URLs. - * Can be a string or an array that contains a URI template followed - * by an associative array of expansion variables to inject into the - * URI template. - * - handler: callable RingPHP handler used to transfer requests - * - message_factory: Factory used to create request and response object - * - defaults: Default request options to apply to each request - * - emitter: Event emitter used for request events - * - fsm: (internal use only) The request finite state machine. A - * function that accepts a transaction and optional final state. The - * function is responsible for transitioning a request through its - * lifecycle events. - */ - public function __construct(array $config = []) - { - $this->configureBaseUrl($config); - $this->configureDefaults($config); - - if (isset($config['emitter'])) { - $this->emitter = $config['emitter']; - } - - $this->messageFactory = isset($config['message_factory']) - ? $config['message_factory'] - : new MessageFactory(); - - if (isset($config['fsm'])) { - $this->fsm = $config['fsm']; - } else { - if (isset($config['handler'])) { - $handler = $config['handler']; - } elseif (isset($config['adapter'])) { - $handler = $config['adapter']; - } else { - $handler = Utils::getDefaultHandler(); - } - $this->fsm = new RequestFsm($handler, $this->messageFactory); - } - } - - public function getDefaultOption($keyOrPath = null) - { - return $keyOrPath === null - ? $this->defaults - : Utils::getPath($this->defaults, $keyOrPath); - } - - public function setDefaultOption($keyOrPath, $value) - { - Utils::setPath($this->defaults, $keyOrPath, $value); - } - - public function getBaseUrl() - { - return (string) $this->baseUrl; - } - - public function createRequest($method, $url = null, array $options = []) - { - $options = $this->mergeDefaults($options); - // Use a clone of the client's emitter - $options['config']['emitter'] = clone $this->getEmitter(); - $url = $url || (is_string($url) && strlen($url)) - ? $this->buildUrl($url) - : (string) $this->baseUrl; - - return $this->messageFactory->createRequest($method, $url, $options); - } - - public function get($url = null, $options = []) - { - return $this->send($this->createRequest('GET', $url, $options)); - } - - public function head($url = null, array $options = []) - { - return $this->send($this->createRequest('HEAD', $url, $options)); - } - - public function delete($url = null, array $options = []) - { - return $this->send($this->createRequest('DELETE', $url, $options)); - } - - public function put($url = null, array $options = []) - { - return $this->send($this->createRequest('PUT', $url, $options)); - } - - public function patch($url = null, array $options = []) - { - return $this->send($this->createRequest('PATCH', $url, $options)); - } - - public function post($url = null, array $options = []) - { - return $this->send($this->createRequest('POST', $url, $options)); - } - - public function options($url = null, array $options = []) - { - return $this->send($this->createRequest('OPTIONS', $url, $options)); - } - - public function send(RequestInterface $request) - { - $isFuture = $request->getConfig()->get('future'); - $trans = new Transaction($this, $request, $isFuture); - $fn = $this->fsm; - - try { - $fn($trans); - if ($isFuture) { - // Turn the normal response into a future if needed. - return $trans->response instanceof FutureInterface - ? $trans->response - : new FutureResponse(new FulfilledPromise($trans->response)); - } - // Resolve deep futures if this is not a future - // transaction. This accounts for things like retries - // that do not have an immediate side-effect. - while ($trans->response instanceof FutureInterface) { - $trans->response = $trans->response->wait(); - } - return $trans->response; - } catch (\Exception $e) { - if ($isFuture) { - // Wrap the exception in a promise - return new FutureResponse(new RejectedPromise($e)); - } - throw RequestException::wrapException($trans->request, $e); - } - } - - /** - * Get an array of default options to apply to the client - * - * @return array - */ - protected function getDefaultOptions() - { - $settings = [ - 'allow_redirects' => true, - 'exceptions' => true, - 'decode_content' => true, - 'verify' => true - ]; - - // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set. - // We can only trust the HTTP_PROXY environment variable in a CLI - // process due to the fact that PHP has no reliable mechanism to - // get environment variables that start with "HTTP_". - if (php_sapi_name() == 'cli' && getenv('HTTP_PROXY')) { - $settings['proxy']['http'] = getenv('HTTP_PROXY'); - } - - if ($proxy = getenv('HTTPS_PROXY')) { - $settings['proxy']['https'] = $proxy; - } - - return $settings; - } - - /** - * Expand a URI template and inherit from the base URL if it's relative - * - * @param string|array $url URL or an array of the URI template to expand - * followed by a hash of template varnames. - * @return string - * @throws \InvalidArgumentException - */ - private function buildUrl($url) - { - // URI template (absolute or relative) - if (!is_array($url)) { - return strpos($url, '://') - ? (string) $url - : (string) $this->baseUrl->combine($url); - } - - if (!isset($url[1])) { - throw new \InvalidArgumentException('You must provide a hash of ' - . 'varname options in the second element of a URL array.'); - } - - // Absolute URL - if (strpos($url[0], '://')) { - return Utils::uriTemplate($url[0], $url[1]); - } - - // Combine the relative URL with the base URL - return (string) $this->baseUrl->combine( - Utils::uriTemplate($url[0], $url[1]) - ); - } - - private function configureBaseUrl(&$config) - { - if (!isset($config['base_url'])) { - $this->baseUrl = new Url('', ''); - } elseif (!is_array($config['base_url'])) { - $this->baseUrl = Url::fromString($config['base_url']); - } elseif (count($config['base_url']) < 2) { - throw new \InvalidArgumentException('You must provide a hash of ' - . 'varname options in the second element of a base_url array.'); - } else { - $this->baseUrl = Url::fromString( - Utils::uriTemplate( - $config['base_url'][0], - $config['base_url'][1] - ) - ); - $config['base_url'] = (string) $this->baseUrl; - } - } - - private function configureDefaults($config) - { - if (!isset($config['defaults'])) { - $this->defaults = $this->getDefaultOptions(); - } else { - $this->defaults = array_replace( - $this->getDefaultOptions(), - $config['defaults'] - ); - } - - // Add the default user-agent header - if (!isset($this->defaults['headers'])) { - $this->defaults['headers'] = [ - 'User-Agent' => Utils::getDefaultUserAgent() - ]; - } elseif (!Core::hasHeader($this->defaults, 'User-Agent')) { - // Add the User-Agent header if one was not already set - $this->defaults['headers']['User-Agent'] = Utils::getDefaultUserAgent(); - } - } - - /** - * Merges default options into the array passed by reference. - * - * @param array $options Options to modify by reference - * - * @return array - */ - private function mergeDefaults($options) - { - $defaults = $this->defaults; - - // Case-insensitively merge in default headers if both defaults and - // options have headers specified. - if (!empty($defaults['headers']) && !empty($options['headers'])) { - // Create a set of lowercased keys that are present. - $lkeys = []; - foreach (array_keys($options['headers']) as $k) { - $lkeys[strtolower($k)] = true; - } - // Merge in lowercase default keys when not present in above set. - foreach ($defaults['headers'] as $key => $value) { - if (!isset($lkeys[strtolower($key)])) { - $options['headers'][$key] = $value; - } - } - // No longer need to merge in headers. - unset($defaults['headers']); - } - - $result = array_replace_recursive($defaults, $options); - foreach ($options as $k => $v) { - if ($v === null) { - unset($result[$k]); - } - } - - return $result; - } - - /** - * @deprecated Use {@see GuzzleHttp\Pool} instead. - * @see GuzzleHttp\Pool - */ - public function sendAll($requests, array $options = []) - { - Pool::send($this, $requests, $options); - } - - /** - * @deprecated Use GuzzleHttp\Utils::getDefaultHandler - */ - public static function getDefaultHandler() - { - return Utils::getDefaultHandler(); - } - - /** - * @deprecated Use GuzzleHttp\Utils::getDefaultUserAgent - */ - public static function getDefaultUserAgent() - { - return Utils::getDefaultUserAgent(); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/ClientInterface.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/ClientInterface.php deleted file mode 100644 index 6668597d3d..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/ClientInterface.php +++ /dev/null @@ -1,150 +0,0 @@ -data = $data; - } - - /** - * Create a new collection from an array, validate the keys, and add default - * values where missing - * - * @param array $config Configuration values to apply. - * @param array $defaults Default parameters - * @param array $required Required parameter names - * - * @return self - * @throws \InvalidArgumentException if a parameter is missing - */ - public static function fromConfig( - array $config = [], - array $defaults = [], - array $required = [] - ) { - $data = $config + $defaults; - - if ($missing = array_diff($required, array_keys($data))) { - throw new \InvalidArgumentException( - 'Config is missing the following keys: ' . - implode(', ', $missing)); - } - - return new self($data); - } - - /** - * Removes all key value pairs - */ - public function clear() - { - $this->data = []; - } - - /** - * Get a specific key value. - * - * @param string $key Key to retrieve. - * - * @return mixed|null Value of the key or NULL - */ - public function get($key) - { - return isset($this->data[$key]) ? $this->data[$key] : null; - } - - /** - * Set a key value pair - * - * @param string $key Key to set - * @param mixed $value Value to set - */ - public function set($key, $value) - { - $this->data[$key] = $value; - } - - /** - * Add a value to a key. If a key of the same name has already been added, - * the key value will be converted into an array and the new value will be - * pushed to the end of the array. - * - * @param string $key Key to add - * @param mixed $value Value to add to the key - */ - public function add($key, $value) - { - if (!array_key_exists($key, $this->data)) { - $this->data[$key] = $value; - } elseif (is_array($this->data[$key])) { - $this->data[$key][] = $value; - } else { - $this->data[$key] = array($this->data[$key], $value); - } - } - - /** - * Remove a specific key value pair - * - * @param string $key A key to remove - */ - public function remove($key) - { - unset($this->data[$key]); - } - - /** - * Get all keys in the collection - * - * @return array - */ - public function getKeys() - { - return array_keys($this->data); - } - - /** - * Returns whether or not the specified key is present. - * - * @param string $key The key for which to check the existence. - * - * @return bool - */ - public function hasKey($key) - { - return array_key_exists($key, $this->data); - } - - /** - * Checks if any keys contains a certain value - * - * @param string $value Value to search for - * - * @return mixed Returns the key if the value was found FALSE if the value - * was not found. - */ - public function hasValue($value) - { - return array_search($value, $this->data, true); - } - - /** - * Replace the data of the object with the value of an array - * - * @param array $data Associative array of data - */ - public function replace(array $data) - { - $this->data = $data; - } - - /** - * Add and merge in a Collection or array of key value pair data. - * - * @param Collection|array $data Associative array of key value pair data - */ - public function merge($data) - { - foreach ($data as $key => $value) { - $this->add($key, $value); - } - } - - /** - * Overwrite key value pairs in this collection with all of the data from - * an array or collection. - * - * @param array|\Traversable $data Values to override over this config - */ - public function overwriteWith($data) - { - if (is_array($data)) { - $this->data = $data + $this->data; - } elseif ($data instanceof Collection) { - $this->data = $data->toArray() + $this->data; - } else { - foreach ($data as $key => $value) { - $this->data[$key] = $value; - } - } - } - - /** - * Returns a Collection containing all the elements of the collection after - * applying the callback function to each one. - * - * The callable should accept three arguments: - * - (string) $key - * - (string) $value - * - (array) $context - * - * The callable must return a the altered or unaltered value. - * - * @param callable $closure Map function to apply - * @param array $context Context to pass to the callable - * - * @return Collection - */ - public function map(callable $closure, array $context = []) - { - $collection = new static(); - foreach ($this as $key => $value) { - $collection[$key] = $closure($key, $value, $context); - } - - return $collection; - } - - /** - * Iterates over each key value pair in the collection passing them to the - * callable. If the callable returns true, the current value from input is - * returned into the result Collection. - * - * The callable must accept two arguments: - * - (string) $key - * - (string) $value - * - * @param callable $closure Evaluation function - * - * @return Collection - */ - public function filter(callable $closure) - { - $collection = new static(); - foreach ($this->data as $key => $value) { - if ($closure($key, $value)) { - $collection[$key] = $value; - } - } - - return $collection; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php deleted file mode 100644 index f8ac7dd350..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php +++ /dev/null @@ -1,248 +0,0 @@ -strictMode = $strictMode; - - foreach ($cookieArray as $cookie) { - if (!($cookie instanceof SetCookie)) { - $cookie = new SetCookie($cookie); - } - $this->setCookie($cookie); - } - } - - /** - * Create a new Cookie jar from an associative array and domain. - * - * @param array $cookies Cookies to create the jar from - * @param string $domain Domain to set the cookies to - * - * @return self - */ - public static function fromArray(array $cookies, $domain) - { - $cookieJar = new self(); - foreach ($cookies as $name => $value) { - $cookieJar->setCookie(new SetCookie([ - 'Domain' => $domain, - 'Name' => $name, - 'Value' => $value, - 'Discard' => true - ])); - } - - return $cookieJar; - } - - /** - * Quote the cookie value if it is not already quoted and it contains - * problematic characters. - * - * @param string $value Value that may or may not need to be quoted - * - * @return string - */ - public static function getCookieValue($value) - { - if (substr($value, 0, 1) !== '"' && - substr($value, -1, 1) !== '"' && - strpbrk($value, ';,') - ) { - $value = '"' . $value . '"'; - } - - return $value; - } - - public function toArray() - { - return array_map(function (SetCookie $cookie) { - return $cookie->toArray(); - }, $this->getIterator()->getArrayCopy()); - } - - public function clear($domain = null, $path = null, $name = null) - { - if (!$domain) { - $this->cookies = []; - return; - } elseif (!$path) { - $this->cookies = array_filter( - $this->cookies, - function (SetCookie $cookie) use ($path, $domain) { - return !$cookie->matchesDomain($domain); - } - ); - } elseif (!$name) { - $this->cookies = array_filter( - $this->cookies, - function (SetCookie $cookie) use ($path, $domain) { - return !($cookie->matchesPath($path) && - $cookie->matchesDomain($domain)); - } - ); - } else { - $this->cookies = array_filter( - $this->cookies, - function (SetCookie $cookie) use ($path, $domain, $name) { - return !($cookie->getName() == $name && - $cookie->matchesPath($path) && - $cookie->matchesDomain($domain)); - } - ); - } - } - - public function clearSessionCookies() - { - $this->cookies = array_filter( - $this->cookies, - function (SetCookie $cookie) { - return !$cookie->getDiscard() && $cookie->getExpires(); - } - ); - } - - public function setCookie(SetCookie $cookie) - { - // Only allow cookies with set and valid domain, name, value - $result = $cookie->validate(); - if ($result !== true) { - if ($this->strictMode) { - throw new \RuntimeException('Invalid cookie: ' . $result); - } else { - $this->removeCookieIfEmpty($cookie); - return false; - } - } - - // Resolve conflicts with previously set cookies - foreach ($this->cookies as $i => $c) { - - // Two cookies are identical, when their path, and domain are - // identical. - if ($c->getPath() != $cookie->getPath() || - $c->getDomain() != $cookie->getDomain() || - $c->getName() != $cookie->getName() - ) { - continue; - } - - // The previously set cookie is a discard cookie and this one is - // not so allow the new cookie to be set - if (!$cookie->getDiscard() && $c->getDiscard()) { - unset($this->cookies[$i]); - continue; - } - - // If the new cookie's expiration is further into the future, then - // replace the old cookie - if ($cookie->getExpires() > $c->getExpires()) { - unset($this->cookies[$i]); - continue; - } - - // If the value has changed, we better change it - if ($cookie->getValue() !== $c->getValue()) { - unset($this->cookies[$i]); - continue; - } - - // The cookie exists, so no need to continue - return false; - } - - $this->cookies[] = $cookie; - - return true; - } - - public function count() - { - return count($this->cookies); - } - - public function getIterator() - { - return new \ArrayIterator(array_values($this->cookies)); - } - - public function extractCookies( - RequestInterface $request, - ResponseInterface $response - ) { - if ($cookieHeader = $response->getHeaderAsArray('Set-Cookie')) { - foreach ($cookieHeader as $cookie) { - $sc = SetCookie::fromString($cookie); - if (!$sc->getDomain()) { - $sc->setDomain($request->getHost()); - } - $this->setCookie($sc); - } - } - } - - public function addCookieHeader(RequestInterface $request) - { - $values = []; - $scheme = $request->getScheme(); - $host = $request->getHost(); - $path = $request->getPath(); - - foreach ($this->cookies as $cookie) { - if ($cookie->matchesPath($path) && - $cookie->matchesDomain($host) && - !$cookie->isExpired() && - (!$cookie->getSecure() || $scheme == 'https') - ) { - $values[] = $cookie->getName() . '=' - . self::getCookieValue($cookie->getValue()); - } - } - - if ($values) { - $request->setHeader('Cookie', implode('; ', $values)); - } - } - - /** - * If a cookie already exists and the server asks to set it again with a - * null value, the cookie must be deleted. - * - * @param SetCookie $cookie - */ - private function removeCookieIfEmpty(SetCookie $cookie) - { - $cookieValue = $cookie->getValue(); - if ($cookieValue === null || $cookieValue === '') { - $this->clear( - $cookie->getDomain(), - $cookie->getPath(), - $cookie->getName() - ); - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php deleted file mode 100644 index 4ea8567e87..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php +++ /dev/null @@ -1,75 +0,0 @@ -filename = $cookieFile; - - if (file_exists($cookieFile)) { - $this->load($cookieFile); - } - } - - /** - * Saves the file when shutting down - */ - public function __destruct() - { - $this->save($this->filename); - } - - /** - * Saves the cookies to a file. - * - * @param string $filename File to save - * @throws \RuntimeException if the file cannot be found or created - */ - public function save($filename) - { - $json = []; - foreach ($this as $cookie) { - if ($cookie->getExpires() && !$cookie->getDiscard()) { - $json[] = $cookie->toArray(); - } - } - - if (false === file_put_contents($filename, json_encode($json))) { - // @codeCoverageIgnoreStart - throw new \RuntimeException("Unable to save file {$filename}"); - // @codeCoverageIgnoreEnd - } - } - - /** - * Load cookies from a JSON formatted file. - * - * Old cookies are kept unless overwritten by newly loaded ones. - * - * @param string $filename Cookie file to load. - * @throws \RuntimeException if the file cannot be loaded. - */ - public function load($filename) - { - $json = file_get_contents($filename); - if (false === $json) { - // @codeCoverageIgnoreStart - throw new \RuntimeException("Unable to load file {$filename}"); - // @codeCoverageIgnoreEnd - } - - $data = Utils::jsonDecode($json, true); - if (is_array($data)) { - foreach (Utils::jsonDecode($json, true) as $cookie) { - $this->setCookie(new SetCookie($cookie)); - } - } elseif (strlen($data)) { - throw new \RuntimeException("Invalid cookie file: {$filename}"); - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php deleted file mode 100644 index 71a02d56dc..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php +++ /dev/null @@ -1,66 +0,0 @@ -sessionKey = $sessionKey; - $this->load(); - } - - /** - * Saves cookies to session when shutting down - */ - public function __destruct() - { - $this->save(); - } - - /** - * Save cookies to the client session - */ - public function save() - { - $json = []; - foreach ($this as $cookie) { - if ($cookie->getExpires() && !$cookie->getDiscard()) { - $json[] = $cookie->toArray(); - } - } - - $_SESSION[$this->sessionKey] = json_encode($json); - } - - /** - * Load the contents of the client session into the data array - */ - protected function load() - { - $cookieJar = isset($_SESSION[$this->sessionKey]) - ? $_SESSION[$this->sessionKey] - : null; - - $data = Utils::jsonDecode($cookieJar, true); - if (is_array($data)) { - foreach ($data as $cookie) { - $this->setCookie(new SetCookie($cookie)); - } - } elseif (strlen($data)) { - throw new \RuntimeException("Invalid cookie data"); - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php deleted file mode 100644 index ac9a890813..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php +++ /dev/null @@ -1,373 +0,0 @@ - null, - 'Value' => null, - 'Domain' => null, - 'Path' => '/', - 'Max-Age' => null, - 'Expires' => null, - 'Secure' => false, - 'Discard' => false, - 'HttpOnly' => false - ]; - - /** @var array Cookie data */ - private $data; - - /** - * Create a new SetCookie object from a string - * - * @param string $cookie Set-Cookie header string - * - * @return self - */ - public static function fromString($cookie) - { - // Create the default return array - $data = self::$defaults; - // Explode the cookie string using a series of semicolons - $pieces = array_filter(array_map('trim', explode(';', $cookie))); - // The name of the cookie (first kvp) must include an equal sign. - if (empty($pieces) || !strpos($pieces[0], '=')) { - return new self($data); - } - - // Add the cookie pieces into the parsed data array - foreach ($pieces as $part) { - - $cookieParts = explode('=', $part, 2); - $key = trim($cookieParts[0]); - $value = isset($cookieParts[1]) - ? trim($cookieParts[1], " \n\r\t\0\x0B\"") - : true; - - // Only check for non-cookies when cookies have been found - if (empty($data['Name'])) { - $data['Name'] = $key; - $data['Value'] = $value; - } else { - foreach (array_keys(self::$defaults) as $search) { - if (!strcasecmp($search, $key)) { - $data[$search] = $value; - continue 2; - } - } - $data[$key] = $value; - } - } - - return new self($data); - } - - /** - * @param array $data Array of cookie data provided by a Cookie parser - */ - public function __construct(array $data = []) - { - $this->data = array_replace(self::$defaults, $data); - // Extract the Expires value and turn it into a UNIX timestamp if needed - if (!$this->getExpires() && $this->getMaxAge()) { - // Calculate the Expires date - $this->setExpires(time() + $this->getMaxAge()); - } elseif ($this->getExpires() && !is_numeric($this->getExpires())) { - $this->setExpires($this->getExpires()); - } - } - - public function __toString() - { - $str = $this->data['Name'] . '=' . $this->data['Value'] . '; '; - foreach ($this->data as $k => $v) { - if ($k != 'Name' && $k != 'Value' && $v !== null && $v !== false) { - if ($k == 'Expires') { - $str .= 'Expires=' . gmdate('D, d M Y H:i:s \G\M\T', $v) . '; '; - } else { - $str .= ($v === true ? $k : "{$k}={$v}") . '; '; - } - } - } - - return rtrim($str, '; '); - } - - public function toArray() - { - return $this->data; - } - - /** - * Get the cookie name - * - * @return string - */ - public function getName() - { - return $this->data['Name']; - } - - /** - * Set the cookie name - * - * @param string $name Cookie name - */ - public function setName($name) - { - $this->data['Name'] = $name; - } - - /** - * Get the cookie value - * - * @return string - */ - public function getValue() - { - return $this->data['Value']; - } - - /** - * Set the cookie value - * - * @param string $value Cookie value - */ - public function setValue($value) - { - $this->data['Value'] = $value; - } - - /** - * Get the domain - * - * @return string|null - */ - public function getDomain() - { - return $this->data['Domain']; - } - - /** - * Set the domain of the cookie - * - * @param string $domain - */ - public function setDomain($domain) - { - $this->data['Domain'] = $domain; - } - - /** - * Get the path - * - * @return string - */ - public function getPath() - { - return $this->data['Path']; - } - - /** - * Set the path of the cookie - * - * @param string $path Path of the cookie - */ - public function setPath($path) - { - $this->data['Path'] = $path; - } - - /** - * Maximum lifetime of the cookie in seconds - * - * @return int|null - */ - public function getMaxAge() - { - return $this->data['Max-Age']; - } - - /** - * Set the max-age of the cookie - * - * @param int $maxAge Max age of the cookie in seconds - */ - public function setMaxAge($maxAge) - { - $this->data['Max-Age'] = $maxAge; - } - - /** - * The UNIX timestamp when the cookie Expires - * - * @return mixed - */ - public function getExpires() - { - return $this->data['Expires']; - } - - /** - * Set the unix timestamp for which the cookie will expire - * - * @param int $timestamp Unix timestamp - */ - public function setExpires($timestamp) - { - $this->data['Expires'] = is_numeric($timestamp) - ? (int) $timestamp - : strtotime($timestamp); - } - - /** - * Get whether or not this is a secure cookie - * - * @return null|bool - */ - public function getSecure() - { - return $this->data['Secure']; - } - - /** - * Set whether or not the cookie is secure - * - * @param bool $secure Set to true or false if secure - */ - public function setSecure($secure) - { - $this->data['Secure'] = $secure; - } - - /** - * Get whether or not this is a session cookie - * - * @return null|bool - */ - public function getDiscard() - { - return $this->data['Discard']; - } - - /** - * Set whether or not this is a session cookie - * - * @param bool $discard Set to true or false if this is a session cookie - */ - public function setDiscard($discard) - { - $this->data['Discard'] = $discard; - } - - /** - * Get whether or not this is an HTTP only cookie - * - * @return bool - */ - public function getHttpOnly() - { - return $this->data['HttpOnly']; - } - - /** - * Set whether or not this is an HTTP only cookie - * - * @param bool $httpOnly Set to true or false if this is HTTP only - */ - public function setHttpOnly($httpOnly) - { - $this->data['HttpOnly'] = $httpOnly; - } - - /** - * Check if the cookie matches a path value - * - * @param string $path Path to check against - * - * @return bool - */ - public function matchesPath($path) - { - return !$this->getPath() || 0 === stripos($path, $this->getPath()); - } - - /** - * Check if the cookie matches a domain value - * - * @param string $domain Domain to check against - * - * @return bool - */ - public function matchesDomain($domain) - { - // Remove the leading '.' as per spec in RFC 6265. - // http://tools.ietf.org/html/rfc6265#section-5.2.3 - $cookieDomain = ltrim($this->getDomain(), '.'); - - // Domain not set or exact match. - if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) { - return true; - } - - // Matching the subdomain according to RFC 6265. - // http://tools.ietf.org/html/rfc6265#section-5.1.3 - if (filter_var($domain, FILTER_VALIDATE_IP)) { - return false; - } - - return (bool) preg_match('/\.' . preg_quote($cookieDomain) . '$/i', $domain); - } - - /** - * Check if the cookie is expired - * - * @return bool - */ - public function isExpired() - { - return $this->getExpires() && time() > $this->getExpires(); - } - - /** - * Check if the cookie is valid according to RFC 6265 - * - * @return bool|string Returns true if valid or an error message if invalid - */ - public function validate() - { - // Names must not be empty, but can be 0 - $name = $this->getName(); - if (empty($name) && !is_numeric($name)) { - return 'The cookie name must not be empty'; - } - - // Check if any of the invalid characters are present in the cookie name - if (preg_match("/[=,; \t\r\n\013\014]/", $name)) { - return "Cookie name must not cannot invalid characters: =,; \\t\\r\\n\\013\\014"; - } - - // Value must not be empty, but can be 0 - $value = $this->getValue(); - if (empty($value) && !is_numeric($value)) { - return 'The cookie value must not be empty'; - } - - // Domains must not be empty, but can be 0 - // A "0" is not a valid internet domain, but may be used as server name - // in a private network. - $domain = $this->getDomain(); - if (empty($domain) && !is_numeric($domain)) { - return 'The cookie domain must not be empty'; - } - - return true; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/AbstractEvent.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/AbstractEvent.php deleted file mode 100644 index 0d2f4dbf0c..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/AbstractEvent.php +++ /dev/null @@ -1,20 +0,0 @@ -propagationStopped; - } - - public function stopPropagation() - { - $this->propagationStopped = true; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php deleted file mode 100644 index 8a6ee47c85..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -transaction = $transaction; - } - - /** - * Get the HTTP client associated with the event. - * - * @return ClientInterface - */ - public function getClient() - { - return $this->transaction->client; - } - - /** - * Get the request object - * - * @return RequestInterface - */ - public function getRequest() - { - return $this->transaction->request; - } - - /** - * Get the number of transaction retries. - * - * @return int - */ - public function getRetryCount() - { - return $this->transaction->retries; - } - - /** - * @return Transaction - */ - public function getTransaction() - { - return $this->transaction; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/AbstractRetryableEvent.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/AbstractRetryableEvent.php deleted file mode 100644 index bbbdfaf831..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/AbstractRetryableEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -transaction->state = 'retry'; - - if ($afterDelay) { - $this->transaction->request->getConfig()->set('delay', $afterDelay); - } - - $this->stopPropagation(); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php deleted file mode 100644 index 3b106df007..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php +++ /dev/null @@ -1,63 +0,0 @@ -transaction->transferInfo; - } - - return isset($this->transaction->transferInfo[$name]) - ? $this->transaction->transferInfo[$name] - : null; - } - - /** - * Returns true/false if a response is available. - * - * @return bool - */ - public function hasResponse() - { - return !($this->transaction->response instanceof FutureInterface); - } - - /** - * Get the response. - * - * @return ResponseInterface|null - */ - public function getResponse() - { - return $this->hasResponse() ? $this->transaction->response : null; - } - - /** - * Intercept the request and associate a response - * - * @param ResponseInterface $response Response to set - */ - public function intercept(ResponseInterface $response) - { - $this->transaction->response = $response; - $this->transaction->exception = null; - $this->stopPropagation(); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/BeforeEvent.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/BeforeEvent.php deleted file mode 100644 index f313c37561..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/BeforeEvent.php +++ /dev/null @@ -1,26 +0,0 @@ -transaction->response = $response; - $this->transaction->exception = null; - $this->stopPropagation(); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/CompleteEvent.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/CompleteEvent.php deleted file mode 100644 index 56cc557e3e..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/CompleteEvent.php +++ /dev/null @@ -1,14 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * @link https://github.com/symfony/symfony/tree/master/src/Symfony/Component/EventDispatcher - */ -class Emitter implements EmitterInterface -{ - /** @var array */ - private $listeners = []; - - /** @var array */ - private $sorted = []; - - public function on($eventName, callable $listener, $priority = 0) - { - if ($priority === 'first') { - $priority = isset($this->listeners[$eventName]) - ? max(array_keys($this->listeners[$eventName])) + 1 - : 1; - } elseif ($priority === 'last') { - $priority = isset($this->listeners[$eventName]) - ? min(array_keys($this->listeners[$eventName])) - 1 - : -1; - } - - $this->listeners[$eventName][$priority][] = $listener; - unset($this->sorted[$eventName]); - } - - public function once($eventName, callable $listener, $priority = 0) - { - $onceListener = function ( - EventInterface $event - ) use (&$onceListener, $eventName, $listener, $priority) { - $this->removeListener($eventName, $onceListener); - $listener($event, $eventName); - }; - - $this->on($eventName, $onceListener, $priority); - } - - public function removeListener($eventName, callable $listener) - { - if (empty($this->listeners[$eventName])) { - return; - } - - foreach ($this->listeners[$eventName] as $priority => $listeners) { - if (false !== ($key = array_search($listener, $listeners, true))) { - unset( - $this->listeners[$eventName][$priority][$key], - $this->sorted[$eventName] - ); - } - } - } - - public function listeners($eventName = null) - { - // Return all events in a sorted priority order - if ($eventName === null) { - foreach (array_keys($this->listeners) as $eventName) { - if (empty($this->sorted[$eventName])) { - $this->listeners($eventName); - } - } - return $this->sorted; - } - - // Return the listeners for a specific event, sorted in priority order - if (empty($this->sorted[$eventName])) { - $this->sorted[$eventName] = []; - if (isset($this->listeners[$eventName])) { - krsort($this->listeners[$eventName], SORT_NUMERIC); - foreach ($this->listeners[$eventName] as $listeners) { - foreach ($listeners as $listener) { - $this->sorted[$eventName][] = $listener; - } - } - } - } - - return $this->sorted[$eventName]; - } - - public function hasListeners($eventName) - { - return !empty($this->listeners[$eventName]); - } - - public function emit($eventName, EventInterface $event) - { - if (isset($this->listeners[$eventName])) { - foreach ($this->listeners($eventName) as $listener) { - $listener($event, $eventName); - if ($event->isPropagationStopped()) { - break; - } - } - } - - return $event; - } - - public function attach(SubscriberInterface $subscriber) - { - foreach ($subscriber->getEvents() as $eventName => $listeners) { - if (is_array($listeners[0])) { - foreach ($listeners as $listener) { - $this->on( - $eventName, - [$subscriber, $listener[0]], - isset($listener[1]) ? $listener[1] : 0 - ); - } - } else { - $this->on( - $eventName, - [$subscriber, $listeners[0]], - isset($listeners[1]) ? $listeners[1] : 0 - ); - } - } - } - - public function detach(SubscriberInterface $subscriber) - { - foreach ($subscriber->getEvents() as $eventName => $listener) { - $this->removeListener($eventName, [$subscriber, $listener[0]]); - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/EmitterInterface.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/EmitterInterface.php deleted file mode 100644 index 9783efd15a..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/EmitterInterface.php +++ /dev/null @@ -1,96 +0,0 @@ -transaction->exception; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/ErrorEvent.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/ErrorEvent.php deleted file mode 100644 index 7432134d03..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/ErrorEvent.php +++ /dev/null @@ -1,27 +0,0 @@ -transaction->exception; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/EventInterface.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/EventInterface.php deleted file mode 100644 index 97247e84c6..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/EventInterface.php +++ /dev/null @@ -1,23 +0,0 @@ -emitter) { - $this->emitter = new Emitter(); - } - - return $this->emitter; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/ListenerAttacherTrait.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/ListenerAttacherTrait.php deleted file mode 100644 index 407dc92dd5..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/ListenerAttacherTrait.php +++ /dev/null @@ -1,88 +0,0 @@ -getEmitter(); - foreach ($listeners as $el) { - if ($el['once']) { - $emitter->once($el['name'], $el['fn'], $el['priority']); - } else { - $emitter->on($el['name'], $el['fn'], $el['priority']); - } - } - } - - /** - * Extracts the allowed events from the provided array, and ignores anything - * else in the array. The event listener must be specified as a callable or - * as an array of event listener data ("name", "fn", "priority", "once"). - * - * @param array $source Array containing callables or hashes of data to be - * prepared as event listeners. - * @param array $events Names of events to look for in the provided $source - * array. Other keys are ignored. - * @return array - */ - private function prepareListeners(array $source, array $events) - { - $listeners = []; - foreach ($events as $name) { - if (isset($source[$name])) { - $this->buildListener($name, $source[$name], $listeners); - } - } - - return $listeners; - } - - /** - * Creates a complete event listener definition from the provided array of - * listener data. Also works recursively if more than one listeners are - * contained in the provided array. - * - * @param string $name Name of the event the listener is for. - * @param array|callable $data Event listener data to prepare. - * @param array $listeners Array of listeners, passed by reference. - * - * @throws \InvalidArgumentException if the event data is malformed. - */ - private function buildListener($name, $data, &$listeners) - { - static $defaults = ['priority' => 0, 'once' => false]; - - // If a callable is provided, normalize it to the array format. - if (is_callable($data)) { - $data = ['fn' => $data]; - } - - // Prepare the listener and add it to the array, recursively. - if (isset($data['fn'])) { - $data['name'] = $name; - $listeners[] = $data + $defaults; - } elseif (is_array($data)) { - foreach ($data as $listenerData) { - $this->buildListener($name, $listenerData, $listeners); - } - } else { - throw new \InvalidArgumentException('Each event listener must be a ' - . 'callable or an associative array containing a "fn" key.'); - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/ProgressEvent.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/ProgressEvent.php deleted file mode 100644 index 3fd0de4ac0..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/ProgressEvent.php +++ /dev/null @@ -1,51 +0,0 @@ -downloadSize = $downloadSize; - $this->downloaded = $downloaded; - $this->uploadSize = $uploadSize; - $this->uploaded = $uploaded; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/RequestEvents.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/RequestEvents.php deleted file mode 100644 index f51d420654..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Event/RequestEvents.php +++ /dev/null @@ -1,56 +0,0 @@ - ['methodName']] - * - ['eventName' => ['methodName', $priority]] - * - ['eventName' => [['methodName'], ['otherMethod']] - * - ['eventName' => [['methodName'], ['otherMethod', $priority]] - * - ['eventName' => [['methodName', $priority], ['otherMethod', $priority]] - * - * @return array - */ - public function getEvents(); -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php deleted file mode 100644 index fd78431ea7..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php +++ /dev/null @@ -1,7 +0,0 @@ -response = $response; - } - /** - * Get the associated response - * - * @return ResponseInterface|null - */ - public function getResponse() - { - return $this->response; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php deleted file mode 100644 index 3f052d36db..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php +++ /dev/null @@ -1,121 +0,0 @@ -getStatusCode() - : 0; - parent::__construct($message, $code, $previous); - $this->request = $request; - $this->response = $response; - } - - /** - * Wrap non-RequestExceptions with a RequestException - * - * @param RequestInterface $request - * @param \Exception $e - * - * @return RequestException - */ - public static function wrapException(RequestInterface $request, \Exception $e) - { - if ($e instanceof RequestException) { - return $e; - } elseif ($e instanceof ConnectException) { - return new HttpConnectException($e->getMessage(), $request, null, $e); - } else { - return new RequestException($e->getMessage(), $request, null, $e); - } - } - - /** - * Factory method to create a new exception with a normalized error message - * - * @param RequestInterface $request Request - * @param ResponseInterface $response Response received - * @param \Exception $previous Previous exception - * - * @return self - */ - public static function create( - RequestInterface $request, - ResponseInterface $response = null, - \Exception $previous = null - ) { - if (!$response) { - return new self('Error completing request', $request, null, $previous); - } - - $level = floor($response->getStatusCode() / 100); - if ($level == '4') { - $label = 'Client error response'; - $className = __NAMESPACE__ . '\\ClientException'; - } elseif ($level == '5') { - $label = 'Server error response'; - $className = __NAMESPACE__ . '\\ServerException'; - } else { - $label = 'Unsuccessful response'; - $className = __CLASS__; - } - - $message = $label . ' [url] ' . $request->getUrl() - . ' [status code] ' . $response->getStatusCode() - . ' [reason phrase] ' . $response->getReasonPhrase(); - - return new $className($message, $request, $response, $previous); - } - - /** - * Get the request that caused the exception - * - * @return RequestInterface - */ - public function getRequest() - { - return $this->request; - } - - /** - * Get the associated response - * - * @return ResponseInterface|null - */ - public function getResponse() - { - return $this->response; - } - - /** - * Check if a response was received - * - * @return bool - */ - public function hasResponse() - { - return $this->response !== null; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php deleted file mode 100644 index 7cdd340866..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php +++ /dev/null @@ -1,7 +0,0 @@ -error = $error; - } - - /** - * Get the associated error - * - * @return \LibXMLError|null - */ - public function getError() - { - return $this->error; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/HasDataTrait.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/HasDataTrait.php deleted file mode 100644 index 020dfc9aba..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/HasDataTrait.php +++ /dev/null @@ -1,75 +0,0 @@ -data); - } - - public function offsetGet($offset) - { - return isset($this->data[$offset]) ? $this->data[$offset] : null; - } - - public function offsetSet($offset, $value) - { - $this->data[$offset] = $value; - } - - public function offsetExists($offset) - { - return isset($this->data[$offset]); - } - - public function offsetUnset($offset) - { - unset($this->data[$offset]); - } - - public function toArray() - { - return $this->data; - } - - public function count() - { - return count($this->data); - } - - /** - * Get a value from the collection using a path syntax to retrieve nested - * data. - * - * @param string $path Path to traverse and retrieve a value from - * - * @return mixed|null - */ - public function getPath($path) - { - return Utils::getPath($this->data, $path); - } - - /** - * Set a value into a nested array key. Keys will be created as needed to - * set the value. - * - * @param string $path Path to set - * @param mixed $value Value to set at the key - * - * @throws \RuntimeException when trying to setPath using a nested path - * that travels through a scalar value - */ - public function setPath($path, $value) - { - Utils::setPath($this->data, $path, $value); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/AbstractMessage.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/AbstractMessage.php deleted file mode 100644 index f118e0fe5f..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/AbstractMessage.php +++ /dev/null @@ -1,253 +0,0 @@ -getBody(); - } - - public function getProtocolVersion() - { - return $this->protocolVersion; - } - - public function getBody() - { - return $this->body; - } - - public function setBody(StreamInterface $body = null) - { - if ($body === null) { - // Setting a null body will remove the body of the request - $this->removeHeader('Content-Length'); - $this->removeHeader('Transfer-Encoding'); - } - - $this->body = $body; - } - - public function addHeader($header, $value) - { - if (is_array($value)) { - $current = array_merge($this->getHeaderAsArray($header), $value); - } else { - $current = $this->getHeaderAsArray($header); - $current[] = (string) $value; - } - - $this->setHeader($header, $current); - } - - public function addHeaders(array $headers) - { - foreach ($headers as $name => $header) { - $this->addHeader($name, $header); - } - } - - public function getHeader($header) - { - $name = strtolower($header); - return isset($this->headers[$name]) - ? implode(', ', $this->headers[$name]) - : ''; - } - - public function getHeaderAsArray($header) - { - $name = strtolower($header); - return isset($this->headers[$name]) ? $this->headers[$name] : []; - } - - public function getHeaders() - { - $headers = []; - foreach ($this->headers as $name => $values) { - $headers[$this->headerNames[$name]] = $values; - } - - return $headers; - } - - public function setHeader($header, $value) - { - $header = trim($header); - $name = strtolower($header); - $this->headerNames[$name] = $header; - - if (is_array($value)) { - foreach ($value as &$v) { - $v = trim($v); - } - $this->headers[$name] = $value; - } else { - $this->headers[$name] = [trim($value)]; - } - } - - public function setHeaders(array $headers) - { - $this->headers = $this->headerNames = []; - foreach ($headers as $key => $value) { - $this->addHeader($key, $value); - } - } - - public function hasHeader($header) - { - return isset($this->headers[strtolower($header)]); - } - - public function removeHeader($header) - { - $name = strtolower($header); - unset($this->headers[$name], $this->headerNames[$name]); - } - - /** - * Parse an array of header values containing ";" separated data into an - * array of associative arrays representing the header key value pair - * data of the header. When a parameter does not contain a value, but just - * contains a key, this function will inject a key with a '' string value. - * - * @param MessageInterface $message That contains the header - * @param string $header Header to retrieve from the message - * - * @return array Returns the parsed header values. - */ - public static function parseHeader(MessageInterface $message, $header) - { - static $trimmed = "\"' \n\t\r"; - $params = $matches = []; - - foreach (self::normalizeHeader($message, $header) as $val) { - $part = []; - foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) { - if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) { - $m = $matches[0]; - if (isset($m[1])) { - $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed); - } else { - $part[] = trim($m[0], $trimmed); - } - } - } - if ($part) { - $params[] = $part; - } - } - - return $params; - } - - /** - * Converts an array of header values that may contain comma separated - * headers into an array of headers with no comma separated values. - * - * @param MessageInterface $message That contains the header - * @param string $header Header to retrieve from the message - * - * @return array Returns the normalized header field values. - */ - public static function normalizeHeader(MessageInterface $message, $header) - { - $h = $message->getHeaderAsArray($header); - for ($i = 0, $total = count($h); $i < $total; $i++) { - if (strpos($h[$i], ',') === false) { - continue; - } - foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $h[$i]) as $v) { - $h[] = trim($v); - } - unset($h[$i]); - } - - return $h; - } - - /** - * Gets the start-line and headers of a message as a string - * - * @param MessageInterface $message - * - * @return string - */ - public static function getStartLineAndHeaders(MessageInterface $message) - { - return static::getStartLine($message) - . self::getHeadersAsString($message); - } - - /** - * Gets the headers of a message as a string - * - * @param MessageInterface $message - * - * @return string - */ - public static function getHeadersAsString(MessageInterface $message) - { - $result = ''; - foreach ($message->getHeaders() as $name => $values) { - $result .= "\r\n{$name}: " . implode(', ', $values); - } - - return $result; - } - - /** - * Gets the start line of a message - * - * @param MessageInterface $message - * - * @return string - * @throws \InvalidArgumentException - */ - public static function getStartLine(MessageInterface $message) - { - if ($message instanceof RequestInterface) { - return trim($message->getMethod() . ' ' - . $message->getResource()) - . ' HTTP/' . $message->getProtocolVersion(); - } elseif ($message instanceof ResponseInterface) { - return 'HTTP/' . $message->getProtocolVersion() . ' ' - . $message->getStatusCode() . ' ' - . $message->getReasonPhrase(); - } else { - throw new \InvalidArgumentException('Unknown message type'); - } - } - - /** - * Accepts and modifies the options provided to the message in the - * constructor. - * - * Can be overridden in subclasses as necessary. - * - * @param array $options Options array passed by reference. - */ - protected function handleOptions(array &$options) - { - if (isset($options['protocol_version'])) { - $this->protocolVersion = $options['protocol_version']; - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/AppliesHeadersInterface.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/AppliesHeadersInterface.php deleted file mode 100644 index ca42f20f30..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/AppliesHeadersInterface.php +++ /dev/null @@ -1,24 +0,0 @@ -then($onFulfilled, $onRejected, $onProgress), - [$future, 'wait'], - [$future, 'cancel'] - ); - } - - public function getStatusCode() - { - return $this->_value->getStatusCode(); - } - - public function setStatusCode($code) - { - $this->_value->setStatusCode($code); - } - - public function getReasonPhrase() - { - return $this->_value->getReasonPhrase(); - } - - public function setReasonPhrase($phrase) - { - $this->_value->setReasonPhrase($phrase); - } - - public function getEffectiveUrl() - { - return $this->_value->getEffectiveUrl(); - } - - public function setEffectiveUrl($url) - { - $this->_value->setEffectiveUrl($url); - } - - public function json(array $config = []) - { - return $this->_value->json($config); - } - - public function xml(array $config = []) - { - return $this->_value->xml($config); - } - - public function __toString() - { - try { - return $this->_value->__toString(); - } catch (\Exception $e) { - trigger_error($e->getMessage(), E_USER_WARNING); - return ''; - } - } - - public function getProtocolVersion() - { - return $this->_value->getProtocolVersion(); - } - - public function setBody(StreamInterface $body = null) - { - $this->_value->setBody($body); - } - - public function getBody() - { - return $this->_value->getBody(); - } - - public function getHeaders() - { - return $this->_value->getHeaders(); - } - - public function getHeader($header) - { - return $this->_value->getHeader($header); - } - - public function getHeaderAsArray($header) - { - return $this->_value->getHeaderAsArray($header); - } - - public function hasHeader($header) - { - return $this->_value->hasHeader($header); - } - - public function removeHeader($header) - { - $this->_value->removeHeader($header); - } - - public function addHeader($header, $value) - { - $this->_value->addHeader($header, $value); - } - - public function addHeaders(array $headers) - { - $this->_value->addHeaders($headers); - } - - public function setHeader($header, $value) - { - $this->_value->setHeader($header, $value); - } - - public function setHeaders(array $headers) - { - $this->_value->setHeaders($headers); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/MessageFactory.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/MessageFactory.php deleted file mode 100644 index d469ef1e6b..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/MessageFactory.php +++ /dev/null @@ -1,364 +0,0 @@ - 1, 'timeout' => 1, 'verify' => 1, 'ssl_key' => 1, - 'cert' => 1, 'proxy' => 1, 'debug' => 1, 'save_to' => 1, 'stream' => 1, - 'expect' => 1, 'future' => 1 - ]; - - /** @var array Default allow_redirects request option settings */ - private static $defaultRedirect = [ - 'max' => 5, - 'strict' => false, - 'referer' => false, - 'protocols' => ['http', 'https'] - ]; - - /** - * @param array $customOptions Associative array of custom request option - * names mapping to functions used to apply - * the option. The function accepts the request - * and the option value to apply. - */ - public function __construct(array $customOptions = []) - { - $this->errorPlugin = new HttpError(); - $this->redirectPlugin = new Redirect(); - $this->customOptions = $customOptions; - } - - public function createResponse( - $statusCode, - array $headers = [], - $body = null, - array $options = [] - ) { - if (null !== $body) { - $body = Stream::factory($body); - } - - return new Response($statusCode, $headers, $body, $options); - } - - public function createRequest($method, $url, array $options = []) - { - // Handle the request protocol version option that needs to be - // specified in the request constructor. - if (isset($options['version'])) { - $options['config']['protocol_version'] = $options['version']; - unset($options['version']); - } - - $request = new Request($method, $url, [], null, - isset($options['config']) ? $options['config'] : []); - - unset($options['config']); - - // Use a POST body by default - if (strtoupper($method) == 'POST' - && !isset($options['body']) - && !isset($options['json']) - ) { - $options['body'] = []; - } - - if ($options) { - $this->applyOptions($request, $options); - } - - return $request; - } - - /** - * Create a request or response object from an HTTP message string - * - * @param string $message Message to parse - * - * @return RequestInterface|ResponseInterface - * @throws \InvalidArgumentException if unable to parse a message - */ - public function fromMessage($message) - { - static $parser; - if (!$parser) { - $parser = new MessageParser(); - } - - // Parse a response - if (strtoupper(substr($message, 0, 4)) == 'HTTP') { - $data = $parser->parseResponse($message); - return $this->createResponse( - $data['code'], - $data['headers'], - $data['body'] === '' ? null : $data['body'], - $data - ); - } - - // Parse a request - if (!($data = ($parser->parseRequest($message)))) { - throw new \InvalidArgumentException('Unable to parse request'); - } - - return $this->createRequest( - $data['method'], - Url::buildUrl($data['request_url']), - [ - 'headers' => $data['headers'], - 'body' => $data['body'] === '' ? null : $data['body'], - 'config' => [ - 'protocol_version' => $data['protocol_version'] - ] - ] - ); - } - - /** - * Apply POST fields and files to a request to attempt to give an accurate - * representation. - * - * @param RequestInterface $request Request to update - * @param array $body Body to apply - */ - protected function addPostData(RequestInterface $request, array $body) - { - static $fields = ['string' => true, 'array' => true, 'NULL' => true, - 'boolean' => true, 'double' => true, 'integer' => true]; - - $post = new PostBody(); - foreach ($body as $key => $value) { - if (isset($fields[gettype($value)])) { - $post->setField($key, $value); - } elseif ($value instanceof PostFileInterface) { - $post->addFile($value); - } else { - $post->addFile(new PostFile($key, $value)); - } - } - - if ($request->getHeader('Content-Type') == 'multipart/form-data') { - $post->forceMultipartUpload(true); - } - - $request->setBody($post); - } - - protected function applyOptions( - RequestInterface $request, - array $options = [] - ) { - $config = $request->getConfig(); - $emitter = $request->getEmitter(); - - foreach ($options as $key => $value) { - - if (isset(self::$configMap[$key])) { - $config[$key] = $value; - continue; - } - - switch ($key) { - - case 'allow_redirects': - - if ($value === false) { - continue; - } - - if ($value === true) { - $value = self::$defaultRedirect; - } elseif (!is_array($value)) { - throw new Iae('allow_redirects must be true, false, or array'); - } else { - // Merge the default settings with the provided settings - $value += self::$defaultRedirect; - } - - $config['redirect'] = $value; - $emitter->attach($this->redirectPlugin); - break; - - case 'decode_content': - - if ($value === false) { - continue; - } - - $config['decode_content'] = true; - if ($value !== true) { - $request->setHeader('Accept-Encoding', $value); - } - break; - - case 'headers': - - if (!is_array($value)) { - throw new Iae('header value must be an array'); - } - foreach ($value as $k => $v) { - $request->setHeader($k, $v); - } - break; - - case 'exceptions': - - if ($value === true) { - $emitter->attach($this->errorPlugin); - } - break; - - case 'body': - - if (is_array($value)) { - $this->addPostData($request, $value); - } elseif ($value !== null) { - $request->setBody(Stream::factory($value)); - } - break; - - case 'auth': - - if (!$value) { - continue; - } - - if (is_array($value)) { - $type = isset($value[2]) ? strtolower($value[2]) : 'basic'; - } else { - $type = strtolower($value); - } - - $config['auth'] = $value; - - if ($type == 'basic') { - $request->setHeader( - 'Authorization', - 'Basic ' . base64_encode("$value[0]:$value[1]") - ); - } elseif ($type == 'digest') { - // @todo: Do not rely on curl - $config->setPath('curl/' . CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); - $config->setPath('curl/' . CURLOPT_USERPWD, "$value[0]:$value[1]"); - } - break; - - case 'query': - - if ($value instanceof Query) { - $original = $request->getQuery(); - // Do not overwrite existing query string variables by - // overwriting the object with the query string data passed - // in the URL - $value->overwriteWith($original->toArray()); - $request->setQuery($value); - } elseif (is_array($value)) { - // Do not overwrite existing query string variables - $query = $request->getQuery(); - foreach ($value as $k => $v) { - if (!isset($query[$k])) { - $query[$k] = $v; - } - } - } else { - throw new Iae('query must be an array or Query object'); - } - break; - - case 'cookies': - - if ($value === true) { - static $cookie = null; - if (!$cookie) { - $cookie = new Cookie(); - } - $emitter->attach($cookie); - } elseif (is_array($value)) { - $emitter->attach( - new Cookie(CookieJar::fromArray($value, $request->getHost())) - ); - } elseif ($value instanceof CookieJarInterface) { - $emitter->attach(new Cookie($value)); - } elseif ($value !== false) { - throw new Iae('cookies must be an array, true, or CookieJarInterface'); - } - break; - - case 'events': - - if (!is_array($value)) { - throw new Iae('events must be an array'); - } - - $this->attachListeners($request, - $this->prepareListeners( - $value, - ['before', 'complete', 'error', 'progress', 'end'] - ) - ); - break; - - case 'subscribers': - - if (!is_array($value)) { - throw new Iae('subscribers must be an array'); - } - - foreach ($value as $subscribers) { - $emitter->attach($subscribers); - } - break; - - case 'json': - - $request->setBody(Stream::factory(json_encode($value))); - if (!$request->hasHeader('Content-Type')) { - $request->setHeader('Content-Type', 'application/json'); - } - break; - - default: - - // Check for custom handler functions. - if (isset($this->customOptions[$key])) { - $fn = $this->customOptions[$key]; - $fn($request, $value); - continue; - } - - throw new Iae("No method can handle the {$key} config key"); - } - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/MessageFactoryInterface.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/MessageFactoryInterface.php deleted file mode 100644 index 86ae9c7ee0..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/MessageFactoryInterface.php +++ /dev/null @@ -1,71 +0,0 @@ -getHeaders() as $name => $values) { - * echo $name . ": " . implode(", ", $values); - * } - * - * @return array Returns an associative array of the message's headers. - */ - public function getHeaders(); - - /** - * Retrieve a header by the given case-insensitive name. - * - * @param string $header Case-insensitive header name. - * - * @return string - */ - public function getHeader($header); - - /** - * Retrieves a header by the given case-insensitive name as an array of strings. - * - * @param string $header Case-insensitive header name. - * - * @return string[] - */ - public function getHeaderAsArray($header); - - /** - * Checks if a header exists by the given case-insensitive name. - * - * @param string $header Case-insensitive header name. - * - * @return bool Returns true if any header names match the given header - * name using a case-insensitive string comparison. Returns false if - * no matching header name is found in the message. - */ - public function hasHeader($header); - - /** - * Remove a specific header by case-insensitive name. - * - * @param string $header Case-insensitive header name. - */ - public function removeHeader($header); - - /** - * Appends a header value to any existing values associated with the - * given header name. - * - * @param string $header Header name to add - * @param string $value Value of the header - */ - public function addHeader($header, $value); - - /** - * Merges in an associative array of headers. - * - * Each array key MUST be a string representing the case-insensitive name - * of a header. Each value MUST be either a string or an array of strings. - * For each value, the value is appended to any existing header of the same - * name, or, if a header does not already exist by the given name, then the - * header is added. - * - * @param array $headers Associative array of headers to add to the message - */ - public function addHeaders(array $headers); - - /** - * Sets a header, replacing any existing values of any headers with the - * same case-insensitive name. - * - * The header values MUST be a string or an array of strings. - * - * @param string $header Header name - * @param string|array $value Header value(s) - */ - public function setHeader($header, $value); - - /** - * Sets headers, replacing any headers that have already been set on the - * message. - * - * The array keys MUST be a string. The array values must be either a - * string or an array of strings. - * - * @param array $headers Headers to set. - */ - public function setHeaders(array $headers); -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/MessageParser.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/MessageParser.php deleted file mode 100644 index c3cc195e32..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/MessageParser.php +++ /dev/null @@ -1,171 +0,0 @@ -parseMessage($message))) { - return false; - } - - // Parse the protocol and protocol version - if (isset($parts['start_line'][2])) { - $startParts = explode('/', $parts['start_line'][2]); - $protocol = strtoupper($startParts[0]); - $version = isset($startParts[1]) ? $startParts[1] : '1.1'; - } else { - $protocol = 'HTTP'; - $version = '1.1'; - } - - $parsed = [ - 'method' => strtoupper($parts['start_line'][0]), - 'protocol' => $protocol, - 'protocol_version' => $version, - 'headers' => $parts['headers'], - 'body' => $parts['body'] - ]; - - $parsed['request_url'] = $this->getUrlPartsFromMessage( - (isset($parts['start_line'][1]) ? $parts['start_line'][1] : ''), $parsed); - - return $parsed; - } - - /** - * Parse an HTTP response message into an associative array of parts. - * - * @param string $message HTTP response to parse - * - * @return array|bool Returns false if the message is invalid - */ - public function parseResponse($message) - { - if (!($parts = $this->parseMessage($message))) { - return false; - } - - list($protocol, $version) = explode('/', trim($parts['start_line'][0])); - - return [ - 'protocol' => $protocol, - 'protocol_version' => $version, - 'code' => $parts['start_line'][1], - 'reason_phrase' => isset($parts['start_line'][2]) ? $parts['start_line'][2] : '', - 'headers' => $parts['headers'], - 'body' => $parts['body'] - ]; - } - - /** - * Parse a message into parts - * - * @param string $message Message to parse - * - * @return array|bool - */ - private function parseMessage($message) - { - if (!$message) { - return false; - } - - $startLine = null; - $headers = []; - $body = ''; - - // Iterate over each line in the message, accounting for line endings - $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE); - for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) { - - $line = $lines[$i]; - - // If two line breaks were encountered, then this is the end of body - if (empty($line)) { - if ($i < $totalLines - 1) { - $body = implode('', array_slice($lines, $i + 2)); - } - break; - } - - // Parse message headers - if (!$startLine) { - $startLine = explode(' ', $line, 3); - } elseif (strpos($line, ':')) { - $parts = explode(':', $line, 2); - $key = trim($parts[0]); - $value = isset($parts[1]) ? trim($parts[1]) : ''; - if (!isset($headers[$key])) { - $headers[$key] = $value; - } elseif (!is_array($headers[$key])) { - $headers[$key] = [$headers[$key], $value]; - } else { - $headers[$key][] = $value; - } - } - } - - return [ - 'start_line' => $startLine, - 'headers' => $headers, - 'body' => $body - ]; - } - - /** - * Create URL parts from HTTP message parts - * - * @param string $requestUrl Associated URL - * @param array $parts HTTP message parts - * - * @return array - */ - private function getUrlPartsFromMessage($requestUrl, array $parts) - { - // Parse the URL information from the message - $urlParts = ['path' => $requestUrl, 'scheme' => 'http']; - - // Check for the Host header - if (isset($parts['headers']['Host'])) { - $urlParts['host'] = $parts['headers']['Host']; - } elseif (isset($parts['headers']['host'])) { - $urlParts['host'] = $parts['headers']['host']; - } else { - $urlParts['host'] = null; - } - - if (false === strpos($urlParts['host'], ':')) { - $urlParts['port'] = ''; - } else { - $hostParts = explode(':', $urlParts['host']); - $urlParts['host'] = trim($hostParts[0]); - $urlParts['port'] = (int) trim($hostParts[1]); - if ($urlParts['port'] == 443) { - $urlParts['scheme'] = 'https'; - } - } - - // Check if a query is present - $path = $urlParts['path']; - $qpos = strpos($path, '?'); - if ($qpos) { - $urlParts['query'] = substr($path, $qpos + 1); - $urlParts['path'] = substr($path, 0, $qpos); - } else { - $urlParts['query'] = ''; - } - - return $urlParts; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/Request.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/Request.php deleted file mode 100644 index 38714af80f..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/Request.php +++ /dev/null @@ -1,195 +0,0 @@ -setUrl($url); - $this->method = strtoupper($method); - $this->handleOptions($options); - $this->transferOptions = new Collection($options); - $this->addPrepareEvent(); - - if ($body !== null) { - $this->setBody($body); - } - - if ($headers) { - foreach ($headers as $key => $value) { - $this->addHeader($key, $value); - } - } - } - - public function __clone() - { - if ($this->emitter) { - $this->emitter = clone $this->emitter; - } - $this->transferOptions = clone $this->transferOptions; - $this->url = clone $this->url; - } - - public function setUrl($url) - { - $this->url = $url instanceof Url ? $url : Url::fromString($url); - $this->updateHostHeaderFromUrl(); - } - - public function getUrl() - { - return (string) $this->url; - } - - public function setQuery($query) - { - $this->url->setQuery($query); - } - - public function getQuery() - { - return $this->url->getQuery(); - } - - public function setMethod($method) - { - $this->method = strtoupper($method); - } - - public function getMethod() - { - return $this->method; - } - - public function getScheme() - { - return $this->url->getScheme(); - } - - public function setScheme($scheme) - { - $this->url->setScheme($scheme); - } - - public function getPort() - { - return $this->url->getPort(); - } - - public function setPort($port) - { - $this->url->setPort($port); - $this->updateHostHeaderFromUrl(); - } - - public function getHost() - { - return $this->url->getHost(); - } - - public function setHost($host) - { - $this->url->setHost($host); - $this->updateHostHeaderFromUrl(); - } - - public function getPath() - { - return '/' . ltrim($this->url->getPath(), '/'); - } - - public function setPath($path) - { - $this->url->setPath($path); - } - - public function getResource() - { - $resource = $this->getPath(); - if ($query = (string) $this->url->getQuery()) { - $resource .= '?' . $query; - } - - return $resource; - } - - public function getConfig() - { - return $this->transferOptions; - } - - protected function handleOptions(array &$options) - { - parent::handleOptions($options); - // Use a custom emitter if one is specified, and remove it from - // options that are exposed through getConfig() - if (isset($options['emitter'])) { - $this->emitter = $options['emitter']; - unset($options['emitter']); - } - } - - /** - * Adds a subscriber that ensures a request's body is prepared before - * sending. - */ - private function addPrepareEvent() - { - static $subscriber; - if (!$subscriber) { - $subscriber = new Prepare(); - } - - $this->getEmitter()->attach($subscriber); - } - - private function updateHostHeaderFromUrl() - { - $port = $this->url->getPort(); - $scheme = $this->url->getScheme(); - if ($host = $this->url->getHost()) { - if (($port == 80 && $scheme == 'http') || - ($port == 443 && $scheme == 'https') - ) { - $this->setHeader('Host', $host); - } else { - $this->setHeader('Host', "{$host}:{$port}"); - } - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/RequestInterface.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/RequestInterface.php deleted file mode 100644 index f6a69d1e1f..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/RequestInterface.php +++ /dev/null @@ -1,136 +0,0 @@ - 'Continue', - 101 => 'Switching Protocols', - 102 => 'Processing', - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - 207 => 'Multi-Status', - 208 => 'Already Reported', - 226 => 'IM Used', - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 307 => 'Temporary Redirect', - 308 => 'Permanent Redirect', - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Long', - 415 => 'Unsupported Media Type', - 416 => 'Requested Range Not Satisfiable', - 417 => 'Expectation Failed', - 422 => 'Unprocessable Entity', - 423 => 'Locked', - 424 => 'Failed Dependency', - 425 => 'Reserved for WebDAV advanced collections expired proposal', - 426 => 'Upgrade required', - 428 => 'Precondition Required', - 429 => 'Too Many Requests', - 431 => 'Request Header Fields Too Large', - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported', - 506 => 'Variant Also Negotiates (Experimental)', - 507 => 'Insufficient Storage', - 508 => 'Loop Detected', - 510 => 'Not Extended', - 511 => 'Network Authentication Required', - ]; - - /** @var string The reason phrase of the response (human readable code) */ - private $reasonPhrase; - - /** @var string The status code of the response */ - private $statusCode; - - /** @var string The effective URL that returned this response */ - private $effectiveUrl; - - /** - * @param int|string $statusCode The response status code (e.g. 200) - * @param array $headers The response headers - * @param StreamInterface $body The body of the response - * @param array $options Response message options - * - reason_phrase: Set a custom reason phrase - * - protocol_version: Set a custom protocol version - */ - public function __construct( - $statusCode, - array $headers = [], - StreamInterface $body = null, - array $options = [] - ) { - $this->statusCode = (int) $statusCode; - $this->handleOptions($options); - - // Assume a reason phrase if one was not applied as an option - if (!$this->reasonPhrase && - isset(self::$statusTexts[$this->statusCode]) - ) { - $this->reasonPhrase = self::$statusTexts[$this->statusCode]; - } - - if ($headers) { - $this->setHeaders($headers); - } - - if ($body) { - $this->setBody($body); - } - } - - public function getStatusCode() - { - return $this->statusCode; - } - - public function setStatusCode($code) - { - return $this->statusCode = (int) $code; - } - - public function getReasonPhrase() - { - return $this->reasonPhrase; - } - - public function setReasonPhrase($phrase) - { - return $this->reasonPhrase = $phrase; - } - - public function json(array $config = []) - { - try { - return Utils::jsonDecode( - (string) $this->getBody(), - isset($config['object']) ? !$config['object'] : true, - 512, - isset($config['big_int_strings']) ? JSON_BIGINT_AS_STRING : 0 - ); - } catch (\InvalidArgumentException $e) { - throw new ParseException( - $e->getMessage(), - $this - ); - } - } - - public function xml(array $config = []) - { - $disableEntities = libxml_disable_entity_loader(true); - $internalErrors = libxml_use_internal_errors(true); - - try { - // Allow XML to be retrieved even if there is no response body - $xml = new \SimpleXMLElement( - (string) $this->getBody() ?: '', - isset($config['libxml_options']) ? $config['libxml_options'] : LIBXML_NONET, - false, - isset($config['ns']) ? $config['ns'] : '', - isset($config['ns_is_prefix']) ? $config['ns_is_prefix'] : false - ); - libxml_disable_entity_loader($disableEntities); - libxml_use_internal_errors($internalErrors); - } catch (\Exception $e) { - libxml_disable_entity_loader($disableEntities); - libxml_use_internal_errors($internalErrors); - throw new XmlParseException( - 'Unable to parse response body into XML: ' . $e->getMessage(), - $this, - $e, - (libxml_get_last_error()) ?: null - ); - } - - return $xml; - } - - public function getEffectiveUrl() - { - return $this->effectiveUrl; - } - - public function setEffectiveUrl($url) - { - $this->effectiveUrl = $url; - } - - /** - * Accepts and modifies the options provided to the response in the - * constructor. - * - * @param array $options Options array passed by reference. - */ - protected function handleOptions(array &$options = []) - { - parent::handleOptions($options); - if (isset($options['reason_phrase'])) { - $this->reasonPhrase = $options['reason_phrase']; - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/ResponseInterface.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/ResponseInterface.php deleted file mode 100644 index c0ae9be93b..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Message/ResponseInterface.php +++ /dev/null @@ -1,111 +0,0 @@ - 'text/vnd.in3d.3dml', - '3g2' => 'video/3gpp2', - '3gp' => 'video/3gpp', - '7z' => 'application/x-7z-compressed', - 'aab' => 'application/x-authorware-bin', - 'aac' => 'audio/x-aac', - 'aam' => 'application/x-authorware-map', - 'aas' => 'application/x-authorware-seg', - 'abw' => 'application/x-abiword', - 'ac' => 'application/pkix-attr-cert', - 'acc' => 'application/vnd.americandynamics.acc', - 'ace' => 'application/x-ace-compressed', - 'acu' => 'application/vnd.acucobol', - 'acutc' => 'application/vnd.acucorp', - 'adp' => 'audio/adpcm', - 'aep' => 'application/vnd.audiograph', - 'afm' => 'application/x-font-type1', - 'afp' => 'application/vnd.ibm.modcap', - 'ahead' => 'application/vnd.ahead.space', - 'ai' => 'application/postscript', - 'aif' => 'audio/x-aiff', - 'aifc' => 'audio/x-aiff', - 'aiff' => 'audio/x-aiff', - 'air' => 'application/vnd.adobe.air-application-installer-package+zip', - 'ait' => 'application/vnd.dvb.ait', - 'ami' => 'application/vnd.amiga.ami', - 'apk' => 'application/vnd.android.package-archive', - 'application' => 'application/x-ms-application', - 'apr' => 'application/vnd.lotus-approach', - 'asa' => 'text/plain', - 'asax' => 'application/octet-stream', - 'asc' => 'application/pgp-signature', - 'ascx' => 'text/plain', - 'asf' => 'video/x-ms-asf', - 'ashx' => 'text/plain', - 'asm' => 'text/x-asm', - 'asmx' => 'text/plain', - 'aso' => 'application/vnd.accpac.simply.aso', - 'asp' => 'text/plain', - 'aspx' => 'text/plain', - 'asx' => 'video/x-ms-asf', - 'atc' => 'application/vnd.acucorp', - 'atom' => 'application/atom+xml', - 'atomcat' => 'application/atomcat+xml', - 'atomsvc' => 'application/atomsvc+xml', - 'atx' => 'application/vnd.antix.game-component', - 'au' => 'audio/basic', - 'avi' => 'video/x-msvideo', - 'aw' => 'application/applixware', - 'axd' => 'text/plain', - 'azf' => 'application/vnd.airzip.filesecure.azf', - 'azs' => 'application/vnd.airzip.filesecure.azs', - 'azw' => 'application/vnd.amazon.ebook', - 'bat' => 'application/x-msdownload', - 'bcpio' => 'application/x-bcpio', - 'bdf' => 'application/x-font-bdf', - 'bdm' => 'application/vnd.syncml.dm+wbxml', - 'bed' => 'application/vnd.realvnc.bed', - 'bh2' => 'application/vnd.fujitsu.oasysprs', - 'bin' => 'application/octet-stream', - 'bmi' => 'application/vnd.bmi', - 'bmp' => 'image/bmp', - 'book' => 'application/vnd.framemaker', - 'box' => 'application/vnd.previewsystems.box', - 'boz' => 'application/x-bzip2', - 'bpk' => 'application/octet-stream', - 'btif' => 'image/prs.btif', - 'bz' => 'application/x-bzip', - 'bz2' => 'application/x-bzip2', - 'c' => 'text/x-c', - 'c11amc' => 'application/vnd.cluetrust.cartomobile-config', - 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg', - 'c4d' => 'application/vnd.clonk.c4group', - 'c4f' => 'application/vnd.clonk.c4group', - 'c4g' => 'application/vnd.clonk.c4group', - 'c4p' => 'application/vnd.clonk.c4group', - 'c4u' => 'application/vnd.clonk.c4group', - 'cab' => 'application/vnd.ms-cab-compressed', - 'car' => 'application/vnd.curl.car', - 'cat' => 'application/vnd.ms-pki.seccat', - 'cc' => 'text/x-c', - 'cct' => 'application/x-director', - 'ccxml' => 'application/ccxml+xml', - 'cdbcmsg' => 'application/vnd.contact.cmsg', - 'cdf' => 'application/x-netcdf', - 'cdkey' => 'application/vnd.mediastation.cdkey', - 'cdmia' => 'application/cdmi-capability', - 'cdmic' => 'application/cdmi-container', - 'cdmid' => 'application/cdmi-domain', - 'cdmio' => 'application/cdmi-object', - 'cdmiq' => 'application/cdmi-queue', - 'cdx' => 'chemical/x-cdx', - 'cdxml' => 'application/vnd.chemdraw+xml', - 'cdy' => 'application/vnd.cinderella', - 'cer' => 'application/pkix-cert', - 'cfc' => 'application/x-coldfusion', - 'cfm' => 'application/x-coldfusion', - 'cgm' => 'image/cgm', - 'chat' => 'application/x-chat', - 'chm' => 'application/vnd.ms-htmlhelp', - 'chrt' => 'application/vnd.kde.kchart', - 'cif' => 'chemical/x-cif', - 'cii' => 'application/vnd.anser-web-certificate-issue-initiation', - 'cil' => 'application/vnd.ms-artgalry', - 'cla' => 'application/vnd.claymore', - 'class' => 'application/java-vm', - 'clkk' => 'application/vnd.crick.clicker.keyboard', - 'clkp' => 'application/vnd.crick.clicker.palette', - 'clkt' => 'application/vnd.crick.clicker.template', - 'clkw' => 'application/vnd.crick.clicker.wordbank', - 'clkx' => 'application/vnd.crick.clicker', - 'clp' => 'application/x-msclip', - 'cmc' => 'application/vnd.cosmocaller', - 'cmdf' => 'chemical/x-cmdf', - 'cml' => 'chemical/x-cml', - 'cmp' => 'application/vnd.yellowriver-custom-menu', - 'cmx' => 'image/x-cmx', - 'cod' => 'application/vnd.rim.cod', - 'com' => 'application/x-msdownload', - 'conf' => 'text/plain', - 'cpio' => 'application/x-cpio', - 'cpp' => 'text/x-c', - 'cpt' => 'application/mac-compactpro', - 'crd' => 'application/x-mscardfile', - 'crl' => 'application/pkix-crl', - 'crt' => 'application/x-x509-ca-cert', - 'cryptonote' => 'application/vnd.rig.cryptonote', - 'cs' => 'text/plain', - 'csh' => 'application/x-csh', - 'csml' => 'chemical/x-csml', - 'csp' => 'application/vnd.commonspace', - 'css' => 'text/css', - 'cst' => 'application/x-director', - 'csv' => 'text/csv', - 'cu' => 'application/cu-seeme', - 'curl' => 'text/vnd.curl', - 'cww' => 'application/prs.cww', - 'cxt' => 'application/x-director', - 'cxx' => 'text/x-c', - 'dae' => 'model/vnd.collada+xml', - 'daf' => 'application/vnd.mobius.daf', - 'dataless' => 'application/vnd.fdsn.seed', - 'davmount' => 'application/davmount+xml', - 'dcr' => 'application/x-director', - 'dcurl' => 'text/vnd.curl.dcurl', - 'dd2' => 'application/vnd.oma.dd2+xml', - 'ddd' => 'application/vnd.fujixerox.ddd', - 'deb' => 'application/x-debian-package', - 'def' => 'text/plain', - 'deploy' => 'application/octet-stream', - 'der' => 'application/x-x509-ca-cert', - 'dfac' => 'application/vnd.dreamfactory', - 'dic' => 'text/x-c', - 'dir' => 'application/x-director', - 'dis' => 'application/vnd.mobius.dis', - 'dist' => 'application/octet-stream', - 'distz' => 'application/octet-stream', - 'djv' => 'image/vnd.djvu', - 'djvu' => 'image/vnd.djvu', - 'dll' => 'application/x-msdownload', - 'dmg' => 'application/octet-stream', - 'dms' => 'application/octet-stream', - 'dna' => 'application/vnd.dna', - 'doc' => 'application/msword', - 'docm' => 'application/vnd.ms-word.document.macroenabled.12', - 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'dot' => 'application/msword', - 'dotm' => 'application/vnd.ms-word.template.macroenabled.12', - 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', - 'dp' => 'application/vnd.osgi.dp', - 'dpg' => 'application/vnd.dpgraph', - 'dra' => 'audio/vnd.dra', - 'dsc' => 'text/prs.lines.tag', - 'dssc' => 'application/dssc+der', - 'dtb' => 'application/x-dtbook+xml', - 'dtd' => 'application/xml-dtd', - 'dts' => 'audio/vnd.dts', - 'dtshd' => 'audio/vnd.dts.hd', - 'dump' => 'application/octet-stream', - 'dvi' => 'application/x-dvi', - 'dwf' => 'model/vnd.dwf', - 'dwg' => 'image/vnd.dwg', - 'dxf' => 'image/vnd.dxf', - 'dxp' => 'application/vnd.spotfire.dxp', - 'dxr' => 'application/x-director', - 'ecelp4800' => 'audio/vnd.nuera.ecelp4800', - 'ecelp7470' => 'audio/vnd.nuera.ecelp7470', - 'ecelp9600' => 'audio/vnd.nuera.ecelp9600', - 'ecma' => 'application/ecmascript', - 'edm' => 'application/vnd.novadigm.edm', - 'edx' => 'application/vnd.novadigm.edx', - 'efif' => 'application/vnd.picsel', - 'ei6' => 'application/vnd.pg.osasli', - 'elc' => 'application/octet-stream', - 'eml' => 'message/rfc822', - 'emma' => 'application/emma+xml', - 'eol' => 'audio/vnd.digital-winds', - 'eot' => 'application/vnd.ms-fontobject', - 'eps' => 'application/postscript', - 'epub' => 'application/epub+zip', - 'es3' => 'application/vnd.eszigno3+xml', - 'esf' => 'application/vnd.epson.esf', - 'et3' => 'application/vnd.eszigno3+xml', - 'etx' => 'text/x-setext', - 'exe' => 'application/x-msdownload', - 'exi' => 'application/exi', - 'ext' => 'application/vnd.novadigm.ext', - 'ez' => 'application/andrew-inset', - 'ez2' => 'application/vnd.ezpix-album', - 'ez3' => 'application/vnd.ezpix-package', - 'f' => 'text/x-fortran', - 'f4v' => 'video/x-f4v', - 'f77' => 'text/x-fortran', - 'f90' => 'text/x-fortran', - 'fbs' => 'image/vnd.fastbidsheet', - 'fcs' => 'application/vnd.isac.fcs', - 'fdf' => 'application/vnd.fdf', - 'fe_launch' => 'application/vnd.denovo.fcselayout-link', - 'fg5' => 'application/vnd.fujitsu.oasysgp', - 'fgd' => 'application/x-director', - 'fh' => 'image/x-freehand', - 'fh4' => 'image/x-freehand', - 'fh5' => 'image/x-freehand', - 'fh7' => 'image/x-freehand', - 'fhc' => 'image/x-freehand', - 'fig' => 'application/x-xfig', - 'fli' => 'video/x-fli', - 'flo' => 'application/vnd.micrografx.flo', - 'flv' => 'video/x-flv', - 'flw' => 'application/vnd.kde.kivio', - 'flx' => 'text/vnd.fmi.flexstor', - 'fly' => 'text/vnd.fly', - 'fm' => 'application/vnd.framemaker', - 'fnc' => 'application/vnd.frogans.fnc', - 'for' => 'text/x-fortran', - 'fpx' => 'image/vnd.fpx', - 'frame' => 'application/vnd.framemaker', - 'fsc' => 'application/vnd.fsc.weblaunch', - 'fst' => 'image/vnd.fst', - 'ftc' => 'application/vnd.fluxtime.clip', - 'fti' => 'application/vnd.anser-web-funds-transfer-initiation', - 'fvt' => 'video/vnd.fvt', - 'fxp' => 'application/vnd.adobe.fxp', - 'fxpl' => 'application/vnd.adobe.fxp', - 'fzs' => 'application/vnd.fuzzysheet', - 'g2w' => 'application/vnd.geoplan', - 'g3' => 'image/g3fax', - 'g3w' => 'application/vnd.geospace', - 'gac' => 'application/vnd.groove-account', - 'gdl' => 'model/vnd.gdl', - 'geo' => 'application/vnd.dynageo', - 'gex' => 'application/vnd.geometry-explorer', - 'ggb' => 'application/vnd.geogebra.file', - 'ggt' => 'application/vnd.geogebra.tool', - 'ghf' => 'application/vnd.groove-help', - 'gif' => 'image/gif', - 'gim' => 'application/vnd.groove-identity-message', - 'gmx' => 'application/vnd.gmx', - 'gnumeric' => 'application/x-gnumeric', - 'gph' => 'application/vnd.flographit', - 'gqf' => 'application/vnd.grafeq', - 'gqs' => 'application/vnd.grafeq', - 'gram' => 'application/srgs', - 'gre' => 'application/vnd.geometry-explorer', - 'grv' => 'application/vnd.groove-injector', - 'grxml' => 'application/srgs+xml', - 'gsf' => 'application/x-font-ghostscript', - 'gtar' => 'application/x-gtar', - 'gtm' => 'application/vnd.groove-tool-message', - 'gtw' => 'model/vnd.gtw', - 'gv' => 'text/vnd.graphviz', - 'gxt' => 'application/vnd.geonext', - 'h' => 'text/x-c', - 'h261' => 'video/h261', - 'h263' => 'video/h263', - 'h264' => 'video/h264', - 'hal' => 'application/vnd.hal+xml', - 'hbci' => 'application/vnd.hbci', - 'hdf' => 'application/x-hdf', - 'hh' => 'text/x-c', - 'hlp' => 'application/winhlp', - 'hpgl' => 'application/vnd.hp-hpgl', - 'hpid' => 'application/vnd.hp-hpid', - 'hps' => 'application/vnd.hp-hps', - 'hqx' => 'application/mac-binhex40', - 'hta' => 'application/octet-stream', - 'htc' => 'text/html', - 'htke' => 'application/vnd.kenameaapp', - 'htm' => 'text/html', - 'html' => 'text/html', - 'hvd' => 'application/vnd.yamaha.hv-dic', - 'hvp' => 'application/vnd.yamaha.hv-voice', - 'hvs' => 'application/vnd.yamaha.hv-script', - 'i2g' => 'application/vnd.intergeo', - 'icc' => 'application/vnd.iccprofile', - 'ice' => 'x-conference/x-cooltalk', - 'icm' => 'application/vnd.iccprofile', - 'ico' => 'image/x-icon', - 'ics' => 'text/calendar', - 'ief' => 'image/ief', - 'ifb' => 'text/calendar', - 'ifm' => 'application/vnd.shana.informed.formdata', - 'iges' => 'model/iges', - 'igl' => 'application/vnd.igloader', - 'igm' => 'application/vnd.insors.igm', - 'igs' => 'model/iges', - 'igx' => 'application/vnd.micrografx.igx', - 'iif' => 'application/vnd.shana.informed.interchange', - 'imp' => 'application/vnd.accpac.simply.imp', - 'ims' => 'application/vnd.ms-ims', - 'in' => 'text/plain', - 'ini' => 'text/plain', - 'ipfix' => 'application/ipfix', - 'ipk' => 'application/vnd.shana.informed.package', - 'irm' => 'application/vnd.ibm.rights-management', - 'irp' => 'application/vnd.irepository.package+xml', - 'iso' => 'application/octet-stream', - 'itp' => 'application/vnd.shana.informed.formtemplate', - 'ivp' => 'application/vnd.immervision-ivp', - 'ivu' => 'application/vnd.immervision-ivu', - 'jad' => 'text/vnd.sun.j2me.app-descriptor', - 'jam' => 'application/vnd.jam', - 'jar' => 'application/java-archive', - 'java' => 'text/x-java-source', - 'jisp' => 'application/vnd.jisp', - 'jlt' => 'application/vnd.hp-jlyt', - 'jnlp' => 'application/x-java-jnlp-file', - 'joda' => 'application/vnd.joost.joda-archive', - 'jpe' => 'image/jpeg', - 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'jpgm' => 'video/jpm', - 'jpgv' => 'video/jpeg', - 'jpm' => 'video/jpm', - 'js' => 'text/javascript', - 'json' => 'application/json', - 'kar' => 'audio/midi', - 'karbon' => 'application/vnd.kde.karbon', - 'kfo' => 'application/vnd.kde.kformula', - 'kia' => 'application/vnd.kidspiration', - 'kml' => 'application/vnd.google-earth.kml+xml', - 'kmz' => 'application/vnd.google-earth.kmz', - 'kne' => 'application/vnd.kinar', - 'knp' => 'application/vnd.kinar', - 'kon' => 'application/vnd.kde.kontour', - 'kpr' => 'application/vnd.kde.kpresenter', - 'kpt' => 'application/vnd.kde.kpresenter', - 'ksp' => 'application/vnd.kde.kspread', - 'ktr' => 'application/vnd.kahootz', - 'ktx' => 'image/ktx', - 'ktz' => 'application/vnd.kahootz', - 'kwd' => 'application/vnd.kde.kword', - 'kwt' => 'application/vnd.kde.kword', - 'lasxml' => 'application/vnd.las.las+xml', - 'latex' => 'application/x-latex', - 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop', - 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml', - 'les' => 'application/vnd.hhe.lesson-player', - 'lha' => 'application/octet-stream', - 'link66' => 'application/vnd.route66.link66+xml', - 'list' => 'text/plain', - 'list3820' => 'application/vnd.ibm.modcap', - 'listafp' => 'application/vnd.ibm.modcap', - 'log' => 'text/plain', - 'lostxml' => 'application/lost+xml', - 'lrf' => 'application/octet-stream', - 'lrm' => 'application/vnd.ms-lrm', - 'ltf' => 'application/vnd.frogans.ltf', - 'lvp' => 'audio/vnd.lucent.voice', - 'lwp' => 'application/vnd.lotus-wordpro', - 'lzh' => 'application/octet-stream', - 'm13' => 'application/x-msmediaview', - 'm14' => 'application/x-msmediaview', - 'm1v' => 'video/mpeg', - 'm21' => 'application/mp21', - 'm2a' => 'audio/mpeg', - 'm2v' => 'video/mpeg', - 'm3a' => 'audio/mpeg', - 'm3u' => 'audio/x-mpegurl', - 'm3u8' => 'application/vnd.apple.mpegurl', - 'm4a' => 'audio/mp4', - 'm4u' => 'video/vnd.mpegurl', - 'm4v' => 'video/mp4', - 'ma' => 'application/mathematica', - 'mads' => 'application/mads+xml', - 'mag' => 'application/vnd.ecowin.chart', - 'maker' => 'application/vnd.framemaker', - 'man' => 'text/troff', - 'mathml' => 'application/mathml+xml', - 'mb' => 'application/mathematica', - 'mbk' => 'application/vnd.mobius.mbk', - 'mbox' => 'application/mbox', - 'mc1' => 'application/vnd.medcalcdata', - 'mcd' => 'application/vnd.mcd', - 'mcurl' => 'text/vnd.curl.mcurl', - 'mdb' => 'application/x-msaccess', - 'mdi' => 'image/vnd.ms-modi', - 'me' => 'text/troff', - 'mesh' => 'model/mesh', - 'meta4' => 'application/metalink4+xml', - 'mets' => 'application/mets+xml', - 'mfm' => 'application/vnd.mfmp', - 'mgp' => 'application/vnd.osgeo.mapguide.package', - 'mgz' => 'application/vnd.proteus.magazine', - 'mid' => 'audio/midi', - 'midi' => 'audio/midi', - 'mif' => 'application/vnd.mif', - 'mime' => 'message/rfc822', - 'mj2' => 'video/mj2', - 'mjp2' => 'video/mj2', - 'mlp' => 'application/vnd.dolby.mlp', - 'mmd' => 'application/vnd.chipnuts.karaoke-mmd', - 'mmf' => 'application/vnd.smaf', - 'mmr' => 'image/vnd.fujixerox.edmics-mmr', - 'mny' => 'application/x-msmoney', - 'mobi' => 'application/x-mobipocket-ebook', - 'mods' => 'application/mods+xml', - 'mov' => 'video/quicktime', - 'movie' => 'video/x-sgi-movie', - 'mp2' => 'audio/mpeg', - 'mp21' => 'application/mp21', - 'mp2a' => 'audio/mpeg', - 'mp3' => 'audio/mpeg', - 'mp4' => 'video/mp4', - 'mp4a' => 'audio/mp4', - 'mp4s' => 'application/mp4', - 'mp4v' => 'video/mp4', - 'mpc' => 'application/vnd.mophun.certificate', - 'mpe' => 'video/mpeg', - 'mpeg' => 'video/mpeg', - 'mpg' => 'video/mpeg', - 'mpg4' => 'video/mp4', - 'mpga' => 'audio/mpeg', - 'mpkg' => 'application/vnd.apple.installer+xml', - 'mpm' => 'application/vnd.blueice.multipass', - 'mpn' => 'application/vnd.mophun.application', - 'mpp' => 'application/vnd.ms-project', - 'mpt' => 'application/vnd.ms-project', - 'mpy' => 'application/vnd.ibm.minipay', - 'mqy' => 'application/vnd.mobius.mqy', - 'mrc' => 'application/marc', - 'mrcx' => 'application/marcxml+xml', - 'ms' => 'text/troff', - 'mscml' => 'application/mediaservercontrol+xml', - 'mseed' => 'application/vnd.fdsn.mseed', - 'mseq' => 'application/vnd.mseq', - 'msf' => 'application/vnd.epson.msf', - 'msh' => 'model/mesh', - 'msi' => 'application/x-msdownload', - 'msl' => 'application/vnd.mobius.msl', - 'msty' => 'application/vnd.muvee.style', - 'mts' => 'model/vnd.mts', - 'mus' => 'application/vnd.musician', - 'musicxml' => 'application/vnd.recordare.musicxml+xml', - 'mvb' => 'application/x-msmediaview', - 'mwf' => 'application/vnd.mfer', - 'mxf' => 'application/mxf', - 'mxl' => 'application/vnd.recordare.musicxml', - 'mxml' => 'application/xv+xml', - 'mxs' => 'application/vnd.triscape.mxs', - 'mxu' => 'video/vnd.mpegurl', - 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install', - 'n3' => 'text/n3', - 'nb' => 'application/mathematica', - 'nbp' => 'application/vnd.wolfram.player', - 'nc' => 'application/x-netcdf', - 'ncx' => 'application/x-dtbncx+xml', - 'ngdat' => 'application/vnd.nokia.n-gage.data', - 'nlu' => 'application/vnd.neurolanguage.nlu', - 'nml' => 'application/vnd.enliven', - 'nnd' => 'application/vnd.noblenet-directory', - 'nns' => 'application/vnd.noblenet-sealer', - 'nnw' => 'application/vnd.noblenet-web', - 'npx' => 'image/vnd.net-fpx', - 'nsf' => 'application/vnd.lotus-notes', - 'oa2' => 'application/vnd.fujitsu.oasys2', - 'oa3' => 'application/vnd.fujitsu.oasys3', - 'oas' => 'application/vnd.fujitsu.oasys', - 'obd' => 'application/x-msbinder', - 'oda' => 'application/oda', - 'odb' => 'application/vnd.oasis.opendocument.database', - 'odc' => 'application/vnd.oasis.opendocument.chart', - 'odf' => 'application/vnd.oasis.opendocument.formula', - 'odft' => 'application/vnd.oasis.opendocument.formula-template', - 'odg' => 'application/vnd.oasis.opendocument.graphics', - 'odi' => 'application/vnd.oasis.opendocument.image', - 'odm' => 'application/vnd.oasis.opendocument.text-master', - 'odp' => 'application/vnd.oasis.opendocument.presentation', - 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', - 'odt' => 'application/vnd.oasis.opendocument.text', - 'oga' => 'audio/ogg', - 'ogg' => 'audio/ogg', - 'ogv' => 'video/ogg', - 'ogx' => 'application/ogg', - 'onepkg' => 'application/onenote', - 'onetmp' => 'application/onenote', - 'onetoc' => 'application/onenote', - 'onetoc2' => 'application/onenote', - 'opf' => 'application/oebps-package+xml', - 'oprc' => 'application/vnd.palm', - 'org' => 'application/vnd.lotus-organizer', - 'osf' => 'application/vnd.yamaha.openscoreformat', - 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml', - 'otc' => 'application/vnd.oasis.opendocument.chart-template', - 'otf' => 'application/x-font-otf', - 'otg' => 'application/vnd.oasis.opendocument.graphics-template', - 'oth' => 'application/vnd.oasis.opendocument.text-web', - 'oti' => 'application/vnd.oasis.opendocument.image-template', - 'otp' => 'application/vnd.oasis.opendocument.presentation-template', - 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', - 'ott' => 'application/vnd.oasis.opendocument.text-template', - 'oxt' => 'application/vnd.openofficeorg.extension', - 'p' => 'text/x-pascal', - 'p10' => 'application/pkcs10', - 'p12' => 'application/x-pkcs12', - 'p7b' => 'application/x-pkcs7-certificates', - 'p7c' => 'application/pkcs7-mime', - 'p7m' => 'application/pkcs7-mime', - 'p7r' => 'application/x-pkcs7-certreqresp', - 'p7s' => 'application/pkcs7-signature', - 'p8' => 'application/pkcs8', - 'pas' => 'text/x-pascal', - 'paw' => 'application/vnd.pawaafile', - 'pbd' => 'application/vnd.powerbuilder6', - 'pbm' => 'image/x-portable-bitmap', - 'pcf' => 'application/x-font-pcf', - 'pcl' => 'application/vnd.hp-pcl', - 'pclxl' => 'application/vnd.hp-pclxl', - 'pct' => 'image/x-pict', - 'pcurl' => 'application/vnd.curl.pcurl', - 'pcx' => 'image/x-pcx', - 'pdb' => 'application/vnd.palm', - 'pdf' => 'application/pdf', - 'pfa' => 'application/x-font-type1', - 'pfb' => 'application/x-font-type1', - 'pfm' => 'application/x-font-type1', - 'pfr' => 'application/font-tdpfr', - 'pfx' => 'application/x-pkcs12', - 'pgm' => 'image/x-portable-graymap', - 'pgn' => 'application/x-chess-pgn', - 'pgp' => 'application/pgp-encrypted', - 'php' => 'text/x-php', - 'phps' => 'application/x-httpd-phps', - 'pic' => 'image/x-pict', - 'pkg' => 'application/octet-stream', - 'pki' => 'application/pkixcmp', - 'pkipath' => 'application/pkix-pkipath', - 'plb' => 'application/vnd.3gpp.pic-bw-large', - 'plc' => 'application/vnd.mobius.plc', - 'plf' => 'application/vnd.pocketlearn', - 'pls' => 'application/pls+xml', - 'pml' => 'application/vnd.ctc-posml', - 'png' => 'image/png', - 'pnm' => 'image/x-portable-anymap', - 'portpkg' => 'application/vnd.macports.portpkg', - 'pot' => 'application/vnd.ms-powerpoint', - 'potm' => 'application/vnd.ms-powerpoint.template.macroenabled.12', - 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', - 'ppam' => 'application/vnd.ms-powerpoint.addin.macroenabled.12', - 'ppd' => 'application/vnd.cups-ppd', - 'ppm' => 'image/x-portable-pixmap', - 'pps' => 'application/vnd.ms-powerpoint', - 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroenabled.12', - 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', - 'ppt' => 'application/vnd.ms-powerpoint', - 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroenabled.12', - 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - 'pqa' => 'application/vnd.palm', - 'prc' => 'application/x-mobipocket-ebook', - 'pre' => 'application/vnd.lotus-freelance', - 'prf' => 'application/pics-rules', - 'ps' => 'application/postscript', - 'psb' => 'application/vnd.3gpp.pic-bw-small', - 'psd' => 'image/vnd.adobe.photoshop', - 'psf' => 'application/x-font-linux-psf', - 'pskcxml' => 'application/pskc+xml', - 'ptid' => 'application/vnd.pvi.ptid1', - 'pub' => 'application/x-mspublisher', - 'pvb' => 'application/vnd.3gpp.pic-bw-var', - 'pwn' => 'application/vnd.3m.post-it-notes', - 'pya' => 'audio/vnd.ms-playready.media.pya', - 'pyv' => 'video/vnd.ms-playready.media.pyv', - 'qam' => 'application/vnd.epson.quickanime', - 'qbo' => 'application/vnd.intu.qbo', - 'qfx' => 'application/vnd.intu.qfx', - 'qps' => 'application/vnd.publishare-delta-tree', - 'qt' => 'video/quicktime', - 'qwd' => 'application/vnd.quark.quarkxpress', - 'qwt' => 'application/vnd.quark.quarkxpress', - 'qxb' => 'application/vnd.quark.quarkxpress', - 'qxd' => 'application/vnd.quark.quarkxpress', - 'qxl' => 'application/vnd.quark.quarkxpress', - 'qxt' => 'application/vnd.quark.quarkxpress', - 'ra' => 'audio/x-pn-realaudio', - 'ram' => 'audio/x-pn-realaudio', - 'rar' => 'application/x-rar-compressed', - 'ras' => 'image/x-cmu-raster', - 'rb' => 'text/plain', - 'rcprofile' => 'application/vnd.ipunplugged.rcprofile', - 'rdf' => 'application/rdf+xml', - 'rdz' => 'application/vnd.data-vision.rdz', - 'rep' => 'application/vnd.businessobjects', - 'res' => 'application/x-dtbresource+xml', - 'resx' => 'text/xml', - 'rgb' => 'image/x-rgb', - 'rif' => 'application/reginfo+xml', - 'rip' => 'audio/vnd.rip', - 'rl' => 'application/resource-lists+xml', - 'rlc' => 'image/vnd.fujixerox.edmics-rlc', - 'rld' => 'application/resource-lists-diff+xml', - 'rm' => 'application/vnd.rn-realmedia', - 'rmi' => 'audio/midi', - 'rmp' => 'audio/x-pn-realaudio-plugin', - 'rms' => 'application/vnd.jcp.javame.midlet-rms', - 'rnc' => 'application/relax-ng-compact-syntax', - 'roff' => 'text/troff', - 'rp9' => 'application/vnd.cloanto.rp9', - 'rpss' => 'application/vnd.nokia.radio-presets', - 'rpst' => 'application/vnd.nokia.radio-preset', - 'rq' => 'application/sparql-query', - 'rs' => 'application/rls-services+xml', - 'rsd' => 'application/rsd+xml', - 'rss' => 'application/rss+xml', - 'rtf' => 'application/rtf', - 'rtx' => 'text/richtext', - 's' => 'text/x-asm', - 'saf' => 'application/vnd.yamaha.smaf-audio', - 'sbml' => 'application/sbml+xml', - 'sc' => 'application/vnd.ibm.secure-container', - 'scd' => 'application/x-msschedule', - 'scm' => 'application/vnd.lotus-screencam', - 'scq' => 'application/scvp-cv-request', - 'scs' => 'application/scvp-cv-response', - 'scurl' => 'text/vnd.curl.scurl', - 'sda' => 'application/vnd.stardivision.draw', - 'sdc' => 'application/vnd.stardivision.calc', - 'sdd' => 'application/vnd.stardivision.impress', - 'sdkd' => 'application/vnd.solent.sdkm+xml', - 'sdkm' => 'application/vnd.solent.sdkm+xml', - 'sdp' => 'application/sdp', - 'sdw' => 'application/vnd.stardivision.writer', - 'see' => 'application/vnd.seemail', - 'seed' => 'application/vnd.fdsn.seed', - 'sema' => 'application/vnd.sema', - 'semd' => 'application/vnd.semd', - 'semf' => 'application/vnd.semf', - 'ser' => 'application/java-serialized-object', - 'setpay' => 'application/set-payment-initiation', - 'setreg' => 'application/set-registration-initiation', - 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data', - 'sfs' => 'application/vnd.spotfire.sfs', - 'sgl' => 'application/vnd.stardivision.writer-global', - 'sgm' => 'text/sgml', - 'sgml' => 'text/sgml', - 'sh' => 'application/x-sh', - 'shar' => 'application/x-shar', - 'shf' => 'application/shf+xml', - 'sig' => 'application/pgp-signature', - 'silo' => 'model/mesh', - 'sis' => 'application/vnd.symbian.install', - 'sisx' => 'application/vnd.symbian.install', - 'sit' => 'application/x-stuffit', - 'sitx' => 'application/x-stuffitx', - 'skd' => 'application/vnd.koan', - 'skm' => 'application/vnd.koan', - 'skp' => 'application/vnd.koan', - 'skt' => 'application/vnd.koan', - 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12', - 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', - 'slt' => 'application/vnd.epson.salt', - 'sm' => 'application/vnd.stepmania.stepchart', - 'smf' => 'application/vnd.stardivision.math', - 'smi' => 'application/smil+xml', - 'smil' => 'application/smil+xml', - 'snd' => 'audio/basic', - 'snf' => 'application/x-font-snf', - 'so' => 'application/octet-stream', - 'spc' => 'application/x-pkcs7-certificates', - 'spf' => 'application/vnd.yamaha.smaf-phrase', - 'spl' => 'application/x-futuresplash', - 'spot' => 'text/vnd.in3d.spot', - 'spp' => 'application/scvp-vp-response', - 'spq' => 'application/scvp-vp-request', - 'spx' => 'audio/ogg', - 'src' => 'application/x-wais-source', - 'sru' => 'application/sru+xml', - 'srx' => 'application/sparql-results+xml', - 'sse' => 'application/vnd.kodak-descriptor', - 'ssf' => 'application/vnd.epson.ssf', - 'ssml' => 'application/ssml+xml', - 'st' => 'application/vnd.sailingtracker.track', - 'stc' => 'application/vnd.sun.xml.calc.template', - 'std' => 'application/vnd.sun.xml.draw.template', - 'stf' => 'application/vnd.wt.stf', - 'sti' => 'application/vnd.sun.xml.impress.template', - 'stk' => 'application/hyperstudio', - 'stl' => 'application/vnd.ms-pki.stl', - 'str' => 'application/vnd.pg.format', - 'stw' => 'application/vnd.sun.xml.writer.template', - 'sub' => 'image/vnd.dvb.subtitle', - 'sus' => 'application/vnd.sus-calendar', - 'susp' => 'application/vnd.sus-calendar', - 'sv4cpio' => 'application/x-sv4cpio', - 'sv4crc' => 'application/x-sv4crc', - 'svc' => 'application/vnd.dvb.service', - 'svd' => 'application/vnd.svd', - 'svg' => 'image/svg+xml', - 'svgz' => 'image/svg+xml', - 'swa' => 'application/x-director', - 'swf' => 'application/x-shockwave-flash', - 'swi' => 'application/vnd.aristanetworks.swi', - 'sxc' => 'application/vnd.sun.xml.calc', - 'sxd' => 'application/vnd.sun.xml.draw', - 'sxg' => 'application/vnd.sun.xml.writer.global', - 'sxi' => 'application/vnd.sun.xml.impress', - 'sxm' => 'application/vnd.sun.xml.math', - 'sxw' => 'application/vnd.sun.xml.writer', - 't' => 'text/troff', - 'tao' => 'application/vnd.tao.intent-module-archive', - 'tar' => 'application/x-tar', - 'tcap' => 'application/vnd.3gpp2.tcap', - 'tcl' => 'application/x-tcl', - 'teacher' => 'application/vnd.smart.teacher', - 'tei' => 'application/tei+xml', - 'teicorpus' => 'application/tei+xml', - 'tex' => 'application/x-tex', - 'texi' => 'application/x-texinfo', - 'texinfo' => 'application/x-texinfo', - 'text' => 'text/plain', - 'tfi' => 'application/thraud+xml', - 'tfm' => 'application/x-tex-tfm', - 'thmx' => 'application/vnd.ms-officetheme', - 'tif' => 'image/tiff', - 'tiff' => 'image/tiff', - 'tmo' => 'application/vnd.tmobile-livetv', - 'torrent' => 'application/x-bittorrent', - 'tpl' => 'application/vnd.groove-tool-template', - 'tpt' => 'application/vnd.trid.tpt', - 'tr' => 'text/troff', - 'tra' => 'application/vnd.trueapp', - 'trm' => 'application/x-msterminal', - 'tsd' => 'application/timestamped-data', - 'tsv' => 'text/tab-separated-values', - 'ttc' => 'application/x-font-ttf', - 'ttf' => 'application/x-font-ttf', - 'ttl' => 'text/turtle', - 'twd' => 'application/vnd.simtech-mindmapper', - 'twds' => 'application/vnd.simtech-mindmapper', - 'txd' => 'application/vnd.genomatix.tuxedo', - 'txf' => 'application/vnd.mobius.txf', - 'txt' => 'text/plain', - 'u32' => 'application/x-authorware-bin', - 'udeb' => 'application/x-debian-package', - 'ufd' => 'application/vnd.ufdl', - 'ufdl' => 'application/vnd.ufdl', - 'umj' => 'application/vnd.umajin', - 'unityweb' => 'application/vnd.unity', - 'uoml' => 'application/vnd.uoml+xml', - 'uri' => 'text/uri-list', - 'uris' => 'text/uri-list', - 'urls' => 'text/uri-list', - 'ustar' => 'application/x-ustar', - 'utz' => 'application/vnd.uiq.theme', - 'uu' => 'text/x-uuencode', - 'uva' => 'audio/vnd.dece.audio', - 'uvd' => 'application/vnd.dece.data', - 'uvf' => 'application/vnd.dece.data', - 'uvg' => 'image/vnd.dece.graphic', - 'uvh' => 'video/vnd.dece.hd', - 'uvi' => 'image/vnd.dece.graphic', - 'uvm' => 'video/vnd.dece.mobile', - 'uvp' => 'video/vnd.dece.pd', - 'uvs' => 'video/vnd.dece.sd', - 'uvt' => 'application/vnd.dece.ttml+xml', - 'uvu' => 'video/vnd.uvvu.mp4', - 'uvv' => 'video/vnd.dece.video', - 'uvva' => 'audio/vnd.dece.audio', - 'uvvd' => 'application/vnd.dece.data', - 'uvvf' => 'application/vnd.dece.data', - 'uvvg' => 'image/vnd.dece.graphic', - 'uvvh' => 'video/vnd.dece.hd', - 'uvvi' => 'image/vnd.dece.graphic', - 'uvvm' => 'video/vnd.dece.mobile', - 'uvvp' => 'video/vnd.dece.pd', - 'uvvs' => 'video/vnd.dece.sd', - 'uvvt' => 'application/vnd.dece.ttml+xml', - 'uvvu' => 'video/vnd.uvvu.mp4', - 'uvvv' => 'video/vnd.dece.video', - 'uvvx' => 'application/vnd.dece.unspecified', - 'uvx' => 'application/vnd.dece.unspecified', - 'vcd' => 'application/x-cdlink', - 'vcf' => 'text/x-vcard', - 'vcg' => 'application/vnd.groove-vcard', - 'vcs' => 'text/x-vcalendar', - 'vcx' => 'application/vnd.vcx', - 'vis' => 'application/vnd.visionary', - 'viv' => 'video/vnd.vivo', - 'vor' => 'application/vnd.stardivision.writer', - 'vox' => 'application/x-authorware-bin', - 'vrml' => 'model/vrml', - 'vsd' => 'application/vnd.visio', - 'vsf' => 'application/vnd.vsf', - 'vss' => 'application/vnd.visio', - 'vst' => 'application/vnd.visio', - 'vsw' => 'application/vnd.visio', - 'vtu' => 'model/vnd.vtu', - 'vxml' => 'application/voicexml+xml', - 'w3d' => 'application/x-director', - 'wad' => 'application/x-doom', - 'wav' => 'audio/x-wav', - 'wax' => 'audio/x-ms-wax', - 'wbmp' => 'image/vnd.wap.wbmp', - 'wbs' => 'application/vnd.criticaltools.wbs+xml', - 'wbxml' => 'application/vnd.wap.wbxml', - 'wcm' => 'application/vnd.ms-works', - 'wdb' => 'application/vnd.ms-works', - 'weba' => 'audio/webm', - 'webm' => 'video/webm', - 'webp' => 'image/webp', - 'wg' => 'application/vnd.pmi.widget', - 'wgt' => 'application/widget', - 'wks' => 'application/vnd.ms-works', - 'wm' => 'video/x-ms-wm', - 'wma' => 'audio/x-ms-wma', - 'wmd' => 'application/x-ms-wmd', - 'wmf' => 'application/x-msmetafile', - 'wml' => 'text/vnd.wap.wml', - 'wmlc' => 'application/vnd.wap.wmlc', - 'wmls' => 'text/vnd.wap.wmlscript', - 'wmlsc' => 'application/vnd.wap.wmlscriptc', - 'wmv' => 'video/x-ms-wmv', - 'wmx' => 'video/x-ms-wmx', - 'wmz' => 'application/x-ms-wmz', - 'woff' => 'application/x-font-woff', - 'wpd' => 'application/vnd.wordperfect', - 'wpl' => 'application/vnd.ms-wpl', - 'wps' => 'application/vnd.ms-works', - 'wqd' => 'application/vnd.wqd', - 'wri' => 'application/x-mswrite', - 'wrl' => 'model/vrml', - 'wsdl' => 'application/wsdl+xml', - 'wspolicy' => 'application/wspolicy+xml', - 'wtb' => 'application/vnd.webturbo', - 'wvx' => 'video/x-ms-wvx', - 'x32' => 'application/x-authorware-bin', - 'x3d' => 'application/vnd.hzn-3d-crossword', - 'xap' => 'application/x-silverlight-app', - 'xar' => 'application/vnd.xara', - 'xbap' => 'application/x-ms-xbap', - 'xbd' => 'application/vnd.fujixerox.docuworks.binder', - 'xbm' => 'image/x-xbitmap', - 'xdf' => 'application/xcap-diff+xml', - 'xdm' => 'application/vnd.syncml.dm+xml', - 'xdp' => 'application/vnd.adobe.xdp+xml', - 'xdssc' => 'application/dssc+xml', - 'xdw' => 'application/vnd.fujixerox.docuworks', - 'xenc' => 'application/xenc+xml', - 'xer' => 'application/patch-ops-error+xml', - 'xfdf' => 'application/vnd.adobe.xfdf', - 'xfdl' => 'application/vnd.xfdl', - 'xht' => 'application/xhtml+xml', - 'xhtml' => 'application/xhtml+xml', - 'xhvml' => 'application/xv+xml', - 'xif' => 'image/vnd.xiff', - 'xla' => 'application/vnd.ms-excel', - 'xlam' => 'application/vnd.ms-excel.addin.macroenabled.12', - 'xlc' => 'application/vnd.ms-excel', - 'xlm' => 'application/vnd.ms-excel', - 'xls' => 'application/vnd.ms-excel', - 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroenabled.12', - 'xlsm' => 'application/vnd.ms-excel.sheet.macroenabled.12', - 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'xlt' => 'application/vnd.ms-excel', - 'xltm' => 'application/vnd.ms-excel.template.macroenabled.12', - 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', - 'xlw' => 'application/vnd.ms-excel', - 'xml' => 'application/xml', - 'xo' => 'application/vnd.olpc-sugar', - 'xop' => 'application/xop+xml', - 'xpi' => 'application/x-xpinstall', - 'xpm' => 'image/x-xpixmap', - 'xpr' => 'application/vnd.is-xpr', - 'xps' => 'application/vnd.ms-xpsdocument', - 'xpw' => 'application/vnd.intercon.formnet', - 'xpx' => 'application/vnd.intercon.formnet', - 'xsl' => 'application/xml', - 'xslt' => 'application/xslt+xml', - 'xsm' => 'application/vnd.syncml+xml', - 'xspf' => 'application/xspf+xml', - 'xul' => 'application/vnd.mozilla.xul+xml', - 'xvm' => 'application/xv+xml', - 'xvml' => 'application/xv+xml', - 'xwd' => 'image/x-xwindowdump', - 'xyz' => 'chemical/x-xyz', - 'yaml' => 'text/yaml', - 'yang' => 'application/yang', - 'yin' => 'application/yin+xml', - 'yml' => 'text/yaml', - 'zaz' => 'application/vnd.zzazz.deck+xml', - 'zip' => 'application/zip', - 'zir' => 'application/vnd.zul', - 'zirz' => 'application/vnd.zul', - 'zmm' => 'application/vnd.handheld-entertainment+xml' - ); - - /** - * Get a singleton instance of the class - * - * @return self - * @codeCoverageIgnore - */ - public static function getInstance() - { - if (!self::$instance) { - self::$instance = new self(); - } - - return self::$instance; - } - - /** - * Get a mimetype value from a file extension - * - * @param string $extension File extension - * - * @return string|null - * - */ - public function fromExtension($extension) - { - $extension = strtolower($extension); - - return isset($this->mimetypes[$extension]) - ? $this->mimetypes[$extension] - : null; - } - - /** - * Get a mimetype from a filename - * - * @param string $filename Filename to generate a mimetype from - * - * @return string|null - */ - public function fromFilename($filename) - { - return $this->fromExtension(pathinfo($filename, PATHINFO_EXTENSION)); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Pool.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Pool.php deleted file mode 100644 index 7b9d83a4ea..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Pool.php +++ /dev/null @@ -1,333 +0,0 @@ -client = $client; - $this->iter = $this->coerceIterable($requests); - $this->deferred = new Deferred(); - $this->promise = $this->deferred->promise(); - $this->poolSize = isset($options['pool_size']) - ? $options['pool_size'] : 25; - $this->eventListeners = $this->prepareListeners( - $options, - ['before', 'complete', 'error', 'end'] - ); - } - - /** - * Sends multiple requests in parallel and returns an array of responses - * and exceptions that uses the same ordering as the provided requests. - * - * IMPORTANT: This method keeps every request and response in memory, and - * as such, is NOT recommended when sending a large number or an - * indeterminate number of requests concurrently. - * - * @param ClientInterface $client Client used to send the requests - * @param array|\Iterator $requests Requests to send in parallel - * @param array $options Passes through the options available in - * {@see GuzzleHttp\Pool::__construct} - * - * @return BatchResults Returns a container for the results. - * @throws \InvalidArgumentException if the event format is incorrect. - */ - public static function batch( - ClientInterface $client, - $requests, - array $options = [] - ) { - $hash = new \SplObjectStorage(); - foreach ($requests as $request) { - $hash->attach($request); - } - - // In addition to the normally run events when requests complete, add - // and event to continuously track the results of transfers in the hash. - (new self($client, $requests, RequestEvents::convertEventArray( - $options, - ['end'], - [ - 'priority' => RequestEvents::LATE, - 'fn' => function (EndEvent $e) use ($hash) { - $hash[$e->getRequest()] = $e->getException() - ? $e->getException() - : $e->getResponse(); - } - ] - )))->wait(); - - return new BatchResults($hash); - } - - /** - * Creates a Pool and immediately sends the requests. - * - * @param ClientInterface $client Client used to send the requests - * @param array|\Iterator $requests Requests to send in parallel - * @param array $options Passes through the options available in - * {@see GuzzleHttp\Pool::__construct} - */ - public static function send( - ClientInterface $client, - $requests, - array $options = [] - ) { - $pool = new self($client, $requests, $options); - $pool->wait(); - } - - private function getPoolSize() - { - return is_callable($this->poolSize) - ? call_user_func($this->poolSize, count($this->waitQueue)) - : $this->poolSize; - } - - /** - * Add as many requests as possible up to the current pool limit. - */ - private function addNextRequests() - { - $limit = max($this->getPoolSize() - count($this->waitQueue), 0); - while ($limit--) { - if (!$this->addNextRequest()) { - break; - } - } - } - - public function wait() - { - if ($this->isRealized) { - return false; - } - - // Seed the pool with N number of requests. - $this->addNextRequests(); - - // Stop if the pool was cancelled while transferring requests. - if ($this->isRealized) { - return false; - } - - // Wait on any outstanding FutureResponse objects. - while ($response = array_pop($this->waitQueue)) { - try { - $response->wait(); - } catch (\Exception $e) { - // Eat exceptions because they should be handled asynchronously - } - $this->addNextRequests(); - } - - // Clean up no longer needed state. - $this->isRealized = true; - $this->waitQueue = $this->eventListeners = []; - $this->client = $this->iter = null; - $this->deferred->resolve(true); - - return true; - } - - /** - * {@inheritdoc} - * - * Attempt to cancel all outstanding requests (requests that are queued for - * dereferencing). Returns true if all outstanding requests can be - * cancelled. - * - * @return bool - */ - public function cancel() - { - if ($this->isRealized) { - return false; - } - - $success = $this->isRealized = true; - foreach ($this->waitQueue as $response) { - if (!$response->cancel()) { - $success = false; - } - } - - return $success; - } - - /** - * Returns a promise that is invoked when the pool completed. There will be - * no passed value. - * - * {@inheritdoc} - */ - public function then( - callable $onFulfilled = null, - callable $onRejected = null, - callable $onProgress = null - ) { - return $this->promise->then($onFulfilled, $onRejected, $onProgress); - } - - public function promise() - { - return $this->promise; - } - - private function coerceIterable($requests) - { - if ($requests instanceof \Iterator) { - return $requests; - } elseif (is_array($requests)) { - return new \ArrayIterator($requests); - } - - throw new \InvalidArgumentException('Expected Iterator or array. ' - . 'Found ' . Core::describeType($requests)); - } - - /** - * Adds the next request to pool and tracks what requests need to be - * dereferenced when completing the pool. - */ - private function addNextRequest() - { - add_next: - - if ($this->isRealized || !$this->iter || !$this->iter->valid()) { - return false; - } - - $request = $this->iter->current(); - $this->iter->next(); - - if (!($request instanceof RequestInterface)) { - throw new \InvalidArgumentException(sprintf( - 'All requests in the provided iterator must implement ' - . 'RequestInterface. Found %s', - Core::describeType($request) - )); - } - - // Be sure to use "lazy" futures, meaning they do not send right away. - $request->getConfig()->set('future', 'lazy'); - $hash = spl_object_hash($request); - $this->attachListeners($request, $this->eventListeners); - $request->getEmitter()->on('before', [$this, '_trackRetries'], RequestEvents::EARLY); - $response = $this->client->send($request); - $this->waitQueue[$hash] = $response; - $promise = $response->promise(); - - // Don't recursively call itself for completed or rejected responses. - if ($promise instanceof FulfilledPromise - || $promise instanceof RejectedPromise - ) { - try { - $this->finishResponse($request, $response->wait(), $hash); - } catch (\Exception $e) { - $this->finishResponse($request, $e, $hash); - } - goto add_next; - } - - // Use this function for both resolution and rejection. - $thenFn = function ($value) use ($request, $hash) { - $this->finishResponse($request, $value, $hash); - if (!$request->getConfig()->get('_pool_retries')) { - $this->addNextRequests(); - } - }; - - $promise->then($thenFn, $thenFn); - - return true; - } - - public function _trackRetries(BeforeEvent $e) - { - $e->getRequest()->getConfig()->set('_pool_retries', $e->getRetryCount()); - } - - private function finishResponse($request, $value, $hash) - { - unset($this->waitQueue[$hash]); - $result = $value instanceof ResponseInterface - ? ['request' => $request, 'response' => $value, 'error' => null] - : ['request' => $request, 'response' => null, 'error' => $value]; - $this->deferred->notify($result); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Post/MultipartBody.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Post/MultipartBody.php deleted file mode 100644 index 1149e62354..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Post/MultipartBody.php +++ /dev/null @@ -1,109 +0,0 @@ -boundary = $boundary ?: uniqid(); - $this->stream = $this->createStream($fields, $files); - } - - /** - * Get the boundary - * - * @return string - */ - public function getBoundary() - { - return $this->boundary; - } - - public function isWritable() - { - return false; - } - - /** - * Get the string needed to transfer a POST field - */ - private function getFieldString($name, $value) - { - return sprintf( - "--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n", - $this->boundary, - $name, - $value - ); - } - - /** - * Get the headers needed before transferring the content of a POST file - */ - private function getFileHeaders(PostFileInterface $file) - { - $headers = ''; - foreach ($file->getHeaders() as $key => $value) { - $headers .= "{$key}: {$value}\r\n"; - } - - return "--{$this->boundary}\r\n" . trim($headers) . "\r\n\r\n"; - } - - /** - * Create the aggregate stream that will be used to upload the POST data - */ - protected function createStream(array $fields, array $files) - { - $stream = new AppendStream(); - - foreach ($fields as $name => $fieldValues) { - foreach ((array) $fieldValues as $value) { - $stream->addStream( - Stream::factory($this->getFieldString($name, $value)) - ); - } - } - - foreach ($files as $file) { - - if (!$file instanceof PostFileInterface) { - throw new \InvalidArgumentException('All POST fields must ' - . 'implement PostFieldInterface'); - } - - $stream->addStream( - Stream::factory($this->getFileHeaders($file)) - ); - $stream->addStream($file->getContent()); - $stream->addStream(Stream::factory("\r\n")); - } - - // Add the trailing boundary with CRLF - $stream->addStream(Stream::factory("--{$this->boundary}--\r\n")); - - return $stream; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Post/PostBody.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Post/PostBody.php deleted file mode 100644 index ed14d1f703..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Post/PostBody.php +++ /dev/null @@ -1,287 +0,0 @@ -files || $this->forceMultipart) { - $request->setHeader( - 'Content-Type', - 'multipart/form-data; boundary=' . $this->getBody()->getBoundary() - ); - } elseif ($this->fields && !$request->hasHeader('Content-Type')) { - $request->setHeader( - 'Content-Type', - 'application/x-www-form-urlencoded' - ); - } - - if ($size = $this->getSize()) { - $request->setHeader('Content-Length', $size); - } - } - - public function forceMultipartUpload($force) - { - $this->forceMultipart = $force; - } - - public function setAggregator(callable $aggregator) - { - $this->aggregator = $aggregator; - } - - public function setField($name, $value) - { - $this->fields[$name] = $value; - $this->mutate(); - } - - public function replaceFields(array $fields) - { - $this->fields = $fields; - $this->mutate(); - } - - public function getField($name) - { - return isset($this->fields[$name]) ? $this->fields[$name] : null; - } - - public function removeField($name) - { - unset($this->fields[$name]); - $this->mutate(); - } - - public function getFields($asString = false) - { - if (!$asString) { - return $this->fields; - } - - $query = new Query($this->fields); - $query->setEncodingType(Query::RFC1738); - $query->setAggregator($this->getAggregator()); - - return (string) $query; - } - - public function hasField($name) - { - return isset($this->fields[$name]); - } - - public function getFile($name) - { - foreach ($this->files as $file) { - if ($file->getName() == $name) { - return $file; - } - } - - return null; - } - - public function getFiles() - { - return $this->files; - } - - public function addFile(PostFileInterface $file) - { - $this->files[] = $file; - $this->mutate(); - } - - public function clearFiles() - { - $this->files = []; - $this->mutate(); - } - - /** - * Returns the numbers of fields + files - * - * @return int - */ - public function count() - { - return count($this->files) + count($this->fields); - } - - public function __toString() - { - return (string) $this->getBody(); - } - - public function getContents($maxLength = -1) - { - return $this->getBody()->getContents(); - } - - public function close() - { - $this->detach(); - } - - public function detach() - { - $this->detached = true; - $this->fields = $this->files = []; - - if ($this->body) { - $this->body->close(); - $this->body = null; - } - } - - public function attach($stream) - { - throw new CannotAttachException(); - } - - public function eof() - { - return $this->getBody()->eof(); - } - - public function tell() - { - return $this->body ? $this->body->tell() : 0; - } - - public function isSeekable() - { - return true; - } - - public function isReadable() - { - return true; - } - - public function isWritable() - { - return false; - } - - public function getSize() - { - return $this->getBody()->getSize(); - } - - public function seek($offset, $whence = SEEK_SET) - { - return $this->getBody()->seek($offset, $whence); - } - - public function read($length) - { - return $this->getBody()->read($length); - } - - public function write($string) - { - return false; - } - - public function getMetadata($key = null) - { - return $key ? null : []; - } - - /** - * Return a stream object that is built from the POST fields and files. - * - * If one has already been created, the previously created stream will be - * returned. - */ - private function getBody() - { - if ($this->body) { - return $this->body; - } elseif ($this->files || $this->forceMultipart) { - return $this->body = $this->createMultipart(); - } elseif ($this->fields) { - return $this->body = $this->createUrlEncoded(); - } else { - return $this->body = Stream::factory(); - } - } - - /** - * Get the aggregator used to join multi-valued field parameters - * - * @return callable - */ - final protected function getAggregator() - { - if (!$this->aggregator) { - $this->aggregator = Query::phpAggregator(); - } - - return $this->aggregator; - } - - /** - * Creates a multipart/form-data body stream - * - * @return MultipartBody - */ - private function createMultipart() - { - // Flatten the nested query string values using the correct aggregator - return new MultipartBody( - call_user_func($this->getAggregator(), $this->fields), - $this->files - ); - } - - /** - * Creates an application/x-www-form-urlencoded stream body - * - * @return StreamInterface - */ - private function createUrlEncoded() - { - return Stream::factory($this->getFields(true)); - } - - /** - * Get rid of any cached data - */ - private function mutate() - { - $this->body = null; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Post/PostBodyInterface.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Post/PostBodyInterface.php deleted file mode 100644 index c2ec9a62c5..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Post/PostBodyInterface.php +++ /dev/null @@ -1,109 +0,0 @@ -headers = $headers; - $this->name = $name; - $this->prepareContent($content); - $this->prepareFilename($filename); - $this->prepareDefaultHeaders(); - } - - public function getName() - { - return $this->name; - } - - public function getFilename() - { - return $this->filename; - } - - public function getContent() - { - return $this->content; - } - - public function getHeaders() - { - return $this->headers; - } - - /** - * Prepares the contents of a POST file. - * - * @param mixed $content Content of the POST file - */ - private function prepareContent($content) - { - $this->content = $content; - - if (!($this->content instanceof StreamInterface)) { - $this->content = Stream::factory($this->content); - } elseif ($this->content instanceof MultipartBody) { - if (!$this->hasHeader('Content-Disposition')) { - $disposition = 'form-data; name="' . $this->name .'"'; - $this->headers['Content-Disposition'] = $disposition; - } - - if (!$this->hasHeader('Content-Type')) { - $this->headers['Content-Type'] = sprintf( - "multipart/form-data; boundary=%s", - $this->content->getBoundary() - ); - } - } - } - - /** - * Applies a file name to the POST file based on various checks. - * - * @param string|null $filename Filename to apply (or null to guess) - */ - private function prepareFilename($filename) - { - $this->filename = $filename; - - if (!$this->filename) { - $this->filename = $this->content->getMetadata('uri'); - } - - if (!$this->filename || substr($this->filename, 0, 6) === 'php://') { - $this->filename = $this->name; - } - } - - /** - * Applies default Content-Disposition and Content-Type headers if needed. - */ - private function prepareDefaultHeaders() - { - // Set a default content-disposition header if one was no provided - if (!$this->hasHeader('Content-Disposition')) { - $this->headers['Content-Disposition'] = sprintf( - 'form-data; name="%s"; filename="%s"', - $this->name, - basename($this->filename) - ); - } - - // Set a default Content-Type if one was not supplied - if (!$this->hasHeader('Content-Type')) { - $this->headers['Content-Type'] = Mimetypes::getInstance() - ->fromFilename($this->filename) ?: 'text/plain'; - } - } - - /** - * Check if a specific header exists on the POST file by name. - * - * @param string $name Case-insensitive header to check - * - * @return bool - */ - private function hasHeader($name) - { - return isset(array_change_key_case($this->headers)[strtolower($name)]); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Post/PostFileInterface.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Post/PostFileInterface.php deleted file mode 100644 index 2e816c0884..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Post/PostFileInterface.php +++ /dev/null @@ -1,41 +0,0 @@ -setEncodingType($urlEncoding); - } - - $qp->parseInto($q, $query, $urlEncoding); - - return $q; - } - - /** - * Convert the query string parameters to a query string string - * - * @return string - */ - public function __toString() - { - if (!$this->data) { - return ''; - } - - // The default aggregator is statically cached - static $defaultAggregator; - - if (!$this->aggregator) { - if (!$defaultAggregator) { - $defaultAggregator = self::phpAggregator(); - } - $this->aggregator = $defaultAggregator; - } - - $result = ''; - $aggregator = $this->aggregator; - $encoder = $this->encoding; - - foreach ($aggregator($this->data) as $key => $values) { - foreach ($values as $value) { - if ($result) { - $result .= '&'; - } - $result .= $encoder($key); - if ($value !== null) { - $result .= '=' . $encoder($value); - } - } - } - - return $result; - } - - /** - * Controls how multi-valued query string parameters are aggregated into a - * string. - * - * $query->setAggregator($query::duplicateAggregator()); - * - * @param callable $aggregator Callable used to convert a deeply nested - * array of query string variables into a flattened array of key value - * pairs. The callable accepts an array of query data and returns a - * flattened array of key value pairs where each value is an array of - * strings. - */ - public function setAggregator(callable $aggregator) - { - $this->aggregator = $aggregator; - } - - /** - * Specify how values are URL encoded - * - * @param string|bool $type One of 'RFC1738', 'RFC3986', or false to disable encoding - * - * @throws \InvalidArgumentException - */ - public function setEncodingType($type) - { - switch ($type) { - case self::RFC3986: - $this->encoding = 'rawurlencode'; - break; - case self::RFC1738: - $this->encoding = 'urlencode'; - break; - case false: - $this->encoding = function ($v) { return $v; }; - break; - default: - throw new \InvalidArgumentException('Invalid URL encoding type'); - } - } - - /** - * Query string aggregator that does not aggregate nested query string - * values and allows duplicates in the resulting array. - * - * Example: http://test.com?q=1&q=2 - * - * @return callable - */ - public static function duplicateAggregator() - { - return function (array $data) { - return self::walkQuery($data, '', function ($key, $prefix) { - return is_int($key) ? $prefix : "{$prefix}[{$key}]"; - }); - }; - } - - /** - * Aggregates nested query string variables using the same technique as - * ``http_build_query()``. - * - * @param bool $numericIndices Pass false to not include numeric indices - * when multi-values query string parameters are present. - * - * @return callable - */ - public static function phpAggregator($numericIndices = true) - { - return function (array $data) use ($numericIndices) { - return self::walkQuery( - $data, - '', - function ($key, $prefix) use ($numericIndices) { - return !$numericIndices && is_int($key) - ? "{$prefix}[]" - : "{$prefix}[{$key}]"; - } - ); - }; - } - - /** - * Easily create query aggregation functions by providing a key prefix - * function to this query string array walker. - * - * @param array $query Query string to walk - * @param string $keyPrefix Key prefix (start with '') - * @param callable $prefixer Function used to create a key prefix - * - * @return array - */ - public static function walkQuery(array $query, $keyPrefix, callable $prefixer) - { - $result = []; - foreach ($query as $key => $value) { - if ($keyPrefix) { - $key = $prefixer($key, $keyPrefix); - } - if (is_array($value)) { - $result += self::walkQuery($value, $key, $prefixer); - } elseif (isset($result[$key])) { - $result[$key][] = $value; - } else { - $result[$key] = array($value); - } - } - - return $result; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/QueryParser.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/QueryParser.php deleted file mode 100644 index 90727cc6cf..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/QueryParser.php +++ /dev/null @@ -1,163 +0,0 @@ -duplicates = false; - $this->numericIndices = true; - $decoder = self::getDecoder($urlEncoding); - - foreach (explode('&', $str) as $kvp) { - - $parts = explode('=', $kvp, 2); - $key = $decoder($parts[0]); - $value = isset($parts[1]) ? $decoder($parts[1]) : null; - - // Special handling needs to be taken for PHP nested array syntax - if (strpos($key, '[') !== false) { - $this->parsePhpValue($key, $value, $result); - continue; - } - - if (!isset($result[$key])) { - $result[$key] = $value; - } else { - $this->duplicates = true; - if (!is_array($result[$key])) { - $result[$key] = [$result[$key]]; - } - $result[$key][] = $value; - } - } - - $query->replace($result); - - if (!$this->numericIndices) { - $query->setAggregator(Query::phpAggregator(false)); - } elseif ($this->duplicates) { - $query->setAggregator(Query::duplicateAggregator()); - } - } - - /** - * Returns a callable that is used to URL decode query keys and values. - * - * @param string|bool $type One of true, false, RFC3986, and RFC1738 - * - * @return callable|string - */ - private static function getDecoder($type) - { - if ($type === true) { - return function ($value) { - return rawurldecode(str_replace('+', ' ', $value)); - }; - } elseif ($type == Query::RFC3986) { - return 'rawurldecode'; - } elseif ($type == Query::RFC1738) { - return 'urldecode'; - } else { - return function ($str) { return $str; }; - } - } - - /** - * Parses a PHP style key value pair. - * - * @param string $key Key to parse (e.g., "foo[a][b]") - * @param string|null $value Value to set - * @param array $result Result to modify by reference - */ - private function parsePhpValue($key, $value, array &$result) - { - $node =& $result; - $keyBuffer = ''; - - for ($i = 0, $t = strlen($key); $i < $t; $i++) { - switch ($key[$i]) { - case '[': - if ($keyBuffer) { - $this->prepareNode($node, $keyBuffer); - $node =& $node[$keyBuffer]; - $keyBuffer = ''; - } - break; - case ']': - $k = $this->cleanKey($node, $keyBuffer); - $this->prepareNode($node, $k); - $node =& $node[$k]; - $keyBuffer = ''; - break; - default: - $keyBuffer .= $key[$i]; - break; - } - } - - if (isset($node)) { - $this->duplicates = true; - $node[] = $value; - } else { - $node = $value; - } - } - - /** - * Prepares a value in the array at the given key. - * - * If the key already exists, the key value is converted into an array. - * - * @param array $node Result node to modify - * @param string $key Key to add or modify in the node - */ - private function prepareNode(&$node, $key) - { - if (!isset($node[$key])) { - $node[$key] = null; - } elseif (!is_array($node[$key])) { - $node[$key] = [$node[$key]]; - } - } - - /** - * Returns the appropriate key based on the node and key. - */ - private function cleanKey($node, $key) - { - if ($key === '') { - $key = $node ? (string) count($node) : 0; - // Found a [] key, so track this to ensure that we disable numeric - // indexing of keys in the resolved query aggregator. - $this->numericIndices = false; - } - - return $key; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/RequestFsm.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/RequestFsm.php deleted file mode 100644 index b37c190d43..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/RequestFsm.php +++ /dev/null @@ -1,153 +0,0 @@ -mf = $messageFactory; - $this->maxTransitions = $maxTransitions; - $this->handler = $handler; - } - - /** - * Runs the state machine until a terminal state is entered or the - * optionally supplied $finalState is entered. - * - * @param Transaction $trans Transaction being transitioned. - * - * @throws \Exception if a terminal state throws an exception. - */ - public function __invoke(Transaction $trans) - { - $trans->_transitionCount = 0; - - if (!$trans->state) { - $trans->state = 'before'; - } - - transition: - - if (++$trans->_transitionCount > $this->maxTransitions) { - throw new StateException("Too many state transitions were " - . "encountered ({$trans->_transitionCount}). This likely " - . "means that a combination of event listeners are in an " - . "infinite loop."); - } - - switch ($trans->state) { - case 'before': goto before; - case 'complete': goto complete; - case 'error': goto error; - case 'retry': goto retry; - case 'send': goto send; - case 'end': goto end; - default: throw new StateException("Invalid state: {$trans->state}"); - } - - before: { - try { - $trans->request->getEmitter()->emit('before', new BeforeEvent($trans)); - $trans->state = 'send'; - if ((bool) $trans->response) { - $trans->state = 'complete'; - } - } catch (\Exception $e) { - $trans->state = 'error'; - $trans->exception = $e; - } - goto transition; - } - - complete: { - try { - if ($trans->response instanceof FutureInterface) { - // Futures will have their own end events emitted when - // dereferenced. - return; - } - $trans->state = 'end'; - $trans->response->setEffectiveUrl($trans->request->getUrl()); - $trans->request->getEmitter()->emit('complete', new CompleteEvent($trans)); - } catch (\Exception $e) { - $trans->state = 'error'; - $trans->exception = $e; - } - goto transition; - } - - error: { - try { - // Convert non-request exception to a wrapped exception - $trans->exception = RequestException::wrapException( - $trans->request, $trans->exception - ); - $trans->state = 'end'; - $trans->request->getEmitter()->emit('error', new ErrorEvent($trans)); - // An intercepted request (not retried) transitions to complete - if (!$trans->exception && $trans->state !== 'retry') { - $trans->state = 'complete'; - } - } catch (\Exception $e) { - $trans->state = 'end'; - $trans->exception = $e; - } - goto transition; - } - - retry: { - $trans->retries++; - $trans->response = null; - $trans->exception = null; - $trans->state = 'before'; - goto transition; - } - - send: { - $fn = $this->handler; - $trans->response = FutureResponse::proxy( - $fn(RingBridge::prepareRingRequest($trans)), - function ($value) use ($trans) { - RingBridge::completeRingResponse($trans, $value, $this->mf, $this); - $this($trans); - return $trans->response; - } - ); - return; - } - - end: { - $trans->request->getEmitter()->emit('end', new EndEvent($trans)); - // Throw exceptions in the terminal event if the exception - // was not handled by an "end" event listener. - if ($trans->exception) { - if (!($trans->exception instanceof RequestException)) { - $trans->exception = RequestException::wrapException( - $trans->request, $trans->exception - ); - } - throw $trans->exception; - } - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/RingBridge.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/RingBridge.php deleted file mode 100644 index bc6841d42b..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/RingBridge.php +++ /dev/null @@ -1,165 +0,0 @@ -getConfig()->toArray(); - $url = $request->getUrl(); - // No need to calculate the query string twice (in URL and query). - $qs = ($pos = strpos($url, '?')) ? substr($url, $pos + 1) : null; - - return [ - 'scheme' => $request->getScheme(), - 'http_method' => $request->getMethod(), - 'url' => $url, - 'uri' => $request->getPath(), - 'headers' => $request->getHeaders(), - 'body' => $request->getBody(), - 'version' => $request->getProtocolVersion(), - 'client' => $options, - 'query_string' => $qs, - 'future' => isset($options['future']) ? $options['future'] : false - ]; - } - - /** - * Creates a Ring request from a request object AND prepares the callbacks. - * - * @param Transaction $trans Transaction to update. - * - * @return array Converted Guzzle Ring request. - */ - public static function prepareRingRequest(Transaction $trans) - { - // Clear out the transaction state when initiating. - $trans->exception = null; - $request = self::createRingRequest($trans->request); - - // Emit progress events if any progress listeners are registered. - if ($trans->request->getEmitter()->hasListeners('progress')) { - $emitter = $trans->request->getEmitter(); - $request['client']['progress'] = function ($a, $b, $c, $d) use ($trans, $emitter) { - $emitter->emit('progress', new ProgressEvent($trans, $a, $b, $c, $d)); - }; - } - - return $request; - } - - /** - * Handles the process of processing a response received from a ring - * handler. The created response is added to the transaction, and the - * transaction stat is set appropriately. - * - * @param Transaction $trans Owns request and response. - * @param array $response Ring response array - * @param MessageFactoryInterface $messageFactory Creates response objects. - */ - public static function completeRingResponse( - Transaction $trans, - array $response, - MessageFactoryInterface $messageFactory - ) { - $trans->state = 'complete'; - $trans->transferInfo = isset($response['transfer_stats']) - ? $response['transfer_stats'] : []; - - if (!empty($response['status'])) { - $options = []; - if (isset($response['version'])) { - $options['protocol_version'] = $response['version']; - } - if (isset($response['reason'])) { - $options['reason_phrase'] = $response['reason']; - } - $trans->response = $messageFactory->createResponse( - $response['status'], - isset($response['headers']) ? $response['headers'] : [], - isset($response['body']) ? $response['body'] : null, - $options - ); - if (isset($response['effective_url'])) { - $trans->response->setEffectiveUrl($response['effective_url']); - } - } elseif (empty($response['error'])) { - // When nothing was returned, then we need to add an error. - $response['error'] = self::getNoRingResponseException($trans->request); - } - - if (isset($response['error'])) { - $trans->state = 'error'; - $trans->exception = $response['error']; - } - } - - /** - * Creates a Guzzle request object using a ring request array. - * - * @param array $request Ring request - * - * @return Request - * @throws \InvalidArgumentException for incomplete requests. - */ - public static function fromRingRequest(array $request) - { - $options = []; - if (isset($request['version'])) { - $options['protocol_version'] = $request['version']; - } - - if (!isset($request['http_method'])) { - throw new \InvalidArgumentException('No http_method'); - } - - return new Request( - $request['http_method'], - Core::url($request), - isset($request['headers']) ? $request['headers'] : [], - isset($request['body']) ? Stream::factory($request['body']) : null, - $options - ); - } - - /** - * Get an exception that can be used when a RingPHP handler does not - * populate a response. - * - * @param RequestInterface $request - * - * @return RequestException - */ - public static function getNoRingResponseException(RequestInterface $request) - { - $message = <<cookieJar = $cookieJar ?: new CookieJar(); - } - - public function getEvents() - { - // Fire the cookie plugin complete event before redirecting - return [ - 'before' => ['onBefore'], - 'complete' => ['onComplete', RequestEvents::REDIRECT_RESPONSE + 10] - ]; - } - - /** - * Get the cookie cookieJar - * - * @return CookieJarInterface - */ - public function getCookieJar() - { - return $this->cookieJar; - } - - public function onBefore(BeforeEvent $event) - { - $this->cookieJar->addCookieHeader($event->getRequest()); - } - - public function onComplete(CompleteEvent $event) - { - $this->cookieJar->extractCookies( - $event->getRequest(), - $event->getResponse() - ); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Subscriber/History.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Subscriber/History.php deleted file mode 100644 index 5cf06119f6..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Subscriber/History.php +++ /dev/null @@ -1,172 +0,0 @@ -limit = $limit; - } - - public function getEvents() - { - return [ - 'complete' => ['onComplete', RequestEvents::EARLY], - 'error' => ['onError', RequestEvents::EARLY], - ]; - } - - /** - * Convert to a string that contains all request and response headers - * - * @return string - */ - public function __toString() - { - $lines = array(); - foreach ($this->transactions as $entry) { - $response = isset($entry['response']) ? $entry['response'] : ''; - $lines[] = '> ' . trim($entry['sent_request']) - . "\n\n< " . trim($response) . "\n"; - } - - return implode("\n", $lines); - } - - public function onComplete(CompleteEvent $event) - { - $this->add($event->getRequest(), $event->getResponse()); - } - - public function onError(ErrorEvent $event) - { - // Only track when no response is present, meaning this didn't ever - // emit a complete event - if (!$event->getResponse()) { - $this->add($event->getRequest()); - } - } - - /** - * Returns an Iterator that yields associative array values where each - * associative array contains the following key value pairs: - * - * - request: Representing the actual request that was received. - * - sent_request: A clone of the request that will not be mutated. - * - response: The response that was received (if available). - * - * @return \Iterator - */ - public function getIterator() - { - return new \ArrayIterator($this->transactions); - } - - /** - * Get all of the requests sent through the plugin. - * - * Requests can be modified after they are logged by the history - * subscriber. By default this method will return the actual request - * instances that were received. Pass true to this method if you wish to - * get copies of the requests that represent the request state when it was - * initially logged by the history subscriber. - * - * @param bool $asSent Set to true to get clones of the requests that have - * not been mutated since the request was received by - * the history subscriber. - * - * @return RequestInterface[] - */ - public function getRequests($asSent = false) - { - return array_map(function ($t) use ($asSent) { - return $asSent ? $t['sent_request'] : $t['request']; - }, $this->transactions); - } - - /** - * Get the number of requests in the history - * - * @return int - */ - public function count() - { - return count($this->transactions); - } - - /** - * Get the last request sent. - * - * Requests can be modified after they are logged by the history - * subscriber. By default this method will return the actual request - * instance that was received. Pass true to this method if you wish to get - * a copy of the request that represents the request state when it was - * initially logged by the history subscriber. - * - * @param bool $asSent Set to true to get a clone of the last request that - * has not been mutated since the request was received - * by the history subscriber. - * - * @return RequestInterface - */ - public function getLastRequest($asSent = false) - { - return $asSent - ? end($this->transactions)['sent_request'] - : end($this->transactions)['request']; - } - - /** - * Get the last response in the history - * - * @return ResponseInterface|null - */ - public function getLastResponse() - { - return end($this->transactions)['response']; - } - - /** - * Clears the history - */ - public function clear() - { - $this->transactions = array(); - } - - /** - * Add a request to the history - * - * @param RequestInterface $request Request to add - * @param ResponseInterface $response Response of the request - */ - private function add( - RequestInterface $request, - ResponseInterface $response = null - ) { - $this->transactions[] = [ - 'request' => $request, - 'sent_request' => clone $request, - 'response' => $response - ]; - if (count($this->transactions) > $this->limit) { - array_shift($this->transactions); - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Subscriber/HttpError.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Subscriber/HttpError.php deleted file mode 100644 index ed9de5bcc0..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Subscriber/HttpError.php +++ /dev/null @@ -1,36 +0,0 @@ - ['onComplete', RequestEvents::VERIFY_RESPONSE]]; - } - - /** - * Throw a RequestException on an HTTP protocol error - * - * @param CompleteEvent $event Emitted event - * @throws RequestException - */ - public function onComplete(CompleteEvent $event) - { - $code = (string) $event->getResponse()->getStatusCode(); - // Throw an exception for an unsuccessful response - if ($code[0] >= 4) { - throw RequestException::create( - $event->getRequest(), - $event->getResponse() - ); - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Subscriber/Mock.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Subscriber/Mock.php deleted file mode 100644 index 2af4d3758d..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Subscriber/Mock.php +++ /dev/null @@ -1,147 +0,0 @@ -factory = new MessageFactory(); - $this->readBodies = $readBodies; - $this->addMultiple($items); - } - - public function getEvents() - { - // Fire the event last, after signing - return ['before' => ['onBefore', RequestEvents::SIGN_REQUEST - 10]]; - } - - /** - * @throws \OutOfBoundsException|\Exception - */ - public function onBefore(BeforeEvent $event) - { - if (!$item = array_shift($this->queue)) { - throw new \OutOfBoundsException('Mock queue is empty'); - } elseif ($item instanceof RequestException) { - throw $item; - } - - // Emulate reading a response body - $request = $event->getRequest(); - if ($this->readBodies && $request->getBody()) { - while (!$request->getBody()->eof()) { - $request->getBody()->read(8096); - } - } - - $saveTo = $event->getRequest()->getConfig()->get('save_to'); - - if (null !== $saveTo) { - $body = $item->getBody(); - - if (is_resource($saveTo)) { - fwrite($saveTo, $body); - } elseif (is_string($saveTo)) { - file_put_contents($saveTo, $body); - } elseif ($saveTo instanceof StreamInterface) { - $saveTo->write($body); - } - } - - $event->intercept($item); - } - - public function count() - { - return count($this->queue); - } - - /** - * Add a response to the end of the queue - * - * @param string|ResponseInterface $response Response or path to response file - * - * @return self - * @throws \InvalidArgumentException if a string or Response is not passed - */ - public function addResponse($response) - { - if (is_string($response)) { - $response = file_exists($response) - ? $this->factory->fromMessage(file_get_contents($response)) - : $this->factory->fromMessage($response); - } elseif (!($response instanceof ResponseInterface)) { - throw new \InvalidArgumentException('Response must a message ' - . 'string, response object, or path to a file'); - } - - $this->queue[] = $response; - - return $this; - } - - /** - * Add an exception to the end of the queue - * - * @param RequestException $e Exception to throw when the request is executed - * - * @return self - */ - public function addException(RequestException $e) - { - $this->queue[] = $e; - - return $this; - } - - /** - * Add multiple items to the queue - * - * @param array $items Items to add - */ - public function addMultiple(array $items) - { - foreach ($items as $item) { - if ($item instanceof RequestException) { - $this->addException($item); - } else { - $this->addResponse($item); - } - } - } - - /** - * Clear the queue - */ - public function clearQueue() - { - $this->queue = []; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Subscriber/Prepare.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Subscriber/Prepare.php deleted file mode 100644 index b5ed4e2609..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Subscriber/Prepare.php +++ /dev/null @@ -1,130 +0,0 @@ - ['onBefore', RequestEvents::PREPARE_REQUEST]]; - } - - public function onBefore(BeforeEvent $event) - { - $request = $event->getRequest(); - - // Set the appropriate Content-Type for a request if one is not set and - // there are form fields - if (!($body = $request->getBody())) { - return; - } - - $this->addContentLength($request, $body); - - if ($body instanceof AppliesHeadersInterface) { - // Synchronize the body with the request headers - $body->applyRequestHeaders($request); - } elseif (!$request->hasHeader('Content-Type')) { - $this->addContentType($request, $body); - } - - $this->addExpectHeader($request, $body); - } - - private function addContentType( - RequestInterface $request, - StreamInterface $body - ) { - if (!($uri = $body->getMetadata('uri'))) { - return; - } - - // Guess the content-type based on the stream's "uri" metadata value. - // The file extension is used to determine the appropriate mime-type. - if ($contentType = Mimetypes::getInstance()->fromFilename($uri)) { - $request->setHeader('Content-Type', $contentType); - } - } - - private function addContentLength( - RequestInterface $request, - StreamInterface $body - ) { - // Set the Content-Length header if it can be determined, and never - // send a Transfer-Encoding: chunked and Content-Length header in - // the same request. - if ($request->hasHeader('Content-Length')) { - // Remove transfer-encoding if content-length is set. - $request->removeHeader('Transfer-Encoding'); - return; - } - - if ($request->hasHeader('Transfer-Encoding')) { - return; - } - - if (null !== ($size = $body->getSize())) { - $request->setHeader('Content-Length', $size); - $request->removeHeader('Transfer-Encoding'); - } elseif ('1.1' == $request->getProtocolVersion()) { - // Use chunked Transfer-Encoding if there is no determinable - // content-length header and we're using HTTP/1.1. - $request->setHeader('Transfer-Encoding', 'chunked'); - $request->removeHeader('Content-Length'); - } - } - - private function addExpectHeader( - RequestInterface $request, - StreamInterface $body - ) { - // Determine if the Expect header should be used - if ($request->hasHeader('Expect')) { - return; - } - - $expect = $request->getConfig()['expect']; - - // Return if disabled or if you're not using HTTP/1.1 - if ($expect === false || $request->getProtocolVersion() !== '1.1') { - return; - } - - // The expect header is unconditionally enabled - if ($expect === true) { - $request->setHeader('Expect', '100-Continue'); - return; - } - - // By default, send the expect header when the payload is > 1mb - if ($expect === null) { - $expect = 1048576; - } - - // Always add if the body cannot be rewound, the size cannot be - // determined, or the size is greater than the cutoff threshold - $size = $body->getSize(); - if ($size === null || $size >= (int) $expect || !$body->isSeekable()) { - $request->setHeader('Expect', '100-Continue'); - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Subscriber/Redirect.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Subscriber/Redirect.php deleted file mode 100644 index ff992268bb..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Subscriber/Redirect.php +++ /dev/null @@ -1,176 +0,0 @@ - ['onComplete', RequestEvents::REDIRECT_RESPONSE]]; - } - - /** - * Rewind the entity body of the request if needed - * - * @param RequestInterface $redirectRequest - * @throws CouldNotRewindStreamException - */ - public static function rewindEntityBody(RequestInterface $redirectRequest) - { - // Rewind the entity body of the request if needed - if ($body = $redirectRequest->getBody()) { - // Only rewind the body if some of it has been read already, and - // throw an exception if the rewind fails - if ($body->tell() && !$body->seek(0)) { - throw new CouldNotRewindStreamException( - 'Unable to rewind the non-seekable request body after redirecting', - $redirectRequest - ); - } - } - } - - /** - * Called when a request receives a redirect response - * - * @param CompleteEvent $event Event emitted - * @throws TooManyRedirectsException - */ - public function onComplete(CompleteEvent $event) - { - $response = $event->getResponse(); - - if (substr($response->getStatusCode(), 0, 1) != '3' - || !$response->hasHeader('Location') - ) { - return; - } - - $request = $event->getRequest(); - $config = $request->getConfig(); - - // Increment the redirect and initialize the redirect state. - if ($redirectCount = $config['redirect_count']) { - $config['redirect_count'] = ++$redirectCount; - } else { - $config['redirect_scheme'] = $request->getScheme(); - $config['redirect_count'] = $redirectCount = 1; - } - - $max = $config->getPath('redirect/max') ?: 5; - - if ($redirectCount > $max) { - throw new TooManyRedirectsException( - "Will not follow more than {$redirectCount} redirects", - $request - ); - } - - $this->modifyRedirectRequest($request, $response); - $event->retry(); - } - - private function modifyRedirectRequest( - RequestInterface $request, - ResponseInterface $response - ) { - $config = $request->getConfig(); - $protocols = $config->getPath('redirect/protocols') ?: ['http', 'https']; - - // Use a GET request if this is an entity enclosing request and we are - // not forcing RFC compliance, but rather emulating what all browsers - // would do. - $statusCode = $response->getStatusCode(); - if ($statusCode == 303 || - ($statusCode <= 302 && $request->getBody() && !$config->getPath('redirect/strict')) - ) { - $request->setMethod('GET'); - $request->setBody(null); - } - - $previousUrl = $request->getUrl(); - $this->setRedirectUrl($request, $response, $protocols); - $this->rewindEntityBody($request); - - // Add the Referer header if it is told to do so and only - // add the header if we are not redirecting from https to http. - if ($config->getPath('redirect/referer') - && ($request->getScheme() == 'https' || $request->getScheme() == $config['redirect_scheme']) - ) { - $url = Url::fromString($previousUrl); - $url->setUsername(null); - $url->setPassword(null); - $request->setHeader('Referer', (string) $url); - } else { - $request->removeHeader('Referer'); - } - } - - /** - * Set the appropriate URL on the request based on the location header - * - * @param RequestInterface $request - * @param ResponseInterface $response - * @param array $protocols - */ - private function setRedirectUrl( - RequestInterface $request, - ResponseInterface $response, - array $protocols - ) { - $location = $response->getHeader('Location'); - $location = Url::fromString($location); - - // Combine location with the original URL if it is not absolute. - if (!$location->isAbsolute()) { - $originalUrl = Url::fromString($request->getUrl()); - // Remove query string parameters and just take what is present on - // the redirect Location header - $originalUrl->getQuery()->clear(); - $location = $originalUrl->combine($location); - } - - // Ensure that the redirect URL is allowed based on the protocols. - if (!in_array($location->getScheme(), $protocols)) { - throw new BadResponseException( - sprintf( - 'Redirect URL, %s, does not use one of the allowed redirect protocols: %s', - $location, - implode(', ', $protocols) - ), - $request, - $response - ); - } - - $request->setUrl($location); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/ToArrayInterface.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/ToArrayInterface.php deleted file mode 100644 index d57c0229a4..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/ToArrayInterface.php +++ /dev/null @@ -1,15 +0,0 @@ -client = $client; - $this->request = $request; - $this->_future = $future; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/UriTemplate.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/UriTemplate.php deleted file mode 100644 index 55dfeb5a4c..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/UriTemplate.php +++ /dev/null @@ -1,241 +0,0 @@ - array('prefix' => '', 'joiner' => ',', 'query' => false), - '+' => array('prefix' => '', 'joiner' => ',', 'query' => false), - '#' => array('prefix' => '#', 'joiner' => ',', 'query' => false), - '.' => array('prefix' => '.', 'joiner' => '.', 'query' => false), - '/' => array('prefix' => '/', 'joiner' => '/', 'query' => false), - ';' => array('prefix' => ';', 'joiner' => ';', 'query' => true), - '?' => array('prefix' => '?', 'joiner' => '&', 'query' => true), - '&' => array('prefix' => '&', 'joiner' => '&', 'query' => true) - ); - - /** @var array Delimiters */ - private static $delims = array(':', '/', '?', '#', '[', ']', '@', '!', '$', - '&', '\'', '(', ')', '*', '+', ',', ';', '='); - - /** @var array Percent encoded delimiters */ - private static $delimsPct = array('%3A', '%2F', '%3F', '%23', '%5B', '%5D', - '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C', - '%3B', '%3D'); - - public function expand($template, array $variables) - { - if (false === strpos($template, '{')) { - return $template; - } - - $this->template = $template; - $this->variables = $variables; - - return preg_replace_callback( - '/\{([^\}]+)\}/', - [$this, 'expandMatch'], - $this->template - ); - } - - /** - * Parse an expression into parts - * - * @param string $expression Expression to parse - * - * @return array Returns an associative array of parts - */ - private function parseExpression($expression) - { - $result = array(); - - if (isset(self::$operatorHash[$expression[0]])) { - $result['operator'] = $expression[0]; - $expression = substr($expression, 1); - } else { - $result['operator'] = ''; - } - - foreach (explode(',', $expression) as $value) { - $value = trim($value); - $varspec = array(); - if ($colonPos = strpos($value, ':')) { - $varspec['value'] = substr($value, 0, $colonPos); - $varspec['modifier'] = ':'; - $varspec['position'] = (int) substr($value, $colonPos + 1); - } elseif (substr($value, -1) == '*') { - $varspec['modifier'] = '*'; - $varspec['value'] = substr($value, 0, -1); - } else { - $varspec['value'] = (string) $value; - $varspec['modifier'] = ''; - } - $result['values'][] = $varspec; - } - - return $result; - } - - /** - * Process an expansion - * - * @param array $matches Matches met in the preg_replace_callback - * - * @return string Returns the replacement string - */ - private function expandMatch(array $matches) - { - static $rfc1738to3986 = array('+' => '%20', '%7e' => '~'); - - $replacements = array(); - $parsed = self::parseExpression($matches[1]); - $prefix = self::$operatorHash[$parsed['operator']]['prefix']; - $joiner = self::$operatorHash[$parsed['operator']]['joiner']; - $useQuery = self::$operatorHash[$parsed['operator']]['query']; - - foreach ($parsed['values'] as $value) { - - if (!isset($this->variables[$value['value']])) { - continue; - } - - $variable = $this->variables[$value['value']]; - $actuallyUseQuery = $useQuery; - $expanded = ''; - - if (is_array($variable)) { - - $isAssoc = $this->isAssoc($variable); - $kvp = array(); - foreach ($variable as $key => $var) { - - if ($isAssoc) { - $key = rawurlencode($key); - $isNestedArray = is_array($var); - } else { - $isNestedArray = false; - } - - if (!$isNestedArray) { - $var = rawurlencode($var); - if ($parsed['operator'] == '+' || - $parsed['operator'] == '#' - ) { - $var = $this->decodeReserved($var); - } - } - - if ($value['modifier'] == '*') { - if ($isAssoc) { - if ($isNestedArray) { - // Nested arrays must allow for deeply nested - // structures. - $var = strtr( - http_build_query([$key => $var]), - $rfc1738to3986 - ); - } else { - $var = $key . '=' . $var; - } - } elseif ($key > 0 && $actuallyUseQuery) { - $var = $value['value'] . '=' . $var; - } - } - - $kvp[$key] = $var; - } - - if (empty($variable)) { - $actuallyUseQuery = false; - } elseif ($value['modifier'] == '*') { - $expanded = implode($joiner, $kvp); - if ($isAssoc) { - // Don't prepend the value name when using the explode - // modifier with an associative array. - $actuallyUseQuery = false; - } - } else { - if ($isAssoc) { - // When an associative array is encountered and the - // explode modifier is not set, then the result must be - // a comma separated list of keys followed by their - // respective values. - foreach ($kvp as $k => &$v) { - $v = $k . ',' . $v; - } - } - $expanded = implode(',', $kvp); - } - - } else { - if ($value['modifier'] == ':') { - $variable = substr($variable, 0, $value['position']); - } - $expanded = rawurlencode($variable); - if ($parsed['operator'] == '+' || $parsed['operator'] == '#') { - $expanded = $this->decodeReserved($expanded); - } - } - - if ($actuallyUseQuery) { - if (!$expanded && $joiner != '&') { - $expanded = $value['value']; - } else { - $expanded = $value['value'] . '=' . $expanded; - } - } - - $replacements[] = $expanded; - } - - $ret = implode($joiner, $replacements); - if ($ret && $prefix) { - return $prefix . $ret; - } - - return $ret; - } - - /** - * Determines if an array is associative. - * - * This makes the assumption that input arrays are sequences or hashes. - * This assumption is a tradeoff for accuracy in favor of speed, but it - * should work in almost every case where input is supplied for a URI - * template. - * - * @param array $array Array to check - * - * @return bool - */ - private function isAssoc(array $array) - { - return $array && array_keys($array)[0] !== 0; - } - - /** - * Removes percent encoding on reserved characters (used with + and # - * modifiers). - * - * @param string $string String to fix - * - * @return string - */ - private function decodeReserved($string) - { - return str_replace(self::$delimsPct, self::$delims, $string); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Url.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Url.php deleted file mode 100644 index 637f60c2ab..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Url.php +++ /dev/null @@ -1,595 +0,0 @@ - 80, 'https' => 443, 'ftp' => 21]; - private static $pathPattern = '/[^a-zA-Z0-9\-\._~!\$&\'\(\)\*\+,;=%:@\/]+|%(?![A-Fa-f0-9]{2})/'; - private static $queryPattern = '/[^a-zA-Z0-9\-\._~!\$\'\(\)\*\+,;%:@\/\?=&]+|%(?![A-Fa-f0-9]{2})/'; - /** @var Query|string Query part of the URL */ - private $query; - - /** - * Factory method to create a new URL from a URL string - * - * @param string $url Full URL used to create a Url object - * - * @return Url - * @throws \InvalidArgumentException - */ - public static function fromString($url) - { - static $defaults = ['scheme' => null, 'host' => null, - 'path' => null, 'port' => null, 'query' => null, - 'user' => null, 'pass' => null, 'fragment' => null]; - - if (false === ($parts = parse_url($url))) { - throw new \InvalidArgumentException('Unable to parse malformed ' - . 'url: ' . $url); - } - - $parts += $defaults; - - // Convert the query string into a Query object - if ($parts['query'] || 0 !== strlen($parts['query'])) { - $parts['query'] = Query::fromString($parts['query']); - } - - return new static($parts['scheme'], $parts['host'], $parts['user'], - $parts['pass'], $parts['port'], $parts['path'], $parts['query'], - $parts['fragment']); - } - - /** - * Build a URL from parse_url parts. The generated URL will be a relative - * URL if a scheme or host are not provided. - * - * @param array $parts Array of parse_url parts - * - * @return string - */ - public static function buildUrl(array $parts) - { - $url = $scheme = ''; - - if (!empty($parts['scheme'])) { - $scheme = $parts['scheme']; - $url .= $scheme . ':'; - } - - if (!empty($parts['host'])) { - $url .= '//'; - if (isset($parts['user'])) { - $url .= $parts['user']; - if (isset($parts['pass'])) { - $url .= ':' . $parts['pass']; - } - $url .= '@'; - } - - $url .= $parts['host']; - - // Only include the port if it is not the default port of the scheme - if (isset($parts['port']) && - (!isset(self::$defaultPorts[$scheme]) || - $parts['port'] != self::$defaultPorts[$scheme]) - ) { - $url .= ':' . $parts['port']; - } - } - - // Add the path component if present - if (isset($parts['path']) && strlen($parts['path'])) { - // Always ensure that the path begins with '/' if set and something - // is before the path - if (!empty($parts['host']) && $parts['path'][0] != '/') { - $url .= '/'; - } - $url .= $parts['path']; - } - - // Add the query string if present - if (isset($parts['query'])) { - $queryStr = (string) $parts['query']; - if ($queryStr || $queryStr === '0') { - $url .= '?' . $queryStr; - } - } - - // Ensure that # is only added to the url if fragment contains anything. - if (isset($parts['fragment'])) { - $url .= '#' . $parts['fragment']; - } - - return $url; - } - - /** - * Create a new URL from URL parts - * - * @param string $scheme Scheme of the URL - * @param string $host Host of the URL - * @param string $username Username of the URL - * @param string $password Password of the URL - * @param int $port Port of the URL - * @param string $path Path of the URL - * @param Query|array|string $query Query string of the URL - * @param string $fragment Fragment of the URL - */ - public function __construct( - $scheme, - $host, - $username = null, - $password = null, - $port = null, - $path = null, - $query = null, - $fragment = null - ) { - $this->scheme = strtolower($scheme); - $this->host = $host; - $this->port = $port; - $this->username = $username; - $this->password = $password; - $this->fragment = $fragment; - - if ($query) { - $this->setQuery($query); - } - - $this->setPath($path); - } - - /** - * Clone the URL - */ - public function __clone() - { - if ($this->query instanceof Query) { - $this->query = clone $this->query; - } - } - - /** - * Returns the URL as a URL string - * - * @return string - */ - public function __toString() - { - return static::buildUrl($this->getParts()); - } - - /** - * Get the parts of the URL as an array - * - * @return array - */ - public function getParts() - { - return array( - 'scheme' => $this->scheme, - 'user' => $this->username, - 'pass' => $this->password, - 'host' => $this->host, - 'port' => $this->port, - 'path' => $this->path, - 'query' => $this->query, - 'fragment' => $this->fragment, - ); - } - - /** - * Set the host of the request. - * - * @param string $host Host to set (e.g. www.yahoo.com, yahoo.com) - * - * @return Url - */ - public function setHost($host) - { - if (strpos($host, ':') === false) { - $this->host = $host; - } else { - list($host, $port) = explode(':', $host); - $this->host = $host; - $this->setPort($port); - } - } - - /** - * Get the host part of the URL - * - * @return string - */ - public function getHost() - { - return $this->host; - } - - /** - * Set the scheme part of the URL (http, https, ftp, etc.) - * - * @param string $scheme Scheme to set - */ - public function setScheme($scheme) - { - // Remove the default port if one is specified - if ($this->port - && isset(self::$defaultPorts[$this->scheme]) - && self::$defaultPorts[$this->scheme] == $this->port - ) { - $this->port = null; - } - - $this->scheme = strtolower($scheme); - } - - /** - * Get the scheme part of the URL - * - * @return string - */ - public function getScheme() - { - return $this->scheme; - } - - /** - * Set the port part of the URL - * - * @param int $port Port to set - */ - public function setPort($port) - { - $this->port = $port; - } - - /** - * Get the port part of the URl. - * - * If no port was set, this method will return the default port for the - * scheme of the URI. - * - * @return int|null - */ - public function getPort() - { - if ($this->port) { - return $this->port; - } elseif (isset(self::$defaultPorts[$this->scheme])) { - return self::$defaultPorts[$this->scheme]; - } - - return null; - } - - /** - * Set the path part of the URL. - * - * The provided URL is URL encoded as necessary. - * - * @param string $path Path string to set - */ - public function setPath($path) - { - $this->path = self::encodePath($path); - } - - /** - * Removes dot segments from a URL - * @link http://tools.ietf.org/html/rfc3986#section-5.2.4 - */ - public function removeDotSegments() - { - static $noopPaths = ['' => true, '/' => true, '*' => true]; - static $ignoreSegments = ['.' => true, '..' => true]; - - if (isset($noopPaths[$this->path])) { - return; - } - - $results = []; - $segments = $this->getPathSegments(); - foreach ($segments as $segment) { - if ($segment == '..') { - array_pop($results); - } elseif (!isset($ignoreSegments[$segment])) { - $results[] = $segment; - } - } - - $newPath = implode('/', $results); - - // Add the leading slash if necessary - if (substr($this->path, 0, 1) === '/' && - substr($newPath, 0, 1) !== '/' - ) { - $newPath = '/' . $newPath; - } - - // Add the trailing slash if necessary - if ($newPath != '/' && isset($ignoreSegments[end($segments)])) { - $newPath .= '/'; - } - - $this->path = $newPath; - } - - /** - * Add a relative path to the currently set path. - * - * @param string $relativePath Relative path to add - */ - public function addPath($relativePath) - { - if ($relativePath != '/' && - is_string($relativePath) && - strlen($relativePath) > 0 - ) { - // Add a leading slash if needed - if ($relativePath[0] !== '/' && - substr($this->path, -1, 1) !== '/' - ) { - $relativePath = '/' . $relativePath; - } - - $this->setPath($this->path . $relativePath); - } - } - - /** - * Get the path part of the URL - * - * @return string - */ - public function getPath() - { - return $this->path; - } - - /** - * Get the path segments of the URL as an array - * - * @return array - */ - public function getPathSegments() - { - return explode('/', $this->path); - } - - /** - * Set the password part of the URL - * - * @param string $password Password to set - */ - public function setPassword($password) - { - $this->password = $password; - } - - /** - * Get the password part of the URL - * - * @return null|string - */ - public function getPassword() - { - return $this->password; - } - - /** - * Set the username part of the URL - * - * @param string $username Username to set - */ - public function setUsername($username) - { - $this->username = $username; - } - - /** - * Get the username part of the URl - * - * @return null|string - */ - public function getUsername() - { - return $this->username; - } - - /** - * Get the query part of the URL as a Query object - * - * @return Query - */ - public function getQuery() - { - // Convert the query string to a query object if not already done. - if (!$this->query instanceof Query) { - $this->query = $this->query === null - ? new Query() - : Query::fromString($this->query); - } - - return $this->query; - } - - /** - * Set the query part of the URL. - * - * You may provide a query string as a string and pass $rawString as true - * to provide a query string that is not parsed until a call to getQuery() - * is made. Setting a raw query string will still encode invalid characters - * in a query string. - * - * @param Query|string|array $query Query string value to set. Can - * be a string that will be parsed into a Query object, an array - * of key value pairs, or a Query object. - * @param bool $rawString Set to true when providing a raw query string. - * - * @throws \InvalidArgumentException - */ - public function setQuery($query, $rawString = false) - { - if ($query instanceof Query) { - $this->query = $query; - } elseif (is_string($query)) { - if (!$rawString) { - $this->query = Query::fromString($query); - } else { - // Ensure the query does not have illegal characters. - $this->query = preg_replace_callback( - self::$queryPattern, - [__CLASS__, 'encodeMatch'], - $query - ); - } - - } elseif (is_array($query)) { - $this->query = new Query($query); - } else { - throw new \InvalidArgumentException('Query must be a Query, ' - . 'array, or string. Got ' . Core::describeType($query)); - } - } - - /** - * Get the fragment part of the URL - * - * @return null|string - */ - public function getFragment() - { - return $this->fragment; - } - - /** - * Set the fragment part of the URL - * - * @param string $fragment Fragment to set - */ - public function setFragment($fragment) - { - $this->fragment = $fragment; - } - - /** - * Check if this is an absolute URL - * - * @return bool - */ - public function isAbsolute() - { - return $this->scheme && $this->host; - } - - /** - * Combine the URL with another URL and return a new URL instance. - * - * Follows the rules specific in RFC 3986 section 5.4. - * - * @param string $url Relative URL to combine with - * - * @return Url - * @throws \InvalidArgumentException - * @link http://tools.ietf.org/html/rfc3986#section-5.4 - */ - public function combine($url) - { - $url = static::fromString($url); - - // Use the more absolute URL as the base URL - if (!$this->isAbsolute() && $url->isAbsolute()) { - $url = $url->combine($this); - } - - $parts = $url->getParts(); - - // Passing a URL with a scheme overrides everything - if ($parts['scheme']) { - return clone $url; - } - - // Setting a host overrides the entire rest of the URL - if ($parts['host']) { - return new static( - $this->scheme, - $parts['host'], - $parts['user'], - $parts['pass'], - $parts['port'], - $parts['path'], - $parts['query'] instanceof Query - ? clone $parts['query'] - : $parts['query'], - $parts['fragment'] - ); - } - - if (!$parts['path'] && $parts['path'] !== '0') { - // The relative URL has no path, so check if it is just a query - $path = $this->path ?: ''; - $query = $parts['query'] ?: $this->query; - } else { - $query = $parts['query']; - if ($parts['path'][0] == '/' || !$this->path) { - // Overwrite the existing path if the rel path starts with "/" - $path = $parts['path']; - } else { - // If the relative URL does not have a path or the base URL - // path does not end in a "/" then overwrite the existing path - // up to the last "/" - $path = substr($this->path, 0, strrpos($this->path, '/') + 1) . $parts['path']; - } - } - - $result = new self( - $this->scheme, - $this->host, - $this->username, - $this->password, - $this->port, - $path, - $query instanceof Query ? clone $query : $query, - $parts['fragment'] - ); - - if ($path) { - $result->removeDotSegments(); - } - - return $result; - } - - /** - * Encodes the path part of a URL without double-encoding percent-encoded - * key value pairs. - * - * @param string $path Path to encode - * - * @return string - */ - public static function encodePath($path) - { - static $cb = [__CLASS__, 'encodeMatch']; - return preg_replace_callback(self::$pathPattern, $cb, $path); - } - - private static function encodeMatch(array $match) - { - return rawurlencode($match[0]); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Utils.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Utils.php deleted file mode 100644 index 1c89661060..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/guzzle/src/Utils.php +++ /dev/null @@ -1,211 +0,0 @@ -expand($template, $variables); - } - - /** - * Wrapper for JSON decode that implements error detection with helpful - * error messages. - * - * @param string $json JSON data to parse - * @param bool $assoc When true, returned objects will be converted - * into associative arrays. - * @param int $depth User specified recursion depth. - * @param int $options Bitmask of JSON decode options. - * - * @return mixed - * @throws \InvalidArgumentException if the JSON cannot be parsed. - * @link http://www.php.net/manual/en/function.json-decode.php - */ - public static function jsonDecode($json, $assoc = false, $depth = 512, $options = 0) - { - static $jsonErrors = [ - JSON_ERROR_DEPTH => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded', - JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch', - JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found', - JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON', - JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded' - ]; - - $data = \json_decode($json, $assoc, $depth, $options); - - if (JSON_ERROR_NONE !== json_last_error()) { - $last = json_last_error(); - throw new \InvalidArgumentException( - 'Unable to parse JSON data: ' - . (isset($jsonErrors[$last]) - ? $jsonErrors[$last] - : 'Unknown error') - ); - } - - return $data; - } - - /** - * Get the default User-Agent string to use with Guzzle - * - * @return string - */ - public static function getDefaultUserAgent() - { - static $defaultAgent = ''; - if (!$defaultAgent) { - $defaultAgent = 'Guzzle/' . ClientInterface::VERSION; - if (extension_loaded('curl')) { - $defaultAgent .= ' curl/' . curl_version()['version']; - } - $defaultAgent .= ' PHP/' . PHP_VERSION; - } - - return $defaultAgent; - } - - /** - * Create a default handler to use based on the environment - * - * @throws \RuntimeException if no viable Handler is available. - */ - public static function getDefaultHandler() - { - $default = $future = null; - - if (extension_loaded('curl')) { - $config = [ - 'select_timeout' => getenv('GUZZLE_CURL_SELECT_TIMEOUT') ?: 1 - ]; - if ($maxHandles = getenv('GUZZLE_CURL_MAX_HANDLES')) { - $config['max_handles'] = $maxHandles; - } - if (function_exists('curl_reset')) { - $default = new CurlHandler(); - $future = new CurlMultiHandler($config); - } else { - $default = new CurlMultiHandler($config); - } - } - - if (ini_get('allow_url_fopen')) { - $default = !$default - ? new StreamHandler() - : Middleware::wrapStreaming($default, new StreamHandler()); - } elseif (!$default) { - throw new \RuntimeException('Guzzle requires cURL, the ' - . 'allow_url_fopen ini setting, or a custom HTTP handler.'); - } - - return $future ? Middleware::wrapFuture($default, $future) : $default; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/.gitignore b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/.gitignore deleted file mode 100644 index 290a945241..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -vendor -build/artifacts/ -composer.lock -docs/_build/ diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/.travis.yml b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/.travis.yml deleted file mode 100644 index e43fbdd9cd..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: php - -php: - - 5.4 - - 5.5 - - 5.6 - - 7.0 - - hhvm - -before_script: - - composer self-update - - composer install --no-interaction --prefer-source --dev - - ~/.nvm/nvm.sh install v0.6.14 - - ~/.nvm/nvm.sh run v0.6.14 - -script: - - make test - -matrix: - allow_failures: - - php: hhvm - fast_finish: true diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/CHANGELOG.md b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/CHANGELOG.md deleted file mode 100644 index d399d82624..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/CHANGELOG.md +++ /dev/null @@ -1,54 +0,0 @@ -# CHANGELOG - -## 1.1.0 - 2015-05-19 - -* Added `CURL_HTTP_VERSION_2_0` -* The PHP stream wrapper handler now sets `allow_self_signed` to `false` to - match the cURL handler when `verify` is set to `true` or a certificate file. -* Ensuring that a directory exists before using the `save_to` option. -* Response protocol version is now correctly extracted from a response. -* Fixed a bug in which the result of `CurlFactory::retryFailedRewind` did not - return an array. - -## 1.0.7 - 2015-03-29 - -* PHP 7 fixes. - -## 1.0.6 - 2015-02-26 - -* Bug fix: futures now extend from React's PromiseInterface to ensure that they - are properly forwarded down the promise chain. -* The multi handle of the CurlMultiHandler is now created lazily. - -## 1.0.5 - 2014-12-10 - -* Adding more error information to PHP stream wrapper exceptions. -* Added digest auth integration test support to test server. - -## 1.0.4 - 2014-12-01 - -* Added support for older versions of cURL that do not have CURLOPT_TIMEOUT_MS. -* Setting debug to `false` does not enable debug output. -* Added a fix to the StreamHandler to return a `FutureArrayInterface` when an - error occurs. - -## 1.0.3 - 2014-11-03 - -* Setting the `header` stream option as a string to be compatible with GAE. -* Header parsing now ensures that header order is maintained in the parsed - message. - -## 1.0.2 - 2014-10-28 - -* Now correctly honoring a `version` option is supplied in a request. - See https://github.com/guzzle/RingPHP/pull/8 - -## 1.0.1 - 2014-10-26 - -* Fixed a header parsing issue with the `CurlHandler` and `CurlMultiHandler` - that caused cURL requests with multiple responses to merge repsonses together - (e.g., requests with digest authentication). - -## 1.0.0 - 2014-10-12 - -* Initial release. diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/LICENSE b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/LICENSE deleted file mode 100644 index 71d3b783cb..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2014 Michael Dowling, https://github.com/mtdowling - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/Makefile b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/Makefile deleted file mode 100644 index 21c812e381..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -all: clean coverage docs - -docs: - cd docs && make html - -view-docs: - open docs/_build/html/index.html - -start-server: stop-server - node tests/Client/server.js &> /dev/null & - -stop-server: - @PID=$(shell ps axo pid,command \ - | grep 'tests/Client/server.js' \ - | grep -v grep \ - | cut -f 1 -d " "\ - ) && [ -n "$$PID" ] && kill $$PID || true - -test: start-server - vendor/bin/phpunit $(TEST) - $(MAKE) stop-server - -coverage: start-server - vendor/bin/phpunit --coverage-html=build/artifacts/coverage $(TEST) - $(MAKE) stop-server - -view-coverage: - open build/artifacts/coverage/index.html - -clean: - rm -rf build/artifacts/* - cd docs && make clean - -tag: - $(if $(TAG),,$(error TAG is not defined. Pass via "make tag TAG=4.2.1")) - @echo Tagging $(TAG) - chag update -m '$(TAG) ()' - git add -A - git commit -m '$(TAG) release' - chag tag - -perf: start-server - php tests/perf.php - $(MAKE) stop-server - -.PHONY: docs diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/README.rst b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/README.rst deleted file mode 100644 index 10374e813b..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/README.rst +++ /dev/null @@ -1,46 +0,0 @@ -======= -RingPHP -======= - -Provides a simple API and specification that abstracts away the details of HTTP -into a single PHP function. RingPHP be used to power HTTP clients and servers -through a PHP function that accepts a request hash and returns a response hash -that is fulfilled using a `promise `_, -allowing RingPHP to support both synchronous and asynchronous workflows. - -By abstracting the implementation details of different HTTP clients and -servers, RingPHP allows you to utilize pluggable HTTP clients and servers -without tying your application to a specific implementation. - -.. code-block:: php - - 'GET', - 'uri' => '/', - 'headers' => [ - 'host' => ['www.google.com'], - 'x-foo' => ['baz'] - ] - ]); - - $response->then(function (array $response) { - echo $response['status']; - }); - - $response->wait(); - -RingPHP is inspired by Clojure's `Ring `_, -which, in turn, was inspired by Python's WSGI and Ruby's Rack. RingPHP is -utilized as the handler layer in `Guzzle `_ 5.0+ to send -HTTP requests. - -Documentation -------------- - -See http://ringphp.readthedocs.org/ for the full online documentation. diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/composer.json b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/composer.json deleted file mode 100644 index 22002efca7..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/composer.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "guzzlehttp/ringphp", - "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.", - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.4.0", - "guzzlehttp/streams": "~3.0", - "react/promise": "~2.0" - }, - "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "ext-curl": "Guzzle will use specific adapters if cURL is present" - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Ring\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "GuzzleHttp\\Tests\\Ring\\": "tests/" - } - }, - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/Makefile b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/Makefile deleted file mode 100644 index 51270aa5d3..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/Makefile +++ /dev/null @@ -1,153 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/GuzzleRing.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/GuzzleRing.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/GuzzleRing" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/GuzzleRing" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/client_handlers.rst b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/client_handlers.rst deleted file mode 100644 index 3151f00216..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/client_handlers.rst +++ /dev/null @@ -1,173 +0,0 @@ -=============== -Client Handlers -=============== - -Client handlers accept a request array and return a future response array that -can be used synchronously as an array or asynchronously using a promise. - -Built-In Handlers ------------------ - -RingPHP comes with three built-in client handlers. - -Stream Handler -~~~~~~~~~~~~~~ - -The ``GuzzleHttp\Ring\Client\StreamHandler`` uses PHP's -`http stream wrapper `_ to send -requests. - -.. note:: - - This handler cannot send requests concurrently. - -You can provide an associative array of custom stream context options to the -StreamHandler using the ``stream_context`` key of the ``client`` request -option. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\StreamHandler; - - $response = $handler([ - 'http_method' => 'GET', - 'uri' => '/', - 'headers' => ['host' => ['httpbin.org']], - 'client' => [ - 'stream_context' => [ - 'http' => [ - 'request_fulluri' => true, - 'method' => 'HEAD' - ], - 'socket' => [ - 'bindto' => '127.0.0.1:0' - ], - 'ssl' => [ - 'verify_peer' => false - ] - ] - ] - ]); - - // Even though it's already completed, you can still use a promise - $response->then(function ($response) { - echo $response['status']; // 200 - }); - - // Or access the response using the future interface - echo $response['status']; // 200 - -cURL Handler -~~~~~~~~~~~~ - -The ``GuzzleHttp\Ring\Client\CurlHandler`` can be used with PHP 5.5+ to send -requests using cURL easy handles. This handler is great for sending requests -one at a time because the execute and select loop is implemented in C code -which executes faster and consumes less memory than using PHP's -``curl_multi_*`` interface. - -.. note:: - - This handler cannot send requests concurrently. - -When using the CurlHandler, custom curl options can be specified as an -associative array of `cURL option constants `_ -mapping to values in the ``client`` option of a requst using the **curl** key. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\CurlHandler; - - $handler = new CurlHandler(); - - $request = [ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'client' => ['curl' => [CURLOPT_LOW_SPEED_LIMIT => 10]] - ]; - - $response = $handler($request); - - // The response can be used directly as an array. - echo $response['status']; // 200 - - // Or, it can be used as a promise (that has already fulfilled). - $response->then(function ($response) { - echo $response['status']; // 200 - }); - -cURL Multi Handler -~~~~~~~~~~~~~~~~~~ - -The ``GuzzleHttp\Ring\Client\CurlMultiHandler`` transfers requests using -cURL's `multi API `_. The -``CurlMultiHandler`` is great for sending requests concurrently. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\CurlMultiHandler; - - $handler = new CurlMultiHandler(); - - $request = [ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]] - ]; - - // this call returns a future array immediately. - $response = $handler($request); - - // Ideally, you should use the promise API to not block. - $response - ->then(function ($response) { - // Got the response at some point in the future - echo $response['status']; // 200 - // Don't break the chain - return $response; - })->then(function ($response) { - // ... - }); - - // If you really need to block, then you can use the response as an - // associative array. This will block until it has completed. - echo $response['status']; // 200 - -Just like the ``CurlHandler``, the ``CurlMultiHandler`` accepts custom curl -option in the ``curl`` key of the ``client`` request option. - -Mock Handler -~~~~~~~~~~~~ - -The ``GuzzleHttp\Ring\Client\MockHandler`` is used to return mock responses. -When constructed, the handler can be configured to return the same response -array over and over, a future response, or a the evaluation of a callback -function. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\MockHandler; - - // Return a canned response. - $mock = new MockHandler(['status' => 200]); - $response = $mock([]); - assert(200 == $response['status']); - assert([] == $response['headers']); - -Implementing Handlers ---------------------- - -Client handlers are just PHP callables (functions or classes that have the -``__invoke`` magic method). The callable accepts a request array and MUST -return an instance of ``GuzzleHttp\Ring\Future\FutureArrayInterface`` so that -the response can be used by both blocking and non-blocking consumers. - -Handlers need to follow a few simple rules: - -1. Do not throw exceptions. If an error is encountered, return an array that - contains the ``error`` key that maps to an ``\Exception`` value. -2. If the request has a ``delay`` client option, then the handler should only - send the request after the specified delay time in seconds. Blocking - handlers may find it convenient to just let the - ``GuzzleHttp\Ring\Core::doSleep($request)`` function handle this for them. -3. Always return an instance of ``GuzzleHttp\Ring\Future\FutureArrayInterface``. -4. Complete any outstanding requests when the handler is destructed. diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/client_middleware.rst b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/client_middleware.rst deleted file mode 100644 index 5a2c1a8abd..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/client_middleware.rst +++ /dev/null @@ -1,165 +0,0 @@ -================= -Client Middleware -================= - -Middleware intercepts requests before they are sent over the wire and can be -used to add functionality to handlers. - -Modifying Requests ------------------- - -Let's say you wanted to modify requests before they are sent over the wire -so that they always add specific headers. This can be accomplished by creating -a function that accepts a handler and returns a new function that adds the -composed behavior. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\CurlHandler; - - $handler = new CurlHandler(); - - $addHeaderHandler = function (callable $handler, array $headers = []) { - return function (array $request) use ($handler, $headers) { - // Add our custom headers - foreach ($headers as $key => $value) { - $request['headers'][$key] = $value; - } - - // Send the request using the handler and return the response. - return $handler($request); - } - }; - - // Create a new handler that adds headers to each request. - $handler = $addHeaderHandler($handler, [ - 'X-AddMe' => 'hello', - 'Authorization' => 'Basic xyz' - ]); - - $response = $handler([ - 'http_method' => 'GET', - 'headers' => ['Host' => ['httpbin.org']] - ]); - -Modifying Responses -------------------- - -You can change a response as it's returned from a middleware. Remember that -responses returned from an handler (including middleware) must implement -``GuzzleHttp\Ring\Future\FutureArrayInterface``. In order to be a good citizen, -you should not expect that the responses returned through your middleware will -be completed synchronously. Instead, you should use the -``GuzzleHttp\Ring\Core::proxy()`` function to modify the response when the -underlying promise is resolved. This function is a helper function that makes it -easy to create a new instance of ``FutureArrayInterface`` that wraps an existing -``FutureArrayInterface`` object. - -Let's say you wanted to add headers to a response as they are returned from -your middleware, but you want to make sure you aren't causing future -responses to be dereferenced right away. You can achieve this by modifying the -incoming request and using the ``Core::proxy`` function. - -.. code-block:: php - - use GuzzleHttp\Ring\Core; - use GuzzleHttp\Ring\Client\CurlHandler; - - $handler = new CurlHandler(); - - $responseHeaderHandler = function (callable $handler, array $headers) { - return function (array $request) use ($handler, $headers) { - // Send the request using the wrapped handler. - return Core::proxy($handler($request), function ($response) use ($headers) { - // Add the headers to the response when it is available. - foreach ($headers as $key => $value) { - $response['headers'][$key] = (array) $value; - } - // Note that you can return a regular response array when using - // the proxy method. - return $response; - }); - } - }; - - // Create a new handler that adds headers to each response. - $handler = $responseHeaderHandler($handler, ['X-Header' => 'hello!']); - - $response = $handler([ - 'http_method' => 'GET', - 'headers' => ['Host' => ['httpbin.org']] - ]); - - assert($response['headers']['X-Header'] == 'hello!'); - -Built-In Middleware -------------------- - -RingPHP comes with a few basic client middlewares that modify requests -and responses. - -Streaming Middleware -~~~~~~~~~~~~~~~~~~~~ - -If you want to send all requests with the ``streaming`` option to a specific -handler but other requests to a different handler, then use the streaming -middleware. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\CurlHandler; - use GuzzleHttp\Ring\Client\StreamHandler; - use GuzzleHttp\Ring\Client\Middleware; - - $defaultHandler = new CurlHandler(); - $streamingHandler = new StreamHandler(); - $streamingHandler = Middleware::wrapStreaming( - $defaultHandler, - $streamingHandler - ); - - // Send the request using the streaming handler. - $response = $streamingHandler([ - 'http_method' => 'GET', - 'headers' => ['Host' => ['www.google.com']], - 'stream' => true - ]); - - // Send the request using the default handler. - $response = $streamingHandler([ - 'http_method' => 'GET', - 'headers' => ['Host' => ['www.google.com']] - ]); - -Future Middleware -~~~~~~~~~~~~~~~~~ - -If you want to send all requests with the ``future`` option to a specific -handler but other requests to a different handler, then use the future -middleware. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\CurlHandler; - use GuzzleHttp\Ring\Client\CurlMultiHandler; - use GuzzleHttp\Ring\Client\Middleware; - - $defaultHandler = new CurlHandler(); - $futureHandler = new CurlMultiHandler(); - $futureHandler = Middleware::wrapFuture( - $defaultHandler, - $futureHandler - ); - - // Send the request using the blocking CurlHandler. - $response = $futureHandler([ - 'http_method' => 'GET', - 'headers' => ['Host' => ['www.google.com']] - ]); - - // Send the request using the non-blocking CurlMultiHandler. - $response = $futureHandler([ - 'http_method' => 'GET', - 'headers' => ['Host' => ['www.google.com']], - 'future' => true - ]); diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/conf.py b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/conf.py deleted file mode 100644 index c6404aa1e1..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/conf.py +++ /dev/null @@ -1,23 +0,0 @@ -import sys, os -import sphinx_rtd_theme -from sphinx.highlighting import lexers -from pygments.lexers.web import PhpLexer - - -lexers['php'] = PhpLexer(startinline=True, linenos=1) -lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1) -primary_domain = 'php' - -extensions = [] -templates_path = ['_templates'] -source_suffix = '.rst' -master_doc = 'index' -project = u'RingPHP' -copyright = u'2014, Michael Dowling' -version = '1.0.0-alpha' -exclude_patterns = ['_build'] - -html_title = "RingPHP" -html_short_title = "RingPHP" -html_theme = "sphinx_rtd_theme" -html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/futures.rst b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/futures.rst deleted file mode 100644 index af29cb3780..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/futures.rst +++ /dev/null @@ -1,164 +0,0 @@ -======= -Futures -======= - -Futures represent a computation that may have not yet completed. RingPHP -uses hybrid of futures and promises to provide a consistent API that can be -used for both blocking and non-blocking consumers. - -Promises --------- - -You can get the result of a future when it is ready using the promise interface -of a future. Futures expose a promise API via a ``then()`` method that utilizes -`React's promise library `_. You should -use this API when you do not wish to block. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\CurlMultiHandler; - - $request = [ - 'http_method' => 'GET', - 'uri' => '/', - 'headers' => ['host' => ['httpbin.org']] - ]; - - $response = $handler($request); - - // Use the then() method to use the promise API of the future. - $response->then(function ($response) { - echo $response['status']; - }); - -You can get the promise used by a future, an instance of -``React\Promise\PromiseInterface``, by calling the ``promise()`` method. - -.. code-block:: php - - $response = $handler($request); - $promise = $response->promise(); - $promise->then(function ($response) { - echo $response['status']; - }); - -This promise value can be used with React's -`aggregate promise functions `_. - -Waiting -------- - -You can wait on a future to complete and retrieve the value, or *dereference* -the future, using the ``wait()`` method. Calling the ``wait()`` method of a -future will block until the result is available. The result is then returned or -an exception is thrown if and exception was encountered while waiting on the -the result. Subsequent calls to dereference a future will return the previously -completed result or throw the previously encountered exception. Futures can be -cancelled, which stops the computation if possible. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\CurlMultiHandler; - - $response = $handler([ - 'http_method' => 'GET', - 'uri' => '/', - 'headers' => ['host' => ['httpbin.org']] - ]); - - // You can explicitly call block to wait on a result. - $realizedResponse = $response->wait(); - - // Future responses can be used like a regular PHP array. - echo $response['status']; - -In addition to explicitly calling the ``wait()`` function, using a future like -a normal value will implicitly trigger the ``wait()`` function. - -Future Responses ----------------- - -RingPHP uses futures to return asynchronous responses immediately. Client -handlers always return future responses that implement -``GuzzleHttp\Ring\Future\ArrayFutureInterface``. These future responses act -just like normal PHP associative arrays for blocking access and provide a -promise interface for non-blocking access. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\CurlMultiHandler; - - $handler = new CurlMultiHandler(); - - $request = [ - 'http_method' => 'GET', - 'uri' => '/', - 'headers' => ['Host' => ['www.google.com']] - ]; - - $response = $handler($request); - - // Use the promise API for non-blocking access to the response. The actual - // response value will be delivered to the promise. - $response->then(function ($response) { - echo $response['status']; - }); - - // You can wait (block) until the future is completed. - $response->wait(); - - // This will implicitly call wait(), and will block too! - $response['status']; - -.. important:: - - Futures that are not completed by the time the underlying handler is - destructed will be completed when the handler is shutting down. - -Cancelling ----------- - -Futures can be cancelled if they have not already been dereferenced. - -RingPHP futures are typically implemented with the -``GuzzleHttp\Ring\Future\BaseFutureTrait``. This trait provides the cancellation -functionality that should be common to most implementations. Cancelling a -future response will try to prevent the request from sending over the wire. - -When a future is cancelled, the cancellation function is invoked and performs -the actual work needed to cancel the request from sending if possible -(e.g., telling an event loop to stop sending a request or to close a socket). -If no cancellation function is provided, then a request cannot be cancelled. If -a cancel function is provided, then it should accept the future as an argument -and return true if the future was successfully cancelled or false if it could -not be cancelled. - -Wrapping an existing Promise ----------------------------- - -You can easily create a future from any existing promise using the -``GuzzleHttp\Ring\Future\FutureValue`` class. This class's constructor -accepts a promise as the first argument, a wait function as the second -argument, and a cancellation function as the third argument. The dereference -function is used to force the promise to resolve (for example, manually ticking -an event loop). The cancel function is optional and is used to tell the thing -that created the promise that it can stop computing the result (for example, -telling an event loop to stop transferring a request). - -.. code-block:: php - - use GuzzleHttp\Ring\Future\FutureValue; - use React\Promise\Deferred; - - $deferred = new Deferred(); - $promise = $deferred->promise(); - - $f = new FutureValue( - $promise, - function () use ($deferred) { - // This function is responsible for blocking and resolving the - // promise. Here we pass in a reference to the deferred so that - // it can be resolved or rejected. - $deferred->resolve('foo'); - } - ); diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/index.rst b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/index.rst deleted file mode 100644 index 4bbce631c7..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/index.rst +++ /dev/null @@ -1,50 +0,0 @@ -======= -RingPHP -======= - -Provides a simple API and specification that abstracts away the details of HTTP -into a single PHP function. RingPHP be used to power HTTP clients and servers -through a PHP function that accepts a request hash and returns a response hash -that is fulfilled using a `promise `_, -allowing RingPHP to support both synchronous and asynchronous workflows. - -By abstracting the implementation details of different HTTP clients and -servers, RingPHP allows you to utilize pluggable HTTP clients and servers -without tying your application to a specific implementation. - -.. toctree:: - :maxdepth: 2 - - spec - futures - client_middleware - client_handlers - testing - -.. code-block:: php - - 'GET', - 'uri' => '/', - 'headers' => [ - 'host' => ['www.google.com'], - 'x-foo' => ['baz'] - ] - ]); - - $response->then(function (array $response) { - echo $response['status']; - }); - - $response->wait(); - -RingPHP is inspired by Clojure's `Ring `_, -which, in turn, was inspired by Python's WSGI and Ruby's Rack. RingPHP is -utilized as the handler layer in `Guzzle `_ 5.0+ to send -HTTP requests. diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/requirements.txt b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/requirements.txt deleted file mode 100644 index 483a4e9600..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -sphinx_rtd_theme diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/spec.rst b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/spec.rst deleted file mode 100644 index bc9107898f..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/spec.rst +++ /dev/null @@ -1,311 +0,0 @@ -============= -Specification -============= - -RingPHP applications consist of handlers, requests, responses, and -middleware. - -Handlers --------- - -Handlers are implemented as a PHP ``callable`` that accept a request array -and return a response array (``GuzzleHttp\Ring\Future\FutureArrayInterface``). - -For example: - -.. code-block:: php - - use GuzzleHttp\Ring\Future\CompletedFutureArray; - - $mockHandler = function (array $request) { - return new CompletedFutureArray([ - 'status' => 200, - 'headers' => ['X-Foo' => ['Bar']], - 'body' => 'Hello!' - ]); - }; - -This handler returns the same response each time it is invoked. All RingPHP -handlers must return a ``GuzzleHttp\Ring\Future\FutureArrayInterface``. Use -``GuzzleHttp\Ring\Future\CompletedFutureArray`` when returning a response that -has already completed. - -Requests --------- - -A request array is a PHP associative array that contains the configuration -settings need to send a request. - -.. code-block:: php - - $request = [ - 'http_method' => 'GET', - 'scheme' => 'http', - 'uri' => '/', - 'body' => 'hello!', - 'client' => ['timeout' => 1.0], - 'headers' => [ - 'host' => ['httpbin.org'], - 'X-Foo' => ['baz', 'bar'] - ] - ]; - -The request array contains the following key value pairs: - -request_method - (string, required) The HTTP request method, must be all caps corresponding - to a HTTP request method, such as ``GET`` or ``POST``. - -scheme - (string) The transport protocol, must be one of ``http`` or ``https``. - Defaults to ``http``. - -uri - (string, required) The request URI excluding the query string. Must - start with "/". - -query_string - (string) The query string, if present (e.g., ``foo=bar``). - -version - (string) HTTP protocol version. Defaults to ``1.1``. - -headers - (required, array) Associative array of headers. Each key represents the - header name. Each value contains an array of strings where each entry of - the array SHOULD be sent over the wire on a separate header line. - -body - (string, fopen resource, ``Iterator``, ``GuzzleHttp\Stream\StreamInterface``) - The body of the request, if present. Can be a string, resource returned - from fopen, an ``Iterator`` that yields chunks of data, an object that - implemented ``__toString``, or a ``GuzzleHttp\Stream\StreamInterface``. - -future - (bool, string) Controls the asynchronous behavior of a response. - - Set to ``true`` or omit the ``future`` option to *request* that a request - will be completed asynchronously. Keep in mind that your request might not - necessarily be completed asynchronously based on the handler you are using. - Set the ``future`` option to ``false`` to request that a synchronous - response be provided. - - You can provide a string value to specify fine-tuned future behaviors that - may be specific to the underlying handlers you are using. There are, - however, some common future options that handlers should implement if - possible. - - lazy - Requests that the handler does not open and send the request - immediately, but rather only opens and sends the request once the - future is dereferenced. This option is often useful for sending a large - number of requests concurrently to allow handlers to take better - advantage of non-blocking transfers by first building up a pool of - requests. - - If an handler does not implement or understand a provided string value, - then the request MUST be treated as if the user provided ``true`` rather - than the string value. - - Future responses created by asynchronous handlers MUST attempt to complete - any outstanding future responses when they are destructed. Asynchronous - handlers MAY choose to automatically complete responses when the number - of outstanding requests reaches an handler-specific threshold. - -Client Specific Options -~~~~~~~~~~~~~~~~~~~~~~~ - -The following options are only used in ring client handlers. - -.. _client-options: - -client - (array) Associative array of client specific transfer options. The - ``client`` request key value pair can contain the following keys: - - cert - (string, array) Set to a string to specify the path to a file - containing a PEM formatted SSL client side certificate. If a password - is required, then set ``cert`` to an array containing the path to the - PEM file in the first array element followed by the certificate - password in the second array element. - - connect_timeout - (float) Float describing the number of seconds to wait while trying to - connect to a server. Use ``0`` to wait indefinitely (the default - behavior). - - debug - (bool, fopen() resource) Set to true or set to a PHP stream returned by - fopen() to enable debug output with the handler used to send a request. - If set to ``true``, the output is written to PHP's STDOUT. If a PHP - ``fopen`` resource handle is provided, the output is written to the - stream. - - "Debug output" is handler specific: different handlers will yield - different output and various various level of detail. For example, when - using cURL to transfer requests, cURL's `CURLOPT_VERBOSE `_ - will be used. When using the PHP stream wrapper, `stream notifications `_ - will be emitted. - - decode_content - (bool) Specify whether or not ``Content-Encoding`` responses - (gzip, deflate, etc.) are automatically decoded. Set to ``true`` to - automatically decode encoded responses. Set to ``false`` to not decode - responses. By default, content is *not* decoded automatically. - - delay - (int) The number of milliseconds to delay before sending the request. - This is often used for delaying before retrying a request. Handlers - SHOULD implement this if possible, but it is not a strict requirement. - - progress - (function) Defines a function to invoke when transfer progress is made. - The function accepts the following arguments: - - 1. The total number of bytes expected to be downloaded - 2. The number of bytes downloaded so far - 3. The number of bytes expected to be uploaded - 4. The number of bytes uploaded so far - - proxy - (string, array) Pass a string to specify an HTTP proxy, or an - associative array to specify different proxies for different protocols - where the scheme is the key and the value is the proxy address. - - .. code-block:: php - - $request = [ - 'http_method' => 'GET', - 'headers' => ['host' => ['httpbin.org']], - 'client' => [ - // Use different proxies for different URI schemes. - 'proxy' => [ - 'http' => 'http://proxy.example.com:5100', - 'https' => 'https://proxy.example.com:6100' - ] - ] - ]; - - ssl_key - (string, array) Specify the path to a file containing a private SSL key - in PEM format. If a password is required, then set to an array - containing the path to the SSL key in the first array element followed - by the password required for the certificate in the second element. - - save_to - (string, fopen resource, ``GuzzleHttp\Stream\StreamInterface``) - Specifies where the body of the response is downloaded. Pass a string to - open a local file on disk and save the output to the file. Pass an fopen - resource to save the output to a PHP stream resource. Pass a - ``GuzzleHttp\Stream\StreamInterface`` to save the output to a Guzzle - StreamInterface. Omitting this option will typically save the body of a - response to a PHP temp stream. - - stream - (bool) Set to true to stream a response rather than download it all - up-front. This option will only be utilized when the corresponding - handler supports it. - - timeout - (float) Float describing the timeout of the request in seconds. Use 0 to - wait indefinitely (the default behavior). - - verify - (bool, string) Describes the SSL certificate verification behavior of a - request. Set to true to enable SSL certificate verification using the - system CA bundle when available (the default). Set to false to disable - certificate verification (this is insecure!). Set to a string to provide - the path to a CA bundle on disk to enable verification using a custom - certificate. - - version - (string) HTTP protocol version to use with the request. - -Server Specific Options -~~~~~~~~~~~~~~~~~~~~~~~ - -The following options are only used in ring server handlers. - -server_port - (integer) The port on which the request is being handled. This is only - used with ring servers, and is required. - -server_name - (string) The resolved server name, or the server IP address. Required when - using a Ring server. - -remote_addr - (string) The IP address of the client or the last proxy that sent the - request. Required when using a Ring server. - -Responses ---------- - -A response is an array-like object that implements -``GuzzleHttp\Ring\Future\FutureArrayInterface``. Responses contain the -following key value pairs: - -body - (string, fopen resource, ``Iterator``, ``GuzzleHttp\Stream\StreamInterface``) - The body of the response, if present. Can be a string, resource returned - from fopen, an ``Iterator`` that yields chunks of data, an object that - implemented ``__toString``, or a ``GuzzleHttp\Stream\StreamInterface``. - -effective_url - (string) The URL that returned the resulting response. - -error - (``\Exception``) Contains an exception describing any errors that were - encountered during the transfer. - -headers - (Required, array) Associative array of headers. Each key represents the - header name. Each value contains an array of strings where each entry of - the array is a header line. The headers array MAY be an empty array in the - event an error occurred before a response was received. - -reason - (string) Optional reason phrase. This option should be provided when the - reason phrase does not match the typical reason phrase associated with the - ``status`` code. See `RFC 7231 `_ - for a list of HTTP reason phrases mapped to status codes. - -status - (Required, integer) The HTTP status code. The status code MAY be set to - ``null`` in the event an error occurred before a response was received - (e.g., a networking error). - -transfer_stats - (array) Provides an associative array of arbitrary transfer statistics if - provided by the underlying handler. - -version - (string) HTTP protocol version. Defaults to ``1.1``. - -Middleware ----------- - -Ring middleware augments the functionality of handlers by invoking them in the -process of generating responses. Middleware is typically implemented as a -higher-order function that takes one or more handlers as arguments followed by -an optional associative array of options as the last argument, returning a new -handler with the desired compound behavior. - -Here's an example of a middleware that adds a Content-Type header to each -request. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\CurlHandler; - use GuzzleHttp\Ring\Core; - - $contentTypeHandler = function(callable $handler, $contentType) { - return function (array $request) use ($handler, $contentType) { - return $handler(Core::setHeader('Content-Type', $contentType)); - }; - }; - - $baseHandler = new CurlHandler(); - $wrappedHandler = $contentTypeHandler($baseHandler, 'text/html'); - $response = $wrappedHandler([/** request hash **/]); diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/testing.rst b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/testing.rst deleted file mode 100644 index 9df2562ed4..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/docs/testing.rst +++ /dev/null @@ -1,74 +0,0 @@ -======= -Testing -======= - -RingPHP tests client handlers using `PHPUnit `_ and a -built-in node.js web server. - -Running Tests -------------- - -First, install the dependencies using `Composer `_. - - composer.phar install - -Next, run the unit tests using ``Make``. - - make test - -The tests are also run on Travis-CI on each commit: https://travis-ci.org/guzzle/guzzle-ring - -Test Server ------------ - -Testing client handlers usually involves actually sending HTTP requests. -RingPHP provides a node.js web server that returns canned responses and -keep a list of the requests that have been received. The server can then -be queried to get a list of the requests that were sent by the client so that -you can ensure that the client serialized and transferred requests as intended. - -The server keeps a list of queued responses and returns responses that are -popped off of the queue as HTTP requests are received. When there are not -more responses to serve, the server returns a 500 error response. - -The test server uses the ``GuzzleHttp\Tests\Ring\Client\Server`` class to -control the server. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\StreamHandler; - use GuzzleHttp\Tests\Ring\Client\Server; - - // First return a 200 followed by a 404 response. - Server::enqueue([ - ['status' => 200], - ['status' => 404] - ]); - - $handler = new StreamHandler(); - - $response = $handler([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'uri' => '/' - ]); - - assert(200 == $response['status']); - - $response = $handler([ - 'http_method' => 'HEAD', - 'headers' => ['host' => [Server::$host]], - 'uri' => '/' - ]); - - assert(404 == $response['status']); - -After requests have been sent, you can get a list of the requests as they -were sent over the wire to ensure they were sent correctly. - -.. code-block:: php - - $received = Server::received(); - - assert('GET' == $received[0]['http_method']); - assert('HEAD' == $received[1]['http_method']); diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/phpunit.xml.dist b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/phpunit.xml.dist deleted file mode 100644 index 1d19290250..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/phpunit.xml.dist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - tests - - - - - src - - - diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Client/ClientUtils.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Client/ClientUtils.php deleted file mode 100644 index 2acf92eba0..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Client/ClientUtils.php +++ /dev/null @@ -1,74 +0,0 @@ -getDefaultOptions($request, $headers); - $this->applyMethod($request, $options); - - if (isset($request['client'])) { - $this->applyHandlerOptions($request, $options); - } - - $this->applyHeaders($request, $options); - unset($options['_headers']); - - // Add handler options from the request's configuration options - if (isset($request['client']['curl'])) { - $options = $this->applyCustomCurlOptions( - $request['client']['curl'], - $options - ); - } - - if (!$handle) { - $handle = curl_init(); - } - - $body = $this->getOutputBody($request, $options); - curl_setopt_array($handle, $options); - - return [$handle, &$headers, $body]; - } - - /** - * Creates a response hash from a cURL result. - * - * @param callable $handler Handler that was used. - * @param array $request Request that sent. - * @param array $response Response hash to update. - * @param array $headers Headers received during transfer. - * @param resource $body Body fopen response. - * - * @return array - */ - public static function createResponse( - callable $handler, - array $request, - array $response, - array $headers, - $body - ) { - if (isset($response['transfer_stats']['url'])) { - $response['effective_url'] = $response['transfer_stats']['url']; - } - - if (!empty($headers)) { - $startLine = explode(' ', array_shift($headers), 3); - $headerList = Core::headersFromLines($headers); - $response['headers'] = $headerList; - $response['version'] = isset($startLine[0]) ? substr($startLine[0], 5) : null; - $response['status'] = isset($startLine[1]) ? (int) $startLine[1] : null; - $response['reason'] = isset($startLine[2]) ? $startLine[2] : null; - $response['body'] = $body; - Core::rewindBody($response); - } - - return !empty($response['curl']['errno']) || !isset($response['status']) - ? self::createErrorResponse($handler, $request, $response) - : $response; - } - - private static function createErrorResponse( - callable $handler, - array $request, - array $response - ) { - static $connectionErrors = [ - CURLE_OPERATION_TIMEOUTED => true, - CURLE_COULDNT_RESOLVE_HOST => true, - CURLE_COULDNT_CONNECT => true, - CURLE_SSL_CONNECT_ERROR => true, - CURLE_GOT_NOTHING => true, - ]; - - // Retry when nothing is present or when curl failed to rewind. - if (!isset($response['err_message']) - && (empty($response['curl']['errno']) - || $response['curl']['errno'] == 65) - ) { - return self::retryFailedRewind($handler, $request, $response); - } - - $message = isset($response['err_message']) - ? $response['err_message'] - : sprintf('cURL error %s: %s', - $response['curl']['errno'], - isset($response['curl']['error']) - ? $response['curl']['error'] - : 'See http://curl.haxx.se/libcurl/c/libcurl-errors.html'); - - $error = isset($response['curl']['errno']) - && isset($connectionErrors[$response['curl']['errno']]) - ? new ConnectException($message) - : new RingException($message); - - return $response + [ - 'status' => null, - 'reason' => null, - 'body' => null, - 'headers' => [], - 'error' => $error, - ]; - } - - private function getOutputBody(array $request, array &$options) - { - // Determine where the body of the response (if any) will be streamed. - if (isset($options[CURLOPT_WRITEFUNCTION])) { - return $request['client']['save_to']; - } - - if (isset($options[CURLOPT_FILE])) { - return $options[CURLOPT_FILE]; - } - - if ($request['http_method'] != 'HEAD') { - // Create a default body if one was not provided - return $options[CURLOPT_FILE] = fopen('php://temp', 'w+'); - } - - return null; - } - - private function getDefaultOptions(array $request, array &$headers) - { - $url = Core::url($request); - $startingResponse = false; - - $options = [ - '_headers' => $request['headers'], - CURLOPT_CUSTOMREQUEST => $request['http_method'], - CURLOPT_URL => $url, - CURLOPT_RETURNTRANSFER => false, - CURLOPT_HEADER => false, - CURLOPT_CONNECTTIMEOUT => 150, - CURLOPT_HEADERFUNCTION => function ($ch, $h) use (&$headers, &$startingResponse) { - $value = trim($h); - if ($value === '') { - $startingResponse = true; - } elseif ($startingResponse) { - $startingResponse = false; - $headers = [$value]; - } else { - $headers[] = $value; - } - return strlen($h); - }, - ]; - - if (isset($request['version'])) { - if ($request['version'] == 2.0) { - $options[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0; - } else if ($request['version'] == 1.1) { - $options[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1; - } else { - $options[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0; - } - } - - if (defined('CURLOPT_PROTOCOLS')) { - $options[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; - } - - return $options; - } - - private function applyMethod(array $request, array &$options) - { - if (isset($request['body'])) { - $this->applyBody($request, $options); - return; - } - - switch ($request['http_method']) { - case 'PUT': - case 'POST': - // See http://tools.ietf.org/html/rfc7230#section-3.3.2 - if (!Core::hasHeader($request, 'Content-Length')) { - $options[CURLOPT_HTTPHEADER][] = 'Content-Length: 0'; - } - break; - case 'HEAD': - $options[CURLOPT_NOBODY] = true; - unset( - $options[CURLOPT_WRITEFUNCTION], - $options[CURLOPT_READFUNCTION], - $options[CURLOPT_FILE], - $options[CURLOPT_INFILE] - ); - } - } - - private function applyBody(array $request, array &$options) - { - $contentLength = Core::firstHeader($request, 'Content-Length'); - $size = $contentLength !== null ? (int) $contentLength : null; - - // Send the body as a string if the size is less than 1MB OR if the - // [client][curl][body_as_string] request value is set. - if (($size !== null && $size < 1000000) || - isset($request['client']['curl']['body_as_string']) || - is_string($request['body']) - ) { - $options[CURLOPT_POSTFIELDS] = Core::body($request); - // Don't duplicate the Content-Length header - $this->removeHeader('Content-Length', $options); - $this->removeHeader('Transfer-Encoding', $options); - } else { - $options[CURLOPT_UPLOAD] = true; - if ($size !== null) { - // Let cURL handle setting the Content-Length header - $options[CURLOPT_INFILESIZE] = $size; - $this->removeHeader('Content-Length', $options); - } - $this->addStreamingBody($request, $options); - } - - // If the Expect header is not present, prevent curl from adding it - if (!Core::hasHeader($request, 'Expect')) { - $options[CURLOPT_HTTPHEADER][] = 'Expect:'; - } - - // cURL sometimes adds a content-type by default. Prevent this. - if (!Core::hasHeader($request, 'Content-Type')) { - $options[CURLOPT_HTTPHEADER][] = 'Content-Type:'; - } - } - - private function addStreamingBody(array $request, array &$options) - { - $body = $request['body']; - - if ($body instanceof StreamInterface) { - $options[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) { - return (string) $body->read($length); - }; - if (!isset($options[CURLOPT_INFILESIZE])) { - if ($size = $body->getSize()) { - $options[CURLOPT_INFILESIZE] = $size; - } - } - } elseif (is_resource($body)) { - $options[CURLOPT_INFILE] = $body; - } elseif ($body instanceof \Iterator) { - $buf = ''; - $options[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body, &$buf) { - if ($body->valid()) { - $buf .= $body->current(); - $body->next(); - } - $result = (string) substr($buf, 0, $length); - $buf = substr($buf, $length); - return $result; - }; - } else { - throw new \InvalidArgumentException('Invalid request body provided'); - } - } - - private function applyHeaders(array $request, array &$options) - { - foreach ($options['_headers'] as $name => $values) { - foreach ($values as $value) { - $options[CURLOPT_HTTPHEADER][] = "$name: $value"; - } - } - - // Remove the Accept header if one was not set - if (!Core::hasHeader($request, 'Accept')) { - $options[CURLOPT_HTTPHEADER][] = 'Accept:'; - } - } - - /** - * Takes an array of curl options specified in the 'curl' option of a - * request's configuration array and maps them to CURLOPT_* options. - * - * This method is only called when a request has a 'curl' config setting. - * - * @param array $config Configuration array of custom curl option - * @param array $options Array of existing curl options - * - * @return array Returns a new array of curl options - */ - private function applyCustomCurlOptions(array $config, array $options) - { - $curlOptions = []; - foreach ($config as $key => $value) { - if (is_int($key)) { - $curlOptions[$key] = $value; - } - } - - return $curlOptions + $options; - } - - /** - * Remove a header from the options array. - * - * @param string $name Case-insensitive header to remove - * @param array $options Array of options to modify - */ - private function removeHeader($name, array &$options) - { - foreach (array_keys($options['_headers']) as $key) { - if (!strcasecmp($key, $name)) { - unset($options['_headers'][$key]); - return; - } - } - } - - /** - * Applies an array of request client options to a the options array. - * - * This method uses a large switch rather than double-dispatch to save on - * high overhead of calling functions in PHP. - */ - private function applyHandlerOptions(array $request, array &$options) - { - foreach ($request['client'] as $key => $value) { - switch ($key) { - // Violating PSR-4 to provide more room. - case 'verify': - - if ($value === false) { - unset($options[CURLOPT_CAINFO]); - $options[CURLOPT_SSL_VERIFYHOST] = 0; - $options[CURLOPT_SSL_VERIFYPEER] = false; - continue; - } - - $options[CURLOPT_SSL_VERIFYHOST] = 2; - $options[CURLOPT_SSL_VERIFYPEER] = true; - - if (is_string($value)) { - $options[CURLOPT_CAINFO] = $value; - if (!file_exists($value)) { - throw new \InvalidArgumentException( - "SSL CA bundle not found: $value" - ); - } - } - break; - - case 'decode_content': - - if ($value === false) { - continue; - } - - $accept = Core::firstHeader($request, 'Accept-Encoding'); - if ($accept) { - $options[CURLOPT_ENCODING] = $accept; - } else { - $options[CURLOPT_ENCODING] = ''; - // Don't let curl send the header over the wire - $options[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:'; - } - break; - - case 'save_to': - - if (is_string($value)) { - if (!is_dir(dirname($value))) { - throw new \RuntimeException(sprintf( - 'Directory %s does not exist for save_to value of %s', - dirname($value), - $value - )); - } - $value = new LazyOpenStream($value, 'w+'); - } - - if ($value instanceof StreamInterface) { - $options[CURLOPT_WRITEFUNCTION] = - function ($ch, $write) use ($value) { - return $value->write($write); - }; - } elseif (is_resource($value)) { - $options[CURLOPT_FILE] = $value; - } else { - throw new \InvalidArgumentException('save_to must be a ' - . 'GuzzleHttp\Stream\StreamInterface or resource'); - } - break; - - case 'timeout': - - if (defined('CURLOPT_TIMEOUT_MS')) { - $options[CURLOPT_TIMEOUT_MS] = $value * 1000; - } else { - $options[CURLOPT_TIMEOUT] = $value; - } - break; - - case 'connect_timeout': - - if (defined('CURLOPT_CONNECTTIMEOUT_MS')) { - $options[CURLOPT_CONNECTTIMEOUT_MS] = $value * 1000; - } else { - $options[CURLOPT_CONNECTTIMEOUT] = $value; - } - break; - - case 'proxy': - - if (!is_array($value)) { - $options[CURLOPT_PROXY] = $value; - } elseif (isset($request['scheme'])) { - $scheme = $request['scheme']; - if (isset($value[$scheme])) { - $options[CURLOPT_PROXY] = $value[$scheme]; - } - } - break; - - case 'cert': - - if (is_array($value)) { - $options[CURLOPT_SSLCERTPASSWD] = $value[1]; - $value = $value[0]; - } - - if (!file_exists($value)) { - throw new \InvalidArgumentException( - "SSL certificate not found: {$value}" - ); - } - - $options[CURLOPT_SSLCERT] = $value; - break; - - case 'ssl_key': - - if (is_array($value)) { - $options[CURLOPT_SSLKEYPASSWD] = $value[1]; - $value = $value[0]; - } - - if (!file_exists($value)) { - throw new \InvalidArgumentException( - "SSL private key not found: {$value}" - ); - } - - $options[CURLOPT_SSLKEY] = $value; - break; - - case 'progress': - - if (!is_callable($value)) { - throw new \InvalidArgumentException( - 'progress client option must be callable' - ); - } - - $options[CURLOPT_NOPROGRESS] = false; - $options[CURLOPT_PROGRESSFUNCTION] = - function () use ($value) { - $args = func_get_args(); - // PHP 5.5 pushed the handle onto the start of the args - if (is_resource($args[0])) { - array_shift($args); - } - call_user_func_array($value, $args); - }; - break; - - case 'debug': - - if ($value) { - $options[CURLOPT_STDERR] = Core::getDebugResource($value); - $options[CURLOPT_VERBOSE] = true; - } - break; - } - } - } - - /** - * This function ensures that a response was set on a transaction. If one - * was not set, then the request is retried if possible. This error - * typically means you are sending a payload, curl encountered a - * "Connection died, retrying a fresh connect" error, tried to rewind the - * stream, and then encountered a "necessary data rewind wasn't possible" - * error, causing the request to be sent through curl_multi_info_read() - * without an error status. - */ - private static function retryFailedRewind( - callable $handler, - array $request, - array $response - ) { - // If there is no body, then there is some other kind of issue. This - // is weird and should probably never happen. - if (!isset($request['body'])) { - $response['err_message'] = 'No response was received for a request ' - . 'with no body. This could mean that you are saturating your ' - . 'network.'; - return self::createErrorResponse($handler, $request, $response); - } - - if (!Core::rewindBody($request)) { - $response['err_message'] = 'The connection unexpectedly failed ' - . 'without providing an error. The request would have been ' - . 'retried, but attempting to rewind the request body failed.'; - return self::createErrorResponse($handler, $request, $response); - } - - // Retry no more than 3 times before giving up. - if (!isset($request['curl']['retries'])) { - $request['curl']['retries'] = 1; - } elseif ($request['curl']['retries'] == 2) { - $response['err_message'] = 'The cURL request was retried 3 times ' - . 'and did no succeed. cURL was unable to rewind the body of ' - . 'the request and subsequent retries resulted in the same ' - . 'error. Turn on the debug option to see what went wrong. ' - . 'See https://bugs.php.net/bug.php?id=47204 for more information.'; - return self::createErrorResponse($handler, $request, $response); - } else { - $request['curl']['retries']++; - } - - return $handler($request); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php deleted file mode 100644 index e00aa4eaca..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php +++ /dev/null @@ -1,135 +0,0 @@ -handles = $this->ownedHandles = []; - $this->factory = isset($options['handle_factory']) - ? $options['handle_factory'] - : new CurlFactory(); - $this->maxHandles = isset($options['max_handles']) - ? $options['max_handles'] - : 5; - } - - public function __destruct() - { - foreach ($this->handles as $handle) { - if (is_resource($handle)) { - curl_close($handle); - } - } - } - - /** - * @param array $request - * - * @return CompletedFutureArray - */ - public function __invoke(array $request) - { - return new CompletedFutureArray( - $this->_invokeAsArray($request) - ); - } - - /** - * @internal - * - * @param array $request - * - * @return array - */ - public function _invokeAsArray(array $request) - { - $factory = $this->factory; - - // Ensure headers are by reference. They're updated elsewhere. - $result = $factory($request, $this->checkoutEasyHandle()); - $h = $result[0]; - $hd =& $result[1]; - $bd = $result[2]; - Core::doSleep($request); - curl_exec($h); - $response = ['transfer_stats' => curl_getinfo($h)]; - $response['curl']['error'] = curl_error($h); - $response['curl']['errno'] = curl_errno($h); - $response['transfer_stats'] = array_merge($response['transfer_stats'], $response['curl']); - $this->releaseEasyHandle($h); - - return CurlFactory::createResponse([$this, '_invokeAsArray'], $request, $response, $hd, $bd); - } - - private function checkoutEasyHandle() - { - // Find an unused handle in the cache - if (false !== ($key = array_search(false, $this->ownedHandles, true))) { - $this->ownedHandles[$key] = true; - return $this->handles[$key]; - } - - // Add a new handle - $handle = curl_init(); - $id = (int) $handle; - $this->handles[$id] = $handle; - $this->ownedHandles[$id] = true; - - return $handle; - } - - private function releaseEasyHandle($handle) - { - $id = (int) $handle; - if (count($this->ownedHandles) > $this->maxHandles) { - curl_close($this->handles[$id]); - unset($this->handles[$id], $this->ownedHandles[$id]); - } else { - // curl_reset doesn't clear these out for some reason - static $unsetValues = [ - CURLOPT_HEADERFUNCTION => null, - CURLOPT_WRITEFUNCTION => null, - CURLOPT_READFUNCTION => null, - CURLOPT_PROGRESSFUNCTION => null, - ]; - curl_setopt_array($handle, $unsetValues); - curl_reset($handle); - $this->ownedHandles[$id] = false; - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php deleted file mode 100644 index b45f6c3979..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php +++ /dev/null @@ -1,250 +0,0 @@ -_mh = $options['mh']; - } - $this->factory = isset($options['handle_factory']) - ? $options['handle_factory'] : new CurlFactory(); - $this->selectTimeout = isset($options['select_timeout']) - ? $options['select_timeout'] : 1; - $this->maxHandles = isset($options['max_handles']) - ? $options['max_handles'] : 100; - } - - public function __get($name) - { - if ($name === '_mh') { - return $this->_mh = curl_multi_init(); - } - - throw new \BadMethodCallException(); - } - - public function __destruct() - { - // Finish any open connections before terminating the script. - if ($this->handles) { - $this->execute(); - } - - if (isset($this->_mh)) { - curl_multi_close($this->_mh); - unset($this->_mh); - } - } - - public function __invoke(array $request) - { - $factory = $this->factory; - $result = $factory($request); - $entry = [ - 'request' => $request, - 'response' => [], - 'handle' => $result[0], - 'headers' => &$result[1], - 'body' => $result[2], - 'deferred' => new Deferred(), - ]; - - $id = (int) $result[0]; - - $future = new FutureArray( - $entry['deferred']->promise(), - [$this, 'execute'], - function () use ($id) { - return $this->cancel($id); - } - ); - - $this->addRequest($entry); - - // Transfer outstanding requests if there are too many open handles. - if (count($this->handles) >= $this->maxHandles) { - $this->execute(); - } - - return $future; - } - - /** - * Runs until all outstanding connections have completed. - */ - public function execute() - { - do { - - if ($this->active && - curl_multi_select($this->_mh, $this->selectTimeout) === -1 - ) { - // Perform a usleep if a select returns -1. - // See: https://bugs.php.net/bug.php?id=61141 - usleep(250); - } - - // Add any delayed futures if needed. - if ($this->delays) { - $this->addDelays(); - } - - do { - $mrc = curl_multi_exec($this->_mh, $this->active); - } while ($mrc === CURLM_CALL_MULTI_PERFORM); - - $this->processMessages(); - - // If there are delays but no transfers, then sleep for a bit. - if (!$this->active && $this->delays) { - usleep(500); - } - - } while ($this->active || $this->handles); - } - - private function addRequest(array &$entry) - { - $id = (int) $entry['handle']; - $this->handles[$id] = $entry; - - // If the request is a delay, then add the reques to the curl multi - // pool only after the specified delay. - if (isset($entry['request']['client']['delay'])) { - $this->delays[$id] = microtime(true) + ($entry['request']['client']['delay'] / 1000); - } elseif (empty($entry['request']['future'])) { - curl_multi_add_handle($this->_mh, $entry['handle']); - } else { - curl_multi_add_handle($this->_mh, $entry['handle']); - // "lazy" futures are only sent once the pool has many requests. - if ($entry['request']['future'] !== 'lazy') { - do { - $mrc = curl_multi_exec($this->_mh, $this->active); - } while ($mrc === CURLM_CALL_MULTI_PERFORM); - $this->processMessages(); - } - } - } - - private function removeProcessed($id) - { - if (isset($this->handles[$id])) { - curl_multi_remove_handle( - $this->_mh, - $this->handles[$id]['handle'] - ); - curl_close($this->handles[$id]['handle']); - unset($this->handles[$id], $this->delays[$id]); - } - } - - /** - * Cancels a handle from sending and removes references to it. - * - * @param int $id Handle ID to cancel and remove. - * - * @return bool True on success, false on failure. - */ - private function cancel($id) - { - // Cannot cancel if it has been processed. - if (!isset($this->handles[$id])) { - return false; - } - - $handle = $this->handles[$id]['handle']; - unset($this->delays[$id], $this->handles[$id]); - curl_multi_remove_handle($this->_mh, $handle); - curl_close($handle); - - return true; - } - - private function addDelays() - { - $currentTime = microtime(true); - - foreach ($this->delays as $id => $delay) { - if ($currentTime >= $delay) { - unset($this->delays[$id]); - curl_multi_add_handle( - $this->_mh, - $this->handles[$id]['handle'] - ); - } - } - } - - private function processMessages() - { - while ($done = curl_multi_info_read($this->_mh)) { - $id = (int) $done['handle']; - - if (!isset($this->handles[$id])) { - // Probably was cancelled. - continue; - } - - $entry = $this->handles[$id]; - $entry['response']['transfer_stats'] = curl_getinfo($done['handle']); - - if ($done['result'] !== CURLM_OK) { - $entry['response']['curl']['errno'] = $done['result']; - if (function_exists('curl_strerror')) { - $entry['response']['curl']['error'] = curl_strerror($done['result']); - } - } - - $result = CurlFactory::createResponse( - $this, - $entry['request'], - $entry['response'], - $entry['headers'], - $entry['body'] - ); - - $this->removeProcessed($id); - $entry['deferred']->resolve($result); - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Client/Middleware.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Client/Middleware.php deleted file mode 100644 index 6fa7318aba..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Client/Middleware.php +++ /dev/null @@ -1,58 +0,0 @@ -result = $result; - } - - public function __invoke(array $request) - { - Core::doSleep($request); - $response = is_callable($this->result) - ? call_user_func($this->result, $request) - : $this->result; - - if (is_array($response)) { - $response = new CompletedFutureArray($response + [ - 'status' => null, - 'body' => null, - 'headers' => [], - 'reason' => null, - 'effective_url' => null, - ]); - } elseif (!$response instanceof FutureArrayInterface) { - throw new \InvalidArgumentException( - 'Response must be an array or FutureArrayInterface. Found ' - . Core::describeType($request) - ); - } - - return $response; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Client/StreamHandler.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Client/StreamHandler.php deleted file mode 100644 index 4bacec1337..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Client/StreamHandler.php +++ /dev/null @@ -1,414 +0,0 @@ -options = $options; - } - - public function __invoke(array $request) - { - $url = Core::url($request); - Core::doSleep($request); - - try { - // Does not support the expect header. - $request = Core::removeHeader($request, 'Expect'); - $stream = $this->createStream($url, $request); - return $this->createResponse($request, $url, $stream); - } catch (RingException $e) { - return $this->createErrorResponse($url, $e); - } - } - - private function createResponse(array $request, $url, $stream) - { - $hdrs = $this->lastHeaders; - $this->lastHeaders = null; - $parts = explode(' ', array_shift($hdrs), 3); - $response = [ - 'version' => substr($parts[0], 5), - 'status' => $parts[1], - 'reason' => isset($parts[2]) ? $parts[2] : null, - 'headers' => Core::headersFromLines($hdrs), - 'effective_url' => $url, - ]; - - $stream = $this->checkDecode($request, $response, $stream); - - // If not streaming, then drain the response into a stream. - if (empty($request['client']['stream'])) { - $dest = isset($request['client']['save_to']) - ? $request['client']['save_to'] - : fopen('php://temp', 'r+'); - $stream = $this->drain($stream, $dest); - } - - $response['body'] = $stream; - - return new CompletedFutureArray($response); - } - - private function checkDecode(array $request, array $response, $stream) - { - // Automatically decode responses when instructed. - if (!empty($request['client']['decode_content'])) { - switch (Core::firstHeader($response, 'Content-Encoding', true)) { - case 'gzip': - case 'deflate': - $stream = new InflateStream(Stream::factory($stream)); - break; - } - } - - return $stream; - } - - /** - * Drains the stream into the "save_to" client option. - * - * @param resource $stream - * @param string|resource|StreamInterface $dest - * - * @return Stream - * @throws \RuntimeException when the save_to option is invalid. - */ - private function drain($stream, $dest) - { - if (is_resource($stream)) { - if (!is_resource($dest)) { - $stream = Stream::factory($stream); - } else { - stream_copy_to_stream($stream, $dest); - fclose($stream); - rewind($dest); - return $dest; - } - } - - // Stream the response into the destination stream - $dest = is_string($dest) - ? new Stream(Utils::open($dest, 'r+')) - : Stream::factory($dest); - - Utils::copyToStream($stream, $dest); - $dest->seek(0); - $stream->close(); - - return $dest; - } - - /** - * Creates an error response for the given stream. - * - * @param string $url - * @param RingException $e - * - * @return array - */ - private function createErrorResponse($url, RingException $e) - { - // Determine if the error was a networking error. - $message = $e->getMessage(); - - // This list can probably get more comprehensive. - if (strpos($message, 'getaddrinfo') // DNS lookup failed - || strpos($message, 'Connection refused') - ) { - $e = new ConnectException($e->getMessage(), 0, $e); - } - - return new CompletedFutureArray([ - 'status' => null, - 'body' => null, - 'headers' => [], - 'effective_url' => $url, - 'error' => $e - ]); - } - - /** - * Create a resource and check to ensure it was created successfully - * - * @param callable $callback Callable that returns stream resource - * - * @return resource - * @throws \RuntimeException on error - */ - private function createResource(callable $callback) - { - $errors = null; - set_error_handler(function ($_, $msg, $file, $line) use (&$errors) { - $errors[] = [ - 'message' => $msg, - 'file' => $file, - 'line' => $line - ]; - return true; - }); - - $resource = $callback(); - restore_error_handler(); - - if (!$resource) { - $message = 'Error creating resource: '; - foreach ($errors as $err) { - foreach ($err as $key => $value) { - $message .= "[$key] $value" . PHP_EOL; - } - } - throw new RingException(trim($message)); - } - - return $resource; - } - - private function createStream($url, array $request) - { - static $methods; - if (!$methods) { - $methods = array_flip(get_class_methods(__CLASS__)); - } - - // HTTP/1.1 streams using the PHP stream wrapper require a - // Connection: close header - if ((!isset($request['version']) || $request['version'] == '1.1') - && !Core::hasHeader($request, 'Connection') - ) { - $request['headers']['Connection'] = ['close']; - } - - // Ensure SSL is verified by default - if (!isset($request['client']['verify'])) { - $request['client']['verify'] = true; - } - - $params = []; - $options = $this->getDefaultOptions($request); - - if (isset($request['client'])) { - foreach ($request['client'] as $key => $value) { - $method = "add_{$key}"; - if (isset($methods[$method])) { - $this->{$method}($request, $options, $value, $params); - } - } - } - - return $this->createStreamResource( - $url, - $request, - $options, - $this->createContext($request, $options, $params) - ); - } - - private function getDefaultOptions(array $request) - { - $headers = ""; - foreach ($request['headers'] as $name => $value) { - foreach ((array) $value as $val) { - $headers .= "$name: $val\r\n"; - } - } - - $context = [ - 'http' => [ - 'method' => $request['http_method'], - 'header' => $headers, - 'protocol_version' => isset($request['version']) ? $request['version'] : 1.1, - 'ignore_errors' => true, - 'follow_location' => 0, - ], - ]; - - $body = Core::body($request); - if (isset($body)) { - $context['http']['content'] = $body; - // Prevent the HTTP handler from adding a Content-Type header. - if (!Core::hasHeader($request, 'Content-Type')) { - $context['http']['header'] .= "Content-Type:\r\n"; - } - } - - $context['http']['header'] = rtrim($context['http']['header']); - - return $context; - } - - private function add_proxy(array $request, &$options, $value, &$params) - { - if (!is_array($value)) { - $options['http']['proxy'] = $value; - } else { - $scheme = isset($request['scheme']) ? $request['scheme'] : 'http'; - if (isset($value[$scheme])) { - $options['http']['proxy'] = $value[$scheme]; - } - } - } - - private function add_timeout(array $request, &$options, $value, &$params) - { - $options['http']['timeout'] = $value; - } - - private function add_verify(array $request, &$options, $value, &$params) - { - if ($value === true) { - // PHP 5.6 or greater will find the system cert by default. When - // < 5.6, use the Guzzle bundled cacert. - if (PHP_VERSION_ID < 50600) { - $options['ssl']['cafile'] = ClientUtils::getDefaultCaBundle(); - } - } elseif (is_string($value)) { - $options['ssl']['cafile'] = $value; - if (!file_exists($value)) { - throw new RingException("SSL CA bundle not found: $value"); - } - } elseif ($value === false) { - $options['ssl']['verify_peer'] = false; - $options['ssl']['allow_self_signed'] = true; - return; - } else { - throw new RingException('Invalid verify request option'); - } - - $options['ssl']['verify_peer'] = true; - $options['ssl']['allow_self_signed'] = false; - } - - private function add_cert(array $request, &$options, $value, &$params) - { - if (is_array($value)) { - $options['ssl']['passphrase'] = $value[1]; - $value = $value[0]; - } - - if (!file_exists($value)) { - throw new RingException("SSL certificate not found: {$value}"); - } - - $options['ssl']['local_cert'] = $value; - } - - private function add_progress(array $request, &$options, $value, &$params) - { - $fn = function ($code, $_1, $_2, $_3, $transferred, $total) use ($value) { - if ($code == STREAM_NOTIFY_PROGRESS) { - $value($total, $transferred, null, null); - } - }; - - // Wrap the existing function if needed. - $params['notification'] = isset($params['notification']) - ? Core::callArray([$params['notification'], $fn]) - : $fn; - } - - private function add_debug(array $request, &$options, $value, &$params) - { - if ($value === false) { - return; - } - - static $map = [ - STREAM_NOTIFY_CONNECT => 'CONNECT', - STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED', - STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT', - STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS', - STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS', - STREAM_NOTIFY_REDIRECTED => 'REDIRECTED', - STREAM_NOTIFY_PROGRESS => 'PROGRESS', - STREAM_NOTIFY_FAILURE => 'FAILURE', - STREAM_NOTIFY_COMPLETED => 'COMPLETED', - STREAM_NOTIFY_RESOLVE => 'RESOLVE', - ]; - - static $args = ['severity', 'message', 'message_code', - 'bytes_transferred', 'bytes_max']; - - $value = Core::getDebugResource($value); - $ident = $request['http_method'] . ' ' . Core::url($request); - $fn = function () use ($ident, $value, $map, $args) { - $passed = func_get_args(); - $code = array_shift($passed); - fprintf($value, '<%s> [%s] ', $ident, $map[$code]); - foreach (array_filter($passed) as $i => $v) { - fwrite($value, $args[$i] . ': "' . $v . '" '); - } - fwrite($value, "\n"); - }; - - // Wrap the existing function if needed. - $params['notification'] = isset($params['notification']) - ? Core::callArray([$params['notification'], $fn]) - : $fn; - } - - private function applyCustomOptions(array $request, array &$options) - { - if (!isset($request['client']['stream_context'])) { - return; - } - - if (!is_array($request['client']['stream_context'])) { - throw new RingException('stream_context must be an array'); - } - - $options = array_replace_recursive( - $options, - $request['client']['stream_context'] - ); - } - - private function createContext(array $request, array $options, array $params) - { - $this->applyCustomOptions($request, $options); - return $this->createResource( - function () use ($request, $options, $params) { - return stream_context_create($options, $params); - }, - $request, - $options - ); - } - - private function createStreamResource( - $url, - array $request, - array $options, - $context - ) { - return $this->createResource( - function () use ($url, $context) { - if (false === strpos($url, 'http')) { - trigger_error("URL is invalid: {$url}", E_USER_WARNING); - return null; - } - $resource = fopen($url, 'r', null, $context); - $this->lastHeaders = $http_response_header; - return $resource; - }, - $request, - $options - ); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Core.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Core.php deleted file mode 100644 index dd7d1a0c5a..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Core.php +++ /dev/null @@ -1,364 +0,0 @@ - $value) { - if (!strcasecmp($name, $header)) { - $result = array_merge($result, $value); - } - } - } - - return $result; - } - - /** - * Gets a header value from a message as a string or null - * - * This method searches through the "headers" key of a message for a header - * using a case-insensitive search. The lines of the header are imploded - * using commas into a single string return value. - * - * @param array $message Request or response hash. - * @param string $header Header to retrieve - * - * @return string|null Returns the header string if found, or null if not. - */ - public static function header($message, $header) - { - $match = self::headerLines($message, $header); - return $match ? implode(', ', $match) : null; - } - - /** - * Returns the first header value from a message as a string or null. If - * a header line contains multiple values separated by a comma, then this - * function will return the first value in the list. - * - * @param array $message Request or response hash. - * @param string $header Header to retrieve - * - * @return string|null Returns the value as a string if found. - */ - public static function firstHeader($message, $header) - { - if (!empty($message['headers'])) { - foreach ($message['headers'] as $name => $value) { - if (!strcasecmp($name, $header)) { - // Return the match itself if it is a single value. - $pos = strpos($value[0], ','); - return $pos ? substr($value[0], 0, $pos) : $value[0]; - } - } - } - - return null; - } - - /** - * Returns true if a message has the provided case-insensitive header. - * - * @param array $message Request or response hash. - * @param string $header Header to check - * - * @return bool - */ - public static function hasHeader($message, $header) - { - if (!empty($message['headers'])) { - foreach ($message['headers'] as $name => $value) { - if (!strcasecmp($name, $header)) { - return true; - } - } - } - - return false; - } - - /** - * Parses an array of header lines into an associative array of headers. - * - * @param array $lines Header lines array of strings in the following - * format: "Name: Value" - * @return array - */ - public static function headersFromLines($lines) - { - $headers = []; - - foreach ($lines as $line) { - $parts = explode(':', $line, 2); - $headers[trim($parts[0])][] = isset($parts[1]) - ? trim($parts[1]) - : null; - } - - return $headers; - } - - /** - * Removes a header from a message using a case-insensitive comparison. - * - * @param array $message Message that contains 'headers' - * @param string $header Header to remove - * - * @return array - */ - public static function removeHeader(array $message, $header) - { - if (isset($message['headers'])) { - foreach (array_keys($message['headers']) as $key) { - if (!strcasecmp($header, $key)) { - unset($message['headers'][$key]); - } - } - } - - return $message; - } - - /** - * Replaces any existing case insensitive headers with the given value. - * - * @param array $message Message that contains 'headers' - * @param string $header Header to set. - * @param array $value Value to set. - * - * @return array - */ - public static function setHeader(array $message, $header, array $value) - { - $message = self::removeHeader($message, $header); - $message['headers'][$header] = $value; - - return $message; - } - - /** - * Creates a URL string from a request. - * - * If the "url" key is present on the request, it is returned, otherwise - * the url is built up based on the scheme, host, uri, and query_string - * request values. - * - * @param array $request Request to get the URL from - * - * @return string Returns the request URL as a string. - * @throws \InvalidArgumentException if no Host header is present. - */ - public static function url(array $request) - { - if (isset($request['url'])) { - return $request['url']; - } - - $uri = (isset($request['scheme']) - ? $request['scheme'] : 'http') . '://'; - - if ($host = self::header($request, 'host')) { - $uri .= $host; - } else { - throw new \InvalidArgumentException('No Host header was provided'); - } - - if (isset($request['uri'])) { - $uri .= $request['uri']; - } - - if (isset($request['query_string'])) { - $uri .= '?' . $request['query_string']; - } - - return $uri; - } - - /** - * Reads the body of a message into a string. - * - * @param array|FutureArrayInterface $message Array containing a "body" key - * - * @return null|string Returns the body as a string or null if not set. - * @throws \InvalidArgumentException if a request body is invalid. - */ - public static function body($message) - { - if (!isset($message['body'])) { - return null; - } - - if ($message['body'] instanceof StreamInterface) { - return (string) $message['body']; - } - - switch (gettype($message['body'])) { - case 'string': - return $message['body']; - case 'resource': - return stream_get_contents($message['body']); - case 'object': - if ($message['body'] instanceof \Iterator) { - return implode('', iterator_to_array($message['body'])); - } elseif (method_exists($message['body'], '__toString')) { - return (string) $message['body']; - } - default: - throw new \InvalidArgumentException('Invalid request body: ' - . self::describeType($message['body'])); - } - } - - /** - * Rewind the body of the provided message if possible. - * - * @param array $message Message that contains a 'body' field. - * - * @return bool Returns true on success, false on failure - */ - public static function rewindBody($message) - { - if ($message['body'] instanceof StreamInterface) { - return $message['body']->seek(0); - } - - if ($message['body'] instanceof \Generator) { - return false; - } - - if ($message['body'] instanceof \Iterator) { - $message['body']->rewind(); - return true; - } - - if (is_resource($message['body'])) { - return rewind($message['body']); - } - - return is_string($message['body']) - || (is_object($message['body']) - && method_exists($message['body'], '__toString')); - } - - /** - * Debug function used to describe the provided value type and class. - * - * @param mixed $input - * - * @return string Returns a string containing the type of the variable and - * if a class is provided, the class name. - */ - public static function describeType($input) - { - switch (gettype($input)) { - case 'object': - return 'object(' . get_class($input) . ')'; - case 'array': - return 'array(' . count($input) . ')'; - default: - ob_start(); - var_dump($input); - // normalize float vs double - return str_replace('double(', 'float(', rtrim(ob_get_clean())); - } - } - - /** - * Sleep for the specified amount of time specified in the request's - * ['client']['delay'] option if present. - * - * This function should only be used when a non-blocking sleep is not - * possible. - * - * @param array $request Request to sleep - */ - public static function doSleep(array $request) - { - if (isset($request['client']['delay'])) { - usleep($request['client']['delay'] * 1000); - } - } - - /** - * Returns a proxied future that modifies the dereferenced value of another - * future using a promise. - * - * @param FutureArrayInterface $future Future to wrap with a new future - * @param callable $onFulfilled Invoked when the future fulfilled - * @param callable $onRejected Invoked when the future rejected - * @param callable $onProgress Invoked when the future progresses - * - * @return FutureArray - */ - public static function proxy( - FutureArrayInterface $future, - callable $onFulfilled = null, - callable $onRejected = null, - callable $onProgress = null - ) { - return new FutureArray( - $future->then($onFulfilled, $onRejected, $onProgress), - [$future, 'wait'], - [$future, 'cancel'] - ); - } - - /** - * Returns a debug stream based on the provided variable. - * - * @param mixed $value Optional value - * - * @return resource - */ - public static function getDebugResource($value = null) - { - if (is_resource($value)) { - return $value; - } elseif (defined('STDOUT')) { - return STDOUT; - } else { - return fopen('php://output', 'w'); - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Exception/CancelledException.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Exception/CancelledException.php deleted file mode 100644 index 95b353acfd..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Exception/CancelledException.php +++ /dev/null @@ -1,7 +0,0 @@ -wrappedPromise = $promise; - $this->waitfn = $wait; - $this->cancelfn = $cancel; - } - - public function wait() - { - if (!$this->isRealized) { - $this->addShadow(); - if (!$this->isRealized && $this->waitfn) { - $this->invokeWait(); - } - if (!$this->isRealized) { - $this->error = new RingException('Waiting did not resolve future'); - } - } - - if ($this->error) { - throw $this->error; - } - - return $this->result; - } - - public function promise() - { - return $this->wrappedPromise; - } - - public function then( - callable $onFulfilled = null, - callable $onRejected = null, - callable $onProgress = null - ) { - return $this->wrappedPromise->then($onFulfilled, $onRejected, $onProgress); - } - - public function cancel() - { - if (!$this->isRealized) { - $cancelfn = $this->cancelfn; - $this->waitfn = $this->cancelfn = null; - $this->isRealized = true; - $this->error = new CancelledFutureAccessException(); - if ($cancelfn) { - $cancelfn($this); - } - } - } - - private function addShadow() - { - // Get the result and error when the promise is resolved. Note that - // calling this function might trigger the resolution immediately. - $this->wrappedPromise->then( - function ($value) { - $this->isRealized = true; - $this->result = $value; - $this->waitfn = $this->cancelfn = null; - }, - function ($error) { - $this->isRealized = true; - $this->error = $error; - $this->waitfn = $this->cancelfn = null; - } - ); - } - - private function invokeWait() - { - try { - $wait = $this->waitfn; - $this->waitfn = null; - $wait(); - } catch (\Exception $e) { - // Defer can throw to reject. - $this->error = $e; - $this->isRealized = true; - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php deleted file mode 100644 index 0a90c939f7..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php +++ /dev/null @@ -1,43 +0,0 @@ -result[$offset]); - } - - public function offsetGet($offset) - { - return $this->result[$offset]; - } - - public function offsetSet($offset, $value) - { - $this->result[$offset] = $value; - } - - public function offsetUnset($offset) - { - unset($this->result[$offset]); - } - - public function count() - { - return count($this->result); - } - - public function getIterator() - { - return new \ArrayIterator($this->result); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php deleted file mode 100644 index 0d25af72d4..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php +++ /dev/null @@ -1,57 +0,0 @@ -result = $result; - $this->error = $e; - } - - public function wait() - { - if ($this->error) { - throw $this->error; - } - - return $this->result; - } - - public function cancel() {} - - public function promise() - { - if (!$this->cachedPromise) { - $this->cachedPromise = $this->error - ? new RejectedPromise($this->error) - : new FulfilledPromise($this->result); - } - - return $this->cachedPromise; - } - - public function then( - callable $onFulfilled = null, - callable $onRejected = null, - callable $onProgress = null - ) { - return $this->promise()->then($onFulfilled, $onRejected, $onProgress); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Future/FutureArray.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Future/FutureArray.php deleted file mode 100644 index 3d64c9643a..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Future/FutureArray.php +++ /dev/null @@ -1,40 +0,0 @@ -_value[$offset]); - } - - public function offsetGet($offset) - { - return $this->_value[$offset]; - } - - public function offsetSet($offset, $value) - { - $this->_value[$offset] = $value; - } - - public function offsetUnset($offset) - { - unset($this->_value[$offset]); - } - - public function count() - { - return count($this->_value); - } - - public function getIterator() - { - return new \ArrayIterator($this->_value); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php deleted file mode 100644 index 58f5f73670..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php +++ /dev/null @@ -1,11 +0,0 @@ -_value = $this->wait(); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/.gitignore b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/.gitignore deleted file mode 100644 index c33d3965fd..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -.idea -.DS_STORE -coverage -phpunit.xml -composer.lock -vendor/ diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/.travis.yml b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/.travis.yml deleted file mode 100644 index a6f8a87b7d..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -language: php - -php: - - 5.4 - - 5.5 - - 5.6 - - hhvm - -before_script: - - composer self-update - - composer install --no-interaction --prefer-source --dev - -script: vendor/bin/phpunit - -matrix: - allow_failures: - - php: hhvm diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/CHANGELOG.rst b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/CHANGELOG.rst deleted file mode 100644 index 0018ffe353..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/CHANGELOG.rst +++ /dev/null @@ -1,94 +0,0 @@ -========= -Changelog -========= - -3.0.0 (2014-10-12) ------------------- - -* Now supports creating streams from functions and iterators. -* Supports creating buffered streams and asynchronous streams. -* Removed ``functions.php``. Use the corresponding functions provided by - ``GuzzleHttp\Streams\Utils`` instead. -* Moved ``GuzzleHttp\Stream\MetadataStreamInterface::getMetadata`` to - ``GuzzleHttp\Stream\StreamInterface``. MetadataStreamInterface is no longer - used and is marked as deprecated. -* Added ``attach()`` to ``GuzzleHttp\Stream\StreamInterface`` for PSR-7 - compatibility. -* Removed ``flush()`` from StreamInterface. -* Removed the ``$maxLength`` parameter from - ``GuzzleHttp\Stream\StreamInterface::getContents()``. This function now - returns the entire remainder of the stream. If you want to limit the maximum - amount of data read from the stream, use the - ``GuzzleHttp\Stream\Utils::copyToString()`` function. -* Streams that return an empty string, ``''``, are no longer considered a - failure. You MUST return ``false`` to mark the read as a failure, and ensure - that any decorators you create properly return ``true`` in response to the - ``eof()`` method when the stream is consumed. -* ``GuzzleHttp\Stream\Stream::__construct``, - ``GuzzleHttp\Stream\Stream::factory``, and - ``GuzzleHttp\Stream\Utils::create`` no longer accept a size in the second - argument. They now accept an associative array of options, including the - "size" key and "metadata" key which can be used to provide custom metadata. -* Added ``GuzzleHttp\Stream\BufferStream`` to add support for buffering data, - and when read, shifting data off of the buffer. -* Added ``GuzzleHttp\Stream\NullBuffer`` which can be used as a buffer that - does not actually store any data. -* Added ``GuzzleHttp\Stream\AsyncStream`` to provide support for non-blocking - streams that can be filled by a remote source (e.g., an event-loop). If a - ``drain`` option is provided, the stream can also act as if it is a blocking - stream. - -2.1.0 (2014-08-17) ------------------- - -* Added an InflateStream to inflate gzipped or deflated content. -* Added ``flush`` to stream wrapper. -* Added the ability to easily register the GuzzleStreamWrapper if needed. - -2.0.0 (2014-08-16) ------------------- - -* Deprecated functions.php and moved all of those methods to - ``GuzzleHttp\Streams\Utils``. Use ``GuzzleHttp\Stream\Stream::factory()`` - instead of ``GuzzleHttp\Stream\create()`` to create new streams. -* Added ``flush()`` to ``StreamInterface``. This method is used to flush any - underlying stream write buffers. -* Added ``FnStream`` to easily decorate stream behavior with callables. -* ``Utils::hash`` now throws an exception when the stream cannot seek to 0. - -1.5.1 (2014-09-10) ------------------- - -* Stream metadata is grabbed from the underlying stream each time - ``getMetadata`` is called rather than returning a value from a cache. -* Properly closing all underlying streams when AppendStream is closed. -* Seek functions no longer throw exceptions. -* LazyOpenStream now correctly returns the underlying stream resource when - detached. - -1.5.0 (2014-08-07) ------------------- - -* Added ``Stream\safe_open`` to open stream resources and throw exceptions - instead of raising errors. - -1.4.0 (2014-07-19) ------------------- - -* Added a LazyOpenStream - -1.3.0 (2014-07-15) ------------------- - -* Added an AppendStream to stream over multiple stream one after the other. - -1.2.0 (2014-07-15) ------------------- - -* Updated the ``detach()`` method to return the underlying stream resource or - ``null`` if it does not wrap a resource. -* Multiple fixes for how streams behave when the underlying resource is - detached -* Do not clear statcache when a stream does not have a 'uri' -* Added a fix to LimitStream -* Added a condition to ensure that functions.php can be required multiple times diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/LICENSE b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/LICENSE deleted file mode 100644 index 71d3b783cb..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2014 Michael Dowling, https://github.com/mtdowling - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/Makefile b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/Makefile deleted file mode 100644 index f4d42849e5..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -all: clean coverage - -release: tag - git push origin --tags - -tag: - chag tag --sign --debug CHANGELOG.rst - -test: - vendor/bin/phpunit - -coverage: - vendor/bin/phpunit --coverage-html=artifacts/coverage - -view-coverage: - open artifacts/coverage/index.html - -clean: - rm -rf artifacts/* diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/README.rst b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/README.rst deleted file mode 100644 index baff63b37d..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/README.rst +++ /dev/null @@ -1,36 +0,0 @@ -============== -Guzzle Streams -============== - -Provides a simple abstraction over streams of data. - -This library is used in `Guzzle 5 `_, and is -(currently) compatible with the WIP PSR-7. - -Installation -============ - -This package can be installed easily using `Composer `_. -Simply add the following to the composer.json file at the root of your project: - -.. code-block:: javascript - - { - "require": { - "guzzlehttp/streams": "~3.0" - } - } - -Then install your dependencies using ``composer.phar install``. - -Documentation -============= - -The documentation for this package can be found on the main Guzzle website at -http://docs.guzzlephp.org/en/guzzle4/streams.html. - -Testing -======= - -This library is tested using PHPUnit. You'll need to install the dependencies -using `Composer `_ then run ``make test``. diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/composer.json b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/composer.json deleted file mode 100644 index 6d7034370b..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/composer.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "guzzlehttp/streams", - "description": "Provides a simple abstraction over streams of data", - "homepage": "http://guzzlephp.org/", - "keywords": ["stream", "guzzle"], - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "autoload": { - "psr-4": { "GuzzleHttp\\Stream\\": "src/" } - }, - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/phpunit.xml.dist b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/phpunit.xml.dist deleted file mode 100644 index 6e758c1927..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/phpunit.xml.dist +++ /dev/null @@ -1,17 +0,0 @@ - - - - - tests - - - - - src - - src/functions.php - - - - diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/AppendStream.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/AppendStream.php deleted file mode 100644 index 94bda7173f..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/AppendStream.php +++ /dev/null @@ -1,220 +0,0 @@ -addStream($stream); - } - } - - public function __toString() - { - try { - $this->seek(0); - return $this->getContents(); - } catch (\Exception $e) { - return ''; - } - } - - /** - * Add a stream to the AppendStream - * - * @param StreamInterface $stream Stream to append. Must be readable. - * - * @throws \InvalidArgumentException if the stream is not readable - */ - public function addStream(StreamInterface $stream) - { - if (!$stream->isReadable()) { - throw new \InvalidArgumentException('Each stream must be readable'); - } - - // The stream is only seekable if all streams are seekable - if (!$stream->isSeekable()) { - $this->seekable = false; - } - - $this->streams[] = $stream; - } - - public function getContents() - { - return Utils::copyToString($this); - } - - /** - * Closes each attached stream. - * - * {@inheritdoc} - */ - public function close() - { - $this->pos = $this->current = 0; - - foreach ($this->streams as $stream) { - $stream->close(); - } - - $this->streams = []; - } - - /** - * Detaches each attached stream - * - * {@inheritdoc} - */ - public function detach() - { - $this->close(); - $this->detached = true; - } - - public function attach($stream) - { - throw new CannotAttachException(); - } - - public function tell() - { - return $this->pos; - } - - /** - * Tries to calculate the size by adding the size of each stream. - * - * If any of the streams do not return a valid number, then the size of the - * append stream cannot be determined and null is returned. - * - * {@inheritdoc} - */ - public function getSize() - { - $size = 0; - - foreach ($this->streams as $stream) { - $s = $stream->getSize(); - if ($s === null) { - return null; - } - $size += $s; - } - - return $size; - } - - public function eof() - { - return !$this->streams || - ($this->current >= count($this->streams) - 1 && - $this->streams[$this->current]->eof()); - } - - /** - * Attempts to seek to the given position. Only supports SEEK_SET. - * - * {@inheritdoc} - */ - public function seek($offset, $whence = SEEK_SET) - { - if (!$this->seekable || $whence !== SEEK_SET) { - return false; - } - - $success = true; - $this->pos = $this->current = 0; - - // Rewind each stream - foreach ($this->streams as $stream) { - if (!$stream->seek(0)) { - $success = false; - } - } - - if (!$success) { - return false; - } - - // Seek to the actual position by reading from each stream - while ($this->pos < $offset && !$this->eof()) { - $this->read(min(8096, $offset - $this->pos)); - } - - return $this->pos == $offset; - } - - /** - * Reads from all of the appended streams until the length is met or EOF. - * - * {@inheritdoc} - */ - public function read($length) - { - $buffer = ''; - $total = count($this->streams) - 1; - $remaining = $length; - - while ($remaining > 0) { - // Progress to the next stream if needed. - if ($this->streams[$this->current]->eof()) { - if ($this->current == $total) { - break; - } - $this->current++; - } - $buffer .= $this->streams[$this->current]->read($remaining); - $remaining = $length - strlen($buffer); - } - - $this->pos += strlen($buffer); - - return $buffer; - } - - public function isReadable() - { - return true; - } - - public function isWritable() - { - return false; - } - - public function isSeekable() - { - return $this->seekable; - } - - public function write($string) - { - return false; - } - - public function getMetadata($key = null) - { - return $key ? null : []; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/AsyncReadStream.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/AsyncReadStream.php deleted file mode 100644 index 25ad96021a..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/AsyncReadStream.php +++ /dev/null @@ -1,207 +0,0 @@ -isReadable() || !$buffer->isWritable()) { - throw new \InvalidArgumentException( - 'Buffer must be readable and writable' - ); - } - - if (isset($config['size'])) { - $this->size = $config['size']; - } - - static $callables = ['pump', 'drain']; - foreach ($callables as $check) { - if (isset($config[$check])) { - if (!is_callable($config[$check])) { - throw new \InvalidArgumentException( - $check . ' must be callable' - ); - } - $this->{$check} = $config[$check]; - } - } - - $this->hwm = $buffer->getMetadata('hwm'); - - // Cannot drain when there's no high water mark. - if ($this->hwm === null) { - $this->drain = null; - } - - $this->stream = $buffer; - } - - /** - * Factory method used to create new async stream and an underlying buffer - * if no buffer is provided. - * - * This function accepts the same options as AsyncReadStream::__construct, - * but added the following key value pairs: - * - * - buffer: (StreamInterface) Buffer used to buffer data. If none is - * provided, a default buffer is created. - * - hwm: (int) High water mark to use if a buffer is created on your - * behalf. - * - max_buffer: (int) If provided, wraps the utilized buffer in a - * DroppingStream decorator to ensure that buffer does not exceed a given - * length. When exceeded, the stream will begin dropping data. Set the - * max_buffer to 0, to use a NullStream which does not store data. - * - write: (callable) A function that is invoked when data is written - * to the underlying buffer. The function accepts the buffer as the first - * argument, and the data being written as the second. The function MUST - * return the number of bytes that were written or false to let writers - * know to slow down. - * - drain: (callable) See constructor documentation. - * - pump: (callable) See constructor documentation. - * - * @param array $options Associative array of options. - * - * @return array Returns an array containing the buffer used to buffer - * data, followed by the ready to use AsyncReadStream object. - */ - public static function create(array $options = []) - { - $maxBuffer = isset($options['max_buffer']) - ? $options['max_buffer'] - : null; - - if ($maxBuffer === 0) { - $buffer = new NullStream(); - } elseif (isset($options['buffer'])) { - $buffer = $options['buffer']; - } else { - $hwm = isset($options['hwm']) ? $options['hwm'] : 16384; - $buffer = new BufferStream($hwm); - } - - if ($maxBuffer > 0) { - $buffer = new DroppingStream($buffer, $options['max_buffer']); - } - - // Call the on_write callback if an on_write function was provided. - if (isset($options['write'])) { - $onWrite = $options['write']; - $buffer = FnStream::decorate($buffer, [ - 'write' => function ($string) use ($buffer, $onWrite) { - $result = $buffer->write($string); - $onWrite($buffer, $string); - return $result; - } - ]); - } - - return [$buffer, new self($buffer, $options)]; - } - - public function getSize() - { - return $this->size; - } - - public function isWritable() - { - return false; - } - - public function write($string) - { - return false; - } - - public function read($length) - { - if (!$this->needsDrain && $this->drain) { - $this->needsDrain = $this->stream->getSize() >= $this->hwm; - } - - $result = $this->stream->read($length); - - // If we need to drain, then drain when the buffer is empty. - if ($this->needsDrain && $this->stream->getSize() === 0) { - $this->needsDrain = false; - $drainFn = $this->drain; - $drainFn($this->stream); - } - - $resultLen = strlen($result); - - // If a pump was provided, the buffer is still open, and not enough - // data was given, then block until the data is provided. - if ($this->pump && $resultLen < $length) { - $pumpFn = $this->pump; - $result .= $pumpFn($length - $resultLen); - } - - return $result; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/BufferStream.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/BufferStream.php deleted file mode 100644 index 0fffbd63a8..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/BufferStream.php +++ /dev/null @@ -1,138 +0,0 @@ -hwm = $hwm; - } - - public function __toString() - { - return $this->getContents(); - } - - public function getContents() - { - $buffer = $this->buffer; - $this->buffer = ''; - - return $buffer; - } - - public function close() - { - $this->buffer = ''; - } - - public function detach() - { - $this->close(); - } - - public function attach($stream) - { - throw new CannotAttachException(); - } - - public function getSize() - { - return strlen($this->buffer); - } - - public function isReadable() - { - return true; - } - - public function isWritable() - { - return true; - } - - public function isSeekable() - { - return false; - } - - public function seek($offset, $whence = SEEK_SET) - { - return false; - } - - public function eof() - { - return strlen($this->buffer) === 0; - } - - public function tell() - { - return false; - } - - /** - * Reads data from the buffer. - */ - public function read($length) - { - $currentLength = strlen($this->buffer); - - if ($length >= $currentLength) { - // No need to slice the buffer because we don't have enough data. - $result = $this->buffer; - $this->buffer = ''; - } else { - // Slice up the result to provide a subset of the buffer. - $result = substr($this->buffer, 0, $length); - $this->buffer = substr($this->buffer, $length); - } - - return $result; - } - - /** - * Writes data to the buffer. - */ - public function write($string) - { - $this->buffer .= $string; - - if (strlen($this->buffer) >= $this->hwm) { - return false; - } - - return strlen($string); - } - - public function getMetadata($key = null) - { - if ($key == 'hwm') { - return $this->hwm; - } - - return $key ? null : []; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/CachingStream.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/CachingStream.php deleted file mode 100644 index 60bb9056c4..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/CachingStream.php +++ /dev/null @@ -1,122 +0,0 @@ -remoteStream = $stream; - $this->stream = $target ?: new Stream(fopen('php://temp', 'r+')); - } - - public function getSize() - { - return max($this->stream->getSize(), $this->remoteStream->getSize()); - } - - /** - * {@inheritdoc} - * @throws SeekException When seeking with SEEK_END or when seeking - * past the total size of the buffer stream - */ - public function seek($offset, $whence = SEEK_SET) - { - if ($whence == SEEK_SET) { - $byte = $offset; - } elseif ($whence == SEEK_CUR) { - $byte = $offset + $this->tell(); - } else { - return false; - } - - // You cannot skip ahead past where you've read from the remote stream - if ($byte > $this->stream->getSize()) { - throw new SeekException( - $this, - $byte, - sprintf('Cannot seek to byte %d when the buffered stream only' - . ' contains %d bytes', $byte, $this->stream->getSize()) - ); - } - - return $this->stream->seek($byte); - } - - public function read($length) - { - // Perform a regular read on any previously read data from the buffer - $data = $this->stream->read($length); - $remaining = $length - strlen($data); - - // More data was requested so read from the remote stream - if ($remaining) { - // If data was written to the buffer in a position that would have - // been filled from the remote stream, then we must skip bytes on - // the remote stream to emulate overwriting bytes from that - // position. This mimics the behavior of other PHP stream wrappers. - $remoteData = $this->remoteStream->read( - $remaining + $this->skipReadBytes - ); - - if ($this->skipReadBytes) { - $len = strlen($remoteData); - $remoteData = substr($remoteData, $this->skipReadBytes); - $this->skipReadBytes = max(0, $this->skipReadBytes - $len); - } - - $data .= $remoteData; - $this->stream->write($remoteData); - } - - return $data; - } - - public function write($string) - { - // When appending to the end of the currently read stream, you'll want - // to skip bytes from being read from the remote stream to emulate - // other stream wrappers. Basically replacing bytes of data of a fixed - // length. - $overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell(); - if ($overflow > 0) { - $this->skipReadBytes += $overflow; - } - - return $this->stream->write($string); - } - - public function eof() - { - return $this->stream->eof() && $this->remoteStream->eof(); - } - - /** - * Close both the remote stream and buffer stream - */ - public function close() - { - $this->remoteStream->close() && $this->stream->close(); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/DroppingStream.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/DroppingStream.php deleted file mode 100644 index 56ee80c123..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/DroppingStream.php +++ /dev/null @@ -1,42 +0,0 @@ -stream = $stream; - $this->maxLength = $maxLength; - } - - public function write($string) - { - $diff = $this->maxLength - $this->stream->getSize(); - - // Begin returning false when the underlying stream is too large. - if ($diff <= 0) { - return false; - } - - // Write the stream or a subset of the stream if needed. - if (strlen($string) < $diff) { - return $this->stream->write($string); - } - - $this->stream->write(substr($string, 0, $diff)); - - return false; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/Exception/CannotAttachException.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/Exception/CannotAttachException.php deleted file mode 100644 index e631b9fa44..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/Exception/CannotAttachException.php +++ /dev/null @@ -1,4 +0,0 @@ -stream = $stream; - $msg = $msg ?: 'Could not seek the stream to position ' . $pos; - parent::__construct($msg); - } - - /** - * @return StreamInterface - */ - public function getStream() - { - return $this->stream; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/FnStream.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/FnStream.php deleted file mode 100644 index 6b5872d7f6..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/FnStream.php +++ /dev/null @@ -1,147 +0,0 @@ -methods = $methods; - - // Create the functions on the class - foreach ($methods as $name => $fn) { - $this->{'_fn_' . $name} = $fn; - } - } - - /** - * Lazily determine which methods are not implemented. - * @throws \BadMethodCallException - */ - public function __get($name) - { - throw new \BadMethodCallException(str_replace('_fn_', '', $name) - . '() is not implemented in the FnStream'); - } - - /** - * The close method is called on the underlying stream only if possible. - */ - public function __destruct() - { - if (isset($this->_fn_close)) { - call_user_func($this->_fn_close); - } - } - - /** - * Adds custom functionality to an underlying stream by intercepting - * specific method calls. - * - * @param StreamInterface $stream Stream to decorate - * @param array $methods Hash of method name to a closure - * - * @return FnStream - */ - public static function decorate(StreamInterface $stream, array $methods) - { - // If any of the required methods were not provided, then simply - // proxy to the decorated stream. - foreach (array_diff(self::$slots, array_keys($methods)) as $diff) { - $methods[$diff] = [$stream, $diff]; - } - - return new self($methods); - } - - public function __toString() - { - return call_user_func($this->_fn___toString); - } - - public function close() - { - return call_user_func($this->_fn_close); - } - - public function detach() - { - return call_user_func($this->_fn_detach); - } - - public function attach($stream) - { - return call_user_func($this->_fn_attach, $stream); - } - - public function getSize() - { - return call_user_func($this->_fn_getSize); - } - - public function tell() - { - return call_user_func($this->_fn_tell); - } - - public function eof() - { - return call_user_func($this->_fn_eof); - } - - public function isSeekable() - { - return call_user_func($this->_fn_isSeekable); - } - - public function seek($offset, $whence = SEEK_SET) - { - return call_user_func($this->_fn_seek, $offset, $whence); - } - - public function isWritable() - { - return call_user_func($this->_fn_isWritable); - } - - public function write($string) - { - return call_user_func($this->_fn_write, $string); - } - - public function isReadable() - { - return call_user_func($this->_fn_isReadable); - } - - public function read($length) - { - return call_user_func($this->_fn_read, $length); - } - - public function getContents() - { - return call_user_func($this->_fn_getContents); - } - - public function getMetadata($key = null) - { - return call_user_func($this->_fn_getMetadata, $key); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/GuzzleStreamWrapper.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/GuzzleStreamWrapper.php deleted file mode 100644 index 4d049a6936..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/GuzzleStreamWrapper.php +++ /dev/null @@ -1,117 +0,0 @@ -isReadable()) { - $mode = $stream->isWritable() ? 'r+' : 'r'; - } elseif ($stream->isWritable()) { - $mode = 'w'; - } else { - throw new \InvalidArgumentException('The stream must be readable, ' - . 'writable, or both.'); - } - - return fopen('guzzle://stream', $mode, null, stream_context_create([ - 'guzzle' => ['stream' => $stream] - ])); - } - - /** - * Registers the stream wrapper if needed - */ - public static function register() - { - if (!in_array('guzzle', stream_get_wrappers())) { - stream_wrapper_register('guzzle', __CLASS__); - } - } - - public function stream_open($path, $mode, $options, &$opened_path) - { - $options = stream_context_get_options($this->context); - - if (!isset($options['guzzle']['stream'])) { - return false; - } - - $this->mode = $mode; - $this->stream = $options['guzzle']['stream']; - - return true; - } - - public function stream_read($count) - { - return $this->stream->read($count); - } - - public function stream_write($data) - { - return (int) $this->stream->write($data); - } - - public function stream_tell() - { - return $this->stream->tell(); - } - - public function stream_eof() - { - return $this->stream->eof(); - } - - public function stream_seek($offset, $whence) - { - return $this->stream->seek($offset, $whence); - } - - public function stream_stat() - { - static $modeMap = [ - 'r' => 33060, - 'r+' => 33206, - 'w' => 33188 - ]; - - return [ - 'dev' => 0, - 'ino' => 0, - 'mode' => $modeMap[$this->mode], - 'nlink' => 0, - 'uid' => 0, - 'gid' => 0, - 'rdev' => 0, - 'size' => $this->stream->getSize() ?: 0, - 'atime' => 0, - 'mtime' => 0, - 'ctime' => 0, - 'blksize' => 0, - 'blocks' => 0 - ]; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/InflateStream.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/InflateStream.php deleted file mode 100644 index 978af21031..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/InflateStream.php +++ /dev/null @@ -1,27 +0,0 @@ -stream = new Stream($resource); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/LazyOpenStream.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/LazyOpenStream.php deleted file mode 100644 index 6242ee7b57..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/LazyOpenStream.php +++ /dev/null @@ -1,37 +0,0 @@ -filename = $filename; - $this->mode = $mode; - } - - /** - * Creates the underlying stream lazily when required. - * - * @return StreamInterface - */ - protected function createStream() - { - return Stream::factory(Utils::open($this->filename, $this->mode)); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/LimitStream.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/LimitStream.php deleted file mode 100644 index e9fad98573..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/LimitStream.php +++ /dev/null @@ -1,161 +0,0 @@ -stream = $stream; - $this->setLimit($limit); - $this->setOffset($offset); - } - - public function eof() - { - // Always return true if the underlying stream is EOF - if ($this->stream->eof()) { - return true; - } - - // No limit and the underlying stream is not at EOF - if ($this->limit == -1) { - return false; - } - - $tell = $this->stream->tell(); - if ($tell === false) { - return false; - } - - return $tell >= $this->offset + $this->limit; - } - - /** - * Returns the size of the limited subset of data - * {@inheritdoc} - */ - public function getSize() - { - if (null === ($length = $this->stream->getSize())) { - return null; - } elseif ($this->limit == -1) { - return $length - $this->offset; - } else { - return min($this->limit, $length - $this->offset); - } - } - - /** - * Allow for a bounded seek on the read limited stream - * {@inheritdoc} - */ - public function seek($offset, $whence = SEEK_SET) - { - if ($whence !== SEEK_SET || $offset < 0) { - return false; - } - - $offset += $this->offset; - - if ($this->limit !== -1) { - if ($offset > $this->offset + $this->limit) { - $offset = $this->offset + $this->limit; - } - } - - return $this->stream->seek($offset); - } - - /** - * Give a relative tell() - * {@inheritdoc} - */ - public function tell() - { - return $this->stream->tell() - $this->offset; - } - - /** - * Set the offset to start limiting from - * - * @param int $offset Offset to seek to and begin byte limiting from - * - * @return self - * @throws SeekException - */ - public function setOffset($offset) - { - $current = $this->stream->tell(); - - if ($current !== $offset) { - // If the stream cannot seek to the offset position, then read to it - if (!$this->stream->seek($offset)) { - if ($current > $offset) { - throw new SeekException($this, $offset); - } else { - $this->stream->read($offset - $current); - } - } - } - - $this->offset = $offset; - - return $this; - } - - /** - * Set the limit of bytes that the decorator allows to be read from the - * stream. - * - * @param int $limit Number of bytes to allow to be read from the stream. - * Use -1 for no limit. - * @return self - */ - public function setLimit($limit) - { - $this->limit = $limit; - - return $this; - } - - public function read($length) - { - if ($this->limit == -1) { - return $this->stream->read($length); - } - - // Check if the current position is less than the total allowed - // bytes + original offset - $remaining = ($this->offset + $this->limit) - $this->stream->tell(); - if ($remaining > 0) { - // Only return the amount of requested data, ensuring that the byte - // limit is not exceeded - return $this->stream->read(min($remaining, $length)); - } else { - return false; - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/MetadataStreamInterface.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/MetadataStreamInterface.php deleted file mode 100644 index c1433ad83e..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/MetadataStreamInterface.php +++ /dev/null @@ -1,11 +0,0 @@ -stream->attach($stream); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/NullStream.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/NullStream.php deleted file mode 100644 index 41ee776683..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/NullStream.php +++ /dev/null @@ -1,78 +0,0 @@ -source = $source; - $this->size = isset($options['size']) ? $options['size'] : null; - $this->metadata = isset($options['metadata']) ? $options['metadata'] : []; - $this->buffer = new BufferStream(); - } - - public function __toString() - { - return Utils::copyToString($this); - } - - public function close() - { - $this->detach(); - } - - public function detach() - { - $this->tellPos = false; - $this->source = null; - } - - public function attach($stream) - { - throw new CannotAttachException(); - } - - public function getSize() - { - return $this->size; - } - - public function tell() - { - return $this->tellPos; - } - - public function eof() - { - return !$this->source; - } - - public function isSeekable() - { - return false; - } - - public function seek($offset, $whence = SEEK_SET) - { - return false; - } - - public function isWritable() - { - return false; - } - - public function write($string) - { - return false; - } - - public function isReadable() - { - return true; - } - - public function read($length) - { - $data = $this->buffer->read($length); - $readLen = strlen($data); - $this->tellPos += $readLen; - $remaining = $length - $readLen; - - if ($remaining) { - $this->pump($remaining); - $data .= $this->buffer->read($remaining); - $this->tellPos += strlen($data) - $readLen; - } - - return $data; - } - - public function getContents() - { - $result = ''; - while (!$this->eof()) { - $result .= $this->read(1000000); - } - - return $result; - } - - public function getMetadata($key = null) - { - if (!$key) { - return $this->metadata; - } - - return isset($this->metadata[$key]) ? $this->metadata[$key] : null; - } - - private function pump($length) - { - if ($this->source) { - do { - $data = call_user_func($this->source, $length); - if ($data === false || $data === null) { - $this->source = null; - return; - } - $this->buffer->write($data); - $length -= strlen($data); - } while ($length > 0); - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/Stream.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/Stream.php deleted file mode 100644 index 7adbc5e3f3..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/Stream.php +++ /dev/null @@ -1,261 +0,0 @@ - [ - 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true, - 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, - 'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true, - 'x+t' => true, 'c+t' => true, 'a+' => true - ], - 'write' => [ - 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, - 'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true, - 'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true, - 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true - ] - ]; - - /** - * Create a new stream based on the input type. - * - * This factory accepts the same associative array of options as described - * in the constructor. - * - * @param resource|string|StreamInterface $resource Entity body data - * @param array $options Additional options - * - * @return Stream - * @throws \InvalidArgumentException if the $resource arg is not valid. - */ - public static function factory($resource = '', array $options = []) - { - $type = gettype($resource); - - if ($type == 'string') { - $stream = fopen('php://temp', 'r+'); - if ($resource !== '') { - fwrite($stream, $resource); - fseek($stream, 0); - } - return new self($stream, $options); - } - - if ($type == 'resource') { - return new self($resource, $options); - } - - if ($resource instanceof StreamInterface) { - return $resource; - } - - if ($type == 'object' && method_exists($resource, '__toString')) { - return self::factory((string) $resource, $options); - } - - if (is_callable($resource)) { - return new PumpStream($resource, $options); - } - - if ($resource instanceof \Iterator) { - return new PumpStream(function () use ($resource) { - if (!$resource->valid()) { - return false; - } - $result = $resource->current(); - $resource->next(); - return $result; - }, $options); - } - - throw new \InvalidArgumentException('Invalid resource type: ' . $type); - } - - /** - * This constructor accepts an associative array of options. - * - * - size: (int) If a read stream would otherwise have an indeterminate - * size, but the size is known due to foreknownledge, then you can - * provide that size, in bytes. - * - metadata: (array) Any additional metadata to return when the metadata - * of the stream is accessed. - * - * @param resource $stream Stream resource to wrap. - * @param array $options Associative array of options. - * - * @throws \InvalidArgumentException if the stream is not a stream resource - */ - public function __construct($stream, $options = []) - { - if (!is_resource($stream)) { - throw new \InvalidArgumentException('Stream must be a resource'); - } - - if (isset($options['size'])) { - $this->size = $options['size']; - } - - $this->customMetadata = isset($options['metadata']) - ? $options['metadata'] - : []; - - $this->attach($stream); - } - - /** - * Closes the stream when the destructed - */ - public function __destruct() - { - $this->close(); - } - - public function __toString() - { - if (!$this->stream) { - return ''; - } - - $this->seek(0); - - return (string) stream_get_contents($this->stream); - } - - public function getContents() - { - return $this->stream ? stream_get_contents($this->stream) : ''; - } - - public function close() - { - if (is_resource($this->stream)) { - fclose($this->stream); - } - - $this->detach(); - } - - public function detach() - { - $result = $this->stream; - $this->stream = $this->size = $this->uri = null; - $this->readable = $this->writable = $this->seekable = false; - - return $result; - } - - public function attach($stream) - { - $this->stream = $stream; - $meta = stream_get_meta_data($this->stream); - $this->seekable = $meta['seekable']; - $this->readable = isset(self::$readWriteHash['read'][$meta['mode']]); - $this->writable = isset(self::$readWriteHash['write'][$meta['mode']]); - $this->uri = $this->getMetadata('uri'); - } - - public function getSize() - { - if ($this->size !== null) { - return $this->size; - } - - if (!$this->stream) { - return null; - } - - // Clear the stat cache if the stream has a URI - if ($this->uri) { - clearstatcache(true, $this->uri); - } - - $stats = fstat($this->stream); - if (isset($stats['size'])) { - $this->size = $stats['size']; - return $this->size; - } - - return null; - } - - public function isReadable() - { - return $this->readable; - } - - public function isWritable() - { - return $this->writable; - } - - public function isSeekable() - { - return $this->seekable; - } - - public function eof() - { - return !$this->stream || feof($this->stream); - } - - public function tell() - { - return $this->stream ? ftell($this->stream) : false; - } - - public function setSize($size) - { - $this->size = $size; - - return $this; - } - - public function seek($offset, $whence = SEEK_SET) - { - return $this->seekable - ? fseek($this->stream, $offset, $whence) === 0 - : false; - } - - public function read($length) - { - return $this->readable ? fread($this->stream, $length) : false; - } - - public function write($string) - { - // We can't know the size after writing anything - $this->size = null; - - return $this->writable ? fwrite($this->stream, $string) : false; - } - - public function getMetadata($key = null) - { - if (!$this->stream) { - return $key ? null : []; - } elseif (!$key) { - return $this->customMetadata + stream_get_meta_data($this->stream); - } elseif (isset($this->customMetadata[$key])) { - return $this->customMetadata[$key]; - } - - $meta = stream_get_meta_data($this->stream); - - return isset($meta[$key]) ? $meta[$key] : null; - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/StreamDecoratorTrait.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/StreamDecoratorTrait.php deleted file mode 100644 index 39c19c58cc..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/StreamDecoratorTrait.php +++ /dev/null @@ -1,143 +0,0 @@ -stream = $stream; - } - - /** - * Magic method used to create a new stream if streams are not added in - * the constructor of a decorator (e.g., LazyOpenStream). - */ - public function __get($name) - { - if ($name == 'stream') { - $this->stream = $this->createStream(); - return $this->stream; - } - - throw new \UnexpectedValueException("$name not found on class"); - } - - public function __toString() - { - try { - $this->seek(0); - return $this->getContents(); - } catch (\Exception $e) { - // Really, PHP? https://bugs.php.net/bug.php?id=53648 - trigger_error('StreamDecorator::__toString exception: ' - . (string) $e, E_USER_ERROR); - return ''; - } - } - - public function getContents() - { - return Utils::copyToString($this); - } - - /** - * Allow decorators to implement custom methods - * - * @param string $method Missing method name - * @param array $args Method arguments - * - * @return mixed - */ - public function __call($method, array $args) - { - $result = call_user_func_array(array($this->stream, $method), $args); - - // Always return the wrapped object if the result is a return $this - return $result === $this->stream ? $this : $result; - } - - public function close() - { - $this->stream->close(); - } - - public function getMetadata($key = null) - { - return $this->stream->getMetadata($key); - } - - public function detach() - { - return $this->stream->detach(); - } - - public function attach($stream) - { - throw new CannotAttachException(); - } - - public function getSize() - { - return $this->stream->getSize(); - } - - public function eof() - { - return $this->stream->eof(); - } - - public function tell() - { - return $this->stream->tell(); - } - - public function isReadable() - { - return $this->stream->isReadable(); - } - - public function isWritable() - { - return $this->stream->isWritable(); - } - - public function isSeekable() - { - return $this->stream->isSeekable(); - } - - public function seek($offset, $whence = SEEK_SET) - { - return $this->stream->seek($offset, $whence); - } - - public function read($length) - { - return $this->stream->read($length); - } - - public function write($string) - { - return $this->stream->write($string); - } - - /** - * Implement in subclasses to dynamically create streams when requested. - * - * @return StreamInterface - * @throws \BadMethodCallException - */ - protected function createStream() - { - throw new \BadMethodCallException('createStream() not implemented in ' - . get_class($this)); - } -} diff --git a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/StreamInterface.php b/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/StreamInterface.php deleted file mode 100644 index fd19c6f25a..0000000000 --- a/core/src/core/classes/guzzle/vendor/guzzlehttp/streams/src/StreamInterface.php +++ /dev/null @@ -1,159 +0,0 @@ -eof()) { - $buf = $stream->read(1048576); - if ($buf === false) { - break; - } - $buffer .= $buf; - } - return $buffer; - } - - $len = 0; - while (!$stream->eof() && $len < $maxLen) { - $buf = $stream->read($maxLen - $len); - if ($buf === false) { - break; - } - $buffer .= $buf; - $len = strlen($buffer); - } - - return $buffer; - } - - /** - * Copy the contents of a stream into another stream until the given number - * of bytes have been read. - * - * @param StreamInterface $source Stream to read from - * @param StreamInterface $dest Stream to write to - * @param int $maxLen Maximum number of bytes to read. Pass -1 - * to read the entire stream. - */ - public static function copyToStream( - StreamInterface $source, - StreamInterface $dest, - $maxLen = -1 - ) { - if ($maxLen === -1) { - while (!$source->eof()) { - if (!$dest->write($source->read(1048576))) { - break; - } - } - return; - } - - $bytes = 0; - while (!$source->eof()) { - $buf = $source->read($maxLen - $bytes); - if (!($len = strlen($buf))) { - break; - } - $bytes += $len; - $dest->write($buf); - if ($bytes == $maxLen) { - break; - } - } - } - - /** - * Calculate a hash of a Stream - * - * @param StreamInterface $stream Stream to calculate the hash for - * @param string $algo Hash algorithm (e.g. md5, crc32, etc) - * @param bool $rawOutput Whether or not to use raw output - * - * @return string Returns the hash of the stream - * @throws SeekException - */ - public static function hash( - StreamInterface $stream, - $algo, - $rawOutput = false - ) { - $pos = $stream->tell(); - - if ($pos > 0 && !$stream->seek(0)) { - throw new SeekException($stream); - } - - $ctx = hash_init($algo); - while (!$stream->eof()) { - hash_update($ctx, $stream->read(1048576)); - } - - $out = hash_final($ctx, (bool) $rawOutput); - $stream->seek($pos); - - return $out; - } - - /** - * Read a line from the stream up to the maximum allowed buffer length - * - * @param StreamInterface $stream Stream to read from - * @param int $maxLength Maximum buffer length - * - * @return string|bool - */ - public static function readline(StreamInterface $stream, $maxLength = null) - { - $buffer = ''; - $size = 0; - - while (!$stream->eof()) { - if (false === ($byte = $stream->read(1))) { - return $buffer; - } - $buffer .= $byte; - // Break when a new line is found or the max length - 1 is reached - if ($byte == PHP_EOL || ++$size == $maxLength - 1) { - break; - } - } - - return $buffer; - } - - /** - * Alias of GuzzleHttp\Stream\Stream::factory. - * - * @param mixed $resource Resource to create - * @param array $options Associative array of stream options defined in - * {@see \GuzzleHttp\Stream\Stream::__construct} - * - * @return StreamInterface - * - * @see GuzzleHttp\Stream\Stream::factory - * @see GuzzleHttp\Stream\Stream::__construct - */ - public static function create($resource, array $options = []) - { - return Stream::factory($resource, $options); - } -} diff --git a/core/src/core/classes/guzzle/vendor/react/promise/.gitignore b/core/src/core/classes/guzzle/vendor/react/promise/.gitignore deleted file mode 100644 index 5241c60aba..0000000000 --- a/core/src/core/classes/guzzle/vendor/react/promise/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -composer.lock -composer.phar -phpunit.xml -build/ -vendor/ diff --git a/core/src/core/classes/guzzle/vendor/react/promise/.travis.yml b/core/src/core/classes/guzzle/vendor/react/promise/.travis.yml deleted file mode 100644 index 19c08ab1a9..0000000000 --- a/core/src/core/classes/guzzle/vendor/react/promise/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: php - -php: - - 5.4 - - 5.5 - - 5.6 - - 7.0 - - nightly - - hhvm - -before_install: - - composer self-update - -install: - - composer install - -script: - - phpunit -v --coverage-text --coverage-clover=./build/logs/clover.xml - -after_script: - - if [ -f ./build/logs/clover.xml ]; then travis_retry composer require satooshi/php-coveralls --no-interaction --update-with-dependencies; fi - - if [ -f ./build/logs/clover.xml ]; then php vendor/bin/coveralls -v; fi diff --git a/core/src/core/classes/guzzle/vendor/react/promise/CHANGELOG.md b/core/src/core/classes/guzzle/vendor/react/promise/CHANGELOG.md deleted file mode 100644 index 4b1d59d02f..0000000000 --- a/core/src/core/classes/guzzle/vendor/react/promise/CHANGELOG.md +++ /dev/null @@ -1,93 +0,0 @@ -CHANGELOG -========= - -* 2.4.1 (2016-05-03) - - * Fix `some()` not cancelling pending promises when too much input promises - reject (16ff799). - -* 2.4.0 (2016-03-31) - - * Support foreign thenables in `resolve()`. - Any object that provides a `then()` method is now assimilated to a trusted - promise that follows the state of this thenable (#52). - * Fix `some()` and `any()` for input arrays containing not enough items - (#34). - -* 2.3.0 (2016-03-24) - - * Allow cancellation of promises returned by functions working on promise - collections (#36). - * Handle `\Throwable` in the same way as `\Exception` (#51 by @joshdifabio). - -* 2.2.2 (2016-02-26) - - * Fix cancellation handlers called multiple times (#47 by @clue). - -* 2.2.1 (2015-07-03) - - * Fix stack error when resolving a promise in its own fulfillment or - rejection handlers. - -* 2.2.0 (2014-12-30) - - * Introduce new `ExtendedPromiseInterface` implemented by all promises. - * Add new `done()` method (part of the `ExtendedPromiseInterface`). - * Add new `otherwise()` method (part of the `ExtendedPromiseInterface`). - * Add new `always()` method (part of the `ExtendedPromiseInterface`). - * Add new `progress()` method (part of the `ExtendedPromiseInterface`). - * Rename `Deferred::progress` to `Deferred::notify` to avoid confusion with - `ExtendedPromiseInterface::progress` (a `Deferred::progress` alias is - still available for backward compatibility) - * `resolve()` now always returns a `ExtendedPromiseInterface`. - -* 2.1.0 (2014-10-15) - - * Introduce new `CancellablePromiseInterface` implemented by all promises. - * Add new `cancel()` method (part of the `CancellablePromiseInterface`). - -* 2.0.0 (2013-12-10) - - New major release. The goal is to streamline the API and to make it more - compliant with other promise libraries and especially with the new upcoming - [ES6 promises specification](https://github.com/domenic/promises-unwrapping/). - - * Add standalone Promise class. - * Add new `race()` function. - * BC break: Bump minimum PHP version to PHP 5.4. - * BC break: Remove `ResolverInterface` and `PromiseInterface` from - `Deferred`. - * BC break: Change signature of `PromiseInterface`. - * BC break: Remove `When` and `Util` classes and move static methods to - functions. - * BC break: `FulfilledPromise` and `RejectedPromise` now throw an exception - when initialized with a promise instead of a value/reason. - * BC break: `Deferred::resolve()` and `Deferred::reject()` no longer return - a promise. - -* 1.0.4 (2013-04-03) - - * Trigger PHP errors when invalid callback is passed. - * Fully resolve rejection value before calling rejection handler. - * Add `When::lazy()` to create lazy promises which will be initialized once - a consumer calls the `then()` method. - -* 1.0.3 (2012-11-17) - - * Add `PromisorInterface` for objects that have a `promise()` method. - -* 1.0.2 (2012-11-14) - - * Fix bug in `When::any()` not correctly unwrapping to a single result - value. - * `$promiseOrValue` argument of `When::resolve()` and When::reject() is now - optional. - -* 1.0.1 (2012-11-13) - - * Prevent deep recursion which was reaching `xdebug.max_nesting_level` - default of 100. - -* 1.0.0 (2012-11-07) - - * First tagged release. diff --git a/core/src/core/classes/guzzle/vendor/react/promise/LICENSE b/core/src/core/classes/guzzle/vendor/react/promise/LICENSE deleted file mode 100644 index 5919d20ff3..0000000000 --- a/core/src/core/classes/guzzle/vendor/react/promise/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2012-2016 Jan Sorgalla - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. diff --git a/core/src/core/classes/guzzle/vendor/react/promise/README.md b/core/src/core/classes/guzzle/vendor/react/promise/README.md deleted file mode 100644 index 9c0558c04d..0000000000 --- a/core/src/core/classes/guzzle/vendor/react/promise/README.md +++ /dev/null @@ -1,840 +0,0 @@ -React/Promise -============= - -A lightweight implementation of -[CommonJS Promises/A](http://wiki.commonjs.org/wiki/Promises/A) for PHP. - -[![Build Status](https://travis-ci.org/reactphp/promise.svg?branch=master)](http://travis-ci.org/reactphp/promise) -[![Coverage Status](https://coveralls.io/repos/github/reactphp/promise/badge.svg?branch=master)](https://coveralls.io/github/reactphp/promise?branch=master) - -Table of Contents ------------------ - -1. [Introduction](#introduction) -2. [Concepts](#concepts) - * [Deferred](#deferred) - * [Promise](#promise) -3. [API](#api) - * [Deferred](#deferred-1) - * [Deferred::promise()](#deferredpromise) - * [Deferred::resolve()](#deferredresolve) - * [Deferred::reject()](#deferredreject) - * [Deferred::notify()](#deferrednotify) - * [PromiseInterface](#promiseinterface) - * [PromiseInterface::then()](#promiseinterfacethen) - * [ExtendedPromiseInterface](#extendedpromiseinterface) - * [ExtendedPromiseInterface::done()](#extendedpromiseinterfacedone) - * [ExtendedPromiseInterface::otherwise()](#extendedpromiseinterfaceotherwise) - * [ExtendedPromiseInterface::always()](#extendedpromiseinterfacealways) - * [ExtendedPromiseInterface::progress()](#extendedpromiseinterfaceprogress) - * [CancellablePromiseInterface](#cancellablepromiseinterface) - * [CancellablePromiseInterface::cancel()](#cancellablepromiseinterfacecancel) - * [Promise](#promise-1) - * [FulfilledPromise](#fulfilledpromise) - * [RejectedPromise](#rejectedpromise) - * [LazyPromise](#lazypromise) - * [Functions](#functions) - * [resolve()](#resolve) - * [reject()](#reject) - * [all()](#all) - * [race()](#race) - * [any()](#any) - * [some()](#some) - * [map()](#map) - * [reduce()](#reduce) - * [PromisorInterface](#promisorinterface) -4. [Examples](#examples) - * [How to use Deferred](#how-to-use-deferred) - * [How promise forwarding works](#how-promise-forwarding-works) - * [Resolution forwarding](#resolution-forwarding) - * [Rejection forwarding](#rejection-forwarding) - * [Mixed resolution and rejection forwarding](#mixed-resolution-and-rejection-forwarding) - * [Progress event forwarding](#progress-event-forwarding) - * [done() vs. then()](#done-vs-then) -5. [Credits](#credits) -6. [License](#license) - -Introduction ------------- - -React/Promise is a library implementing -[CommonJS Promises/A](http://wiki.commonjs.org/wiki/Promises/A) for PHP. - -It also provides several other useful promise-related concepts, such as joining -multiple promises and mapping and reducing collections of promises. - -If you've never heard about promises before, -[read this first](https://gist.github.com/3889970). - -Concepts --------- - -### Deferred - -A **Deferred** represents a computation or unit of work that may not have -completed yet. Typically (but not always), that computation will be something -that executes asynchronously and completes at some point in the future. - -### Promise - -While a deferred represents the computation itself, a **Promise** represents -the result of that computation. Thus, each deferred has a promise that acts as -a placeholder for its actual result. - -API ---- - -### Deferred - -A deferred represents an operation whose resolution is pending. It has separate -promise and resolver parts. - -```php -$deferred = new React\Promise\Deferred(); - -$promise = $deferred->promise(); - -$deferred->resolve(mixed $value = null); -$deferred->reject(mixed $reason = null); -$deferred->notify(mixed $update = null); -``` - -The `promise` method returns the promise of the deferred. - -The `resolve` and `reject` methods control the state of the deferred. - -The `notify` method is for progress notification. - -The constructor of the `Deferred` accepts an optional `$canceller` argument. -See [Promise](#promise-1) for more information. - -#### Deferred::promise() - -```php -$promise = $deferred->promise(); -``` - -Returns the promise of the deferred, which you can hand out to others while -keeping the authority to modify its state to yourself. - -#### Deferred::resolve() - -```php -$deferred->resolve(mixed $value = null); -``` - -Resolves the promise returned by `promise()`. All consumers are notified by -having `$onFulfilled` (which they registered via `$promise->then()`) called with -`$value`. - -If `$value` itself is a promise, the promise will transition to the state of -this promise once it is resolved. - -#### Deferred::reject() - -```php -$deferred->reject(mixed $reason = null); -``` - -Rejects the promise returned by `promise()`, signalling that the deferred's -computation failed. -All consumers are notified by having `$onRejected` (which they registered via -`$promise->then()`) called with `$reason`. - -If `$reason` itself is a promise, the promise will be rejected with the outcome -of this promise regardless whether it fulfills or rejects. - -#### Deferred::notify() - -```php -$deferred->notify(mixed $update = null); -``` - -Triggers progress notifications, to indicate to consumers that the computation -is making progress toward its result. - -All consumers are notified by having `$onProgress` (which they registered via -`$promise->then()`) called with `$update`. - -### PromiseInterface - -The promise interface provides the common interface for all promise -implementations. - -A promise represents an eventual outcome, which is either fulfillment (success) -and an associated value, or rejection (failure) and an associated reason. - -Once in the fulfilled or rejected state, a promise becomes immutable. -Neither its state nor its result (or error) can be modified. - -#### Implementations - -* [Promise](#promise-1) -* [FulfilledPromise](#fulfilledpromise) -* [RejectedPromise](#rejectedpromise) -* [LazyPromise](#lazypromise) - -#### PromiseInterface::then() - -```php -$transformedPromise = $promise->then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); -``` - -Transforms a promise's value by applying a function to the promise's fulfillment -or rejection value. Returns a new promise for the transformed result. - -The `then()` method registers new fulfilled, rejection and progress handlers -with a promise (all parameters are optional): - - * `$onFulfilled` will be invoked once the promise is fulfilled and passed - the result as the first argument. - * `$onRejected` will be invoked once the promise is rejected and passed the - reason as the first argument. - * `$onProgress` will be invoked whenever the producer of the promise - triggers progress notifications and passed a single argument (whatever it - wants) to indicate progress. - -It returns a new promise that will fulfill with the return value of either -`$onFulfilled` or `$onRejected`, whichever is called, or will reject with -the thrown exception if either throws. - -A promise makes the following guarantees about handlers registered in -the same call to `then()`: - - 1. Only one of `$onFulfilled` or `$onRejected` will be called, - never both. - 2. `$onFulfilled` and `$onRejected` will never be called more - than once. - 3. `$onProgress` may be called multiple times. - -#### See also - -* [resolve()](#resolve) - Creating a resolved promise -* [reject()](#reject) - Creating a rejected promise -* [ExtendedPromiseInterface::done()](#extendedpromiseinterfacedone) -* [done() vs. then()](#done-vs-then) - -### ExtendedPromiseInterface - -The ExtendedPromiseInterface extends the PromiseInterface with useful shortcut -and utility methods which are not part of the Promises/A specification. - -#### Implementations - -* [Promise](#promise-1) -* [FulfilledPromise](#fulfilledpromise) -* [RejectedPromise](#rejectedpromise) -* [LazyPromise](#lazypromise) - -#### ExtendedPromiseInterface::done() - -```php -$promise->done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); -``` - -Consumes the promise's ultimate value if the promise fulfills, or handles the -ultimate error. - -It will cause a fatal error if either `$onFulfilled` or `$onRejected` throw or -return a rejected promise. - -Since the purpose of `done()` is consumption rather than transformation, -`done()` always returns `null`. - -#### See also - -* [PromiseInterface::then()](#promiseinterfacethen) -* [done() vs. then()](#done-vs-then) - -#### ExtendedPromiseInterface::otherwise() - -```php -$promise->otherwise(callable $onRejected); -``` - -Registers a rejection handler for promise. It is a shortcut for: - -```php -$promise->then(null, $onRejected); -``` - -Additionally, you can type hint the `$reason` argument of `$onRejected` to catch -only specific errors. - -```php -$promise - ->otherwise(function (\RuntimeException $reason) { - // Only catch \RuntimeException instances - // All other types of errors will propagate automatically - }) - ->otherwise(function ($reason) { - // Catch other errors - )}; -``` - -#### ExtendedPromiseInterface::always() - -```php -$newPromise = $promise->always(callable $onFulfilledOrRejected); -``` - -Allows you to execute "cleanup" type tasks in a promise chain. - -It arranges for `$onFulfilledOrRejected` to be called, with no arguments, -when the promise is either fulfilled or rejected. - -* If `$promise` fulfills, and `$onFulfilledOrRejected` returns successfully, - `$newPromise` will fulfill with the same value as `$promise`. -* If `$promise` fulfills, and `$onFulfilledOrRejected` throws or returns a - rejected promise, `$newPromise` will reject with the thrown exception or - rejected promise's reason. -* If `$promise` rejects, and `$onFulfilledOrRejected` returns successfully, - `$newPromise` will reject with the same reason as `$promise`. -* If `$promise` rejects, and `$onFulfilledOrRejected` throws or returns a - rejected promise, `$newPromise` will reject with the thrown exception or - rejected promise's reason. - -`always()` behaves similarly to the synchronous finally statement. When combined -with `otherwise()`, `always()` allows you to write code that is similar to the familiar -synchronous catch/finally pair. - -Consider the following synchronous code: - -```php -try { - return doSomething(); -} catch(\Exception $e) { - return handleError($e); -} finally { - cleanup(); -} -``` - -Similar asynchronous code (with `doSomething()` that returns a promise) can be -written: - -```php -return doSomething() - ->otherwise('handleError') - ->always('cleanup'); -``` - -#### ExtendedPromiseInterface::progress() - -```php -$promise->progress(callable $onProgress); -``` - -Registers a handler for progress updates from promise. It is a shortcut for: - -```php -$promise->then(null, null, $onProgress); -``` - -### CancellablePromiseInterface - -A cancellable promise provides a mechanism for consumers to notify the creator -of the promise that they are not longer interested in the result of an -operation. - -#### CancellablePromiseInterface::cancel() - -``` php -$promise->cancel(); -``` - -The `cancel()` method notifies the creator of the promise that there is no -further interest in the results of the operation. - -Once a promise is settled (either fulfilled or rejected), calling `cancel()` on -a promise has no effect. - -#### Implementations - -* [Promise](#promise-1) -* [FulfilledPromise](#fulfilledpromise) -* [RejectedPromise](#rejectedpromise) -* [LazyPromise](#lazypromise) - -### Promise - -Creates a promise whose state is controlled by the functions passed to -`$resolver`. - -```php -$resolver = function (callable $resolve, callable $reject, callable $notify) { - // Do some work, possibly asynchronously, and then - // resolve or reject. You can notify of progress events - // along the way if you want/need. - - $resolve($awesomeResult); - // or $resolve($anotherPromise); - // or $reject($nastyError); - // or $notify($progressNotification); -}; - -$canceller = function (callable $resolve, callable $reject, callable $progress) { - // Cancel/abort any running operations like network connections, streams etc. - - $reject(new \Exception('Promise cancelled')); -}; - -$promise = new React\Promise\Promise($resolver, $canceller); -``` - -The promise constructor receives a resolver function and an optional canceller -function which both will be called with 3 arguments: - - * `$resolve($value)` - Primary function that seals the fate of the - returned promise. Accepts either a non-promise value, or another promise. - When called with a non-promise value, fulfills promise with that value. - When called with another promise, e.g. `$resolve($otherPromise)`, promise's - fate will be equivalent to that of `$otherPromise`. - * `$reject($reason)` - Function that rejects the promise. - * `$notify($update)` - Function that issues progress events for the promise. - -If the resolver or canceller throw an exception, the promise will be rejected -with that thrown exception as the rejection reason. - -The resolver function will be called immediately, the canceller function only -once all consumers called the `cancel()` method of the promise. - -### FulfilledPromise - -Creates a already fulfilled promise. - -```php -$promise = React\Promise\FulfilledPromise($value); -``` - -Note, that `$value` **cannot** be a promise. It's recommended to use -[resolve()](#resolve) for creating resolved promises. - -### RejectedPromise - -Creates a already rejected promise. - -```php -$promise = React\Promise\RejectedPromise($reason); -``` - -Note, that `$reason` **cannot** be a promise. It's recommended to use -[reject()](#reject) for creating rejected promises. - -### LazyPromise - -Creates a promise which will be lazily initialized by `$factory` once a consumer -calls the `then()` method. - -```php -$factory = function () { - $deferred = new React\Promise\Deferred(); - - // Do some heavy stuff here and resolve the deferred once completed - - return $deferred->promise(); -}; - -$promise = React\Promise\LazyPromise($factory); - -// $factory will only be executed once we call then() -$promise->then(function ($value) { -}); -``` - -### Functions - -Useful functions for creating, joining, mapping and reducing collections of -promises. - -All functions working on promise collections (like `all()`, `race()`, `some()` -etc.) support cancellation. This means, if you call `cancel()` on the returned -promise, all promises in the collection are cancelled. If the collection itself -is a promise which resolves to an array, this promise is also cancelled. - -#### resolve() - -```php -$promise = React\Promise\resolve(mixed $promiseOrValue); -``` - -Creates a promise for the supplied `$promiseOrValue`. - -If `$promiseOrValue` is a value, it will be the resolution value of the -returned promise. - -If `$promiseOrValue` is a thenable (any object that provides a `then()` method), -a trusted promise that follows the state of the thenable is returned. - -If `$promiseOrValue` is a promise, it will be returned as is. - -Note: The promise returned is always a promise implementing -[ExtendedPromiseInterface](#extendedpromiseinterface). If you pass in a custom -promise which only implements [PromiseInterface](#promiseinterface), this -promise will be assimilated to a extended promise following `$promiseOrValue`. - -#### reject() - -```php -$promise = React\Promise\reject(mixed $promiseOrValue); -``` - -Creates a rejected promise for the supplied `$promiseOrValue`. - -If `$promiseOrValue` is a value, it will be the rejection value of the -returned promise. - -If `$promiseOrValue` is a promise, its completion value will be the rejected -value of the returned promise. - -This can be useful in situations where you need to reject a promise without -throwing an exception. For example, it allows you to propagate a rejection with -the value of another promise. - -#### all() - -```php -$promise = React\Promise\all(array|React\Promise\PromiseInterface $promisesOrValues); -``` - -Returns a promise that will resolve only once all the items in -`$promisesOrValues` have resolved. The resolution value of the returned promise -will be an array containing the resolution values of each of the items in -`$promisesOrValues`. - -#### race() - -```php -$promise = React\Promise\race(array|React\Promise\PromiseInterface $promisesOrValues); -``` - -Initiates a competitive race that allows one winner. Returns a promise which is -resolved in the same way the first settled promise resolves. - -#### any() - -```php -$promise = React\Promise\any(array|React\Promise\PromiseInterface $promisesOrValues); -``` - -Returns a promise that will resolve when any one of the items in -`$promisesOrValues` resolves. The resolution value of the returned promise -will be the resolution value of the triggering item. - -The returned promise will only reject if *all* items in `$promisesOrValues` are -rejected. The rejection value will be an array of all rejection reasons. - -The returned promise will also reject with a `React\Promise\Exception\LengthException` -if `$promisesOrValues` contains 0 items. - -#### some() - -```php -$promise = React\Promise\some(array|React\Promise\PromiseInterface $promisesOrValues, integer $howMany); -``` - -Returns a promise that will resolve when `$howMany` of the supplied items in -`$promisesOrValues` resolve. The resolution value of the returned promise -will be an array of length `$howMany` containing the resolution values of the -triggering items. - -The returned promise will reject if it becomes impossible for `$howMany` items -to resolve (that is, when `(count($promisesOrValues) - $howMany) + 1` items -reject). The rejection value will be an array of -`(count($promisesOrValues) - $howMany) + 1` rejection reasons. - -The returned promise will also reject with a `React\Promise\Exception\LengthException` -if `$promisesOrValues` contains less items than `$howMany`. - -#### map() - -```php -$promise = React\Promise\map(array|React\Promise\PromiseInterface $promisesOrValues, callable $mapFunc); -``` - -Traditional map function, similar to `array_map()`, but allows input to contain -promises and/or values, and `$mapFunc` may return either a value or a promise. - -The map function receives each item as argument, where item is a fully resolved -value of a promise or value in `$promisesOrValues`. - -#### reduce() - -```php -$promise = React\Promise\reduce(array|React\Promise\PromiseInterface $promisesOrValues, callable $reduceFunc , $initialValue = null); -``` - -Traditional reduce function, similar to `array_reduce()`, but input may contain -promises and/or values, and `$reduceFunc` may return either a value or a -promise, *and* `$initialValue` may be a promise or a value for the starting -value. - -### PromisorInterface - -The `React\Promise\PromisorInterface` provides a common interface for objects -that provide a promise. `React\Promise\Deferred` implements it, but since it -is part of the public API anyone can implement it. - -Examples --------- - -### How to use Deferred - -```php -function getAwesomeResultPromise() -{ - $deferred = new React\Promise\Deferred(); - - // Execute a Node.js-style function using the callback pattern - computeAwesomeResultAsynchronously(function ($error, $result) use ($deferred) { - if ($error) { - $deferred->reject($error); - } else { - $deferred->resolve($result); - } - }); - - // Return the promise - return $deferred->promise(); -} - -getAwesomeResultPromise() - ->then( - function ($value) { - // Deferred resolved, do something with $value - }, - function ($reason) { - // Deferred rejected, do something with $reason - }, - function ($update) { - // Progress notification triggered, do something with $update - } - ); -``` - -### How promise forwarding works - -A few simple examples to show how the mechanics of Promises/A forwarding works. -These examples are contrived, of course, and in real usage, promise chains will -typically be spread across several function calls, or even several levels of -your application architecture. - -#### Resolution forwarding - -Resolved promises forward resolution values to the next promise. -The first promise, `$deferred->promise()`, will resolve with the value passed -to `$deferred->resolve()` below. - -Each call to `then()` returns a new promise that will resolve with the return -value of the previous handler. This creates a promise "pipeline". - -```php -$deferred = new React\Promise\Deferred(); - -$deferred->promise() - ->then(function ($x) { - // $x will be the value passed to $deferred->resolve() below - // and returns a *new promise* for $x + 1 - return $x + 1; - }) - ->then(function ($x) { - // $x === 2 - // This handler receives the return value of the - // previous handler. - return $x + 1; - }) - ->then(function ($x) { - // $x === 3 - // This handler receives the return value of the - // previous handler. - return $x + 1; - }) - ->then(function ($x) { - // $x === 4 - // This handler receives the return value of the - // previous handler. - echo 'Resolve ' . $x; - }); - -$deferred->resolve(1); // Prints "Resolve 4" -``` - -#### Rejection forwarding - -Rejected promises behave similarly, and also work similarly to try/catch: -When you catch an exception, you must rethrow for it to propagate. - -Similarly, when you handle a rejected promise, to propagate the rejection, -"rethrow" it by either returning a rejected promise, or actually throwing -(since promise translates thrown exceptions into rejections) - -```php -$deferred = new React\Promise\Deferred(); - -$deferred->promise() - ->then(function ($x) { - throw new \Exception($x + 1); - }) - ->otherwise(function (\Exception $x) { - // Propagate the rejection - throw $x; - }) - ->otherwise(function (\Exception $x) { - // Can also propagate by returning another rejection - return React\Promise\reject( - new \Exception($x->getMessage() + 1) - ); - }) - ->otherwise(function ($x) { - echo 'Reject ' . $x->getMessage(); // 3 - }); - -$deferred->resolve(1); // Prints "Reject 3" -``` - -#### Mixed resolution and rejection forwarding - -Just like try/catch, you can choose to propagate or not. Mixing resolutions and -rejections will still forward handler results in a predictable way. - -```php -$deferred = new React\Promise\Deferred(); - -$deferred->promise() - ->then(function ($x) { - return $x + 1; - }) - ->then(function ($x) { - throw new \Exception($x + 1); - }) - ->otherwise(function (\Exception $x) { - // Handle the rejection, and don't propagate. - // This is like catch without a rethrow - return $x->getMessage() + 1; - }) - ->then(function ($x) { - echo 'Mixed ' . $x; // 4 - }); - -$deferred->resolve(1); // Prints "Mixed 4" -``` - -#### Progress event forwarding - -In the same way as resolution and rejection handlers, your progress handler -**MUST** return a progress event to be propagated to the next link in the chain. -If you return nothing, `null` will be propagated. - -Also in the same way as resolutions and rejections, if you don't register a -progress handler, the update will be propagated through. - -If your progress handler throws an exception, the exception will be propagated -to the next link in the chain. The best thing to do is to ensure your progress -handlers do not throw exceptions. - -This gives you the opportunity to transform progress events at each step in the -chain so that they are meaningful to the next step. It also allows you to choose -not to transform them, and simply let them propagate untransformed, by not -registering a progress handler. - -```php -$deferred = new React\Promise\Deferred(); - -$deferred->promise() - ->progress(function ($update) { - return $update + 1; - }) - ->progress(function ($update) { - echo 'Progress ' . $update; // 2 - }); - -$deferred->notify(1); // Prints "Progress 2" -``` - -### done() vs. then() - -The golden rule is: - - Either return your promise, or call done() on it. - -At a first glance, `then()` and `done()` seem very similar. However, there are -important distinctions. - -The intent of `then()` is to transform a promise's value and to pass or return -a new promise for the transformed value along to other parts of your code. - -The intent of `done()` is to consume a promise's value, transferring -responsibility for the value to your code. - -In addition to transforming a value, `then()` allows you to recover from, or -propagate intermediate errors. Any errors that are not handled will be caught -by the promise machinery and used to reject the promise returned by `then()`. - -Calling `done()` transfers all responsibility for errors to your code. If an -error (either a thrown exception or returned rejection) escapes the -`$onFulfilled` or `$onRejected` callbacks you provide to done, it will be -rethrown in an uncatchable way causing a fatal error. - -```php -function getJsonResult() -{ - return queryApi() - ->then( - // Transform API results to an object - function ($jsonResultString) { - return json_decode($jsonResultString); - }, - // Transform API errors to an exception - function ($jsonErrorString) { - $object = json_decode($jsonErrorString); - throw new ApiErrorException($object->errorMessage); - } - ); -} - -// Here we provide no rejection handler. If the promise returned has been -// rejected, the ApiErrorException will be thrown -getJsonResult() - ->done( - // Consume transformed object - function ($jsonResultObject) { - // Do something with $jsonResultObject - } - ); - -// Here we provide a rejection handler which will either throw while debugging -// or log the exception -getJsonResult() - ->done( - function ($jsonResultObject) { - // Do something with $jsonResultObject - }, - function (ApiErrorException $exception) { - if (isDebug()) { - throw $exception; - } else { - logException($exception); - } - } - ); -``` - -Note that if a rejection value is not an instance of `\Exception`, it will be -wrapped in an exception of the type `React\Promise\UnhandledRejectionException`. - -You can get the original rejection reason by calling `$exception->getReason()`. - -Credits -------- - -React/Promise is a port of [when.js](https://github.com/cujojs/when) -by [Brian Cavalier](https://github.com/briancavalier). - -Also, large parts of the documentation have been ported from the when.js -[Wiki](https://github.com/cujojs/when/wiki) and the -[API docs](https://github.com/cujojs/when/blob/master/docs/api.md). - -License -------- - -React/Promise is released under the [MIT](https://github.com/reactphp/promise/blob/master/LICENSE) license. diff --git a/core/src/core/classes/guzzle/vendor/react/promise/composer.json b/core/src/core/classes/guzzle/vendor/react/promise/composer.json deleted file mode 100644 index 22dae5a25e..0000000000 --- a/core/src/core/classes/guzzle/vendor/react/promise/composer.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "react/promise", - "description": "A lightweight implementation of CommonJS Promises/A for PHP", - "license": "MIT", - "authors": [ - {"name": "Jan Sorgalla", "email": "jsorgalla@gmail.com"} - ], - "require": { - "php": ">=5.4.0" - }, - "autoload": { - "psr-4": { - "React\\Promise\\": "src/" - }, - "files": ["src/functions_include.php"] - }, - "autoload-dev": { - "psr-4": { - "React\\Promise\\": "tests/fixtures" - } - }, - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/react/promise/phpunit.xml.dist b/core/src/core/classes/guzzle/vendor/react/promise/phpunit.xml.dist deleted file mode 100644 index b9a689d74f..0000000000 --- a/core/src/core/classes/guzzle/vendor/react/promise/phpunit.xml.dist +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - ./tests/ - - - - - - ./src/ - - ./src/functions_include.php - - - - diff --git a/core/src/core/classes/guzzle/vendor/react/promise/src/CancellablePromiseInterface.php b/core/src/core/classes/guzzle/vendor/react/promise/src/CancellablePromiseInterface.php deleted file mode 100644 index 896db2d372..0000000000 --- a/core/src/core/classes/guzzle/vendor/react/promise/src/CancellablePromiseInterface.php +++ /dev/null @@ -1,11 +0,0 @@ -canceller = $canceller; - } - - public function promise() - { - if (null === $this->promise) { - $this->promise = new Promise(function ($resolve, $reject, $notify) { - $this->resolveCallback = $resolve; - $this->rejectCallback = $reject; - $this->notifyCallback = $notify; - }, $this->canceller); - } - - return $this->promise; - } - - public function resolve($value = null) - { - $this->promise(); - - call_user_func($this->resolveCallback, $value); - } - - public function reject($reason = null) - { - $this->promise(); - - call_user_func($this->rejectCallback, $reason); - } - - public function notify($update = null) - { - $this->promise(); - - call_user_func($this->notifyCallback, $update); - } - - /** - * @deprecated 2.2.0 - * @see Deferred::notify() - */ - public function progress($update = null) - { - $this->notify($update); - } -} diff --git a/core/src/core/classes/guzzle/vendor/react/promise/src/ExtendedPromiseInterface.php b/core/src/core/classes/guzzle/vendor/react/promise/src/ExtendedPromiseInterface.php deleted file mode 100644 index 9cb6435987..0000000000 --- a/core/src/core/classes/guzzle/vendor/react/promise/src/ExtendedPromiseInterface.php +++ /dev/null @@ -1,26 +0,0 @@ -value = $value; - } - - public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - if (null === $onFulfilled) { - return $this; - } - - try { - return resolve($onFulfilled($this->value)); - } catch (\Throwable $exception) { - return new RejectedPromise($exception); - } catch (\Exception $exception) { - return new RejectedPromise($exception); - } - } - - public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - if (null === $onFulfilled) { - return; - } - - $result = $onFulfilled($this->value); - - if ($result instanceof ExtendedPromiseInterface) { - $result->done(); - } - } - - public function otherwise(callable $onRejected) - { - return $this; - } - - public function always(callable $onFulfilledOrRejected) - { - return $this->then(function ($value) use ($onFulfilledOrRejected) { - return resolve($onFulfilledOrRejected())->then(function () use ($value) { - return $value; - }); - }); - } - - public function progress(callable $onProgress) - { - return $this; - } - - public function cancel() - { - } -} diff --git a/core/src/core/classes/guzzle/vendor/react/promise/src/LazyPromise.php b/core/src/core/classes/guzzle/vendor/react/promise/src/LazyPromise.php deleted file mode 100644 index d72619e13d..0000000000 --- a/core/src/core/classes/guzzle/vendor/react/promise/src/LazyPromise.php +++ /dev/null @@ -1,59 +0,0 @@ -factory = $factory; - } - - public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - return $this->promise()->then($onFulfilled, $onRejected, $onProgress); - } - - public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - return $this->promise()->done($onFulfilled, $onRejected, $onProgress); - } - - public function otherwise(callable $onRejected) - { - return $this->promise()->otherwise($onRejected); - } - - public function always(callable $onFulfilledOrRejected) - { - return $this->promise()->always($onFulfilledOrRejected); - } - - public function progress(callable $onProgress) - { - return $this->promise()->progress($onProgress); - } - - public function cancel() - { - return $this->promise()->cancel(); - } - - private function promise() - { - if (null === $this->promise) { - try { - $this->promise = resolve(call_user_func($this->factory)); - } catch (\Throwable $exception) { - $this->promise = new RejectedPromise($exception); - } catch (\Exception $exception) { - $this->promise = new RejectedPromise($exception); - } - } - - return $this->promise; - } -} diff --git a/core/src/core/classes/guzzle/vendor/react/promise/src/Promise.php b/core/src/core/classes/guzzle/vendor/react/promise/src/Promise.php deleted file mode 100644 index bfbdc06dd0..0000000000 --- a/core/src/core/classes/guzzle/vendor/react/promise/src/Promise.php +++ /dev/null @@ -1,197 +0,0 @@ -canceller = $canceller; - $this->call($resolver); - } - - public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - if (null !== $this->result) { - return $this->result()->then($onFulfilled, $onRejected, $onProgress); - } - - if (null === $this->canceller) { - return new static($this->resolver($onFulfilled, $onRejected, $onProgress)); - } - - $this->requiredCancelRequests++; - - return new static($this->resolver($onFulfilled, $onRejected, $onProgress), function () { - if (++$this->cancelRequests < $this->requiredCancelRequests) { - return; - } - - $this->cancel(); - }); - } - - public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - if (null !== $this->result) { - return $this->result()->done($onFulfilled, $onRejected, $onProgress); - } - - $this->handlers[] = function (ExtendedPromiseInterface $promise) use ($onFulfilled, $onRejected) { - $promise - ->done($onFulfilled, $onRejected); - }; - - if ($onProgress) { - $this->progressHandlers[] = $onProgress; - } - } - - public function otherwise(callable $onRejected) - { - return $this->then(null, function ($reason) use ($onRejected) { - if (!_checkTypehint($onRejected, $reason)) { - return new RejectedPromise($reason); - } - - return $onRejected($reason); - }); - } - - public function always(callable $onFulfilledOrRejected) - { - return $this->then(function ($value) use ($onFulfilledOrRejected) { - return resolve($onFulfilledOrRejected())->then(function () use ($value) { - return $value; - }); - }, function ($reason) use ($onFulfilledOrRejected) { - return resolve($onFulfilledOrRejected())->then(function () use ($reason) { - return new RejectedPromise($reason); - }); - }); - } - - public function progress(callable $onProgress) - { - return $this->then(null, null, $onProgress); - } - - public function cancel() - { - if (null === $this->canceller || null !== $this->result) { - return; - } - - $canceller = $this->canceller; - $this->canceller = null; - - $this->call($canceller); - } - - private function resolver(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - return function ($resolve, $reject, $notify) use ($onFulfilled, $onRejected, $onProgress) { - if ($onProgress) { - $progressHandler = function ($update) use ($notify, $onProgress) { - try { - $notify($onProgress($update)); - } catch (\Throwable $e) { - $notify($e); - } catch (\Exception $e) { - $notify($e); - } - }; - } else { - $progressHandler = $notify; - } - - $this->handlers[] = function (ExtendedPromiseInterface $promise) use ($onFulfilled, $onRejected, $resolve, $reject, $progressHandler) { - $promise - ->then($onFulfilled, $onRejected) - ->done($resolve, $reject, $progressHandler); - }; - - $this->progressHandlers[] = $progressHandler; - }; - } - - private function resolve($value = null) - { - if (null !== $this->result) { - return; - } - - $this->settle(resolve($value)); - } - - private function reject($reason = null) - { - if (null !== $this->result) { - return; - } - - $this->settle(reject($reason)); - } - - private function notify($update = null) - { - if (null !== $this->result) { - return; - } - - foreach ($this->progressHandlers as $handler) { - $handler($update); - } - } - - private function settle(ExtendedPromiseInterface $promise) - { - $handlers = $this->handlers; - - $this->progressHandlers = $this->handlers = []; - $this->result = $promise; - - foreach ($handlers as $handler) { - $handler($promise); - } - } - - private function result() - { - while ($this->result instanceof self && null !== $this->result->result) { - $this->result = $this->result->result; - } - - return $this->result; - } - - private function call(callable $callback) - { - try { - $callback( - function ($value = null) { - $this->resolve($value); - }, - function ($reason = null) { - $this->reject($reason); - }, - function ($update = null) { - $this->notify($update); - } - ); - } catch (\Throwable $e) { - $this->reject($e); - } catch (\Exception $e) { - $this->reject($e); - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/react/promise/src/PromiseInterface.php b/core/src/core/classes/guzzle/vendor/react/promise/src/PromiseInterface.php deleted file mode 100644 index d80d11421a..0000000000 --- a/core/src/core/classes/guzzle/vendor/react/promise/src/PromiseInterface.php +++ /dev/null @@ -1,11 +0,0 @@ -reason = $reason; - } - - public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - if (null === $onRejected) { - return $this; - } - - try { - return resolve($onRejected($this->reason)); - } catch (\Throwable $exception) { - return new RejectedPromise($exception); - } catch (\Exception $exception) { - return new RejectedPromise($exception); - } - } - - public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - if (null === $onRejected) { - throw UnhandledRejectionException::resolve($this->reason); - } - - $result = $onRejected($this->reason); - - if ($result instanceof self) { - throw UnhandledRejectionException::resolve($result->reason); - } - - if ($result instanceof ExtendedPromiseInterface) { - $result->done(); - } - } - - public function otherwise(callable $onRejected) - { - if (!_checkTypehint($onRejected, $this->reason)) { - return $this; - } - - return $this->then(null, $onRejected); - } - - public function always(callable $onFulfilledOrRejected) - { - return $this->then(null, function ($reason) use ($onFulfilledOrRejected) { - return resolve($onFulfilledOrRejected())->then(function () use ($reason) { - return new RejectedPromise($reason); - }); - }); - } - - public function progress(callable $onProgress) - { - return $this; - } - - public function cancel() - { - } -} diff --git a/core/src/core/classes/guzzle/vendor/react/promise/src/UnhandledRejectionException.php b/core/src/core/classes/guzzle/vendor/react/promise/src/UnhandledRejectionException.php deleted file mode 100644 index a44b7a1bff..0000000000 --- a/core/src/core/classes/guzzle/vendor/react/promise/src/UnhandledRejectionException.php +++ /dev/null @@ -1,31 +0,0 @@ -reason = $reason; - - $message = sprintf('Unhandled Rejection: %s', json_encode($reason)); - - parent::__construct($message, 0); - } - - public function getReason() - { - return $this->reason; - } -} diff --git a/core/src/core/classes/guzzle/vendor/react/promise/src/functions.php b/core/src/core/classes/guzzle/vendor/react/promise/src/functions.php deleted file mode 100644 index 5f55f2eaf3..0000000000 --- a/core/src/core/classes/guzzle/vendor/react/promise/src/functions.php +++ /dev/null @@ -1,255 +0,0 @@ -then($resolve, $reject, $notify); - }, $canceller); - } - - return new FulfilledPromise($promiseOrValue); -} - -function reject($promiseOrValue = null) -{ - if ($promiseOrValue instanceof PromiseInterface) { - return resolve($promiseOrValue)->then(function ($value) { - return new RejectedPromise($value); - }); - } - - return new RejectedPromise($promiseOrValue); -} - -function all($promisesOrValues) -{ - return map($promisesOrValues, function ($val) { - return $val; - }); -} - -function race($promisesOrValues) -{ - $cancellationQueue = new CancellationQueue(); - $cancellationQueue->enqueue($promisesOrValues); - - return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $cancellationQueue) { - resolve($promisesOrValues) - ->done(function ($array) use ($cancellationQueue, $resolve, $reject, $notify) { - if (!is_array($array) || !$array) { - $resolve(); - return; - } - - $fulfiller = function ($value) use ($cancellationQueue, $resolve) { - $cancellationQueue(); - $resolve($value); - }; - - $rejecter = function ($reason) use ($cancellationQueue, $reject) { - $cancellationQueue(); - $reject($reason); - }; - - foreach ($array as $promiseOrValue) { - $cancellationQueue->enqueue($promiseOrValue); - - resolve($promiseOrValue) - ->done($fulfiller, $rejecter, $notify); - } - }, $reject, $notify); - }, $cancellationQueue); -} - -function any($promisesOrValues) -{ - return some($promisesOrValues, 1) - ->then(function ($val) { - return array_shift($val); - }); -} - -function some($promisesOrValues, $howMany) -{ - $cancellationQueue = new CancellationQueue(); - $cancellationQueue->enqueue($promisesOrValues); - - return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $howMany, $cancellationQueue) { - resolve($promisesOrValues) - ->done(function ($array) use ($howMany, $cancellationQueue, $resolve, $reject, $notify) { - if (!is_array($array) || $howMany < 1) { - $resolve([]); - return; - } - - $len = count($array); - - if ($len < $howMany) { - throw new Exception\LengthException( - sprintf( - 'Input array must contain at least %d item%s but contains only %s item%s.', - $howMany, - 1 === $howMany ? '' : 's', - $len, - 1 === $len ? '' : 's' - ) - ); - } - - $toResolve = $howMany; - $toReject = ($len - $toResolve) + 1; - $values = []; - $reasons = []; - - foreach ($array as $i => $promiseOrValue) { - $fulfiller = function ($val) use ($i, &$values, &$toResolve, $toReject, $resolve, $cancellationQueue) { - if ($toResolve < 1 || $toReject < 1) { - return; - } - - $values[$i] = $val; - - if (0 === --$toResolve) { - $cancellationQueue(); - $resolve($values); - } - }; - - $rejecter = function ($reason) use ($i, &$reasons, &$toReject, $toResolve, $reject, $cancellationQueue) { - if ($toResolve < 1 || $toReject < 1) { - return; - } - - $reasons[$i] = $reason; - - if (0 === --$toReject) { - $cancellationQueue(); - $reject($reasons); - } - }; - - $cancellationQueue->enqueue($promiseOrValue); - - resolve($promiseOrValue) - ->done($fulfiller, $rejecter, $notify); - } - }, $reject, $notify); - }, $cancellationQueue); -} - -function map($promisesOrValues, callable $mapFunc) -{ - $cancellationQueue = new CancellationQueue(); - $cancellationQueue->enqueue($promisesOrValues); - - return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $mapFunc, $cancellationQueue) { - resolve($promisesOrValues) - ->done(function ($array) use ($mapFunc, $cancellationQueue, $resolve, $reject, $notify) { - if (!is_array($array) || !$array) { - $resolve([]); - return; - } - - $toResolve = count($array); - $values = []; - - foreach ($array as $i => $promiseOrValue) { - $cancellationQueue->enqueue($promiseOrValue); - - resolve($promiseOrValue) - ->then($mapFunc) - ->done( - function ($mapped) use ($i, &$values, &$toResolve, $resolve) { - $values[$i] = $mapped; - - if (0 === --$toResolve) { - $resolve($values); - } - }, - $reject, - $notify - ); - } - }, $reject, $notify); - }, $cancellationQueue); -} - -function reduce($promisesOrValues, callable $reduceFunc, $initialValue = null) -{ - $cancellationQueue = new CancellationQueue(); - $cancellationQueue->enqueue($promisesOrValues); - - return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $reduceFunc, $initialValue, $cancellationQueue) { - resolve($promisesOrValues) - ->done(function ($array) use ($reduceFunc, $initialValue, $cancellationQueue, $resolve, $reject, $notify) { - if (!is_array($array)) { - $array = []; - } - - $total = count($array); - $i = 0; - - // Wrap the supplied $reduceFunc with one that handles promises and then - // delegates to the supplied. - $wrappedReduceFunc = function ($current, $val) use ($reduceFunc, $cancellationQueue, $total, &$i) { - $cancellationQueue->enqueue($val); - - return $current - ->then(function ($c) use ($reduceFunc, $total, &$i, $val) { - return resolve($val) - ->then(function ($value) use ($reduceFunc, $total, &$i, $c) { - return $reduceFunc($c, $value, $i++, $total); - }); - }); - }; - - $cancellationQueue->enqueue($initialValue); - - array_reduce($array, $wrappedReduceFunc, resolve($initialValue)) - ->done($resolve, $reject, $notify); - }, $reject, $notify); - }, $cancellationQueue); -} - -// Internal functions -function _checkTypehint(callable $callback, $object) -{ - if (!is_object($object)) { - return true; - } - - if (is_array($callback)) { - $callbackReflection = new \ReflectionMethod($callback[0], $callback[1]); - } elseif (is_object($callback) && !$callback instanceof \Closure) { - $callbackReflection = new \ReflectionMethod($callback, '__invoke'); - } else { - $callbackReflection = new \ReflectionFunction($callback); - } - - $parameters = $callbackReflection->getParameters(); - - if (!isset($parameters[0])) { - return true; - } - - $expectedException = $parameters[0]; - - if (!$expectedException->getClass()) { - return true; - } - - return $expectedException->getClass()->isInstance($object); -} diff --git a/core/src/core/classes/guzzle/vendor/react/promise/src/functions_include.php b/core/src/core/classes/guzzle/vendor/react/promise/src/functions_include.php deleted file mode 100644 index c71decbf98..0000000000 --- a/core/src/core/classes/guzzle/vendor/react/promise/src/functions_include.php +++ /dev/null @@ -1,5 +0,0 @@ -children() - ->enumNode('variable') - ->values(array('value')) - ->end() - ->end() -; -``` - -Before: `InvalidArgumentException` (variable must contain at least two -distinct elements). -After: the code will work as expected and it will restrict the values of the -`variable` option to just `value`. - - * deprecated the `ResourceInterface::isFresh()` method. If you implement custom resource types and they - can be validated that way, make them implement the new `SelfCheckingResourceInterface`. - * deprecated the getResource() method in ResourceInterface. You can still call this method - on concrete classes implementing the interface, but it does not make sense at the interface - level as you need to know about the particular type of resource at hand to understand the - semantics of the returned value. - -2.7.0 ------ - - * added `ConfigCacheInterface`, `ConfigCacheFactoryInterface` and a basic `ConfigCacheFactory` - implementation to delegate creation of ConfigCache instances - -2.2.0 ------ - - * added `ArrayNodeDefinition::canBeEnabled()` and `ArrayNodeDefinition::canBeDisabled()` - to ease configuration when some sections are respectively disabled / enabled - by default. - * added a `normalizeKeys()` method for array nodes (to avoid key normalization) - * added numerical type handling for config definitions - * added convenience methods for optional configuration sections to `ArrayNodeDefinition` - * added a utils class for XML manipulations - -2.1.0 ------ - - * added a way to add documentation on configuration - * implemented `Serializable` on resources - * `LoaderResolverInterface` is now used instead of `LoaderResolver` for type - hinting diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/ConfigCache.php b/core/src/core/classes/guzzle/vendor/symfony/config/ConfigCache.php deleted file mode 100644 index 8318684186..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/ConfigCache.php +++ /dev/null @@ -1,58 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config; - -use Symfony\Component\Config\Resource\SelfCheckingResourceChecker; - -/** - * ConfigCache caches arbitrary content in files on disk. - * - * When in debug mode, those metadata resources that implement - * \Symfony\Component\Config\Resource\SelfCheckingResourceInterface will - * be used to check cache freshness. - * - * @author Fabien Potencier - * @author Matthias Pigulla - */ -class ConfigCache extends ResourceCheckerConfigCache -{ - private $debug; - - /** - * @param string $file The absolute cache path - * @param bool $debug Whether debugging is enabled or not - */ - public function __construct($file, $debug) - { - parent::__construct($file, array( - new SelfCheckingResourceChecker(), - )); - $this->debug = (bool) $debug; - } - - /** - * Checks if the cache is still fresh. - * - * This implementation always returns true when debug is off and the - * cache file exists. - * - * @return bool true if the cache is fresh, false otherwise - */ - public function isFresh() - { - if (!$this->debug && is_file($this->getPath())) { - return true; - } - - return parent::isFresh(); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/ConfigCacheFactory.php b/core/src/core/classes/guzzle/vendor/symfony/config/ConfigCacheFactory.php deleted file mode 100644 index 396536e2d8..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/ConfigCacheFactory.php +++ /dev/null @@ -1,54 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config; - -/** - * Basic implementation of ConfigCacheFactoryInterface that - * creates an instance of the default ConfigCache. - * - * This factory and/or cache do not support cache validation - * by means of ResourceChecker instances (that is, service-based). - * - * @author Matthias Pigulla - */ -class ConfigCacheFactory implements ConfigCacheFactoryInterface -{ - /** - * @var bool Debug flag passed to the ConfigCache - */ - private $debug; - - /** - * @param bool $debug The debug flag to pass to ConfigCache - */ - public function __construct($debug) - { - $this->debug = $debug; - } - - /** - * {@inheritdoc} - */ - public function cache($file, $callback) - { - if (!is_callable($callback)) { - throw new \InvalidArgumentException(sprintf('Invalid type for callback argument. Expected callable, but got "%s".', gettype($callback))); - } - - $cache = new ConfigCache($file, $this->debug); - if (!$cache->isFresh()) { - call_user_func($callback, $cache); - } - - return $cache; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/ConfigCacheFactoryInterface.php b/core/src/core/classes/guzzle/vendor/symfony/config/ConfigCacheFactoryInterface.php deleted file mode 100644 index bd614c4b6b..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/ConfigCacheFactoryInterface.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config; - -/** - * Interface for a ConfigCache factory. This factory creates - * an instance of ConfigCacheInterface and initializes the - * cache if necessary. - * - * @author Matthias Pigulla - */ -interface ConfigCacheFactoryInterface -{ - /** - * Creates a cache instance and (re-)initializes it if necessary. - * - * @param string $file The absolute cache file path - * @param callable $callable The callable to be executed when the cache needs to be filled (i. e. is not fresh). The cache will be passed as the only parameter to this callback - * - * @return ConfigCacheInterface $configCache The cache instance - */ - public function cache($file, $callable); -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/ConfigCacheInterface.php b/core/src/core/classes/guzzle/vendor/symfony/config/ConfigCacheInterface.php deleted file mode 100644 index 7c47ad70a5..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/ConfigCacheInterface.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config; - -use Symfony\Component\Config\Resource\ResourceInterface; - -/** - * Interface for ConfigCache. - * - * @author Matthias Pigulla - */ -interface ConfigCacheInterface -{ - /** - * Gets the cache file path. - * - * @return string The cache file path - */ - public function getPath(); - - /** - * Checks if the cache is still fresh. - * - * This check should take the metadata passed to the write() method into consideration. - * - * @return bool Whether the cache is still fresh - */ - public function isFresh(); - - /** - * Writes the given content into the cache file. Metadata will be stored - * independently and can be used to check cache freshness at a later time. - * - * @param string $content The content to write into the cache - * @param ResourceInterface[]|null $metadata An array of ResourceInterface instances - * - * @throws \RuntimeException When the cache file cannot be written - */ - public function write($content, array $metadata = null); -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/ArrayNode.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/ArrayNode.php deleted file mode 100644 index fe5599da8a..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/ArrayNode.php +++ /dev/null @@ -1,400 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition; - -use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; -use Symfony\Component\Config\Definition\Exception\InvalidTypeException; -use Symfony\Component\Config\Definition\Exception\UnsetKeyException; - -/** - * Represents an Array node in the config tree. - * - * @author Johannes M. Schmitt - */ -class ArrayNode extends BaseNode implements PrototypeNodeInterface -{ - protected $xmlRemappings = array(); - protected $children = array(); - protected $allowFalse = false; - protected $allowNewKeys = true; - protected $addIfNotSet = false; - protected $performDeepMerging = true; - protected $ignoreExtraKeys = false; - protected $removeExtraKeys = true; - protected $normalizeKeys = true; - - public function setNormalizeKeys($normalizeKeys) - { - $this->normalizeKeys = (bool) $normalizeKeys; - } - - /** - * Normalizes keys between the different configuration formats. - * - * Namely, you mostly have foo_bar in YAML while you have foo-bar in XML. - * After running this method, all keys are normalized to foo_bar. - * - * If you have a mixed key like foo-bar_moo, it will not be altered. - * The key will also not be altered if the target key already exists. - * - * @param mixed $value - * - * @return array The value with normalized keys - */ - protected function preNormalize($value) - { - if (!$this->normalizeKeys || !is_array($value)) { - return $value; - } - - $normalized = array(); - - foreach ($value as $k => $v) { - if (false !== strpos($k, '-') && false === strpos($k, '_') && !array_key_exists($normalizedKey = str_replace('-', '_', $k), $value)) { - $normalized[$normalizedKey] = $v; - } else { - $normalized[$k] = $v; - } - } - - return $normalized; - } - - /** - * Retrieves the children of this node. - * - * @return array The children - */ - public function getChildren() - { - return $this->children; - } - - /** - * Sets the xml remappings that should be performed. - * - * @param array $remappings an array of the form array(array(string, string)) - */ - public function setXmlRemappings(array $remappings) - { - $this->xmlRemappings = $remappings; - } - - /** - * Gets the xml remappings that should be performed. - * - * @return array $remappings an array of the form array(array(string, string)) - */ - public function getXmlRemappings() - { - return $this->xmlRemappings; - } - - /** - * Sets whether to add default values for this array if it has not been - * defined in any of the configuration files. - * - * @param bool $boolean - */ - public function setAddIfNotSet($boolean) - { - $this->addIfNotSet = (bool) $boolean; - } - - /** - * Sets whether false is allowed as value indicating that the array should be unset. - * - * @param bool $allow - */ - public function setAllowFalse($allow) - { - $this->allowFalse = (bool) $allow; - } - - /** - * Sets whether new keys can be defined in subsequent configurations. - * - * @param bool $allow - */ - public function setAllowNewKeys($allow) - { - $this->allowNewKeys = (bool) $allow; - } - - /** - * Sets if deep merging should occur. - * - * @param bool $boolean - */ - public function setPerformDeepMerging($boolean) - { - $this->performDeepMerging = (bool) $boolean; - } - - /** - * Whether extra keys should just be ignore without an exception. - * - * @param bool $boolean To allow extra keys - * @param bool $remove To remove extra keys - */ - public function setIgnoreExtraKeys($boolean, $remove = true) - { - $this->ignoreExtraKeys = (bool) $boolean; - $this->removeExtraKeys = $this->ignoreExtraKeys && $remove; - } - - /** - * Sets the node Name. - * - * @param string $name The node's name - */ - public function setName($name) - { - $this->name = $name; - } - - /** - * Checks if the node has a default value. - * - * @return bool - */ - public function hasDefaultValue() - { - return $this->addIfNotSet; - } - - /** - * Retrieves the default value. - * - * @return array The default value - * - * @throws \RuntimeException if the node has no default value - */ - public function getDefaultValue() - { - if (!$this->hasDefaultValue()) { - throw new \RuntimeException(sprintf('The node at path "%s" has no default value.', $this->getPath())); - } - - $defaults = array(); - foreach ($this->children as $name => $child) { - if ($child->hasDefaultValue()) { - $defaults[$name] = $child->getDefaultValue(); - } - } - - return $defaults; - } - - /** - * Adds a child node. - * - * @param NodeInterface $node The child node to add - * - * @throws \InvalidArgumentException when the child node has no name - * @throws \InvalidArgumentException when the child node's name is not unique - */ - public function addChild(NodeInterface $node) - { - $name = $node->getName(); - if (!strlen($name)) { - throw new \InvalidArgumentException('Child nodes must be named.'); - } - if (isset($this->children[$name])) { - throw new \InvalidArgumentException(sprintf('A child node named "%s" already exists.', $name)); - } - - $this->children[$name] = $node; - } - - /** - * Finalizes the value of this node. - * - * @param mixed $value - * - * @return mixed The finalised value - * - * @throws UnsetKeyException - * @throws InvalidConfigurationException if the node doesn't have enough children - */ - protected function finalizeValue($value) - { - if (false === $value) { - $msg = sprintf('Unsetting key for path "%s", value: %s', $this->getPath(), json_encode($value)); - throw new UnsetKeyException($msg); - } - - foreach ($this->children as $name => $child) { - if (!array_key_exists($name, $value)) { - if ($child->isRequired()) { - $msg = sprintf('The child node "%s" at path "%s" must be configured.', $name, $this->getPath()); - $ex = new InvalidConfigurationException($msg); - $ex->setPath($this->getPath()); - - throw $ex; - } - - if ($child->hasDefaultValue()) { - $value[$name] = $child->getDefaultValue(); - } - - continue; - } - - try { - $value[$name] = $child->finalize($value[$name]); - } catch (UnsetKeyException $e) { - unset($value[$name]); - } - } - - return $value; - } - - /** - * Validates the type of the value. - * - * @param mixed $value - * - * @throws InvalidTypeException - */ - protected function validateType($value) - { - if (!is_array($value) && (!$this->allowFalse || false !== $value)) { - $ex = new InvalidTypeException(sprintf( - 'Invalid type for path "%s". Expected array, but got %s', - $this->getPath(), - gettype($value) - )); - if ($hint = $this->getInfo()) { - $ex->addHint($hint); - } - $ex->setPath($this->getPath()); - - throw $ex; - } - } - - /** - * Normalizes the value. - * - * @param mixed $value The value to normalize - * - * @return mixed The normalized value - * - * @throws InvalidConfigurationException - */ - protected function normalizeValue($value) - { - if (false === $value) { - return $value; - } - - $value = $this->remapXml($value); - - $normalized = array(); - foreach ($value as $name => $val) { - if (isset($this->children[$name])) { - $normalized[$name] = $this->children[$name]->normalize($val); - unset($value[$name]); - } elseif (!$this->removeExtraKeys) { - $normalized[$name] = $val; - } - } - - // if extra fields are present, throw exception - if (count($value) && !$this->ignoreExtraKeys) { - $msg = sprintf('Unrecognized option%s "%s" under "%s"', 1 === count($value) ? '' : 's', implode(', ', array_keys($value)), $this->getPath()); - $ex = new InvalidConfigurationException($msg); - $ex->setPath($this->getPath()); - - throw $ex; - } - - return $normalized; - } - - /** - * Remaps multiple singular values to a single plural value. - * - * @param array $value The source values - * - * @return array The remapped values - */ - protected function remapXml($value) - { - foreach ($this->xmlRemappings as $transition) { - list($singular, $plural) = $transition; - if (!isset($value[$singular])) { - continue; - } - - $value[$plural] = Processor::normalizeConfig($value, $singular, $plural); - unset($value[$singular]); - } - - return $value; - } - - /** - * Merges values together. - * - * @param mixed $leftSide The left side to merge - * @param mixed $rightSide The right side to merge - * - * @return mixed The merged values - * - * @throws InvalidConfigurationException - * @throws \RuntimeException - */ - protected function mergeValues($leftSide, $rightSide) - { - if (false === $rightSide) { - // if this is still false after the last config has been merged the - // finalization pass will take care of removing this key entirely - return false; - } - - if (false === $leftSide || !$this->performDeepMerging) { - return $rightSide; - } - - foreach ($rightSide as $k => $v) { - // no conflict - if (!array_key_exists($k, $leftSide)) { - if (!$this->allowNewKeys) { - $ex = new InvalidConfigurationException(sprintf( - 'You are not allowed to define new elements for path "%s". ' - .'Please define all elements for this path in one config file. ' - .'If you are trying to overwrite an element, make sure you redefine it ' - .'with the same name.', - $this->getPath() - )); - $ex->setPath($this->getPath()); - - throw $ex; - } - - $leftSide[$k] = $v; - continue; - } - - if (!isset($this->children[$k])) { - throw new \RuntimeException('merge() expects a normalized config array.'); - } - - $leftSide[$k] = $this->children[$k]->merge($leftSide[$k], $v); - } - - return $leftSide; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/BaseNode.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/BaseNode.php deleted file mode 100644 index dbf36335b6..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/BaseNode.php +++ /dev/null @@ -1,356 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition; - -use Symfony\Component\Config\Definition\Exception\Exception; -use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException; -use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; -use Symfony\Component\Config\Definition\Exception\InvalidTypeException; - -/** - * The base node class. - * - * @author Johannes M. Schmitt - */ -abstract class BaseNode implements NodeInterface -{ - protected $name; - protected $parent; - protected $normalizationClosures = array(); - protected $finalValidationClosures = array(); - protected $allowOverwrite = true; - protected $required = false; - protected $equivalentValues = array(); - protected $attributes = array(); - - /** - * Constructor. - * - * @param string $name The name of the node - * @param NodeInterface $parent The parent of this node - * - * @throws \InvalidArgumentException if the name contains a period. - */ - public function __construct($name, NodeInterface $parent = null) - { - if (false !== strpos($name, '.')) { - throw new \InvalidArgumentException('The name must not contain ".".'); - } - - $this->name = $name; - $this->parent = $parent; - } - - public function setAttribute($key, $value) - { - $this->attributes[$key] = $value; - } - - public function getAttribute($key, $default = null) - { - return isset($this->attributes[$key]) ? $this->attributes[$key] : $default; - } - - public function hasAttribute($key) - { - return isset($this->attributes[$key]); - } - - public function getAttributes() - { - return $this->attributes; - } - - public function setAttributes(array $attributes) - { - $this->attributes = $attributes; - } - - public function removeAttribute($key) - { - unset($this->attributes[$key]); - } - - /** - * Sets an info message. - * - * @param string $info - */ - public function setInfo($info) - { - $this->setAttribute('info', $info); - } - - /** - * Returns info message. - * - * @return string The info text - */ - public function getInfo() - { - return $this->getAttribute('info'); - } - - /** - * Sets the example configuration for this node. - * - * @param string|array $example - */ - public function setExample($example) - { - $this->setAttribute('example', $example); - } - - /** - * Retrieves the example configuration for this node. - * - * @return string|array The example - */ - public function getExample() - { - return $this->getAttribute('example'); - } - - /** - * Adds an equivalent value. - * - * @param mixed $originalValue - * @param mixed $equivalentValue - */ - public function addEquivalentValue($originalValue, $equivalentValue) - { - $this->equivalentValues[] = array($originalValue, $equivalentValue); - } - - /** - * Set this node as required. - * - * @param bool $boolean Required node - */ - public function setRequired($boolean) - { - $this->required = (bool) $boolean; - } - - /** - * Sets if this node can be overridden. - * - * @param bool $allow - */ - public function setAllowOverwrite($allow) - { - $this->allowOverwrite = (bool) $allow; - } - - /** - * Sets the closures used for normalization. - * - * @param \Closure[] $closures An array of Closures used for normalization - */ - public function setNormalizationClosures(array $closures) - { - $this->normalizationClosures = $closures; - } - - /** - * Sets the closures used for final validation. - * - * @param \Closure[] $closures An array of Closures used for final validation - */ - public function setFinalValidationClosures(array $closures) - { - $this->finalValidationClosures = $closures; - } - - /** - * Checks if this node is required. - * - * @return bool - */ - public function isRequired() - { - return $this->required; - } - - /** - * Returns the name of this node. - * - * @return string The Node's name - */ - public function getName() - { - return $this->name; - } - - /** - * Retrieves the path of this node. - * - * @return string The Node's path - */ - public function getPath() - { - $path = $this->name; - - if (null !== $this->parent) { - $path = $this->parent->getPath().'.'.$path; - } - - return $path; - } - - /** - * Merges two values together. - * - * @param mixed $leftSide - * @param mixed $rightSide - * - * @return mixed The merged value - * - * @throws ForbiddenOverwriteException - */ - final public function merge($leftSide, $rightSide) - { - if (!$this->allowOverwrite) { - throw new ForbiddenOverwriteException(sprintf( - 'Configuration path "%s" cannot be overwritten. You have to ' - .'define all options for this path, and any of its sub-paths in ' - .'one configuration section.', - $this->getPath() - )); - } - - $this->validateType($leftSide); - $this->validateType($rightSide); - - return $this->mergeValues($leftSide, $rightSide); - } - - /** - * Normalizes a value, applying all normalization closures. - * - * @param mixed $value Value to normalize - * - * @return mixed The normalized value - */ - final public function normalize($value) - { - $value = $this->preNormalize($value); - - // run custom normalization closures - foreach ($this->normalizationClosures as $closure) { - $value = $closure($value); - } - - // replace value with their equivalent - foreach ($this->equivalentValues as $data) { - if ($data[0] === $value) { - $value = $data[1]; - } - } - - // validate type - $this->validateType($value); - - // normalize value - return $this->normalizeValue($value); - } - - /** - * Normalizes the value before any other normalization is applied. - * - * @param $value - * - * @return $value The normalized array value - */ - protected function preNormalize($value) - { - return $value; - } - - /** - * Returns parent node for this node. - * - * @return NodeInterface|null - */ - public function getParent() - { - return $this->parent; - } - - /** - * Finalizes a value, applying all finalization closures. - * - * @param mixed $value The value to finalize - * - * @return mixed The finalized value - * - * @throws Exception - * @throws InvalidConfigurationException - */ - final public function finalize($value) - { - $this->validateType($value); - - $value = $this->finalizeValue($value); - - // Perform validation on the final value if a closure has been set. - // The closure is also allowed to return another value. - foreach ($this->finalValidationClosures as $closure) { - try { - $value = $closure($value); - } catch (Exception $e) { - throw $e; - } catch (\Exception $e) { - throw new InvalidConfigurationException(sprintf('Invalid configuration for path "%s": %s', $this->getPath(), $e->getMessage()), $e->getCode(), $e); - } - } - - return $value; - } - - /** - * Validates the type of a Node. - * - * @param mixed $value The value to validate - * - * @throws InvalidTypeException when the value is invalid - */ - abstract protected function validateType($value); - - /** - * Normalizes the value. - * - * @param mixed $value The value to normalize - * - * @return mixed The normalized value - */ - abstract protected function normalizeValue($value); - - /** - * Merges two values together. - * - * @param mixed $leftSide - * @param mixed $rightSide - * - * @return mixed The merged value - */ - abstract protected function mergeValues($leftSide, $rightSide); - - /** - * Finalizes a value. - * - * @param mixed $value The value to finalize - * - * @return mixed The finalized value - */ - abstract protected function finalizeValue($value); -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/BooleanNode.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/BooleanNode.php deleted file mode 100644 index 08e1a77307..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/BooleanNode.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition; - -use Symfony\Component\Config\Definition\Exception\InvalidTypeException; - -/** - * This node represents a Boolean value in the config tree. - * - * @author Johannes M. Schmitt - */ -class BooleanNode extends ScalarNode -{ - /** - * {@inheritdoc} - */ - protected function validateType($value) - { - if (!is_bool($value)) { - $ex = new InvalidTypeException(sprintf( - 'Invalid type for path "%s". Expected boolean, but got %s.', - $this->getPath(), - gettype($value) - )); - if ($hint = $this->getInfo()) { - $ex->addHint($hint); - } - $ex->setPath($this->getPath()); - - throw $ex; - } - } - - /** - * {@inheritdoc} - */ - protected function isValueEmpty($value) - { - // a boolean value cannot be empty - return false; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php deleted file mode 100644 index dc1c2fd8db..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php +++ /dev/null @@ -1,493 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Builder; - -use Symfony\Component\Config\Definition\ArrayNode; -use Symfony\Component\Config\Definition\PrototypedArrayNode; -use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; - -/** - * This class provides a fluent interface for defining an array node. - * - * @author Johannes M. Schmitt - */ -class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinitionInterface -{ - protected $performDeepMerging = true; - protected $ignoreExtraKeys = false; - protected $removeExtraKeys = true; - protected $children = array(); - protected $prototype; - protected $atLeastOne = false; - protected $allowNewKeys = true; - protected $key; - protected $removeKeyItem; - protected $addDefaults = false; - protected $addDefaultChildren = false; - protected $nodeBuilder; - protected $normalizeKeys = true; - - /** - * {@inheritdoc} - */ - public function __construct($name, NodeParentInterface $parent = null) - { - parent::__construct($name, $parent); - - $this->nullEquivalent = array(); - $this->trueEquivalent = array(); - } - - /** - * Sets a custom children builder. - * - * @param NodeBuilder $builder A custom NodeBuilder - */ - public function setBuilder(NodeBuilder $builder) - { - $this->nodeBuilder = $builder; - } - - /** - * Returns a builder to add children nodes. - * - * @return NodeBuilder - */ - public function children() - { - return $this->getNodeBuilder(); - } - - /** - * Sets a prototype for child nodes. - * - * @param string $type the type of node - * - * @return NodeDefinition - */ - public function prototype($type) - { - return $this->prototype = $this->getNodeBuilder()->node(null, $type)->setParent($this); - } - - /** - * Adds the default value if the node is not set in the configuration. - * - * This method is applicable to concrete nodes only (not to prototype nodes). - * If this function has been called and the node is not set during the finalization - * phase, it's default value will be derived from its children default values. - * - * @return ArrayNodeDefinition - */ - public function addDefaultsIfNotSet() - { - $this->addDefaults = true; - - return $this; - } - - /** - * Adds children with a default value when none are defined. - * - * @param int|string|array|null $children The number of children|The child name|The children names to be added - * - * This method is applicable to prototype nodes only. - * - * @return ArrayNodeDefinition - */ - public function addDefaultChildrenIfNoneSet($children = null) - { - $this->addDefaultChildren = $children; - - return $this; - } - - /** - * Requires the node to have at least one element. - * - * This method is applicable to prototype nodes only. - * - * @return ArrayNodeDefinition - */ - public function requiresAtLeastOneElement() - { - $this->atLeastOne = true; - - return $this; - } - - /** - * Disallows adding news keys in a subsequent configuration. - * - * If used all keys have to be defined in the same configuration file. - * - * @return ArrayNodeDefinition - */ - public function disallowNewKeysInSubsequentConfigs() - { - $this->allowNewKeys = false; - - return $this; - } - - /** - * Sets a normalization rule for XML configurations. - * - * @param string $singular The key to remap - * @param string $plural The plural of the key for irregular plurals - * - * @return ArrayNodeDefinition - */ - public function fixXmlConfig($singular, $plural = null) - { - $this->normalization()->remap($singular, $plural); - - return $this; - } - - /** - * Sets the attribute which value is to be used as key. - * - * This is useful when you have an indexed array that should be an - * associative array. You can select an item from within the array - * to be the key of the particular item. For example, if "id" is the - * "key", then: - * - * array( - * array('id' => 'my_name', 'foo' => 'bar'), - * ); - * - * becomes - * - * array( - * 'my_name' => array('foo' => 'bar'), - * ); - * - * If you'd like "'id' => 'my_name'" to still be present in the resulting - * array, then you can set the second argument of this method to false. - * - * This method is applicable to prototype nodes only. - * - * @param string $name The name of the key - * @param bool $removeKeyItem Whether or not the key item should be removed - * - * @return ArrayNodeDefinition - */ - public function useAttributeAsKey($name, $removeKeyItem = true) - { - $this->key = $name; - $this->removeKeyItem = $removeKeyItem; - - return $this; - } - - /** - * Sets whether the node can be unset. - * - * @param bool $allow - * - * @return ArrayNodeDefinition - */ - public function canBeUnset($allow = true) - { - $this->merge()->allowUnset($allow); - - return $this; - } - - /** - * Adds an "enabled" boolean to enable the current section. - * - * By default, the section is disabled. If any configuration is specified then - * the node will be automatically enabled: - * - * enableableArrayNode: {enabled: true, ...} # The config is enabled & default values get overridden - * enableableArrayNode: ~ # The config is enabled & use the default values - * enableableArrayNode: true # The config is enabled & use the default values - * enableableArrayNode: {other: value, ...} # The config is enabled & default values get overridden - * enableableArrayNode: {enabled: false, ...} # The config is disabled - * enableableArrayNode: false # The config is disabled - * - * @return ArrayNodeDefinition - */ - public function canBeEnabled() - { - $this - ->addDefaultsIfNotSet() - ->treatFalseLike(array('enabled' => false)) - ->treatTrueLike(array('enabled' => true)) - ->treatNullLike(array('enabled' => true)) - ->beforeNormalization() - ->ifArray() - ->then(function ($v) { - $v['enabled'] = isset($v['enabled']) ? $v['enabled'] : true; - - return $v; - }) - ->end() - ->children() - ->booleanNode('enabled') - ->defaultFalse() - ; - - return $this; - } - - /** - * Adds an "enabled" boolean to enable the current section. - * - * By default, the section is enabled. - * - * @return ArrayNodeDefinition - */ - public function canBeDisabled() - { - $this - ->addDefaultsIfNotSet() - ->treatFalseLike(array('enabled' => false)) - ->treatTrueLike(array('enabled' => true)) - ->treatNullLike(array('enabled' => true)) - ->children() - ->booleanNode('enabled') - ->defaultTrue() - ; - - return $this; - } - - /** - * Disables the deep merging of the node. - * - * @return ArrayNodeDefinition - */ - public function performNoDeepMerging() - { - $this->performDeepMerging = false; - - return $this; - } - - /** - * Allows extra config keys to be specified under an array without - * throwing an exception. - * - * Those config values are simply ignored and removed from the - * resulting array. This should be used only in special cases where - * you want to send an entire configuration array through a special - * tree that processes only part of the array. - * - * @param bool $remove Whether to remove the extra keys - * - * @return ArrayNodeDefinition - */ - public function ignoreExtraKeys($remove = true) - { - $this->ignoreExtraKeys = true; - $this->removeExtraKeys = $remove; - - return $this; - } - - /** - * Sets key normalization. - * - * @param bool $bool Whether to enable key normalization - * - * @return ArrayNodeDefinition - */ - public function normalizeKeys($bool) - { - $this->normalizeKeys = (bool) $bool; - - return $this; - } - - /** - * Appends a node definition. - * - * $node = new ArrayNodeDefinition() - * ->children() - * ->scalarNode('foo')->end() - * ->scalarNode('baz')->end() - * ->end() - * ->append($this->getBarNodeDefinition()) - * ; - * - * @param NodeDefinition $node A NodeDefinition instance - * - * @return ArrayNodeDefinition This node - */ - public function append(NodeDefinition $node) - { - $this->children[$node->name] = $node->setParent($this); - - return $this; - } - - /** - * Returns a node builder to be used to add children and prototype. - * - * @return NodeBuilder The node builder - */ - protected function getNodeBuilder() - { - if (null === $this->nodeBuilder) { - $this->nodeBuilder = new NodeBuilder(); - } - - return $this->nodeBuilder->setParent($this); - } - - /** - * {@inheritdoc} - */ - protected function createNode() - { - if (null === $this->prototype) { - $node = new ArrayNode($this->name, $this->parent); - - $this->validateConcreteNode($node); - - $node->setAddIfNotSet($this->addDefaults); - - foreach ($this->children as $child) { - $child->parent = $node; - $node->addChild($child->getNode()); - } - } else { - $node = new PrototypedArrayNode($this->name, $this->parent); - - $this->validatePrototypeNode($node); - - if (null !== $this->key) { - $node->setKeyAttribute($this->key, $this->removeKeyItem); - } - - if (true === $this->atLeastOne) { - $node->setMinNumberOfElements(1); - } - - if ($this->default) { - $node->setDefaultValue($this->defaultValue); - } - - if (false !== $this->addDefaultChildren) { - $node->setAddChildrenIfNoneSet($this->addDefaultChildren); - if ($this->prototype instanceof static && null === $this->prototype->prototype) { - $this->prototype->addDefaultsIfNotSet(); - } - } - - $this->prototype->parent = $node; - $node->setPrototype($this->prototype->getNode()); - } - - $node->setAllowNewKeys($this->allowNewKeys); - $node->addEquivalentValue(null, $this->nullEquivalent); - $node->addEquivalentValue(true, $this->trueEquivalent); - $node->addEquivalentValue(false, $this->falseEquivalent); - $node->setPerformDeepMerging($this->performDeepMerging); - $node->setRequired($this->required); - $node->setIgnoreExtraKeys($this->ignoreExtraKeys, $this->removeExtraKeys); - $node->setNormalizeKeys($this->normalizeKeys); - - if (null !== $this->normalization) { - $node->setNormalizationClosures($this->normalization->before); - $node->setXmlRemappings($this->normalization->remappings); - } - - if (null !== $this->merge) { - $node->setAllowOverwrite($this->merge->allowOverwrite); - $node->setAllowFalse($this->merge->allowFalse); - } - - if (null !== $this->validation) { - $node->setFinalValidationClosures($this->validation->rules); - } - - return $node; - } - - /** - * Validate the configuration of a concrete node. - * - * @param ArrayNode $node The related node - * - * @throws InvalidDefinitionException - */ - protected function validateConcreteNode(ArrayNode $node) - { - $path = $node->getPath(); - - if (null !== $this->key) { - throw new InvalidDefinitionException( - sprintf('->useAttributeAsKey() is not applicable to concrete nodes at path "%s"', $path) - ); - } - - if (true === $this->atLeastOne) { - throw new InvalidDefinitionException( - sprintf('->requiresAtLeastOneElement() is not applicable to concrete nodes at path "%s"', $path) - ); - } - - if ($this->default) { - throw new InvalidDefinitionException( - sprintf('->defaultValue() is not applicable to concrete nodes at path "%s"', $path) - ); - } - - if (false !== $this->addDefaultChildren) { - throw new InvalidDefinitionException( - sprintf('->addDefaultChildrenIfNoneSet() is not applicable to concrete nodes at path "%s"', $path) - ); - } - } - - /** - * Validate the configuration of a prototype node. - * - * @param PrototypedArrayNode $node The related node - * - * @throws InvalidDefinitionException - */ - protected function validatePrototypeNode(PrototypedArrayNode $node) - { - $path = $node->getPath(); - - if ($this->addDefaults) { - throw new InvalidDefinitionException( - sprintf('->addDefaultsIfNotSet() is not applicable to prototype nodes at path "%s"', $path) - ); - } - - if (false !== $this->addDefaultChildren) { - if ($this->default) { - throw new InvalidDefinitionException( - sprintf('A default value and default children might not be used together at path "%s"', $path) - ); - } - - if (null !== $this->key && (null === $this->addDefaultChildren || is_int($this->addDefaultChildren) && $this->addDefaultChildren > 0)) { - throw new InvalidDefinitionException( - sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s"', $path) - ); - } - - if (null === $this->key && (is_string($this->addDefaultChildren) || is_array($this->addDefaultChildren))) { - throw new InvalidDefinitionException( - sprintf('->addDefaultChildrenIfNoneSet() might not set default children names as ->useAttributeAsKey() is not used at path "%s"', $path) - ); - } - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php deleted file mode 100644 index 28e56579ad..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php +++ /dev/null @@ -1,53 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Builder; - -use Symfony\Component\Config\Definition\BooleanNode; -use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; - -/** - * This class provides a fluent interface for defining a node. - * - * @author Johannes M. Schmitt - */ -class BooleanNodeDefinition extends ScalarNodeDefinition -{ - /** - * {@inheritdoc} - */ - public function __construct($name, NodeParentInterface $parent = null) - { - parent::__construct($name, $parent); - - $this->nullEquivalent = true; - } - - /** - * Instantiate a Node. - * - * @return BooleanNode The node - */ - protected function instantiateNode() - { - return new BooleanNode($this->name, $this->parent); - } - - /** - * {@inheritdoc} - * - * @throws InvalidDefinitionException - */ - public function cannotBeEmpty() - { - throw new InvalidDefinitionException('->cannotBeEmpty() is not applicable to BooleanNodeDefinition.'); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php deleted file mode 100644 index 5d3ff014f1..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php +++ /dev/null @@ -1,58 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Builder; - -use Symfony\Component\Config\Definition\EnumNode; - -/** - * Enum Node Definition. - * - * @author Johannes M. Schmitt - */ -class EnumNodeDefinition extends ScalarNodeDefinition -{ - private $values; - - /** - * @param array $values - * - * @return EnumNodeDefinition|$this - */ - public function values(array $values) - { - $values = array_unique($values); - - if (empty($values)) { - throw new \InvalidArgumentException('->values() must be called with at least one value.'); - } - - $this->values = $values; - - return $this; - } - - /** - * Instantiate a Node. - * - * @return EnumNode The node - * - * @throws \RuntimeException - */ - protected function instantiateNode() - { - if (null === $this->values) { - throw new \RuntimeException('You must call ->values() on enum nodes.'); - } - - return new EnumNode($this->name, $this->parent, $this->values); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/ExprBuilder.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/ExprBuilder.php deleted file mode 100644 index 3d79b29858..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/ExprBuilder.php +++ /dev/null @@ -1,238 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Builder; - -use Symfony\Component\Config\Definition\Exception\UnsetKeyException; - -/** - * This class builds an if expression. - * - * @author Johannes M. Schmitt - * @author Christophe Coevoet - */ -class ExprBuilder -{ - protected $node; - public $ifPart; - public $thenPart; - - /** - * Constructor. - * - * @param NodeDefinition $node The related node - */ - public function __construct(NodeDefinition $node) - { - $this->node = $node; - } - - /** - * Marks the expression as being always used. - * - * @param \Closure $then - * - * @return ExprBuilder - */ - public function always(\Closure $then = null) - { - $this->ifPart = function ($v) { return true; }; - - if (null !== $then) { - $this->thenPart = $then; - } - - return $this; - } - - /** - * Sets a closure to use as tests. - * - * The default one tests if the value is true. - * - * @param \Closure $closure - * - * @return ExprBuilder - */ - public function ifTrue(\Closure $closure = null) - { - if (null === $closure) { - $closure = function ($v) { return true === $v; }; - } - - $this->ifPart = $closure; - - return $this; - } - - /** - * Tests if the value is a string. - * - * @return ExprBuilder - */ - public function ifString() - { - $this->ifPart = function ($v) { return is_string($v); }; - - return $this; - } - - /** - * Tests if the value is null. - * - * @return ExprBuilder - */ - public function ifNull() - { - $this->ifPart = function ($v) { return null === $v; }; - - return $this; - } - - /** - * Tests if the value is an array. - * - * @return ExprBuilder - */ - public function ifArray() - { - $this->ifPart = function ($v) { return is_array($v); }; - - return $this; - } - - /** - * Tests if the value is in an array. - * - * @param array $array - * - * @return ExprBuilder - */ - public function ifInArray(array $array) - { - $this->ifPart = function ($v) use ($array) { return in_array($v, $array, true); }; - - return $this; - } - - /** - * Tests if the value is not in an array. - * - * @param array $array - * - * @return ExprBuilder - */ - public function ifNotInArray(array $array) - { - $this->ifPart = function ($v) use ($array) { return !in_array($v, $array, true); }; - - return $this; - } - - /** - * Sets the closure to run if the test pass. - * - * @param \Closure $closure - * - * @return ExprBuilder - */ - public function then(\Closure $closure) - { - $this->thenPart = $closure; - - return $this; - } - - /** - * Sets a closure returning an empty array. - * - * @return ExprBuilder - */ - public function thenEmptyArray() - { - $this->thenPart = function ($v) { return array(); }; - - return $this; - } - - /** - * Sets a closure marking the value as invalid at validation time. - * - * if you want to add the value of the node in your message just use a %s placeholder. - * - * @param string $message - * - * @return ExprBuilder - * - * @throws \InvalidArgumentException - */ - public function thenInvalid($message) - { - $this->thenPart = function ($v) use ($message) {throw new \InvalidArgumentException(sprintf($message, json_encode($v))); }; - - return $this; - } - - /** - * Sets a closure unsetting this key of the array at validation time. - * - * @return ExprBuilder - * - * @throws UnsetKeyException - */ - public function thenUnset() - { - $this->thenPart = function ($v) { throw new UnsetKeyException('Unsetting key'); }; - - return $this; - } - - /** - * Returns the related node. - * - * @return NodeDefinition - * - * @throws \RuntimeException - */ - public function end() - { - if (null === $this->ifPart) { - throw new \RuntimeException('You must specify an if part.'); - } - if (null === $this->thenPart) { - throw new \RuntimeException('You must specify a then part.'); - } - - return $this->node; - } - - /** - * Builds the expressions. - * - * @param ExprBuilder[] $expressions An array of ExprBuilder instances to build - * - * @return array - */ - public static function buildExpressions(array $expressions) - { - foreach ($expressions as $k => $expr) { - if ($expr instanceof self) { - $if = $expr->ifPart; - $then = $expr->thenPart; - $expressions[$k] = function ($v) use ($if, $then) { - return $if($v) ? $then($v) : $v; - }; - } - } - - return $expressions; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/FloatNodeDefinition.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/FloatNodeDefinition.php deleted file mode 100644 index c0bed462bf..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/FloatNodeDefinition.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Builder; - -use Symfony\Component\Config\Definition\FloatNode; - -/** - * This class provides a fluent interface for defining a float node. - * - * @author Jeanmonod David - */ -class FloatNodeDefinition extends NumericNodeDefinition -{ - /** - * Instantiates a Node. - * - * @return FloatNode The node - */ - protected function instantiateNode() - { - return new FloatNode($this->name, $this->parent, $this->min, $this->max); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php deleted file mode 100644 index f6c3c147f3..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Builder; - -use Symfony\Component\Config\Definition\IntegerNode; - -/** - * This class provides a fluent interface for defining an integer node. - * - * @author Jeanmonod David - */ -class IntegerNodeDefinition extends NumericNodeDefinition -{ - /** - * Instantiates a Node. - * - * @return IntegerNode The node - */ - protected function instantiateNode() - { - return new IntegerNode($this->name, $this->parent, $this->min, $this->max); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/MergeBuilder.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/MergeBuilder.php deleted file mode 100644 index f908a499ca..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/MergeBuilder.php +++ /dev/null @@ -1,72 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Builder; - -/** - * This class builds merge conditions. - * - * @author Johannes M. Schmitt - */ -class MergeBuilder -{ - protected $node; - public $allowFalse = false; - public $allowOverwrite = true; - - /** - * Constructor. - * - * @param NodeDefinition $node The related node - */ - public function __construct(NodeDefinition $node) - { - $this->node = $node; - } - - /** - * Sets whether the node can be unset. - * - * @param bool $allow - * - * @return MergeBuilder - */ - public function allowUnset($allow = true) - { - $this->allowFalse = $allow; - - return $this; - } - - /** - * Sets whether the node can be overwritten. - * - * @param bool $deny Whether the overwriting is forbidden or not - * - * @return MergeBuilder - */ - public function denyOverwrite($deny = true) - { - $this->allowOverwrite = !$deny; - - return $this; - } - - /** - * Returns the related node. - * - * @return NodeDefinition - */ - public function end() - { - return $this->node; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/NodeBuilder.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/NodeBuilder.php deleted file mode 100644 index b2b63368e2..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/NodeBuilder.php +++ /dev/null @@ -1,245 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Builder; - -/** - * This class provides a fluent interface for building a node. - * - * @author Johannes M. Schmitt - */ -class NodeBuilder implements NodeParentInterface -{ - protected $parent; - protected $nodeMapping; - - /** - * Constructor. - */ - public function __construct() - { - $this->nodeMapping = array( - 'variable' => __NAMESPACE__.'\\VariableNodeDefinition', - 'scalar' => __NAMESPACE__.'\\ScalarNodeDefinition', - 'boolean' => __NAMESPACE__.'\\BooleanNodeDefinition', - 'integer' => __NAMESPACE__.'\\IntegerNodeDefinition', - 'float' => __NAMESPACE__.'\\FloatNodeDefinition', - 'array' => __NAMESPACE__.'\\ArrayNodeDefinition', - 'enum' => __NAMESPACE__.'\\EnumNodeDefinition', - ); - } - - /** - * Set the parent node. - * - * @param ParentNodeDefinitionInterface $parent The parent node - * - * @return NodeBuilder This node builder - */ - public function setParent(ParentNodeDefinitionInterface $parent = null) - { - $this->parent = $parent; - - return $this; - } - - /** - * Creates a child array node. - * - * @param string $name The name of the node - * - * @return ArrayNodeDefinition The child node - */ - public function arrayNode($name) - { - return $this->node($name, 'array'); - } - - /** - * Creates a child scalar node. - * - * @param string $name the name of the node - * - * @return ScalarNodeDefinition The child node - */ - public function scalarNode($name) - { - return $this->node($name, 'scalar'); - } - - /** - * Creates a child Boolean node. - * - * @param string $name The name of the node - * - * @return BooleanNodeDefinition The child node - */ - public function booleanNode($name) - { - return $this->node($name, 'boolean'); - } - - /** - * Creates a child integer node. - * - * @param string $name the name of the node - * - * @return IntegerNodeDefinition The child node - */ - public function integerNode($name) - { - return $this->node($name, 'integer'); - } - - /** - * Creates a child float node. - * - * @param string $name the name of the node - * - * @return FloatNodeDefinition The child node - */ - public function floatNode($name) - { - return $this->node($name, 'float'); - } - - /** - * Creates a child EnumNode. - * - * @param string $name - * - * @return EnumNodeDefinition - */ - public function enumNode($name) - { - return $this->node($name, 'enum'); - } - - /** - * Creates a child variable node. - * - * @param string $name The name of the node - * - * @return VariableNodeDefinition The builder of the child node - */ - public function variableNode($name) - { - return $this->node($name, 'variable'); - } - - /** - * Returns the parent node. - * - * @return ParentNodeDefinitionInterface The parent node - */ - public function end() - { - return $this->parent; - } - - /** - * Creates a child node. - * - * @param string $name The name of the node - * @param string $type The type of the node - * - * @return NodeDefinition The child node - * - * @throws \RuntimeException When the node type is not registered - * @throws \RuntimeException When the node class is not found - */ - public function node($name, $type) - { - $class = $this->getNodeClass($type); - - $node = new $class($name); - - $this->append($node); - - return $node; - } - - /** - * Appends a node definition. - * - * Usage: - * - * $node = new ArrayNodeDefinition('name') - * ->children() - * ->scalarNode('foo')->end() - * ->scalarNode('baz')->end() - * ->append($this->getBarNodeDefinition()) - * ->end() - * ; - * - * @param NodeDefinition $node - * - * @return NodeBuilder This node builder - */ - public function append(NodeDefinition $node) - { - if ($node instanceof ParentNodeDefinitionInterface) { - $builder = clone $this; - $builder->setParent(null); - $node->setBuilder($builder); - } - - if (null !== $this->parent) { - $this->parent->append($node); - // Make this builder the node parent to allow for a fluid interface - $node->setParent($this); - } - - return $this; - } - - /** - * Adds or overrides a node Type. - * - * @param string $type The name of the type - * @param string $class The fully qualified name the node definition class - * - * @return NodeBuilder This node builder - */ - public function setNodeClass($type, $class) - { - $this->nodeMapping[strtolower($type)] = $class; - - return $this; - } - - /** - * Returns the class name of the node definition. - * - * @param string $type The node type - * - * @return string The node definition class name - * - * @throws \RuntimeException When the node type is not registered - * @throws \RuntimeException When the node class is not found - */ - protected function getNodeClass($type) - { - $type = strtolower($type); - - if (!isset($this->nodeMapping[$type])) { - throw new \RuntimeException(sprintf('The node type "%s" is not registered.', $type)); - } - - $class = $this->nodeMapping[$type]; - - if (!class_exists($class)) { - throw new \RuntimeException(sprintf('The node class "%s" does not exist.', $class)); - } - - return $class; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/NodeDefinition.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/NodeDefinition.php deleted file mode 100644 index f7f84bc071..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/NodeDefinition.php +++ /dev/null @@ -1,343 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Builder; - -use Symfony\Component\Config\Definition\NodeInterface; -use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; - -/** - * This class provides a fluent interface for defining a node. - * - * @author Johannes M. Schmitt - */ -abstract class NodeDefinition implements NodeParentInterface -{ - protected $name; - protected $normalization; - protected $validation; - protected $defaultValue; - protected $default = false; - protected $required = false; - protected $merge; - protected $allowEmptyValue = true; - protected $nullEquivalent; - protected $trueEquivalent = true; - protected $falseEquivalent = false; - - /** - * @var NodeParentInterface|null - */ - protected $parent; - protected $attributes = array(); - - /** - * Constructor. - * - * @param string $name The name of the node - * @param NodeParentInterface|null $parent The parent - */ - public function __construct($name, NodeParentInterface $parent = null) - { - $this->parent = $parent; - $this->name = $name; - } - - /** - * Sets the parent node. - * - * @param NodeParentInterface $parent The parent - * - * @return NodeDefinition|$this - */ - public function setParent(NodeParentInterface $parent) - { - $this->parent = $parent; - - return $this; - } - - /** - * Sets info message. - * - * @param string $info The info text - * - * @return NodeDefinition|$this - */ - public function info($info) - { - return $this->attribute('info', $info); - } - - /** - * Sets example configuration. - * - * @param string|array $example - * - * @return NodeDefinition|$this - */ - public function example($example) - { - return $this->attribute('example', $example); - } - - /** - * Sets an attribute on the node. - * - * @param string $key - * @param mixed $value - * - * @return NodeDefinition|$this - */ - public function attribute($key, $value) - { - $this->attributes[$key] = $value; - - return $this; - } - - /** - * Returns the parent node. - * - * @return NodeParentInterface|null The builder of the parent node - */ - public function end() - { - return $this->parent; - } - - /** - * Creates the node. - * - * @param bool $forceRootNode Whether to force this node as the root node - * - * @return NodeInterface - */ - public function getNode($forceRootNode = false) - { - if ($forceRootNode) { - $this->parent = null; - } - - if (null !== $this->normalization) { - $this->normalization->before = ExprBuilder::buildExpressions($this->normalization->before); - } - - if (null !== $this->validation) { - $this->validation->rules = ExprBuilder::buildExpressions($this->validation->rules); - } - - $node = $this->createNode(); - $node->setAttributes($this->attributes); - - return $node; - } - - /** - * Sets the default value. - * - * @param mixed $value The default value - * - * @return NodeDefinition|$this - */ - public function defaultValue($value) - { - $this->default = true; - $this->defaultValue = $value; - - return $this; - } - - /** - * Sets the node as required. - * - * @return NodeDefinition|$this - */ - public function isRequired() - { - $this->required = true; - - return $this; - } - - /** - * Sets the equivalent value used when the node contains null. - * - * @param mixed $value - * - * @return NodeDefinition|$this - */ - public function treatNullLike($value) - { - $this->nullEquivalent = $value; - - return $this; - } - - /** - * Sets the equivalent value used when the node contains true. - * - * @param mixed $value - * - * @return NodeDefinition|$this - */ - public function treatTrueLike($value) - { - $this->trueEquivalent = $value; - - return $this; - } - - /** - * Sets the equivalent value used when the node contains false. - * - * @param mixed $value - * - * @return NodeDefinition|$this - */ - public function treatFalseLike($value) - { - $this->falseEquivalent = $value; - - return $this; - } - - /** - * Sets null as the default value. - * - * @return NodeDefinition|$this - */ - public function defaultNull() - { - return $this->defaultValue(null); - } - - /** - * Sets true as the default value. - * - * @return NodeDefinition|$this - */ - public function defaultTrue() - { - return $this->defaultValue(true); - } - - /** - * Sets false as the default value. - * - * @return NodeDefinition|$this - */ - public function defaultFalse() - { - return $this->defaultValue(false); - } - - /** - * Sets an expression to run before the normalization. - * - * @return ExprBuilder - */ - public function beforeNormalization() - { - return $this->normalization()->before(); - } - - /** - * Denies the node value being empty. - * - * @return NodeDefinition|$this - */ - public function cannotBeEmpty() - { - $this->allowEmptyValue = false; - - return $this; - } - - /** - * Sets an expression to run for the validation. - * - * The expression receives the value of the node and must return it. It can - * modify it. - * An exception should be thrown when the node is not valid. - * - * @return ExprBuilder - */ - public function validate() - { - return $this->validation()->rule(); - } - - /** - * Sets whether the node can be overwritten. - * - * @param bool $deny Whether the overwriting is forbidden or not - * - * @return NodeDefinition|$this - */ - public function cannotBeOverwritten($deny = true) - { - $this->merge()->denyOverwrite($deny); - - return $this; - } - - /** - * Gets the builder for validation rules. - * - * @return ValidationBuilder - */ - protected function validation() - { - if (null === $this->validation) { - $this->validation = new ValidationBuilder($this); - } - - return $this->validation; - } - - /** - * Gets the builder for merging rules. - * - * @return MergeBuilder - */ - protected function merge() - { - if (null === $this->merge) { - $this->merge = new MergeBuilder($this); - } - - return $this->merge; - } - - /** - * Gets the builder for normalization rules. - * - * @return NormalizationBuilder - */ - protected function normalization() - { - if (null === $this->normalization) { - $this->normalization = new NormalizationBuilder($this); - } - - return $this->normalization; - } - - /** - * Instantiate and configure the node according to this definition. - * - * @return NodeInterface $node The node instance - * - * @throws InvalidDefinitionException When the definition is invalid - */ - abstract protected function createNode(); -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/NodeParentInterface.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/NodeParentInterface.php deleted file mode 100644 index 305e993167..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/NodeParentInterface.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Builder; - -/** - * An interface that must be implemented by all node parents. - * - * @author Victor Berchet - */ -interface NodeParentInterface -{ -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php deleted file mode 100644 index 748c9f28cb..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php +++ /dev/null @@ -1,67 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Builder; - -/** - * This class builds normalization conditions. - * - * @author Johannes M. Schmitt - */ -class NormalizationBuilder -{ - protected $node; - public $before = array(); - public $remappings = array(); - - /** - * Constructor. - * - * @param NodeDefinition $node The related node - */ - public function __construct(NodeDefinition $node) - { - $this->node = $node; - } - - /** - * Registers a key to remap to its plural form. - * - * @param string $key The key to remap - * @param string $plural The plural of the key in case of irregular plural - * - * @return NormalizationBuilder - */ - public function remap($key, $plural = null) - { - $this->remappings[] = array($key, null === $plural ? $key.'s' : $plural); - - return $this; - } - - /** - * Registers a closure to run before the normalization or an expression builder to build it if null is provided. - * - * @param \Closure $closure - * - * @return ExprBuilder|NormalizationBuilder - */ - public function before(\Closure $closure = null) - { - if (null !== $closure) { - $this->before[] = $closure; - - return $this; - } - - return $this->before[] = new ExprBuilder($this->node); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php deleted file mode 100644 index 8451e75b26..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Builder; - -use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; - -/** - * Abstract class that contains common code of integer and float node definitions. - * - * @author David Jeanmonod - */ -abstract class NumericNodeDefinition extends ScalarNodeDefinition -{ - protected $min; - protected $max; - - /** - * Ensures that the value is smaller than the given reference. - * - * @param mixed $max - * - * @return NumericNodeDefinition - * - * @throws \InvalidArgumentException when the constraint is inconsistent - */ - public function max($max) - { - if (isset($this->min) && $this->min > $max) { - throw new \InvalidArgumentException(sprintf('You cannot define a max(%s) as you already have a min(%s)', $max, $this->min)); - } - $this->max = $max; - - return $this; - } - - /** - * Ensures that the value is bigger than the given reference. - * - * @param mixed $min - * - * @return NumericNodeDefinition - * - * @throws \InvalidArgumentException when the constraint is inconsistent - */ - public function min($min) - { - if (isset($this->max) && $this->max < $min) { - throw new \InvalidArgumentException(sprintf('You cannot define a min(%s) as you already have a max(%s)', $min, $this->max)); - } - $this->min = $min; - - return $this; - } - - /** - * {@inheritdoc} - * - * @throws InvalidDefinitionException - */ - public function cannotBeEmpty() - { - throw new InvalidDefinitionException('->cannotBeEmpty() is not applicable to NumericNodeDefinition.'); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php deleted file mode 100644 index 575495bb68..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Builder; - -/** - * An interface that must be implemented by nodes which can have children. - * - * @author Victor Berchet - */ -interface ParentNodeDefinitionInterface -{ - public function children(); - - public function append(NodeDefinition $node); - - public function setBuilder(NodeBuilder $builder); -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.php deleted file mode 100644 index 6170555ccf..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Builder; - -use Symfony\Component\Config\Definition\ScalarNode; - -/** - * This class provides a fluent interface for defining a node. - * - * @author Johannes M. Schmitt - */ -class ScalarNodeDefinition extends VariableNodeDefinition -{ - /** - * Instantiate a Node. - * - * @return ScalarNode The node - */ - protected function instantiateNode() - { - return new ScalarNode($this->name, $this->parent); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/TreeBuilder.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/TreeBuilder.php deleted file mode 100644 index 5d02848a09..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/TreeBuilder.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Builder; - -use Symfony\Component\Config\Definition\NodeInterface; - -/** - * This is the entry class for building a config tree. - * - * @author Johannes M. Schmitt - */ -class TreeBuilder implements NodeParentInterface -{ - protected $tree; - protected $root; - protected $builder; - - /** - * Creates the root node. - * - * @param string $name The name of the root node - * @param string $type The type of the root node - * @param NodeBuilder $builder A custom node builder instance - * - * @return ArrayNodeDefinition|NodeDefinition The root node (as an ArrayNodeDefinition when the type is 'array') - * - * @throws \RuntimeException When the node type is not supported - */ - public function root($name, $type = 'array', NodeBuilder $builder = null) - { - $builder = $builder ?: new NodeBuilder(); - - return $this->root = $builder->node($name, $type)->setParent($this); - } - - /** - * Builds the tree. - * - * @return NodeInterface - * - * @throws \RuntimeException - */ - public function buildTree() - { - if (null === $this->root) { - throw new \RuntimeException('The configuration tree has no root node.'); - } - if (null !== $this->tree) { - return $this->tree; - } - - return $this->tree = $this->root->getNode(true); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/ValidationBuilder.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/ValidationBuilder.php deleted file mode 100644 index e885823892..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/ValidationBuilder.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Builder; - -/** - * This class builds validation conditions. - * - * @author Christophe Coevoet - */ -class ValidationBuilder -{ - protected $node; - public $rules = array(); - - /** - * Constructor. - * - * @param NodeDefinition $node The related node - */ - public function __construct(NodeDefinition $node) - { - $this->node = $node; - } - - /** - * Registers a closure to run as normalization or an expression builder to build it if null is provided. - * - * @param \Closure $closure - * - * @return ExprBuilder|ValidationBuilder - */ - public function rule(\Closure $closure = null) - { - if (null !== $closure) { - $this->rules[] = $closure; - - return $this; - } - - return $this->rules[] = new ExprBuilder($this->node); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php deleted file mode 100644 index a46b7ea61d..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php +++ /dev/null @@ -1,64 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Builder; - -use Symfony\Component\Config\Definition\VariableNode; - -/** - * This class provides a fluent interface for defining a node. - * - * @author Johannes M. Schmitt - */ -class VariableNodeDefinition extends NodeDefinition -{ - /** - * Instantiate a Node. - * - * @return VariableNode The node - */ - protected function instantiateNode() - { - return new VariableNode($this->name, $this->parent); - } - - /** - * {@inheritdoc} - */ - protected function createNode() - { - $node = $this->instantiateNode(); - - if (null !== $this->normalization) { - $node->setNormalizationClosures($this->normalization->before); - } - - if (null !== $this->merge) { - $node->setAllowOverwrite($this->merge->allowOverwrite); - } - - if (true === $this->default) { - $node->setDefaultValue($this->defaultValue); - } - - $node->setAllowEmptyValue($this->allowEmptyValue); - $node->addEquivalentValue(null, $this->nullEquivalent); - $node->addEquivalentValue(true, $this->trueEquivalent); - $node->addEquivalentValue(false, $this->falseEquivalent); - $node->setRequired($this->required); - - if (null !== $this->validation) { - $node->setFinalValidationClosures($this->validation->rules); - } - - return $node; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/ConfigurationInterface.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/ConfigurationInterface.php deleted file mode 100644 index d6456edb84..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/ConfigurationInterface.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition; - -/** - * Configuration interface. - * - * @author Victor Berchet - */ -interface ConfigurationInterface -{ - /** - * Generates the configuration tree builder. - * - * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder - */ - public function getConfigTreeBuilder(); -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php deleted file mode 100644 index 2cc71b344e..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php +++ /dev/null @@ -1,304 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Dumper; - -use Symfony\Component\Config\Definition\ConfigurationInterface; -use Symfony\Component\Config\Definition\NodeInterface; -use Symfony\Component\Config\Definition\ArrayNode; -use Symfony\Component\Config\Definition\EnumNode; -use Symfony\Component\Config\Definition\PrototypedArrayNode; - -/** - * Dumps a XML reference configuration for the given configuration/node instance. - * - * @author Wouter J - */ -class XmlReferenceDumper -{ - private $reference; - - public function dump(ConfigurationInterface $configuration, $namespace = null) - { - return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree(), $namespace); - } - - public function dumpNode(NodeInterface $node, $namespace = null) - { - $this->reference = ''; - $this->writeNode($node, 0, true, $namespace); - $ref = $this->reference; - $this->reference = null; - - return $ref; - } - - /** - * @param NodeInterface $node - * @param int $depth - * @param bool $root If the node is the root node - * @param string $namespace The namespace of the node - */ - private function writeNode(NodeInterface $node, $depth = 0, $root = false, $namespace = null) - { - $rootName = ($root ? 'config' : $node->getName()); - $rootNamespace = ($namespace ?: ($root ? 'http://example.org/schema/dic/'.$node->getName() : null)); - - // xml remapping - if ($node->getParent()) { - $remapping = array_filter($node->getParent()->getXmlRemappings(), function ($mapping) use ($rootName) { - return $rootName === $mapping[1]; - }); - - if (count($remapping)) { - list($singular) = current($remapping); - $rootName = $singular; - } - } - $rootName = str_replace('_', '-', $rootName); - - $rootAttributes = array(); - $rootAttributeComments = array(); - $rootChildren = array(); - $rootComments = array(); - - if ($node instanceof ArrayNode) { - $children = $node->getChildren(); - - // comments about the root node - if ($rootInfo = $node->getInfo()) { - $rootComments[] = $rootInfo; - } - - if ($rootNamespace) { - $rootComments[] = 'Namespace: '.$rootNamespace; - } - - // render prototyped nodes - if ($node instanceof PrototypedArrayNode) { - $prototype = $node->getPrototype(); - - $info = 'prototype'; - if (null !== $prototype->getInfo()) { - $info .= ': '.$prototype->getInfo(); - } - array_unshift($rootComments, $info); - - if ($key = $node->getKeyAttribute()) { - $rootAttributes[$key] = str_replace('-', ' ', $rootName).' '.$key; - } - - if ($prototype instanceof ArrayNode) { - $children = $prototype->getChildren(); - } else { - if ($prototype->hasDefaultValue()) { - $prototypeValue = $prototype->getDefaultValue(); - } else { - switch (get_class($prototype)) { - case 'Symfony\Component\Config\Definition\ScalarNode': - $prototypeValue = 'scalar value'; - break; - - case 'Symfony\Component\Config\Definition\FloatNode': - case 'Symfony\Component\Config\Definition\IntegerNode': - $prototypeValue = 'numeric value'; - break; - - case 'Symfony\Component\Config\Definition\BooleanNode': - $prototypeValue = 'true|false'; - break; - - case 'Symfony\Component\Config\Definition\EnumNode': - $prototypeValue = implode('|', array_map('json_encode', $prototype->getValues())); - break; - - default: - $prototypeValue = 'value'; - } - } - } - } - - // get attributes and elements - foreach ($children as $child) { - if (!$child instanceof ArrayNode) { - // get attributes - - // metadata - $name = str_replace('_', '-', $child->getName()); - $value = '%%%%not_defined%%%%'; // use a string which isn't used in the normal world - - // comments - $comments = array(); - if ($info = $child->getInfo()) { - $comments[] = $info; - } - - if ($example = $child->getExample()) { - $comments[] = 'Example: '.$example; - } - - if ($child->isRequired()) { - $comments[] = 'Required'; - } - - if ($child instanceof EnumNode) { - $comments[] = 'One of '.implode('; ', array_map('json_encode', $child->getValues())); - } - - if (count($comments)) { - $rootAttributeComments[$name] = implode(";\n", $comments); - } - - // default values - if ($child->hasDefaultValue()) { - $value = $child->getDefaultValue(); - } - - // append attribute - $rootAttributes[$name] = $value; - } else { - // get elements - $rootChildren[] = $child; - } - } - } - - // render comments - - // root node comment - if (count($rootComments)) { - foreach ($rootComments as $comment) { - $this->writeLine('', $depth); - } - } - - // attribute comments - if (count($rootAttributeComments)) { - foreach ($rootAttributeComments as $attrName => $comment) { - $commentDepth = $depth + 4 + strlen($attrName) + 2; - $commentLines = explode("\n", $comment); - $multiline = (count($commentLines) > 1); - $comment = implode(PHP_EOL.str_repeat(' ', $commentDepth), $commentLines); - - if ($multiline) { - $this->writeLine('', $depth); - } else { - $this->writeLine('', $depth); - } - } - } - - // render start tag + attributes - $rootIsVariablePrototype = isset($prototypeValue); - $rootIsEmptyTag = (0 === count($rootChildren) && !$rootIsVariablePrototype); - $rootOpenTag = '<'.$rootName; - if (1 >= ($attributesCount = count($rootAttributes))) { - if (1 === $attributesCount) { - $rootOpenTag .= sprintf(' %s="%s"', current(array_keys($rootAttributes)), $this->writeValue(current($rootAttributes))); - } - - $rootOpenTag .= $rootIsEmptyTag ? ' />' : '>'; - - if ($rootIsVariablePrototype) { - $rootOpenTag .= $prototypeValue.''; - } - - $this->writeLine($rootOpenTag, $depth); - } else { - $this->writeLine($rootOpenTag, $depth); - - $i = 1; - - foreach ($rootAttributes as $attrName => $attrValue) { - $attr = sprintf('%s="%s"', $attrName, $this->writeValue($attrValue)); - - $this->writeLine($attr, $depth + 4); - - if ($attributesCount === $i++) { - $this->writeLine($rootIsEmptyTag ? '/>' : '>', $depth); - - if ($rootIsVariablePrototype) { - $rootOpenTag .= $prototypeValue.''; - } - } - } - } - - // render children tags - foreach ($rootChildren as $child) { - $this->writeLine(''); - $this->writeNode($child, $depth + 4); - } - - // render end tag - if (!$rootIsEmptyTag && !$rootIsVariablePrototype) { - $this->writeLine(''); - - $rootEndTag = ''; - $this->writeLine($rootEndTag, $depth); - } - } - - /** - * Outputs a single config reference line. - * - * @param string $text - * @param int $indent - */ - private function writeLine($text, $indent = 0) - { - $indent = strlen($text) + $indent; - $format = '%'.$indent.'s'; - - $this->reference .= sprintf($format, $text).PHP_EOL; - } - - /** - * Renders the string conversion of the value. - * - * @param mixed $value - * - * @return string - */ - private function writeValue($value) - { - if ('%%%%not_defined%%%%' === $value) { - return ''; - } - - if (is_string($value) || is_numeric($value)) { - return $value; - } - - if (false === $value) { - return 'false'; - } - - if (true === $value) { - return 'true'; - } - - if (null === $value) { - return 'null'; - } - - if (empty($value)) { - return ''; - } - - if (is_array($value)) { - return implode(',', $value); - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Dumper/YamlReferenceDumper.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Dumper/YamlReferenceDumper.php deleted file mode 100644 index d38e6ebeff..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Dumper/YamlReferenceDumper.php +++ /dev/null @@ -1,203 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Dumper; - -use Symfony\Component\Config\Definition\ConfigurationInterface; -use Symfony\Component\Config\Definition\NodeInterface; -use Symfony\Component\Config\Definition\ArrayNode; -use Symfony\Component\Config\Definition\EnumNode; -use Symfony\Component\Config\Definition\PrototypedArrayNode; -use Symfony\Component\Yaml\Inline; - -/** - * Dumps a Yaml reference configuration for the given configuration/node instance. - * - * @author Kevin Bond - */ -class YamlReferenceDumper -{ - private $reference; - - public function dump(ConfigurationInterface $configuration) - { - return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree()); - } - - public function dumpNode(NodeInterface $node) - { - $this->reference = ''; - $this->writeNode($node); - $ref = $this->reference; - $this->reference = null; - - return $ref; - } - - /** - * @param NodeInterface $node - * @param int $depth - */ - private function writeNode(NodeInterface $node, $depth = 0) - { - $comments = array(); - $default = ''; - $defaultArray = null; - $children = null; - $example = $node->getExample(); - - // defaults - if ($node instanceof ArrayNode) { - $children = $node->getChildren(); - - if ($node instanceof PrototypedArrayNode) { - $prototype = $node->getPrototype(); - - if ($prototype instanceof ArrayNode) { - $children = $prototype->getChildren(); - } - - // check for attribute as key - if ($key = $node->getKeyAttribute()) { - $keyNodeClass = 'Symfony\Component\Config\Definition\\'.($prototype instanceof ArrayNode ? 'ArrayNode' : 'ScalarNode'); - $keyNode = new $keyNodeClass($key, $node); - - $info = 'Prototype'; - if (null !== $prototype->getInfo()) { - $info .= ': '.$prototype->getInfo(); - } - $keyNode->setInfo($info); - - // add children - foreach ($children as $childNode) { - $keyNode->addChild($childNode); - } - $children = array($key => $keyNode); - } - } - - if (!$children) { - if ($node->hasDefaultValue() && count($defaultArray = $node->getDefaultValue())) { - $default = ''; - } elseif (!is_array($example)) { - $default = '[]'; - } - } - } elseif ($node instanceof EnumNode) { - $comments[] = 'One of '.implode('; ', array_map('json_encode', $node->getValues())); - $default = $node->hasDefaultValue() ? Inline::dump($node->getDefaultValue()) : '~'; - } else { - $default = '~'; - - if ($node->hasDefaultValue()) { - $default = $node->getDefaultValue(); - - if (is_array($default)) { - if (count($defaultArray = $node->getDefaultValue())) { - $default = ''; - } elseif (!is_array($example)) { - $default = '[]'; - } - } else { - $default = Inline::dump($default); - } - } - } - - // required? - if ($node->isRequired()) { - $comments[] = 'Required'; - } - - // example - if ($example && !is_array($example)) { - $comments[] = 'Example: '.$example; - } - - $default = (string) $default != '' ? ' '.$default : ''; - $comments = count($comments) ? '# '.implode(', ', $comments) : ''; - - $text = rtrim(sprintf('%-20s %s %s', $node->getName().':', $default, $comments), ' '); - - if ($info = $node->getInfo()) { - $this->writeLine(''); - // indenting multi-line info - $info = str_replace("\n", sprintf("\n%".($depth * 4).'s# ', ' '), $info); - $this->writeLine('# '.$info, $depth * 4); - } - - $this->writeLine($text, $depth * 4); - - // output defaults - if ($defaultArray) { - $this->writeLine(''); - - $message = count($defaultArray) > 1 ? 'Defaults' : 'Default'; - - $this->writeLine('# '.$message.':', $depth * 4 + 4); - - $this->writeArray($defaultArray, $depth + 1); - } - - if (is_array($example)) { - $this->writeLine(''); - - $message = count($example) > 1 ? 'Examples' : 'Example'; - - $this->writeLine('# '.$message.':', $depth * 4 + 4); - - $this->writeArray($example, $depth + 1); - } - - if ($children) { - foreach ($children as $childNode) { - $this->writeNode($childNode, $depth + 1); - } - } - } - - /** - * Outputs a single config reference line. - * - * @param string $text - * @param int $indent - */ - private function writeLine($text, $indent = 0) - { - $indent = strlen($text) + $indent; - $format = '%'.$indent.'s'; - - $this->reference .= sprintf($format, $text)."\n"; - } - - private function writeArray(array $array, $depth) - { - $isIndexed = array_values($array) === $array; - - foreach ($array as $key => $value) { - if (is_array($value)) { - $val = ''; - } else { - $val = $value; - } - - if ($isIndexed) { - $this->writeLine('- '.$val, $depth * 4); - } else { - $this->writeLine(sprintf('%-20s %s', $key.':', $val), $depth * 4); - } - - if (is_array($value)) { - $this->writeArray($value, $depth + 1); - } - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/EnumNode.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/EnumNode.php deleted file mode 100644 index 9b4c4156e3..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/EnumNode.php +++ /dev/null @@ -1,58 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition; - -use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; - -/** - * Node which only allows a finite set of values. - * - * @author Johannes M. Schmitt - */ -class EnumNode extends ScalarNode -{ - private $values; - - public function __construct($name, NodeInterface $parent = null, array $values = array()) - { - $values = array_unique($values); - if (empty($values)) { - throw new \InvalidArgumentException('$values must contain at least one element.'); - } - - parent::__construct($name, $parent); - $this->values = $values; - } - - public function getValues() - { - return $this->values; - } - - protected function finalizeValue($value) - { - $value = parent::finalizeValue($value); - - if (!in_array($value, $this->values, true)) { - $ex = new InvalidConfigurationException(sprintf( - 'The value %s is not allowed for path "%s". Permissible values: %s', - json_encode($value), - $this->getPath(), - implode(', ', array_map('json_encode', $this->values)))); - $ex->setPath($this->getPath()); - - throw $ex; - } - - return $value; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/DuplicateKeyException.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/DuplicateKeyException.php deleted file mode 100644 index 48dd9325e9..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/DuplicateKeyException.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Exception; - -/** - * This exception is thrown whenever the key of an array is not unique. This can - * only be the case if the configuration is coming from an XML file. - * - * @author Johannes M. Schmitt - */ -class DuplicateKeyException extends InvalidConfigurationException -{ -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/Exception.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/Exception.php deleted file mode 100644 index 8933a49233..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/Exception.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Exception; - -/** - * Base exception for all configuration exceptions. - * - * @author Johannes M. Schmitt - */ -class Exception extends \RuntimeException -{ -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/ForbiddenOverwriteException.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/ForbiddenOverwriteException.php deleted file mode 100644 index 726c07fb8b..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/ForbiddenOverwriteException.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Exception; - -/** - * This exception is thrown when a configuration path is overwritten from a - * subsequent configuration file, but the entry node specifically forbids this. - * - * @author Johannes M. Schmitt - */ -class ForbiddenOverwriteException extends InvalidConfigurationException -{ -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/InvalidConfigurationException.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/InvalidConfigurationException.php deleted file mode 100644 index 3dbc57b153..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/InvalidConfigurationException.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Exception; - -/** - * A very general exception which can be thrown whenever non of the more specific - * exceptions is suitable. - * - * @author Johannes M. Schmitt - */ -class InvalidConfigurationException extends Exception -{ - private $path; - private $containsHints = false; - - public function setPath($path) - { - $this->path = $path; - } - - public function getPath() - { - return $this->path; - } - - /** - * Adds extra information that is suffixed to the original exception message. - * - * @param string $hint - */ - public function addHint($hint) - { - if (!$this->containsHints) { - $this->message .= "\nHint: ".$hint; - $this->containsHints = true; - } else { - $this->message .= ', '.$hint; - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/InvalidDefinitionException.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/InvalidDefinitionException.php deleted file mode 100644 index 98310dae02..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/InvalidDefinitionException.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Exception; - -/** - * Thrown when an error is detected in a node Definition. - * - * @author Victor Berchet - */ -class InvalidDefinitionException extends Exception -{ -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/InvalidTypeException.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/InvalidTypeException.php deleted file mode 100644 index d7ca8c9d09..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/InvalidTypeException.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Exception; - -/** - * This exception is thrown if an invalid type is encountered. - * - * @author Johannes M. Schmitt - */ -class InvalidTypeException extends InvalidConfigurationException -{ -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/UnsetKeyException.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/UnsetKeyException.php deleted file mode 100644 index 863181a33f..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Exception/UnsetKeyException.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition\Exception; - -/** - * This exception is usually not encountered by the end-user, but only used - * internally to signal the parent scope to unset a key. - * - * @author Johannes M. Schmitt - */ -class UnsetKeyException extends Exception -{ -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/FloatNode.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/FloatNode.php deleted file mode 100644 index 5e1af17ada..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/FloatNode.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition; - -use Symfony\Component\Config\Definition\Exception\InvalidTypeException; - -/** - * This node represents a float value in the config tree. - * - * @author Jeanmonod David - */ -class FloatNode extends NumericNode -{ - /** - * {@inheritdoc} - */ - protected function validateType($value) - { - // Integers are also accepted, we just cast them - if (is_int($value)) { - $value = (float) $value; - } - - if (!is_float($value)) { - $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected float, but got %s.', $this->getPath(), gettype($value))); - if ($hint = $this->getInfo()) { - $ex->addHint($hint); - } - $ex->setPath($this->getPath()); - - throw $ex; - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/IntegerNode.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/IntegerNode.php deleted file mode 100644 index ba2307024c..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/IntegerNode.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition; - -use Symfony\Component\Config\Definition\Exception\InvalidTypeException; - -/** - * This node represents an integer value in the config tree. - * - * @author Jeanmonod David - */ -class IntegerNode extends NumericNode -{ - /** - * {@inheritdoc} - */ - protected function validateType($value) - { - if (!is_int($value)) { - $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected int, but got %s.', $this->getPath(), gettype($value))); - if ($hint = $this->getInfo()) { - $ex->addHint($hint); - } - $ex->setPath($this->getPath()); - - throw $ex; - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/NodeInterface.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/NodeInterface.php deleted file mode 100644 index b9bddc4938..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/NodeInterface.php +++ /dev/null @@ -1,88 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition; - -/** - * Common Interface among all nodes. - * - * In most cases, it is better to inherit from BaseNode instead of implementing - * this interface yourself. - * - * @author Johannes M. Schmitt - */ -interface NodeInterface -{ - /** - * Returns the name of the node. - * - * @return string The name of the node - */ - public function getName(); - - /** - * Returns the path of the node. - * - * @return string The node path - */ - public function getPath(); - - /** - * Returns true when the node is required. - * - * @return bool If the node is required - */ - public function isRequired(); - - /** - * Returns true when the node has a default value. - * - * @return bool If the node has a default value - */ - public function hasDefaultValue(); - - /** - * Returns the default value of the node. - * - * @return mixed The default value - * - * @throws \RuntimeException if the node has no default value - */ - public function getDefaultValue(); - - /** - * Normalizes the supplied value. - * - * @param mixed $value The value to normalize - * - * @return mixed The normalized value - */ - public function normalize($value); - - /** - * Merges two values together. - * - * @param mixed $leftSide - * @param mixed $rightSide - * - * @return mixed The merged values - */ - public function merge($leftSide, $rightSide); - - /** - * Finalizes a value. - * - * @param mixed $value The value to finalize - * - * @return mixed The finalized value - */ - public function finalize($value); -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/NumericNode.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/NumericNode.php deleted file mode 100644 index 439935e455..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/NumericNode.php +++ /dev/null @@ -1,64 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition; - -use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; - -/** - * This node represents a numeric value in the config tree. - * - * @author David Jeanmonod - */ -class NumericNode extends ScalarNode -{ - protected $min; - protected $max; - - public function __construct($name, NodeInterface $parent = null, $min = null, $max = null) - { - parent::__construct($name, $parent); - $this->min = $min; - $this->max = $max; - } - - /** - * {@inheritdoc} - */ - protected function finalizeValue($value) - { - $value = parent::finalizeValue($value); - - $errorMsg = null; - if (isset($this->min) && $value < $this->min) { - $errorMsg = sprintf('The value %s is too small for path "%s". Should be greater than or equal to %s', $value, $this->getPath(), $this->min); - } - if (isset($this->max) && $value > $this->max) { - $errorMsg = sprintf('The value %s is too big for path "%s". Should be less than or equal to %s', $value, $this->getPath(), $this->max); - } - if (isset($errorMsg)) { - $ex = new InvalidConfigurationException($errorMsg); - $ex->setPath($this->getPath()); - throw $ex; - } - - return $value; - } - - /** - * {@inheritdoc} - */ - protected function isValueEmpty($value) - { - // a numeric value cannot be empty - return false; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Processor.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Processor.php deleted file mode 100644 index 025e69378f..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/Processor.php +++ /dev/null @@ -1,97 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition; - -/** - * This class is the entry point for config normalization/merging/finalization. - * - * @author Johannes M. Schmitt - */ -class Processor -{ - /** - * Processes an array of configurations. - * - * @param NodeInterface $configTree The node tree describing the configuration - * @param array $configs An array of configuration items to process - * - * @return array The processed configuration - */ - public function process(NodeInterface $configTree, array $configs) - { - $currentConfig = array(); - foreach ($configs as $config) { - $config = $configTree->normalize($config); - $currentConfig = $configTree->merge($currentConfig, $config); - } - - return $configTree->finalize($currentConfig); - } - - /** - * Processes an array of configurations. - * - * @param ConfigurationInterface $configuration The configuration class - * @param array $configs An array of configuration items to process - * - * @return array The processed configuration - */ - public function processConfiguration(ConfigurationInterface $configuration, array $configs) - { - return $this->process($configuration->getConfigTreeBuilder()->buildTree(), $configs); - } - - /** - * Normalizes a configuration entry. - * - * This method returns a normalize configuration array for a given key - * to remove the differences due to the original format (YAML and XML mainly). - * - * Here is an example. - * - * The configuration in XML: - * - * twig.extension.foo - * twig.extension.bar - * - * And the same configuration in YAML: - * - * extensions: ['twig.extension.foo', 'twig.extension.bar'] - * - * @param array $config A config array - * @param string $key The key to normalize - * @param string $plural The plural form of the key if it is irregular - * - * @return array - */ - public static function normalizeConfig($config, $key, $plural = null) - { - if (null === $plural) { - $plural = $key.'s'; - } - - if (isset($config[$plural])) { - return $config[$plural]; - } - - if (isset($config[$key])) { - if (is_string($config[$key]) || !is_int(key($config[$key]))) { - // only one - return array($config[$key]); - } - - return $config[$key]; - } - - return array(); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/PrototypeNodeInterface.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/PrototypeNodeInterface.php deleted file mode 100644 index 8bbb84d4dc..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/PrototypeNodeInterface.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition; - -/** - * This interface must be implemented by nodes which can be used as prototypes. - * - * @author Johannes M. Schmitt - */ -interface PrototypeNodeInterface extends NodeInterface -{ - /** - * Sets the name of the node. - * - * @param string $name The name of the node - */ - public function setName($name); -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/PrototypedArrayNode.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/PrototypedArrayNode.php deleted file mode 100644 index ae1a76663a..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/PrototypedArrayNode.php +++ /dev/null @@ -1,331 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition; - -use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; -use Symfony\Component\Config\Definition\Exception\DuplicateKeyException; -use Symfony\Component\Config\Definition\Exception\UnsetKeyException; -use Symfony\Component\Config\Definition\Exception\Exception; - -/** - * Represents a prototyped Array node in the config tree. - * - * @author Johannes M. Schmitt - */ -class PrototypedArrayNode extends ArrayNode -{ - protected $prototype; - protected $keyAttribute; - protected $removeKeyAttribute = false; - protected $minNumberOfElements = 0; - protected $defaultValue = array(); - protected $defaultChildren; - - /** - * Sets the minimum number of elements that a prototype based node must - * contain. By default this is zero, meaning no elements. - * - * @param int $number - */ - public function setMinNumberOfElements($number) - { - $this->minNumberOfElements = $number; - } - - /** - * Sets the attribute which value is to be used as key. - * - * This is useful when you have an indexed array that should be an - * associative array. You can select an item from within the array - * to be the key of the particular item. For example, if "id" is the - * "key", then: - * - * array( - * array('id' => 'my_name', 'foo' => 'bar'), - * ); - * - * becomes - * - * array( - * 'my_name' => array('foo' => 'bar'), - * ); - * - * If you'd like "'id' => 'my_name'" to still be present in the resulting - * array, then you can set the second argument of this method to false. - * - * @param string $attribute The name of the attribute which value is to be used as a key - * @param bool $remove Whether or not to remove the key - */ - public function setKeyAttribute($attribute, $remove = true) - { - $this->keyAttribute = $attribute; - $this->removeKeyAttribute = $remove; - } - - /** - * Retrieves the name of the attribute which value should be used as key. - * - * @return string The name of the attribute - */ - public function getKeyAttribute() - { - return $this->keyAttribute; - } - - /** - * Sets the default value of this node. - * - * @param string $value - * - * @throws \InvalidArgumentException if the default value is not an array - */ - public function setDefaultValue($value) - { - if (!is_array($value)) { - throw new \InvalidArgumentException($this->getPath().': the default value of an array node has to be an array.'); - } - - $this->defaultValue = $value; - } - - /** - * Checks if the node has a default value. - * - * @return bool - */ - public function hasDefaultValue() - { - return true; - } - - /** - * Adds default children when none are set. - * - * @param int|string|array|null $children The number of children|The child name|The children names to be added - */ - public function setAddChildrenIfNoneSet($children = array('defaults')) - { - if (null === $children) { - $this->defaultChildren = array('defaults'); - } else { - $this->defaultChildren = is_int($children) && $children > 0 ? range(1, $children) : (array) $children; - } - } - - /** - * Retrieves the default value. - * - * The default value could be either explicited or derived from the prototype - * default value. - * - * @return array The default value - */ - public function getDefaultValue() - { - if (null !== $this->defaultChildren) { - $default = $this->prototype->hasDefaultValue() ? $this->prototype->getDefaultValue() : array(); - $defaults = array(); - foreach (array_values($this->defaultChildren) as $i => $name) { - $defaults[null === $this->keyAttribute ? $i : $name] = $default; - } - - return $defaults; - } - - return $this->defaultValue; - } - - /** - * Sets the node prototype. - * - * @param PrototypeNodeInterface $node - */ - public function setPrototype(PrototypeNodeInterface $node) - { - $this->prototype = $node; - } - - /** - * Retrieves the prototype. - * - * @return PrototypeNodeInterface The prototype - */ - public function getPrototype() - { - return $this->prototype; - } - - /** - * Disable adding concrete children for prototyped nodes. - * - * @param NodeInterface $node The child node to add - * - * @throws Exception - */ - public function addChild(NodeInterface $node) - { - throw new Exception('A prototyped array node can not have concrete children.'); - } - - /** - * Finalizes the value of this node. - * - * @param mixed $value - * - * @return mixed The finalized value - * - * @throws UnsetKeyException - * @throws InvalidConfigurationException if the node doesn't have enough children - */ - protected function finalizeValue($value) - { - if (false === $value) { - $msg = sprintf('Unsetting key for path "%s", value: %s', $this->getPath(), json_encode($value)); - throw new UnsetKeyException($msg); - } - - foreach ($value as $k => $v) { - $this->prototype->setName($k); - try { - $value[$k] = $this->prototype->finalize($v); - } catch (UnsetKeyException $e) { - unset($value[$k]); - } - } - - if (count($value) < $this->minNumberOfElements) { - $msg = sprintf('The path "%s" should have at least %d element(s) defined.', $this->getPath(), $this->minNumberOfElements); - $ex = new InvalidConfigurationException($msg); - $ex->setPath($this->getPath()); - - throw $ex; - } - - return $value; - } - - /** - * Normalizes the value. - * - * @param mixed $value The value to normalize - * - * @return mixed The normalized value - * - * @throws InvalidConfigurationException - * @throws DuplicateKeyException - */ - protected function normalizeValue($value) - { - if (false === $value) { - return $value; - } - - $value = $this->remapXml($value); - - $isAssoc = array_keys($value) !== range(0, count($value) - 1); - $normalized = array(); - foreach ($value as $k => $v) { - if (null !== $this->keyAttribute && is_array($v)) { - if (!isset($v[$this->keyAttribute]) && is_int($k) && !$isAssoc) { - $msg = sprintf('The attribute "%s" must be set for path "%s".', $this->keyAttribute, $this->getPath()); - $ex = new InvalidConfigurationException($msg); - $ex->setPath($this->getPath()); - - throw $ex; - } elseif (isset($v[$this->keyAttribute])) { - $k = $v[$this->keyAttribute]; - - // remove the key attribute when required - if ($this->removeKeyAttribute) { - unset($v[$this->keyAttribute]); - } - - // if only "value" is left - if (1 == count($v) && isset($v['value'])) { - $v = $v['value']; - } - } - - if (array_key_exists($k, $normalized)) { - $msg = sprintf('Duplicate key "%s" for path "%s".', $k, $this->getPath()); - $ex = new DuplicateKeyException($msg); - $ex->setPath($this->getPath()); - - throw $ex; - } - } - - $this->prototype->setName($k); - if (null !== $this->keyAttribute || $isAssoc) { - $normalized[$k] = $this->prototype->normalize($v); - } else { - $normalized[] = $this->prototype->normalize($v); - } - } - - return $normalized; - } - - /** - * Merges values together. - * - * @param mixed $leftSide The left side to merge - * @param mixed $rightSide The right side to merge - * - * @return mixed The merged values - * - * @throws InvalidConfigurationException - * @throws \RuntimeException - */ - protected function mergeValues($leftSide, $rightSide) - { - if (false === $rightSide) { - // if this is still false after the last config has been merged the - // finalization pass will take care of removing this key entirely - return false; - } - - if (false === $leftSide || !$this->performDeepMerging) { - return $rightSide; - } - - foreach ($rightSide as $k => $v) { - // prototype, and key is irrelevant, so simply append the element - if (null === $this->keyAttribute) { - $leftSide[] = $v; - continue; - } - - // no conflict - if (!array_key_exists($k, $leftSide)) { - if (!$this->allowNewKeys) { - $ex = new InvalidConfigurationException(sprintf( - 'You are not allowed to define new elements for path "%s". '. - 'Please define all elements for this path in one config file.', - $this->getPath() - )); - $ex->setPath($this->getPath()); - - throw $ex; - } - - $leftSide[$k] = $v; - continue; - } - - $this->prototype->setName($k); - $leftSide[$k] = $this->prototype->merge($leftSide[$k], $v); - } - - return $leftSide; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/ScalarNode.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/ScalarNode.php deleted file mode 100644 index 6b3fd0b68f..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/ScalarNode.php +++ /dev/null @@ -1,57 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition; - -use Symfony\Component\Config\Definition\Exception\InvalidTypeException; - -/** - * This node represents a scalar value in the config tree. - * - * The following values are considered scalars: - * * booleans - * * strings - * * null - * * integers - * * floats - * - * @author Johannes M. Schmitt - */ -class ScalarNode extends VariableNode -{ - /** - * {@inheritdoc} - */ - protected function validateType($value) - { - if (!is_scalar($value) && null !== $value) { - $ex = new InvalidTypeException(sprintf( - 'Invalid type for path "%s". Expected scalar, but got %s.', - $this->getPath(), - gettype($value) - )); - if ($hint = $this->getInfo()) { - $ex->addHint($hint); - } - $ex->setPath($this->getPath()); - - throw $ex; - } - } - - /** - * {@inheritdoc} - */ - protected function isValueEmpty($value) - { - return null === $value || '' === $value; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/VariableNode.php b/core/src/core/classes/guzzle/vendor/symfony/config/Definition/VariableNode.php deleted file mode 100644 index a9c35284cd..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Definition/VariableNode.php +++ /dev/null @@ -1,135 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Definition; - -use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; - -/** - * This node represents a value of variable type in the config tree. - * - * This node is intended for values of arbitrary type. - * Any PHP type is accepted as a value. - * - * @author Jeremy Mikola - */ -class VariableNode extends BaseNode implements PrototypeNodeInterface -{ - protected $defaultValueSet = false; - protected $defaultValue; - protected $allowEmptyValue = true; - - /** - * {@inheritdoc} - */ - public function setDefaultValue($value) - { - $this->defaultValueSet = true; - $this->defaultValue = $value; - } - - /** - * {@inheritdoc} - */ - public function hasDefaultValue() - { - return $this->defaultValueSet; - } - - /** - * {@inheritdoc} - */ - public function getDefaultValue() - { - $v = $this->defaultValue; - - return $v instanceof \Closure ? $v() : $v; - } - - /** - * Sets if this node is allowed to have an empty value. - * - * @param bool $boolean True if this entity will accept empty values - */ - public function setAllowEmptyValue($boolean) - { - $this->allowEmptyValue = (bool) $boolean; - } - - /** - * {@inheritdoc} - */ - public function setName($name) - { - $this->name = $name; - } - - /** - * {@inheritdoc} - */ - protected function validateType($value) - { - } - - /** - * {@inheritdoc} - */ - protected function finalizeValue($value) - { - if (!$this->allowEmptyValue && $this->isValueEmpty($value)) { - $ex = new InvalidConfigurationException(sprintf( - 'The path "%s" cannot contain an empty value, but got %s.', - $this->getPath(), - json_encode($value) - )); - if ($hint = $this->getInfo()) { - $ex->addHint($hint); - } - $ex->setPath($this->getPath()); - - throw $ex; - } - - return $value; - } - - /** - * {@inheritdoc} - */ - protected function normalizeValue($value) - { - return $value; - } - - /** - * {@inheritdoc} - */ - protected function mergeValues($leftSide, $rightSide) - { - return $rightSide; - } - - /** - * Evaluates if the given value is to be treated as empty. - * - * By default, PHP's empty() function is used to test for emptiness. This - * method may be overridden by subtypes to better match their understanding - * of empty data. - * - * @param mixed $value - * - * @return bool - */ - protected function isValueEmpty($value) - { - return empty($value); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Exception/FileLoaderImportCircularReferenceException.php b/core/src/core/classes/guzzle/vendor/symfony/config/Exception/FileLoaderImportCircularReferenceException.php deleted file mode 100644 index 6a3b01cfbe..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Exception/FileLoaderImportCircularReferenceException.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Exception; - -/** - * Exception class for when a circular reference is detected when importing resources. - * - * @author Fabien Potencier - */ -class FileLoaderImportCircularReferenceException extends FileLoaderLoadException -{ - public function __construct(array $resources, $code = null, $previous = null) - { - $message = sprintf('Circular reference detected in "%s" ("%s" > "%s").', $this->varToString($resources[0]), implode('" > "', $resources), $resources[0]); - - \Exception::__construct($message, $code, $previous); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Exception/FileLoaderLoadException.php b/core/src/core/classes/guzzle/vendor/symfony/config/Exception/FileLoaderLoadException.php deleted file mode 100644 index 6af3dd0a6d..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Exception/FileLoaderLoadException.php +++ /dev/null @@ -1,101 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Exception; - -/** - * Exception class for when a resource cannot be loaded or imported. - * - * @author Ryan Weaver - */ -class FileLoaderLoadException extends \Exception -{ - /** - * @param string $resource The resource that could not be imported - * @param string $sourceResource The original resource importing the new resource - * @param int $code The error code - * @param \Exception $previous A previous exception - */ - public function __construct($resource, $sourceResource = null, $code = null, $previous = null) - { - $message = ''; - if ($previous) { - // Include the previous exception, to help the user see what might be the underlying cause - - // Trim the trailing period of the previous message. We only want 1 period remove so no rtrim... - if ('.' === substr($previous->getMessage(), -1)) { - $trimmedMessage = substr($previous->getMessage(), 0, -1); - $message .= sprintf('%s', $trimmedMessage).' in '; - } else { - $message .= sprintf('%s', $previous->getMessage()).' in '; - } - $message .= $resource.' '; - - // show tweaked trace to complete the human readable sentence - if (null === $sourceResource) { - $message .= sprintf('(which is loaded in resource "%s")', $this->varToString($resource)); - } else { - $message .= sprintf('(which is being imported from "%s")', $this->varToString($sourceResource)); - } - $message .= '.'; - - // if there's no previous message, present it the default way - } elseif (null === $sourceResource) { - $message .= sprintf('Cannot load resource "%s".', $this->varToString($resource)); - } else { - $message .= sprintf('Cannot import resource "%s" from "%s".', $this->varToString($resource), $this->varToString($sourceResource)); - } - - // Is the resource located inside a bundle? - if ('@' === $resource[0]) { - $parts = explode(DIRECTORY_SEPARATOR, $resource); - $bundle = substr($parts[0], 1); - $message .= sprintf(' Make sure the "%s" bundle is correctly registered and loaded in the application kernel class.', $bundle); - $message .= sprintf(' If the bundle is registered, make sure the bundle path "%s" is not empty.', $resource); - } - - parent::__construct($message, $code, $previous); - } - - protected function varToString($var) - { - if (is_object($var)) { - return sprintf('Object(%s)', get_class($var)); - } - - if (is_array($var)) { - $a = array(); - foreach ($var as $k => $v) { - $a[] = sprintf('%s => %s', $k, $this->varToString($v)); - } - - return sprintf('Array(%s)', implode(', ', $a)); - } - - if (is_resource($var)) { - return sprintf('Resource(%s)', get_resource_type($var)); - } - - if (null === $var) { - return 'null'; - } - - if (false === $var) { - return 'false'; - } - - if (true === $var) { - return 'true'; - } - - return (string) $var; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/FileLocator.php b/core/src/core/classes/guzzle/vendor/symfony/config/FileLocator.php deleted file mode 100644 index c6600c7783..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/FileLocator.php +++ /dev/null @@ -1,96 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config; - -/** - * FileLocator uses an array of pre-defined paths to find files. - * - * @author Fabien Potencier - */ -class FileLocator implements FileLocatorInterface -{ - protected $paths; - - /** - * Constructor. - * - * @param string|array $paths A path or an array of paths where to look for resources - */ - public function __construct($paths = array()) - { - $this->paths = (array) $paths; - } - - /** - * {@inheritdoc} - */ - public function locate($name, $currentPath = null, $first = true) - { - if ('' == $name) { - throw new \InvalidArgumentException('An empty file name is not valid to be located.'); - } - - if ($this->isAbsolutePath($name)) { - if (!file_exists($name)) { - throw new \InvalidArgumentException(sprintf('The file "%s" does not exist.', $name)); - } - - return $name; - } - - $paths = $this->paths; - - if (null !== $currentPath) { - array_unshift($paths, $currentPath); - } - - $paths = array_unique($paths); - $filepaths = array(); - - foreach ($paths as $path) { - if (file_exists($file = $path.DIRECTORY_SEPARATOR.$name)) { - if (true === $first) { - return $file; - } - $filepaths[] = $file; - } - } - - if (!$filepaths) { - throw new \InvalidArgumentException(sprintf('The file "%s" does not exist (in: %s).', $name, implode(', ', $paths))); - } - - return $filepaths; - } - - /** - * Returns whether the file path is an absolute path. - * - * @param string $file A file path - * - * @return bool - */ - private function isAbsolutePath($file) - { - if ($file[0] === '/' || $file[0] === '\\' - || (strlen($file) > 3 && ctype_alpha($file[0]) - && $file[1] === ':' - && ($file[2] === '\\' || $file[2] === '/') - ) - || null !== parse_url($file, PHP_URL_SCHEME) - ) { - return true; - } - - return false; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/FileLocatorInterface.php b/core/src/core/classes/guzzle/vendor/symfony/config/FileLocatorInterface.php deleted file mode 100644 index 66057982db..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/FileLocatorInterface.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config; - -/** - * @author Fabien Potencier - */ -interface FileLocatorInterface -{ - /** - * Returns a full path for a given file name. - * - * @param string $name The file name to locate - * @param string|null $currentPath The current path - * @param bool $first Whether to return the first occurrence or an array of filenames - * - * @return string|array The full path to the file or an array of file paths - * - * @throws \InvalidArgumentException When file is not found - */ - public function locate($name, $currentPath = null, $first = true); -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/LICENSE b/core/src/core/classes/guzzle/vendor/symfony/config/LICENSE deleted file mode 100644 index 12a74531e4..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2004-2016 Fabien Potencier - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Loader/DelegatingLoader.php b/core/src/core/classes/guzzle/vendor/symfony/config/Loader/DelegatingLoader.php deleted file mode 100644 index 3097878bf0..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Loader/DelegatingLoader.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Loader; - -use Symfony\Component\Config\Exception\FileLoaderLoadException; - -/** - * DelegatingLoader delegates loading to other loaders using a loader resolver. - * - * This loader acts as an array of LoaderInterface objects - each having - * a chance to load a given resource (handled by the resolver) - * - * @author Fabien Potencier - */ -class DelegatingLoader extends Loader -{ - /** - * Constructor. - * - * @param LoaderResolverInterface $resolver A LoaderResolverInterface instance - */ - public function __construct(LoaderResolverInterface $resolver) - { - $this->resolver = $resolver; - } - - /** - * {@inheritdoc} - */ - public function load($resource, $type = null) - { - if (false === $loader = $this->resolver->resolve($resource, $type)) { - throw new FileLoaderLoadException($resource); - } - - return $loader->load($resource, $type); - } - - /** - * {@inheritdoc} - */ - public function supports($resource, $type = null) - { - return false !== $this->resolver->resolve($resource, $type); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Loader/FileLoader.php b/core/src/core/classes/guzzle/vendor/symfony/config/Loader/FileLoader.php deleted file mode 100644 index 3bd3fae17c..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Loader/FileLoader.php +++ /dev/null @@ -1,125 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Loader; - -use Symfony\Component\Config\FileLocatorInterface; -use Symfony\Component\Config\Exception\FileLoaderLoadException; -use Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException; - -/** - * FileLoader is the abstract class used by all built-in loaders that are file based. - * - * @author Fabien Potencier - */ -abstract class FileLoader extends Loader -{ - /** - * @var array - */ - protected static $loading = array(); - - /** - * @var FileLocatorInterface - */ - protected $locator; - - private $currentDir; - - /** - * Constructor. - * - * @param FileLocatorInterface $locator A FileLocatorInterface instance - */ - public function __construct(FileLocatorInterface $locator) - { - $this->locator = $locator; - } - - /** - * Sets the current directory. - * - * @param string $dir - */ - public function setCurrentDir($dir) - { - $this->currentDir = $dir; - } - - /** - * Returns the file locator used by this loader. - * - * @return FileLocatorInterface - */ - public function getLocator() - { - return $this->locator; - } - - /** - * Imports a resource. - * - * @param mixed $resource A Resource - * @param string|null $type The resource type or null if unknown - * @param bool $ignoreErrors Whether to ignore import errors or not - * @param string|null $sourceResource The original resource importing the new resource - * - * @return mixed - * - * @throws FileLoaderLoadException - * @throws FileLoaderImportCircularReferenceException - */ - public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null) - { - try { - $loader = $this->resolve($resource, $type); - - if ($loader instanceof self && null !== $this->currentDir) { - $resource = $loader->getLocator()->locate($resource, $this->currentDir, false); - } - - $resources = is_array($resource) ? $resource : array($resource); - for ($i = 0; $i < $resourcesCount = count($resources); ++$i) { - if (isset(self::$loading[$resources[$i]])) { - if ($i == $resourcesCount - 1) { - throw new FileLoaderImportCircularReferenceException(array_keys(self::$loading)); - } - } else { - $resource = $resources[$i]; - break; - } - } - self::$loading[$resource] = true; - - try { - $ret = $loader->load($resource, $type); - } catch (\Exception $e) { - unset(self::$loading[$resource]); - throw $e; - } - - unset(self::$loading[$resource]); - - return $ret; - } catch (FileLoaderImportCircularReferenceException $e) { - throw $e; - } catch (\Exception $e) { - if (!$ignoreErrors) { - // prevent embedded imports from nesting multiple exceptions - if ($e instanceof FileLoaderLoadException) { - throw $e; - } - - throw new FileLoaderLoadException($resource, $sourceResource, null, $e); - } - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Loader/Loader.php b/core/src/core/classes/guzzle/vendor/symfony/config/Loader/Loader.php deleted file mode 100644 index de4e127386..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Loader/Loader.php +++ /dev/null @@ -1,78 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Loader; - -use Symfony\Component\Config\Exception\FileLoaderLoadException; - -/** - * Loader is the abstract class used by all built-in loaders. - * - * @author Fabien Potencier - */ -abstract class Loader implements LoaderInterface -{ - protected $resolver; - - /** - * {@inheritdoc} - */ - public function getResolver() - { - return $this->resolver; - } - - /** - * {@inheritdoc} - */ - public function setResolver(LoaderResolverInterface $resolver) - { - $this->resolver = $resolver; - } - - /** - * Imports a resource. - * - * @param mixed $resource A resource - * @param string|null $type The resource type or null if unknown - * - * @return mixed - */ - public function import($resource, $type = null) - { - return $this->resolve($resource, $type)->load($resource, $type); - } - - /** - * Finds a loader able to load an imported resource. - * - * @param mixed $resource A resource - * @param string|null $type The resource type or null if unknown - * - * @return LoaderInterface A LoaderInterface instance - * - * @throws FileLoaderLoadException If no loader is found - */ - public function resolve($resource, $type = null) - { - if ($this->supports($resource, $type)) { - return $this; - } - - $loader = null === $this->resolver ? false : $this->resolver->resolve($resource, $type); - - if (false === $loader) { - throw new FileLoaderLoadException($resource); - } - - return $loader; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Loader/LoaderInterface.php b/core/src/core/classes/guzzle/vendor/symfony/config/Loader/LoaderInterface.php deleted file mode 100644 index dd0a85a6b0..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Loader/LoaderInterface.php +++ /dev/null @@ -1,54 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Loader; - -/** - * LoaderInterface is the interface implemented by all loader classes. - * - * @author Fabien Potencier - */ -interface LoaderInterface -{ - /** - * Loads a resource. - * - * @param mixed $resource The resource - * @param string|null $type The resource type or null if unknown - * - * @throws \Exception If something went wrong - */ - public function load($resource, $type = null); - - /** - * Returns whether this class supports the given resource. - * - * @param mixed $resource A resource - * @param string|null $type The resource type or null if unknown - * - * @return bool True if this class supports the given resource, false otherwise - */ - public function supports($resource, $type = null); - - /** - * Gets the loader resolver. - * - * @return LoaderResolverInterface A LoaderResolverInterface instance - */ - public function getResolver(); - - /** - * Sets the loader resolver. - * - * @param LoaderResolverInterface $resolver A LoaderResolverInterface instance - */ - public function setResolver(LoaderResolverInterface $resolver); -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Loader/LoaderResolver.php b/core/src/core/classes/guzzle/vendor/symfony/config/Loader/LoaderResolver.php deleted file mode 100644 index dc6846df8d..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Loader/LoaderResolver.php +++ /dev/null @@ -1,75 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Loader; - -/** - * LoaderResolver selects a loader for a given resource. - * - * A resource can be anything (e.g. a full path to a config file or a Closure). - * Each loader determines whether it can load a resource and how. - * - * @author Fabien Potencier - */ -class LoaderResolver implements LoaderResolverInterface -{ - /** - * @var LoaderInterface[] An array of LoaderInterface objects - */ - private $loaders = array(); - - /** - * Constructor. - * - * @param LoaderInterface[] $loaders An array of loaders - */ - public function __construct(array $loaders = array()) - { - foreach ($loaders as $loader) { - $this->addLoader($loader); - } - } - - /** - * {@inheritdoc} - */ - public function resolve($resource, $type = null) - { - foreach ($this->loaders as $loader) { - if ($loader->supports($resource, $type)) { - return $loader; - } - } - - return false; - } - - /** - * Adds a loader. - * - * @param LoaderInterface $loader A LoaderInterface instance - */ - public function addLoader(LoaderInterface $loader) - { - $this->loaders[] = $loader; - $loader->setResolver($this); - } - - /** - * Returns the registered loaders. - * - * @return LoaderInterface[] An array of LoaderInterface instances - */ - public function getLoaders() - { - return $this->loaders; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Loader/LoaderResolverInterface.php b/core/src/core/classes/guzzle/vendor/symfony/config/Loader/LoaderResolverInterface.php deleted file mode 100644 index 40f1a1a63f..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Loader/LoaderResolverInterface.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Loader; - -/** - * LoaderResolverInterface selects a loader for a given resource. - * - * @author Fabien Potencier - */ -interface LoaderResolverInterface -{ - /** - * Returns a loader able to load the resource. - * - * @param mixed $resource A resource - * @param string|null $type The resource type or null if unknown - * - * @return LoaderInterface|false The loader or false if none is able to load the resource - */ - public function resolve($resource, $type = null); -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/README.md b/core/src/core/classes/guzzle/vendor/symfony/config/README.md deleted file mode 100644 index bf400da196..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/README.md +++ /dev/null @@ -1,15 +0,0 @@ -Config Component -================ - -The Config component provides several classes to help you find, load, combine, -autofill and validate configuration values of any kind, whatever their source -may be (YAML, XML, INI files, or for instance a database). - -Resources ---------- - - * [Documentation](https://symfony.com/doc/current/components/config/index.html) - * [Contributing](https://symfony.com/doc/current/contributing/index.html) - * [Report issues](https://github.com/symfony/symfony/issues) and - [send Pull Requests](https://github.com/symfony/symfony/pulls) - in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Resource/DirectoryResource.php b/core/src/core/classes/guzzle/vendor/symfony/config/Resource/DirectoryResource.php deleted file mode 100644 index eaef8d5b1e..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Resource/DirectoryResource.php +++ /dev/null @@ -1,105 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Resource; - -/** - * DirectoryResource represents a resources stored in a subdirectory tree. - * - * @author Fabien Potencier - */ -class DirectoryResource implements SelfCheckingResourceInterface, \Serializable -{ - private $resource; - private $pattern; - - /** - * Constructor. - * - * @param string $resource The file path to the resource - * @param string|null $pattern A pattern to restrict monitored files - * - * @throws \InvalidArgumentException - */ - public function __construct($resource, $pattern = null) - { - $this->resource = realpath($resource); - $this->pattern = $pattern; - - if (false === $this->resource || !is_dir($this->resource)) { - throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist.', $resource)); - } - } - - /** - * {@inheritdoc} - */ - public function __toString() - { - return md5(serialize(array($this->resource, $this->pattern))); - } - - /** - * @return string The file path to the resource - */ - public function getResource() - { - return $this->resource; - } - - /** - * Returns the pattern to restrict monitored files. - * - * @return string|null - */ - public function getPattern() - { - return $this->pattern; - } - - /** - * {@inheritdoc} - */ - public function isFresh($timestamp) - { - if (!is_dir($this->resource)) { - return false; - } - - $newestMTime = filemtime($this->resource); - foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->resource), \RecursiveIteratorIterator::SELF_FIRST) as $file) { - // if regex filtering is enabled only check matching files - if ($this->pattern && $file->isFile() && !preg_match($this->pattern, $file->getBasename())) { - continue; - } - - // always monitor directories for changes, except the .. entries - // (otherwise deleted files wouldn't get detected) - if ($file->isDir() && '/..' === substr($file, -3)) { - continue; - } - - $newestMTime = max($file->getMTime(), $newestMTime); - } - - return $newestMTime < $timestamp; - } - - public function serialize() - { - return serialize(array($this->resource, $this->pattern)); - } - - public function unserialize($serialized) - { - list($this->resource, $this->pattern) = unserialize($serialized); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Resource/FileExistenceResource.php b/core/src/core/classes/guzzle/vendor/symfony/config/Resource/FileExistenceResource.php deleted file mode 100644 index 349402edf0..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Resource/FileExistenceResource.php +++ /dev/null @@ -1,78 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Resource; - -/** - * FileExistenceResource represents a resource stored on the filesystem. - * Freshness is only evaluated against resource creation or deletion. - * - * The resource can be a file or a directory. - * - * @author Charles-Henri Bruyand - */ -class FileExistenceResource implements SelfCheckingResourceInterface, \Serializable -{ - private $resource; - - private $exists; - - /** - * Constructor. - * - * @param string $resource The file path to the resource - */ - public function __construct($resource) - { - $this->resource = (string) $resource; - $this->exists = file_exists($resource); - } - - /** - * {@inheritdoc} - */ - public function __toString() - { - return $this->resource; - } - - /** - * @return string The file path to the resource - */ - public function getResource() - { - return $this->resource; - } - - /** - * {@inheritdoc} - */ - public function isFresh($timestamp) - { - return file_exists($this->resource) === $this->exists; - } - - /** - * {@inheritdoc} - */ - public function serialize() - { - return serialize(array($this->resource, $this->exists)); - } - - /** - * {@inheritdoc} - */ - public function unserialize($serialized) - { - list($this->resource, $this->exists) = unserialize($serialized); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Resource/FileResource.php b/core/src/core/classes/guzzle/vendor/symfony/config/Resource/FileResource.php deleted file mode 100644 index 6a06dcd609..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Resource/FileResource.php +++ /dev/null @@ -1,81 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Resource; - -/** - * FileResource represents a resource stored on the filesystem. - * - * The resource can be a file or a directory. - * - * @author Fabien Potencier - */ -class FileResource implements SelfCheckingResourceInterface, \Serializable -{ - /** - * @var string|false - */ - private $resource; - - /** - * Constructor. - * - * @param string $resource The file path to the resource - * - * @throws \InvalidArgumentException - */ - public function __construct($resource) - { - $this->resource = realpath($resource); - - if (false === $this->resource && file_exists($resource)) { - $this->resource = $resource; - } - - if (false === $this->resource) { - throw new \InvalidArgumentException(sprintf('The file "%s" does not exist.', $resource)); - } - } - - /** - * {@inheritdoc} - */ - public function __toString() - { - return $this->resource; - } - - /** - * @return string The canonicalized, absolute path to the resource - */ - public function getResource() - { - return $this->resource; - } - - /** - * {@inheritdoc} - */ - public function isFresh($timestamp) - { - return file_exists($this->resource) && @filemtime($this->resource) <= $timestamp; - } - - public function serialize() - { - return serialize($this->resource); - } - - public function unserialize($serialized) - { - $this->resource = unserialize($serialized); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Resource/ResourceInterface.php b/core/src/core/classes/guzzle/vendor/symfony/config/Resource/ResourceInterface.php deleted file mode 100644 index d98fd427a2..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Resource/ResourceInterface.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Resource; - -/** - * ResourceInterface is the interface that must be implemented by all Resource classes. - * - * @author Fabien Potencier - */ -interface ResourceInterface -{ - /** - * Returns a string representation of the Resource. - * - * This method is necessary to allow for resource de-duplication, for example by means - * of array_unique(). The string returned need not have a particular meaning, but has - * to be identical for different ResourceInterface instances referring to the same - * resource; and it should be unlikely to collide with that of other, unrelated - * resource instances. - * - * @return string A string representation unique to the underlying Resource - */ - public function __toString(); -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Resource/SelfCheckingResourceChecker.php b/core/src/core/classes/guzzle/vendor/symfony/config/Resource/SelfCheckingResourceChecker.php deleted file mode 100644 index d72203bc1a..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Resource/SelfCheckingResourceChecker.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Resource; - -use Symfony\Component\Config\ResourceCheckerInterface; - -/** - * Resource checker for instances of SelfCheckingResourceInterface. - * - * As these resources perform the actual check themselves, we can provide - * this class as a standard way of validating them. - * - * @author Matthias Pigulla - */ -class SelfCheckingResourceChecker implements ResourceCheckerInterface -{ - public function supports(ResourceInterface $metadata) - { - return $metadata instanceof SelfCheckingResourceInterface; - } - - public function isFresh(ResourceInterface $resource, $timestamp) - { - /* @var SelfCheckingResourceInterface $resource */ - return $resource->isFresh($timestamp); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Resource/SelfCheckingResourceInterface.php b/core/src/core/classes/guzzle/vendor/symfony/config/Resource/SelfCheckingResourceInterface.php deleted file mode 100644 index b3260f2be3..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Resource/SelfCheckingResourceInterface.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Resource; - -/** - * Interface for Resources that can check for freshness autonomously, - * without special support from external services. - * - * @author Matthias Pigulla - */ -interface SelfCheckingResourceInterface extends ResourceInterface -{ - /** - * Returns true if the resource has not been updated since the given timestamp. - * - * @param int $timestamp The last time the resource was loaded - * - * @return bool True if the resource has not been updated, false otherwise - */ - public function isFresh($timestamp); -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/ResourceCheckerConfigCache.php b/core/src/core/classes/guzzle/vendor/symfony/config/ResourceCheckerConfigCache.php deleted file mode 100644 index 3cef781907..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/ResourceCheckerConfigCache.php +++ /dev/null @@ -1,140 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config; - -use Symfony\Component\Config\Resource\ResourceInterface; -use Symfony\Component\Filesystem\Exception\IOException; -use Symfony\Component\Filesystem\Filesystem; - -/** - * ResourceCheckerConfigCache uses instances of ResourceCheckerInterface - * to check whether cached data is still fresh. - * - * @author Matthias Pigulla - */ -class ResourceCheckerConfigCache implements ConfigCacheInterface -{ - /** - * @var string - */ - private $file; - - /** - * @var ResourceCheckerInterface[] - */ - private $resourceCheckers; - - /** - * @param string $file The absolute cache path - * @param ResourceCheckerInterface[] $resourceCheckers The ResourceCheckers to use for the freshness check - */ - public function __construct($file, array $resourceCheckers = array()) - { - $this->file = $file; - $this->resourceCheckers = $resourceCheckers; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return $this->file; - } - - /** - * Checks if the cache is still fresh. - * - * This implementation will make a decision solely based on the ResourceCheckers - * passed in the constructor. - * - * The first ResourceChecker that supports a given resource is considered authoritative. - * Resources with no matching ResourceChecker will silently be ignored and considered fresh. - * - * @return bool true if the cache is fresh, false otherwise - */ - public function isFresh() - { - if (!is_file($this->file)) { - return false; - } - - if (!$this->resourceCheckers) { - return true; // shortcut - if we don't have any checkers we don't need to bother with the meta file at all - } - - $metadata = $this->getMetaFile(); - if (!is_file($metadata)) { - return true; - } - - $time = filemtime($this->file); - $meta = unserialize(file_get_contents($metadata)); - - foreach ($meta as $resource) { - /* @var ResourceInterface $resource */ - foreach ($this->resourceCheckers as $checker) { - if (!$checker->supports($resource)) { - continue; // next checker - } - if ($checker->isFresh($resource, $time)) { - break; // no need to further check this resource - } - - return false; // cache is stale - } - // no suitable checker found, ignore this resource - } - - return true; - } - - /** - * Writes cache. - * - * @param string $content The content to write in the cache - * @param ResourceInterface[] $metadata An array of metadata - * - * @throws \RuntimeException When cache file can't be written - */ - public function write($content, array $metadata = null) - { - $mode = 0666; - $umask = umask(); - $filesystem = new Filesystem(); - $filesystem->dumpFile($this->file, $content, null); - try { - $filesystem->chmod($this->file, $mode, $umask); - } catch (IOException $e) { - // discard chmod failure (some filesystem may not support it) - } - - if (null !== $metadata) { - $filesystem->dumpFile($this->getMetaFile(), serialize($metadata), null); - try { - $filesystem->chmod($this->getMetaFile(), $mode, $umask); - } catch (IOException $e) { - // discard chmod failure (some filesystem may not support it) - } - } - } - - /** - * Gets the meta file path. - * - * @return string The meta file path - */ - private function getMetaFile() - { - return $this->file.'.meta'; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/ResourceCheckerConfigCacheFactory.php b/core/src/core/classes/guzzle/vendor/symfony/config/ResourceCheckerConfigCacheFactory.php deleted file mode 100644 index 61d732cc1c..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/ResourceCheckerConfigCacheFactory.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config; - -/** - * A ConfigCacheFactory implementation that validates the - * cache with an arbitrary set of ResourceCheckers. - * - * @author Matthias Pigulla - */ -class ResourceCheckerConfigCacheFactory implements ConfigCacheFactoryInterface -{ - /** - * @var ResourceCheckerInterface[] - */ - private $resourceCheckers = array(); - - /** - * @param ResourceCheckerInterface[] $resourceCheckers - */ - public function __construct(array $resourceCheckers = array()) - { - $this->resourceCheckers = $resourceCheckers; - } - - /** - * {@inheritdoc} - */ - public function cache($file, $callback) - { - if (!is_callable($callback)) { - throw new \InvalidArgumentException(sprintf('Invalid type for callback argument. Expected callable, but got "%s".', gettype($callback))); - } - - $cache = new ResourceCheckerConfigCache($file, $this->resourceCheckers); - if (!$cache->isFresh()) { - call_user_func($callback, $cache); - } - - return $cache; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/ResourceCheckerInterface.php b/core/src/core/classes/guzzle/vendor/symfony/config/ResourceCheckerInterface.php deleted file mode 100644 index 612d777864..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/ResourceCheckerInterface.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config; - -use Symfony\Component\Config\Resource\ResourceInterface; - -/** - * Interface for ResourceCheckers. - * - * When a ResourceCheckerConfigCache instance is checked for freshness, all its associated - * metadata resources are passed to ResourceCheckers. The ResourceCheckers - * can then inspect the resources and decide whether the cache can be considered - * fresh or not. - * - * @author Matthias Pigulla - * @author Benjamin Klotz - */ -interface ResourceCheckerInterface -{ - /** - * Queries the ResourceChecker whether it can validate a given - * resource or not. - * - * @param ResourceInterface $metadata The resource to be checked for freshness - * - * @return bool True if the ResourceChecker can handle this resource type, false if not - */ - public function supports(ResourceInterface $metadata); - - /** - * Validates the resource. - * - * @param ResourceInterface $resource The resource to be validated - * @param int $timestamp The timestamp at which the cache associated with this resource was created - * - * @return bool True if the resource has not changed since the given timestamp, false otherwise - */ - public function isFresh(ResourceInterface $resource, $timestamp); -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/ConfigCacheFactoryTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/ConfigCacheFactoryTest.php deleted file mode 100644 index 291243dead..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/ConfigCacheFactoryTest.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests; - -use Symfony\Component\Config\ConfigCacheFactory; - -class ConfigCacheFactoryTest extends \PHPUnit_Framework_TestCase -{ - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Invalid type for callback argument. Expected callable, but got "object". - */ - public function testCachWithInvalidCallback() - { - $cacheFactory = new ConfigCacheFactory(true); - - $cacheFactory->cache('file', new \stdClass()); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/ConfigCacheTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/ConfigCacheTest.php deleted file mode 100644 index faa37f0e9d..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/ConfigCacheTest.php +++ /dev/null @@ -1,98 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests; - -use Symfony\Component\Config\ConfigCache; -use Symfony\Component\Config\Tests\Resource\ResourceStub; - -class ConfigCacheTest extends \PHPUnit_Framework_TestCase -{ - private $cacheFile = null; - - protected function setUp() - { - $this->cacheFile = tempnam(sys_get_temp_dir(), 'config_'); - } - - protected function tearDown() - { - $files = array($this->cacheFile, $this->cacheFile.'.meta'); - - foreach ($files as $file) { - if (file_exists($file)) { - unlink($file); - } - } - } - - /** - * @dataProvider debugModes - */ - public function testCacheIsNotValidIfNothingHasBeenCached($debug) - { - unlink($this->cacheFile); // remove tempnam() side effect - $cache = new ConfigCache($this->cacheFile, $debug); - - $this->assertFalse($cache->isFresh()); - } - - public function testIsAlwaysFreshInProduction() - { - $staleResource = new ResourceStub(); - $staleResource->setFresh(false); - - $cache = new ConfigCache($this->cacheFile, false); - $cache->write('', array($staleResource)); - - $this->assertTrue($cache->isFresh()); - } - - /** - * @dataProvider debugModes - */ - public function testIsFreshWhenNoResourceProvided($debug) - { - $cache = new ConfigCache($this->cacheFile, $debug); - $cache->write('', array()); - $this->assertTrue($cache->isFresh()); - } - - public function testFreshResourceInDebug() - { - $freshResource = new ResourceStub(); - $freshResource->setFresh(true); - - $cache = new ConfigCache($this->cacheFile, true); - $cache->write('', array($freshResource)); - - $this->assertTrue($cache->isFresh()); - } - - public function testStaleResourceInDebug() - { - $staleResource = new ResourceStub(); - $staleResource->setFresh(false); - - $cache = new ConfigCache($this->cacheFile, true); - $cache->write('', array($staleResource)); - - $this->assertFalse($cache->isFresh()); - } - - public function debugModes() - { - return array( - array(true), - array(false), - ); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/ArrayNodeTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/ArrayNodeTest.php deleted file mode 100644 index 993941f9a6..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/ArrayNodeTest.php +++ /dev/null @@ -1,176 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition; - -use Symfony\Component\Config\Definition\ArrayNode; -use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; -use Symfony\Component\Config\Definition\ScalarNode; - -class ArrayNodeTest extends \PHPUnit_Framework_TestCase -{ - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException - */ - public function testNormalizeThrowsExceptionWhenFalseIsNotAllowed() - { - $node = new ArrayNode('root'); - $node->normalize(false); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - * @expectedExceptionMessage Unrecognized option "foo" under "root" - */ - public function testExceptionThrownOnUnrecognizedChild() - { - $node = new ArrayNode('root'); - $node->normalize(array('foo' => 'bar')); - } - - public function ignoreAndRemoveMatrixProvider() - { - $unrecognizedOptionException = new InvalidConfigurationException('Unrecognized option "foo" under "root"'); - - return array( - array(true, true, array(), 'no exception is thrown for an unrecognized child if the ignoreExtraKeys option is set to true'), - array(true, false, array('foo' => 'bar'), 'extra keys are not removed when ignoreExtraKeys second option is set to false'), - array(false, true, $unrecognizedOptionException), - array(false, false, $unrecognizedOptionException), - ); - } - - /** - * @dataProvider ignoreAndRemoveMatrixProvider - */ - public function testIgnoreAndRemoveBehaviors($ignore, $remove, $expected, $message = '') - { - if ($expected instanceof \Exception) { - $this->setExpectedException(get_class($expected), $expected->getMessage()); - } - $node = new ArrayNode('root'); - $node->setIgnoreExtraKeys($ignore, $remove); - $result = $node->normalize(array('foo' => 'bar')); - $this->assertSame($expected, $result, $message); - } - - /** - * @dataProvider getPreNormalizationTests - */ - public function testPreNormalize($denormalized, $normalized) - { - $node = new ArrayNode('foo'); - - $r = new \ReflectionMethod($node, 'preNormalize'); - $r->setAccessible(true); - - $this->assertSame($normalized, $r->invoke($node, $denormalized)); - } - - public function getPreNormalizationTests() - { - return array( - array( - array('foo-bar' => 'foo'), - array('foo_bar' => 'foo'), - ), - array( - array('foo-bar_moo' => 'foo'), - array('foo-bar_moo' => 'foo'), - ), - array( - array('anything-with-dash-and-no-underscore' => 'first', 'no_dash' => 'second'), - array('anything_with_dash_and_no_underscore' => 'first', 'no_dash' => 'second'), - ), - array( - array('foo-bar' => null, 'foo_bar' => 'foo'), - array('foo-bar' => null, 'foo_bar' => 'foo'), - ), - ); - } - - /** - * @dataProvider getZeroNamedNodeExamplesData - */ - public function testNodeNameCanBeZero($denormalized, $normalized) - { - $zeroNode = new ArrayNode(0); - $zeroNode->addChild(new ScalarNode('name')); - $fiveNode = new ArrayNode(5); - $fiveNode->addChild(new ScalarNode(0)); - $fiveNode->addChild(new ScalarNode('new_key')); - $rootNode = new ArrayNode('root'); - $rootNode->addChild($zeroNode); - $rootNode->addChild($fiveNode); - $rootNode->addChild(new ScalarNode('string_key')); - $r = new \ReflectionMethod($rootNode, 'normalizeValue'); - $r->setAccessible(true); - - $this->assertSame($normalized, $r->invoke($rootNode, $denormalized)); - } - - public function getZeroNamedNodeExamplesData() - { - return array( - array( - array( - 0 => array( - 'name' => 'something', - ), - 5 => array( - 0 => 'this won\'t work too', - 'new_key' => 'some other value', - ), - 'string_key' => 'just value', - ), - array( - 0 => array( - 'name' => 'something', - ), - 5 => array( - 0 => 'this won\'t work too', - 'new_key' => 'some other value', - ), - 'string_key' => 'just value', - ), - ), - ); - } - - /** - * @dataProvider getPreNormalizedNormalizedOrderedData - */ - public function testChildrenOrderIsMaintainedOnNormalizeValue($prenormalized, $normalized) - { - $scalar1 = new ScalarNode('1'); - $scalar2 = new ScalarNode('2'); - $scalar3 = new ScalarNode('3'); - $node = new ArrayNode('foo'); - $node->addChild($scalar1); - $node->addChild($scalar3); - $node->addChild($scalar2); - - $r = new \ReflectionMethod($node, 'normalizeValue'); - $r->setAccessible(true); - - $this->assertSame($normalized, $r->invoke($node, $prenormalized)); - } - - public function getPreNormalizedNormalizedOrderedData() - { - return array( - array( - array('2' => 'two', '1' => 'one', '3' => 'three'), - array('2' => 'two', '1' => 'one', '3' => 'three'), - ), - ); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/BooleanNodeTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/BooleanNodeTest.php deleted file mode 100644 index b0cb079e96..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/BooleanNodeTest.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition; - -use Symfony\Component\Config\Definition\BooleanNode; - -class BooleanNodeTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider getValidValues - */ - public function testNormalize($value) - { - $node = new BooleanNode('test'); - $this->assertSame($value, $node->normalize($value)); - } - - /** - * @dataProvider getValidValues - * - * @param bool $value - */ - public function testValidNonEmptyValues($value) - { - $node = new BooleanNode('test'); - $node->setAllowEmptyValue(false); - - $this->assertSame($value, $node->finalize($value)); - } - - public function getValidValues() - { - return array( - array(false), - array(true), - ); - } - - /** - * @dataProvider getInvalidValues - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException - */ - public function testNormalizeThrowsExceptionOnInvalidValues($value) - { - $node = new BooleanNode('test'); - $node->normalize($value); - } - - public function getInvalidValues() - { - return array( - array(null), - array(''), - array('foo'), - array(0), - array(1), - array(0.0), - array(0.1), - array(array()), - array(array('foo' => 'bar')), - array(new \stdClass()), - ); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php deleted file mode 100644 index e75ed34f46..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php +++ /dev/null @@ -1,207 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition\Builder; - -use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; -use Symfony\Component\Config\Definition\Processor; -use Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition; -use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; - -class ArrayNodeDefinitionTest extends \PHPUnit_Framework_TestCase -{ - public function testAppendingSomeNode() - { - $parent = new ArrayNodeDefinition('root'); - $child = new ScalarNodeDefinition('child'); - - $parent - ->children() - ->scalarNode('foo')->end() - ->scalarNode('bar')->end() - ->end() - ->append($child); - - $this->assertCount(3, $this->getField($parent, 'children')); - $this->assertTrue(in_array($child, $this->getField($parent, 'children'))); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException - * @dataProvider providePrototypeNodeSpecificCalls - */ - public function testPrototypeNodeSpecificOption($method, $args) - { - $node = new ArrayNodeDefinition('root'); - - call_user_func_array(array($node, $method), $args); - - $node->getNode(); - } - - public function providePrototypeNodeSpecificCalls() - { - return array( - array('defaultValue', array(array())), - array('addDefaultChildrenIfNoneSet', array()), - array('requiresAtLeastOneElement', array()), - array('useAttributeAsKey', array('foo')), - ); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException - */ - public function testConcreteNodeSpecificOption() - { - $node = new ArrayNodeDefinition('root'); - $node - ->addDefaultsIfNotSet() - ->prototype('array') - ; - $node->getNode(); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException - */ - public function testPrototypeNodesCantHaveADefaultValueWhenUsingDefaultChildren() - { - $node = new ArrayNodeDefinition('root'); - $node - ->defaultValue(array()) - ->addDefaultChildrenIfNoneSet('foo') - ->prototype('array') - ; - $node->getNode(); - } - - public function testPrototypedArrayNodeDefaultWhenUsingDefaultChildren() - { - $node = new ArrayNodeDefinition('root'); - $node - ->addDefaultChildrenIfNoneSet() - ->prototype('array') - ; - $tree = $node->getNode(); - $this->assertEquals(array(array()), $tree->getDefaultValue()); - } - - /** - * @dataProvider providePrototypedArrayNodeDefaults - */ - public function testPrototypedArrayNodeDefault($args, $shouldThrowWhenUsingAttrAsKey, $shouldThrowWhenNotUsingAttrAsKey, $defaults) - { - $node = new ArrayNodeDefinition('root'); - $node - ->addDefaultChildrenIfNoneSet($args) - ->prototype('array') - ; - - try { - $tree = $node->getNode(); - $this->assertFalse($shouldThrowWhenNotUsingAttrAsKey); - $this->assertEquals($defaults, $tree->getDefaultValue()); - } catch (InvalidDefinitionException $e) { - $this->assertTrue($shouldThrowWhenNotUsingAttrAsKey); - } - - $node = new ArrayNodeDefinition('root'); - $node - ->useAttributeAsKey('attr') - ->addDefaultChildrenIfNoneSet($args) - ->prototype('array') - ; - - try { - $tree = $node->getNode(); - $this->assertFalse($shouldThrowWhenUsingAttrAsKey); - $this->assertEquals($defaults, $tree->getDefaultValue()); - } catch (InvalidDefinitionException $e) { - $this->assertTrue($shouldThrowWhenUsingAttrAsKey); - } - } - - public function providePrototypedArrayNodeDefaults() - { - return array( - array(null, true, false, array(array())), - array(2, true, false, array(array(), array())), - array('2', false, true, array('2' => array())), - array('foo', false, true, array('foo' => array())), - array(array('foo'), false, true, array('foo' => array())), - array(array('foo', 'bar'), false, true, array('foo' => array(), 'bar' => array())), - ); - } - - public function testNestedPrototypedArrayNodes() - { - $node = new ArrayNodeDefinition('root'); - $node - ->addDefaultChildrenIfNoneSet() - ->prototype('array') - ->prototype('array') - ; - $node->getNode(); - } - - public function testEnabledNodeDefaults() - { - $node = new ArrayNodeDefinition('root'); - $node - ->canBeEnabled() - ->children() - ->scalarNode('foo')->defaultValue('bar')->end() - ; - - $this->assertEquals(array('enabled' => false, 'foo' => 'bar'), $node->getNode()->getDefaultValue()); - } - - /** - * @dataProvider getEnableableNodeFixtures - */ - public function testTrueEnableEnabledNode($expected, $config, $message) - { - $processor = new Processor(); - $node = new ArrayNodeDefinition('root'); - $node - ->canBeEnabled() - ->children() - ->scalarNode('foo')->defaultValue('bar')->end() - ; - - $this->assertEquals( - $expected, - $processor->process($node->getNode(), $config), - $message - ); - } - - public function getEnableableNodeFixtures() - { - return array( - array(array('enabled' => true, 'foo' => 'bar'), array(true), 'true enables an enableable node'), - array(array('enabled' => true, 'foo' => 'bar'), array(null), 'null enables an enableable node'), - array(array('enabled' => true, 'foo' => 'bar'), array(array('enabled' => true)), 'An enableable node can be enabled'), - array(array('enabled' => true, 'foo' => 'baz'), array(array('foo' => 'baz')), 'any configuration enables an enableable node'), - array(array('enabled' => false, 'foo' => 'baz'), array(array('foo' => 'baz', 'enabled' => false)), 'An enableable node can be disabled'), - array(array('enabled' => false, 'foo' => 'bar'), array(false), 'false disables an enableable node'), - ); - } - - protected function getField($object, $field) - { - $reflection = new \ReflectionProperty($object, $field); - $reflection->setAccessible(true); - - return $reflection->getValue($object); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Builder/EnumNodeDefinitionTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Builder/EnumNodeDefinitionTest.php deleted file mode 100644 index 8d7ab2f7f0..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Builder/EnumNodeDefinitionTest.php +++ /dev/null @@ -1,64 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition\Builder; - -use Symfony\Component\Config\Definition\Builder\EnumNodeDefinition; - -class EnumNodeDefinitionTest extends \PHPUnit_Framework_TestCase -{ - public function testWithOneValue() - { - $def = new EnumNodeDefinition('foo'); - $def->values(array('foo')); - - $node = $def->getNode(); - $this->assertEquals(array('foo'), $node->getValues()); - } - - public function testWithOneDistinctValue() - { - $def = new EnumNodeDefinition('foo'); - $def->values(array('foo', 'foo')); - - $node = $def->getNode(); - $this->assertEquals(array('foo'), $node->getValues()); - } - - /** - * @expectedException \RuntimeException - * @expectedExceptionMessage You must call ->values() on enum nodes. - */ - public function testNoValuesPassed() - { - $def = new EnumNodeDefinition('foo'); - $def->getNode(); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage ->values() must be called with at least one value. - */ - public function testWithNoValues() - { - $def = new EnumNodeDefinition('foo'); - $def->values(array()); - } - - public function testGetNode() - { - $def = new EnumNodeDefinition('foo'); - $def->values(array('foo', 'bar')); - - $node = $def->getNode(); - $this->assertEquals(array('foo', 'bar'), $node->getValues()); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Builder/ExprBuilderTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Builder/ExprBuilderTest.php deleted file mode 100644 index 147bf1324c..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Builder/ExprBuilderTest.php +++ /dev/null @@ -1,215 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition\Builder; - -use Symfony\Component\Config\Definition\Builder\TreeBuilder; - -class ExprBuilderTest extends \PHPUnit_Framework_TestCase -{ - public function testAlwaysExpression() - { - $test = $this->getTestBuilder() - ->always($this->returnClosure('new_value')) - ->end(); - - $this->assertFinalizedValueIs('new_value', $test); - } - - public function testIfTrueExpression() - { - $test = $this->getTestBuilder() - ->ifTrue() - ->then($this->returnClosure('new_value')) - ->end(); - $this->assertFinalizedValueIs('new_value', $test, array('key' => true)); - - $test = $this->getTestBuilder() - ->ifTrue(function ($v) { return true; }) - ->then($this->returnClosure('new_value')) - ->end(); - $this->assertFinalizedValueIs('new_value', $test); - - $test = $this->getTestBuilder() - ->ifTrue(function ($v) { return false; }) - ->then($this->returnClosure('new_value')) - ->end(); - $this->assertFinalizedValueIs('value', $test); - } - - public function testIfStringExpression() - { - $test = $this->getTestBuilder() - ->ifString() - ->then($this->returnClosure('new_value')) - ->end(); - $this->assertFinalizedValueIs('new_value', $test); - - $test = $this->getTestBuilder() - ->ifString() - ->then($this->returnClosure('new_value')) - ->end(); - $this->assertFinalizedValueIs(45, $test, array('key' => 45)); - } - - public function testIfNullExpression() - { - $test = $this->getTestBuilder() - ->ifNull() - ->then($this->returnClosure('new_value')) - ->end(); - $this->assertFinalizedValueIs('new_value', $test, array('key' => null)); - - $test = $this->getTestBuilder() - ->ifNull() - ->then($this->returnClosure('new_value')) - ->end(); - $this->assertFinalizedValueIs('value', $test); - } - - public function testIfArrayExpression() - { - $test = $this->getTestBuilder() - ->ifArray() - ->then($this->returnClosure('new_value')) - ->end(); - $this->assertFinalizedValueIs('new_value', $test, array('key' => array())); - - $test = $this->getTestBuilder() - ->ifArray() - ->then($this->returnClosure('new_value')) - ->end(); - $this->assertFinalizedValueIs('value', $test); - } - - public function testIfInArrayExpression() - { - $test = $this->getTestBuilder() - ->ifInArray(array('foo', 'bar', 'value')) - ->then($this->returnClosure('new_value')) - ->end(); - $this->assertFinalizedValueIs('new_value', $test); - - $test = $this->getTestBuilder() - ->ifInArray(array('foo', 'bar')) - ->then($this->returnClosure('new_value')) - ->end(); - $this->assertFinalizedValueIs('value', $test); - } - - public function testIfNotInArrayExpression() - { - $test = $this->getTestBuilder() - ->ifNotInArray(array('foo', 'bar')) - ->then($this->returnClosure('new_value')) - ->end(); - $this->assertFinalizedValueIs('new_value', $test); - - $test = $this->getTestBuilder() - ->ifNotInArray(array('foo', 'bar', 'value_from_config')) - ->then($this->returnClosure('new_value')) - ->end(); - $this->assertFinalizedValueIs('new_value', $test); - } - - public function testThenEmptyArrayExpression() - { - $test = $this->getTestBuilder() - ->ifString() - ->thenEmptyArray() - ->end(); - $this->assertFinalizedValueIs(array(), $test); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - */ - public function testThenInvalid() - { - $test = $this->getTestBuilder() - ->ifString() - ->thenInvalid('Invalid value') - ->end(); - $this->finalizeTestBuilder($test); - } - - public function testThenUnsetExpression() - { - $test = $this->getTestBuilder() - ->ifString() - ->thenUnset() - ->end(); - $this->assertEquals(array(), $this->finalizeTestBuilder($test)); - } - - /** - * Create a test treebuilder with a variable node, and init the validation. - * - * @return TreeBuilder - */ - protected function getTestBuilder() - { - $builder = new TreeBuilder(); - - return $builder - ->root('test') - ->children() - ->variableNode('key') - ->validate() - ; - } - - /** - * Close the validation process and finalize with the given config. - * - * @param TreeBuilder $testBuilder The tree builder to finalize - * @param array $config The config you want to use for the finalization, if nothing provided - * a simple array('key'=>'value') will be used - * - * @return array The finalized config values - */ - protected function finalizeTestBuilder($testBuilder, $config = null) - { - return $testBuilder - ->end() - ->end() - ->end() - ->buildTree() - ->finalize(null === $config ? array('key' => 'value') : $config) - ; - } - - /** - * Return a closure that will return the given value. - * - * @param mixed $val The value that the closure must return - * - * @return \Closure - */ - protected function returnClosure($val) - { - return function ($v) use ($val) { - return $val; - }; - } - - /** - * Assert that the given test builder, will return the given value. - * - * @param mixed $value The value to test - * @param TreeBuilder $treeBuilder The tree builder to finalize - * @param mixed $config The config values that new to be finalized - */ - protected function assertFinalizedValueIs($value, $treeBuilder, $config = null) - { - $this->assertEquals(array('key' => $value), $this->finalizeTestBuilder($treeBuilder, $config)); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Builder/NodeBuilderTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Builder/NodeBuilderTest.php deleted file mode 100644 index 22c399ca9d..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Builder/NodeBuilderTest.php +++ /dev/null @@ -1,94 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition\Builder; - -use Symfony\Component\Config\Definition\Builder\NodeBuilder as BaseNodeBuilder; -use Symfony\Component\Config\Definition\Builder\VariableNodeDefinition as BaseVariableNodeDefinition; - -class NodeBuilderTest extends \PHPUnit_Framework_TestCase -{ - /** - * @expectedException \RuntimeException - */ - public function testThrowsAnExceptionWhenTryingToCreateANonRegisteredNodeType() - { - $builder = new BaseNodeBuilder(); - $builder->node('', 'foobar'); - } - - /** - * @expectedException \RuntimeException - */ - public function testThrowsAnExceptionWhenTheNodeClassIsNotFound() - { - $builder = new BaseNodeBuilder(); - $builder - ->setNodeClass('noclasstype', '\\foo\\bar\\noclass') - ->node('', 'noclasstype'); - } - - public function testAddingANewNodeType() - { - $class = __NAMESPACE__.'\\SomeNodeDefinition'; - - $builder = new BaseNodeBuilder(); - $node = $builder - ->setNodeClass('newtype', $class) - ->node('', 'newtype'); - - $this->assertInstanceOf($class, $node); - } - - public function testOverridingAnExistingNodeType() - { - $class = __NAMESPACE__.'\\SomeNodeDefinition'; - - $builder = new BaseNodeBuilder(); - $node = $builder - ->setNodeClass('variable', $class) - ->node('', 'variable'); - - $this->assertInstanceOf($class, $node); - } - - public function testNodeTypesAreNotCaseSensitive() - { - $builder = new BaseNodeBuilder(); - - $node1 = $builder->node('', 'VaRiAbLe'); - $node2 = $builder->node('', 'variable'); - - $this->assertInstanceOf(get_class($node1), $node2); - - $builder->setNodeClass('CuStOm', __NAMESPACE__.'\\SomeNodeDefinition'); - - $node1 = $builder->node('', 'CUSTOM'); - $node2 = $builder->node('', 'custom'); - - $this->assertInstanceOf(get_class($node1), $node2); - } - - public function testNumericNodeCreation() - { - $builder = new BaseNodeBuilder(); - - $node = $builder->integerNode('foo')->min(3)->max(5); - $this->assertInstanceOf('Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition', $node); - - $node = $builder->floatNode('bar')->min(3.0)->max(5.0); - $this->assertInstanceOf('Symfony\Component\Config\Definition\Builder\FloatNodeDefinition', $node); - } -} - -class SomeNodeDefinition extends BaseVariableNodeDefinition -{ -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Builder/NumericNodeDefinitionTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Builder/NumericNodeDefinitionTest.php deleted file mode 100644 index 8f0cdbb4eb..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Builder/NumericNodeDefinitionTest.php +++ /dev/null @@ -1,103 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition\Builder; - -use Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition as NumericNodeDefinition; -use Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition; -use Symfony\Component\Config\Definition\Builder\FloatNodeDefinition; - -class NumericNodeDefinitionTest extends \PHPUnit_Framework_TestCase -{ - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage You cannot define a min(4) as you already have a max(3) - */ - public function testIncoherentMinAssertion() - { - $def = new NumericNodeDefinition('foo'); - $def->max(3)->min(4); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage You cannot define a max(2) as you already have a min(3) - */ - public function testIncoherentMaxAssertion() - { - $node = new NumericNodeDefinition('foo'); - $node->min(3)->max(2); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - * @expectedExceptionMessage The value 4 is too small for path "foo". Should be greater than or equal to 5 - */ - public function testIntegerMinAssertion() - { - $def = new IntegerNodeDefinition('foo'); - $def->min(5)->getNode()->finalize(4); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - * @expectedExceptionMessage The value 4 is too big for path "foo". Should be less than or equal to 3 - */ - public function testIntegerMaxAssertion() - { - $def = new IntegerNodeDefinition('foo'); - $def->max(3)->getNode()->finalize(4); - } - - public function testIntegerValidMinMaxAssertion() - { - $def = new IntegerNodeDefinition('foo'); - $node = $def->min(3)->max(7)->getNode(); - $this->assertEquals(4, $node->finalize(4)); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - * @expectedExceptionMessage The value 400 is too small for path "foo". Should be greater than or equal to 500 - */ - public function testFloatMinAssertion() - { - $def = new FloatNodeDefinition('foo'); - $def->min(5E2)->getNode()->finalize(4e2); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - * @expectedExceptionMessage The value 4.3 is too big for path "foo". Should be less than or equal to 0.3 - */ - public function testFloatMaxAssertion() - { - $def = new FloatNodeDefinition('foo'); - $def->max(0.3)->getNode()->finalize(4.3); - } - - public function testFloatValidMinMaxAssertion() - { - $def = new FloatNodeDefinition('foo'); - $node = $def->min(3.0)->max(7e2)->getNode(); - $this->assertEquals(4.5, $node->finalize(4.5)); - } - - /** - * @expectedException Symfony\Component\Config\Definition\Exception\InvalidDefinitionException - * @expectedExceptionMessage ->cannotBeEmpty() is not applicable to NumericNodeDefinition. - */ - public function testCannotBeEmptyThrowsAnException() - { - $def = new NumericNodeDefinition('foo'); - $def->cannotBeEmpty(); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Builder/TreeBuilderTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Builder/TreeBuilderTest.php deleted file mode 100644 index a146e89c32..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Builder/TreeBuilderTest.php +++ /dev/null @@ -1,126 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition\Builder; - -use Symfony\Component\Config\Tests\Definition\Builder\NodeBuilder as CustomNodeBuilder; -use Symfony\Component\Config\Definition\Builder\TreeBuilder; - -require __DIR__.'/../../Fixtures/Builder/NodeBuilder.php'; -require __DIR__.'/../../Fixtures/Builder/BarNodeDefinition.php'; -require __DIR__.'/../../Fixtures/Builder/VariableNodeDefinition.php'; - -class TreeBuilderTest extends \PHPUnit_Framework_TestCase -{ - public function testUsingACustomNodeBuilder() - { - $builder = new TreeBuilder(); - $root = $builder->root('custom', 'array', new CustomNodeBuilder()); - - $nodeBuilder = $root->children(); - - $this->assertInstanceOf('Symfony\Component\Config\Tests\Definition\Builder\NodeBuilder', $nodeBuilder); - - $nodeBuilder = $nodeBuilder->arrayNode('deeper')->children(); - - $this->assertInstanceOf('Symfony\Component\Config\Tests\Definition\Builder\NodeBuilder', $nodeBuilder); - } - - public function testOverrideABuiltInNodeType() - { - $builder = new TreeBuilder(); - $root = $builder->root('override', 'array', new CustomNodeBuilder()); - - $definition = $root->children()->variableNode('variable'); - - $this->assertInstanceOf('Symfony\Component\Config\Tests\Definition\Builder\VariableNodeDefinition', $definition); - } - - public function testAddANodeType() - { - $builder = new TreeBuilder(); - $root = $builder->root('override', 'array', new CustomNodeBuilder()); - - $definition = $root->children()->barNode('variable'); - - $this->assertInstanceOf('Symfony\Component\Config\Tests\Definition\Builder\BarNodeDefinition', $definition); - } - - public function testCreateABuiltInNodeTypeWithACustomNodeBuilder() - { - $builder = new TreeBuilder(); - $root = $builder->root('builtin', 'array', new CustomNodeBuilder()); - - $definition = $root->children()->booleanNode('boolean'); - - $this->assertInstanceOf('Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition', $definition); - } - - public function testPrototypedArrayNodeUseTheCustomNodeBuilder() - { - $builder = new TreeBuilder(); - $root = $builder->root('override', 'array', new CustomNodeBuilder()); - - $root->prototype('bar')->end(); - } - - public function testAnExtendedNodeBuilderGetsPropagatedToTheChildren() - { - $builder = new TreeBuilder(); - - $builder->root('propagation') - ->children() - ->setNodeClass('extended', 'Symfony\Component\Config\Tests\Definition\Builder\VariableNodeDefinition') - ->node('foo', 'extended')->end() - ->arrayNode('child') - ->children() - ->node('foo', 'extended') - ->end() - ->end() - ->end() - ->end(); - } - - public function testDefinitionInfoGetsTransferredToNode() - { - $builder = new TreeBuilder(); - - $builder->root('test')->info('root info') - ->children() - ->node('child', 'variable')->info('child info')->defaultValue('default') - ->end() - ->end(); - - $tree = $builder->buildTree(); - $children = $tree->getChildren(); - - $this->assertEquals('root info', $tree->getInfo()); - $this->assertEquals('child info', $children['child']->getInfo()); - } - - public function testDefinitionExampleGetsTransferredToNode() - { - $builder = new TreeBuilder(); - - $builder->root('test') - ->example(array('key' => 'value')) - ->children() - ->node('child', 'variable')->info('child info')->defaultValue('default')->example('example') - ->end() - ->end(); - - $tree = $builder->buildTree(); - $children = $tree->getChildren(); - - $this->assertInternalType('array', $tree->getExample()); - $this->assertEquals('example', $children['child']->getExample()); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Dumper/XmlReferenceDumperTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Dumper/XmlReferenceDumperTest.php deleted file mode 100644 index 6d9d52dd3a..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Dumper/XmlReferenceDumperTest.php +++ /dev/null @@ -1,83 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition\Dumper; - -use Symfony\Component\Config\Definition\Dumper\XmlReferenceDumper; -use Symfony\Component\Config\Tests\Fixtures\Configuration\ExampleConfiguration; - -class XmlReferenceDumperTest extends \PHPUnit_Framework_TestCase -{ - public function testDumper() - { - $configuration = new ExampleConfiguration(); - - $dumper = new XmlReferenceDumper(); - $this->assertEquals($this->getConfigurationAsString(), $dumper->dump($configuration)); - } - - public function testNamespaceDumper() - { - $configuration = new ExampleConfiguration(); - - $dumper = new XmlReferenceDumper(); - $this->assertEquals(str_replace('http://example.org/schema/dic/acme_root', 'http://symfony.com/schema/dic/symfony', $this->getConfigurationAsString()), $dumper->dump($configuration, 'http://symfony.com/schema/dic/symfony')); - } - - private function getConfigurationAsString() - { - return str_replace("\n", PHP_EOL, << - - - - - - - - - - - scalar value - - - - - - -EOL - ); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Dumper/YamlReferenceDumperTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Dumper/YamlReferenceDumperTest.php deleted file mode 100644 index 28d0cd6ad2..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/Dumper/YamlReferenceDumperTest.php +++ /dev/null @@ -1,68 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition\Dumper; - -use Symfony\Component\Config\Definition\Dumper\YamlReferenceDumper; -use Symfony\Component\Config\Tests\Fixtures\Configuration\ExampleConfiguration; - -class YamlReferenceDumperTest extends \PHPUnit_Framework_TestCase -{ - public function testDumper() - { - $configuration = new ExampleConfiguration(); - - $dumper = new YamlReferenceDumper(); - - $this->markTestIncomplete('The Yaml Dumper currently does not support prototyped arrays'); - $this->assertEquals($this->getConfigurationAsString(), $dumper->dump($configuration)); - } - - private function getConfigurationAsString() - { - return <<<'EOL' -acme_root: - boolean: true - scalar_empty: ~ - scalar_null: ~ - scalar_true: true - scalar_false: false - scalar_default: default - scalar_array_empty: [] - scalar_array_defaults: - - # Defaults: - - elem1 - - elem2 - scalar_required: ~ # Required - enum_with_default: this # One of "this"; "that" - enum: ~ # One of "this"; "that" - - # some info - array: - child1: ~ - child2: ~ - - # this is a long - # multi-line info text - # which should be indented - child3: ~ # Example: example setting - parameters: - - # Prototype: Parameter name - name: ~ - connections: - # Prototype - - { user: ~, pass: ~ } - -EOL; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/EnumNodeTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/EnumNodeTest.php deleted file mode 100644 index 654d5050dd..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/EnumNodeTest.php +++ /dev/null @@ -1,54 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition; - -use Symfony\Component\Config\Definition\EnumNode; - -class EnumNodeTest extends \PHPUnit_Framework_TestCase -{ - public function testFinalizeValue() - { - $node = new EnumNode('foo', null, array('foo', 'bar')); - $this->assertSame('foo', $node->finalize('foo')); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage $values must contain at least one element. - */ - public function testConstructionWithNoValues() - { - new EnumNode('foo', null, array()); - } - - public function testConstructionWithOneValue() - { - $node = new EnumNode('foo', null, array('foo')); - $this->assertSame('foo', $node->finalize('foo')); - } - - public function testConstructionWithOneDistinctValue() - { - $node = new EnumNode('foo', null, array('foo', 'foo')); - $this->assertSame('foo', $node->finalize('foo')); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - * @expectedExceptionMessage The value "foobar" is not allowed for path "foo". Permissible values: "foo", "bar" - */ - public function testFinalizeWithInvalidValue() - { - $node = new EnumNode('foo', null, array('foo', 'bar')); - $node->finalize('foobar'); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/FinalizationTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/FinalizationTest.php deleted file mode 100644 index 19fc347d8f..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/FinalizationTest.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition; - -use Symfony\Component\Config\Definition\Builder\TreeBuilder; -use Symfony\Component\Config\Definition\Processor; -use Symfony\Component\Config\Definition\NodeInterface; - -class FinalizationTest extends \PHPUnit_Framework_TestCase -{ - public function testUnsetKeyWithDeepHierarchy() - { - $tb = new TreeBuilder(); - $tree = $tb - ->root('config', 'array') - ->children() - ->node('level1', 'array') - ->canBeUnset() - ->children() - ->node('level2', 'array') - ->canBeUnset() - ->children() - ->node('somevalue', 'scalar')->end() - ->node('anothervalue', 'scalar')->end() - ->end() - ->end() - ->node('level1_scalar', 'scalar')->end() - ->end() - ->end() - ->end() - ->end() - ->buildTree() - ; - - $a = array( - 'level1' => array( - 'level2' => array( - 'somevalue' => 'foo', - 'anothervalue' => 'bar', - ), - 'level1_scalar' => 'foo', - ), - ); - - $b = array( - 'level1' => array( - 'level2' => false, - ), - ); - - $this->assertEquals(array( - 'level1' => array( - 'level1_scalar' => 'foo', - ), - ), $this->process($tree, array($a, $b))); - } - - protected function process(NodeInterface $tree, array $configs) - { - $processor = new Processor(); - - return $processor->process($tree, $configs); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/FloatNodeTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/FloatNodeTest.php deleted file mode 100644 index 84afd6c104..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/FloatNodeTest.php +++ /dev/null @@ -1,77 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition; - -use Symfony\Component\Config\Definition\FloatNode; - -class FloatNodeTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider getValidValues - */ - public function testNormalize($value) - { - $node = new FloatNode('test'); - $this->assertSame($value, $node->normalize($value)); - } - - /** - * @dataProvider getValidValues - * - * @param int $value - */ - public function testValidNonEmptyValues($value) - { - $node = new FloatNode('test'); - $node->setAllowEmptyValue(false); - - $this->assertSame($value, $node->finalize($value)); - } - - public function getValidValues() - { - return array( - array(1798.0), - array(-678.987), - array(12.56E45), - array(0.0), - // Integer are accepted too, they will be cast - array(17), - array(-10), - array(0), - ); - } - - /** - * @dataProvider getInvalidValues - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException - */ - public function testNormalizeThrowsExceptionOnInvalidValues($value) - { - $node = new FloatNode('test'); - $node->normalize($value); - } - - public function getInvalidValues() - { - return array( - array(null), - array(''), - array('foo'), - array(true), - array(false), - array(array()), - array(array('foo' => 'bar')), - array(new \stdClass()), - ); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/IntegerNodeTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/IntegerNodeTest.php deleted file mode 100644 index 58d2148599..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/IntegerNodeTest.php +++ /dev/null @@ -1,74 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition; - -use Symfony\Component\Config\Definition\IntegerNode; - -class IntegerNodeTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider getValidValues - */ - public function testNormalize($value) - { - $node = new IntegerNode('test'); - $this->assertSame($value, $node->normalize($value)); - } - - /** - * @dataProvider getValidValues - * - * @param int $value - */ - public function testValidNonEmptyValues($value) - { - $node = new IntegerNode('test'); - $node->setAllowEmptyValue(false); - - $this->assertSame($value, $node->finalize($value)); - } - - public function getValidValues() - { - return array( - array(1798), - array(-678), - array(0), - ); - } - - /** - * @dataProvider getInvalidValues - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException - */ - public function testNormalizeThrowsExceptionOnInvalidValues($value) - { - $node = new IntegerNode('test'); - $node->normalize($value); - } - - public function getInvalidValues() - { - return array( - array(null), - array(''), - array('foo'), - array(true), - array(false), - array(0.0), - array(0.1), - array(array()), - array(array('foo' => 'bar')), - array(new \stdClass()), - ); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/MergeTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/MergeTest.php deleted file mode 100644 index 08ddc3209e..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/MergeTest.php +++ /dev/null @@ -1,195 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition; - -use Symfony\Component\Config\Definition\Builder\TreeBuilder; - -class MergeTest extends \PHPUnit_Framework_TestCase -{ - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException - */ - public function testForbiddenOverwrite() - { - $tb = new TreeBuilder(); - $tree = $tb - ->root('root', 'array') - ->children() - ->node('foo', 'scalar') - ->cannotBeOverwritten() - ->end() - ->end() - ->end() - ->buildTree() - ; - - $a = array( - 'foo' => 'bar', - ); - - $b = array( - 'foo' => 'moo', - ); - - $tree->merge($a, $b); - } - - public function testUnsetKey() - { - $tb = new TreeBuilder(); - $tree = $tb - ->root('root', 'array') - ->children() - ->node('foo', 'scalar')->end() - ->node('bar', 'scalar')->end() - ->node('unsettable', 'array') - ->canBeUnset() - ->children() - ->node('foo', 'scalar')->end() - ->node('bar', 'scalar')->end() - ->end() - ->end() - ->node('unsetted', 'array') - ->canBeUnset() - ->prototype('scalar')->end() - ->end() - ->end() - ->end() - ->buildTree() - ; - - $a = array( - 'foo' => 'bar', - 'unsettable' => array( - 'foo' => 'a', - 'bar' => 'b', - ), - 'unsetted' => false, - ); - - $b = array( - 'foo' => 'moo', - 'bar' => 'b', - 'unsettable' => false, - 'unsetted' => array('a', 'b'), - ); - - $this->assertEquals(array( - 'foo' => 'moo', - 'bar' => 'b', - 'unsettable' => false, - 'unsetted' => array('a', 'b'), - ), $tree->merge($a, $b)); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - */ - public function testDoesNotAllowNewKeysInSubsequentConfigs() - { - $tb = new TreeBuilder(); - $tree = $tb - ->root('config', 'array') - ->children() - ->node('test', 'array') - ->disallowNewKeysInSubsequentConfigs() - ->useAttributeAsKey('key') - ->prototype('array') - ->children() - ->node('value', 'scalar')->end() - ->end() - ->end() - ->end() - ->end() - ->end() - ->buildTree(); - - $a = array( - 'test' => array( - 'a' => array('value' => 'foo'), - ), - ); - - $b = array( - 'test' => array( - 'b' => array('value' => 'foo'), - ), - ); - - $tree->merge($a, $b); - } - - public function testPerformsNoDeepMerging() - { - $tb = new TreeBuilder(); - - $tree = $tb - ->root('config', 'array') - ->children() - ->node('no_deep_merging', 'array') - ->performNoDeepMerging() - ->children() - ->node('foo', 'scalar')->end() - ->node('bar', 'scalar')->end() - ->end() - ->end() - ->end() - ->end() - ->buildTree() - ; - - $a = array( - 'no_deep_merging' => array( - 'foo' => 'a', - 'bar' => 'b', - ), - ); - - $b = array( - 'no_deep_merging' => array( - 'c' => 'd', - ), - ); - - $this->assertEquals(array( - 'no_deep_merging' => array( - 'c' => 'd', - ), - ), $tree->merge($a, $b)); - } - - public function testPrototypeWithoutAKeyAttribute() - { - $tb = new TreeBuilder(); - - $tree = $tb - ->root('config', 'array') - ->children() - ->arrayNode('append_elements') - ->prototype('scalar')->end() - ->end() - ->end() - ->end() - ->buildTree() - ; - - $a = array( - 'append_elements' => array('a', 'b'), - ); - - $b = array( - 'append_elements' => array('c', 'd'), - ); - - $this->assertEquals(array('append_elements' => array('a', 'b', 'c', 'd')), $tree->merge($a, $b)); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/NormalizationTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/NormalizationTest.php deleted file mode 100644 index a896f96221..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/NormalizationTest.php +++ /dev/null @@ -1,229 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition; - -use Symfony\Component\Config\Definition\NodeInterface; -use Symfony\Component\Config\Definition\Builder\TreeBuilder; - -class NormalizationTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider getEncoderTests - */ - public function testNormalizeEncoders($denormalized) - { - $tb = new TreeBuilder(); - $tree = $tb - ->root('root_name', 'array') - ->fixXmlConfig('encoder') - ->children() - ->node('encoders', 'array') - ->useAttributeAsKey('class') - ->prototype('array') - ->beforeNormalization()->ifString()->then(function ($v) { return array('algorithm' => $v); })->end() - ->children() - ->node('algorithm', 'scalar')->end() - ->end() - ->end() - ->end() - ->end() - ->end() - ->buildTree() - ; - - $normalized = array( - 'encoders' => array( - 'foo' => array('algorithm' => 'plaintext'), - ), - ); - - $this->assertNormalized($tree, $denormalized, $normalized); - } - - public function getEncoderTests() - { - $configs = array(); - - // XML - $configs[] = array( - 'encoder' => array( - array('class' => 'foo', 'algorithm' => 'plaintext'), - ), - ); - - // XML when only one element of this type - $configs[] = array( - 'encoder' => array('class' => 'foo', 'algorithm' => 'plaintext'), - ); - - // YAML/PHP - $configs[] = array( - 'encoders' => array( - array('class' => 'foo', 'algorithm' => 'plaintext'), - ), - ); - - // YAML/PHP - $configs[] = array( - 'encoders' => array( - 'foo' => 'plaintext', - ), - ); - - // YAML/PHP - $configs[] = array( - 'encoders' => array( - 'foo' => array('algorithm' => 'plaintext'), - ), - ); - - return array_map(function ($v) { - return array($v); - }, $configs); - } - - /** - * @dataProvider getAnonymousKeysTests - */ - public function testAnonymousKeysArray($denormalized) - { - $tb = new TreeBuilder(); - $tree = $tb - ->root('root', 'array') - ->children() - ->node('logout', 'array') - ->fixXmlConfig('handler') - ->children() - ->node('handlers', 'array') - ->prototype('scalar')->end() - ->end() - ->end() - ->end() - ->end() - ->end() - ->buildTree() - ; - - $normalized = array('logout' => array('handlers' => array('a', 'b', 'c'))); - - $this->assertNormalized($tree, $denormalized, $normalized); - } - - public function getAnonymousKeysTests() - { - $configs = array(); - - $configs[] = array( - 'logout' => array( - 'handlers' => array('a', 'b', 'c'), - ), - ); - - $configs[] = array( - 'logout' => array( - 'handler' => array('a', 'b', 'c'), - ), - ); - - return array_map(function ($v) { return array($v); }, $configs); - } - - /** - * @dataProvider getNumericKeysTests - */ - public function testNumericKeysAsAttributes($denormalized) - { - $normalized = array( - 'thing' => array(42 => array('foo', 'bar'), 1337 => array('baz', 'qux')), - ); - - $this->assertNormalized($this->getNumericKeysTestTree(), $denormalized, $normalized); - } - - public function getNumericKeysTests() - { - $configs = array(); - - $configs[] = array( - 'thing' => array( - 42 => array('foo', 'bar'), 1337 => array('baz', 'qux'), - ), - ); - - $configs[] = array( - 'thing' => array( - array('foo', 'bar', 'id' => 42), array('baz', 'qux', 'id' => 1337), - ), - ); - - return array_map(function ($v) { return array($v); }, $configs); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - * @expectedExceptionMessage The attribute "id" must be set for path "root.thing". - */ - public function testNonAssociativeArrayThrowsExceptionIfAttributeNotSet() - { - $denormalized = array( - 'thing' => array( - array('foo', 'bar'), array('baz', 'qux'), - ), - ); - - $this->assertNormalized($this->getNumericKeysTestTree(), $denormalized, array()); - } - - public function testAssociativeArrayPreserveKeys() - { - $tb = new TreeBuilder(); - $tree = $tb - ->root('root', 'array') - ->prototype('array') - ->children() - ->node('foo', 'scalar')->end() - ->end() - ->end() - ->end() - ->buildTree() - ; - - $data = array('first' => array('foo' => 'bar')); - - $this->assertNormalized($tree, $data, $data); - } - - public static function assertNormalized(NodeInterface $tree, $denormalized, $normalized) - { - self::assertSame($normalized, $tree->normalize($denormalized)); - } - - private function getNumericKeysTestTree() - { - $tb = new TreeBuilder(); - $tree = $tb - ->root('root', 'array') - ->children() - ->node('thing', 'array') - ->useAttributeAsKey('id') - ->prototype('array') - ->prototype('scalar')->end() - ->end() - ->end() - ->end() - ->end() - ->buildTree() - ; - - return $tree; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/PrototypedArrayNodeTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/PrototypedArrayNodeTest.php deleted file mode 100644 index c343fcfd94..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/PrototypedArrayNodeTest.php +++ /dev/null @@ -1,180 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition; - -use Symfony\Component\Config\Definition\PrototypedArrayNode; -use Symfony\Component\Config\Definition\ArrayNode; -use Symfony\Component\Config\Definition\ScalarNode; - -class PrototypedArrayNodeTest extends \PHPUnit_Framework_TestCase -{ - public function testGetDefaultValueReturnsAnEmptyArrayForPrototypes() - { - $node = new PrototypedArrayNode('root'); - $prototype = new ArrayNode(null, $node); - $node->setPrototype($prototype); - $this->assertEmpty($node->getDefaultValue()); - } - - public function testGetDefaultValueReturnsDefaultValueForPrototypes() - { - $node = new PrototypedArrayNode('root'); - $prototype = new ArrayNode(null, $node); - $node->setPrototype($prototype); - $node->setDefaultValue(array('test')); - $this->assertEquals(array('test'), $node->getDefaultValue()); - } - - // a remapped key (e.g. "mapping" -> "mappings") should be unset after being used - public function testRemappedKeysAreUnset() - { - $node = new ArrayNode('root'); - $mappingsNode = new PrototypedArrayNode('mappings'); - $node->addChild($mappingsNode); - - // each item under mappings is just a scalar - $prototype = new ScalarNode(null, $mappingsNode); - $mappingsNode->setPrototype($prototype); - - $remappings = array(); - $remappings[] = array('mapping', 'mappings'); - $node->setXmlRemappings($remappings); - - $normalized = $node->normalize(array('mapping' => array('foo', 'bar'))); - $this->assertEquals(array('mappings' => array('foo', 'bar')), $normalized); - } - - /** - * Tests that when a key attribute is mapped, that key is removed from the array. - * - * - * - * - * The above should finally be mapped to an array that looks like this - * (because "id" is the key attribute). - * - * array( - * 'things' => array( - * 'option1' => 'foo', - * 'option2' => 'bar', - * ) - * ) - */ - public function testMappedAttributeKeyIsRemoved() - { - $node = new PrototypedArrayNode('root'); - $node->setKeyAttribute('id', true); - - // each item under the root is an array, with one scalar item - $prototype = new ArrayNode(null, $node); - $prototype->addChild(new ScalarNode('foo')); - $node->setPrototype($prototype); - - $children = array(); - $children[] = array('id' => 'item_name', 'foo' => 'bar'); - $normalized = $node->normalize($children); - - $expected = array(); - $expected['item_name'] = array('foo' => 'bar'); - $this->assertEquals($expected, $normalized); - } - - /** - * Tests the opposite of the testMappedAttributeKeyIsRemoved because - * the removal can be toggled with an option. - */ - public function testMappedAttributeKeyNotRemoved() - { - $node = new PrototypedArrayNode('root'); - $node->setKeyAttribute('id', false); - - // each item under the root is an array, with two scalar items - $prototype = new ArrayNode(null, $node); - $prototype->addChild(new ScalarNode('foo')); - $prototype->addChild(new ScalarNode('id')); // the key attribute will remain - $node->setPrototype($prototype); - - $children = array(); - $children[] = array('id' => 'item_name', 'foo' => 'bar'); - $normalized = $node->normalize($children); - - $expected = array(); - $expected['item_name'] = array('id' => 'item_name', 'foo' => 'bar'); - $this->assertEquals($expected, $normalized); - } - - public function testAddDefaultChildren() - { - $node = $this->getPrototypeNodeWithDefaultChildren(); - $node->setAddChildrenIfNoneSet(); - $this->assertTrue($node->hasDefaultValue()); - $this->assertEquals(array(array('foo' => 'bar')), $node->getDefaultValue()); - - $node = $this->getPrototypeNodeWithDefaultChildren(); - $node->setKeyAttribute('foobar'); - $node->setAddChildrenIfNoneSet(); - $this->assertTrue($node->hasDefaultValue()); - $this->assertEquals(array('defaults' => array('foo' => 'bar')), $node->getDefaultValue()); - - $node = $this->getPrototypeNodeWithDefaultChildren(); - $node->setKeyAttribute('foobar'); - $node->setAddChildrenIfNoneSet('defaultkey'); - $this->assertTrue($node->hasDefaultValue()); - $this->assertEquals(array('defaultkey' => array('foo' => 'bar')), $node->getDefaultValue()); - - $node = $this->getPrototypeNodeWithDefaultChildren(); - $node->setKeyAttribute('foobar'); - $node->setAddChildrenIfNoneSet(array('defaultkey')); - $this->assertTrue($node->hasDefaultValue()); - $this->assertEquals(array('defaultkey' => array('foo' => 'bar')), $node->getDefaultValue()); - - $node = $this->getPrototypeNodeWithDefaultChildren(); - $node->setKeyAttribute('foobar'); - $node->setAddChildrenIfNoneSet(array('dk1', 'dk2')); - $this->assertTrue($node->hasDefaultValue()); - $this->assertEquals(array('dk1' => array('foo' => 'bar'), 'dk2' => array('foo' => 'bar')), $node->getDefaultValue()); - - $node = $this->getPrototypeNodeWithDefaultChildren(); - $node->setAddChildrenIfNoneSet(array(5, 6)); - $this->assertTrue($node->hasDefaultValue()); - $this->assertEquals(array(0 => array('foo' => 'bar'), 1 => array('foo' => 'bar')), $node->getDefaultValue()); - - $node = $this->getPrototypeNodeWithDefaultChildren(); - $node->setAddChildrenIfNoneSet(2); - $this->assertTrue($node->hasDefaultValue()); - $this->assertEquals(array(array('foo' => 'bar'), array('foo' => 'bar')), $node->getDefaultValue()); - } - - public function testDefaultChildrenWinsOverDefaultValue() - { - $node = $this->getPrototypeNodeWithDefaultChildren(); - $node->setAddChildrenIfNoneSet(); - $node->setDefaultValue(array('bar' => 'foo')); - $this->assertTrue($node->hasDefaultValue()); - $this->assertEquals(array(array('foo' => 'bar')), $node->getDefaultValue()); - } - - protected function getPrototypeNodeWithDefaultChildren() - { - $node = new PrototypedArrayNode('root'); - $prototype = new ArrayNode(null, $node); - $child = new ScalarNode('foo'); - $child->setDefaultValue('bar'); - $prototype->addChild($child); - $prototype->setAddIfNotSet(true); - $node->setPrototype($prototype); - - return $node; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/ScalarNodeTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/ScalarNodeTest.php deleted file mode 100644 index 86c1803018..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Definition/ScalarNodeTest.php +++ /dev/null @@ -1,126 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition; - -use Symfony\Component\Config\Definition\ScalarNode; - -class ScalarNodeTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider getValidValues - */ - public function testNormalize($value) - { - $node = new ScalarNode('test'); - $this->assertSame($value, $node->normalize($value)); - } - - public function getValidValues() - { - return array( - array(false), - array(true), - array(null), - array(''), - array('foo'), - array(0), - array(1), - array(0.0), - array(0.1), - ); - } - - /** - * @dataProvider getInvalidValues - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException - */ - public function testNormalizeThrowsExceptionOnInvalidValues($value) - { - $node = new ScalarNode('test'); - $node->normalize($value); - } - - public function getInvalidValues() - { - return array( - array(array()), - array(array('foo' => 'bar')), - array(new \stdClass()), - ); - } - - public function testNormalizeThrowsExceptionWithoutHint() - { - $node = new ScalarNode('test'); - - $this->setExpectedException('Symfony\Component\Config\Definition\Exception\InvalidTypeException', 'Invalid type for path "test". Expected scalar, but got array.'); - - $node->normalize(array()); - } - - public function testNormalizeThrowsExceptionWithErrorMessage() - { - $node = new ScalarNode('test'); - $node->setInfo('"the test value"'); - - $this->setExpectedException('Symfony\Component\Config\Definition\Exception\InvalidTypeException', "Invalid type for path \"test\". Expected scalar, but got array.\nHint: \"the test value\""); - - $node->normalize(array()); - } - - /** - * @dataProvider getValidNonEmptyValues - * - * @param mixed $value - */ - public function testValidNonEmptyValues($value) - { - $node = new ScalarNode('test'); - $node->setAllowEmptyValue(false); - - $this->assertSame($value, $node->finalize($value)); - } - - public function getValidNonEmptyValues() - { - return array( - array(false), - array(true), - array('foo'), - array(0), - array(1), - array(0.0), - array(0.1), - ); - } - - /** - * @dataProvider getEmptyValues - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - * - * @param mixed $value - */ - public function testNotAllowedEmptyValuesThrowException($value) - { - $node = new ScalarNode('test'); - $node->setAllowEmptyValue(false); - $node->finalize($value); - } - - public function getEmptyValues() - { - return array( - array(null), - array(''), - ); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Exception/FileLoaderLoadExceptionTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Exception/FileLoaderLoadExceptionTest.php deleted file mode 100644 index c3d050c75d..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Exception/FileLoaderLoadExceptionTest.php +++ /dev/null @@ -1,85 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Exception; - -use Symfony\Component\Config\Exception\FileLoaderLoadException; - -class FileLoaderLoadExceptionTest extends \PHPUnit_Framework_TestCase -{ - public function testMessageCannotLoadResource() - { - $exception = new FileLoaderLoadException('resource', null); - $this->assertEquals('Cannot load resource "resource".', $exception->getMessage()); - } - - public function testMessageCannotImportResourceFromSource() - { - $exception = new FileLoaderLoadException('resource', 'sourceResource'); - $this->assertEquals('Cannot import resource "resource" from "sourceResource".', $exception->getMessage()); - } - - public function testMessageCannotImportBundleResource() - { - $exception = new FileLoaderLoadException('@resource', 'sourceResource'); - $this->assertEquals( - 'Cannot import resource "@resource" from "sourceResource". '. - 'Make sure the "resource" bundle is correctly registered and loaded in the application kernel class. '. - 'If the bundle is registered, make sure the bundle path "@resource" is not empty.', - $exception->getMessage() - ); - } - - public function testMessageHasPreviousErrorWithDotAndUnableToLoad() - { - $exception = new FileLoaderLoadException( - 'resource', - null, - null, - new \Exception('There was a previous error with an ending dot.') - ); - $this->assertEquals( - 'There was a previous error with an ending dot in resource (which is loaded in resource "resource").', - $exception->getMessage() - ); - } - - public function testMessageHasPreviousErrorWithoutDotAndUnableToLoad() - { - $exception = new FileLoaderLoadException( - 'resource', - null, - null, - new \Exception('There was a previous error with no ending dot') - ); - $this->assertEquals( - 'There was a previous error with no ending dot in resource (which is loaded in resource "resource").', - $exception->getMessage() - ); - } - - public function testMessageHasPreviousErrorAndUnableToLoadBundle() - { - $exception = new FileLoaderLoadException( - '@resource', - null, - null, - new \Exception('There was a previous error with an ending dot.') - ); - $this->assertEquals( - 'There was a previous error with an ending dot in @resource '. - '(which is loaded in resource "@resource"). '. - 'Make sure the "resource" bundle is correctly registered and loaded in the application kernel class. '. - 'If the bundle is registered, make sure the bundle path "@resource" is not empty.', - $exception->getMessage() - ); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/FileLocatorTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/FileLocatorTest.php deleted file mode 100644 index d479f2569f..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/FileLocatorTest.php +++ /dev/null @@ -1,119 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests; - -use Symfony\Component\Config\FileLocator; - -class FileLocatorTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider getIsAbsolutePathTests - */ - public function testIsAbsolutePath($path) - { - $loader = new FileLocator(array()); - $r = new \ReflectionObject($loader); - $m = $r->getMethod('isAbsolutePath'); - $m->setAccessible(true); - - $this->assertTrue($m->invoke($loader, $path), '->isAbsolutePath() returns true for an absolute path'); - } - - public function getIsAbsolutePathTests() - { - return array( - array('/foo.xml'), - array('c:\\\\foo.xml'), - array('c:/foo.xml'), - array('\\server\\foo.xml'), - array('https://server/foo.xml'), - array('phar://server/foo.xml'), - ); - } - - public function testLocate() - { - $loader = new FileLocator(__DIR__.'/Fixtures'); - - $this->assertEquals( - __DIR__.DIRECTORY_SEPARATOR.'FileLocatorTest.php', - $loader->locate('FileLocatorTest.php', __DIR__), - '->locate() returns the absolute filename if the file exists in the given path' - ); - - $this->assertEquals( - __DIR__.'/Fixtures'.DIRECTORY_SEPARATOR.'foo.xml', - $loader->locate('foo.xml', __DIR__), - '->locate() returns the absolute filename if the file exists in one of the paths given in the constructor' - ); - - $this->assertEquals( - __DIR__.'/Fixtures'.DIRECTORY_SEPARATOR.'foo.xml', - $loader->locate(__DIR__.'/Fixtures'.DIRECTORY_SEPARATOR.'foo.xml', __DIR__), - '->locate() returns the absolute filename if the file exists in one of the paths given in the constructor' - ); - - $loader = new FileLocator(array(__DIR__.'/Fixtures', __DIR__.'/Fixtures/Again')); - - $this->assertEquals( - array(__DIR__.'/Fixtures'.DIRECTORY_SEPARATOR.'foo.xml', __DIR__.'/Fixtures/Again'.DIRECTORY_SEPARATOR.'foo.xml'), - $loader->locate('foo.xml', __DIR__, false), - '->locate() returns an array of absolute filenames' - ); - - $this->assertEquals( - array(__DIR__.'/Fixtures'.DIRECTORY_SEPARATOR.'foo.xml', __DIR__.'/Fixtures/Again'.DIRECTORY_SEPARATOR.'foo.xml'), - $loader->locate('foo.xml', __DIR__.'/Fixtures', false), - '->locate() returns an array of absolute filenames' - ); - - $loader = new FileLocator(__DIR__.'/Fixtures/Again'); - - $this->assertEquals( - array(__DIR__.'/Fixtures'.DIRECTORY_SEPARATOR.'foo.xml', __DIR__.'/Fixtures/Again'.DIRECTORY_SEPARATOR.'foo.xml'), - $loader->locate('foo.xml', __DIR__.'/Fixtures', false), - '->locate() returns an array of absolute filenames' - ); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The file "foobar.xml" does not exist - */ - public function testLocateThrowsAnExceptionIfTheFileDoesNotExists() - { - $loader = new FileLocator(array(__DIR__.'/Fixtures')); - - $loader->locate('foobar.xml', __DIR__); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testLocateThrowsAnExceptionIfTheFileDoesNotExistsInAbsolutePath() - { - $loader = new FileLocator(array(__DIR__.'/Fixtures')); - - $loader->locate(__DIR__.'/Fixtures/foobar.xml', __DIR__); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage An empty file name is not valid to be located. - */ - public function testLocateEmpty() - { - $loader = new FileLocator(array(__DIR__.'/Fixtures')); - - $loader->locate(null, __DIR__); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Again/foo.xml b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Again/foo.xml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Builder/BarNodeDefinition.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Builder/BarNodeDefinition.php deleted file mode 100644 index 47701c1b29..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Builder/BarNodeDefinition.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition\Builder; - -use Symfony\Component\Config\Definition\Builder\NodeDefinition; - -class BarNodeDefinition extends NodeDefinition -{ - protected function createNode() - { - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Builder/NodeBuilder.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Builder/NodeBuilder.php deleted file mode 100644 index aa5986311b..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Builder/NodeBuilder.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition\Builder; - -use Symfony\Component\Config\Definition\Builder\NodeBuilder as BaseNodeBuilder; - -class NodeBuilder extends BaseNodeBuilder -{ - public function barNode($name) - { - return $this->node($name, 'bar'); - } - - protected function getNodeClass($type) - { - switch ($type) { - case 'variable': - return __NAMESPACE__.'\\'.ucfirst($type).'NodeDefinition'; - case 'bar': - return __NAMESPACE__.'\\'.ucfirst($type).'NodeDefinition'; - default: - return parent::getNodeClass($type); - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Builder/VariableNodeDefinition.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Builder/VariableNodeDefinition.php deleted file mode 100644 index 1017880c11..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Builder/VariableNodeDefinition.php +++ /dev/null @@ -1,18 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition\Builder; - -use Symfony\Component\Config\Definition\Builder\VariableNodeDefinition as BaseVariableNodeDefinition; - -class VariableNodeDefinition extends BaseVariableNodeDefinition -{ -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Configuration/ExampleConfiguration.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Configuration/ExampleConfiguration.php deleted file mode 100644 index 7125dc491e..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Configuration/ExampleConfiguration.php +++ /dev/null @@ -1,72 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Fixtures\Configuration; - -use Symfony\Component\Config\Definition\Builder\TreeBuilder; -use Symfony\Component\Config\Definition\ConfigurationInterface; - -class ExampleConfiguration implements ConfigurationInterface -{ - public function getConfigTreeBuilder() - { - $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root('acme_root'); - - $rootNode - ->fixXmlConfig('parameter') - ->fixXmlConfig('connection') - ->children() - ->booleanNode('boolean')->defaultTrue()->end() - ->scalarNode('scalar_empty')->end() - ->scalarNode('scalar_null')->defaultNull()->end() - ->scalarNode('scalar_true')->defaultTrue()->end() - ->scalarNode('scalar_false')->defaultFalse()->end() - ->scalarNode('scalar_default')->defaultValue('default')->end() - ->scalarNode('scalar_array_empty')->defaultValue(array())->end() - ->scalarNode('scalar_array_defaults')->defaultValue(array('elem1', 'elem2'))->end() - ->scalarNode('scalar_required')->isRequired()->end() - ->enumNode('enum_with_default')->values(array('this', 'that'))->defaultValue('this')->end() - ->enumNode('enum')->values(array('this', 'that'))->end() - ->arrayNode('array') - ->info('some info') - ->canBeUnset() - ->children() - ->scalarNode('child1')->end() - ->scalarNode('child2')->end() - ->scalarNode('child3') - ->info( - "this is a long\n". - "multi-line info text\n". - 'which should be indented' - ) - ->example('example setting') - ->end() - ->end() - ->end() - ->arrayNode('parameters') - ->useAttributeAsKey('name') - ->prototype('scalar')->info('Parameter name')->end() - ->end() - ->arrayNode('connections') - ->prototype('array') - ->children() - ->scalarNode('user')->end() - ->scalarNode('pass')->end() - ->end() - ->end() - ->end() - ->end() - ; - - return $treeBuilder; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Util/document_type.xml b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Util/document_type.xml deleted file mode 100644 index 4c25228263..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Util/document_type.xml +++ /dev/null @@ -1,3 +0,0 @@ - -]> - diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Util/invalid.xml b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Util/invalid.xml deleted file mode 100644 index a07af9fd85..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Util/invalid.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Util/invalid_schema.xml b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Util/invalid_schema.xml deleted file mode 100644 index e2725a2c2a..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Util/invalid_schema.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Util/schema.xsd b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Util/schema.xsd deleted file mode 100644 index e56820f691..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Util/schema.xsd +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Util/valid.xml b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Util/valid.xml deleted file mode 100644 index a96bb38267..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/Util/valid.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/foo.xml b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Fixtures/foo.xml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Loader/DelegatingLoaderTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Loader/DelegatingLoaderTest.php deleted file mode 100644 index cdb0980307..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Loader/DelegatingLoaderTest.php +++ /dev/null @@ -1,70 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Loader; - -use Symfony\Component\Config\Loader\LoaderResolver; -use Symfony\Component\Config\Loader\DelegatingLoader; - -class DelegatingLoaderTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $loader = new DelegatingLoader($resolver = new LoaderResolver()); - $this->assertTrue(true, '__construct() takes a loader resolver as its first argument'); - } - - public function testGetSetResolver() - { - $resolver = new LoaderResolver(); - $loader = new DelegatingLoader($resolver); - $this->assertSame($resolver, $loader->getResolver(), '->getResolver() gets the resolver loader'); - $loader->setResolver($resolver = new LoaderResolver()); - $this->assertSame($resolver, $loader->getResolver(), '->setResolver() sets the resolver loader'); - } - - public function testSupports() - { - $loader1 = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); - $loader1->expects($this->once())->method('supports')->will($this->returnValue(true)); - $loader = new DelegatingLoader(new LoaderResolver(array($loader1))); - $this->assertTrue($loader->supports('foo.xml'), '->supports() returns true if the resource is loadable'); - - $loader1 = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); - $loader1->expects($this->once())->method('supports')->will($this->returnValue(false)); - $loader = new DelegatingLoader(new LoaderResolver(array($loader1))); - $this->assertFalse($loader->supports('foo.foo'), '->supports() returns false if the resource is not loadable'); - } - - public function testLoad() - { - $loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); - $loader->expects($this->once())->method('supports')->will($this->returnValue(true)); - $loader->expects($this->once())->method('load'); - $resolver = new LoaderResolver(array($loader)); - $loader = new DelegatingLoader($resolver); - - $loader->load('foo'); - } - - /** - * @expectedException \Symfony\Component\Config\Exception\FileLoaderLoadException - */ - public function testLoadThrowsAnExceptionIfTheResourceCannotBeLoaded() - { - $loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); - $loader->expects($this->once())->method('supports')->will($this->returnValue(false)); - $resolver = new LoaderResolver(array($loader)); - $loader = new DelegatingLoader($resolver); - - $loader->load('foo'); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Loader/FileLoaderTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Loader/FileLoaderTest.php deleted file mode 100644 index 1e8e91eda1..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Loader/FileLoaderTest.php +++ /dev/null @@ -1,103 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Loader; - -use Symfony\Component\Config\Loader\FileLoader; -use Symfony\Component\Config\Loader\LoaderResolver; - -class FileLoaderTest extends \PHPUnit_Framework_TestCase -{ - public function testImportWithFileLocatorDelegation() - { - $locatorMock = $this->getMock('Symfony\Component\Config\FileLocatorInterface'); - - $locatorMockForAdditionalLoader = $this->getMock('Symfony\Component\Config\FileLocatorInterface'); - $locatorMockForAdditionalLoader->expects($this->any())->method('locate')->will($this->onConsecutiveCalls( - array('path/to/file1'), // Default - array('path/to/file1', 'path/to/file2'), // First is imported - array('path/to/file1', 'path/to/file2'), // Second is imported - array('path/to/file1'), // Exception - array('path/to/file1', 'path/to/file2') // Exception - )); - - $fileLoader = new TestFileLoader($locatorMock); - $fileLoader->setSupports(false); - $fileLoader->setCurrentDir('.'); - - $additionalLoader = new TestFileLoader($locatorMockForAdditionalLoader); - $additionalLoader->setCurrentDir('.'); - - $fileLoader->setResolver($loaderResolver = new LoaderResolver(array($fileLoader, $additionalLoader))); - - // Default case - $this->assertSame('path/to/file1', $fileLoader->import('my_resource')); - - // Check first file is imported if not already loading - $this->assertSame('path/to/file1', $fileLoader->import('my_resource')); - - // Check second file is imported if first is already loading - $fileLoader->addLoading('path/to/file1'); - $this->assertSame('path/to/file2', $fileLoader->import('my_resource')); - - // Check exception throws if first (and only available) file is already loading - try { - $fileLoader->import('my_resource'); - $this->fail('->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException', $e, '->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); - } - - // Check exception throws if all files are already loading - try { - $fileLoader->addLoading('path/to/file2'); - $fileLoader->import('my_resource'); - $this->fail('->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException', $e, '->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); - } - } -} - -class TestFileLoader extends FileLoader -{ - private $supports = true; - - public function load($resource, $type = null) - { - return $resource; - } - - public function supports($resource, $type = null) - { - return $this->supports; - } - - public function addLoading($resource) - { - self::$loading[$resource] = true; - } - - public function removeLoading($resource) - { - unset(self::$loading[$resource]); - } - - public function clearLoading() - { - self::$loading = array(); - } - - public function setSupports($supports) - { - $this->supports = $supports; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Loader/LoaderResolverTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Loader/LoaderResolverTest.php deleted file mode 100644 index 03db21be4e..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Loader/LoaderResolverTest.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Loader; - -use Symfony\Component\Config\Loader\LoaderResolver; - -class LoaderResolverTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $resolver = new LoaderResolver(array( - $loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'), - )); - - $this->assertEquals(array($loader), $resolver->getLoaders(), '__construct() takes an array of loaders as its first argument'); - } - - public function testResolve() - { - $loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); - $resolver = new LoaderResolver(array($loader)); - $this->assertFalse($resolver->resolve('foo.foo'), '->resolve() returns false if no loader is able to load the resource'); - - $loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); - $loader->expects($this->once())->method('supports')->will($this->returnValue(true)); - $resolver = new LoaderResolver(array($loader)); - $this->assertEquals($loader, $resolver->resolve(function () {}), '->resolve() returns the loader for the given resource'); - } - - public function testLoaders() - { - $resolver = new LoaderResolver(); - $resolver->addLoader($loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface')); - - $this->assertEquals(array($loader), $resolver->getLoaders(), 'addLoader() adds a loader'); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Loader/LoaderTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Loader/LoaderTest.php deleted file mode 100644 index e938a4b071..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Loader/LoaderTest.php +++ /dev/null @@ -1,117 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Loader; - -use Symfony\Component\Config\Loader\Loader; - -class LoaderTest extends \PHPUnit_Framework_TestCase -{ - public function testGetSetResolver() - { - $resolver = $this->getMock('Symfony\Component\Config\Loader\LoaderResolverInterface'); - - $loader = new ProjectLoader1(); - $loader->setResolver($resolver); - - $this->assertSame($resolver, $loader->getResolver(), '->setResolver() sets the resolver loader'); - } - - public function testResolve() - { - $resolvedLoader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); - - $resolver = $this->getMock('Symfony\Component\Config\Loader\LoaderResolverInterface'); - $resolver->expects($this->once()) - ->method('resolve') - ->with('foo.xml') - ->will($this->returnValue($resolvedLoader)); - - $loader = new ProjectLoader1(); - $loader->setResolver($resolver); - - $this->assertSame($loader, $loader->resolve('foo.foo'), '->resolve() finds a loader'); - $this->assertSame($resolvedLoader, $loader->resolve('foo.xml'), '->resolve() finds a loader'); - } - - /** - * @expectedException \Symfony\Component\Config\Exception\FileLoaderLoadException - */ - public function testResolveWhenResolverCannotFindLoader() - { - $resolver = $this->getMock('Symfony\Component\Config\Loader\LoaderResolverInterface'); - $resolver->expects($this->once()) - ->method('resolve') - ->with('FOOBAR') - ->will($this->returnValue(false)); - - $loader = new ProjectLoader1(); - $loader->setResolver($resolver); - - $loader->resolve('FOOBAR'); - } - - public function testImport() - { - $resolvedLoader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); - $resolvedLoader->expects($this->once()) - ->method('load') - ->with('foo') - ->will($this->returnValue('yes')); - - $resolver = $this->getMock('Symfony\Component\Config\Loader\LoaderResolverInterface'); - $resolver->expects($this->once()) - ->method('resolve') - ->with('foo') - ->will($this->returnValue($resolvedLoader)); - - $loader = new ProjectLoader1(); - $loader->setResolver($resolver); - - $this->assertEquals('yes', $loader->import('foo')); - } - - public function testImportWithType() - { - $resolvedLoader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); - $resolvedLoader->expects($this->once()) - ->method('load') - ->with('foo', 'bar') - ->will($this->returnValue('yes')); - - $resolver = $this->getMock('Symfony\Component\Config\Loader\LoaderResolverInterface'); - $resolver->expects($this->once()) - ->method('resolve') - ->with('foo', 'bar') - ->will($this->returnValue($resolvedLoader)); - - $loader = new ProjectLoader1(); - $loader->setResolver($resolver); - - $this->assertEquals('yes', $loader->import('foo', 'bar')); - } -} - -class ProjectLoader1 extends Loader -{ - public function load($resource, $type = null) - { - } - - public function supports($resource, $type = null) - { - return is_string($resource) && 'foo' === pathinfo($resource, PATHINFO_EXTENSION); - } - - public function getType() - { - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Resource/DirectoryResourceTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Resource/DirectoryResourceTest.php deleted file mode 100644 index 651a59e39d..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Resource/DirectoryResourceTest.php +++ /dev/null @@ -1,180 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Resource; - -use Symfony\Component\Config\Resource\DirectoryResource; - -class DirectoryResourceTest extends \PHPUnit_Framework_TestCase -{ - protected $directory; - - protected function setUp() - { - $this->directory = sys_get_temp_dir().DIRECTORY_SEPARATOR.'symfonyDirectoryIterator'; - if (!file_exists($this->directory)) { - mkdir($this->directory); - } - touch($this->directory.'/tmp.xml'); - } - - protected function tearDown() - { - if (!is_dir($this->directory)) { - return; - } - $this->removeDirectory($this->directory); - } - - protected function removeDirectory($directory) - { - $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory), \RecursiveIteratorIterator::CHILD_FIRST); - foreach ($iterator as $path) { - if (preg_match('#[/\\\\]\.\.?$#', $path->__toString())) { - continue; - } - if ($path->isDir()) { - rmdir($path->__toString()); - } else { - unlink($path->__toString()); - } - } - rmdir($directory); - } - - public function testGetResource() - { - $resource = new DirectoryResource($this->directory); - $this->assertSame($this->directory, $resource->getResource(), '->getResource() returns the path to the resource'); - } - - public function testGetPattern() - { - $resource = new DirectoryResource($this->directory, 'bar'); - $this->assertEquals('bar', $resource->getPattern()); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessageRegExp /The directory ".*" does not exist./ - */ - public function testResourceDoesNotExist() - { - $resource = new DirectoryResource('/____foo/foobar'.mt_rand(1, 999999)); - } - - public function testIsFresh() - { - $resource = new DirectoryResource($this->directory); - $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if the resource has not changed'); - $this->assertFalse($resource->isFresh(time() - 86400), '->isFresh() returns false if the resource has been updated'); - } - - public function testIsFreshForDeletedResources() - { - $resource = new DirectoryResource($this->directory); - $this->removeDirectory($this->directory); - - $this->assertFalse($resource->isFresh(time()), '->isFresh() returns false if the resource does not exist'); - } - - public function testIsFreshUpdateFile() - { - $resource = new DirectoryResource($this->directory); - touch($this->directory.'/tmp.xml', time() + 20); - $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if an existing file is modified'); - } - - public function testIsFreshNewFile() - { - $resource = new DirectoryResource($this->directory); - touch($this->directory.'/new.xml', time() + 20); - $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if a new file is added'); - } - - public function testIsFreshNewFileWithDifferentPattern() - { - $resource = new DirectoryResource($this->directory, '/.xml$/'); - touch($this->directory.'/new.yaml', time() + 20); - $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if a new file with a non-matching pattern is added'); - } - - public function testIsFreshDeleteFile() - { - $resource = new DirectoryResource($this->directory); - unlink($this->directory.'/tmp.xml'); - $this->assertFalse($resource->isFresh(time()), '->isFresh() returns false if an existing file is removed'); - } - - public function testIsFreshDeleteDirectory() - { - $resource = new DirectoryResource($this->directory); - $this->removeDirectory($this->directory); - $this->assertFalse($resource->isFresh(time()), '->isFresh() returns false if the whole resource is removed'); - } - - public function testIsFreshCreateFileInSubdirectory() - { - $subdirectory = $this->directory.'/subdirectory'; - mkdir($subdirectory); - - $resource = new DirectoryResource($this->directory); - $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if an unmodified subdirectory exists'); - - touch($subdirectory.'/newfile.xml', time() + 20); - $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if a new file in a subdirectory is added'); - } - - public function testIsFreshModifySubdirectory() - { - $resource = new DirectoryResource($this->directory); - - $subdirectory = $this->directory.'/subdirectory'; - mkdir($subdirectory); - touch($subdirectory, time() + 20); - - $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if a subdirectory is modified (e.g. a file gets deleted)'); - } - - public function testFilterRegexListNoMatch() - { - $resource = new DirectoryResource($this->directory, '/\.(foo|xml)$/'); - - touch($this->directory.'/new.bar', time() + 20); - $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if a new file not matching the filter regex is created'); - } - - public function testFilterRegexListMatch() - { - $resource = new DirectoryResource($this->directory, '/\.(foo|xml)$/'); - - touch($this->directory.'/new.xml', time() + 20); - $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if an new file matching the filter regex is created '); - } - - public function testSerializeUnserialize() - { - $resource = new DirectoryResource($this->directory, '/\.(foo|xml)$/'); - - $unserialized = unserialize(serialize($resource)); - - $this->assertSame($this->directory, $resource->getResource()); - $this->assertSame('/\.(foo|xml)$/', $resource->getPattern()); - } - - public function testResourcesWithDifferentPatternsAreDifferent() - { - $resourceA = new DirectoryResource($this->directory, '/.xml$/'); - $resourceB = new DirectoryResource($this->directory, '/.yaml$/'); - - $this->assertEquals(2, count(array_unique(array($resourceA, $resourceB)))); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Resource/FileExistenceResourceTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Resource/FileExistenceResourceTest.php deleted file mode 100644 index 56ee584b1a..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Resource/FileExistenceResourceTest.php +++ /dev/null @@ -1,70 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Resource; - -use Symfony\Component\Config\Resource\FileExistenceResource; - -class FileExistenceResourceTest extends \PHPUnit_Framework_TestCase -{ - protected $resource; - protected $file; - protected $time; - - protected function setUp() - { - $this->file = realpath(sys_get_temp_dir()).'/tmp.xml'; - $this->time = time(); - $this->resource = new FileExistenceResource($this->file); - } - - protected function tearDown() - { - if (file_exists($this->file)) { - unlink($this->file); - } - } - - public function testToString() - { - $this->assertSame($this->file, (string) $this->resource); - } - - public function testGetResource() - { - $this->assertSame($this->file, $this->resource->getResource(), '->getResource() returns the path to the resource'); - } - - public function testIsFreshWithExistingResource() - { - touch($this->file, $this->time); - $serialized = serialize(new FileExistenceResource($this->file)); - - $resource = unserialize($serialized); - $this->assertTrue($resource->isFresh($this->time), '->isFresh() returns true if the resource is still present'); - - unlink($this->file); - $resource = unserialize($serialized); - $this->assertFalse($resource->isFresh($this->time), '->isFresh() returns false if the resource has been deleted'); - } - - public function testIsFreshWithAbsentResource() - { - $serialized = serialize(new FileExistenceResource($this->file)); - - $resource = unserialize($serialized); - $this->assertTrue($resource->isFresh($this->time), '->isFresh() returns true if the resource is still absent'); - - touch($this->file, $this->time); - $resource = unserialize($serialized); - $this->assertFalse($resource->isFresh($this->time), '->isFresh() returns false if the resource has been created'); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Resource/FileResourceTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Resource/FileResourceTest.php deleted file mode 100644 index 6a168e6351..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Resource/FileResourceTest.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Resource; - -use Symfony\Component\Config\Resource\FileResource; - -class FileResourceTest extends \PHPUnit_Framework_TestCase -{ - protected $resource; - protected $file; - protected $time; - - protected function setUp() - { - $this->file = sys_get_temp_dir().'/tmp.xml'; - $this->time = time(); - touch($this->file, $this->time); - $this->resource = new FileResource($this->file); - } - - protected function tearDown() - { - if (!file_exists($this->file)) { - return; - } - - unlink($this->file); - } - - public function testGetResource() - { - $this->assertSame(realpath($this->file), $this->resource->getResource(), '->getResource() returns the path to the resource'); - } - - public function testGetResourceWithScheme() - { - $resource = new FileResource('file://'.$this->file); - $this->assertSame('file://'.$this->file, $resource->getResource(), '->getResource() returns the path to the schemed resource'); - } - - public function testToString() - { - $this->assertSame(realpath($this->file), (string) $this->resource); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessageRegExp /The file ".*" does not exist./ - */ - public function testResourceDoesNotExist() - { - $resource = new FileResource('/____foo/foobar'.mt_rand(1, 999999)); - } - - public function testIsFresh() - { - $this->assertTrue($this->resource->isFresh($this->time), '->isFresh() returns true if the resource has not changed in same second'); - $this->assertTrue($this->resource->isFresh($this->time + 10), '->isFresh() returns true if the resource has not changed'); - $this->assertFalse($this->resource->isFresh($this->time - 86400), '->isFresh() returns false if the resource has been updated'); - } - - public function testIsFreshForDeletedResources() - { - unlink($this->file); - - $this->assertFalse($this->resource->isFresh($this->time), '->isFresh() returns false if the resource does not exist'); - } - - public function testSerializeUnserialize() - { - $unserialized = unserialize(serialize($this->resource)); - - $this->assertSame(realpath($this->file), $this->resource->getResource()); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Resource/ResourceStub.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Resource/ResourceStub.php deleted file mode 100644 index b01729cbff..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Resource/ResourceStub.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Resource; - -use Symfony\Component\Config\Resource\SelfCheckingResourceInterface; - -class ResourceStub implements SelfCheckingResourceInterface -{ - private $fresh = true; - - public function setFresh($isFresh) - { - $this->fresh = $isFresh; - } - - public function __toString() - { - return 'stub'; - } - - public function isFresh($timestamp) - { - return $this->fresh; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/ResourceCheckerConfigCacheTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/ResourceCheckerConfigCacheTest.php deleted file mode 100644 index 6a915ffed7..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/ResourceCheckerConfigCacheTest.php +++ /dev/null @@ -1,118 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests; - -use Symfony\Component\Config\Tests\Resource\ResourceStub; -use Symfony\Component\Config\ResourceCheckerConfigCache; - -class ResourceCheckerConfigCacheTest extends \PHPUnit_Framework_TestCase -{ - private $cacheFile = null; - - protected function setUp() - { - $this->cacheFile = tempnam(sys_get_temp_dir(), 'config_'); - } - - protected function tearDown() - { - $files = array($this->cacheFile, "{$this->cacheFile}.meta"); - - foreach ($files as $file) { - if (file_exists($file)) { - unlink($file); - } - } - } - - public function testGetPath() - { - $cache = new ResourceCheckerConfigCache($this->cacheFile); - - $this->assertSame($this->cacheFile, $cache->getPath()); - } - - public function testCacheIsNotFreshIfEmpty() - { - $checker = $this->getMock('\Symfony\Component\Config\ResourceCheckerInterface') - ->expects($this->never())->method('supports'); - - /* If there is nothing in the cache, it needs to be filled (and thus it's not fresh). - It does not matter if you provide checkers or not. */ - - unlink($this->cacheFile); // remove tempnam() side effect - $cache = new ResourceCheckerConfigCache($this->cacheFile, array($checker)); - - $this->assertFalse($cache->isFresh()); - } - - public function testCacheIsFreshIfNocheckerProvided() - { - /* For example in prod mode, you may choose not to run any checkers - at all. In that case, the cache should always be considered fresh. */ - $cache = new ResourceCheckerConfigCache($this->cacheFile); - $this->assertTrue($cache->isFresh()); - } - - public function testResourcesWithoutcheckersAreIgnoredAndConsideredFresh() - { - /* As in the previous test, but this time we have a resource. */ - $cache = new ResourceCheckerConfigCache($this->cacheFile); - $cache->write('', array(new ResourceStub())); - - $this->assertTrue($cache->isFresh()); // no (matching) ResourceChecker passed - } - - public function testIsFreshWithchecker() - { - $checker = $this->getMock('\Symfony\Component\Config\ResourceCheckerInterface'); - - $checker->expects($this->once()) - ->method('supports') - ->willReturn(true); - - $checker->expects($this->once()) - ->method('isFresh') - ->willReturn(true); - - $cache = new ResourceCheckerConfigCache($this->cacheFile, array($checker)); - $cache->write('', array(new ResourceStub())); - - $this->assertTrue($cache->isFresh()); - } - - public function testIsNotFreshWithchecker() - { - $checker = $this->getMock('\Symfony\Component\Config\ResourceCheckerInterface'); - - $checker->expects($this->once()) - ->method('supports') - ->willReturn(true); - - $checker->expects($this->once()) - ->method('isFresh') - ->willReturn(false); - - $cache = new ResourceCheckerConfigCache($this->cacheFile, array($checker)); - $cache->write('', array(new ResourceStub())); - - $this->assertFalse($cache->isFresh()); - } - - public function testCacheKeepsContent() - { - $cache = new ResourceCheckerConfigCache($this->cacheFile); - $cache->write('FOOBAR'); - - $this->assertSame('FOOBAR', file_get_contents($cache->getPath())); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Util/XmlUtilsTest.php b/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Util/XmlUtilsTest.php deleted file mode 100644 index 4a53c92e07..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Tests/Util/XmlUtilsTest.php +++ /dev/null @@ -1,200 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Util; - -use Symfony\Component\Config\Util\XmlUtils; - -class XmlUtilsTest extends \PHPUnit_Framework_TestCase -{ - public function testLoadFile() - { - $fixtures = __DIR__.'/../Fixtures/Util/'; - - try { - XmlUtils::loadFile($fixtures.'invalid.xml'); - $this->fail(); - } catch (\InvalidArgumentException $e) { - $this->assertContains('ERROR 77', $e->getMessage()); - } - - try { - XmlUtils::loadFile($fixtures.'document_type.xml'); - $this->fail(); - } catch (\InvalidArgumentException $e) { - $this->assertContains('Document types are not allowed', $e->getMessage()); - } - - try { - XmlUtils::loadFile($fixtures.'invalid_schema.xml', $fixtures.'schema.xsd'); - $this->fail(); - } catch (\InvalidArgumentException $e) { - $this->assertContains('ERROR 1845', $e->getMessage()); - } - - try { - XmlUtils::loadFile($fixtures.'invalid_schema.xml', 'invalid_callback_or_file'); - $this->fail(); - } catch (\InvalidArgumentException $e) { - $this->assertContains('XSD file or callable', $e->getMessage()); - } - - $mock = $this->getMock(__NAMESPACE__.'\Validator'); - $mock->expects($this->exactly(2))->method('validate')->will($this->onConsecutiveCalls(false, true)); - - try { - XmlUtils::loadFile($fixtures.'valid.xml', array($mock, 'validate')); - $this->fail(); - } catch (\InvalidArgumentException $e) { - $this->assertContains('is not valid', $e->getMessage()); - } - - $this->assertInstanceOf('DOMDocument', XmlUtils::loadFile($fixtures.'valid.xml', array($mock, 'validate'))); - $this->assertSame(array(), libxml_get_errors()); - } - - public function testLoadFileWithInternalErrorsEnabled() - { - $internalErrors = libxml_use_internal_errors(true); - - $this->assertSame(array(), libxml_get_errors()); - $this->assertInstanceOf('DOMDocument', XmlUtils::loadFile(__DIR__.'/../Fixtures/Util/invalid_schema.xml')); - $this->assertSame(array(), libxml_get_errors()); - - libxml_clear_errors(); - libxml_use_internal_errors($internalErrors); - } - - /** - * @dataProvider getDataForConvertDomToArray - */ - public function testConvertDomToArray($expected, $xml, $root = false, $checkPrefix = true) - { - $dom = new \DOMDocument(); - $dom->loadXML($root ? $xml : ''.$xml.''); - - $this->assertSame($expected, XmlUtils::convertDomElementToArray($dom->documentElement, $checkPrefix)); - } - - public function getDataForConvertDomToArray() - { - return array( - array(null, ''), - array('bar', 'bar'), - array(array('bar' => 'foobar'), '', true), - array(array('foo' => null), ''), - array(array('foo' => 'bar'), 'bar'), - array(array('foo' => array('foo' => 'bar')), ''), - array(array('foo' => array('foo' => 0)), '0'), - array(array('foo' => array('foo' => 'bar')), 'bar'), - array(array('foo' => array('foo' => 'bar', 'value' => 'text')), 'text'), - array(array('foo' => array('attr' => 'bar', 'foo' => 'text')), 'text'), - array(array('foo' => array('bar', 'text')), 'bartext'), - array(array('foo' => array(array('foo' => 'bar'), array('foo' => 'text'))), ''), - array(array('foo' => array('foo' => array('bar', 'text'))), 'text'), - array(array('foo' => 'bar'), 'bar'), - array(array('foo' => 'text'), 'text'), - array(array('foo' => array('bar' => 'bar', 'value' => 'text')), 'text', false, false), - array(array('attr' => 1, 'b' => 'hello'), 'hello2', true), - ); - } - - /** - * @dataProvider getDataForPhpize - */ - public function testPhpize($expected, $value) - { - $this->assertSame($expected, XmlUtils::phpize($value)); - } - - public function getDataForPhpize() - { - return array( - array('', ''), - array(null, 'null'), - array(true, 'true'), - array(false, 'false'), - array(null, 'Null'), - array(true, 'True'), - array(false, 'False'), - array(0, '0'), - array(1, '1'), - array(-1, '-1'), - array(0777, '0777'), - array(255, '0xFF'), - array(100.0, '1e2'), - array(-120.0, '-1.2E2'), - array(-10100.1, '-10100.1'), - array('-10,100.1', '-10,100.1'), - array('1234 5678 9101 1121 3141', '1234 5678 9101 1121 3141'), - array('1,2,3,4', '1,2,3,4'), - array('11,22,33,44', '11,22,33,44'), - array('11,222,333,4', '11,222,333,4'), - array('1,222,333,444', '1,222,333,444'), - array('11,222,333,444', '11,222,333,444'), - array('111,222,333,444', '111,222,333,444'), - array('1111,2222,3333,4444,5555', '1111,2222,3333,4444,5555'), - array('foo', 'foo'), - array(6, '0b0110'), - ); - } - - public function testLoadEmptyXmlFile() - { - $file = __DIR__.'/../Fixtures/foo.xml'; - $this->setExpectedException('InvalidArgumentException', sprintf('File %s does not contain valid XML, it is empty.', $file)); - XmlUtils::loadFile($file); - } - - // test for issue https://github.com/symfony/symfony/issues/9731 - public function testLoadWrongEmptyXMLWithErrorHandler() - { - $originalDisableEntities = libxml_disable_entity_loader(false); - $errorReporting = error_reporting(-1); - - set_error_handler(function ($errno, $errstr) { - throw new \Exception($errstr, $errno); - }); - - $file = __DIR__.'/../Fixtures/foo.xml'; - try { - try { - XmlUtils::loadFile($file); - $this->fail('An exception should have been raised'); - } catch (\InvalidArgumentException $e) { - $this->assertEquals(sprintf('File %s does not contain valid XML, it is empty.', $file), $e->getMessage()); - } - } catch (\Exception $e) { - restore_error_handler(); - error_reporting($errorReporting); - - throw $e; - } - - restore_error_handler(); - error_reporting($errorReporting); - - $disableEntities = libxml_disable_entity_loader(true); - libxml_disable_entity_loader($disableEntities); - - libxml_disable_entity_loader($originalDisableEntities); - - $this->assertFalse($disableEntities); - - // should not throw an exception - XmlUtils::loadFile(__DIR__.'/../Fixtures/Util/valid.xml', __DIR__.'/../Fixtures/Util/schema.xsd'); - } -} - -interface Validator -{ - public function validate(); -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/Util/XmlUtils.php b/core/src/core/classes/guzzle/vendor/symfony/config/Util/XmlUtils.php deleted file mode 100644 index d8d4eaa3b1..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/Util/XmlUtils.php +++ /dev/null @@ -1,238 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Util; - -/** - * XMLUtils is a bunch of utility methods to XML operations. - * - * This class contains static methods only and is not meant to be instantiated. - * - * @author Fabien Potencier - * @author Martin Hasoň - */ -class XmlUtils -{ - /** - * This class should not be instantiated. - */ - private function __construct() - { - } - - /** - * Loads an XML file. - * - * @param string $file An XML file path - * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation - * - * @return \DOMDocument - * - * @throws \InvalidArgumentException When loading of XML file returns error - */ - public static function loadFile($file, $schemaOrCallable = null) - { - $content = @file_get_contents($file); - if ('' === trim($content)) { - throw new \InvalidArgumentException(sprintf('File %s does not contain valid XML, it is empty.', $file)); - } - - $internalErrors = libxml_use_internal_errors(true); - $disableEntities = libxml_disable_entity_loader(true); - libxml_clear_errors(); - - $dom = new \DOMDocument(); - $dom->validateOnParse = true; - if (!$dom->loadXML($content, LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) { - libxml_disable_entity_loader($disableEntities); - - throw new \InvalidArgumentException(implode("\n", static::getXmlErrors($internalErrors))); - } - - $dom->normalizeDocument(); - - libxml_use_internal_errors($internalErrors); - libxml_disable_entity_loader($disableEntities); - - foreach ($dom->childNodes as $child) { - if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) { - throw new \InvalidArgumentException('Document types are not allowed.'); - } - } - - if (null !== $schemaOrCallable) { - $internalErrors = libxml_use_internal_errors(true); - libxml_clear_errors(); - - $e = null; - if (is_callable($schemaOrCallable)) { - try { - $valid = call_user_func($schemaOrCallable, $dom, $internalErrors); - } catch (\Exception $e) { - $valid = false; - } - } elseif (!is_array($schemaOrCallable) && is_file((string) $schemaOrCallable)) { - $schemaSource = file_get_contents((string) $schemaOrCallable); - $valid = @$dom->schemaValidateSource($schemaSource); - } else { - libxml_use_internal_errors($internalErrors); - - throw new \InvalidArgumentException('The schemaOrCallable argument has to be a valid path to XSD file or callable.'); - } - - if (!$valid) { - $messages = static::getXmlErrors($internalErrors); - if (empty($messages)) { - $messages = array(sprintf('The XML file "%s" is not valid.', $file)); - } - throw new \InvalidArgumentException(implode("\n", $messages), 0, $e); - } - } - - libxml_clear_errors(); - libxml_use_internal_errors($internalErrors); - - return $dom; - } - - /** - * Converts a \DomElement object to a PHP array. - * - * The following rules applies during the conversion: - * - * * Each tag is converted to a key value or an array - * if there is more than one "value" - * - * * The content of a tag is set under a "value" key (bar) - * if the tag also has some nested tags - * - * * The attributes are converted to keys () - * - * * The nested-tags are converted to keys (bar) - * - * @param \DomElement $element A \DomElement instance - * @param bool $checkPrefix Check prefix in an element or an attribute name - * - * @return array A PHP array - */ - public static function convertDomElementToArray(\DomElement $element, $checkPrefix = true) - { - $prefix = (string) $element->prefix; - $empty = true; - $config = array(); - foreach ($element->attributes as $name => $node) { - if ($checkPrefix && !in_array((string) $node->prefix, array('', $prefix), true)) { - continue; - } - $config[$name] = static::phpize($node->value); - $empty = false; - } - - $nodeValue = false; - foreach ($element->childNodes as $node) { - if ($node instanceof \DOMText) { - if ('' !== trim($node->nodeValue)) { - $nodeValue = trim($node->nodeValue); - $empty = false; - } - } elseif ($checkPrefix && $prefix != (string) $node->prefix) { - continue; - } elseif (!$node instanceof \DOMComment) { - $value = static::convertDomElementToArray($node, $checkPrefix); - - $key = $node->localName; - if (isset($config[$key])) { - if (!is_array($config[$key]) || !is_int(key($config[$key]))) { - $config[$key] = array($config[$key]); - } - $config[$key][] = $value; - } else { - $config[$key] = $value; - } - - $empty = false; - } - } - - if (false !== $nodeValue) { - $value = static::phpize($nodeValue); - if (count($config)) { - $config['value'] = $value; - } else { - $config = $value; - } - } - - return !$empty ? $config : null; - } - - /** - * Converts an xml value to a PHP type. - * - * @param mixed $value - * - * @return mixed - */ - public static function phpize($value) - { - $value = (string) $value; - $lowercaseValue = strtolower($value); - - switch (true) { - case 'null' === $lowercaseValue: - return; - case ctype_digit($value): - $raw = $value; - $cast = (int) $value; - - return '0' == $value[0] ? octdec($value) : (((string) $raw === (string) $cast) ? $cast : $raw); - case isset($value[1]) && '-' === $value[0] && ctype_digit(substr($value, 1)): - $raw = $value; - $cast = (int) $value; - - return '0' == $value[1] ? octdec($value) : (((string) $raw === (string) $cast) ? $cast : $raw); - case 'true' === $lowercaseValue: - return true; - case 'false' === $lowercaseValue: - return false; - case isset($value[1]) && '0b' == $value[0].$value[1]: - return bindec($value); - case is_numeric($value): - return '0x' === $value[0].$value[1] ? hexdec($value) : (float) $value; - case preg_match('/^0x[0-9a-f]++$/i', $value): - return hexdec($value); - case preg_match('/^(-|\+)?[0-9]+(\.[0-9]+)?$/', $value): - return (float) $value; - default: - return $value; - } - } - - protected static function getXmlErrors($internalErrors) - { - $errors = array(); - foreach (libxml_get_errors() as $error) { - $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)', - LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', - $error->code, - trim($error->message), - $error->file ?: 'n/a', - $error->line, - $error->column - ); - } - - libxml_clear_errors(); - libxml_use_internal_errors($internalErrors); - - return $errors; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/composer.json b/core/src/core/classes/guzzle/vendor/symfony/config/composer.json deleted file mode 100644 index 711ed9605d..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/composer.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "symfony/config", - "type": "library", - "description": "Symfony Config Component", - "keywords": [], - "homepage": "https://symfony.com", - "license": "MIT", - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "require": { - "php": ">=5.5.9", - "symfony/filesystem": "~2.8|~3.0" - }, - "suggest": { - "symfony/yaml": "To use the yaml reference dumper" - }, - "autoload": { - "psr-4": { "Symfony\\Component\\Config\\": "" }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "minimum-stability": "dev", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/config/phpunit.xml.dist b/core/src/core/classes/guzzle/vendor/symfony/config/phpunit.xml.dist deleted file mode 100644 index 3fe6fd87c8..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/config/phpunit.xml.dist +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - ./Tests/ - - - - - - ./ - - ./Resources - ./Tests - ./vendor - - - - diff --git a/core/src/core/classes/guzzle/vendor/symfony/filesystem/.gitignore b/core/src/core/classes/guzzle/vendor/symfony/filesystem/.gitignore deleted file mode 100644 index c49a5d8df5..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/filesystem/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -vendor/ -composer.lock -phpunit.xml diff --git a/core/src/core/classes/guzzle/vendor/symfony/filesystem/CHANGELOG.md b/core/src/core/classes/guzzle/vendor/symfony/filesystem/CHANGELOG.md deleted file mode 100644 index 9e303821e6..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/filesystem/CHANGELOG.md +++ /dev/null @@ -1,38 +0,0 @@ -CHANGELOG -========= - -3.0.0 ------ - - * removed `$mode` argument from `Filesystem::dumpFile()` - -2.8.0 ------ - - * added tempnam() a stream aware version of PHP's native tempnam() - -2.6.0 ------ - - * added LockHandler - -2.3.12 ------- - - * deprecated dumpFile() file mode argument. - -2.3.0 ------ - - * added the dumpFile() method to atomically write files - -2.2.0 ------ - - * added a delete option for the mirror() method - -2.1.0 ------ - - * 24eb396 : BC Break : mkdir() function now throws exception in case of failure instead of returning Boolean value - * created the component diff --git a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Exception/ExceptionInterface.php b/core/src/core/classes/guzzle/vendor/symfony/filesystem/Exception/ExceptionInterface.php deleted file mode 100644 index 8f4f10aac7..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Exception/ExceptionInterface.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Exception; - -/** - * Exception interface for all exceptions thrown by the component. - * - * @author Romain Neutron - */ -interface ExceptionInterface -{ -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Exception/FileNotFoundException.php b/core/src/core/classes/guzzle/vendor/symfony/filesystem/Exception/FileNotFoundException.php deleted file mode 100644 index bcc8fe81fc..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Exception/FileNotFoundException.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Exception; - -/** - * Exception class thrown when a file couldn't be found. - * - * @author Fabien Potencier - * @author Christian Gärtner - */ -class FileNotFoundException extends IOException -{ - public function __construct($message = null, $code = 0, \Exception $previous = null, $path = null) - { - if (null === $message) { - if (null === $path) { - $message = 'File could not be found.'; - } else { - $message = sprintf('File "%s" could not be found.', $path); - } - } - - parent::__construct($message, $code, $previous, $path); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Exception/IOException.php b/core/src/core/classes/guzzle/vendor/symfony/filesystem/Exception/IOException.php deleted file mode 100644 index 144e0e602b..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Exception/IOException.php +++ /dev/null @@ -1,39 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Exception; - -/** - * Exception class thrown when a filesystem operation failure happens. - * - * @author Romain Neutron - * @author Christian Gärtner - * @author Fabien Potencier - */ -class IOException extends \RuntimeException implements IOExceptionInterface -{ - private $path; - - public function __construct($message, $code = 0, \Exception $previous = null, $path = null) - { - $this->path = $path; - - parent::__construct($message, $code, $previous); - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return $this->path; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Exception/IOExceptionInterface.php b/core/src/core/classes/guzzle/vendor/symfony/filesystem/Exception/IOExceptionInterface.php deleted file mode 100644 index c11965a424..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Exception/IOExceptionInterface.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Exception; - -/** - * IOException interface for file and input/output stream related exceptions thrown by the component. - * - * @author Christian Gärtner - */ -interface IOExceptionInterface extends ExceptionInterface -{ - /** - * Returns the associated path for the exception. - * - * @return string The path - */ - public function getPath(); -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Filesystem.php b/core/src/core/classes/guzzle/vendor/symfony/filesystem/Filesystem.php deleted file mode 100644 index bc58b070e3..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Filesystem.php +++ /dev/null @@ -1,589 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem; - -use Symfony\Component\Filesystem\Exception\IOException; -use Symfony\Component\Filesystem\Exception\FileNotFoundException; - -/** - * Provides basic utility to manipulate the file system. - * - * @author Fabien Potencier - */ -class Filesystem -{ - /** - * Copies a file. - * - * If the target file is older than the origin file, it's always overwritten. - * If the target file is newer, it is overwritten only when the - * $overwriteNewerFiles option is set to true. - * - * @param string $originFile The original filename - * @param string $targetFile The target filename - * @param bool $overwriteNewerFiles If true, target files newer than origin files are overwritten - * - * @throws FileNotFoundException When originFile doesn't exist - * @throws IOException When copy fails - */ - public function copy($originFile, $targetFile, $overwriteNewerFiles = false) - { - if (stream_is_local($originFile) && !is_file($originFile)) { - throw new FileNotFoundException(sprintf('Failed to copy "%s" because file does not exist.', $originFile), 0, null, $originFile); - } - - $this->mkdir(dirname($targetFile)); - - $doCopy = true; - if (!$overwriteNewerFiles && null === parse_url($originFile, PHP_URL_HOST) && is_file($targetFile)) { - $doCopy = filemtime($originFile) > filemtime($targetFile); - } - - if ($doCopy) { - // https://bugs.php.net/bug.php?id=64634 - if (false === $source = @fopen($originFile, 'r')) { - throw new IOException(sprintf('Failed to copy "%s" to "%s" because source file could not be opened for reading.', $originFile, $targetFile), 0, null, $originFile); - } - - // Stream context created to allow files overwrite when using FTP stream wrapper - disabled by default - if (false === $target = @fopen($targetFile, 'w', null, stream_context_create(array('ftp' => array('overwrite' => true))))) { - throw new IOException(sprintf('Failed to copy "%s" to "%s" because target file could not be opened for writing.', $originFile, $targetFile), 0, null, $originFile); - } - - $bytesCopied = stream_copy_to_stream($source, $target); - fclose($source); - fclose($target); - unset($source, $target); - - if (!is_file($targetFile)) { - throw new IOException(sprintf('Failed to copy "%s" to "%s".', $originFile, $targetFile), 0, null, $originFile); - } - - // Like `cp`, preserve executable permission bits - @chmod($targetFile, fileperms($targetFile) | (fileperms($originFile) & 0111)); - - if (stream_is_local($originFile) && $bytesCopied !== ($bytesOrigin = filesize($originFile))) { - throw new IOException(sprintf('Failed to copy the whole content of "%s" to "%s" (%g of %g bytes copied).', $originFile, $targetFile, $bytesCopied, $bytesOrigin), 0, null, $originFile); - } - } - } - - /** - * Creates a directory recursively. - * - * @param string|array|\Traversable $dirs The directory path - * @param int $mode The directory mode - * - * @throws IOException On any directory creation failure - */ - public function mkdir($dirs, $mode = 0777) - { - foreach ($this->toIterator($dirs) as $dir) { - if (is_dir($dir)) { - continue; - } - - if (true !== @mkdir($dir, $mode, true)) { - $error = error_get_last(); - if (!is_dir($dir)) { - // The directory was not created by a concurrent process. Let's throw an exception with a developer friendly error message if we have one - if ($error) { - throw new IOException(sprintf('Failed to create "%s": %s.', $dir, $error['message']), 0, null, $dir); - } - throw new IOException(sprintf('Failed to create "%s"', $dir), 0, null, $dir); - } - } - } - } - - /** - * Checks the existence of files or directories. - * - * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to check - * - * @return bool true if the file exists, false otherwise - */ - public function exists($files) - { - foreach ($this->toIterator($files) as $file) { - if ('\\' === DIRECTORY_SEPARATOR && strlen($file) > 258) { - throw new IOException('Could not check if file exist because path length exceeds 258 characters.', 0, null, $file); - } - - if (!file_exists($file)) { - return false; - } - } - - return true; - } - - /** - * Sets access and modification time of file. - * - * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to create - * @param int $time The touch time as a Unix timestamp - * @param int $atime The access time as a Unix timestamp - * - * @throws IOException When touch fails - */ - public function touch($files, $time = null, $atime = null) - { - foreach ($this->toIterator($files) as $file) { - $touch = $time ? @touch($file, $time, $atime) : @touch($file); - if (true !== $touch) { - throw new IOException(sprintf('Failed to touch "%s".', $file), 0, null, $file); - } - } - } - - /** - * Removes files or directories. - * - * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove - * - * @throws IOException When removal fails - */ - public function remove($files) - { - if ($files instanceof \Traversable) { - $files = iterator_to_array($files, false); - } elseif (!is_array($files)) { - $files = array($files); - } - $files = array_reverse($files); - foreach ($files as $file) { - if (is_link($file)) { - // See https://bugs.php.net/52176 - if (!@(unlink($file) || '\\' !== DIRECTORY_SEPARATOR || rmdir($file)) && file_exists($file)) { - $error = error_get_last(); - throw new IOException(sprintf('Failed to remove symlink "%s": %s.', $file, $error['message'])); - } - } elseif (is_dir($file)) { - $this->remove(new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS)); - - if (!@rmdir($file) && file_exists($file)) { - $error = error_get_last(); - throw new IOException(sprintf('Failed to remove directory "%s": %s.', $file, $error['message'])); - } - } elseif (!@unlink($file) && file_exists($file)) { - $error = error_get_last(); - throw new IOException(sprintf('Failed to remove file "%s": %s.', $file, $error['message'])); - } - } - } - - /** - * Change mode for an array of files or directories. - * - * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change mode - * @param int $mode The new mode (octal) - * @param int $umask The mode mask (octal) - * @param bool $recursive Whether change the mod recursively or not - * - * @throws IOException When the change fail - */ - public function chmod($files, $mode, $umask = 0000, $recursive = false) - { - foreach ($this->toIterator($files) as $file) { - if (true !== @chmod($file, $mode & ~$umask)) { - throw new IOException(sprintf('Failed to chmod file "%s".', $file), 0, null, $file); - } - if ($recursive && is_dir($file) && !is_link($file)) { - $this->chmod(new \FilesystemIterator($file), $mode, $umask, true); - } - } - } - - /** - * Change the owner of an array of files or directories. - * - * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change owner - * @param string $user The new owner user name - * @param bool $recursive Whether change the owner recursively or not - * - * @throws IOException When the change fail - */ - public function chown($files, $user, $recursive = false) - { - foreach ($this->toIterator($files) as $file) { - if ($recursive && is_dir($file) && !is_link($file)) { - $this->chown(new \FilesystemIterator($file), $user, true); - } - if (is_link($file) && function_exists('lchown')) { - if (true !== @lchown($file, $user)) { - throw new IOException(sprintf('Failed to chown file "%s".', $file), 0, null, $file); - } - } else { - if (true !== @chown($file, $user)) { - throw new IOException(sprintf('Failed to chown file "%s".', $file), 0, null, $file); - } - } - } - } - - /** - * Change the group of an array of files or directories. - * - * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change group - * @param string $group The group name - * @param bool $recursive Whether change the group recursively or not - * - * @throws IOException When the change fail - */ - public function chgrp($files, $group, $recursive = false) - { - foreach ($this->toIterator($files) as $file) { - if ($recursive && is_dir($file) && !is_link($file)) { - $this->chgrp(new \FilesystemIterator($file), $group, true); - } - if (is_link($file) && function_exists('lchgrp')) { - if (true !== @lchgrp($file, $group) || (defined('HHVM_VERSION') && !posix_getgrnam($group))) { - throw new IOException(sprintf('Failed to chgrp file "%s".', $file), 0, null, $file); - } - } else { - if (true !== @chgrp($file, $group)) { - throw new IOException(sprintf('Failed to chgrp file "%s".', $file), 0, null, $file); - } - } - } - } - - /** - * Renames a file or a directory. - * - * @param string $origin The origin filename or directory - * @param string $target The new filename or directory - * @param bool $overwrite Whether to overwrite the target if it already exists - * - * @throws IOException When target file or directory already exists - * @throws IOException When origin cannot be renamed - */ - public function rename($origin, $target, $overwrite = false) - { - // we check that target does not exist - if (!$overwrite && $this->isReadable($target)) { - throw new IOException(sprintf('Cannot rename because the target "%s" already exists.', $target), 0, null, $target); - } - - if (true !== @rename($origin, $target)) { - throw new IOException(sprintf('Cannot rename "%s" to "%s".', $origin, $target), 0, null, $target); - } - } - - /** - * Tells whether a file exists and is readable. - * - * @param string $filename Path to the file - * - * @throws IOException When windows path is longer than 258 characters - */ - private function isReadable($filename) - { - if ('\\' === DIRECTORY_SEPARATOR && strlen($filename) > 258) { - throw new IOException('Could not check if file is readable because path length exceeds 258 characters.', 0, null, $filename); - } - - return is_readable($filename); - } - - /** - * Creates a symbolic link or copy a directory. - * - * @param string $originDir The origin directory path - * @param string $targetDir The symbolic link name - * @param bool $copyOnWindows Whether to copy files if on Windows - * - * @throws IOException When symlink fails - */ - public function symlink($originDir, $targetDir, $copyOnWindows = false) - { - if ('\\' === DIRECTORY_SEPARATOR) { - $originDir = strtr($originDir, '/', '\\'); - $targetDir = strtr($targetDir, '/', '\\'); - - if ($copyOnWindows) { - $this->mirror($originDir, $targetDir); - - return; - } - } - - $this->mkdir(dirname($targetDir)); - - $ok = false; - if (is_link($targetDir)) { - if (readlink($targetDir) != $originDir) { - $this->remove($targetDir); - } else { - $ok = true; - } - } - - if (!$ok && true !== @symlink($originDir, $targetDir)) { - $report = error_get_last(); - if (is_array($report)) { - if ('\\' === DIRECTORY_SEPARATOR && false !== strpos($report['message'], 'error code(1314)')) { - throw new IOException('Unable to create symlink due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?', 0, null, $targetDir); - } - } - throw new IOException(sprintf('Failed to create symbolic link from "%s" to "%s".', $originDir, $targetDir), 0, null, $targetDir); - } - } - - /** - * Given an existing path, convert it to a path relative to a given starting path. - * - * @param string $endPath Absolute path of target - * @param string $startPath Absolute path where traversal begins - * - * @return string Path of target relative to starting path - */ - public function makePathRelative($endPath, $startPath) - { - // Normalize separators on Windows - if ('\\' === DIRECTORY_SEPARATOR) { - $endPath = str_replace('\\', '/', $endPath); - $startPath = str_replace('\\', '/', $startPath); - } - - // Split the paths into arrays - $startPathArr = explode('/', trim($startPath, '/')); - $endPathArr = explode('/', trim($endPath, '/')); - - // Find for which directory the common path stops - $index = 0; - while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) { - ++$index; - } - - // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels) - $depth = count($startPathArr) - $index; - - // When we need to traverse from the start, and we are starting from a root path, don't add '../' - if ('/' === $startPath[0] && 0 === $index && 1 === $depth) { - $traverser = ''; - } else { - // Repeated "../" for each level need to reach the common path - $traverser = str_repeat('../', $depth); - } - - $endPathRemainder = implode('/', array_slice($endPathArr, $index)); - - // Construct $endPath from traversing to the common path, then to the remaining $endPath - $relativePath = $traverser.('' !== $endPathRemainder ? $endPathRemainder.'/' : ''); - - return '' === $relativePath ? './' : $relativePath; - } - - /** - * Mirrors a directory to another. - * - * @param string $originDir The origin directory - * @param string $targetDir The target directory - * @param \Traversable $iterator A Traversable instance - * @param array $options An array of boolean options - * Valid options are: - * - $options['override'] Whether to override an existing file on copy or not (see copy()) - * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink()) - * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false) - * - * @throws IOException When file type is unknown - */ - public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array()) - { - $targetDir = rtrim($targetDir, '/\\'); - $originDir = rtrim($originDir, '/\\'); - - // Iterate in destination folder to remove obsolete entries - if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) { - $deleteIterator = $iterator; - if (null === $deleteIterator) { - $flags = \FilesystemIterator::SKIP_DOTS; - $deleteIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($targetDir, $flags), \RecursiveIteratorIterator::CHILD_FIRST); - } - foreach ($deleteIterator as $file) { - $origin = str_replace($targetDir, $originDir, $file->getPathname()); - if (!$this->exists($origin)) { - $this->remove($file); - } - } - } - - $copyOnWindows = false; - if (isset($options['copy_on_windows'])) { - $copyOnWindows = $options['copy_on_windows']; - } - - if (null === $iterator) { - $flags = $copyOnWindows ? \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS : \FilesystemIterator::SKIP_DOTS; - $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST); - } - - if ($this->exists($originDir)) { - $this->mkdir($targetDir); - } - - foreach ($iterator as $file) { - $target = str_replace($originDir, $targetDir, $file->getPathname()); - - if ($copyOnWindows) { - if (is_file($file)) { - $this->copy($file, $target, isset($options['override']) ? $options['override'] : false); - } elseif (is_dir($file)) { - $this->mkdir($target); - } else { - throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file); - } - } else { - if (is_link($file)) { - $this->symlink($file->getLinkTarget(), $target); - } elseif (is_dir($file)) { - $this->mkdir($target); - } elseif (is_file($file)) { - $this->copy($file, $target, isset($options['override']) ? $options['override'] : false); - } else { - throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file); - } - } - } - } - - /** - * Returns whether the file path is an absolute path. - * - * @param string $file A file path - * - * @return bool - */ - public function isAbsolutePath($file) - { - return strspn($file, '/\\', 0, 1) - || (strlen($file) > 3 && ctype_alpha($file[0]) - && substr($file, 1, 1) === ':' - && strspn($file, '/\\', 2, 1) - ) - || null !== parse_url($file, PHP_URL_SCHEME) - ; - } - - /** - * Creates a temporary file with support for custom stream wrappers. - * - * @param string $dir The directory where the temporary filename will be created - * @param string $prefix The prefix of the generated temporary filename - * Note: Windows uses only the first three characters of prefix - * - * @return string The new temporary filename (with path), or throw an exception on failure - */ - public function tempnam($dir, $prefix) - { - list($scheme, $hierarchy) = $this->getSchemeAndHierarchy($dir); - - // If no scheme or scheme is "file" or "gs" (Google Cloud) create temp file in local filesystem - if (null === $scheme || 'file' === $scheme || 'gs' === $scheme) { - $tmpFile = tempnam($hierarchy, $prefix); - - // If tempnam failed or no scheme return the filename otherwise prepend the scheme - if (false !== $tmpFile) { - if (null !== $scheme && 'gs' !== $scheme) { - return $scheme.'://'.$tmpFile; - } - - return $tmpFile; - } - - throw new IOException('A temporary file could not be created.'); - } - - // Loop until we create a valid temp file or have reached 10 attempts - for ($i = 0; $i < 10; ++$i) { - // Create a unique filename - $tmpFile = $dir.'/'.$prefix.uniqid(mt_rand(), true); - - // Use fopen instead of file_exists as some streams do not support stat - // Use mode 'x+' to atomically check existence and create to avoid a TOCTOU vulnerability - $handle = @fopen($tmpFile, 'x+'); - - // If unsuccessful restart the loop - if (false === $handle) { - continue; - } - - // Close the file if it was successfully opened - @fclose($handle); - - return $tmpFile; - } - - throw new IOException('A temporary file could not be created.'); - } - - /** - * Atomically dumps content into a file. - * - * @param string $filename The file to be written to - * @param string $content The data to write into the file - * - * @throws IOException If the file cannot be written to. - */ - public function dumpFile($filename, $content) - { - $dir = dirname($filename); - - if (!is_dir($dir)) { - $this->mkdir($dir); - } elseif (!is_writable($dir)) { - throw new IOException(sprintf('Unable to write to the "%s" directory.', $dir), 0, null, $dir); - } - - // Will create a temp file with 0600 access rights - // when the filesystem supports chmod. - $tmpFile = $this->tempnam($dir, basename($filename)); - - if (false === @file_put_contents($tmpFile, $content)) { - throw new IOException(sprintf('Failed to write file "%s".', $filename), 0, null, $filename); - } - - // Ignore for filesystems that do not support umask - @chmod($tmpFile, 0666); - $this->rename($tmpFile, $filename, true); - } - - /** - * @param mixed $files - * - * @return \Traversable - */ - private function toIterator($files) - { - if (!$files instanceof \Traversable) { - $files = new \ArrayObject(is_array($files) ? $files : array($files)); - } - - return $files; - } - - /** - * Gets a 2-tuple of scheme (may be null) and hierarchical part of a filename (e.g. file:///tmp -> array(file, tmp)). - * - * @param string $filename The filename to be parsed - * - * @return array The filename scheme and hierarchical part - */ - private function getSchemeAndHierarchy($filename) - { - $components = explode('://', $filename, 2); - - return 2 === count($components) ? array($components[0], $components[1]) : array(null, $components[0]); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/filesystem/LICENSE b/core/src/core/classes/guzzle/vendor/symfony/filesystem/LICENSE deleted file mode 100644 index 12a74531e4..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/filesystem/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2004-2016 Fabien Potencier - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/core/src/core/classes/guzzle/vendor/symfony/filesystem/LockHandler.php b/core/src/core/classes/guzzle/vendor/symfony/filesystem/LockHandler.php deleted file mode 100644 index 67e6f8f522..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/filesystem/LockHandler.php +++ /dev/null @@ -1,112 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem; - -use Symfony\Component\Filesystem\Exception\IOException; - -/** - * LockHandler class provides a simple abstraction to lock anything by means of - * a file lock. - * - * A locked file is created based on the lock name when calling lock(). Other - * lock handlers will not be able to lock the same name until it is released - * (explicitly by calling release() or implicitly when the instance holding the - * lock is destroyed). - * - * @author Grégoire Pineau - * @author Romain Neutron - * @author Nicolas Grekas - */ -class LockHandler -{ - private $file; - private $handle; - - /** - * @param string $name The lock name - * @param string|null $lockPath The directory to store the lock. Default values will use temporary directory - * - * @throws IOException If the lock directory could not be created or is not writable - */ - public function __construct($name, $lockPath = null) - { - $lockPath = $lockPath ?: sys_get_temp_dir(); - - if (!is_dir($lockPath)) { - $fs = new Filesystem(); - $fs->mkdir($lockPath); - } - - if (!is_writable($lockPath)) { - throw new IOException(sprintf('The directory "%s" is not writable.', $lockPath), 0, null, $lockPath); - } - - $this->file = sprintf('%s/sf.%s.%s.lock', $lockPath, preg_replace('/[^a-z0-9\._-]+/i', '-', $name), hash('sha256', $name)); - } - - /** - * Lock the resource. - * - * @param bool $blocking wait until the lock is released - * - * @return bool Returns true if the lock was acquired, false otherwise - * - * @throws IOException If the lock file could not be created or opened - */ - public function lock($blocking = false) - { - if ($this->handle) { - return true; - } - - // Silence error reporting - set_error_handler(function () {}); - - if (!$this->handle = fopen($this->file, 'r')) { - if ($this->handle = fopen($this->file, 'x')) { - chmod($this->file, 0444); - } elseif (!$this->handle = fopen($this->file, 'r')) { - usleep(100); // Give some time for chmod() to complete - $this->handle = fopen($this->file, 'r'); - } - } - restore_error_handler(); - - if (!$this->handle) { - $error = error_get_last(); - throw new IOException($error['message'], 0, null, $this->file); - } - - // On Windows, even if PHP doc says the contrary, LOCK_NB works, see - // https://bugs.php.net/54129 - if (!flock($this->handle, LOCK_EX | ($blocking ? 0 : LOCK_NB))) { - fclose($this->handle); - $this->handle = null; - - return false; - } - - return true; - } - - /** - * Release the resource. - */ - public function release() - { - if ($this->handle) { - flock($this->handle, LOCK_UN | LOCK_NB); - fclose($this->handle); - $this->handle = null; - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/filesystem/README.md b/core/src/core/classes/guzzle/vendor/symfony/filesystem/README.md deleted file mode 100644 index 877ab3543f..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/filesystem/README.md +++ /dev/null @@ -1,13 +0,0 @@ -Filesystem Component -==================== - -The Filesystem component provides basic utilities for the filesystem. - -Resources ---------- - - * [Documentation](https://symfony.com/doc/current/components/filesystem/index.html) - * [Contributing](https://symfony.com/doc/current/contributing/index.html) - * [Report issues](https://github.com/symfony/symfony/issues) and - [send Pull Requests](https://github.com/symfony/symfony/pulls) - in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Tests/ExceptionTest.php b/core/src/core/classes/guzzle/vendor/symfony/filesystem/Tests/ExceptionTest.php deleted file mode 100644 index 53bd8db76a..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Tests/ExceptionTest.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Tests; - -use Symfony\Component\Filesystem\Exception\IOException; -use Symfony\Component\Filesystem\Exception\FileNotFoundException; - -/** - * Test class for Filesystem. - */ -class ExceptionTest extends \PHPUnit_Framework_TestCase -{ - public function testGetPath() - { - $e = new IOException('', 0, null, '/foo'); - $this->assertEquals('/foo', $e->getPath(), 'The pass should be returned.'); - } - - public function testGeneratedMessage() - { - $e = new FileNotFoundException(null, 0, null, '/foo'); - $this->assertEquals('/foo', $e->getPath()); - $this->assertEquals('File "/foo" could not be found.', $e->getMessage(), 'A message should be generated.'); - } - - public function testGeneratedMessageWithoutPath() - { - $e = new FileNotFoundException(); - $this->assertEquals('File could not be found.', $e->getMessage(), 'A message should be generated.'); - } - - public function testCustomMessage() - { - $e = new FileNotFoundException('bar', 0, null, '/foo'); - $this->assertEquals('bar', $e->getMessage(), 'A custom message should be possible still.'); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Tests/FilesystemTest.php b/core/src/core/classes/guzzle/vendor/symfony/filesystem/Tests/FilesystemTest.php deleted file mode 100644 index 9a65543098..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Tests/FilesystemTest.php +++ /dev/null @@ -1,1166 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Tests; - -/** - * Test class for Filesystem. - */ -class FilesystemTest extends FilesystemTestCase -{ - public function testCopyCreatesNewFile() - { - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - - $this->filesystem->copy($sourceFilePath, $targetFilePath); - - $this->assertFileExists($targetFilePath); - $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testCopyFails() - { - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - $this->filesystem->copy($sourceFilePath, $targetFilePath); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testCopyUnreadableFileFails() - { - // skip test on Windows; PHP can't easily set file as unreadable on Windows - if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('This test cannot run on Windows.'); - } - - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - - // make sure target cannot be read - $this->filesystem->chmod($sourceFilePath, 0222); - - $this->filesystem->copy($sourceFilePath, $targetFilePath); - } - - public function testCopyOverridesExistingFileIfModified() - { - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - file_put_contents($targetFilePath, 'TARGET FILE'); - touch($targetFilePath, time() - 1000); - - $this->filesystem->copy($sourceFilePath, $targetFilePath); - - $this->assertFileExists($targetFilePath); - $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); - } - - public function testCopyDoesNotOverrideExistingFileByDefault() - { - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - file_put_contents($targetFilePath, 'TARGET FILE'); - - // make sure both files have the same modification time - $modificationTime = time() - 1000; - touch($sourceFilePath, $modificationTime); - touch($targetFilePath, $modificationTime); - - $this->filesystem->copy($sourceFilePath, $targetFilePath); - - $this->assertFileExists($targetFilePath); - $this->assertEquals('TARGET FILE', file_get_contents($targetFilePath)); - } - - public function testCopyOverridesExistingFileIfForced() - { - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - file_put_contents($targetFilePath, 'TARGET FILE'); - - // make sure both files have the same modification time - $modificationTime = time() - 1000; - touch($sourceFilePath, $modificationTime); - touch($targetFilePath, $modificationTime); - - $this->filesystem->copy($sourceFilePath, $targetFilePath, true); - - $this->assertFileExists($targetFilePath); - $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testCopyWithOverrideWithReadOnlyTargetFails() - { - // skip test on Windows; PHP can't easily set file as unwritable on Windows - if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('This test cannot run on Windows.'); - } - - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - file_put_contents($targetFilePath, 'TARGET FILE'); - - // make sure both files have the same modification time - $modificationTime = time() - 1000; - touch($sourceFilePath, $modificationTime); - touch($targetFilePath, $modificationTime); - - // make sure target is read-only - $this->filesystem->chmod($targetFilePath, 0444); - - $this->filesystem->copy($sourceFilePath, $targetFilePath, true); - } - - public function testCopyCreatesTargetDirectoryIfItDoesNotExist() - { - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFileDirectory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; - $targetFilePath = $targetFileDirectory.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - - $this->filesystem->copy($sourceFilePath, $targetFilePath); - - $this->assertTrue(is_dir($targetFileDirectory)); - $this->assertFileExists($targetFilePath); - $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); - } - - public function testCopyForOriginUrlsAndExistingLocalFileDefaultsToCopy() - { - $sourceFilePath = 'http://symfony.com/images/common/logo/logo_symfony_header.png'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($targetFilePath, 'TARGET FILE'); - - $this->filesystem->copy($sourceFilePath, $targetFilePath, false); - - $this->assertFileExists($targetFilePath); - $this->assertEquals(file_get_contents($sourceFilePath), file_get_contents($targetFilePath)); - } - - public function testMkdirCreatesDirectoriesRecursively() - { - $directory = $this->workspace - .DIRECTORY_SEPARATOR.'directory' - .DIRECTORY_SEPARATOR.'sub_directory'; - - $this->filesystem->mkdir($directory); - - $this->assertTrue(is_dir($directory)); - } - - public function testMkdirCreatesDirectoriesFromArray() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - $directories = array( - $basePath.'1', $basePath.'2', $basePath.'3', - ); - - $this->filesystem->mkdir($directories); - - $this->assertTrue(is_dir($basePath.'1')); - $this->assertTrue(is_dir($basePath.'2')); - $this->assertTrue(is_dir($basePath.'3')); - } - - public function testMkdirCreatesDirectoriesFromTraversableObject() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - $directories = new \ArrayObject(array( - $basePath.'1', $basePath.'2', $basePath.'3', - )); - - $this->filesystem->mkdir($directories); - - $this->assertTrue(is_dir($basePath.'1')); - $this->assertTrue(is_dir($basePath.'2')); - $this->assertTrue(is_dir($basePath.'3')); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testMkdirCreatesDirectoriesFails() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - $dir = $basePath.'2'; - - file_put_contents($dir, ''); - - $this->filesystem->mkdir($dir); - } - - public function testTouchCreatesEmptyFile() - { - $file = $this->workspace.DIRECTORY_SEPARATOR.'1'; - - $this->filesystem->touch($file); - - $this->assertFileExists($file); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testTouchFails() - { - $file = $this->workspace.DIRECTORY_SEPARATOR.'1'.DIRECTORY_SEPARATOR.'2'; - - $this->filesystem->touch($file); - } - - public function testTouchCreatesEmptyFilesFromArray() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - $files = array( - $basePath.'1', $basePath.'2', $basePath.'3', - ); - - $this->filesystem->touch($files); - - $this->assertFileExists($basePath.'1'); - $this->assertFileExists($basePath.'2'); - $this->assertFileExists($basePath.'3'); - } - - public function testTouchCreatesEmptyFilesFromTraversableObject() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - $files = new \ArrayObject(array( - $basePath.'1', $basePath.'2', $basePath.'3', - )); - - $this->filesystem->touch($files); - - $this->assertFileExists($basePath.'1'); - $this->assertFileExists($basePath.'2'); - $this->assertFileExists($basePath.'3'); - } - - public function testRemoveCleansFilesAndDirectoriesIteratively() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; - - mkdir($basePath); - mkdir($basePath.'dir'); - touch($basePath.'file'); - - $this->filesystem->remove($basePath); - - $this->assertFileNotExists($basePath); - } - - public function testRemoveCleansArrayOfFilesAndDirectories() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - - mkdir($basePath.'dir'); - touch($basePath.'file'); - - $files = array( - $basePath.'dir', $basePath.'file', - ); - - $this->filesystem->remove($files); - - $this->assertFileNotExists($basePath.'dir'); - $this->assertFileNotExists($basePath.'file'); - } - - public function testRemoveCleansTraversableObjectOfFilesAndDirectories() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - - mkdir($basePath.'dir'); - touch($basePath.'file'); - - $files = new \ArrayObject(array( - $basePath.'dir', $basePath.'file', - )); - - $this->filesystem->remove($files); - - $this->assertFileNotExists($basePath.'dir'); - $this->assertFileNotExists($basePath.'file'); - } - - public function testRemoveIgnoresNonExistingFiles() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - - mkdir($basePath.'dir'); - - $files = array( - $basePath.'dir', $basePath.'file', - ); - - $this->filesystem->remove($files); - - $this->assertFileNotExists($basePath.'dir'); - } - - public function testRemoveCleansInvalidLinks() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; - - mkdir($basePath); - mkdir($basePath.'dir'); - // create symlink to nonexistent file - @symlink($basePath.'file', $basePath.'file-link'); - - // create symlink to dir using trailing forward slash - $this->filesystem->symlink($basePath.'dir/', $basePath.'dir-link'); - $this->assertTrue(is_dir($basePath.'dir-link')); - - // create symlink to nonexistent dir - rmdir($basePath.'dir'); - $this->assertFalse('\\' === DIRECTORY_SEPARATOR ? @readlink($basePath.'dir-link') : is_dir($basePath.'dir-link')); - - $this->filesystem->remove($basePath); - - $this->assertFileNotExists($basePath); - } - - public function testFilesExists() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; - - mkdir($basePath); - touch($basePath.'file1'); - mkdir($basePath.'folder'); - - $this->assertTrue($this->filesystem->exists($basePath.'file1')); - $this->assertTrue($this->filesystem->exists($basePath.'folder')); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testFilesExistsFails() - { - if ('\\' !== DIRECTORY_SEPARATOR) { - $this->markTestSkipped('Test covers edge case on Windows only.'); - } - - $basePath = $this->workspace.'\\directory\\'; - - $oldPath = getcwd(); - mkdir($basePath); - chdir($basePath); - $file = str_repeat('T', 259 - strlen($basePath)); - $path = $basePath.$file; - exec('TYPE NUL >>'.$file); // equivalent of touch, we can not use the php touch() here because it suffers from the same limitation - $this->longPathNamesWindows[] = $path; // save this so we can clean up later - chdir($oldPath); - $this->filesystem->exists($path); - } - - public function testFilesExistsTraversableObjectOfFilesAndDirectories() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - - mkdir($basePath.'dir'); - touch($basePath.'file'); - - $files = new \ArrayObject(array( - $basePath.'dir', $basePath.'file', - )); - - $this->assertTrue($this->filesystem->exists($files)); - } - - public function testFilesNotExistsTraversableObjectOfFilesAndDirectories() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - - mkdir($basePath.'dir'); - touch($basePath.'file'); - touch($basePath.'file2'); - - $files = new \ArrayObject(array( - $basePath.'dir', $basePath.'file', $basePath.'file2', - )); - - unlink($basePath.'file'); - - $this->assertFalse($this->filesystem->exists($files)); - } - - public function testInvalidFileNotExists() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; - - $this->assertFalse($this->filesystem->exists($basePath.time())); - } - - public function testChmodChangesFileMode() - { - $this->markAsSkippedIfChmodIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - $file = $dir.DIRECTORY_SEPARATOR.'file'; - touch($file); - - $this->filesystem->chmod($file, 0400); - $this->filesystem->chmod($dir, 0753); - - $this->assertFilePermissions(753, $dir); - $this->assertFilePermissions(400, $file); - } - - public function testChmodWrongMod() - { - $this->markAsSkippedIfChmodIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'file'; - touch($dir); - - $this->filesystem->chmod($dir, 'Wrongmode'); - } - - public function testChmodRecursive() - { - $this->markAsSkippedIfChmodIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - $file = $dir.DIRECTORY_SEPARATOR.'file'; - touch($file); - - $this->filesystem->chmod($file, 0400, 0000, true); - $this->filesystem->chmod($dir, 0753, 0000, true); - - $this->assertFilePermissions(753, $dir); - $this->assertFilePermissions(753, $file); - } - - public function testChmodAppliesUmask() - { - $this->markAsSkippedIfChmodIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - touch($file); - - $this->filesystem->chmod($file, 0770, 0022); - $this->assertFilePermissions(750, $file); - } - - public function testChmodChangesModeOfArrayOfFiles() - { - $this->markAsSkippedIfChmodIsMissing(); - - $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $files = array($directory, $file); - - mkdir($directory); - touch($file); - - $this->filesystem->chmod($files, 0753); - - $this->assertFilePermissions(753, $file); - $this->assertFilePermissions(753, $directory); - } - - public function testChmodChangesModeOfTraversableFileObject() - { - $this->markAsSkippedIfChmodIsMissing(); - - $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $files = new \ArrayObject(array($directory, $file)); - - mkdir($directory); - touch($file); - - $this->filesystem->chmod($files, 0753); - - $this->assertFilePermissions(753, $file); - $this->assertFilePermissions(753, $directory); - } - - public function testChmodChangesZeroModeOnSubdirectoriesOnRecursive() - { - $this->markAsSkippedIfChmodIsMissing(); - - $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; - $subdirectory = $directory.DIRECTORY_SEPARATOR.'subdirectory'; - - mkdir($directory); - mkdir($subdirectory); - chmod($subdirectory, 0000); - - $this->filesystem->chmod($directory, 0753, 0000, true); - - $this->assertFilePermissions(753, $subdirectory); - } - - public function testChown() - { - $this->markAsSkippedIfPosixIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - - $this->filesystem->chown($dir, $this->getFileOwner($dir)); - } - - public function testChownRecursive() - { - $this->markAsSkippedIfPosixIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - $file = $dir.DIRECTORY_SEPARATOR.'file'; - touch($file); - - $this->filesystem->chown($dir, $this->getFileOwner($dir), true); - } - - public function testChownSymlink() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->symlink($file, $link); - - $this->filesystem->chown($link, $this->getFileOwner($link)); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testChownSymlinkFails() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->symlink($file, $link); - - $this->filesystem->chown($link, 'user'.time().mt_rand(1000, 9999)); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testChownFail() - { - $this->markAsSkippedIfPosixIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - - $this->filesystem->chown($dir, 'user'.time().mt_rand(1000, 9999)); - } - - public function testChgrp() - { - $this->markAsSkippedIfPosixIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - - $this->filesystem->chgrp($dir, $this->getFileGroup($dir)); - } - - public function testChgrpRecursive() - { - $this->markAsSkippedIfPosixIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - $file = $dir.DIRECTORY_SEPARATOR.'file'; - touch($file); - - $this->filesystem->chgrp($dir, $this->getFileGroup($dir), true); - } - - public function testChgrpSymlink() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->symlink($file, $link); - - $this->filesystem->chgrp($link, $this->getFileGroup($link)); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testChgrpSymlinkFails() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->symlink($file, $link); - - $this->filesystem->chgrp($link, 'user'.time().mt_rand(1000, 9999)); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testChgrpFail() - { - $this->markAsSkippedIfPosixIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - - $this->filesystem->chgrp($dir, 'user'.time().mt_rand(1000, 9999)); - } - - public function testRename() - { - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; - touch($file); - - $this->filesystem->rename($file, $newPath); - - $this->assertFileNotExists($file); - $this->assertFileExists($newPath); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testRenameThrowsExceptionIfTargetAlreadyExists() - { - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; - - touch($file); - touch($newPath); - - $this->filesystem->rename($file, $newPath); - } - - public function testRenameOverwritesTheTargetIfItAlreadyExists() - { - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; - - touch($file); - touch($newPath); - - $this->filesystem->rename($file, $newPath, true); - - $this->assertFileNotExists($file); - $this->assertFileExists($newPath); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testRenameThrowsExceptionOnError() - { - $file = $this->workspace.DIRECTORY_SEPARATOR.uniqid('fs_test_', true); - $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; - - $this->filesystem->rename($file, $newPath); - } - - public function testSymlink() - { - if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('Windows does not support creating "broken" symlinks'); - } - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - // $file does not exists right now: creating "broken" links is a wanted feature - $this->filesystem->symlink($file, $link); - - $this->assertTrue(is_link($link)); - - // Create the linked file AFTER creating the link - touch($file); - - $this->assertEquals($file, readlink($link)); - } - - /** - * @depends testSymlink - */ - public function testRemoveSymlink() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - $this->filesystem->remove($link); - - $this->assertTrue(!is_link($link)); - $this->assertTrue(!is_file($link)); - $this->assertTrue(!is_dir($link)); - } - - public function testSymlinkIsOverwrittenIfPointsToDifferentTarget() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - symlink($this->workspace, $link); - - $this->filesystem->symlink($file, $link); - - $this->assertTrue(is_link($link)); - $this->assertEquals($file, readlink($link)); - } - - public function testSymlinkIsNotOverwrittenIfAlreadyCreated() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - symlink($file, $link); - - $this->filesystem->symlink($file, $link); - - $this->assertTrue(is_link($link)); - $this->assertEquals($file, readlink($link)); - } - - public function testSymlinkCreatesTargetDirectoryIfItDoesNotExist() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link1 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'link'; - $link2 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'subdir'.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->symlink($file, $link1); - $this->filesystem->symlink($file, $link2); - - $this->assertTrue(is_link($link1)); - $this->assertEquals($file, readlink($link1)); - $this->assertTrue(is_link($link2)); - $this->assertEquals($file, readlink($link2)); - } - - /** - * @dataProvider providePathsForMakePathRelative - */ - public function testMakePathRelative($endPath, $startPath, $expectedPath) - { - $path = $this->filesystem->makePathRelative($endPath, $startPath); - - $this->assertEquals($expectedPath, $path); - } - - /** - * @return array - */ - public function providePathsForMakePathRelative() - { - $paths = array( - array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/src/Symfony/Component', '../'), - array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/src/Symfony/Component/', '../'), - array('/var/lib/symfony/src/Symfony', '/var/lib/symfony/src/Symfony/Component', '../'), - array('/var/lib/symfony/src/Symfony', '/var/lib/symfony/src/Symfony/Component/', '../'), - array('var/lib/symfony/', 'var/lib/symfony/src/Symfony/Component', '../../../'), - array('/usr/lib/symfony/', '/var/lib/symfony/src/Symfony/Component', '../../../../../../usr/lib/symfony/'), - array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/', 'src/Symfony/'), - array('/aa/bb', '/aa/bb', './'), - array('/aa/bb', '/aa/bb/', './'), - array('/aa/bb/', '/aa/bb', './'), - array('/aa/bb/', '/aa/bb/', './'), - array('/aa/bb/cc', '/aa/bb/cc/dd', '../'), - array('/aa/bb/cc', '/aa/bb/cc/dd/', '../'), - array('/aa/bb/cc/', '/aa/bb/cc/dd', '../'), - array('/aa/bb/cc/', '/aa/bb/cc/dd/', '../'), - array('/aa/bb/cc', '/aa', 'bb/cc/'), - array('/aa/bb/cc', '/aa/', 'bb/cc/'), - array('/aa/bb/cc/', '/aa', 'bb/cc/'), - array('/aa/bb/cc/', '/aa/', 'bb/cc/'), - array('/a/aab/bb', '/a/aa', '../aab/bb/'), - array('/a/aab/bb', '/a/aa/', '../aab/bb/'), - array('/a/aab/bb/', '/a/aa', '../aab/bb/'), - array('/a/aab/bb/', '/a/aa/', '../aab/bb/'), - array('/a/aab/bb/', '/', 'a/aab/bb/'), - array('/a/aab/bb/', '/b/aab', '../../a/aab/bb/'), - ); - - if ('\\' === DIRECTORY_SEPARATOR) { - $paths[] = array('c:\var\lib/symfony/src/Symfony/', 'c:/var/lib/symfony/', 'src/Symfony/'); - } - - return $paths; - } - - public function testMirrorCopiesFilesAndDirectoriesRecursively() - { - $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; - $directory = $sourcePath.'directory'.DIRECTORY_SEPARATOR; - $file1 = $directory.'file1'; - $file2 = $sourcePath.'file2'; - - mkdir($sourcePath); - mkdir($directory); - file_put_contents($file1, 'FILE1'); - file_put_contents($file2, 'FILE2'); - - $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; - - $this->filesystem->mirror($sourcePath, $targetPath); - - $this->assertTrue(is_dir($targetPath)); - $this->assertTrue(is_dir($targetPath.'directory')); - $this->assertFileEquals($file1, $targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'); - $this->assertFileEquals($file2, $targetPath.'file2'); - - $this->filesystem->remove($file1); - - $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => false)); - $this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); - - $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); - $this->assertFalse($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); - - file_put_contents($file1, 'FILE1'); - - $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); - $this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); - - $this->filesystem->remove($directory); - $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); - $this->assertFalse($this->filesystem->exists($targetPath.'directory')); - $this->assertFalse($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); - } - - public function testMirrorCreatesEmptyDirectory() - { - $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; - - mkdir($sourcePath); - - $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; - - $this->filesystem->mirror($sourcePath, $targetPath); - - $this->assertTrue(is_dir($targetPath)); - - $this->filesystem->remove($sourcePath); - } - - public function testMirrorCopiesLinks() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; - - mkdir($sourcePath); - file_put_contents($sourcePath.'file1', 'FILE1'); - symlink($sourcePath.'file1', $sourcePath.'link1'); - - $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; - - $this->filesystem->mirror($sourcePath, $targetPath); - - $this->assertTrue(is_dir($targetPath)); - $this->assertFileEquals($sourcePath.'file1', $targetPath.'link1'); - $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1')); - } - - public function testMirrorCopiesLinkedDirectoryContents() - { - $this->markAsSkippedIfSymlinkIsMissing(true); - - $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; - - mkdir($sourcePath.'nested/', 0777, true); - file_put_contents($sourcePath.'/nested/file1.txt', 'FILE1'); - // Note: We symlink directory, not file - symlink($sourcePath.'nested', $sourcePath.'link1'); - - $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; - - $this->filesystem->mirror($sourcePath, $targetPath); - - $this->assertTrue(is_dir($targetPath)); - $this->assertFileEquals($sourcePath.'/nested/file1.txt', $targetPath.'link1/file1.txt'); - $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1')); - } - - public function testMirrorCopiesRelativeLinkedContents() - { - $this->markAsSkippedIfSymlinkIsMissing(true); - - $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; - $oldPath = getcwd(); - - mkdir($sourcePath.'nested/', 0777, true); - file_put_contents($sourcePath.'/nested/file1.txt', 'FILE1'); - // Note: Create relative symlink - chdir($sourcePath); - symlink('nested', 'link1'); - - chdir($oldPath); - - $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; - - $this->filesystem->mirror($sourcePath, $targetPath); - - $this->assertTrue(is_dir($targetPath)); - $this->assertFileEquals($sourcePath.'/nested/file1.txt', $targetPath.'link1/file1.txt'); - $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1')); - $this->assertEquals('\\' === DIRECTORY_SEPARATOR ? realpath($sourcePath.'\nested') : 'nested', readlink($targetPath.DIRECTORY_SEPARATOR.'link1')); - } - - /** - * @dataProvider providePathsForIsAbsolutePath - */ - public function testIsAbsolutePath($path, $expectedResult) - { - $result = $this->filesystem->isAbsolutePath($path); - - $this->assertEquals($expectedResult, $result); - } - - /** - * @return array - */ - public function providePathsForIsAbsolutePath() - { - return array( - array('/var/lib', true), - array('c:\\\\var\\lib', true), - array('\\var\\lib', true), - array('var/lib', false), - array('../var/lib', false), - array('', false), - array(null, false), - ); - } - - public function testTempnam() - { - $dirname = $this->workspace; - - $filename = $this->filesystem->tempnam($dirname, 'foo'); - - $this->assertFileExists($filename); - } - - public function testTempnamWithFileScheme() - { - $scheme = 'file://'; - $dirname = $scheme.$this->workspace; - - $filename = $this->filesystem->tempnam($dirname, 'foo'); - - $this->assertStringStartsWith($scheme, $filename); - $this->assertFileExists($filename); - } - - public function testTempnamWithMockScheme() - { - stream_wrapper_register('mock', 'Symfony\Component\Filesystem\Tests\Fixtures\MockStream\MockStream'); - - $scheme = 'mock://'; - $dirname = $scheme.$this->workspace; - - $filename = $this->filesystem->tempnam($dirname, 'foo'); - - $this->assertStringStartsWith($scheme, $filename); - $this->assertFileExists($filename); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testTempnamWithZlibSchemeFails() - { - $scheme = 'compress.zlib://'; - $dirname = $scheme.$this->workspace; - - // The compress.zlib:// stream does not support mode x: creates the file, errors "failed to open stream: operation failed" and returns false - $this->filesystem->tempnam($dirname, 'bar'); - } - - public function testTempnamWithPHPTempSchemeFails() - { - $scheme = 'php://temp'; - $dirname = $scheme; - - $filename = $this->filesystem->tempnam($dirname, 'bar'); - - $this->assertStringStartsWith($scheme, $filename); - - // The php://temp stream deletes the file after close - $this->assertFileNotExists($filename); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testTempnamWithPharSchemeFails() - { - // Skip test if Phar disabled phar.readonly must be 0 in php.ini - if (!\Phar::canWrite()) { - $this->markTestSkipped('This test cannot run when phar.readonly is 1.'); - } - - $scheme = 'phar://'; - $dirname = $scheme.$this->workspace; - $pharname = 'foo.phar'; - - new \Phar($this->workspace.'/'.$pharname, 0, $pharname); - // The phar:// stream does not support mode x: fails to create file, errors "failed to open stream: phar error: "$filename" is not a file in phar "$pharname"" and returns false - $this->filesystem->tempnam($dirname, $pharname.'/bar'); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testTempnamWithHTTPSchemeFails() - { - $scheme = 'http://'; - $dirname = $scheme.$this->workspace; - - // The http:// scheme is read-only - $this->filesystem->tempnam($dirname, 'bar'); - } - - public function testTempnamOnUnwritableFallsBackToSysTmp() - { - $scheme = 'file://'; - $dirname = $scheme.$this->workspace.DIRECTORY_SEPARATOR.'does_not_exist'; - - $filename = $this->filesystem->tempnam($dirname, 'bar'); - $realTempDir = realpath(sys_get_temp_dir()); - $this->assertStringStartsWith(rtrim($scheme.$realTempDir, DIRECTORY_SEPARATOR), $filename); - $this->assertFileExists($filename); - - // Tear down - @unlink($filename); - } - - public function testDumpFile() - { - $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt'; - - $this->filesystem->dumpFile($filename, 'bar'); - $this->assertFileExists($filename); - $this->assertSame('bar', file_get_contents($filename)); - - // skip mode check on Windows - if ('\\' !== DIRECTORY_SEPARATOR) { - $this->assertFilePermissions(666, $filename); - } - } - - public function testDumpFileOverwritesAnExistingFile() - { - $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo.txt'; - file_put_contents($filename, 'FOO BAR'); - - $this->filesystem->dumpFile($filename, 'bar'); - - $this->assertFileExists($filename); - $this->assertSame('bar', file_get_contents($filename)); - } - - public function testDumpFileWithFileScheme() - { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('HHVM does not handle the file:// scheme correctly'); - } - - $scheme = 'file://'; - $filename = $scheme.$this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt'; - - $this->filesystem->dumpFile($filename, 'bar', null); - - $this->assertFileExists($filename); - $this->assertSame('bar', file_get_contents($filename)); - } - - public function testDumpFileWithZlibScheme() - { - $scheme = 'compress.zlib://'; - $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt'; - - $this->filesystem->dumpFile($filename, 'bar', null); - - // Zlib stat uses file:// wrapper so remove scheme - $this->assertFileExists(str_replace($scheme, '', $filename)); - $this->assertSame('bar', file_get_contents($filename)); - } - - public function testCopyShouldKeepExecutionPermission() - { - $this->markAsSkippedIfChmodIsMissing(); - - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - chmod($sourceFilePath, 0745); - - $this->filesystem->copy($sourceFilePath, $targetFilePath); - - $this->assertFilePermissions(767, $targetFilePath); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Tests/FilesystemTestCase.php b/core/src/core/classes/guzzle/vendor/symfony/filesystem/Tests/FilesystemTestCase.php deleted file mode 100644 index 63d8b8fc90..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Tests/FilesystemTestCase.php +++ /dev/null @@ -1,128 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Tests; - -use Symfony\Component\Filesystem\Filesystem; - -class FilesystemTestCase extends \PHPUnit_Framework_TestCase -{ - private $umask; - - protected $longPathNamesWindows = array(); - - /** - * @var \Symfony\Component\Filesystem\Filesystem - */ - protected $filesystem = null; - - /** - * @var string - */ - protected $workspace = null; - - private static $symlinkOnWindows = null; - - public static function setUpBeforeClass() - { - if ('\\' === DIRECTORY_SEPARATOR && null === self::$symlinkOnWindows) { - $target = tempnam(sys_get_temp_dir(), 'sl'); - $link = sys_get_temp_dir().'/sl'.microtime(true).mt_rand(); - self::$symlinkOnWindows = @symlink($target, $link) && is_link($link); - @unlink($link); - unlink($target); - } - } - - protected function setUp() - { - $this->umask = umask(0); - $this->filesystem = new Filesystem(); - $this->workspace = sys_get_temp_dir().'/'.microtime(true).'.'.mt_rand(); - mkdir($this->workspace, 0777, true); - $this->workspace = realpath($this->workspace); - } - - protected function tearDown() - { - if (!empty($this->longPathNamesWindows)) { - foreach ($this->longPathNamesWindows as $path) { - exec('DEL '.$path); - } - $this->longPathNamesWindows = array(); - } - - $this->filesystem->remove($this->workspace); - umask($this->umask); - } - - /** - * @param int $expectedFilePerms expected file permissions as three digits (i.e. 755) - * @param string $filePath - */ - protected function assertFilePermissions($expectedFilePerms, $filePath) - { - $actualFilePerms = (int) substr(sprintf('%o', fileperms($filePath)), -3); - $this->assertEquals( - $expectedFilePerms, - $actualFilePerms, - sprintf('File permissions for %s must be %s. Actual %s', $filePath, $expectedFilePerms, $actualFilePerms) - ); - } - - protected function getFileOwner($filepath) - { - $this->markAsSkippedIfPosixIsMissing(); - - $infos = stat($filepath); - if ($datas = posix_getpwuid($infos['uid'])) { - return $datas['name']; - } - } - - protected function getFileGroup($filepath) - { - $this->markAsSkippedIfPosixIsMissing(); - - $infos = stat($filepath); - if ($datas = posix_getgrgid($infos['gid'])) { - return $datas['name']; - } - - $this->markTestSkipped('Unable to retrieve file group name'); - } - - protected function markAsSkippedIfSymlinkIsMissing($relative = false) - { - if ('\\' === DIRECTORY_SEPARATOR && false === self::$symlinkOnWindows) { - $this->markTestSkipped('symlink requires "Create symbolic links" privilege on Windows'); - } - - // https://bugs.php.net/bug.php?id=69473 - if ($relative && '\\' === DIRECTORY_SEPARATOR && 1 === PHP_ZTS) { - $this->markTestSkipped('symlink does not support relative paths on thread safe Windows PHP versions'); - } - } - - protected function markAsSkippedIfChmodIsMissing() - { - if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('chmod is not supported on Windows'); - } - } - - protected function markAsSkippedIfPosixIsMissing() - { - if (!function_exists('posix_isatty')) { - $this->markTestSkipped('Function posix_isatty is required.'); - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Tests/Fixtures/MockStream/MockStream.php b/core/src/core/classes/guzzle/vendor/symfony/filesystem/Tests/Fixtures/MockStream/MockStream.php deleted file mode 100644 index f14420fb60..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Tests/Fixtures/MockStream/MockStream.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Tests\Fixtures\MockStream; - -/** - * Mock stream class to be used with stream_wrapper_register. - * stream_wrapper_register('mock', 'Symfony\Component\Filesystem\Tests\Fixtures\MockStream\MockStream'). - */ -class MockStream -{ - /** - * Opens file or URL. - * - * @param string $path Specifies the URL that was passed to the original function - * @param string $mode The mode used to open the file, as detailed for fopen() - * @param int $options Holds additional flags set by the streams API - * @param string $opened_path If the path is opened successfully, and STREAM_USE_PATH is set in options, - * opened_path should be set to the full path of the file/resource that was actually opened - * - * @return bool - */ - public function stream_open($path, $mode, $options, &$opened_path) - { - return true; - } - - /** - * @param string $path The file path or URL to stat - * @param array $flags Holds additional flags set by the streams API - * - * @return array File stats - */ - public function url_stat($path, $flags) - { - return array(); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Tests/LockHandlerTest.php b/core/src/core/classes/guzzle/vendor/symfony/filesystem/Tests/LockHandlerTest.php deleted file mode 100644 index c7509f61e6..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/filesystem/Tests/LockHandlerTest.php +++ /dev/null @@ -1,100 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Tests; - -use Symfony\Component\Filesystem\LockHandler; - -class LockHandlerTest extends \PHPUnit_Framework_TestCase -{ - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - * @expectedExceptionMessage Failed to create "/a/b/c/d/e": mkdir(): Permission denied. - */ - public function testConstructWhenRepositoryDoesNotExist() - { - if (!getenv('USER') || 'root' === getenv('USER')) { - $this->markTestSkipped('This test will fail if run under superuser'); - } - new LockHandler('lock', '/a/b/c/d/e'); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - * @expectedExceptionMessage The directory "/" is not writable. - */ - public function testConstructWhenRepositoryIsNotWriteable() - { - if (!getenv('USER') || 'root' === getenv('USER')) { - $this->markTestSkipped('This test will fail if run under superuser'); - } - new LockHandler('lock', '/'); - } - - public function testConstructSanitizeName() - { - $lock = new LockHandler(''); - - $file = sprintf('%s/sf.-php-echo-hello-word-.4b3d9d0d27ddef3a78a64685dda3a963e478659a9e5240feaf7b4173a8f28d5f.lock', sys_get_temp_dir()); - // ensure the file does not exist before the lock - @unlink($file); - - $lock->lock(); - - $this->assertFileExists($file); - - $lock->release(); - } - - public function testLockRelease() - { - $name = 'symfony-test-filesystem.lock'; - - $l1 = new LockHandler($name); - $l2 = new LockHandler($name); - - $this->assertTrue($l1->lock()); - $this->assertFalse($l2->lock()); - - $l1->release(); - - $this->assertTrue($l2->lock()); - $l2->release(); - } - - public function testLockTwice() - { - $name = 'symfony-test-filesystem.lock'; - - $lockHandler = new LockHandler($name); - - $this->assertTrue($lockHandler->lock()); - $this->assertTrue($lockHandler->lock()); - - $lockHandler->release(); - } - - public function testLockIsReleased() - { - $name = 'symfony-test-filesystem.lock'; - - $l1 = new LockHandler($name); - $l2 = new LockHandler($name); - - $this->assertTrue($l1->lock()); - $this->assertFalse($l2->lock()); - - $l1 = null; - - $this->assertTrue($l2->lock()); - $l2->release(); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/filesystem/composer.json b/core/src/core/classes/guzzle/vendor/symfony/filesystem/composer.json deleted file mode 100644 index cead795d0f..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/filesystem/composer.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "symfony/filesystem", - "type": "library", - "description": "Symfony Filesystem Component", - "keywords": [], - "homepage": "https://symfony.com", - "license": "MIT", - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "require": { - "php": ">=5.5.9" - }, - "autoload": { - "psr-4": { "Symfony\\Component\\Filesystem\\": "" }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "minimum-stability": "dev", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/filesystem/phpunit.xml.dist b/core/src/core/classes/guzzle/vendor/symfony/filesystem/phpunit.xml.dist deleted file mode 100644 index d066ed7969..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/filesystem/phpunit.xml.dist +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - ./Tests/ - - - - - - ./ - - ./Tests - ./vendor - - - - diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/.gitignore b/core/src/core/classes/guzzle/vendor/symfony/yaml/.gitignore deleted file mode 100644 index c49a5d8df5..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -vendor/ -composer.lock -phpunit.xml diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/CHANGELOG.md b/core/src/core/classes/guzzle/vendor/symfony/yaml/CHANGELOG.md deleted file mode 100644 index aa0c3fa565..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/CHANGELOG.md +++ /dev/null @@ -1,67 +0,0 @@ -CHANGELOG -========= - -3.1.0 ------ - - * Strings that are not UTF-8 encoded will be dumped as base64 encoded binary - data. - - * Added support for dumping multi line strings as literal blocks. - - * Added support for parsing base64 encoded binary data when they are tagged - with the `!!binary` tag. - - * Added support for parsing timestamps as `\DateTime` objects: - - ```php - Yaml::parse('2001-12-15 21:59:43.10 -5', Yaml::PARSE_DATETIME); - ``` - - * `\DateTime` and `\DateTimeImmutable` objects are dumped as YAML timestamps. - - * Deprecated usage of `%` at the beginning of an unquoted string. - - * Added support for customizing the YAML parser behavior through an optional bit field: - - ```php - Yaml::parse('{ "foo": "bar", "fiz": "cat" }', Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE | Yaml::PARSE_OBJECT | Yaml::PARSE_OBJECT_FOR_MAP); - ``` - - * Added support for customizing the dumped YAML string through an optional bit field: - - ```php - Yaml::dump(array('foo' => new A(), 'bar' => 1), 0, 0, Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE | Yaml::DUMP_OBJECT); - ``` - -3.0.0 ------ - - * Yaml::parse() now throws an exception when a blackslash is not escaped - in double-quoted strings - -2.8.0 ------ - - * Deprecated usage of a colon in an unquoted mapping value - * Deprecated usage of @, \`, | and > at the beginning of an unquoted string - * When surrounding strings with double-quotes, you must now escape `\` characters. Not - escaping those characters (when surrounded by double-quotes) is deprecated. - - Before: - - ```yml - class: "Foo\Var" - ``` - - After: - - ```yml - class: "Foo\\Var" - ``` - -2.1.0 ------ - - * Yaml::parse() does not evaluate loaded files as PHP files by default - anymore (call Yaml::enablePhpParsing() to get back the old behavior) diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Dumper.php b/core/src/core/classes/guzzle/vendor/symfony/yaml/Dumper.php deleted file mode 100644 index 10ffe82b01..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Dumper.php +++ /dev/null @@ -1,114 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml; - -/** - * Dumper dumps PHP variables to YAML strings. - * - * @author Fabien Potencier - */ -class Dumper -{ - /** - * The amount of spaces to use for indentation of nested nodes. - * - * @var int - */ - protected $indentation; - - /** - * @param int $indentation - */ - public function __construct($indentation = 4) - { - if ($indentation < 1) { - throw new \InvalidArgumentException('The indentation must be greater than zero.'); - } - - $this->indentation = $indentation; - } - - /** - * Sets the indentation. - * - * @param int $num The amount of spaces to use for indentation of nested nodes - */ - public function setIndentation($num) - { - @trigger_error('The '.__METHOD__.' method is deprecated since version 3.1 and will be removed in 4.0. Pass the indentation to the constructor instead.', E_USER_DEPRECATED); - - $this->indentation = (int) $num; - } - - /** - * Dumps a PHP value to YAML. - * - * @param mixed $input The PHP value - * @param int $inline The level where you switch to inline YAML - * @param int $indent The level of indentation (used internally) - * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string - * - * @return string The YAML representation of the PHP value - */ - public function dump($input, $inline = 0, $indent = 0, $flags = 0) - { - if (is_bool($flags)) { - @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); - - if ($flags) { - $flags = Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE; - } else { - $flags = 0; - } - } - - if (func_num_args() >= 5) { - @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_OBJECT flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(4)) { - $flags |= Yaml::DUMP_OBJECT; - } - } - - $output = ''; - $prefix = $indent ? str_repeat(' ', $indent) : ''; - - if ($inline <= 0 || !is_array($input) || empty($input)) { - $output .= $prefix.Inline::dump($input, $flags); - } else { - $isAHash = Inline::isHash($input); - - foreach ($input as $key => $value) { - if ($inline > 1 && Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && is_string($value) && false !== strpos($value, "\n")) { - $output .= sprintf("%s%s%s |\n", $prefix, $isAHash ? Inline::dump($key, $flags).':' : '-', ''); - - foreach (preg_split('/\n|\r\n/', $value) as $row) { - $output .= sprintf("%s%s%s\n", $prefix, str_repeat(' ', $this->indentation), $row); - } - - continue; - } - - $willBeInlined = $inline - 1 <= 0 || !is_array($value) || empty($value); - - $output .= sprintf('%s%s%s%s', - $prefix, - $isAHash ? Inline::dump($key, $flags).':' : '-', - $willBeInlined ? ' ' : "\n", - $this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags) - ).($willBeInlined ? "\n" : ''); - } - } - - return $output; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Escaper.php b/core/src/core/classes/guzzle/vendor/symfony/yaml/Escaper.php deleted file mode 100644 index a74f14dd9c..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Escaper.php +++ /dev/null @@ -1,99 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml; - -/** - * Escaper encapsulates escaping rules for single and double-quoted - * YAML strings. - * - * @author Matthew Lewinski - * - * @internal - */ -class Escaper -{ - // Characters that would cause a dumped string to require double quoting. - const REGEX_CHARACTER_TO_ESCAPE = "[\\x00-\\x1f]|\xc2\x85|\xc2\xa0|\xe2\x80\xa8|\xe2\x80\xa9"; - - // Mapping arrays for escaping a double quoted string. The backslash is - // first to ensure proper escaping because str_replace operates iteratively - // on the input arrays. This ordering of the characters avoids the use of strtr, - // which performs more slowly. - private static $escapees = array('\\', '\\\\', '\\"', '"', - "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", - "\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f", - "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", - "\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f", - "\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9"); - private static $escaped = array('\\\\', '\\"', '\\\\', '\\"', - '\\0', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\a', - '\\b', '\\t', '\\n', '\\v', '\\f', '\\r', '\\x0e', '\\x0f', - '\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17', - '\\x18', '\\x19', '\\x1a', '\\e', '\\x1c', '\\x1d', '\\x1e', '\\x1f', - '\\N', '\\_', '\\L', '\\P'); - - /** - * Determines if a PHP value would require double quoting in YAML. - * - * @param string $value A PHP value - * - * @return bool True if the value would require double quotes - */ - public static function requiresDoubleQuoting($value) - { - return preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u', $value); - } - - /** - * Escapes and surrounds a PHP value with double quotes. - * - * @param string $value A PHP value - * - * @return string The quoted, escaped string - */ - public static function escapeWithDoubleQuotes($value) - { - return sprintf('"%s"', str_replace(self::$escapees, self::$escaped, $value)); - } - - /** - * Determines if a PHP value would require single quoting in YAML. - * - * @param string $value A PHP value - * - * @return bool True if the value would require single quotes - */ - public static function requiresSingleQuoting($value) - { - // Determines if a PHP value is entirely composed of a value that would - // require single quoting in YAML. - if (in_array(strtolower($value), array('null', '~', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'))) { - return true; - } - - // Determines if the PHP value contains any single characters that would - // cause it to require single quoting in YAML. - return preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` ]/x', $value); - } - - /** - * Escapes and surrounds a PHP value with single quotes. - * - * @param string $value A PHP value - * - * @return string The quoted, escaped string - */ - public static function escapeWithSingleQuotes($value) - { - return sprintf("'%s'", str_replace('\'', '\'\'', $value)); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Exception/DumpException.php b/core/src/core/classes/guzzle/vendor/symfony/yaml/Exception/DumpException.php deleted file mode 100644 index cce972f246..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Exception/DumpException.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml\Exception; - -/** - * Exception class thrown when an error occurs during dumping. - * - * @author Fabien Potencier - */ -class DumpException extends RuntimeException -{ -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Exception/ExceptionInterface.php b/core/src/core/classes/guzzle/vendor/symfony/yaml/Exception/ExceptionInterface.php deleted file mode 100644 index ad850eea1d..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Exception/ExceptionInterface.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml\Exception; - -/** - * Exception interface for all exceptions thrown by the component. - * - * @author Fabien Potencier - */ -interface ExceptionInterface -{ -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Exception/ParseException.php b/core/src/core/classes/guzzle/vendor/symfony/yaml/Exception/ParseException.php deleted file mode 100644 index ba3be7d4fa..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Exception/ParseException.php +++ /dev/null @@ -1,141 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml\Exception; - -/** - * Exception class thrown when an error occurs during parsing. - * - * @author Fabien Potencier - */ -class ParseException extends RuntimeException -{ - private $parsedFile; - private $parsedLine; - private $snippet; - private $rawMessage; - - /** - * Constructor. - * - * @param string $message The error message - * @param int $parsedLine The line where the error occurred - * @param int $snippet The snippet of code near the problem - * @param string $parsedFile The file name where the error occurred - * @param \Exception $previous The previous exception - */ - public function __construct($message, $parsedLine = -1, $snippet = null, $parsedFile = null, \Exception $previous = null) - { - $this->parsedFile = $parsedFile; - $this->parsedLine = $parsedLine; - $this->snippet = $snippet; - $this->rawMessage = $message; - - $this->updateRepr(); - - parent::__construct($this->message, 0, $previous); - } - - /** - * Gets the snippet of code near the error. - * - * @return string The snippet of code - */ - public function getSnippet() - { - return $this->snippet; - } - - /** - * Sets the snippet of code near the error. - * - * @param string $snippet The code snippet - */ - public function setSnippet($snippet) - { - $this->snippet = $snippet; - - $this->updateRepr(); - } - - /** - * Gets the filename where the error occurred. - * - * This method returns null if a string is parsed. - * - * @return string The filename - */ - public function getParsedFile() - { - return $this->parsedFile; - } - - /** - * Sets the filename where the error occurred. - * - * @param string $parsedFile The filename - */ - public function setParsedFile($parsedFile) - { - $this->parsedFile = $parsedFile; - - $this->updateRepr(); - } - - /** - * Gets the line where the error occurred. - * - * @return int The file line - */ - public function getParsedLine() - { - return $this->parsedLine; - } - - /** - * Sets the line where the error occurred. - * - * @param int $parsedLine The file line - */ - public function setParsedLine($parsedLine) - { - $this->parsedLine = $parsedLine; - - $this->updateRepr(); - } - - private function updateRepr() - { - $this->message = $this->rawMessage; - - $dot = false; - if ('.' === substr($this->message, -1)) { - $this->message = substr($this->message, 0, -1); - $dot = true; - } - - if (null !== $this->parsedFile) { - $this->message .= sprintf(' in %s', json_encode($this->parsedFile, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); - } - - if ($this->parsedLine >= 0) { - $this->message .= sprintf(' at line %d', $this->parsedLine); - } - - if ($this->snippet) { - $this->message .= sprintf(' (near "%s")', $this->snippet); - } - - if ($dot) { - $this->message .= '.'; - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Exception/RuntimeException.php b/core/src/core/classes/guzzle/vendor/symfony/yaml/Exception/RuntimeException.php deleted file mode 100644 index 3f36b73bec..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Exception/RuntimeException.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml\Exception; - -/** - * Exception class thrown when an error occurs during parsing. - * - * @author Romain Neutron - */ -class RuntimeException extends \RuntimeException implements ExceptionInterface -{ -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Inline.php b/core/src/core/classes/guzzle/vendor/symfony/yaml/Inline.php deleted file mode 100644 index 95d9a90963..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Inline.php +++ /dev/null @@ -1,685 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml; - -use Symfony\Component\Yaml\Exception\ParseException; -use Symfony\Component\Yaml\Exception\DumpException; - -/** - * Inline implements a YAML parser/dumper for the YAML inline syntax. - * - * @author Fabien Potencier - * - * @internal - */ -class Inline -{ - const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')'; - - private static $exceptionOnInvalidType = false; - private static $objectSupport = false; - private static $objectForMap = false; - - /** - * Converts a YAML string to a PHP array. - * - * @param string $value A YAML string - * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior - * @param array $references Mapping of variable names to values - * - * @return array A PHP array representing the YAML string - * - * @throws ParseException - */ - public static function parse($value, $flags = 0, $references = array()) - { - if (is_bool($flags)) { - @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); - - if ($flags) { - $flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE; - } else { - $flags = 0; - } - } - - if (func_num_args() >= 3 && !is_array($references)) { - @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED); - - if ($references) { - $flags |= Yaml::PARSE_OBJECT; - } - - if (func_num_args() >= 4) { - @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(3)) { - $flags |= Yaml::PARSE_OBJECT_FOR_MAP; - } - } - - if (func_num_args() >= 5) { - $references = func_get_arg(4); - } else { - $references = array(); - } - } - - self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags); - self::$objectSupport = (bool) (Yaml::PARSE_OBJECT & $flags); - self::$objectForMap = (bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags); - - $value = trim($value); - - if ('' === $value) { - return ''; - } - - if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) { - $mbEncoding = mb_internal_encoding(); - mb_internal_encoding('ASCII'); - } - - $i = 0; - switch ($value[0]) { - case '[': - $result = self::parseSequence($value, $flags, $i, $references); - ++$i; - break; - case '{': - $result = self::parseMapping($value, $flags, $i, $references); - ++$i; - break; - default: - $result = self::parseScalar($value, $flags, null, array('"', "'"), $i, true, $references); - } - - // some comments are allowed at the end - if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) { - throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i))); - } - - if (isset($mbEncoding)) { - mb_internal_encoding($mbEncoding); - } - - return $result; - } - - /** - * Dumps a given PHP variable to a YAML string. - * - * @param mixed $value The PHP variable to convert - * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string - * - * @return string The YAML string representing the PHP array - * - * @throws DumpException When trying to dump PHP resource - */ - public static function dump($value, $flags = 0) - { - if (is_bool($flags)) { - @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); - - if ($flags) { - $flags = Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE; - } else { - $flags = 0; - } - } - - if (func_num_args() >= 3) { - @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_OBJECT flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(2)) { - $flags |= Yaml::DUMP_OBJECT; - } - } - - switch (true) { - case is_resource($value): - if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) { - throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value))); - } - - return 'null'; - case $value instanceof \DateTimeInterface: - return $value->format('c'); - case is_object($value): - if (Yaml::DUMP_OBJECT & $flags) { - return '!php/object:'.serialize($value); - } - - if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) { - return self::dumpArray((array) $value, $flags); - } - - if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) { - throw new DumpException('Object support when dumping a YAML file has been disabled.'); - } - - return 'null'; - case is_array($value): - return self::dumpArray($value, $flags); - case null === $value: - return 'null'; - case true === $value: - return 'true'; - case false === $value: - return 'false'; - case ctype_digit($value): - return is_string($value) ? "'$value'" : (int) $value; - case is_numeric($value): - $locale = setlocale(LC_NUMERIC, 0); - if (false !== $locale) { - setlocale(LC_NUMERIC, 'C'); - } - if (is_float($value)) { - $repr = (string) $value; - if (is_infinite($value)) { - $repr = str_ireplace('INF', '.Inf', $repr); - } elseif (floor($value) == $value && $repr == $value) { - // Preserve float data type since storing a whole number will result in integer value. - $repr = '!!float '.$repr; - } - } else { - $repr = is_string($value) ? "'$value'" : (string) $value; - } - if (false !== $locale) { - setlocale(LC_NUMERIC, $locale); - } - - return $repr; - case '' == $value: - return "''"; - case self::isBinaryString($value): - return '!!binary '.base64_encode($value); - case Escaper::requiresDoubleQuoting($value): - return Escaper::escapeWithDoubleQuotes($value); - case Escaper::requiresSingleQuoting($value): - case preg_match(self::getHexRegex(), $value): - case preg_match(self::getTimestampRegex(), $value): - return Escaper::escapeWithSingleQuotes($value); - default: - return $value; - } - } - - /** - * Check if given array is hash or just normal indexed array. - * - * @internal - * - * @param array $value The PHP array to check - * - * @return bool true if value is hash array, false otherwise - */ - public static function isHash(array $value) - { - $expectedKey = 0; - - foreach ($value as $key => $val) { - if ($key !== $expectedKey++) { - return true; - } - } - - return false; - } - - /** - * Dumps a PHP array to a YAML string. - * - * @param array $value The PHP array to dump - * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string - * - * @return string The YAML string representing the PHP array - */ - private static function dumpArray($value, $flags) - { - // array - if ($value && !self::isHash($value)) { - $output = array(); - foreach ($value as $val) { - $output[] = self::dump($val, $flags); - } - - return sprintf('[%s]', implode(', ', $output)); - } - - // hash - $output = array(); - foreach ($value as $key => $val) { - $output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags)); - } - - return sprintf('{ %s }', implode(', ', $output)); - } - - /** - * Parses a scalar to a YAML string. - * - * @param string $scalar - * @param int $flags - * @param string $delimiters - * @param array $stringDelimiters - * @param int &$i - * @param bool $evaluate - * @param array $references - * - * @return string A YAML string - * - * @throws ParseException When malformed inline YAML string is parsed - * - * @internal - */ - public static function parseScalar($scalar, $flags = 0, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true, $references = array()) - { - if (in_array($scalar[$i], $stringDelimiters)) { - // quoted scalar - $output = self::parseQuotedScalar($scalar, $i); - - if (null !== $delimiters) { - $tmp = ltrim(substr($scalar, $i), ' '); - if (!in_array($tmp[0], $delimiters)) { - throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i))); - } - } - } else { - // "normal" string - if (!$delimiters) { - $output = substr($scalar, $i); - $i += strlen($output); - - // remove comments - if (preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) { - $output = substr($output, 0, $match[0][1]); - } - } elseif (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) { - $output = $match[1]; - $i += strlen($output); - } else { - throw new ParseException(sprintf('Malformed inline YAML string (%s).', $scalar)); - } - - // a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >) - if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0])) { - throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0])); - } - - if ($output && '%' === $output[0]) { - @trigger_error('Not quoting a scalar starting with the "%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.', E_USER_DEPRECATED); - } - - if ($evaluate) { - $output = self::evaluateScalar($output, $flags, $references); - } - } - - return $output; - } - - /** - * Parses a quoted scalar to YAML. - * - * @param string $scalar - * @param int &$i - * - * @return string A YAML string - * - * @throws ParseException When malformed inline YAML string is parsed - */ - private static function parseQuotedScalar($scalar, &$i) - { - if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) { - throw new ParseException(sprintf('Malformed inline YAML string (%s).', substr($scalar, $i))); - } - - $output = substr($match[0], 1, strlen($match[0]) - 2); - - $unescaper = new Unescaper(); - if ('"' == $scalar[$i]) { - $output = $unescaper->unescapeDoubleQuotedString($output); - } else { - $output = $unescaper->unescapeSingleQuotedString($output); - } - - $i += strlen($match[0]); - - return $output; - } - - /** - * Parses a sequence to a YAML string. - * - * @param string $sequence - * @param int $flags - * @param int &$i - * @param array $references - * - * @return string A YAML string - * - * @throws ParseException When malformed inline YAML string is parsed - */ - private static function parseSequence($sequence, $flags, &$i = 0, $references = array()) - { - $output = array(); - $len = strlen($sequence); - ++$i; - - // [foo, bar, ...] - while ($i < $len) { - switch ($sequence[$i]) { - case '[': - // nested sequence - $output[] = self::parseSequence($sequence, $flags, $i, $references); - break; - case '{': - // nested mapping - $output[] = self::parseMapping($sequence, $flags, $i, $references); - break; - case ']': - return $output; - case ',': - case ' ': - break; - default: - $isQuoted = in_array($sequence[$i], array('"', "'")); - $value = self::parseScalar($sequence, $flags, array(',', ']'), array('"', "'"), $i, true, $references); - - // the value can be an array if a reference has been resolved to an array var - if (is_string($value) && !$isQuoted && false !== strpos($value, ': ')) { - // embedded mapping? - try { - $pos = 0; - $value = self::parseMapping('{'.$value.'}', $flags, $pos, $references); - } catch (\InvalidArgumentException $e) { - // no, it's not - } - } - - $output[] = $value; - - --$i; - } - - ++$i; - } - - throw new ParseException(sprintf('Malformed inline YAML string %s', $sequence)); - } - - /** - * Parses a mapping to a YAML string. - * - * @param string $mapping - * @param int $flags - * @param int &$i - * @param array $references - * - * @return string A YAML string - * - * @throws ParseException When malformed inline YAML string is parsed - */ - private static function parseMapping($mapping, $flags, &$i = 0, $references = array()) - { - $output = array(); - $len = strlen($mapping); - ++$i; - - // {foo: bar, bar:foo, ...} - while ($i < $len) { - switch ($mapping[$i]) { - case ' ': - case ',': - ++$i; - continue 2; - case '}': - if (self::$objectForMap) { - return (object) $output; - } - - return $output; - } - - // key - $key = self::parseScalar($mapping, $flags, array(':', ' '), array('"', "'"), $i, false); - - // value - $done = false; - - while ($i < $len) { - switch ($mapping[$i]) { - case '[': - // nested sequence - $value = self::parseSequence($mapping, $flags, $i, $references); - // Spec: Keys MUST be unique; first one wins. - // Parser cannot abort this mapping earlier, since lines - // are processed sequentially. - if (!isset($output[$key])) { - $output[$key] = $value; - } - $done = true; - break; - case '{': - // nested mapping - $value = self::parseMapping($mapping, $flags, $i, $references); - // Spec: Keys MUST be unique; first one wins. - // Parser cannot abort this mapping earlier, since lines - // are processed sequentially. - if (!isset($output[$key])) { - $output[$key] = $value; - } - $done = true; - break; - case ':': - case ' ': - break; - default: - $value = self::parseScalar($mapping, $flags, array(',', '}'), array('"', "'"), $i, true, $references); - // Spec: Keys MUST be unique; first one wins. - // Parser cannot abort this mapping earlier, since lines - // are processed sequentially. - if (!isset($output[$key])) { - $output[$key] = $value; - } - $done = true; - --$i; - } - - ++$i; - - if ($done) { - continue 2; - } - } - } - - throw new ParseException(sprintf('Malformed inline YAML string %s', $mapping)); - } - - /** - * Evaluates scalars and replaces magic values. - * - * @param string $scalar - * @param int $flags - * @param array $references - * - * @return string A YAML string - * - * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved - */ - private static function evaluateScalar($scalar, $flags, $references = array()) - { - $scalar = trim($scalar); - $scalarLower = strtolower($scalar); - - if (0 === strpos($scalar, '*')) { - if (false !== $pos = strpos($scalar, '#')) { - $value = substr($scalar, 1, $pos - 2); - } else { - $value = substr($scalar, 1); - } - - // an unquoted * - if (false === $value || '' === $value) { - throw new ParseException('A reference must contain at least one character.'); - } - - if (!array_key_exists($value, $references)) { - throw new ParseException(sprintf('Reference "%s" does not exist.', $value)); - } - - return $references[$value]; - } - - switch (true) { - case 'null' === $scalarLower: - case '' === $scalar: - case '~' === $scalar: - return; - case 'true' === $scalarLower: - return true; - case 'false' === $scalarLower: - return false; - // Optimise for returning strings. - case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || $scalar[0] === '!' || is_numeric($scalar[0]): - switch (true) { - case 0 === strpos($scalar, '!str'): - return (string) substr($scalar, 5); - case 0 === strpos($scalar, '! '): - return (int) self::parseScalar(substr($scalar, 2), $flags); - case 0 === strpos($scalar, '!php/object:'): - if (self::$objectSupport) { - return unserialize(substr($scalar, 12)); - } - - if (self::$exceptionOnInvalidType) { - throw new ParseException('Object support when parsing a YAML file has been disabled.'); - } - - return; - case 0 === strpos($scalar, '!!php/object:'): - if (self::$objectSupport) { - @trigger_error('The !!php/object tag to indicate dumped PHP objects is deprecated since version 3.1 and will be removed in 4.0. Use the !php/object tag instead.', E_USER_DEPRECATED); - - return unserialize(substr($scalar, 13)); - } - - if (self::$exceptionOnInvalidType) { - throw new ParseException('Object support when parsing a YAML file has been disabled.'); - } - - return; - case 0 === strpos($scalar, '!!float '): - return (float) substr($scalar, 8); - case ctype_digit($scalar): - $raw = $scalar; - $cast = (int) $scalar; - - return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw); - case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)): - $raw = $scalar; - $cast = (int) $scalar; - - return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw); - case is_numeric($scalar): - case preg_match(self::getHexRegex(), $scalar): - return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar; - case '.inf' === $scalarLower: - case '.nan' === $scalarLower: - return -log(0); - case '-.inf' === $scalarLower: - return log(0); - case 0 === strpos($scalar, '!!binary '): - return self::evaluateBinaryScalar(substr($scalar, 9)); - case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar): - return (float) str_replace(',', '', $scalar); - case preg_match(self::getTimestampRegex(), $scalar): - if (Yaml::PARSE_DATETIME & $flags) { - return new \DateTime($scalar, new \DateTimeZone('UTC')); - } - - $timeZone = date_default_timezone_get(); - date_default_timezone_set('UTC'); - $time = strtotime($scalar); - date_default_timezone_set($timeZone); - - return $time; - } - default: - return (string) $scalar; - } - } - - /** - * @param string $scalar - * - * @return string - * - * @internal - */ - public static function evaluateBinaryScalar($scalar) - { - $parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar)); - - if (0 !== (strlen($parsedBinaryData) % 4)) { - throw new ParseException(sprintf('The normalized base64 encoded data (data without whitespace characters) length must be a multiple of four (%d bytes given).', strlen($parsedBinaryData))); - } - - if (!preg_match('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) { - throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData)); - } - - return base64_decode($parsedBinaryData, true); - } - - private static function isBinaryString($value) - { - return !preg_match('//u', $value) || preg_match('/[^\x09-\x0d\x20-\xff]/', $value); - } - - /** - * Gets a regex that matches a YAML date. - * - * @return string The regular expression - * - * @see http://www.yaml.org/spec/1.2/spec.html#id2761573 - */ - private static function getTimestampRegex() - { - return <<[0-9][0-9][0-9][0-9]) - -(?P[0-9][0-9]?) - -(?P[0-9][0-9]?) - (?:(?:[Tt]|[ \t]+) - (?P[0-9][0-9]?) - :(?P[0-9][0-9]) - :(?P[0-9][0-9]) - (?:\.(?P[0-9]*))? - (?:[ \t]*(?PZ|(?P[-+])(?P[0-9][0-9]?) - (?::(?P[0-9][0-9]))?))?)? - $~x -EOF; - } - - /** - * Gets a regex that matches a YAML number in hexadecimal notation. - * - * @return string - */ - private static function getHexRegex() - { - return '~^0x[0-9a-f]++$~i'; - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/LICENSE b/core/src/core/classes/guzzle/vendor/symfony/yaml/LICENSE deleted file mode 100644 index 12a74531e4..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2004-2016 Fabien Potencier - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Parser.php b/core/src/core/classes/guzzle/vendor/symfony/yaml/Parser.php deleted file mode 100644 index 5cf5a88824..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Parser.php +++ /dev/null @@ -1,856 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml; - -use Symfony\Component\Yaml\Exception\ParseException; - -/** - * Parser parses YAML strings to convert them to PHP arrays. - * - * @author Fabien Potencier - */ -class Parser -{ - const TAG_PATTERN = '((?P![\w!.\/:-]+) +)?'; - const BLOCK_SCALAR_HEADER_PATTERN = '(?P\||>)(?P\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P +#.*)?'; - - private $offset = 0; - private $totalNumberOfLines; - private $lines = array(); - private $currentLineNb = -1; - private $currentLine = ''; - private $refs = array(); - private $skippedLineNumbers = array(); - private $locallySkippedLineNumbers = array(); - - /** - * Constructor. - * - * @param int $offset The offset of YAML document (used for line numbers in error messages) - * @param int|null $totalNumberOfLines The overall number of lines being parsed - * @param int[] $skippedLineNumbers Number of comment lines that have been skipped by the parser - */ - public function __construct($offset = 0, $totalNumberOfLines = null, array $skippedLineNumbers = array()) - { - $this->offset = $offset; - $this->totalNumberOfLines = $totalNumberOfLines; - $this->skippedLineNumbers = $skippedLineNumbers; - } - - /** - * Parses a YAML string to a PHP value. - * - * @param string $value A YAML string - * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior - * - * @return mixed A PHP value - * - * @throws ParseException If the YAML is not valid - */ - public function parse($value, $flags = 0) - { - if (is_bool($flags)) { - @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); - - if ($flags) { - $flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE; - } else { - $flags = 0; - } - } - - if (func_num_args() >= 3) { - @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(2)) { - $flags |= Yaml::PARSE_OBJECT; - } - } - - if (func_num_args() >= 4) { - @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(3)) { - $flags |= Yaml::PARSE_OBJECT_FOR_MAP; - } - } - - if (!preg_match('//u', $value)) { - throw new ParseException('The YAML value does not appear to be valid UTF-8.'); - } - $this->currentLineNb = -1; - $this->currentLine = ''; - $value = $this->cleanup($value); - $this->lines = explode("\n", $value); - - if (null === $this->totalNumberOfLines) { - $this->totalNumberOfLines = count($this->lines); - } - - if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) { - $mbEncoding = mb_internal_encoding(); - mb_internal_encoding('UTF-8'); - } - - $data = array(); - $context = null; - $allowOverwrite = false; - while ($this->moveToNextLine()) { - if ($this->isCurrentLineEmpty()) { - continue; - } - - // tab? - if ("\t" === $this->currentLine[0]) { - throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - - $isRef = $mergeNode = false; - if (preg_match('#^\-((?P\s+)(?P.+?))?\s*$#u', $this->currentLine, $values)) { - if ($context && 'mapping' == $context) { - throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - $context = 'sequence'; - - if (isset($values['value']) && preg_match('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) { - $isRef = $matches['ref']; - $values['value'] = $matches['value']; - } - - // array - if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { - $data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags); - } else { - if (isset($values['leadspaces']) - && preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P.+?))?\s*$#u', $values['value'], $matches) - ) { - // this is a compact notation element, add to next block and parse - $block = $values['value']; - if ($this->isNextLineIndented()) { - $block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + strlen($values['leadspaces']) + 1); - } - - $data[] = $this->parseBlock($this->getRealCurrentLineNb(), $block, $flags); - } else { - $data[] = $this->parseValue($values['value'], $flags, $context); - } - } - if ($isRef) { - $this->refs[$isRef] = end($data); - } - } elseif (preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P.+?))?\s*$#u', $this->currentLine, $values) && (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'")))) { - if ($context && 'sequence' == $context) { - throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine); - } - $context = 'mapping'; - - // force correct settings - Inline::parse(null, $flags, $this->refs); - try { - $key = Inline::parseScalar($values['key']); - } catch (ParseException $e) { - $e->setParsedLine($this->getRealCurrentLineNb() + 1); - $e->setSnippet($this->currentLine); - - throw $e; - } - - // Convert float keys to strings, to avoid being converted to integers by PHP - if (is_float($key)) { - $key = (string) $key; - } - - if ('<<' === $key) { - $mergeNode = true; - $allowOverwrite = true; - if (isset($values['value']) && 0 === strpos($values['value'], '*')) { - $refName = substr($values['value'], 1); - if (!array_key_exists($refName, $this->refs)) { - throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - - $refValue = $this->refs[$refName]; - - if (!is_array($refValue)) { - throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - - foreach ($refValue as $key => $value) { - if (!isset($data[$key])) { - $data[$key] = $value; - } - } - } else { - if (isset($values['value']) && $values['value'] !== '') { - $value = $values['value']; - } else { - $value = $this->getNextEmbedBlock(); - } - $parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $flags); - - if (!is_array($parsed)) { - throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - - if (isset($parsed[0])) { - // If the value associated with the merge key is a sequence, then this sequence is expected to contain mapping nodes - // and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier - // in the sequence override keys specified in later mapping nodes. - foreach ($parsed as $parsedItem) { - if (!is_array($parsedItem)) { - throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem); - } - - foreach ($parsedItem as $key => $value) { - if (!isset($data[$key])) { - $data[$key] = $value; - } - } - } - } else { - // If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the - // current mapping, unless the key already exists in it. - foreach ($parsed as $key => $value) { - if (!isset($data[$key])) { - $data[$key] = $value; - } - } - } - } - } elseif (isset($values['value']) && preg_match('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) { - $isRef = $matches['ref']; - $values['value'] = $matches['value']; - } - - if ($mergeNode) { - // Merge keys - } elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { - // hash - // if next line is less indented or equal, then it means that the current value is null - if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) { - // Spec: Keys MUST be unique; first one wins. - // But overwriting is allowed when a merge node is used in current block. - if ($allowOverwrite || !isset($data[$key])) { - $data[$key] = null; - } - } else { - $value = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(), $flags); - // Spec: Keys MUST be unique; first one wins. - // But overwriting is allowed when a merge node is used in current block. - if ($allowOverwrite || !isset($data[$key])) { - $data[$key] = $value; - } - } - } else { - $value = $this->parseValue($values['value'], $flags, $context); - // Spec: Keys MUST be unique; first one wins. - // But overwriting is allowed when a merge node is used in current block. - if ($allowOverwrite || !isset($data[$key])) { - $data[$key] = $value; - } - } - if ($isRef) { - $this->refs[$isRef] = $data[$key]; - } - } else { - // multiple documents are not supported - if ('---' === $this->currentLine) { - throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine); - } - - // 1-liner optionally followed by newline(s) - if (is_string($value) && $this->lines[0] === trim($value)) { - try { - $value = Inline::parse($this->lines[0], $flags, $this->refs); - } catch (ParseException $e) { - $e->setParsedLine($this->getRealCurrentLineNb() + 1); - $e->setSnippet($this->currentLine); - - throw $e; - } - - if (is_array($value)) { - $first = reset($value); - if (is_string($first) && 0 === strpos($first, '*')) { - $data = array(); - foreach ($value as $alias) { - $data[] = $this->refs[substr($alias, 1)]; - } - $value = $data; - } - } - - if (isset($mbEncoding)) { - mb_internal_encoding($mbEncoding); - } - - return $value; - } - - switch (preg_last_error()) { - case PREG_INTERNAL_ERROR: - $error = 'Internal PCRE error.'; - break; - case PREG_BACKTRACK_LIMIT_ERROR: - $error = 'pcre.backtrack_limit reached.'; - break; - case PREG_RECURSION_LIMIT_ERROR: - $error = 'pcre.recursion_limit reached.'; - break; - case PREG_BAD_UTF8_ERROR: - $error = 'Malformed UTF-8 data.'; - break; - case PREG_BAD_UTF8_OFFSET_ERROR: - $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.'; - break; - default: - $error = 'Unable to parse.'; - } - - throw new ParseException($error, $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - } - - if (isset($mbEncoding)) { - mb_internal_encoding($mbEncoding); - } - - if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && !is_object($data) && 'mapping' === $context) { - $object = new \stdClass(); - - foreach ($data as $key => $value) { - $object->$key = $value; - } - - $data = $object; - } - - return empty($data) ? null : $data; - } - - private function parseBlock($offset, $yaml, $flags) - { - $skippedLineNumbers = $this->skippedLineNumbers; - - foreach ($this->locallySkippedLineNumbers as $lineNumber) { - if ($lineNumber < $offset) { - continue; - } - - $skippedLineNumbers[] = $lineNumber; - } - - $parser = new self($offset, $this->totalNumberOfLines, $skippedLineNumbers); - $parser->refs = &$this->refs; - - return $parser->parse($yaml, $flags); - } - - /** - * Returns the current line number (takes the offset into account). - * - * @return int The current line number - */ - private function getRealCurrentLineNb() - { - $realCurrentLineNumber = $this->currentLineNb + $this->offset; - - foreach ($this->skippedLineNumbers as $skippedLineNumber) { - if ($skippedLineNumber > $realCurrentLineNumber) { - break; - } - - ++$realCurrentLineNumber; - } - - return $realCurrentLineNumber; - } - - /** - * Returns the current line indentation. - * - * @return int The current line indentation - */ - private function getCurrentLineIndentation() - { - return strlen($this->currentLine) - strlen(ltrim($this->currentLine, ' ')); - } - - /** - * Returns the next embed block of YAML. - * - * @param int $indentation The indent level at which the block is to be read, or null for default - * @param bool $inSequence True if the enclosing data structure is a sequence - * - * @return string A YAML string - * - * @throws ParseException When indentation problem are detected - */ - private function getNextEmbedBlock($indentation = null, $inSequence = false) - { - $oldLineIndentation = $this->getCurrentLineIndentation(); - $blockScalarIndentations = array(); - - if ($this->isBlockScalarHeader()) { - $blockScalarIndentations[] = $this->getCurrentLineIndentation(); - } - - if (!$this->moveToNextLine()) { - return; - } - - if (null === $indentation) { - $newIndent = $this->getCurrentLineIndentation(); - - $unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem(); - - if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) { - throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - } else { - $newIndent = $indentation; - } - - $data = array(); - if ($this->getCurrentLineIndentation() >= $newIndent) { - $data[] = substr($this->currentLine, $newIndent); - } else { - $this->moveToPreviousLine(); - - return; - } - - if ($inSequence && $oldLineIndentation === $newIndent && isset($data[0][0]) && '-' === $data[0][0]) { - // the previous line contained a dash but no item content, this line is a sequence item with the same indentation - // and therefore no nested list or mapping - $this->moveToPreviousLine(); - - return; - } - - $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem(); - - if (empty($blockScalarIndentations) && $this->isBlockScalarHeader()) { - $blockScalarIndentations[] = $this->getCurrentLineIndentation(); - } - - $previousLineIndentation = $this->getCurrentLineIndentation(); - - while ($this->moveToNextLine()) { - $indent = $this->getCurrentLineIndentation(); - - // terminate all block scalars that are more indented than the current line - if (!empty($blockScalarIndentations) && $indent < $previousLineIndentation && trim($this->currentLine) !== '') { - foreach ($blockScalarIndentations as $key => $blockScalarIndentation) { - if ($blockScalarIndentation >= $this->getCurrentLineIndentation()) { - unset($blockScalarIndentations[$key]); - } - } - } - - if (empty($blockScalarIndentations) && !$this->isCurrentLineComment() && $this->isBlockScalarHeader()) { - $blockScalarIndentations[] = $this->getCurrentLineIndentation(); - } - - $previousLineIndentation = $indent; - - if ($isItUnindentedCollection && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) { - $this->moveToPreviousLine(); - break; - } - - if ($this->isCurrentLineBlank()) { - $data[] = substr($this->currentLine, $newIndent); - continue; - } - - // we ignore "comment" lines only when we are not inside a scalar block - if (empty($blockScalarIndentations) && $this->isCurrentLineComment()) { - // remember ignored comment lines (they are used later in nested - // parser calls to determine real line numbers) - // - // CAUTION: beware to not populate the global property here as it - // will otherwise influence the getRealCurrentLineNb() call here - // for consecutive comment lines and subsequent embedded blocks - $this->locallySkippedLineNumbers[] = $this->getRealCurrentLineNb(); - - continue; - } - - if ($indent >= $newIndent) { - $data[] = substr($this->currentLine, $newIndent); - } elseif (0 == $indent) { - $this->moveToPreviousLine(); - - break; - } else { - throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - } - - return implode("\n", $data); - } - - /** - * Moves the parser to the next line. - * - * @return bool - */ - private function moveToNextLine() - { - if ($this->currentLineNb >= count($this->lines) - 1) { - return false; - } - - $this->currentLine = $this->lines[++$this->currentLineNb]; - - return true; - } - - /** - * Moves the parser to the previous line. - * - * @return bool - */ - private function moveToPreviousLine() - { - if ($this->currentLineNb < 1) { - return false; - } - - $this->currentLine = $this->lines[--$this->currentLineNb]; - - return true; - } - - /** - * Parses a YAML value. - * - * @param string $value A YAML value - * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior - * @param string $context The parser context (either sequence or mapping) - * - * @return mixed A PHP value - * - * @throws ParseException When reference does not exist - */ - private function parseValue($value, $flags, $context) - { - if (0 === strpos($value, '*')) { - if (false !== $pos = strpos($value, '#')) { - $value = substr($value, 1, $pos - 2); - } else { - $value = substr($value, 1); - } - - if (!array_key_exists($value, $this->refs)) { - throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine); - } - - return $this->refs[$value]; - } - - if (preg_match('/^'.self::TAG_PATTERN.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) { - $modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : ''; - - $data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs($modifiers)); - - if (isset($matches['tag']) && '!!binary' === $matches['tag']) { - return Inline::evaluateBinaryScalar($data); - } - - return $data; - } - - try { - $parsedValue = Inline::parse($value, $flags, $this->refs); - - if ('mapping' === $context && is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) { - throw new ParseException('A colon cannot be used in an unquoted mapping value.'); - } - - return $parsedValue; - } catch (ParseException $e) { - $e->setParsedLine($this->getRealCurrentLineNb() + 1); - $e->setSnippet($this->currentLine); - - throw $e; - } - } - - /** - * Parses a block scalar. - * - * @param string $style The style indicator that was used to begin this block scalar (| or >) - * @param string $chomping The chomping indicator that was used to begin this block scalar (+ or -) - * @param int $indentation The indentation indicator that was used to begin this block scalar - * - * @return string The text value - */ - private function parseBlockScalar($style, $chomping = '', $indentation = 0) - { - $notEOF = $this->moveToNextLine(); - if (!$notEOF) { - return ''; - } - - $isCurrentLineBlank = $this->isCurrentLineBlank(); - $blockLines = array(); - - // leading blank lines are consumed before determining indentation - while ($notEOF && $isCurrentLineBlank) { - // newline only if not EOF - if ($notEOF = $this->moveToNextLine()) { - $blockLines[] = ''; - $isCurrentLineBlank = $this->isCurrentLineBlank(); - } - } - - // determine indentation if not specified - if (0 === $indentation) { - if (preg_match('/^ +/', $this->currentLine, $matches)) { - $indentation = strlen($matches[0]); - } - } - - if ($indentation > 0) { - $pattern = sprintf('/^ {%d}(.*)$/', $indentation); - - while ( - $notEOF && ( - $isCurrentLineBlank || - preg_match($pattern, $this->currentLine, $matches) - ) - ) { - if ($isCurrentLineBlank && strlen($this->currentLine) > $indentation) { - $blockLines[] = substr($this->currentLine, $indentation); - } elseif ($isCurrentLineBlank) { - $blockLines[] = ''; - } else { - $blockLines[] = $matches[1]; - } - - // newline only if not EOF - if ($notEOF = $this->moveToNextLine()) { - $isCurrentLineBlank = $this->isCurrentLineBlank(); - } - } - } elseif ($notEOF) { - $blockLines[] = ''; - } - - if ($notEOF) { - $blockLines[] = ''; - $this->moveToPreviousLine(); - } elseif (!$notEOF && !$this->isCurrentLineLastLineInDocument()) { - $blockLines[] = ''; - } - - // folded style - if ('>' === $style) { - $text = ''; - $previousLineIndented = false; - $previousLineBlank = false; - - for ($i = 0; $i < count($blockLines); ++$i) { - if ('' === $blockLines[$i]) { - $text .= "\n"; - $previousLineIndented = false; - $previousLineBlank = true; - } elseif (' ' === $blockLines[$i][0]) { - $text .= "\n".$blockLines[$i]; - $previousLineIndented = true; - $previousLineBlank = false; - } elseif ($previousLineIndented) { - $text .= "\n".$blockLines[$i]; - $previousLineIndented = false; - $previousLineBlank = false; - } elseif ($previousLineBlank || 0 === $i) { - $text .= $blockLines[$i]; - $previousLineIndented = false; - $previousLineBlank = false; - } else { - $text .= ' '.$blockLines[$i]; - $previousLineIndented = false; - $previousLineBlank = false; - } - } - } else { - $text = implode("\n", $blockLines); - } - - // deal with trailing newlines - if ('' === $chomping) { - $text = preg_replace('/\n+$/', "\n", $text); - } elseif ('-' === $chomping) { - $text = preg_replace('/\n+$/', '', $text); - } - - return $text; - } - - /** - * Returns true if the next line is indented. - * - * @return bool Returns true if the next line is indented, false otherwise - */ - private function isNextLineIndented() - { - $currentIndentation = $this->getCurrentLineIndentation(); - $EOF = !$this->moveToNextLine(); - - while (!$EOF && $this->isCurrentLineEmpty()) { - $EOF = !$this->moveToNextLine(); - } - - if ($EOF) { - return false; - } - - $ret = false; - if ($this->getCurrentLineIndentation() > $currentIndentation) { - $ret = true; - } - - $this->moveToPreviousLine(); - - return $ret; - } - - /** - * Returns true if the current line is blank or if it is a comment line. - * - * @return bool Returns true if the current line is empty or if it is a comment line, false otherwise - */ - private function isCurrentLineEmpty() - { - return $this->isCurrentLineBlank() || $this->isCurrentLineComment(); - } - - /** - * Returns true if the current line is blank. - * - * @return bool Returns true if the current line is blank, false otherwise - */ - private function isCurrentLineBlank() - { - return '' == trim($this->currentLine, ' '); - } - - /** - * Returns true if the current line is a comment line. - * - * @return bool Returns true if the current line is a comment line, false otherwise - */ - private function isCurrentLineComment() - { - //checking explicitly the first char of the trim is faster than loops or strpos - $ltrimmedLine = ltrim($this->currentLine, ' '); - - return '' !== $ltrimmedLine && $ltrimmedLine[0] === '#'; - } - - private function isCurrentLineLastLineInDocument() - { - return ($this->offset + $this->currentLineNb) >= ($this->totalNumberOfLines - 1); - } - - /** - * Cleanups a YAML string to be parsed. - * - * @param string $value The input YAML string - * - * @return string A cleaned up YAML string - */ - private function cleanup($value) - { - $value = str_replace(array("\r\n", "\r"), "\n", $value); - - // strip YAML header - $count = 0; - $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#u', '', $value, -1, $count); - $this->offset += $count; - - // remove leading comments - $trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count); - if ($count == 1) { - // items have been removed, update the offset - $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); - $value = $trimmedValue; - } - - // remove start of the document marker (---) - $trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count); - if ($count == 1) { - // items have been removed, update the offset - $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); - $value = $trimmedValue; - - // remove end of the document marker (...) - $value = preg_replace('#\.\.\.\s*$#', '', $value); - } - - return $value; - } - - /** - * Returns true if the next line starts unindented collection. - * - * @return bool Returns true if the next line starts unindented collection, false otherwise - */ - private function isNextLineUnIndentedCollection() - { - $currentIndentation = $this->getCurrentLineIndentation(); - $notEOF = $this->moveToNextLine(); - - while ($notEOF && $this->isCurrentLineEmpty()) { - $notEOF = $this->moveToNextLine(); - } - - if (false === $notEOF) { - return false; - } - - $ret = false; - if ( - $this->getCurrentLineIndentation() == $currentIndentation - && - $this->isStringUnIndentedCollectionItem() - ) { - $ret = true; - } - - $this->moveToPreviousLine(); - - return $ret; - } - - /** - * Returns true if the string is un-indented collection item. - * - * @return bool Returns true if the string is un-indented collection item, false otherwise - */ - private function isStringUnIndentedCollectionItem() - { - return '-' === rtrim($this->currentLine) || 0 === strpos($this->currentLine, '- '); - } - - /** - * Tests whether or not the current line is the header of a block scalar. - * - * @return bool - */ - private function isBlockScalarHeader() - { - return (bool) preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/README.md b/core/src/core/classes/guzzle/vendor/symfony/yaml/README.md deleted file mode 100644 index 0d324881ce..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/README.md +++ /dev/null @@ -1,13 +0,0 @@ -Yaml Component -============== - -The Yaml component loads and dumps YAML files. - -Resources ---------- - - * [Documentation](https://symfony.com/doc/current/components/yaml/index.html) - * [Contributing](https://symfony.com/doc/current/contributing/index.html) - * [Report issues](https://github.com/symfony/symfony/issues) and - [send Pull Requests](https://github.com/symfony/symfony/pulls) - in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/DumperTest.php b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/DumperTest.php deleted file mode 100644 index afc4912095..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/DumperTest.php +++ /dev/null @@ -1,370 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml\Tests; - -use Symfony\Component\Yaml\Parser; -use Symfony\Component\Yaml\Dumper; -use Symfony\Component\Yaml\Yaml; - -class DumperTest extends \PHPUnit_Framework_TestCase -{ - protected $parser; - protected $dumper; - protected $path; - - protected $array = array( - '' => 'bar', - 'foo' => '#bar', - 'foo\'bar' => array(), - 'bar' => array(1, 'foo'), - 'foobar' => array( - 'foo' => 'bar', - 'bar' => array(1, 'foo'), - 'foobar' => array( - 'foo' => 'bar', - 'bar' => array(1, 'foo'), - ), - ), - ); - - protected function setUp() - { - $this->parser = new Parser(); - $this->dumper = new Dumper(); - $this->path = __DIR__.'/Fixtures'; - } - - protected function tearDown() - { - $this->parser = null; - $this->dumper = null; - $this->path = null; - $this->array = null; - } - - public function testIndentationInConstructor() - { - $dumper = new Dumper(7); - $expected = <<<'EOF' -'': bar -foo: '#bar' -'foo''bar': { } -bar: - - 1 - - foo -foobar: - foo: bar - bar: - - 1 - - foo - foobar: - foo: bar - bar: - - 1 - - foo - -EOF; - $this->assertEquals($expected, $dumper->dump($this->array, 4, 0)); - } - - /** - * @group legacy - */ - public function testSetIndentation() - { - $this->dumper->setIndentation(7); - - $expected = <<<'EOF' -'': bar -foo: '#bar' -'foo''bar': { } -bar: - - 1 - - foo -foobar: - foo: bar - bar: - - 1 - - foo - foobar: - foo: bar - bar: - - 1 - - foo - -EOF; - $this->assertEquals($expected, $this->dumper->dump($this->array, 4, 0)); - } - - public function testSpecifications() - { - $files = $this->parser->parse(file_get_contents($this->path.'/index.yml')); - foreach ($files as $file) { - $yamls = file_get_contents($this->path.'/'.$file.'.yml'); - - // split YAMLs documents - foreach (preg_split('/^---( %YAML\:1\.0)?/m', $yamls) as $yaml) { - if (!$yaml) { - continue; - } - - $test = $this->parser->parse($yaml); - if (isset($test['dump_skip']) && $test['dump_skip']) { - continue; - } elseif (isset($test['todo']) && $test['todo']) { - // TODO - } else { - eval('$expected = '.trim($test['php']).';'); - $this->assertSame($expected, $this->parser->parse($this->dumper->dump($expected, 10)), $test['test']); - } - } - } - } - - public function testInlineLevel() - { - $expected = <<<'EOF' -{ '': bar, foo: '#bar', 'foo''bar': { }, bar: [1, foo], foobar: { foo: bar, bar: [1, foo], foobar: { foo: bar, bar: [1, foo] } } } -EOF; - $this->assertEquals($expected, $this->dumper->dump($this->array, -10), '->dump() takes an inline level argument'); - $this->assertEquals($expected, $this->dumper->dump($this->array, 0), '->dump() takes an inline level argument'); - - $expected = <<<'EOF' -'': bar -foo: '#bar' -'foo''bar': { } -bar: [1, foo] -foobar: { foo: bar, bar: [1, foo], foobar: { foo: bar, bar: [1, foo] } } - -EOF; - $this->assertEquals($expected, $this->dumper->dump($this->array, 1), '->dump() takes an inline level argument'); - - $expected = <<<'EOF' -'': bar -foo: '#bar' -'foo''bar': { } -bar: - - 1 - - foo -foobar: - foo: bar - bar: [1, foo] - foobar: { foo: bar, bar: [1, foo] } - -EOF; - $this->assertEquals($expected, $this->dumper->dump($this->array, 2), '->dump() takes an inline level argument'); - - $expected = <<<'EOF' -'': bar -foo: '#bar' -'foo''bar': { } -bar: - - 1 - - foo -foobar: - foo: bar - bar: - - 1 - - foo - foobar: - foo: bar - bar: [1, foo] - -EOF; - $this->assertEquals($expected, $this->dumper->dump($this->array, 3), '->dump() takes an inline level argument'); - - $expected = <<<'EOF' -'': bar -foo: '#bar' -'foo''bar': { } -bar: - - 1 - - foo -foobar: - foo: bar - bar: - - 1 - - foo - foobar: - foo: bar - bar: - - 1 - - foo - -EOF; - $this->assertEquals($expected, $this->dumper->dump($this->array, 4), '->dump() takes an inline level argument'); - $this->assertEquals($expected, $this->dumper->dump($this->array, 10), '->dump() takes an inline level argument'); - } - - public function testObjectSupportEnabled() - { - $dump = $this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, Yaml::DUMP_OBJECT); - - $this->assertEquals('{ foo: !php/object:O:30:"Symfony\Component\Yaml\Tests\A":1:{s:1:"a";s:3:"foo";}, bar: 1 }', $dump, '->dump() is able to dump objects'); - } - - /** - * @group legacy - */ - public function testObjectSupportEnabledPassingTrue() - { - $dump = $this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, false, true); - - $this->assertEquals('{ foo: !php/object:O:30:"Symfony\Component\Yaml\Tests\A":1:{s:1:"a";s:3:"foo";}, bar: 1 }', $dump, '->dump() is able to dump objects'); - } - - public function testObjectSupportDisabledButNoExceptions() - { - $dump = $this->dumper->dump(array('foo' => new A(), 'bar' => 1)); - - $this->assertEquals('{ foo: null, bar: 1 }', $dump, '->dump() does not dump objects when disabled'); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\DumpException - */ - public function testObjectSupportDisabledWithExceptions() - { - $this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE); - } - - /** - * @group legacy - * @expectedException \Symfony\Component\Yaml\Exception\DumpException - */ - public function testObjectSupportDisabledWithExceptionsPassingTrue() - { - $this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, true); - } - - /** - * @dataProvider getEscapeSequences - */ - public function testEscapedEscapeSequencesInQuotedScalar($input, $expected) - { - $this->assertEquals($expected, $this->dumper->dump($input)); - } - - public function getEscapeSequences() - { - return array( - 'null' => array("\t\\0", '"\t\\\\0"'), - 'bell' => array("\t\\a", '"\t\\\\a"'), - 'backspace' => array("\t\\b", '"\t\\\\b"'), - 'horizontal-tab' => array("\t\\t", '"\t\\\\t"'), - 'line-feed' => array("\t\\n", '"\t\\\\n"'), - 'vertical-tab' => array("\t\\v", '"\t\\\\v"'), - 'form-feed' => array("\t\\f", '"\t\\\\f"'), - 'carriage-return' => array("\t\\r", '"\t\\\\r"'), - 'escape' => array("\t\\e", '"\t\\\\e"'), - 'space' => array("\t\\ ", '"\t\\\\ "'), - 'double-quote' => array("\t\\\"", '"\t\\\\\\""'), - 'slash' => array("\t\\/", '"\t\\\\/"'), - 'backslash' => array("\t\\\\", '"\t\\\\\\\\"'), - 'next-line' => array("\t\\N", '"\t\\\\N"'), - 'non-breaking-space' => array("\t\\�", '"\t\\\\�"'), - 'line-separator' => array("\t\\L", '"\t\\\\L"'), - 'paragraph-separator' => array("\t\\P", '"\t\\\\P"'), - ); - } - - public function testBinaryDataIsDumpedBase64Encoded() - { - $binaryData = file_get_contents(__DIR__.'/Fixtures/arrow.gif'); - $expected = '{ data: !!binary '.base64_encode($binaryData).' }'; - - $this->assertSame($expected, $this->dumper->dump(array('data' => $binaryData))); - } - - public function testNonUtf8DataIsDumpedBase64Encoded() - { - // "für" (ISO-8859-1 encoded) - $this->assertSame('!!binary ZsM/cg==', $this->dumper->dump("f\xc3\x3fr")); - } - - /** - * @dataProvider objectAsMapProvider - */ - public function testDumpObjectAsMap($object, $expected) - { - $yaml = $this->dumper->dump($object, 0, 0, Yaml::DUMP_OBJECT_AS_MAP); - - $this->assertEquals($expected, Yaml::parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP)); - } - - public function objectAsMapProvider() - { - $tests = array(); - - $bar = new \stdClass(); - $bar->class = 'classBar'; - $bar->args = array('bar'); - $zar = new \stdClass(); - $foo = new \stdClass(); - $foo->bar = $bar; - $foo->zar = $zar; - $object = new \stdClass(); - $object->foo = $foo; - $tests['stdClass'] = array($object, $object); - - $arrayObject = new \ArrayObject(); - $arrayObject['foo'] = 'bar'; - $arrayObject['baz'] = 'foobar'; - $parsedArrayObject = new \stdClass(); - $parsedArrayObject->foo = 'bar'; - $parsedArrayObject->baz = 'foobar'; - $tests['ArrayObject'] = array($arrayObject, $parsedArrayObject); - - $a = new A(); - $tests['arbitrary-object'] = array($a, null); - - return $tests; - } - - public function testDumpMultiLineStringAsScalarBlock() - { - $data = array( - 'data' => array( - 'single_line' => 'foo bar baz', - 'multi_line' => "foo\nline with trailing spaces:\n \nbar\r\ninteger like line:\n123456789\nempty line:\n\nbaz", - 'nested_inlined_multi_line_string' => array( - 'inlined_multi_line' => "foo\nbar\r\nempty line:\n\nbaz", - ), - ), - ); - - $this->assertSame(file_get_contents(__DIR__.'/Fixtures/multiple_lines_as_literal_block.yml'), $this->dumper->dump($data, 3, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK)); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The indentation must be greater than zero - */ - public function testZeroIndentationThrowsException() - { - new Dumper(0); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The indentation must be greater than zero - */ - public function testNegativeIndentationThrowsException() - { - new Dumper(-4); - } -} - -class A -{ - public $a = 'foo'; -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsAnchorAlias.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsAnchorAlias.yml deleted file mode 100644 index 5f9c94275b..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsAnchorAlias.yml +++ /dev/null @@ -1,31 +0,0 @@ ---- %YAML:1.0 -test: Simple Alias Example -brief: > - If you need to refer to the same item of data twice, - you can give that item an alias. The alias is a plain - string, starting with an ampersand. The item may then - be referred to by the alias throughout your document - by using an asterisk before the name of the alias. - This is called an anchor. -yaml: | - - &showell Steve - - Clark - - Brian - - Oren - - *showell -php: | - array('Steve', 'Clark', 'Brian', 'Oren', 'Steve') - ---- -test: Alias of a Mapping -brief: > - An alias can be used on any item of data, including - sequences, mappings, and other complex data types. -yaml: | - - &hello - Meat: pork - Starch: potato - - banana - - *hello -php: | - array(array('Meat'=>'pork', 'Starch'=>'potato'), 'banana', array('Meat'=>'pork', 'Starch'=>'potato')) diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsBasicTests.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsBasicTests.yml deleted file mode 100644 index dfd93021d1..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsBasicTests.yml +++ /dev/null @@ -1,202 +0,0 @@ ---- %YAML:1.0 -test: Simple Sequence -brief: | - You can specify a list in YAML by placing each - member of the list on a new line with an opening - dash. These lists are called sequences. -yaml: | - - apple - - banana - - carrot -php: | - array('apple', 'banana', 'carrot') ---- -test: Sequence With Item Being Null In The Middle -brief: | - You can specify a list in YAML by placing each - member of the list on a new line with an opening - dash. These lists are called sequences. -yaml: | - - apple - - - - carrot -php: | - array('apple', null, 'carrot') ---- -test: Sequence With Last Item Being Null -brief: | - You can specify a list in YAML by placing each - member of the list on a new line with an opening - dash. These lists are called sequences. -yaml: | - - apple - - banana - - -php: | - array('apple', 'banana', null) ---- -test: Nested Sequences -brief: | - You can include a sequence within another - sequence by giving the sequence an empty - dash, followed by an indented list. -yaml: | - - - - foo - - bar - - baz -php: | - array(array('foo', 'bar', 'baz')) ---- -test: Mixed Sequences -brief: | - Sequences can contain any YAML data, - including strings and other sequences. -yaml: | - - apple - - - - foo - - bar - - x123 - - banana - - carrot -php: | - array('apple', array('foo', 'bar', 'x123'), 'banana', 'carrot') ---- -test: Deeply Nested Sequences -brief: | - Sequences can be nested even deeper, with each - level of indentation representing a level of - depth. -yaml: | - - - - - - uno - - dos -php: | - array(array(array('uno', 'dos'))) ---- -test: Simple Mapping -brief: | - You can add a keyed list (also known as a dictionary or - hash) to your document by placing each member of the - list on a new line, with a colon separating the key - from its value. In YAML, this type of list is called - a mapping. -yaml: | - foo: whatever - bar: stuff -php: | - array('foo' => 'whatever', 'bar' => 'stuff') ---- -test: Sequence in a Mapping -brief: | - A value in a mapping can be a sequence. -yaml: | - foo: whatever - bar: - - uno - - dos -php: | - array('foo' => 'whatever', 'bar' => array('uno', 'dos')) ---- -test: Nested Mappings -brief: | - A value in a mapping can be another mapping. -yaml: | - foo: whatever - bar: - fruit: apple - name: steve - sport: baseball -php: | - array( - 'foo' => 'whatever', - 'bar' => array( - 'fruit' => 'apple', - 'name' => 'steve', - 'sport' => 'baseball' - ) - ) ---- -test: Mixed Mapping -brief: | - A mapping can contain any assortment - of mappings and sequences as values. -yaml: | - foo: whatever - bar: - - - fruit: apple - name: steve - sport: baseball - - more - - - python: rocks - perl: papers - ruby: scissorses -php: | - array( - 'foo' => 'whatever', - 'bar' => array( - array( - 'fruit' => 'apple', - 'name' => 'steve', - 'sport' => 'baseball' - ), - 'more', - array( - 'python' => 'rocks', - 'perl' => 'papers', - 'ruby' => 'scissorses' - ) - ) - ) ---- -test: Mapping-in-Sequence Shortcut -todo: true -brief: | - If you are adding a mapping to a sequence, you - can place the mapping on the same line as the - dash as a shortcut. -yaml: | - - work on YAML.py: - - work on Store -php: | - array(array('work on YAML.py' => array('work on Store'))) ---- -test: Sequence-in-Mapping Shortcut -todo: true -brief: | - The dash in a sequence counts as indentation, so - you can add a sequence inside of a mapping without - needing spaces as indentation. -yaml: | - allow: - - 'localhost' - - '%.sourceforge.net' - - '%.freepan.org' -php: | - array('allow' => array('localhost', '%.sourceforge.net', '%.freepan.org')) ---- -todo: true -test: Merge key -brief: | - A merge key ('<<') can be used in a mapping to insert other mappings. If - the value associated with the merge key is a mapping, each of its key/value - pairs is inserted into the current mapping. -yaml: | - mapping: - name: Joe - job: Accountant - <<: - age: 38 -php: | - array( - 'mapping' => - array( - 'name' => 'Joe', - 'job' => 'Accountant', - 'age' => 38 - ) - ) diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsBlockMapping.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsBlockMapping.yml deleted file mode 100644 index f7ca469b40..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsBlockMapping.yml +++ /dev/null @@ -1,51 +0,0 @@ ---- -test: One Element Mapping -brief: | - A mapping with one key/value pair -yaml: | - foo: bar -php: | - array('foo' => 'bar') ---- -test: Multi Element Mapping -brief: | - More than one key/value pair -yaml: | - red: baron - white: walls - blue: berries -php: | - array( - 'red' => 'baron', - 'white' => 'walls', - 'blue' => 'berries', - ) ---- -test: Values aligned -brief: | - Often times human editors of documents will align the values even - though YAML emitters generally don't. -yaml: | - red: baron - white: walls - blue: berries -php: | - array( - 'red' => 'baron', - 'white' => 'walls', - 'blue' => 'berries', - ) ---- -test: Colons aligned -brief: | - Spaces can come before the ': ' key/value separator. -yaml: | - red : baron - white : walls - blue : berries -php: | - array( - 'red' => 'baron', - 'white' => 'walls', - 'blue' => 'berries', - ) diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsDocumentSeparator.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsDocumentSeparator.yml deleted file mode 100644 index d98810256e..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsDocumentSeparator.yml +++ /dev/null @@ -1,85 +0,0 @@ ---- %YAML:1.0 -test: Trailing Document Separator -todo: true -brief: > - You can separate YAML documents - with a string of three dashes. -yaml: | - - foo: 1 - bar: 2 - --- - more: stuff -python: | - [ - [ { 'foo': 1, 'bar': 2 } ], - { 'more': 'stuff' } - ] -ruby: | - [ { 'foo' => 1, 'bar' => 2 } ] - ---- -test: Leading Document Separator -todo: true -brief: > - You can explicitly give an opening - document separator to your YAML stream. -yaml: | - --- - - foo: 1 - bar: 2 - --- - more: stuff -python: | - [ - [ {'foo': 1, 'bar': 2}], - {'more': 'stuff'} - ] -ruby: | - [ { 'foo' => 1, 'bar' => 2 } ] - ---- -test: YAML Header -todo: true -brief: > - The opening separator can contain directives - to the YAML parser, such as the version - number. -yaml: | - --- %YAML:1.0 - foo: 1 - bar: 2 -php: | - array('foo' => 1, 'bar' => 2) -documents: 1 - ---- -test: Red Herring Document Separator -brief: > - Separators included in blocks or strings - are treated as blocks or strings, as the - document separator should have no indentation - preceding it. -yaml: | - foo: | - --- -php: | - array('foo' => "---\n") - ---- -test: Multiple Document Separators in Block -brief: > - This technique allows you to embed other YAML - documents within literal blocks. -yaml: | - foo: | - --- - foo: bar - --- - yo: baz - bar: | - fooness -php: | - array( - 'foo' => "---\nfoo: bar\n---\nyo: baz\n", - 'bar' => "fooness\n" - ) diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsErrorTests.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsErrorTests.yml deleted file mode 100644 index e8506fcb66..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsErrorTests.yml +++ /dev/null @@ -1,25 +0,0 @@ ---- -test: Missing value for hash item -todo: true -brief: | - Third item in this hash doesn't have a value -yaml: | - okay: value - also okay: ~ - causes error because no value specified - last key: value okay here too -python-error: causes error because no value specified - ---- -test: Not indenting enough -brief: | - There was a bug in PyYaml where it was off by one - in the indentation check. It was allowing the YAML - below. -# This is actually valid YAML now. Someone should tell showell. -yaml: | - foo: - firstline: 1 - secondline: 2 -php: | - array('foo' => null, 'firstline' => 1, 'secondline' => 2) diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsFlowCollections.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsFlowCollections.yml deleted file mode 100644 index 03090e4abc..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsFlowCollections.yml +++ /dev/null @@ -1,60 +0,0 @@ ---- -test: Simple Inline Array -brief: > - Sequences can be contained on a - single line, using the inline syntax. - Separate each entry with commas and - enclose in square brackets. -yaml: | - seq: [ a, b, c ] -php: | - array('seq' => array('a', 'b', 'c')) ---- -test: Simple Inline Hash -brief: > - Mapping can also be contained on - a single line, using the inline - syntax. Each key-value pair is - separated by a colon, with a comma - between each entry in the mapping. - Enclose with curly braces. -yaml: | - hash: { name: Steve, foo: bar } -php: | - array('hash' => array('name' => 'Steve', 'foo' => 'bar')) ---- -test: Multi-line Inline Collections -todo: true -brief: > - Both inline sequences and inline mappings - can span multiple lines, provided that you - indent the additional lines. -yaml: | - languages: [ Ruby, - Perl, - Python ] - websites: { YAML: yaml.org, - Ruby: ruby-lang.org, - Python: python.org, - Perl: use.perl.org } -php: | - array( - 'languages' => array('Ruby', 'Perl', 'Python'), - 'websites' => array( - 'YAML' => 'yaml.org', - 'Ruby' => 'ruby-lang.org', - 'Python' => 'python.org', - 'Perl' => 'use.perl.org' - ) - ) ---- -test: Commas in Values (not in the spec!) -todo: true -brief: > - List items in collections are delimited by commas, but - there must be a space after each comma. This allows you - to add numbers without quoting. -yaml: | - attendances: [ 45,123, 70,000, 17,222 ] -php: | - array('attendances' => array(45123, 70000, 17222)) diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsFoldedScalars.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsFoldedScalars.yml deleted file mode 100644 index a14735a55a..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsFoldedScalars.yml +++ /dev/null @@ -1,176 +0,0 @@ ---- %YAML:1.0 -test: Single ending newline -brief: > - A pipe character, followed by an indented - block of text is treated as a literal - block, in which newlines are preserved - throughout the block, including the final - newline. -yaml: | - --- - this: | - Foo - Bar -php: | - array('this' => "Foo\nBar\n") ---- -test: The '+' indicator -brief: > - The '+' indicator says to keep newlines at the end of text - blocks. -yaml: | - normal: | - extra new lines not kept - - preserving: |+ - extra new lines are kept - - - dummy: value -php: | - array( - 'normal' => "extra new lines not kept\n", - 'preserving' => "extra new lines are kept\n\n\n", - 'dummy' => 'value' - ) ---- -test: Three trailing newlines in literals -brief: > - To give you more control over how space - is preserved in text blocks, YAML has - the keep '+' and chomp '-' indicators. - The keep indicator will preserve all - ending newlines, while the chomp indicator - will strip all ending newlines. -yaml: | - clipped: | - This has one newline. - - - - same as "clipped" above: "This has one newline.\n" - - stripped: |- - This has no newline. - - - - same as "stripped" above: "This has no newline." - - kept: |+ - This has four newlines. - - - - same as "kept" above: "This has four newlines.\n\n\n\n" -php: | - array( - 'clipped' => "This has one newline.\n", - 'same as "clipped" above' => "This has one newline.\n", - 'stripped' => 'This has no newline.', - 'same as "stripped" above' => 'This has no newline.', - 'kept' => "This has four newlines.\n\n\n\n", - 'same as "kept" above' => "This has four newlines.\n\n\n\n" - ) ---- -test: Extra trailing newlines with spaces -todo: true -brief: > - Normally, only a single newline is kept - from the end of a literal block, unless the - keep '+' character is used in combination - with the pipe. The following example - will preserve all ending whitespace - since the last line of both literal blocks - contains spaces which extend past the indentation - level. -yaml: | - --- - this: | - Foo - - - kept: |+ - Foo - - -php: | - array('this' => "Foo\n\n \n", - 'kept' => "Foo\n\n \n" ) - ---- -test: Folded Block in a Sequence -brief: > - A greater-then character, followed by an indented - block of text is treated as a folded block, in - which lines of text separated by a single newline - are concatenated as a single line. -yaml: | - --- - - apple - - banana - - > - can't you see - the beauty of yaml? - hmm - - dog -php: | - array( - 'apple', - 'banana', - "can't you see the beauty of yaml? hmm\n", - 'dog' - ) ---- -test: Folded Block as a Mapping Value -brief: > - Both literal and folded blocks can be - used in collections, as values in a - sequence or a mapping. -yaml: | - --- - quote: > - Mark McGwire's - year was crippled - by a knee injury. - source: espn -php: | - array( - 'quote' => "Mark McGwire's year was crippled by a knee injury.\n", - 'source' => 'espn' - ) ---- -test: Three trailing newlines in folded blocks -brief: > - The keep and chomp indicators can also - be applied to folded blocks. -yaml: | - clipped: > - This has one newline. - - - - same as "clipped" above: "This has one newline.\n" - - stripped: >- - This has no newline. - - - - same as "stripped" above: "This has no newline." - - kept: >+ - This has four newlines. - - - - same as "kept" above: "This has four newlines.\n\n\n\n" -php: | - array( - 'clipped' => "This has one newline.\n", - 'same as "clipped" above' => "This has one newline.\n", - 'stripped' => 'This has no newline.', - 'same as "stripped" above' => 'This has no newline.', - 'kept' => "This has four newlines.\n\n\n\n", - 'same as "kept" above' => "This has four newlines.\n\n\n\n" - ) diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsNullsAndEmpties.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsNullsAndEmpties.yml deleted file mode 100644 index 9a5300f2ef..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsNullsAndEmpties.yml +++ /dev/null @@ -1,45 +0,0 @@ ---- %YAML:1.0 -test: Empty Sequence -brief: > - You can represent the empty sequence - with an empty inline sequence. -yaml: | - empty: [] -php: | - array('empty' => array()) ---- -test: Empty Mapping -brief: > - You can represent the empty mapping - with an empty inline mapping. -yaml: | - empty: {} -php: | - array('empty' => array()) ---- -test: Empty Sequence as Entire Document -yaml: | - [] -php: | - array() ---- -test: Empty Mapping as Entire Document -yaml: | - {} -php: | - array() ---- -test: Null as Document -yaml: | - ~ -php: | - null ---- -test: Empty String -brief: > - You can represent an empty string - with a pair of quotes. -yaml: | - '' -php: | - '' diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsSpecificationExamples.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsSpecificationExamples.yml deleted file mode 100644 index ec1c4c3a1a..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsSpecificationExamples.yml +++ /dev/null @@ -1,1697 +0,0 @@ ---- %YAML:1.0 -test: Sequence of scalars -spec: 2.1 -yaml: | - - Mark McGwire - - Sammy Sosa - - Ken Griffey -php: | - array('Mark McGwire', 'Sammy Sosa', 'Ken Griffey') ---- -test: Mapping of scalars to scalars -spec: 2.2 -yaml: | - hr: 65 - avg: 0.278 - rbi: 147 -php: | - array('hr' => 65, 'avg' => 0.278, 'rbi' => 147) ---- -test: Mapping of scalars to sequences -spec: 2.3 -yaml: | - american: - - Boston Red Sox - - Detroit Tigers - - New York Yankees - national: - - New York Mets - - Chicago Cubs - - Atlanta Braves -php: | - array('american' => - array( 'Boston Red Sox', 'Detroit Tigers', - 'New York Yankees' ), - 'national' => - array( 'New York Mets', 'Chicago Cubs', - 'Atlanta Braves' ) - ) ---- -test: Sequence of mappings -spec: 2.4 -yaml: | - - - name: Mark McGwire - hr: 65 - avg: 0.278 - - - name: Sammy Sosa - hr: 63 - avg: 0.288 -php: | - array( - array('name' => 'Mark McGwire', 'hr' => 65, 'avg' => 0.278), - array('name' => 'Sammy Sosa', 'hr' => 63, 'avg' => 0.288) - ) ---- -test: Legacy A5 -todo: true -spec: legacy_A5 -yaml: | - ? - - New York Yankees - - Atlanta Braves - : - - 2001-07-02 - - 2001-08-12 - - 2001-08-14 - ? - - Detroit Tigers - - Chicago Cubs - : - - 2001-07-23 -perl-busted: > - YAML.pm will be able to emulate this behavior soon. In this regard - it may be somewhat more correct than Python's native behaviour which - can only use tuples as mapping keys. PyYAML will also need to figure - out some clever way to roundtrip structured keys. -python: | - [ - { - ('New York Yankees', 'Atlanta Braves'): - [yaml.timestamp('2001-07-02'), - yaml.timestamp('2001-08-12'), - yaml.timestamp('2001-08-14')], - ('Detroit Tigers', 'Chicago Cubs'): - [yaml.timestamp('2001-07-23')] - } - ] -ruby: | - { - [ 'New York Yankees', 'Atlanta Braves' ] => - [ Date.new( 2001, 7, 2 ), Date.new( 2001, 8, 12 ), Date.new( 2001, 8, 14 ) ], - [ 'Detroit Tigers', 'Chicago Cubs' ] => - [ Date.new( 2001, 7, 23 ) ] - } -syck: | - struct test_node seq1[] = { - { T_STR, 0, "New York Yankees" }, - { T_STR, 0, "Atlanta Braves" }, - end_node - }; - struct test_node seq2[] = { - { T_STR, 0, "2001-07-02" }, - { T_STR, 0, "2001-08-12" }, - { T_STR, 0, "2001-08-14" }, - end_node - }; - struct test_node seq3[] = { - { T_STR, 0, "Detroit Tigers" }, - { T_STR, 0, "Chicago Cubs" }, - end_node - }; - struct test_node seq4[] = { - { T_STR, 0, "2001-07-23" }, - end_node - }; - struct test_node map[] = { - { T_SEQ, 0, 0, seq1 }, - { T_SEQ, 0, 0, seq2 }, - { T_SEQ, 0, 0, seq3 }, - { T_SEQ, 0, 0, seq4 }, - end_node - }; - struct test_node stream[] = { - { T_MAP, 0, 0, map }, - end_node - }; - ---- -test: Sequence of sequences -spec: 2.5 -yaml: | - - [ name , hr , avg ] - - [ Mark McGwire , 65 , 0.278 ] - - [ Sammy Sosa , 63 , 0.288 ] -php: | - array( - array( 'name', 'hr', 'avg' ), - array( 'Mark McGwire', 65, 0.278 ), - array( 'Sammy Sosa', 63, 0.288 ) - ) ---- -test: Mapping of mappings -todo: true -spec: 2.6 -yaml: | - Mark McGwire: {hr: 65, avg: 0.278} - Sammy Sosa: { - hr: 63, - avg: 0.288 - } -php: | - array( - 'Mark McGwire' => - array( 'hr' => 65, 'avg' => 0.278 ), - 'Sammy Sosa' => - array( 'hr' => 63, 'avg' => 0.288 ) - ) ---- -test: Two documents in a stream each with a leading comment -todo: true -spec: 2.7 -yaml: | - # Ranking of 1998 home runs - --- - - Mark McGwire - - Sammy Sosa - - Ken Griffey - - # Team ranking - --- - - Chicago Cubs - - St Louis Cardinals -ruby: | - y = YAML::Stream.new - y.add( [ 'Mark McGwire', 'Sammy Sosa', 'Ken Griffey' ] ) - y.add( [ 'Chicago Cubs', 'St Louis Cardinals' ] ) -documents: 2 - ---- -test: Play by play feed from a game -todo: true -spec: 2.8 -yaml: | - --- - time: 20:03:20 - player: Sammy Sosa - action: strike (miss) - ... - --- - time: 20:03:47 - player: Sammy Sosa - action: grand slam - ... -perl: | - [ 'Mark McGwire', 'Sammy Sosa', 'Ken Griffey' ] -documents: 2 - ---- -test: Single document with two comments -spec: 2.9 -yaml: | - hr: # 1998 hr ranking - - Mark McGwire - - Sammy Sosa - rbi: - # 1998 rbi ranking - - Sammy Sosa - - Ken Griffey -php: | - array( - 'hr' => array( 'Mark McGwire', 'Sammy Sosa' ), - 'rbi' => array( 'Sammy Sosa', 'Ken Griffey' ) - ) ---- -test: Node for Sammy Sosa appears twice in this document -spec: 2.10 -yaml: | - --- - hr: - - Mark McGwire - # Following node labeled SS - - &SS Sammy Sosa - rbi: - - *SS # Subsequent occurrence - - Ken Griffey -php: | - array( - 'hr' => - array('Mark McGwire', 'Sammy Sosa'), - 'rbi' => - array('Sammy Sosa', 'Ken Griffey') - ) ---- -test: Mapping between sequences -todo: true -spec: 2.11 -yaml: | - ? # PLAY SCHEDULE - - Detroit Tigers - - Chicago Cubs - : - - 2001-07-23 - - ? [ New York Yankees, - Atlanta Braves ] - : [ 2001-07-02, 2001-08-12, - 2001-08-14 ] -ruby: | - { - [ 'Detroit Tigers', 'Chicago Cubs' ] => [ Date.new( 2001, 7, 23 ) ], - [ 'New York Yankees', 'Atlanta Braves' ] => [ Date.new( 2001, 7, 2 ), Date.new( 2001, 8, 12 ), Date.new( 2001, 8, 14 ) ] - } -syck: | - struct test_node seq1[] = { - { T_STR, 0, "New York Yankees" }, - { T_STR, 0, "Atlanta Braves" }, - end_node - }; - struct test_node seq2[] = { - { T_STR, 0, "2001-07-02" }, - { T_STR, 0, "2001-08-12" }, - { T_STR, 0, "2001-08-14" }, - end_node - }; - struct test_node seq3[] = { - { T_STR, 0, "Detroit Tigers" }, - { T_STR, 0, "Chicago Cubs" }, - end_node - }; - struct test_node seq4[] = { - { T_STR, 0, "2001-07-23" }, - end_node - }; - struct test_node map[] = { - { T_SEQ, 0, 0, seq3 }, - { T_SEQ, 0, 0, seq4 }, - { T_SEQ, 0, 0, seq1 }, - { T_SEQ, 0, 0, seq2 }, - end_node - }; - struct test_node stream[] = { - { T_MAP, 0, 0, map }, - end_node - }; - ---- -test: Sequence key shortcut -spec: 2.12 -yaml: | - --- - # products purchased - - item : Super Hoop - quantity: 1 - - item : Basketball - quantity: 4 - - item : Big Shoes - quantity: 1 -php: | - array ( - array ( - 'item' => 'Super Hoop', - 'quantity' => 1, - ), - array ( - 'item' => 'Basketball', - 'quantity' => 4, - ), - array ( - 'item' => 'Big Shoes', - 'quantity' => 1, - ) - ) -perl: | - [ - { item => 'Super Hoop', quantity => 1 }, - { item => 'Basketball', quantity => 4 }, - { item => 'Big Shoes', quantity => 1 } - ] - -ruby: | - [ - { 'item' => 'Super Hoop', 'quantity' => 1 }, - { 'item' => 'Basketball', 'quantity' => 4 }, - { 'item' => 'Big Shoes', 'quantity' => 1 } - ] -python: | - [ - { 'item': 'Super Hoop', 'quantity': 1 }, - { 'item': 'Basketball', 'quantity': 4 }, - { 'item': 'Big Shoes', 'quantity': 1 } - ] -syck: | - struct test_node map1[] = { - { T_STR, 0, "item" }, - { T_STR, 0, "Super Hoop" }, - { T_STR, 0, "quantity" }, - { T_STR, 0, "1" }, - end_node - }; - struct test_node map2[] = { - { T_STR, 0, "item" }, - { T_STR, 0, "Basketball" }, - { T_STR, 0, "quantity" }, - { T_STR, 0, "4" }, - end_node - }; - struct test_node map3[] = { - { T_STR, 0, "item" }, - { T_STR, 0, "Big Shoes" }, - { T_STR, 0, "quantity" }, - { T_STR, 0, "1" }, - end_node - }; - struct test_node seq[] = { - { T_MAP, 0, 0, map1 }, - { T_MAP, 0, 0, map2 }, - { T_MAP, 0, 0, map3 }, - end_node - }; - struct test_node stream[] = { - { T_SEQ, 0, 0, seq }, - end_node - }; - - ---- -test: Literal perserves newlines -todo: true -spec: 2.13 -yaml: | - # ASCII Art - --- | - \//||\/|| - // || ||_ -perl: | - "\\//||\\/||\n// || ||_\n" -ruby: | - "\\//||\\/||\n// || ||_\n" -python: | - [ - flushLeft( - """ - \//||\/|| - // || ||_ - """ - ) - ] -syck: | - struct test_node stream[] = { - { T_STR, 0, "\\//||\\/||\n// || ||_\n" }, - end_node - }; - ---- -test: Folded treats newlines as a space -todo: true -spec: 2.14 -yaml: | - --- - Mark McGwire's - year was crippled - by a knee injury. -perl: | - "Mark McGwire's year was crippled by a knee injury." -ruby: | - "Mark McGwire's year was crippled by a knee injury." -python: | - [ "Mark McGwire's year was crippled by a knee injury." ] -syck: | - struct test_node stream[] = { - { T_STR, 0, "Mark McGwire's year was crippled by a knee injury." }, - end_node - }; - ---- -test: Newlines preserved for indented and blank lines -todo: true -spec: 2.15 -yaml: | - --- > - Sammy Sosa completed another - fine season with great stats. - - 63 Home Runs - 0.288 Batting Average - - What a year! -perl: | - "Sammy Sosa completed another fine season with great stats.\n\n 63 Home Runs\n 0.288 Batting Average\n\nWhat a year!\n" -ruby: | - "Sammy Sosa completed another fine season with great stats.\n\n 63 Home Runs\n 0.288 Batting Average\n\nWhat a year!\n" -python: | - [ - flushLeft( - """ - Sammy Sosa completed another fine season with great stats. - - 63 Home Runs - 0.288 Batting Average - - What a year! - """ - ) - ] -syck: | - struct test_node stream[] = { - { T_STR, 0, "Sammy Sosa completed another fine season with great stats.\n\n 63 Home Runs\n 0.288 Batting Average\n\nWhat a year!\n" }, - end_node - }; - - ---- -test: Indentation determines scope -spec: 2.16 -yaml: | - name: Mark McGwire - accomplishment: > - Mark set a major league - home run record in 1998. - stats: | - 65 Home Runs - 0.278 Batting Average -php: | - array( - 'name' => 'Mark McGwire', - 'accomplishment' => "Mark set a major league home run record in 1998.\n", - 'stats' => "65 Home Runs\n0.278 Batting Average\n" - ) ---- -test: Quoted scalars -todo: true -spec: 2.17 -yaml: | - unicode: "Sosa did fine.\u263A" - control: "\b1998\t1999\t2000\n" - hexesc: "\x0D\x0A is \r\n" - - single: '"Howdy!" he cried.' - quoted: ' # not a ''comment''.' - tie-fighter: '|\-*-/|' -ruby: | - { - "tie-fighter" => "|\\-*-/|", - "control"=>"\0101998\t1999\t2000\n", - "unicode"=>"Sosa did fine." + ["263A".hex ].pack('U*'), - "quoted"=>" # not a 'comment'.", - "single"=>"\"Howdy!\" he cried.", - "hexesc"=>"\r\n is \r\n" - } ---- -test: Multiline flow scalars -todo: true -spec: 2.18 -yaml: | - plain: - This unquoted scalar - spans many lines. - - quoted: "So does this - quoted scalar.\n" -ruby: | - { - 'plain' => 'This unquoted scalar spans many lines.', - 'quoted' => "So does this quoted scalar.\n" - } ---- -test: Integers -spec: 2.19 -yaml: | - canonical: 12345 - decimal: +12,345 - octal: 014 - hexadecimal: 0xC -php: | - array( - 'canonical' => 12345, - 'decimal' => 12345.0, - 'octal' => 014, - 'hexadecimal' => 0xC - ) ---- -# FIX: spec shows parens around -inf and NaN -test: Floating point -spec: 2.20 -yaml: | - canonical: 1.23015e+3 - exponential: 12.3015e+02 - fixed: 1,230.15 - negative infinity: -.inf - not a number: .NaN - float as whole number: !!float 1 -php: | - array( - 'canonical' => 1230.15, - 'exponential' => 1230.15, - 'fixed' => 1230.15, - 'negative infinity' => log(0), - 'not a number' => -log(0), - 'float as whole number' => (float) 1 - ) ---- -test: Miscellaneous -spec: 2.21 -yaml: | - null: ~ - true: true - false: false - string: '12345' -php: | - array( - '' => null, - 1 => true, - 0 => false, - 'string' => '12345' - ) ---- -test: Timestamps -todo: true -spec: 2.22 -yaml: | - canonical: 2001-12-15T02:59:43.1Z - iso8601: 2001-12-14t21:59:43.10-05:00 - spaced: 2001-12-14 21:59:43.10 -05:00 - date: 2002-12-14 # Time is noon UTC -php: | - array( - 'canonical' => YAML::mktime( 2001, 12, 15, 2, 59, 43, 0.10 ), - 'iso8601' => YAML::mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ), - 'spaced' => YAML::mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ), - 'date' => Date.new( 2002, 12, 14 ) - ) ---- -test: legacy Timestamps test -todo: true -spec: legacy D4 -yaml: | - canonical: 2001-12-15T02:59:43.00Z - iso8601: 2001-02-28t21:59:43.00-05:00 - spaced: 2001-12-14 21:59:43.00 -05:00 - date: 2002-12-14 -php: | - array( - 'canonical' => Time::utc( 2001, 12, 15, 2, 59, 43, 0 ), - 'iso8601' => YAML::mktime( 2001, 2, 28, 21, 59, 43, 0, "-05:00" ), - 'spaced' => YAML::mktime( 2001, 12, 14, 21, 59, 43, 0, "-05:00" ), - 'date' => Date.new( 2002, 12, 14 ) - ) ---- -test: Various explicit families -todo: true -spec: 2.23 -yaml: | - not-date: !str 2002-04-28 - picture: !binary | - R0lGODlhDAAMAIQAAP//9/X - 17unp5WZmZgAAAOfn515eXv - Pz7Y6OjuDg4J+fn5OTk6enp - 56enmleECcgggoBADs= - - application specific tag: !!something | - The semantics of the tag - above may be different for - different documents. - -ruby-setup: | - YAML.add_private_type( "something" ) do |type, val| - "SOMETHING: #{val}" - end -ruby: | - { - 'not-date' => '2002-04-28', - 'picture' => "GIF89a\f\000\f\000\204\000\000\377\377\367\365\365\356\351\351\345fff\000\000\000\347\347\347^^^\363\363\355\216\216\216\340\340\340\237\237\237\223\223\223\247\247\247\236\236\236i^\020' \202\n\001\000;", - 'application specific tag' => "SOMETHING: The semantics of the tag\nabove may be different for\ndifferent documents.\n" - } ---- -test: Application specific family -todo: true -spec: 2.24 -yaml: | - # Establish a tag prefix - --- !clarkevans.com,2002/graph/^shape - # Use the prefix: shorthand for - # !clarkevans.com,2002/graph/circle - - !^circle - center: &ORIGIN {x: 73, 'y': 129} - radius: 7 - - !^line # !clarkevans.com,2002/graph/line - start: *ORIGIN - finish: { x: 89, 'y': 102 } - - !^label - start: *ORIGIN - color: 0xFFEEBB - value: Pretty vector drawing. -ruby-setup: | - YAML.add_domain_type( "clarkevans.com,2002", 'graph/shape' ) { |type, val| - if Array === val - val << "Shape Container" - val - else - raise YAML::Error, "Invalid graph of class #{ val.class }: " + val.inspect - end - } - one_shape_proc = Proc.new { |type, val| - scheme, domain, type = type.split( /:/, 3 ) - if val.is_a? ::Hash - val['TYPE'] = "Shape: #{type}" - val - else - raise YAML::Error, "Invalid graph of class #{ val.class }: " + val.inspect - end - } - YAML.add_domain_type( "clarkevans.com,2002", 'graph/circle', &one_shape_proc ) - YAML.add_domain_type( "clarkevans.com,2002", 'graph/line', &one_shape_proc ) - YAML.add_domain_type( "clarkevans.com,2002", 'graph/label', &one_shape_proc ) -ruby: | - [ - { - "radius" => 7, - "center"=> - { - "x" => 73, - "y" => 129 - }, - "TYPE" => "Shape: graph/circle" - }, { - "finish" => - { - "x" => 89, - "y" => 102 - }, - "TYPE" => "Shape: graph/line", - "start" => - { - "x" => 73, - "y" => 129 - } - }, { - "TYPE" => "Shape: graph/label", - "value" => "Pretty vector drawing.", - "start" => - { - "x" => 73, - "y" => 129 - }, - "color" => 16772795 - }, - "Shape Container" - ] -# --- -# test: Unordered set -# spec: 2.25 -# yaml: | -# # sets are represented as a -# # mapping where each key is -# # associated with the empty string -# --- !set -# ? Mark McGwire -# ? Sammy Sosa -# ? Ken Griff ---- -test: Ordered mappings -todo: true -spec: 2.26 -yaml: | - # ordered maps are represented as - # a sequence of mappings, with - # each mapping having one key - --- !omap - - Mark McGwire: 65 - - Sammy Sosa: 63 - - Ken Griffy: 58 -ruby: | - YAML::Omap[ - 'Mark McGwire', 65, - 'Sammy Sosa', 63, - 'Ken Griffy', 58 - ] ---- -test: Invoice -dump_skip: true -spec: 2.27 -yaml: | - --- !clarkevans.com,2002/^invoice - invoice: 34843 - date : 2001-01-23 - bill-to: &id001 - given : Chris - family : Dumars - address: - lines: | - 458 Walkman Dr. - Suite #292 - city : Royal Oak - state : MI - postal : 48046 - ship-to: *id001 - product: - - - sku : BL394D - quantity : 4 - description : Basketball - price : 450.00 - - - sku : BL4438H - quantity : 1 - description : Super Hoop - price : 2392.00 - tax : 251.42 - total: 4443.52 - comments: > - Late afternoon is best. - Backup contact is Nancy - Billsmer @ 338-4338. -php: | - array( - 'invoice' => 34843, 'date' => gmmktime(0, 0, 0, 1, 23, 2001), - 'bill-to' => - array( 'given' => 'Chris', 'family' => 'Dumars', 'address' => array( 'lines' => "458 Walkman Dr.\nSuite #292\n", 'city' => 'Royal Oak', 'state' => 'MI', 'postal' => 48046 ) ) - , 'ship-to' => - array( 'given' => 'Chris', 'family' => 'Dumars', 'address' => array( 'lines' => "458 Walkman Dr.\nSuite #292\n", 'city' => 'Royal Oak', 'state' => 'MI', 'postal' => 48046 ) ) - , 'product' => - array( - array( 'sku' => 'BL394D', 'quantity' => 4, 'description' => 'Basketball', 'price' => 450.00 ), - array( 'sku' => 'BL4438H', 'quantity' => 1, 'description' => 'Super Hoop', 'price' => 2392.00 ) - ), - 'tax' => 251.42, 'total' => 4443.52, - 'comments' => "Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338.\n" - ) ---- -test: Log file -todo: true -spec: 2.28 -yaml: | - --- - Time: 2001-11-23 15:01:42 -05:00 - User: ed - Warning: > - This is an error message - for the log file - --- - Time: 2001-11-23 15:02:31 -05:00 - User: ed - Warning: > - A slightly different error - message. - --- - Date: 2001-11-23 15:03:17 -05:00 - User: ed - Fatal: > - Unknown variable "bar" - Stack: - - file: TopClass.py - line: 23 - code: | - x = MoreObject("345\n") - - file: MoreClass.py - line: 58 - code: |- - foo = bar -ruby: | - y = YAML::Stream.new - y.add( { 'Time' => YAML::mktime( 2001, 11, 23, 15, 01, 42, 00, "-05:00" ), - 'User' => 'ed', 'Warning' => "This is an error message for the log file\n" } ) - y.add( { 'Time' => YAML::mktime( 2001, 11, 23, 15, 02, 31, 00, "-05:00" ), - 'User' => 'ed', 'Warning' => "A slightly different error message.\n" } ) - y.add( { 'Date' => YAML::mktime( 2001, 11, 23, 15, 03, 17, 00, "-05:00" ), - 'User' => 'ed', 'Fatal' => "Unknown variable \"bar\"\n", - 'Stack' => [ - { 'file' => 'TopClass.py', 'line' => 23, 'code' => "x = MoreObject(\"345\\n\")\n" }, - { 'file' => 'MoreClass.py', 'line' => 58, 'code' => "foo = bar" } ] } ) -documents: 3 - ---- -test: Throwaway comments -yaml: | - ### These are four throwaway comment ### - - ### lines (the second line is empty). ### - this: | # Comments may trail lines. - contains three lines of text. - The third one starts with a - # character. This isn't a comment. - - # These are three throwaway comment - # lines (the first line is empty). -php: | - array( - 'this' => "contains three lines of text.\nThe third one starts with a\n# character. This isn't a comment.\n" - ) ---- -test: Document with a single value -todo: true -yaml: | - --- > - This YAML stream contains a single text value. - The next stream is a log file - a sequence of - log entries. Adding an entry to the log is a - simple matter of appending it at the end. -ruby: | - "This YAML stream contains a single text value. The next stream is a log file - a sequence of log entries. Adding an entry to the log is a simple matter of appending it at the end.\n" ---- -test: Document stream -todo: true -yaml: | - --- - at: 2001-08-12 09:25:00.00 Z - type: GET - HTTP: '1.0' - url: '/index.html' - --- - at: 2001-08-12 09:25:10.00 Z - type: GET - HTTP: '1.0' - url: '/toc.html' -ruby: | - y = YAML::Stream.new - y.add( { - 'at' => Time::utc( 2001, 8, 12, 9, 25, 00 ), - 'type' => 'GET', - 'HTTP' => '1.0', - 'url' => '/index.html' - } ) - y.add( { - 'at' => Time::utc( 2001, 8, 12, 9, 25, 10 ), - 'type' => 'GET', - 'HTTP' => '1.0', - 'url' => '/toc.html' - } ) -documents: 2 - ---- -test: Top level mapping -yaml: | - # This stream is an example of a top-level mapping. - invoice : 34843 - date : 2001-01-23 - total : 4443.52 -php: | - array( - 'invoice' => 34843, - 'date' => gmmktime(0, 0, 0, 1, 23, 2001), - 'total' => 4443.52 - ) ---- -test: Single-line documents -todo: true -yaml: | - # The following is a sequence of three documents. - # The first contains an empty mapping, the second - # an empty sequence, and the last an empty string. - --- {} - --- [ ] - --- '' -ruby: | - y = YAML::Stream.new - y.add( {} ) - y.add( [] ) - y.add( '' ) -documents: 3 - ---- -test: Document with pause -todo: true -yaml: | - # A communication channel based on a YAML stream. - --- - sent at: 2002-06-06 11:46:25.10 Z - payload: Whatever - # Receiver can process this as soon as the following is sent: - ... - # Even if the next message is sent long after: - --- - sent at: 2002-06-06 12:05:53.47 Z - payload: Whatever - ... -ruby: | - y = YAML::Stream.new - y.add( - { 'sent at' => YAML::mktime( 2002, 6, 6, 11, 46, 25, 0.10 ), - 'payload' => 'Whatever' } - ) - y.add( - { "payload" => "Whatever", "sent at" => YAML::mktime( 2002, 6, 6, 12, 5, 53, 0.47 ) } - ) -documents: 2 - ---- -test: Explicit typing -yaml: | - integer: 12 - also int: ! "12" - string: !str 12 -php: | - array( 'integer' => 12, 'also int' => 12, 'string' => '12' ) ---- -test: Private types -todo: true -yaml: | - # Both examples below make use of the 'x-private:ball' - # type family URI, but with different semantics. - --- - pool: !!ball - number: 8 - color: black - --- - bearing: !!ball - material: steel -ruby: | - y = YAML::Stream.new - y.add( { 'pool' => - YAML::PrivateType.new( 'ball', - { 'number' => 8, 'color' => 'black' } ) } - ) - y.add( { 'bearing' => - YAML::PrivateType.new( 'ball', - { 'material' => 'steel' } ) } - ) -documents: 2 - ---- -test: Type family under yaml.org -yaml: | - # The URI is 'tag:yaml.org,2002:str' - - !str a Unicode string -php: | - array( 'a Unicode string' ) ---- -test: Type family under perl.yaml.org -todo: true -yaml: | - # The URI is 'tag:perl.yaml.org,2002:Text::Tabs' - - !perl/Text::Tabs {} -ruby: | - [ YAML::DomainType.new( 'perl.yaml.org,2002', 'Text::Tabs', {} ) ] ---- -test: Type family under clarkevans.com -todo: true -yaml: | - # The URI is 'tag:clarkevans.com,2003-02:timesheet' - - !clarkevans.com,2003-02/timesheet {} -ruby: | - [ YAML::DomainType.new( 'clarkevans.com,2003-02', 'timesheet', {} ) ] ---- -test: URI Escaping -todo: true -yaml: | - same: - - !domain.tld,2002/type\x30 value - - !domain.tld,2002/type0 value - different: # As far as the YAML parser is concerned - - !domain.tld,2002/type%30 value - - !domain.tld,2002/type0 value -ruby-setup: | - YAML.add_domain_type( "domain.tld,2002", "type0" ) { |type, val| - "ONE: #{val}" - } - YAML.add_domain_type( "domain.tld,2002", "type%30" ) { |type, val| - "TWO: #{val}" - } -ruby: | - { 'same' => [ 'ONE: value', 'ONE: value' ], 'different' => [ 'TWO: value', 'ONE: value' ] } ---- -test: URI Prefixing -todo: true -yaml: | - # 'tag:domain.tld,2002:invoice' is some type family. - invoice: !domain.tld,2002/^invoice - # 'seq' is shorthand for 'tag:yaml.org,2002:seq'. - # This does not effect '^customer' below - # because it is does not specify a prefix. - customers: !seq - # '^customer' is shorthand for the full - # notation 'tag:domain.tld,2002:customer'. - - !^customer - given : Chris - family : Dumars -ruby-setup: | - YAML.add_domain_type( "domain.tld,2002", /(invoice|customer)/ ) { |type, val| - if val.is_a? ::Hash - scheme, domain, type = type.split( /:/, 3 ) - val['type'] = "domain #{type}" - val - else - raise YAML::Error, "Not a Hash in domain.tld/invoice: " + val.inspect - end - } -ruby: | - { "invoice"=> { "customers"=> [ { "given"=>"Chris", "type"=>"domain customer", "family"=>"Dumars" } ], "type"=>"domain invoice" } } - ---- -test: Overriding anchors -yaml: | - anchor : &A001 This scalar has an anchor. - override : &A001 > - The alias node below is a - repeated use of this value. - alias : *A001 -php: | - array( 'anchor' => 'This scalar has an anchor.', - 'override' => "The alias node below is a repeated use of this value.\n", - 'alias' => "The alias node below is a repeated use of this value.\n" ) ---- -test: Flow and block formatting -todo: true -yaml: | - empty: [] - flow: [ one, two, three # May span lines, - , four, # indentation is - five ] # mostly ignored. - block: - - First item in top sequence - - - - Subordinate sequence entry - - > - A folded sequence entry - - Sixth item in top sequence -ruby: | - { 'empty' => [], 'flow' => [ 'one', 'two', 'three', 'four', 'five' ], - 'block' => [ 'First item in top sequence', [ 'Subordinate sequence entry' ], - "A folded sequence entry\n", 'Sixth item in top sequence' ] } ---- -test: Complete mapping test -todo: true -yaml: | - empty: {} - flow: { one: 1, two: 2 } - spanning: { one: 1, - two: 2 } - block: - first : First entry - second: - key: Subordinate mapping - third: - - Subordinate sequence - - { } - - Previous mapping is empty. - - A key: value pair in a sequence. - A second: key:value pair. - - The previous entry is equal to the following one. - - - A key: value pair in a sequence. - A second: key:value pair. - !float 12 : This key is a float. - ? > - ? - : This key had to be protected. - "\a" : This key had to be escaped. - ? > - This is a - multi-line - folded key - : Whose value is - also multi-line. - ? this also works as a key - : with a value at the next line. - ? - - This key - - is a sequence - : - - With a sequence value. - ? - This: key - is a: mapping - : - with a: mapping value. -ruby: | - { 'empty' => {}, 'flow' => { 'one' => 1, 'two' => 2 }, - 'spanning' => { 'one' => 1, 'two' => 2 }, - 'block' => { 'first' => 'First entry', 'second' => - { 'key' => 'Subordinate mapping' }, 'third' => - [ 'Subordinate sequence', {}, 'Previous mapping is empty.', - { 'A key' => 'value pair in a sequence.', 'A second' => 'key:value pair.' }, - 'The previous entry is equal to the following one.', - { 'A key' => 'value pair in a sequence.', 'A second' => 'key:value pair.' } ], - 12.0 => 'This key is a float.', "?\n" => 'This key had to be protected.', - "\a" => 'This key had to be escaped.', - "This is a multi-line folded key\n" => "Whose value is also multi-line.", - 'this also works as a key' => 'with a value at the next line.', - [ 'This key', 'is a sequence' ] => [ 'With a sequence value.' ] } } - # Couldn't recreate map exactly, so we'll do a detailed check to be sure it's entact - obj_y['block'].keys.each { |k| - if Hash === k - v = obj_y['block'][k] - if k['This'] == 'key' and k['is a'] == 'mapping' and v['with a'] == 'mapping value.' - obj_r['block'][k] = v - end - end - } ---- -test: Literal explicit indentation -yaml: | - # Explicit indentation must - # be given in all the three - # following cases. - leading spaces: |2 - This value starts with four spaces. - - leading line break: |2 - - This value starts with a line break. - - leading comment indicator: |2 - # first line starts with a - # character. - - # Explicit indentation may - # also be given when it is - # not required. - redundant: |2 - This value is indented 2 spaces. -php: | - array( - 'leading spaces' => " This value starts with four spaces.\n", - 'leading line break' => "\nThis value starts with a line break.\n", - 'leading comment indicator' => "# first line starts with a\n# character.\n", - 'redundant' => "This value is indented 2 spaces.\n" - ) ---- -test: Chomping and keep modifiers -yaml: | - clipped: | - This has one newline. - - same as "clipped" above: "This has one newline.\n" - - stripped: |- - This has no newline. - - same as "stripped" above: "This has no newline." - - kept: |+ - This has two newlines. - - same as "kept" above: "This has two newlines.\n\n" -php: | - array( - 'clipped' => "This has one newline.\n", - 'same as "clipped" above' => "This has one newline.\n", - 'stripped' => 'This has no newline.', - 'same as "stripped" above' => 'This has no newline.', - 'kept' => "This has two newlines.\n\n", - 'same as "kept" above' => "This has two newlines.\n\n" - ) ---- -test: Literal combinations -todo: true -yaml: | - empty: | - - literal: | - The \ ' " characters may be - freely used. Leading white - space is significant. - - Line breaks are significant. - Thus this value contains one - empty line and ends with a - single line break, but does - not start with one. - - is equal to: "The \\ ' \" characters may \ - be\nfreely used. Leading white\n space \ - is significant.\n\nLine breaks are \ - significant.\nThus this value contains \ - one\nempty line and ends with a\nsingle \ - line break, but does\nnot start with one.\n" - - # Comments may follow a block - # scalar value. They must be - # less indented. - - # Modifiers may be combined in any order. - indented and chomped: |2- - This has no newline. - - also written as: |-2 - This has no newline. - - both are equal to: " This has no newline." -php: | - array( - 'empty' => '', - 'literal' => "The \\ ' \" characters may be\nfreely used. Leading white\n space " + - "is significant.\n\nLine breaks are significant.\nThus this value contains one\n" + - "empty line and ends with a\nsingle line break, but does\nnot start with one.\n", - 'is equal to' => "The \\ ' \" characters may be\nfreely used. Leading white\n space " + - "is significant.\n\nLine breaks are significant.\nThus this value contains one\n" + - "empty line and ends with a\nsingle line break, but does\nnot start with one.\n", - 'indented and chomped' => ' This has no newline.', - 'also written as' => ' This has no newline.', - 'both are equal to' => ' This has no newline.' - ) ---- -test: Folded combinations -todo: true -yaml: | - empty: > - - one paragraph: > - Line feeds are converted - to spaces, so this value - contains no line breaks - except for the final one. - - multiple paragraphs: >2 - - An empty line, either - at the start or in - the value: - - Is interpreted as a - line break. Thus this - value contains three - line breaks. - - indented text: > - This is a folded - paragraph followed - by a list: - * first entry - * second entry - Followed by another - folded paragraph, - another list: - - * first entry - - * second entry - - And a final folded - paragraph. - - above is equal to: | - This is a folded paragraph followed by a list: - * first entry - * second entry - Followed by another folded paragraph, another list: - - * first entry - - * second entry - - And a final folded paragraph. - - # Explicit comments may follow - # but must be less indented. -php: | - array( - 'empty' => '', - 'one paragraph' => 'Line feeds are converted to spaces, so this value'. - " contains no line breaks except for the final one.\n", - 'multiple paragraphs' => "\nAn empty line, either at the start or in the value:\n". - "Is interpreted as a line break. Thus this value contains three line breaks.\n", - 'indented text' => "This is a folded paragraph followed by a list:\n". - " * first entry\n * second entry\nFollowed by another folded paragraph, ". - "another list:\n\n * first entry\n\n * second entry\n\nAnd a final folded paragraph.\n", - 'above is equal to' => "This is a folded paragraph followed by a list:\n". - " * first entry\n * second entry\nFollowed by another folded paragraph, ". - "another list:\n\n * first entry\n\n * second entry\n\nAnd a final folded paragraph.\n" - ) ---- -test: Single quotes -todo: true -yaml: | - empty: '' - second: '! : \ etc. can be used freely.' - third: 'a single quote '' must be escaped.' - span: 'this contains - six spaces - - and one - line break' - is same as: "this contains six spaces\nand one line break" -php: | - array( - 'empty' => '', - 'second' => '! : \\ etc. can be used freely.', - 'third' => "a single quote ' must be escaped.", - 'span' => "this contains six spaces\nand one line break", - 'is same as' => "this contains six spaces\nand one line break" - ) ---- -test: Double quotes -todo: true -yaml: | - empty: "" - second: "! : etc. can be used freely." - third: "a \" or a \\ must be escaped." - fourth: "this value ends with an LF.\n" - span: "this contains - four \ - spaces" - is equal to: "this contains four spaces" -php: | - array( - 'empty' => '', - 'second' => '! : etc. can be used freely.', - 'third' => 'a " or a \\ must be escaped.', - 'fourth' => "this value ends with an LF.\n", - 'span' => "this contains four spaces", - 'is equal to' => "this contains four spaces" - ) ---- -test: Unquoted strings -todo: true -yaml: | - first: There is no unquoted empty string. - - second: 12 ## This is an integer. - - third: !str 12 ## This is a string. - - span: this contains - six spaces - - and one - line break - - indicators: this has no comments. - #:foo and bar# are - both text. - - flow: [ can span - lines, # comment - like - this ] - - note: { one-line keys: but multi-line values } - -php: | - array( - 'first' => 'There is no unquoted empty string.', - 'second' => 12, - 'third' => '12', - 'span' => "this contains six spaces\nand one line break", - 'indicators' => "this has no comments. #:foo and bar# are both text.", - 'flow' => [ 'can span lines', 'like this' ], - 'note' => { 'one-line keys' => 'but multi-line values' } - ) ---- -test: Spanning sequences -todo: true -yaml: | - # The following are equal seqs - # with different identities. - flow: [ one, two ] - spanning: [ one, - two ] - block: - - one - - two -php: | - array( - 'flow' => [ 'one', 'two' ], - 'spanning' => [ 'one', 'two' ], - 'block' => [ 'one', 'two' ] - ) ---- -test: Flow mappings -yaml: | - # The following are equal maps - # with different identities. - flow: { one: 1, two: 2 } - block: - one: 1 - two: 2 -php: | - array( - 'flow' => array( 'one' => 1, 'two' => 2 ), - 'block' => array( 'one' => 1, 'two' => 2 ) - ) ---- -test: Representations of 12 -todo: true -yaml: | - - 12 # An integer - # The following scalars - # are loaded to the - # string value '1' '2'. - - !str 12 - - '12' - - "12" - - "\ - 1\ - 2\ - " - # Strings containing paths and regexps can be unquoted: - - /foo/bar - - d:/foo/bar - - foo/bar - - /a.*b/ -php: | - array( 12, '12', '12', '12', '12', '/foo/bar', 'd:/foo/bar', 'foo/bar', '/a.*b/' ) ---- -test: "Null" -todo: true -yaml: | - canonical: ~ - - english: null - - # This sequence has five - # entries, two with values. - sparse: - - ~ - - 2nd entry - - Null - - 4th entry - - - - four: This mapping has five keys, - only two with values. - -php: | - array ( - 'canonical' => null, - 'english' => null, - 'sparse' => array( null, '2nd entry', null, '4th entry', null ]), - 'four' => 'This mapping has five keys, only two with values.' - ) ---- -test: Omap -todo: true -yaml: | - # Explicitly typed dictionary. - Bestiary: !omap - - aardvark: African pig-like ant eater. Ugly. - - anteater: South-American ant eater. Two species. - - anaconda: South-American constrictor snake. Scary. - # Etc. -ruby: | - { - 'Bestiary' => YAML::Omap[ - 'aardvark', 'African pig-like ant eater. Ugly.', - 'anteater', 'South-American ant eater. Two species.', - 'anaconda', 'South-American constrictor snake. Scary.' - ] - } - ---- -test: Pairs -todo: true -yaml: | - # Explicitly typed pairs. - tasks: !pairs - - meeting: with team. - - meeting: with boss. - - break: lunch. - - meeting: with client. -ruby: | - { - 'tasks' => YAML::Pairs[ - 'meeting', 'with team.', - 'meeting', 'with boss.', - 'break', 'lunch.', - 'meeting', 'with client.' - ] - } - ---- -test: Set -todo: true -yaml: | - # Explicitly typed set. - baseball players: !set - Mark McGwire: - Sammy Sosa: - Ken Griffey: -ruby: | - { - 'baseball players' => YAML::Set[ - 'Mark McGwire', nil, - 'Sammy Sosa', nil, - 'Ken Griffey', nil - ] - } - ---- -test: Boolean -yaml: | - false: used as key - logical: true - answer: false -php: | - array( - false => 'used as key', - 'logical' => true, - 'answer' => false - ) ---- -test: Integer -yaml: | - canonical: 12345 - decimal: +12,345 - octal: 014 - hexadecimal: 0xC -php: | - array( - 'canonical' => 12345, - 'decimal' => 12345.0, - 'octal' => 12, - 'hexadecimal' => 12 - ) ---- -test: Float -yaml: | - canonical: 1.23015e+3 - exponential: 12.3015e+02 - fixed: 1,230.15 - negative infinity: -.inf - not a number: .NaN -php: | - array( - 'canonical' => 1230.15, - 'exponential' => 1230.15, - 'fixed' => 1230.15, - 'negative infinity' => log(0), - 'not a number' => -log(0) - ) ---- -test: Timestamp -todo: true -yaml: | - canonical: 2001-12-15T02:59:43.1Z - valid iso8601: 2001-12-14t21:59:43.10-05:00 - space separated: 2001-12-14 21:59:43.10 -05:00 - date (noon UTC): 2002-12-14 -ruby: | - array( - 'canonical' => YAML::mktime( 2001, 12, 15, 2, 59, 43, 0.10 ), - 'valid iso8601' => YAML::mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ), - 'space separated' => YAML::mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ), - 'date (noon UTC)' => Date.new( 2002, 12, 14 ) - ) ---- -test: Binary -todo: true -yaml: | - canonical: !binary "\ - R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5\ - OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+\ - +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC\ - AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs=" - base64: !binary | - R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5 - OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+ - +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC - AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs= - description: > - The binary value above is a tiny arrow - encoded as a gif image. -ruby-setup: | - arrow_gif = "GIF89a\f\000\f\000\204\000\000\377\377\367\365\365\356\351\351\345fff\000\000\000\347\347\347^^^\363\363\355\216\216\216\340\340\340\237\237\237\223\223\223\247\247\247\236\236\236iiiccc\243\243\243\204\204\204\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371!\376\016Made with GIMP\000,\000\000\000\000\f\000\f\000\000\005, \216\2010\236\343@\024\350i\020\304\321\212\010\034\317\200M$z\357\3770\205p\270\2601f\r\e\316\001\303\001\036\020' \202\n\001\000;" -ruby: | - { - 'canonical' => arrow_gif, - 'base64' => arrow_gif, - 'description' => "The binary value above is a tiny arrow encoded as a gif image.\n" - } - ---- -test: Merge key -todo: true -yaml: | - --- - - &CENTER { x: 1, y: 2 } - - &LEFT { x: 0, y: 2 } - - &BIG { r: 10 } - - &SMALL { r: 1 } - - # All the following maps are equal: - - - # Explicit keys - x: 1 - y: 2 - r: 10 - label: center/big - - - # Merge one map - << : *CENTER - r: 10 - label: center/big - - - # Merge multiple maps - << : [ *CENTER, *BIG ] - label: center/big - - - # Override - << : [ *BIG, *LEFT, *SMALL ] - x: 1 - label: center/big - -ruby-setup: | - center = { 'x' => 1, 'y' => 2 } - left = { 'x' => 0, 'y' => 2 } - big = { 'r' => 10 } - small = { 'r' => 1 } - node1 = { 'x' => 1, 'y' => 2, 'r' => 10, 'label' => 'center/big' } - node2 = center.dup - node2.update( { 'r' => 10, 'label' => 'center/big' } ) - node3 = big.dup - node3.update( center ) - node3.update( { 'label' => 'center/big' } ) - node4 = small.dup - node4.update( left ) - node4.update( big ) - node4.update( { 'x' => 1, 'label' => 'center/big' } ) - -ruby: | - [ - center, left, big, small, node1, node2, node3, node4 - ] - ---- -test: Default key -todo: true -yaml: | - --- # Old schema - link with: - - library1.dll - - library2.dll - --- # New schema - link with: - - = : library1.dll - version: 1.2 - - = : library2.dll - version: 2.3 -ruby: | - y = YAML::Stream.new - y.add( { 'link with' => [ 'library1.dll', 'library2.dll' ] } ) - obj_h = Hash[ 'version' => 1.2 ] - obj_h.default = 'library1.dll' - obj_h2 = Hash[ 'version' => 2.3 ] - obj_h2.default = 'library2.dll' - y.add( { 'link with' => [ obj_h, obj_h2 ] } ) -documents: 2 - ---- -test: Special keys -todo: true -yaml: | - "!": These three keys - "&": had to be quoted - "=": and are normal strings. - # NOTE: the following node should NOT be serialized this way. - encoded node : - !special '!' : '!type' - !special|canonical '&' : 12 - = : value - # The proper way to serialize the above node is as follows: - node : !!type &12 value -ruby: | - { '!' => 'These three keys', '&' => 'had to be quoted', - '=' => 'and are normal strings.', - 'encoded node' => YAML::PrivateType.new( 'type', 'value' ), - 'node' => YAML::PrivateType.new( 'type', 'value' ) } diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsTypeTransfers.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsTypeTransfers.yml deleted file mode 100644 index 46c8d4a2c0..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/YtsTypeTransfers.yml +++ /dev/null @@ -1,244 +0,0 @@ ---- %YAML:1.0 -test: Strings -brief: > - Any group of characters beginning with an - alphabetic or numeric character is a string, - unless it belongs to one of the groups below - (such as an Integer or Time). -yaml: | - String -php: | - 'String' ---- -test: String characters -brief: > - A string can contain any alphabetic or - numeric character, along with many - punctuation characters, including the - period, dash, space, quotes, exclamation, and - question mark. -yaml: | - - What's Yaml? - - It's for writing data structures in plain text. - - And? - - And what? That's not good enough for you? - - No, I mean, "And what about Yaml?" - - Oh, oh yeah. Uh.. Yaml for Ruby. -php: | - array( - "What's Yaml?", - "It's for writing data structures in plain text.", - "And?", - "And what? That's not good enough for you?", - "No, I mean, \"And what about Yaml?\"", - "Oh, oh yeah. Uh.. Yaml for Ruby." - ) ---- -test: Indicators in Strings -brief: > - Be careful using indicators in strings. In particular, - the comma, colon, and pound sign must be used carefully. -yaml: | - the colon followed by space is an indicator: but is a string:right here - same for the pound sign: here we have it#in a string - the comma can, honestly, be used in most cases: [ but not in, inline collections ] -php: | - array( - 'the colon followed by space is an indicator' => 'but is a string:right here', - 'same for the pound sign' => 'here we have it#in a string', - 'the comma can, honestly, be used in most cases' => array('but not in', 'inline collections') - ) ---- -test: Forcing Strings -brief: > - Any YAML type can be forced into a string using the - explicit !str method. -yaml: | - date string: !str 2001-08-01 - number string: !str 192 -php: | - array( - 'date string' => '2001-08-01', - 'number string' => '192' - ) ---- -test: Single-quoted Strings -brief: > - You can also enclose your strings within single quotes, - which allows use of slashes, colons, and other indicators - freely. Inside single quotes, you can represent a single - quote in your string by using two single quotes next to - each other. -yaml: | - all my favorite symbols: '#:!/%.)' - a few i hate: '&(*' - why do i hate them?: 'it''s very hard to explain' - entities: '£ me' -php: | - array( - 'all my favorite symbols' => '#:!/%.)', - 'a few i hate' => '&(*', - 'why do i hate them?' => 'it\'s very hard to explain', - 'entities' => '£ me' - ) ---- -test: Double-quoted Strings -brief: > - Enclosing strings in double quotes allows you - to use escapings to represent ASCII and - Unicode characters. -yaml: | - i know where i want my line breaks: "one here\nand another here\n" -php: | - array( - 'i know where i want my line breaks' => "one here\nand another here\n" - ) ---- -test: Multi-line Quoted Strings -todo: true -brief: > - Both single- and double-quoted strings may be - carried on to new lines in your YAML document. - They must be indented a step and indentation - is interpreted as a single space. -yaml: | - i want a long string: "so i'm going to - let it go on and on to other lines - until i end it with a quote." -php: | - array('i want a long string' => "so i'm going to ". - "let it go on and on to other lines ". - "until i end it with a quote." - ) - ---- -test: Plain scalars -todo: true -brief: > - Unquoted strings may also span multiple lines, if they - are free of YAML space indicators and indented. -yaml: | - - My little toe is broken in two places; - - I'm crazy to have skied this way; - - I'm not the craziest he's seen, since there was always the German guy - who skied for 3 hours on a broken shin bone (just below the kneecap); - - Nevertheless, second place is respectable, and he doesn't - recommend going for the record; - - He's going to put my foot in plaster for a month; - - This would impair my skiing ability somewhat for the - duration, as can be imagined. -php: | - array( - "My little toe is broken in two places;", - "I'm crazy to have skied this way;", - "I'm not the craziest he's seen, since there was always ". - "the German guy who skied for 3 hours on a broken shin ". - "bone (just below the kneecap);", - "Nevertheless, second place is respectable, and he doesn't ". - "recommend going for the record;", - "He's going to put my foot in plaster for a month;", - "This would impair my skiing ability somewhat for the duration, ". - "as can be imagined." - ) ---- -test: 'Null' -brief: > - You can use the tilde '~' character for a null value. -yaml: | - name: Mr. Show - hosted by: Bob and David - date of next season: ~ -php: | - array( - 'name' => 'Mr. Show', - 'hosted by' => 'Bob and David', - 'date of next season' => null - ) ---- -test: Boolean -brief: > - You can use 'true' and 'false' for Boolean values. -yaml: | - Is Gus a Liar?: true - Do I rely on Gus for Sustenance?: false -php: | - array( - 'Is Gus a Liar?' => true, - 'Do I rely on Gus for Sustenance?' => false - ) ---- -test: Integers -dump_skip: true -brief: > - An integer is a series of numbers, optionally - starting with a positive or negative sign. Integers - may also contain commas for readability. -yaml: | - zero: 0 - simple: 12 - one-thousand: 1,000 - negative one-thousand: -1,000 -php: | - array( - 'zero' => 0, - 'simple' => 12, - 'one-thousand' => 1000.0, - 'negative one-thousand' => -1000.0 - ) ---- -test: Integers as Map Keys -brief: > - An integer can be used a dictionary key. -yaml: | - 1: one - 2: two - 3: three -php: | - array( - 1 => 'one', - 2 => 'two', - 3 => 'three' - ) ---- -test: Floats -dump_skip: true -brief: > - Floats are represented by numbers with decimals, - allowing for scientific notation, as well as - positive and negative infinity and "not a number." -yaml: | - a simple float: 2.00 - larger float: 1,000.09 - scientific notation: 1.00009e+3 -php: | - array( - 'a simple float' => 2.0, - 'larger float' => 1000.09, - 'scientific notation' => 1000.09 - ) ---- -test: Time -todo: true -brief: > - You can represent timestamps by using - ISO8601 format, or a variation which - allows spaces between the date, time and - time zone. -yaml: | - iso8601: 2001-12-14t21:59:43.10-05:00 - space separated: 2001-12-14 21:59:43.10 -05:00 -php: | - array( - 'iso8601' => mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ), - 'space separated' => mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ) - ) ---- -test: Date -todo: true -brief: > - A date can be represented by its year, - month and day in ISO8601 order. -yaml: | - 1976-07-31 -php: | - date( 1976, 7, 31 ) diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/embededPhp.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/embededPhp.yml deleted file mode 100644 index ec456ed09f..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/embededPhp.yml +++ /dev/null @@ -1 +0,0 @@ -value: diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/escapedCharacters.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/escapedCharacters.yml deleted file mode 100644 index 6ca044c8da..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/escapedCharacters.yml +++ /dev/null @@ -1,155 +0,0 @@ -test: outside double quotes -yaml: | - \0 \ \a \b \n -php: | - "\\0 \\ \\a \\b \\n" ---- -test: null -yaml: | - "\0" -php: | - "\x00" ---- -test: bell -yaml: | - "\a" -php: | - "\x07" ---- -test: backspace -yaml: | - "\b" -php: | - "\x08" ---- -test: horizontal tab (1) -yaml: | - "\t" -php: | - "\x09" ---- -test: horizontal tab (2) -yaml: | - "\ " -php: | - "\x09" ---- -test: line feed -yaml: | - "\n" -php: | - "\x0a" ---- -test: vertical tab -yaml: | - "\v" -php: | - "\x0b" ---- -test: form feed -yaml: | - "\f" -php: | - "\x0c" ---- -test: carriage return -yaml: | - "\r" -php: | - "\x0d" ---- -test: escape -yaml: | - "\e" -php: | - "\x1b" ---- -test: space -yaml: | - "\ " -php: | - "\x20" ---- -test: slash -yaml: | - "\/" -php: | - "\x2f" ---- -test: backslash -yaml: | - "\\" -php: | - "\\" ---- -test: Unicode next line -yaml: | - "\N" -php: | - "\xc2\x85" ---- -test: Unicode non-breaking space -yaml: | - "\_" -php: | - "\xc2\xa0" ---- -test: Unicode line separator -yaml: | - "\L" -php: | - "\xe2\x80\xa8" ---- -test: Unicode paragraph separator -yaml: | - "\P" -php: | - "\xe2\x80\xa9" ---- -test: Escaped 8-bit Unicode -yaml: | - "\x42" -php: | - "B" ---- -test: Escaped 16-bit Unicode -yaml: | - "\u20ac" -php: | - "\xe2\x82\xac" ---- -test: Escaped 32-bit Unicode -yaml: | - "\U00000043" -php: | - "C" ---- -test: Example 5.13 Escaped Characters -note: | - Currently throws an error parsing first line. Maybe Symfony Yaml doesn't support - continuation of string across multiple lines? Keeping test here but disabled. -todo: true -yaml: | - "Fun with \\ - \" \a \b \e \f \ - \n \r \t \v \0 \ - \ \_ \N \L \P \ - \x41 \u0041 \U00000041" -php: | - "Fun with \x5C\n\x22 \x07 \x08 \x1B \x0C\n\x0A \x0D \x09 \x0B \x00\n\x20 \xA0 \x85 \xe2\x80\xa8 \xe2\x80\xa9\nA A A" ---- -test: Double quotes with a line feed -yaml: | - { double: "some value\n \"some quoted string\" and 'some single quotes one'" } -php: | - array( - 'double' => "some value\n \"some quoted string\" and 'some single quotes one'" - ) ---- -test: Backslashes -yaml: | - { single: 'foo\Var', no-quotes: foo\Var, double: "foo\\Var" } -php: | - array( - 'single' => 'foo\Var', 'no-quotes' => 'foo\Var', 'double' => 'foo\Var' - ) diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/index.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/index.yml deleted file mode 100644 index 3216a89ebb..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/index.yml +++ /dev/null @@ -1,18 +0,0 @@ -- escapedCharacters -- sfComments -- sfCompact -- sfTests -- sfObjects -- sfMergeKey -- sfQuotes -- YtsAnchorAlias -- YtsBasicTests -- YtsBlockMapping -- YtsDocumentSeparator -- YtsErrorTests -- YtsFlowCollections -- YtsFoldedScalars -- YtsNullsAndEmpties -- YtsSpecificationExamples -- YtsTypeTransfers -- unindentedCollections diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/sfComments.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/sfComments.yml deleted file mode 100644 index b72a9b6996..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/sfComments.yml +++ /dev/null @@ -1,76 +0,0 @@ ---- %YAML:1.0 -test: Comments at the end of a line -brief: > - Comments at the end of a line -yaml: | - ex1: "foo # bar" - ex2: "foo # bar" # comment - ex3: 'foo # bar' # comment - ex4: foo # comment - ex5: foo # comment with tab before - ex6: foo#foo # comment here - ex7: foo # ignore me # and me -php: | - array('ex1' => 'foo # bar', 'ex2' => 'foo # bar', 'ex3' => 'foo # bar', 'ex4' => 'foo', 'ex5' => 'foo', 'ex6' => 'foo#foo', 'ex7' => 'foo') ---- -test: Comments in the middle -brief: > - Comments in the middle -yaml: | - foo: - # some comment - # some comment - bar: foo - # some comment - # some comment -php: | - array('foo' => array('bar' => 'foo')) ---- -test: Comments on a hash line -brief: > - Comments on a hash line -yaml: | - foo: # a comment - foo: bar # a comment -php: | - array('foo' => array('foo' => 'bar')) ---- -test: 'Value starting with a #' -brief: > - 'Value starting with a #' -yaml: | - foo: '#bar' -php: | - array('foo' => '#bar') ---- -test: Document starting with a comment and a separator -brief: > - Commenting before document start is allowed -yaml: | - # document comment - --- - foo: bar # a comment -php: | - array('foo' => 'bar') ---- -test: Comment containing a colon on a hash line -brief: > - Comment containing a colon on a scalar line -yaml: 'foo # comment: this is also part of the comment' -php: | - 'foo' ---- -test: 'Hash key containing a #' -brief: > - 'Hash key containing a #' -yaml: 'foo#bar: baz' -php: | - array('foo#bar' => 'baz') ---- -test: 'Hash key ending with a space and a #' -brief: > - 'Hash key ending with a space and a #' -yaml: | - 'foo #': baz -php: | - array('foo #' => 'baz') diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/sfCompact.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/sfCompact.yml deleted file mode 100644 index 1339d23a63..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/sfCompact.yml +++ /dev/null @@ -1,159 +0,0 @@ ---- %YAML:1.0 -test: Compact notation -brief: | - Compact notation for sets of mappings with single element -yaml: | - --- - # products purchased - - item : Super Hoop - - item : Basketball - quantity: 1 - - item: - name: Big Shoes - nick: Biggies - quantity: 1 -php: | - array ( - array ( - 'item' => 'Super Hoop', - ), - array ( - 'item' => 'Basketball', - 'quantity' => 1, - ), - array ( - 'item' => array( - 'name' => 'Big Shoes', - 'nick' => 'Biggies' - ), - 'quantity' => 1 - ) - ) ---- -test: Compact notation combined with inline notation -brief: | - Combinations of compact and inline notation are allowed -yaml: | - --- - items: - - { item: Super Hoop, quantity: 1 } - - [ Basketball, Big Shoes ] -php: | - array ( - 'items' => array ( - array ( - 'item' => 'Super Hoop', - 'quantity' => 1, - ), - array ( - 'Basketball', - 'Big Shoes' - ) - ) - ) ---- %YAML:1.0 -test: Compact notation -brief: | - Compact notation for sets of mappings with single element -yaml: | - --- - # products purchased - - item : Super Hoop - - item : Basketball - quantity: 1 - - item: - name: Big Shoes - nick: Biggies - quantity: 1 -php: | - array ( - array ( - 'item' => 'Super Hoop', - ), - array ( - 'item' => 'Basketball', - 'quantity' => 1, - ), - array ( - 'item' => array( - 'name' => 'Big Shoes', - 'nick' => 'Biggies' - ), - 'quantity' => 1 - ) - ) ---- -test: Compact notation combined with inline notation -brief: | - Combinations of compact and inline notation are allowed -yaml: | - --- - items: - - { item: Super Hoop, quantity: 1 } - - [ Basketball, Big Shoes ] -php: | - array ( - 'items' => array ( - array ( - 'item' => 'Super Hoop', - 'quantity' => 1, - ), - array ( - 'Basketball', - 'Big Shoes' - ) - ) - ) ---- %YAML:1.0 -test: Compact notation -brief: | - Compact notation for sets of mappings with single element -yaml: | - --- - # products purchased - - item : Super Hoop - - item : Basketball - quantity: 1 - - item: - name: Big Shoes - nick: Biggies - quantity: 1 -php: | - array ( - array ( - 'item' => 'Super Hoop', - ), - array ( - 'item' => 'Basketball', - 'quantity' => 1, - ), - array ( - 'item' => array( - 'name' => 'Big Shoes', - 'nick' => 'Biggies' - ), - 'quantity' => 1 - ) - ) ---- -test: Compact notation combined with inline notation -brief: | - Combinations of compact and inline notation are allowed -yaml: | - --- - items: - - { item: Super Hoop, quantity: 1 } - - [ Basketball, Big Shoes ] -php: | - array ( - 'items' => array ( - array ( - 'item' => 'Super Hoop', - 'quantity' => 1, - ), - array ( - 'Basketball', - 'Big Shoes' - ) - ) - ) diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/sfMergeKey.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/sfMergeKey.yml deleted file mode 100644 index 4b67d34100..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/sfMergeKey.yml +++ /dev/null @@ -1,58 +0,0 @@ ---- %YAML:1.0 -test: Simple In Place Substitution -brief: > - If you want to reuse an entire alias, only overwriting what is different - you can use a << in place substitution. This is not part of the official - YAML spec, but a widely implemented extension. See the following URL for - details: http://yaml.org/type/merge.html -yaml: | - foo: &foo - a: Steve - b: Clark - c: Brian - bar: - a: before - d: other - <<: *foo - b: new - x: Oren - c: - foo: bar - foo: ignore - bar: foo - duplicate: - foo: bar - foo: ignore - foo2: &foo2 - a: Ballmer - ding: &dong [ fi, fei, fo, fam] - check: - <<: - - *foo - - *dong - isit: tested - head: - <<: [ *foo , *dong , *foo2 ] - taz: &taz - a: Steve - w: - p: 1234 - nested: - <<: *taz - d: Doug - w: &nestedref - p: 12345 - z: - <<: *nestedref -php: | - array( - 'foo' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian'), - 'bar' => array('a' => 'before', 'd' => 'other', 'b' => 'new', 'c' => array('foo' => 'bar', 'bar' => 'foo'), 'x' => 'Oren'), - 'duplicate' => array('foo' => 'bar'), - 'foo2' => array('a' => 'Ballmer'), - 'ding' => array('fi', 'fei', 'fo', 'fam'), - 'check' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam', 'isit' => 'tested'), - 'head' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam'), - 'taz' => array('a' => 'Steve', 'w' => array('p' => 1234)), - 'nested' => array('a' => 'Steve', 'w' => array('p' => 12345), 'd' => 'Doug', 'z' => array('p' => 12345)) - ) diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/sfObjects.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/sfObjects.yml deleted file mode 100644 index ee124b2446..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/sfObjects.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- %YAML:1.0 -test: Objects -brief: > - Comments at the end of a line -yaml: | - ex1: "foo # bar" - ex2: "foo # bar" # comment - ex3: 'foo # bar' # comment - ex4: foo # comment -php: | - array('ex1' => 'foo # bar', 'ex2' => 'foo # bar', 'ex3' => 'foo # bar', 'ex4' => 'foo') diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/sfQuotes.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/sfQuotes.yml deleted file mode 100644 index 7c60baec97..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/sfQuotes.yml +++ /dev/null @@ -1,33 +0,0 @@ ---- %YAML:1.0 -test: Some characters at the beginning of a string must be escaped -brief: > - Some characters at the beginning of a string must be escaped -yaml: | - foo: '| bar' -php: | - array('foo' => '| bar') ---- -test: A key can be a quoted string -brief: > - A key can be a quoted string -yaml: | - "foo1": bar - 'foo2': bar - "foo \" bar": bar - 'foo '' bar': bar - 'foo3: ': bar - "foo4: ": bar - foo5: { "foo \" bar: ": bar, 'foo '' bar: ': bar } -php: | - array( - 'foo1' => 'bar', - 'foo2' => 'bar', - 'foo " bar' => 'bar', - 'foo \' bar' => 'bar', - 'foo3: ' => 'bar', - 'foo4: ' => 'bar', - 'foo5' => array( - 'foo " bar: ' => 'bar', - 'foo \' bar: ' => 'bar', - ), - ) diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/sfTests.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/sfTests.yml deleted file mode 100644 index a427be1c84..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/sfTests.yml +++ /dev/null @@ -1,149 +0,0 @@ ---- %YAML:1.0 -test: Multiple quoted string on one line -brief: > - Multiple quoted string on one line -yaml: | - stripped_title: { name: "foo bar", help: "bar foo" } -php: | - array('stripped_title' => array('name' => 'foo bar', 'help' => 'bar foo')) ---- -test: Empty sequence -yaml: | - foo: [ ] -php: | - array('foo' => array()) ---- -test: Empty value -yaml: | - foo: -php: | - array('foo' => null) ---- -test: Inline string parsing -brief: > - Inline string parsing -yaml: | - test: ['complex: string', 'another [string]'] -php: | - array('test' => array('complex: string', 'another [string]')) ---- -test: Boolean -brief: > - Boolean -yaml: | - - false - - true - - null - - ~ - - 'false' - - 'true' - - 'null' - - '~' -php: | - array( - false, - true, - null, - null, - 'false', - 'true', - 'null', - '~', - ) ---- -test: Empty lines in literal blocks -brief: > - Empty lines in literal blocks -yaml: | - foo: - bar: | - foo - - - - bar -php: | - array('foo' => array('bar' => "foo\n\n\n \nbar\n")) ---- -test: Empty lines in folded blocks -brief: > - Empty lines in folded blocks -yaml: | - foo: - bar: > - - foo - - - bar -php: | - array('foo' => array('bar' => "\nfoo\n\nbar\n")) ---- -test: IP addresses -brief: > - IP addresses -yaml: | - foo: 10.0.0.2 -php: | - array('foo' => '10.0.0.2') ---- -test: A sequence with an embedded mapping -brief: > - A sequence with an embedded mapping -yaml: | - - foo - - bar: { bar: foo } -php: | - array('foo', array('bar' => array('bar' => 'foo'))) ---- -test: A sequence with an unordered array -brief: > - A sequence with an unordered array -yaml: | - 1: foo - 0: bar -php: | - array(1 => 'foo', 0 => 'bar') ---- -test: Octal -brief: as in spec example 2.19, octal value is converted -yaml: | - foo: 0123 -php: | - array('foo' => 83) ---- -test: Octal strings -brief: Octal notation in a string must remain a string -yaml: | - foo: "0123" -php: | - array('foo' => '0123') ---- -test: Octal strings -brief: Octal notation in a string must remain a string -yaml: | - foo: '0123' -php: | - array('foo' => '0123') ---- -test: Octal strings -brief: Octal notation in a string must remain a string -yaml: | - foo: | - 0123 -php: | - array('foo' => "0123\n") ---- -test: Document as a simple hash -brief: Document as a simple hash -yaml: | - { foo: bar } -php: | - array('foo' => 'bar') ---- -test: Document as a simple array -brief: Document as a simple array -yaml: | - [ foo, bar ] -php: | - array('foo', 'bar') diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/unindentedCollections.yml b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/unindentedCollections.yml deleted file mode 100644 index 0c96108e99..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/Fixtures/unindentedCollections.yml +++ /dev/null @@ -1,82 +0,0 @@ ---- %YAML:1.0 -test: Unindented collection -brief: > - Unindented collection -yaml: | - collection: - - item1 - - item2 - - item3 -php: | - array('collection' => array('item1', 'item2', 'item3')) ---- -test: Nested unindented collection (two levels) -brief: > - Nested unindented collection -yaml: | - collection: - key: - - a - - b - - c -php: | - array('collection' => array('key' => array('a', 'b', 'c'))) ---- -test: Nested unindented collection (three levels) -brief: > - Nested unindented collection -yaml: | - collection: - key: - subkey: - - one - - two - - three -php: | - array('collection' => array('key' => array('subkey' => array('one', 'two', 'three')))) ---- -test: Key/value after unindented collection (1) -brief: > - Key/value after unindented collection (1) -yaml: | - collection: - key: - - a - - b - - c - foo: bar -php: | - array('collection' => array('key' => array('a', 'b', 'c')), 'foo' => 'bar') ---- -test: Key/value after unindented collection (at the same level) -brief: > - Key/value after unindented collection -yaml: | - collection: - key: - - a - - b - - c - foo: bar -php: | - array('collection' => array('key' => array('a', 'b', 'c'), 'foo' => 'bar')) ---- -test: Shortcut Key after unindented collection -brief: > - Key/value after unindented collection -yaml: | - collection: - - key: foo - foo: bar -php: | - array('collection' => array(array('key' => 'foo', 'foo' => 'bar'))) ---- -test: Shortcut Key after unindented collection with custom spaces -brief: > - Key/value after unindented collection -yaml: | - collection: - - key: foo - foo: bar -php: | - array('collection' => array(array('key' => 'foo', 'foo' => 'bar'))) diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/InlineTest.php b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/InlineTest.php deleted file mode 100644 index ebcd5e36ac..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/InlineTest.php +++ /dev/null @@ -1,602 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml\Tests; - -use Symfony\Bridge\PhpUnit\ErrorAssert; -use Symfony\Component\Yaml\Inline; -use Symfony\Component\Yaml\Yaml; - -class InlineTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider getTestsForParse - */ - public function testParse($yaml, $value) - { - $this->assertSame($value, Inline::parse($yaml), sprintf('::parse() converts an inline YAML to a PHP structure (%s)', $yaml)); - } - - /** - * @dataProvider getTestsForParseWithMapObjects - */ - public function testParseWithMapObjects($yaml, $value) - { - $actual = Inline::parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP); - - $this->assertSame(serialize($value), serialize($actual)); - } - - /** - * @group legacy - * @dataProvider getTestsForParseWithMapObjects - */ - public function testParseWithMapObjectsPassingTrue($yaml, $value) - { - $actual = Inline::parse($yaml, false, false, true); - - $this->assertSame(serialize($value), serialize($actual)); - } - - /** - * @dataProvider getTestsForDump - */ - public function testDump($yaml, $value) - { - $this->assertEquals($yaml, Inline::dump($value), sprintf('::dump() converts a PHP structure to an inline YAML (%s)', $yaml)); - - $this->assertSame($value, Inline::parse(Inline::dump($value)), 'check consistency'); - } - - public function testDumpNumericValueWithLocale() - { - $locale = setlocale(LC_NUMERIC, 0); - if (false === $locale) { - $this->markTestSkipped('Your platform does not support locales.'); - } - - try { - $requiredLocales = array('fr_FR.UTF-8', 'fr_FR.UTF8', 'fr_FR.utf-8', 'fr_FR.utf8', 'French_France.1252'); - if (false === setlocale(LC_NUMERIC, $requiredLocales)) { - $this->markTestSkipped('Could not set any of required locales: '.implode(', ', $requiredLocales)); - } - - $this->assertEquals('1.2', Inline::dump(1.2)); - $this->assertContains('fr', strtolower(setlocale(LC_NUMERIC, 0))); - setlocale(LC_NUMERIC, $locale); - } catch (\Exception $e) { - setlocale(LC_NUMERIC, $locale); - throw $e; - } - } - - public function testHashStringsResemblingExponentialNumericsShouldNotBeChangedToINF() - { - $value = '686e444'; - - $this->assertSame($value, Inline::parse(Inline::dump($value))); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - * @expectedExceptionMessage Found unknown escape character "\V". - */ - public function testParseScalarWithNonEscapedBlackslashShouldThrowException() - { - Inline::parse('"Foo\Var"'); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testParseScalarWithNonEscapedBlackslashAtTheEndShouldThrowException() - { - Inline::parse('"Foo\\"'); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testParseScalarWithIncorrectlyQuotedStringShouldThrowException() - { - $value = "'don't do somthin' like that'"; - Inline::parse($value); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testParseScalarWithIncorrectlyDoubleQuotedStringShouldThrowException() - { - $value = '"don"t do somthin" like that"'; - Inline::parse($value); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testParseInvalidMappingKeyShouldThrowException() - { - $value = '{ "foo " bar": "bar" }'; - Inline::parse($value); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testParseInvalidMappingShouldThrowException() - { - Inline::parse('[foo] bar'); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testParseInvalidSequenceShouldThrowException() - { - Inline::parse('{ foo: bar } bar'); - } - - public function testParseScalarWithCorrectlyQuotedStringShouldReturnString() - { - $value = "'don''t do somthin'' like that'"; - $expect = "don't do somthin' like that"; - - $this->assertSame($expect, Inline::parseScalar($value)); - } - - /** - * @dataProvider getDataForParseReferences - */ - public function testParseReferences($yaml, $expected) - { - $this->assertSame($expected, Inline::parse($yaml, 0, array('var' => 'var-value'))); - } - - /** - * @group legacy - * @dataProvider getDataForParseReferences - */ - public function testParseReferencesAsFifthArgument($yaml, $expected) - { - $this->assertSame($expected, Inline::parse($yaml, false, false, false, array('var' => 'var-value'))); - } - - public function getDataForParseReferences() - { - return array( - 'scalar' => array('*var', 'var-value'), - 'list' => array('[ *var ]', array('var-value')), - 'list-in-list' => array('[[ *var ]]', array(array('var-value'))), - 'map-in-list' => array('[ { key: *var } ]', array(array('key' => 'var-value'))), - 'embedded-mapping-in-list' => array('[ key: *var ]', array(array('key' => 'var-value'))), - 'map' => array('{ key: *var }', array('key' => 'var-value')), - 'list-in-map' => array('{ key: [*var] }', array('key' => array('var-value'))), - 'map-in-map' => array('{ foo: { bar: *var } }', array('foo' => array('bar' => 'var-value'))), - ); - } - - public function testParseMapReferenceInSequence() - { - $foo = array( - 'a' => 'Steve', - 'b' => 'Clark', - 'c' => 'Brian', - ); - $this->assertSame(array($foo), Inline::parse('[*foo]', 0, array('foo' => $foo))); - } - - /** - * @group legacy - */ - public function testParseMapReferenceInSequenceAsFifthArgument() - { - $foo = array( - 'a' => 'Steve', - 'b' => 'Clark', - 'c' => 'Brian', - ); - $this->assertSame(array($foo), Inline::parse('[*foo]', false, false, false, array('foo' => $foo))); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - * @expectedExceptionMessage A reference must contain at least one character. - */ - public function testParseUnquotedAsterisk() - { - Inline::parse('{ foo: * }'); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - * @expectedExceptionMessage A reference must contain at least one character. - */ - public function testParseUnquotedAsteriskFollowedByAComment() - { - Inline::parse('{ foo: * #foo }'); - } - - /** - * @dataProvider getReservedIndicators - * @expectedException Symfony\Component\Yaml\Exception\ParseException - * @expectedExceptionMessage cannot start a plain scalar; you need to quote the scalar. - */ - public function testParseUnquotedScalarStartingWithReservedIndicator($indicator) - { - Inline::parse(sprintf('{ foo: %sfoo }', $indicator)); - } - - public function getReservedIndicators() - { - return array(array('@'), array('`')); - } - - /** - * @dataProvider getScalarIndicators - * @expectedException Symfony\Component\Yaml\Exception\ParseException - * @expectedExceptionMessage cannot start a plain scalar; you need to quote the scalar. - */ - public function testParseUnquotedScalarStartingWithScalarIndicator($indicator) - { - Inline::parse(sprintf('{ foo: %sfoo }', $indicator)); - } - - public function getScalarIndicators() - { - return array(array('|'), array('>')); - } - - /** - * @group legacy - * throws \Symfony\Component\Yaml\Exception\ParseException in 4.0 - */ - public function testParseUnquotedScalarStartingWithPercentCharacter() - { - ErrorAssert::assertDeprecationsAreTriggered('Not quoting a scalar starting with the "%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.', function () { - Inline::parse('{ foo: %foo }'); - }); - } - - /** - * @dataProvider getDataForIsHash - */ - public function testIsHash($array, $expected) - { - $this->assertSame($expected, Inline::isHash($array)); - } - - public function getDataForIsHash() - { - return array( - array(array(), false), - array(array(1, 2, 3), false), - array(array(2 => 1, 1 => 2, 0 => 3), true), - array(array('foo' => 1, 'bar' => 2), true), - ); - } - - public function getTestsForParse() - { - return array( - array('', ''), - array('null', null), - array('false', false), - array('true', true), - array('12', 12), - array('-12', -12), - array('"quoted string"', 'quoted string'), - array("'quoted string'", 'quoted string'), - array('12.30e+02', 12.30e+02), - array('0x4D2', 0x4D2), - array('02333', 02333), - array('.Inf', -log(0)), - array('-.Inf', log(0)), - array("'686e444'", '686e444'), - array('686e444', 646e444), - array('123456789123456789123456789123456789', '123456789123456789123456789123456789'), - array('"foo\r\nbar"', "foo\r\nbar"), - array("'foo#bar'", 'foo#bar'), - array("'foo # bar'", 'foo # bar'), - array("'#cfcfcf'", '#cfcfcf'), - array('::form_base.html.twig', '::form_base.html.twig'), - - // Pre-YAML-1.2 booleans - array("'y'", 'y'), - array("'n'", 'n'), - array("'yes'", 'yes'), - array("'no'", 'no'), - array("'on'", 'on'), - array("'off'", 'off'), - - array('2007-10-30', gmmktime(0, 0, 0, 10, 30, 2007)), - array('2007-10-30T02:59:43Z', gmmktime(2, 59, 43, 10, 30, 2007)), - array('2007-10-30 02:59:43 Z', gmmktime(2, 59, 43, 10, 30, 2007)), - array('1960-10-30 02:59:43 Z', gmmktime(2, 59, 43, 10, 30, 1960)), - array('1730-10-30T02:59:43Z', gmmktime(2, 59, 43, 10, 30, 1730)), - - array('"a \\"string\\" with \'quoted strings inside\'"', 'a "string" with \'quoted strings inside\''), - array("'a \"string\" with ''quoted strings inside'''", 'a "string" with \'quoted strings inside\''), - - // sequences - // urls are no key value mapping. see #3609. Valid yaml "key: value" mappings require a space after the colon - array('[foo, http://urls.are/no/mappings, false, null, 12]', array('foo', 'http://urls.are/no/mappings', false, null, 12)), - array('[ foo , bar , false , null , 12 ]', array('foo', 'bar', false, null, 12)), - array('[\'foo,bar\', \'foo bar\']', array('foo,bar', 'foo bar')), - - // mappings - array('{foo:bar,bar:foo,false:false,null:null,integer:12}', array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12)), - array('{ foo : bar, bar : foo, false : false, null : null, integer : 12 }', array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12)), - array('{foo: \'bar\', bar: \'foo: bar\'}', array('foo' => 'bar', 'bar' => 'foo: bar')), - array('{\'foo\': \'bar\', "bar": \'foo: bar\'}', array('foo' => 'bar', 'bar' => 'foo: bar')), - array('{\'foo\'\'\': \'bar\', "bar\"": \'foo: bar\'}', array('foo\'' => 'bar', 'bar"' => 'foo: bar')), - array('{\'foo: \': \'bar\', "bar: ": \'foo: bar\'}', array('foo: ' => 'bar', 'bar: ' => 'foo: bar')), - - // nested sequences and mappings - array('[foo, [bar, foo]]', array('foo', array('bar', 'foo'))), - array('[foo, {bar: foo}]', array('foo', array('bar' => 'foo'))), - array('{ foo: {bar: foo} }', array('foo' => array('bar' => 'foo'))), - array('{ foo: [bar, foo] }', array('foo' => array('bar', 'foo'))), - - array('[ foo, [ bar, foo ] ]', array('foo', array('bar', 'foo'))), - - array('[{ foo: {bar: foo} }]', array(array('foo' => array('bar' => 'foo')))), - - array('[foo, [bar, [foo, [bar, foo]], foo]]', array('foo', array('bar', array('foo', array('bar', 'foo')), 'foo'))), - - array('[foo, {bar: foo, foo: [foo, {bar: foo}]}, [foo, {bar: foo}]]', array('foo', array('bar' => 'foo', 'foo' => array('foo', array('bar' => 'foo'))), array('foo', array('bar' => 'foo')))), - - array('[foo, bar: { foo: bar }]', array('foo', '1' => array('bar' => array('foo' => 'bar')))), - array('[foo, \'@foo.baz\', { \'%foo%\': \'foo is %foo%\', bar: \'%foo%\' }, true, \'@service_container\']', array('foo', '@foo.baz', array('%foo%' => 'foo is %foo%', 'bar' => '%foo%'), true, '@service_container')), - ); - } - - public function getTestsForParseWithMapObjects() - { - return array( - array('', ''), - array('null', null), - array('false', false), - array('true', true), - array('12', 12), - array('-12', -12), - array('"quoted string"', 'quoted string'), - array("'quoted string'", 'quoted string'), - array('12.30e+02', 12.30e+02), - array('0x4D2', 0x4D2), - array('02333', 02333), - array('.Inf', -log(0)), - array('-.Inf', log(0)), - array("'686e444'", '686e444'), - array('686e444', 646e444), - array('123456789123456789123456789123456789', '123456789123456789123456789123456789'), - array('"foo\r\nbar"', "foo\r\nbar"), - array("'foo#bar'", 'foo#bar'), - array("'foo # bar'", 'foo # bar'), - array("'#cfcfcf'", '#cfcfcf'), - array('::form_base.html.twig', '::form_base.html.twig'), - - array('2007-10-30', gmmktime(0, 0, 0, 10, 30, 2007)), - array('2007-10-30T02:59:43Z', gmmktime(2, 59, 43, 10, 30, 2007)), - array('2007-10-30 02:59:43 Z', gmmktime(2, 59, 43, 10, 30, 2007)), - array('1960-10-30 02:59:43 Z', gmmktime(2, 59, 43, 10, 30, 1960)), - array('1730-10-30T02:59:43Z', gmmktime(2, 59, 43, 10, 30, 1730)), - - array('"a \\"string\\" with \'quoted strings inside\'"', 'a "string" with \'quoted strings inside\''), - array("'a \"string\" with ''quoted strings inside'''", 'a "string" with \'quoted strings inside\''), - - // sequences - // urls are no key value mapping. see #3609. Valid yaml "key: value" mappings require a space after the colon - array('[foo, http://urls.are/no/mappings, false, null, 12]', array('foo', 'http://urls.are/no/mappings', false, null, 12)), - array('[ foo , bar , false , null , 12 ]', array('foo', 'bar', false, null, 12)), - array('[\'foo,bar\', \'foo bar\']', array('foo,bar', 'foo bar')), - - // mappings - array('{foo:bar,bar:foo,false:false,null:null,integer:12}', (object) array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12)), - array('{ foo : bar, bar : foo, false : false, null : null, integer : 12 }', (object) array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12)), - array('{foo: \'bar\', bar: \'foo: bar\'}', (object) array('foo' => 'bar', 'bar' => 'foo: bar')), - array('{\'foo\': \'bar\', "bar": \'foo: bar\'}', (object) array('foo' => 'bar', 'bar' => 'foo: bar')), - array('{\'foo\'\'\': \'bar\', "bar\"": \'foo: bar\'}', (object) array('foo\'' => 'bar', 'bar"' => 'foo: bar')), - array('{\'foo: \': \'bar\', "bar: ": \'foo: bar\'}', (object) array('foo: ' => 'bar', 'bar: ' => 'foo: bar')), - - // nested sequences and mappings - array('[foo, [bar, foo]]', array('foo', array('bar', 'foo'))), - array('[foo, {bar: foo}]', array('foo', (object) array('bar' => 'foo'))), - array('{ foo: {bar: foo} }', (object) array('foo' => (object) array('bar' => 'foo'))), - array('{ foo: [bar, foo] }', (object) array('foo' => array('bar', 'foo'))), - - array('[ foo, [ bar, foo ] ]', array('foo', array('bar', 'foo'))), - - array('[{ foo: {bar: foo} }]', array((object) array('foo' => (object) array('bar' => 'foo')))), - - array('[foo, [bar, [foo, [bar, foo]], foo]]', array('foo', array('bar', array('foo', array('bar', 'foo')), 'foo'))), - - array('[foo, {bar: foo, foo: [foo, {bar: foo}]}, [foo, {bar: foo}]]', array('foo', (object) array('bar' => 'foo', 'foo' => array('foo', (object) array('bar' => 'foo'))), array('foo', (object) array('bar' => 'foo')))), - - array('[foo, bar: { foo: bar }]', array('foo', '1' => (object) array('bar' => (object) array('foo' => 'bar')))), - array('[foo, \'@foo.baz\', { \'%foo%\': \'foo is %foo%\', bar: \'%foo%\' }, true, \'@service_container\']', array('foo', '@foo.baz', (object) array('%foo%' => 'foo is %foo%', 'bar' => '%foo%'), true, '@service_container')), - - array('{}', new \stdClass()), - array('{ foo : bar, bar : {} }', (object) array('foo' => 'bar', 'bar' => new \stdClass())), - array('{ foo : [], bar : {} }', (object) array('foo' => array(), 'bar' => new \stdClass())), - array('{foo: \'bar\', bar: {} }', (object) array('foo' => 'bar', 'bar' => new \stdClass())), - array('{\'foo\': \'bar\', "bar": {}}', (object) array('foo' => 'bar', 'bar' => new \stdClass())), - array('{\'foo\': \'bar\', "bar": \'{}\'}', (object) array('foo' => 'bar', 'bar' => '{}')), - - array('[foo, [{}, {}]]', array('foo', array(new \stdClass(), new \stdClass()))), - array('[foo, [[], {}]]', array('foo', array(array(), new \stdClass()))), - array('[foo, [[{}, {}], {}]]', array('foo', array(array(new \stdClass(), new \stdClass()), new \stdClass()))), - array('[foo, {bar: {}}]', array('foo', '1' => (object) array('bar' => new \stdClass()))), - ); - } - - public function getTestsForDump() - { - return array( - array('null', null), - array('false', false), - array('true', true), - array('12', 12), - array("'quoted string'", 'quoted string'), - array('!!float 1230', 12.30e+02), - array('1234', 0x4D2), - array('1243', 02333), - array('.Inf', -log(0)), - array('-.Inf', log(0)), - array("'686e444'", '686e444'), - array('"foo\r\nbar"', "foo\r\nbar"), - array("'foo#bar'", 'foo#bar'), - array("'foo # bar'", 'foo # bar'), - array("'#cfcfcf'", '#cfcfcf'), - - array("'a \"string\" with ''quoted strings inside'''", 'a "string" with \'quoted strings inside\''), - - array("'-dash'", '-dash'), - array("'-'", '-'), - - // Pre-YAML-1.2 booleans - array("'y'", 'y'), - array("'n'", 'n'), - array("'yes'", 'yes'), - array("'no'", 'no'), - array("'on'", 'on'), - array("'off'", 'off'), - - // sequences - array('[foo, bar, false, null, 12]', array('foo', 'bar', false, null, 12)), - array('[\'foo,bar\', \'foo bar\']', array('foo,bar', 'foo bar')), - - // mappings - array('{ foo: bar, bar: foo, \'false\': false, \'null\': null, integer: 12 }', array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12)), - array('{ foo: bar, bar: \'foo: bar\' }', array('foo' => 'bar', 'bar' => 'foo: bar')), - - // nested sequences and mappings - array('[foo, [bar, foo]]', array('foo', array('bar', 'foo'))), - - array('[foo, [bar, [foo, [bar, foo]], foo]]', array('foo', array('bar', array('foo', array('bar', 'foo')), 'foo'))), - - array('{ foo: { bar: foo } }', array('foo' => array('bar' => 'foo'))), - - array('[foo, { bar: foo }]', array('foo', array('bar' => 'foo'))), - - array('[foo, { bar: foo, foo: [foo, { bar: foo }] }, [foo, { bar: foo }]]', array('foo', array('bar' => 'foo', 'foo' => array('foo', array('bar' => 'foo'))), array('foo', array('bar' => 'foo')))), - - array('[foo, \'@foo.baz\', { \'%foo%\': \'foo is %foo%\', bar: \'%foo%\' }, true, \'@service_container\']', array('foo', '@foo.baz', array('%foo%' => 'foo is %foo%', 'bar' => '%foo%'), true, '@service_container')), - - array('{ foo: { bar: { 1: 2, baz: 3 } } }', array('foo' => array('bar' => array(1 => 2, 'baz' => 3)))), - ); - } - - /** - * @dataProvider getTimestampTests - */ - public function testParseTimestampAsUnixTimestampByDefault($yaml, $year, $month, $day, $hour, $minute, $second) - { - $this->assertSame(gmmktime($hour, $minute, $second, $month, $day, $year), Inline::parse($yaml)); - } - - /** - * @dataProvider getTimestampTests - */ - public function testParseTimestampAsDateTimeObject($yaml, $year, $month, $day, $hour, $minute, $second) - { - $expected = new \DateTime($yaml); - $expected->setTimeZone(new \DateTimeZone('UTC')); - $expected->setDate($year, $month, $day); - $expected->setTime($hour, $minute, $second); - - $this->assertEquals($expected, Inline::parse($yaml, Yaml::PARSE_DATETIME)); - } - - public function getTimestampTests() - { - return array( - 'canonical' => array('2001-12-15T02:59:43.1Z', 2001, 12, 15, 2, 59, 43), - 'ISO-8601' => array('2001-12-15t21:59:43.10-05:00', 2001, 12, 16, 2, 59, 43), - 'spaced' => array('2001-12-15 21:59:43.10 -5', 2001, 12, 16, 2, 59, 43), - 'date' => array('2001-12-15', 2001, 12, 15, 0, 0, 0), - ); - } - - /** - * @dataProvider getTimestampTests - */ - public function testParseNestedTimestampListAsDateTimeObject($yaml, $year, $month, $day, $hour, $minute, $second) - { - $expected = new \DateTime($yaml); - $expected->setTimeZone(new \DateTimeZone('UTC')); - $expected->setDate($year, $month, $day); - $expected->setTime($hour, $minute, $second); - - $expectedNested = array('nested' => array($expected)); - $yamlNested = "{nested: [$yaml]}"; - - $this->assertEquals($expectedNested, Inline::parse($yamlNested, Yaml::PARSE_DATETIME)); - } - - /** - * @dataProvider getDateTimeDumpTests - */ - public function testDumpDateTime($dateTime, $expected) - { - $this->assertSame($expected, Inline::dump($dateTime)); - } - - public function getDateTimeDumpTests() - { - $tests = array(); - - $dateTime = new \DateTime('2001-12-15 21:59:43', new \DateTimeZone('UTC')); - $tests['date-time-utc'] = array($dateTime, '2001-12-15T21:59:43+00:00'); - - $dateTime = new \DateTimeImmutable('2001-07-15 21:59:43', new \DateTimeZone('Europe/Berlin')); - $tests['immutable-date-time-europe-berlin'] = array($dateTime, '2001-07-15T21:59:43+02:00'); - - return $tests; - } - - /** - * @dataProvider getBinaryData - */ - public function testParseBinaryData($data) - { - $this->assertSame('Hello world', Inline::parse($data)); - } - - public function getBinaryData() - { - return array( - 'enclosed with double quotes' => array('!!binary "SGVsbG8gd29ybGQ="'), - 'enclosed with single quotes' => array("!!binary 'SGVsbG8gd29ybGQ='"), - 'containing spaces' => array('!!binary "SGVs bG8gd 29ybGQ="'), - ); - } - - /** - * @dataProvider getInvalidBinaryData - */ - public function testParseInvalidBinaryData($data, $expectedMessage) - { - $this->setExpectedExceptionRegExp('\Symfony\Component\Yaml\Exception\ParseException', $expectedMessage); - - Inline::parse($data); - } - - public function getInvalidBinaryData() - { - return array( - 'length not a multiple of four' => array('!!binary "SGVsbG8d29ybGQ="', '/The normalized base64 encoded data \(data without whitespace characters\) length must be a multiple of four \(\d+ bytes given\)/'), - 'invalid characters' => array('!!binary "SGVsbG8#d29ybGQ="', '/The base64 encoded data \(.*\) contains invalid characters/'), - 'too many equals characters' => array('!!binary "SGVsbG8gd29yb==="', '/The base64 encoded data \(.*\) contains invalid characters/'), - 'misplaced equals character' => array('!!binary "SGVsbG8gd29ybG=Q"', '/The base64 encoded data \(.*\) contains invalid characters/'), - ); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/ParseExceptionTest.php b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/ParseExceptionTest.php deleted file mode 100644 index 7286d45a09..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/ParseExceptionTest.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml\Tests; - -use Symfony\Component\Yaml\Exception\ParseException; - -class ParseExceptionTest extends \PHPUnit_Framework_TestCase -{ - public function testGetMessage() - { - $exception = new ParseException('Error message', 42, 'foo: bar', '/var/www/app/config.yml'); - $message = 'Error message in "/var/www/app/config.yml" at line 42 (near "foo: bar")'; - - $this->assertEquals($message, $exception->getMessage()); - } - - public function testGetMessageWithUnicodeInFilename() - { - $exception = new ParseException('Error message', 42, 'foo: bar', 'äöü.yml'); - $message = 'Error message in "äöü.yml" at line 42 (near "foo: bar")'; - - $this->assertEquals($message, $exception->getMessage()); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/ParserTest.php b/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/ParserTest.php deleted file mode 100644 index c2f4891d46..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Tests/ParserTest.php +++ /dev/null @@ -1,1338 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml\Tests; - -use Symfony\Component\Yaml\Yaml; -use Symfony\Component\Yaml\Parser; - -class ParserTest extends \PHPUnit_Framework_TestCase -{ - protected $parser; - - protected function setUp() - { - $this->parser = new Parser(); - } - - protected function tearDown() - { - $this->parser = null; - } - - /** - * @dataProvider getDataFormSpecifications - */ - public function testSpecifications($file, $expected, $yaml, $comment) - { - $this->assertEquals($expected, var_export($this->parser->parse($yaml), true), $comment); - } - - public function getDataFormSpecifications() - { - $parser = new Parser(); - $path = __DIR__.'/Fixtures'; - - $tests = array(); - $files = $parser->parse(file_get_contents($path.'/index.yml')); - foreach ($files as $file) { - $yamls = file_get_contents($path.'/'.$file.'.yml'); - - // split YAMLs documents - foreach (preg_split('/^---( %YAML\:1\.0)?/m', $yamls) as $yaml) { - if (!$yaml) { - continue; - } - - $test = $parser->parse($yaml); - if (isset($test['todo']) && $test['todo']) { - // TODO - } else { - eval('$expected = '.trim($test['php']).';'); - - $tests[] = array($file, var_export($expected, true), $test['yaml'], $test['test']); - } - } - } - - return $tests; - } - - public function testTabsInYaml() - { - // test tabs in YAML - $yamls = array( - "foo:\n bar", - "foo:\n bar", - "foo:\n bar", - "foo:\n bar", - ); - - foreach ($yamls as $yaml) { - try { - $content = $this->parser->parse($yaml); - - $this->fail('YAML files must not contain tabs'); - } catch (\Exception $e) { - $this->assertInstanceOf('\Exception', $e, 'YAML files must not contain tabs'); - $this->assertEquals('A YAML file cannot contain tabs as indentation at line 2 (near "'.strpbrk($yaml, "\t").'").', $e->getMessage(), 'YAML files must not contain tabs'); - } - } - } - - public function testEndOfTheDocumentMarker() - { - $yaml = <<<'EOF' ---- %YAML:1.0 -foo -... -EOF; - - $this->assertEquals('foo', $this->parser->parse($yaml)); - } - - public function getBlockChompingTests() - { - $tests = array(); - - $yaml = <<<'EOF' -foo: |- - one - two -bar: |- - one - two - -EOF; - $expected = array( - 'foo' => "one\ntwo", - 'bar' => "one\ntwo", - ); - $tests['Literal block chomping strip with single trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: |- - one - two - -bar: |- - one - two - - -EOF; - $expected = array( - 'foo' => "one\ntwo", - 'bar' => "one\ntwo", - ); - $tests['Literal block chomping strip with multiple trailing newlines'] = array($expected, $yaml); - - $yaml = <<<'EOF' -{} - - -EOF; - $expected = array(); - $tests['Literal block chomping strip with multiple trailing newlines after a 1-liner'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: |- - one - two -bar: |- - one - two -EOF; - $expected = array( - 'foo' => "one\ntwo", - 'bar' => "one\ntwo", - ); - $tests['Literal block chomping strip without trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: | - one - two -bar: | - one - two - -EOF; - $expected = array( - 'foo' => "one\ntwo\n", - 'bar' => "one\ntwo\n", - ); - $tests['Literal block chomping clip with single trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: | - one - two - -bar: | - one - two - - -EOF; - $expected = array( - 'foo' => "one\ntwo\n", - 'bar' => "one\ntwo\n", - ); - $tests['Literal block chomping clip with multiple trailing newlines'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: | - one - two -bar: | - one - two -EOF; - $expected = array( - 'foo' => "one\ntwo\n", - 'bar' => "one\ntwo", - ); - $tests['Literal block chomping clip without trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: |+ - one - two -bar: |+ - one - two - -EOF; - $expected = array( - 'foo' => "one\ntwo\n", - 'bar' => "one\ntwo\n", - ); - $tests['Literal block chomping keep with single trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: |+ - one - two - -bar: |+ - one - two - - -EOF; - $expected = array( - 'foo' => "one\ntwo\n\n", - 'bar' => "one\ntwo\n\n", - ); - $tests['Literal block chomping keep with multiple trailing newlines'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: |+ - one - two -bar: |+ - one - two -EOF; - $expected = array( - 'foo' => "one\ntwo\n", - 'bar' => "one\ntwo", - ); - $tests['Literal block chomping keep without trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: >- - one - two -bar: >- - one - two - -EOF; - $expected = array( - 'foo' => 'one two', - 'bar' => 'one two', - ); - $tests['Folded block chomping strip with single trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: >- - one - two - -bar: >- - one - two - - -EOF; - $expected = array( - 'foo' => 'one two', - 'bar' => 'one two', - ); - $tests['Folded block chomping strip with multiple trailing newlines'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: >- - one - two -bar: >- - one - two -EOF; - $expected = array( - 'foo' => 'one two', - 'bar' => 'one two', - ); - $tests['Folded block chomping strip without trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: > - one - two -bar: > - one - two - -EOF; - $expected = array( - 'foo' => "one two\n", - 'bar' => "one two\n", - ); - $tests['Folded block chomping clip with single trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: > - one - two - -bar: > - one - two - - -EOF; - $expected = array( - 'foo' => "one two\n", - 'bar' => "one two\n", - ); - $tests['Folded block chomping clip with multiple trailing newlines'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: > - one - two -bar: > - one - two -EOF; - $expected = array( - 'foo' => "one two\n", - 'bar' => 'one two', - ); - $tests['Folded block chomping clip without trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: >+ - one - two -bar: >+ - one - two - -EOF; - $expected = array( - 'foo' => "one two\n", - 'bar' => "one two\n", - ); - $tests['Folded block chomping keep with single trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: >+ - one - two - -bar: >+ - one - two - - -EOF; - $expected = array( - 'foo' => "one two\n\n", - 'bar' => "one two\n\n", - ); - $tests['Folded block chomping keep with multiple trailing newlines'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: >+ - one - two -bar: >+ - one - two -EOF; - $expected = array( - 'foo' => "one two\n", - 'bar' => 'one two', - ); - $tests['Folded block chomping keep without trailing newline'] = array($expected, $yaml); - - return $tests; - } - - /** - * @dataProvider getBlockChompingTests - */ - public function testBlockChomping($expected, $yaml) - { - $this->assertSame($expected, $this->parser->parse($yaml)); - } - - /** - * Regression test for issue #7989. - * - * @see https://github.com/symfony/symfony/issues/7989 - */ - public function testBlockLiteralWithLeadingNewlines() - { - $yaml = <<<'EOF' -foo: |- - - - bar - -EOF; - $expected = array( - 'foo' => "\n\nbar", - ); - - $this->assertSame($expected, $this->parser->parse($yaml)); - } - - public function testObjectSupportEnabled() - { - $input = <<assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, Yaml::PARSE_OBJECT), '->parse() is able to parse objects'); - } - - /** - * @group legacy - */ - public function testObjectSupportEnabledPassingTrue() - { - $input = <<assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, false, true), '->parse() is able to parse objects'); - } - - /** - * @group legacy - */ - public function testObjectSupportEnabledWithDeprecatedTag() - { - $input = <<assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, Yaml::PARSE_OBJECT), '->parse() is able to parse objects'); - } - - /** - * @dataProvider invalidDumpedObjectProvider - */ - public function testObjectSupportDisabledButNoExceptions($input) - { - $this->assertEquals(array('foo' => null, 'bar' => 1), $this->parser->parse($input), '->parse() does not parse objects'); - } - - /** - * @dataProvider getObjectForMapTests - */ - public function testObjectForMap($yaml, $expected) - { - $this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP)); - } - - /** - * @group legacy - * @dataProvider getObjectForMapTests - */ - public function testObjectForMapEnabledWithMappingUsingBooleanToggles($yaml, $expected) - { - $this->assertEquals($expected, $this->parser->parse($yaml, false, false, true)); - } - - public function getObjectForMapTests() - { - $tests = array(); - - $yaml = <<foo = new \stdClass(); - $expected->foo->fiz = array('cat'); - $tests['mapping'] = array($yaml, $expected); - - $yaml = '{ "foo": "bar", "fiz": "cat" }'; - $expected = new \stdClass(); - $expected->foo = 'bar'; - $expected->fiz = 'cat'; - $tests['inline-mapping'] = array($yaml, $expected); - - $yaml = "foo: bar\nbaz: foobar"; - $expected = new \stdClass(); - $expected->foo = 'bar'; - $expected->baz = 'foobar'; - $tests['object-for-map-is-applied-after-parsing'] = array($yaml, $expected); - - $yaml = <<array = array(); - $expected->array[0] = new \stdClass(); - $expected->array[0]->key = 'one'; - $expected->array[1] = new \stdClass(); - $expected->array[1]->key = 'two'; - $tests['nest-map-and-sequence'] = array($yaml, $expected); - - $yaml = <<map = new \stdClass(); - $expected->map->{1} = 'one'; - $expected->map->{2} = 'two'; - $tests['numeric-keys'] = array($yaml, $expected); - - $yaml = <<map = new \stdClass(); - $expected->map->{0} = 'one'; - $expected->map->{1} = 'two'; - $tests['zero-indexed-numeric-keys'] = array($yaml, $expected); - - return $tests; - } - - /** - * @dataProvider invalidDumpedObjectProvider - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testObjectsSupportDisabledWithExceptions($yaml) - { - $this->parser->parse($yaml, Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE); - } - - /** - * @group legacy - * @dataProvider invalidDumpedObjectProvider - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testObjectsSupportDisabledWithExceptionsUsingBooleanToggles($yaml) - { - $this->parser->parse($yaml, true); - } - - public function invalidDumpedObjectProvider() - { - $yamlTag = << array($yamlTag), - 'local-tag' => array($localTag), - ); - } - - /** - * @requires extension iconv - */ - public function testNonUtf8Exception() - { - $yamls = array( - iconv('UTF-8', 'ISO-8859-1', "foo: 'äöüß'"), - iconv('UTF-8', 'ISO-8859-15', "euro: '€'"), - iconv('UTF-8', 'CP1252', "cp1252: '©ÉÇáñ'"), - ); - - foreach ($yamls as $yaml) { - try { - $this->parser->parse($yaml); - - $this->fail('charsets other than UTF-8 are rejected.'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\Yaml\Exception\ParseException', $e, 'charsets other than UTF-8 are rejected.'); - } - } - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testUnindentedCollectionException() - { - $yaml = <<<'EOF' - -collection: --item1 --item2 --item3 - -EOF; - - $this->parser->parse($yaml); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testShortcutKeyUnindentedCollectionException() - { - $yaml = <<<'EOF' - -collection: -- key: foo - foo: bar - -EOF; - - $this->parser->parse($yaml); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - * @expectedExceptionMessageRegExp /^Multiple documents are not supported.+/ - */ - public function testMultipleDocumentsNotSupportedException() - { - Yaml::parse(<<<'EOL' -# Ranking of 1998 home runs ---- -- Mark McGwire -- Sammy Sosa -- Ken Griffey - -# Team ranking ---- -- Chicago Cubs -- St Louis Cardinals -EOL - ); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testSequenceInAMapping() - { - Yaml::parse(<<<'EOF' -yaml: - hash: me - - array stuff -EOF - ); - } - - public function testSequenceInMappingStartedBySingleDashLine() - { - $yaml = << array( - array( - 'b' => array( - array( - 'bar' => 'baz', - ), - ), - ), - 'foo', - ), - 'd' => 'e', - ); - - $this->assertSame($expected, $this->parser->parse($yaml)); - } - - public function testSequenceFollowedByCommentEmbeddedInMapping() - { - $yaml = << array( - 'b' => array('c'), - 'd' => 'e', - ), - ); - - $this->assertSame($expected, $this->parser->parse($yaml)); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testMappingInASequence() - { - Yaml::parse(<<<'EOF' -yaml: - - array stuff - hash: me -EOF - ); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - * @expectedExceptionMessage missing colon - */ - public function testScalarInSequence() - { - Yaml::parse(<< It is an error for two equal keys to appear in the same mapping node. - * > In such a case the YAML processor may continue, ignoring the second - * > `key: value` pair and issuing an appropriate warning. This strategy - * > preserves a consistent information model for one-pass and random access - * > applications. - * - * @see http://yaml.org/spec/1.2/spec.html#id2759572 - * @see http://yaml.org/spec/1.1/#id932806 - */ - public function testMappingDuplicateKeyBlock() - { - $input = << array( - 'child' => 'first', - ), - ); - $this->assertSame($expected, Yaml::parse($input)); - } - - public function testMappingDuplicateKeyFlow() - { - $input = << array( - 'child' => 'first', - ), - ); - $this->assertSame($expected, Yaml::parse($input)); - } - - public function testEmptyValue() - { - $input = <<<'EOF' -hash: -EOF; - - $this->assertEquals(array('hash' => null), Yaml::parse($input)); - } - - public function testCommentAtTheRootIndent() - { - $this->assertEquals(array( - 'services' => array( - 'app.foo_service' => array( - 'class' => 'Foo', - ), - 'app/bar_service' => array( - 'class' => 'Bar', - ), - ), - ), Yaml::parse(<<<'EOF' -# comment 1 -services: -# comment 2 - # comment 3 - app.foo_service: - class: Foo -# comment 4 - # comment 5 - app/bar_service: - class: Bar -EOF - )); - } - - public function testStringBlockWithComments() - { - $this->assertEquals(array('content' => <<<'EOT' -# comment 1 -header - - # comment 2 - -

    title

    - - -footer # comment3 -EOT - ), Yaml::parse(<<<'EOF' -content: | - # comment 1 - header - - # comment 2 - -

    title

    - - - footer # comment3 -EOF - )); - } - - public function testFoldedStringBlockWithComments() - { - $this->assertEquals(array(array('content' => <<<'EOT' -# comment 1 -header - - # comment 2 - -

    title

    - - -footer # comment3 -EOT - )), Yaml::parse(<<<'EOF' -- - content: | - # comment 1 - header - - # comment 2 - -

    title

    - - - footer # comment3 -EOF - )); - } - - public function testNestedFoldedStringBlockWithComments() - { - $this->assertEquals(array(array( - 'title' => 'some title', - 'content' => <<<'EOT' -# comment 1 -header - - # comment 2 - -

    title

    - - -footer # comment3 -EOT - )), Yaml::parse(<<<'EOF' -- - title: some title - content: | - # comment 1 - header - - # comment 2 - -

    title

    - - - footer # comment3 -EOF - )); - } - - public function testReferenceResolvingInInlineStrings() - { - $this->assertEquals(array( - 'var' => 'var-value', - 'scalar' => 'var-value', - 'list' => array('var-value'), - 'list_in_list' => array(array('var-value')), - 'map_in_list' => array(array('key' => 'var-value')), - 'embedded_mapping' => array(array('key' => 'var-value')), - 'map' => array('key' => 'var-value'), - 'list_in_map' => array('key' => array('var-value')), - 'map_in_map' => array('foo' => array('bar' => 'var-value')), - ), Yaml::parse(<<<'EOF' -var: &var var-value -scalar: *var -list: [ *var ] -list_in_list: [[ *var ]] -map_in_list: [ { key: *var } ] -embedded_mapping: [ key: *var ] -map: { key: *var } -list_in_map: { key: [*var] } -map_in_map: { foo: { bar: *var } } -EOF - )); - } - - public function testYamlDirective() - { - $yaml = <<<'EOF' -%YAML 1.2 ---- -foo: 1 -bar: 2 -EOF; - $this->assertEquals(array('foo' => 1, 'bar' => 2), $this->parser->parse($yaml)); - } - - public function testFloatKeys() - { - $yaml = <<<'EOF' -foo: - 1.2: "bar" - 1.3: "baz" -EOF; - - $expected = array( - 'foo' => array( - '1.2' => 'bar', - '1.3' => 'baz', - ), - ); - - $this->assertEquals($expected, $this->parser->parse($yaml)); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - * @expectedExceptionMessage A colon cannot be used in an unquoted mapping value - */ - public function testColonInMappingValueException() - { - $yaml = <<parser->parse($yaml); - } - - public function testColonInMappingValueExceptionNotTriggeredByColonInComment() - { - $yaml = <<assertSame(array('foo' => array('bar' => 'foobar')), $this->parser->parse($yaml)); - } - - /** - * @dataProvider getCommentLikeStringInScalarBlockData - */ - public function testCommentLikeStringsAreNotStrippedInBlockScalars($yaml, $expectedParserResult) - { - $this->assertSame($expectedParserResult, $this->parser->parse($yaml)); - } - - public function getCommentLikeStringInScalarBlockData() - { - $tests = array(); - - $yaml = <<<'EOT' -pages: - - - title: some title - content: | - # comment 1 - header - - # comment 2 - -

    title

    - - - footer # comment3 -EOT; - $expected = array( - 'pages' => array( - array( - 'title' => 'some title', - 'content' => <<<'EOT' -# comment 1 -header - - # comment 2 - -

    title

    - - -footer # comment3 -EOT - , - ), - ), - ); - $tests[] = array($yaml, $expected); - - $yaml = <<<'EOT' -test: | - foo - # bar - baz -collection: - - one: | - foo - # bar - baz - - two: | - foo - # bar - baz -EOT; - $expected = array( - 'test' => <<<'EOT' -foo -# bar -baz - -EOT - , - 'collection' => array( - array( - 'one' => <<<'EOT' -foo -# bar -baz - -EOT - , - ), - array( - 'two' => <<<'EOT' -foo -# bar -baz -EOT - , - ), - ), - ); - $tests[] = array($yaml, $expected); - - $yaml = << - line1 - line2> - baz: -# comment - foobar: ~ -EOT; - $expected = array( - 'foo' => array( - 'bar' => array( - 'scalar-block' => "line1 line2>\n", - ), - 'baz' => array( - 'foobar' => null, - ), - ), - ); - $tests[] = array($yaml, $expected); - - $yaml = <<<'EOT' -a: - b: hello -# c: | -# first row -# second row - d: hello -EOT; - $expected = array( - 'a' => array( - 'b' => 'hello', - 'd' => 'hello', - ), - ); - $tests[] = array($yaml, $expected); - - return $tests; - } - - public function testBlankLinesAreParsedAsNewLinesInFoldedBlocks() - { - $yaml = << -

    A heading

    - -
      -
    • a list
    • -
    • may be a good example
    • -
    -EOT; - - $this->assertSame( - array( - 'test' => <<A heading -
    • a list
    • may be a good example
    -EOT - , - ), - $this->parser->parse($yaml) - ); - } - - public function testAdditionallyIndentedLinesAreParsedAsNewLinesInFoldedBlocks() - { - $yaml = << -

    A heading

    - -
      -
    • a list
    • -
    • may be a good example
    • -
    -EOT; - - $this->assertSame( - array( - 'test' => <<A heading -
      -
    • a list
    • -
    • may be a good example
    • -
    -EOT - , - ), - $this->parser->parse($yaml) - ); - } - - /** - * @dataProvider getBinaryData - */ - public function testParseBinaryData($data) - { - $this->assertSame(array('data' => 'Hello world'), $this->parser->parse($data)); - } - - public function getBinaryData() - { - return array( - 'enclosed with double quotes' => array('data: !!binary "SGVsbG8gd29ybGQ="'), - 'enclosed with single quotes' => array("data: !!binary 'SGVsbG8gd29ybGQ='"), - 'containing spaces' => array('data: !!binary "SGVs bG8gd 29ybGQ="'), - 'in block scalar' => array( - << array( - <<setExpectedExceptionRegExp('\Symfony\Component\Yaml\Exception\ParseException', $expectedMessage); - - $this->parser->parse($data); - } - - public function getInvalidBinaryData() - { - return array( - 'length not a multiple of four' => array('data: !!binary "SGVsbG8d29ybGQ="', '/The normalized base64 encoded data \(data without whitespace characters\) length must be a multiple of four \(\d+ bytes given\)/'), - 'invalid characters' => array('!!binary "SGVsbG8#d29ybGQ="', '/The base64 encoded data \(.*\) contains invalid characters/'), - 'too many equals characters' => array('data: !!binary "SGVsbG8gd29yb==="', '/The base64 encoded data \(.*\) contains invalid characters/'), - 'misplaced equals character' => array('data: !!binary "SGVsbG8gd29ybG=Q"', '/The base64 encoded data \(.*\) contains invalid characters/'), - 'length not a multiple of four in block scalar' => array( - << array( - << array( - << array( - <<setTimeZone(new \DateTimeZone('UTC')); - $expectedDate->setDate(2002, 12, 14); - $expectedDate->setTime(0, 0, 0); - - $this->assertEquals(array('date' => $expectedDate), $this->parser->parse($yaml, Yaml::PARSE_DATETIME)); - } - - /** - * @param $lineNumber - * @param $yaml - * @dataProvider parserThrowsExceptionWithCorrectLineNumberProvider - */ - public function testParserThrowsExceptionWithCorrectLineNumber($lineNumber, $yaml) - { - $this->setExpectedException( - '\Symfony\Component\Yaml\Exception\ParseException', - sprintf('Unexpected characters near "," at line %d (near "bar: "123",").', $lineNumber) - ); - - $this->parser->parse($yaml); - } - - public function parserThrowsExceptionWithCorrectLineNumberProvider() - { - return array( - array( - 4, - << - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml\Tests; - -use Symfony\Component\Yaml\Yaml; - -class YamlTest extends \PHPUnit_Framework_TestCase -{ - public function testParseAndDump() - { - $data = array('lorem' => 'ipsum', 'dolor' => 'sit'); - $yml = Yaml::dump($data); - $parsed = Yaml::parse($yml); - $this->assertEquals($data, $parsed); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The indentation must be greater than zero - */ - public function testZeroIndentationThrowsException() - { - Yaml::dump(array('lorem' => 'ipsum', 'dolor' => 'sit'), 2, 0); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The indentation must be greater than zero - */ - public function testNegativeIndentationThrowsException() - { - Yaml::dump(array('lorem' => 'ipsum', 'dolor' => 'sit'), 2, -4); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Unescaper.php b/core/src/core/classes/guzzle/vendor/symfony/yaml/Unescaper.php deleted file mode 100644 index 6e863e12f2..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Unescaper.php +++ /dev/null @@ -1,142 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml; - -use Symfony\Component\Yaml\Exception\ParseException; - -/** - * Unescaper encapsulates unescaping rules for single and double-quoted - * YAML strings. - * - * @author Matthew Lewinski - * - * @internal - */ -class Unescaper -{ - /** - * Regex fragment that matches an escaped character in a double quoted string. - */ - const REGEX_ESCAPED_CHARACTER = '\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)'; - - /** - * Unescapes a single quoted string. - * - * @param string $value A single quoted string - * - * @return string The unescaped string - */ - public function unescapeSingleQuotedString($value) - { - return str_replace('\'\'', '\'', $value); - } - - /** - * Unescapes a double quoted string. - * - * @param string $value A double quoted string - * - * @return string The unescaped string - */ - public function unescapeDoubleQuotedString($value) - { - $callback = function ($match) { - return $this->unescapeCharacter($match[0]); - }; - - // evaluate the string - return preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u', $callback, $value); - } - - /** - * Unescapes a character that was found in a double-quoted string. - * - * @param string $value An escaped character - * - * @return string The unescaped character - */ - private function unescapeCharacter($value) - { - switch ($value[1]) { - case '0': - return "\x0"; - case 'a': - return "\x7"; - case 'b': - return "\x8"; - case 't': - return "\t"; - case "\t": - return "\t"; - case 'n': - return "\n"; - case 'v': - return "\xB"; - case 'f': - return "\xC"; - case 'r': - return "\r"; - case 'e': - return "\x1B"; - case ' ': - return ' '; - case '"': - return '"'; - case '/': - return '/'; - case '\\': - return '\\'; - case 'N': - // U+0085 NEXT LINE - return "\xC2\x85"; - case '_': - // U+00A0 NO-BREAK SPACE - return "\xC2\xA0"; - case 'L': - // U+2028 LINE SEPARATOR - return "\xE2\x80\xA8"; - case 'P': - // U+2029 PARAGRAPH SEPARATOR - return "\xE2\x80\xA9"; - case 'x': - return self::utf8chr(hexdec(substr($value, 2, 2))); - case 'u': - return self::utf8chr(hexdec(substr($value, 2, 4))); - case 'U': - return self::utf8chr(hexdec(substr($value, 2, 8))); - default: - throw new ParseException(sprintf('Found unknown escape character "%s".', $value)); - } - } - - /** - * Get the UTF-8 character for the given code point. - * - * @param int $c The unicode code point - * - * @return string The corresponding UTF-8 character - */ - private static function utf8chr($c) - { - if (0x80 > $c %= 0x200000) { - return chr($c); - } - if (0x800 > $c) { - return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F); - } - if (0x10000 > $c) { - return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); - } - - return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/Yaml.php b/core/src/core/classes/guzzle/vendor/symfony/yaml/Yaml.php deleted file mode 100644 index 213ef898c1..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/Yaml.php +++ /dev/null @@ -1,118 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml; - -use Symfony\Component\Yaml\Exception\ParseException; - -/** - * Yaml offers convenience methods to load and dump YAML. - * - * @author Fabien Potencier - */ -class Yaml -{ - const DUMP_OBJECT = 1; - const PARSE_EXCEPTION_ON_INVALID_TYPE = 2; - const PARSE_OBJECT = 4; - const PARSE_OBJECT_FOR_MAP = 8; - const DUMP_EXCEPTION_ON_INVALID_TYPE = 16; - const PARSE_DATETIME = 32; - const DUMP_OBJECT_AS_MAP = 64; - const DUMP_MULTI_LINE_LITERAL_BLOCK = 128; - - /** - * Parses YAML into a PHP value. - * - * Usage: - * - * $array = Yaml::parse(file_get_contents('config.yml')); - * print_r($array); - * - * - * @param string $input A string containing YAML - * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior - * - * @return mixed The YAML converted to a PHP value - * - * @throws ParseException If the YAML is not valid - */ - public static function parse($input, $flags = 0) - { - if (is_bool($flags)) { - @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); - - if ($flags) { - $flags = self::PARSE_EXCEPTION_ON_INVALID_TYPE; - } else { - $flags = 0; - } - } - - if (func_num_args() >= 3) { - @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the PARSE_OBJECT flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(2)) { - $flags |= self::PARSE_OBJECT; - } - } - - if (func_num_args() >= 4) { - @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(3)) { - $flags |= self::PARSE_OBJECT_FOR_MAP; - } - } - - $yaml = new Parser(); - - return $yaml->parse($input, $flags); - } - - /** - * Dumps a PHP array to a YAML string. - * - * The dump method, when supplied with an array, will do its best - * to convert the array into friendly YAML. - * - * @param array $array PHP array - * @param int $inline The level where you switch to inline YAML - * @param int $indent The amount of spaces to use for indentation of nested nodes - * @param int $flags A bit field of DUMP_* constants to customize the dumped YAML string - * - * @return string A YAML string representing the original PHP array - */ - public static function dump($array, $inline = 2, $indent = 4, $flags = 0) - { - if (is_bool($flags)) { - @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); - - if ($flags) { - $flags = self::DUMP_EXCEPTION_ON_INVALID_TYPE; - } else { - $flags = 0; - } - } - - if (func_num_args() >= 5) { - @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the DUMP_OBJECT flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(4)) { - $flags |= self::DUMP_OBJECT; - } - } - - $yaml = new Dumper($indent); - - return $yaml->dump($array, $inline, 0, $flags); - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/composer.json b/core/src/core/classes/guzzle/vendor/symfony/yaml/composer.json deleted file mode 100644 index b835655f6a..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/composer.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "symfony/yaml", - "type": "library", - "description": "Symfony Yaml Component", - "keywords": [], - "homepage": "https://symfony.com", - "license": "MIT", - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "require": { - "php": ">=5.5.9" - }, - "autoload": { - "psr-4": { "Symfony\\Component\\Yaml\\": "" }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "minimum-stability": "dev", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - } -} diff --git a/core/src/core/classes/guzzle/vendor/symfony/yaml/phpunit.xml.dist b/core/src/core/classes/guzzle/vendor/symfony/yaml/phpunit.xml.dist deleted file mode 100644 index 6bdbea16e6..0000000000 --- a/core/src/core/classes/guzzle/vendor/symfony/yaml/phpunit.xml.dist +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - ./Tests/ - - - - - - ./ - - ./Tests - ./vendor - - - - diff --git a/core/src/core/classes/http_class/LICENSE.txt b/core/src/core/classes/http_class/LICENSE.txt deleted file mode 100644 index 29e83e173b..0000000000 --- a/core/src/core/classes/http_class/LICENSE.txt +++ /dev/null @@ -1,36 +0,0 @@ -HTTP client PHP class - -This LICENSE is in the BSD license style. - -License Version Control: -@(#) $Id: LICENSE.txt,v 1.1 2006/04/17 19:44:04 mlemos Exp $ - -Copyright (c) 1999 - 2006, Manuel Lemos -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of Manuel Lemos nor the names of his contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/core/src/core/classes/http_class/http_class.php b/core/src/core/classes/http_class/http_class.php deleted file mode 100644 index 5d902eb899..0000000000 --- a/core/src/core/classes/http_class/http_class.php +++ /dev/null @@ -1,1883 +0,0 @@ -"01", - "Feb"=>"02", - "Mar"=>"03", - "Apr"=>"04", - "May"=>"05", - "Jun"=>"06", - "Jul"=>"07", - "Aug"=>"08", - "Sep"=>"09", - "Oct"=>"10", - "Nov"=>"11", - "Dec"=>"12"); - public $session=''; - public $connection_close=0; - public $force_close = 0; - public $connected_host = ''; - public $connected_port = -1; - public $connected_ssl = 0; - - /* Private methods - DO NOT CALL */ - - public Function Tokenize($string,$separator="") - { - if (!strcmp($separator,"")) { - $separator=$string; - $string=$this->next_token; - } - for ($character=0;$characternext_token=substr($string,$found+1); - return(substr($string,0,$found)); - } else { - $this->next_token=""; - return($string); - } - } - - public Function CookieEncode($value, $name) - { - return($name ? str_replace("=", "%25", $value) : str_replace(";", "%3B", $value)); - } - - public Function SetError($error, $error_code = HTTP_CLIENT_ERROR_UNSPECIFIED_ERROR) - { - $this->error_code = $error_code; - return($this->error=$error); - } - - public Function SetPHPError($error, &$php_error_message, $error_code = HTTP_CLIENT_ERROR_UNSPECIFIED_ERROR) - { - if(IsSet($php_error_message) - && strlen($php_error_message)) - $error.=": ".$php_error_message; - return($this->SetError($error, $error_code)); - } - - public Function SetDataAccessError($error,$check_connection=0) - { - $this->error=$error; - $this->error_code = HTTP_CLIENT_ERROR_COMMUNICATION_FAILURE; - if(!$this->use_curl - && function_exists("socket_get_status")) - { - $status=socket_get_status($this->connection); - if($status["timed_out"]) - $this->error.=": data access time out"; - elseif ($status["eof"]) { - if($check_connection) - $this->error=""; - else - $this->error.=": the server disconnected"; - } - } - } - - public Function OutputDebug($message) - { - if($this->log_debug) - error_log($message); - else { - $message.="\n"; - if($this->html_debug) - $message=str_replace("\n","
    \n",HtmlEntities($message)); - echo $message; - flush(); - } - } - - public Function GetLine() - { - for ($line="";;) { - if ($this->use_curl) { - $eol=strpos($this->response,"\n",$this->read_response); - $data=($eol ? substr($this->response,$this->read_response,$eol+1-$this->read_response) : ""); - $this->read_response+=strlen($data); - } else { - if (feof($this->connection)) { - $this->SetDataAccessError("reached the end of data while reading from the HTTP server connection"); - return(0); - } - $data=fgets($this->connection,100); - } - if(GetType($data)!="string" - || strlen($data)==0) - { - $this->SetDataAccessError("it was not possible to read line from the HTTP server"); - return(0); - } - $line.=$data; - $length=strlen($line); - if($length - && !strcmp(substr($line,$length-1,1),"\n")) - { - $length-=(($length>=2 && !strcmp(substr($line,$length-2,1),"\r")) ? 2 : 1); - $line=substr($line,0,$length); - if($this->debug) - $this->OutputDebug("S $line"); - return($line); - } - } - } - - public Function PutLine($line) - { - if($this->debug) - $this->OutputDebug("C $line"); - if (!fputs($this->connection,$line."\r\n")) { - $this->SetDataAccessError("it was not possible to send a line to the HTTP server"); - return(0); - } - return(1); - } - - public Function PutData($data) - { - if (strlen($data)) { - if($this->debug) - $this->OutputDebug('C '.$data); - if (!fputs($this->connection,$data)) { - $this->SetDataAccessError("it was not possible to send data to the HTTP server"); - return(0); - } - } - return(1); - } - - public Function FlushData() - { - if (!fflush($this->connection)) { - $this->SetDataAccessError("it was not possible to send data to the HTTP server"); - return(0); - } - return(1); - } - - public Function ReadChunkSize() - { - if ($this->remaining_chunk==0) { - $debug=$this->debug; - if(!$this->debug_response_body) - $this->debug=0; - $line=$this->GetLine(); - $this->debug=$debug; - if(GetType($line)!="string") - return($this->SetError("could not read chunk start: ".$this->error, $this->error_code)); - $this->remaining_chunk=hexdec($line); - if ($this->remaining_chunk == 0) { - if(!$this->debug_response_body) - $this->debug=0; - $line=$this->GetLine(); - $this->debug=$debug; - if(GetType($line)!="string") - return($this->SetError("could not read chunk end: ".$this->error, $this->error_code)); - } - } - return(""); - } - - public Function ReadBytes($length) - { - if ($this->use_curl) { - $bytes=substr($this->response,$this->read_response,min($length,strlen($this->response)-$this->read_response)); - $this->read_response+=strlen($bytes); - if($this->debug - && $this->debug_response_body - && strlen($bytes)) - $this->OutputDebug("S ".$bytes); - } else { - if ($this->chunked) { - for ($bytes="",$remaining=$length;$remaining;) { - if(strlen($this->ReadChunkSize())) - return(""); - if ($this->remaining_chunk==0) { - $this->last_chunk_read=1; - break; - } - $ask=min($this->remaining_chunk,$remaining); - $chunk=@fread($this->connection,$ask); - $read=strlen($chunk); - if ($read==0) { - $this->SetDataAccessError("it was not possible to read data chunk from the HTTP server"); - return(""); - } - if($this->debug - && $this->debug_response_body) - $this->OutputDebug("S ".$chunk); - $bytes.=$chunk; - $this->remaining_chunk-=$read; - $remaining-=$read; - if ($this->remaining_chunk==0) { - if(feof($this->connection)) - return($this->SetError("reached the end of data while reading the end of data chunk mark from the HTTP server", HTTP_CLIENT_ERROR_PROTOCOL_FAILURE)); - $data=@fread($this->connection,2); - if (strcmp($data,"\r\n")) { - $this->SetDataAccessError("it was not possible to read end of data chunk from the HTTP server"); - return(""); - } - } - } - } else { - $bytes=@fread($this->connection,$length); - if (strlen($bytes)) { - if($this->debug - && $this->debug_response_body) - $this->OutputDebug("S ".$bytes); - } else - $this->SetDataAccessError("it was not possible to read data from the HTTP server", $this->connection_close); - } - } - return($bytes); - } - - public Function EndOfInput() - { - if($this->use_curl) - return($this->read_response>=strlen($this->response)); - if($this->chunked) - return($this->last_chunk_read); - if($this->content_length_set) - return($this->content_length <= $this->read_length); - return(feof($this->connection)); - } - - public Function Resolve($domain, &$ip, $server_type) - { - if(preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/',$domain)) - $ip=$domain; - else { - if($this->debug) - $this->OutputDebug('Resolving '.$server_type.' server domain "'.$domain.'"...'); - if(!strcmp($ip=@gethostbyname($domain),$domain)) - $ip=""; - } - if(strlen($ip)==0 - || (strlen($this->exclude_address) - && !strcmp(@gethostbyname($this->exclude_address),$ip))) - return($this->SetError("could not resolve the host domain \"".$domain."\"", HTTP_CLIENT_ERROR_INVALID_SERVER_ADDRESS)); - return(''); - } - - public Function Connect($host_name, $host_port, $ssl, $server_type = 'HTTP') - { - $domain=$host_name; - $port = $host_port; - if(strlen($error = $this->Resolve($domain, $ip, $server_type))) - return($error); - if (strlen($this->socks_host_name)) { - switch ($this->socks_version) { - case '4': - $version = 4; - break; - case '5': - $version = 5; - break; - default: - return('it was not specified a supported SOCKS protocol version'); - break; - } - $host_ip = $ip; - $port = $this->socks_host_port; - $host_server_type = $server_type; - $server_type = 'SOCKS'; - if(strlen($error = $this->Resolve($this->socks_host_name, $ip, $server_type))) - return($error); - } - if($this->debug) - $this->OutputDebug('Connecting to '.$server_type.' server IP '.$ip.' port '.$port.'...'); - if($ssl) - $ip="ssl://".$host_name; - if (($this->connection=($this->timeout ? @fsockopen($ip, $port, $errno, $error, $this->timeout) : @fsockopen($ip, $port, $errno)))==0) { - $error_code = HTTP_CLIENT_ERROR_CANNOT_CONNECT; - switch ($errno) { - case -3: - return($this->SetError("socket could not be created", $error_code)); - case -4: - return($this->SetError("dns lookup on hostname \"".$host_name."\" failed", $error_code)); - case -5: - return($this->SetError("connection refused or timed out", $error_code)); - case -6: - return($this->SetError("fdopen() call failed", $error_code)); - case -7: - return($this->SetError("setvbuf() call failed", $error_code)); - default: - return($this->SetPHPError($errno." could not connect to the host \"".$host_name."\"",$php_errormsg, $error_code)); - } - } else { - if($this->data_timeout - && function_exists("socket_set_timeout")) - socket_set_timeout($this->connection,$this->data_timeout,0); - if (strlen($this->socks_host_name)) { - if($this->debug) - $this->OutputDebug('Connected to the SOCKS server '.$this->socks_host_name); - $send_error = 'it was not possible to send data to the SOCKS server'; - $receive_error = 'it was not possible to receive data from the SOCKS server'; - switch ($version) { - case 4: - $command = 1; - $user = ''; - if(!fputs($this->connection, chr($version).chr($command).pack('nN', $host_port, ip2long($host_ip)).$user.Chr(0))) - $error = $this->SetDataAccessError($send_error); - else { - $response = fgets($this->connection, 9); - if(strlen($response) != 8) - $error = $this->SetDataAccessError($receive_error); - else { - $socks_errors = array( - "\x5a"=>'', - "\x5b"=>'request rejected', - "\x5c"=>'request failed because client is not running identd (or not reachable from the server)', - "\x5d"=>'request failed because client\'s identd could not confirm the user ID string in the request', - ); - $error_code = $response[1]; - $error = (IsSet($socks_errors[$error_code]) ? $socks_errors[$error_code] : 'unknown'); - if(strlen($error)) - $error = 'SOCKS error: '.$error; - } - } - break; - case 5: - if($this->debug) - $this->OutputDebug('Negotiating the authentication method ...'); - $methods = 1; - $method = 0; - if(!fputs($this->connection, chr($version).chr($methods).chr($method))) - $error = $this->SetDataAccessError($send_error); - else { - $response = fgets($this->connection, 3); - if(strlen($response) != 2) - $error = $this->SetDataAccessError($receive_error); - elseif(Ord($response[1]) != $method) - $error = 'the SOCKS server requires an authentication method that is not yet supported'; - else { - if($this->debug) - $this->OutputDebug('Connecting to '.$host_server_type.' server IP '.$host_ip.' port '.$host_port.'...'); - $command = 1; - $address_type = 1; - if(!fputs($this->connection, chr($version).chr($command)."\x00".chr($address_type).pack('Nn', ip2long($host_ip), $host_port))) - $error = $this->SetDataAccessError($send_error); - else { - $response = fgets($this->connection, 11); - if(strlen($response) != 10) - $error = $this->SetDataAccessError($receive_error); - else { - $socks_errors = array( - "\x00"=>'', - "\x01"=>'general SOCKS server failure', - "\x02"=>'connection not allowed by ruleset', - "\x03"=>'Network unreachable', - "\x04"=>'Host unreachable', - "\x05"=>'Connection refused', - "\x06"=>'TTL expired', - "\x07"=>'Command not supported', - "\x08"=>'Address type not supported' - ); - $error_code = $response[1]; - $error = (IsSet($socks_errors[$error_code]) ? $socks_errors[$error_code] : 'unknown'); - if(strlen($error)) - $error = 'SOCKS error: '.$error; - } - } - } - } - break; - default: - $error = 'support for SOCKS protocol version '.$this->socks_version.' is not yet implemented'; - break; - } - if (strlen($error)) { - fclose($this->connection); - return($error); - } - } - if($this->debug) - $this->OutputDebug("Connected to $host_name"); - if(strlen($this->proxy_host_name) - && !strcmp(strtolower($this->protocol), 'https')) - { - if(function_exists('stream_socket_enable_crypto') - && in_array('ssl', stream_get_transports())) - $this->state = "ConnectedToProxy"; - else { - $this->OutputDebug("It is not possible to start SSL after connecting to the proxy server. If the proxy refuses to forward the SSL request, you may need to upgrade to PHP 5.1 or later with OpenSSL support enabled."); - $this->state="Connected"; - } - } else - $this->state="Connected"; - return(""); - } - } - - public Function Disconnect() - { - if($this->debug) - $this->OutputDebug("Disconnected from ".$this->connected_host); - if ($this->use_curl) { - curl_close($this->connection); - $this->response=""; - } else - fclose($this->connection); - $this->state="Disconnected"; - return(""); - } - - /* Public methods */ - - public Function GetRequestArguments($url, &$arguments) - { - $this->error = ''; - $this->error_code = HTTP_CLIENT_ERROR_NO_ERROR; - $arguments=array(); - $url = str_replace(' ', '%20', $url); - $parameters=@parse_url($url); - if(!$parameters) - return($this->SetError("it was not specified a valid URL", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - if(!IsSet($parameters["scheme"])) - return($this->SetError("it was not specified the protocol type argument", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - switch (strtolower($parameters["scheme"])) { - case "http": - case "https": - $arguments["Protocol"]=$parameters["scheme"]; - break; - default: - return($parameters["scheme"]." connection scheme is not yet supported"); - } - if(!IsSet($parameters["host"])) - return($this->SetError("it was not specified the connection host argument", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - $arguments["HostName"]=$parameters["host"]; - $arguments["Headers"]=array("Host"=>$parameters["host"].(IsSet($parameters["port"]) ? ":".$parameters["port"] : "")); - if (IsSet($parameters["user"])) { - $arguments["AuthUser"]=UrlDecode($parameters["user"]); - if(!IsSet($parameters["pass"])) - $arguments["AuthPassword"]=""; - } - if (IsSet($parameters["pass"])) { - if(!IsSet($parameters["user"])) - $arguments["AuthUser"]=""; - $arguments["AuthPassword"]=UrlDecode($parameters["pass"]); - } - if (IsSet($parameters["port"])) { - if(strcmp($parameters["port"],strval(intval($parameters["port"])))) - return($this->SetError("it was not specified a valid connection host argument", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - $arguments["HostPort"]=intval($parameters["port"]); - } else - $arguments["HostPort"]=0; - $arguments["RequestURI"]=(IsSet($parameters["path"]) ? $parameters["path"] : "/").(IsSet($parameters["query"]) ? "?".$parameters["query"] : ""); - if(strlen($this->user_agent)) - $arguments["Headers"]["User-Agent"]=$this->user_agent; - if(strlen($this->accept)) - $arguments["Headers"]["Accept"]=$this->accept; - return(""); - } - - public Function Open($arguments) - { - if(strlen($this->error)) - return($this->error); - $error_code = HTTP_CLIENT_ERROR_UNSPECIFIED_ERROR; - if(IsSet($arguments["HostName"])) - $this->host_name=$arguments["HostName"]; - if(IsSet($arguments["HostPort"])) - $this->host_port=$arguments["HostPort"]; - if(IsSet($arguments["ProxyHostName"])) - $this->proxy_host_name=$arguments["ProxyHostName"]; - if(IsSet($arguments["ProxyHostPort"])) - $this->proxy_host_port=$arguments["ProxyHostPort"]; - if(IsSet($arguments["SOCKSHostName"])) - $this->socks_host_name=$arguments["SOCKSHostName"]; - if(IsSet($arguments["SOCKSHostPort"])) - $this->socks_host_port=$arguments["SOCKSHostPort"]; - if(IsSet($arguments["SOCKSVersion"])) - $this->socks_version=$arguments["SOCKSVersion"]; - if(IsSet($arguments["Protocol"])) - $this->protocol=$arguments["Protocol"]; - switch (strtolower($this->protocol)) { - case "http": - $default_port=80; - break; - case "https": - $default_port=443; - break; - default: - return($this->SetError("it was not specified a valid connection protocol", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - } - if (strlen($this->proxy_host_name)==0) { - if(strlen($this->host_name)==0) - return($this->SetError("it was not specified a valid hostname", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - $host_name=$this->host_name; - $host_port=($this->host_port ? $this->host_port : $default_port); - $server_type = 'HTTP'; - } else { - $host_name=$this->proxy_host_name; - $host_port=$this->proxy_host_port; - $server_type = 'HTTP proxy'; - } - $ssl=(strtolower($this->protocol)=="https" && strlen($this->proxy_host_name)==0); - if($ssl - && strlen($this->socks_host_name)) - return($this->SetError('establishing SSL connections via a SOCKS server is not yet supported', HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - $this->use_curl=($ssl && $this->prefer_curl && function_exists("curl_init")); - switch ($this->state) { - case 'Connected': - if(!strcmp($host_name, $this->connected_host) - && intval($host_port) == $this->connected_port - && intval($ssl) == $this->connected_ssl) - { - if($this->debug) - $this->OutputDebug("Reusing connection to ".$this->connected_host); - return(''); - } - if(strlen($error = $this->Disconnect())) - return($error); - case "Disconnected": - break; - default: - return("1 already connected"); - } - if($this->debug) - $this->OutputDebug("Connecting to ".$this->host_name); - if ($this->use_curl) { - $error=(($this->connection=curl_init($this->protocol."://".$this->host_name.($host_port==$default_port ? "" : ":".strval($host_port))."/")) ? "" : "Could not initialize a CURL session"); - if (strlen($error)==0) { - if(IsSet($arguments["SSLCertificateFile"])) - curl_setopt($this->connection,CURLOPT_SSLCERT,$arguments["SSLCertificateFile"]); - if(IsSet($arguments["SSLCertificatePassword"])) - curl_setopt($this->connection,CURLOPT_SSLCERTPASSWD,$arguments["SSLCertificatePassword"]); - if(IsSet($arguments["SSLKeyFile"])) - curl_setopt($this->connection,CURLOPT_SSLKEY,$arguments["SSLKeyFile"]); - if(IsSet($arguments["SSLKeyPassword"])) - curl_setopt($this->connection,CURLOPT_SSLKEYPASSWD,$arguments["SSLKeyPassword"]); - } - $this->state="Connected"; - } else { - $error=""; - if(strlen($this->proxy_host_name) - && (IsSet($arguments["SSLCertificateFile"]) - || IsSet($arguments["SSLCertificateFile"]))) - $error="establishing SSL connections using certificates or private keys via non-SSL proxies is not supported"; - else { - if ($ssl) { - if(IsSet($arguments["SSLCertificateFile"])) - $error="establishing SSL connections using certificates is only supported when the cURL extension is enabled"; - elseif(IsSet($arguments["SSLKeyFile"])) - $error="establishing SSL connections using a private key is only supported when the cURL extension is enabled"; - else { - $version=explode(".",function_exists("phpversion") ? phpversion() : "3.0.7"); - $php_version=intval($version[0])*1000000+intval($version[1])*1000+intval($version[2]); - if($php_version<4003000) - $error="establishing SSL connections requires at least PHP version 4.3.0 or having the cURL extension enabled"; - elseif(!function_exists("extension_loaded") - || !extension_loaded("openssl")) - $error="establishing SSL connections requires the OpenSSL extension enabled"; - } - } - if (strlen($error)==0) { - $error=$this->Connect($host_name, $host_port, $ssl, $server_type); - $error_code = $this->error_code; - } - } - } - if(strlen($error)) - return($this->SetError($error, $error_code)); - $this->session=md5(uniqid("")); - $this->connected_host = $host_name; - $this->connected_port = intval($host_port); - $this->connected_ssl = intval($ssl); - return(""); - } - - public Function Close($force = 0) - { - if($this->state=="Disconnected") - return("1 already disconnected"); - if(!$this->force_close - && $this->keep_alive - && !$force - && $this->state == 'ResponseReceived') - { - if($this->debug) - $this->OutputDebug('Keeping the connection alive to '.$this->connected_host); - $this->state = 'Connected'; - return(''); - } - return($this->Disconnect()); - } - - public Function PickCookies(&$cookies,$secure) - { - if (IsSet($this->cookies[$secure])) { - $now=gmdate("Y-m-d H-i-s"); - for ($domain=0,Reset($this->cookies[$secure]);$domaincookies[$secure]);Next($this->cookies[$secure]),$domain++) { - $domain_pattern=Key($this->cookies[$secure]); - $match=strlen($this->request_host)-strlen($domain_pattern); - if($match>=0 - && !strcmp($domain_pattern,substr($this->request_host,$match)) - && ($match==0 - || $domain_pattern[0]=="." - || $this->request_host[$match-1]==".")) - { - for (Reset($this->cookies[$secure][$domain_pattern]),$path_part=0;$path_partcookies[$secure][$domain_pattern]);Next($this->cookies[$secure][$domain_pattern]),$path_part++) { - $path=Key($this->cookies[$secure][$domain_pattern]); - if(strlen($this->request_uri)>=strlen($path) - && substr($this->request_uri,0,strlen($path))==$path) - { - for (Reset($this->cookies[$secure][$domain_pattern][$path]),$cookie=0;$cookiecookies[$secure][$domain_pattern][$path]);Next($this->cookies[$secure][$domain_pattern][$path]),$cookie++) { - $cookie_name=Key($this->cookies[$secure][$domain_pattern][$path]); - $expires=$this->cookies[$secure][$domain_pattern][$path][$cookie_name]["expires"]; - if($expires=="" - || strcmp($now,$expires)<0) - $cookies[$cookie_name]=$this->cookies[$secure][$domain_pattern][$path][$cookie_name]; - } - } - } - } - } - } - } - - public Function GetFileDefinition($file, &$definition) - { - $name=""; - if(IsSet($file["FileName"])) - $name=basename($file["FileName"]); - if(IsSet($file["Name"])) - $name=$file["Name"]; - if(strlen($name)==0) - return("it was not specified the file part name"); - if (IsSet($file["Content-Type"])) { - $content_type=$file["Content-Type"]; - $type=$this->Tokenize(strtolower($content_type),"/"); - $sub_type=$this->Tokenize(""); - switch ($type) { - case "text": - case "image": - case "audio": - case "video": - case "application": - case "message": - break; - case "automatic": - switch ($sub_type) { - case "name": - switch (GetType($dot=strrpos($name,"."))=="integer" ? strtolower(substr($name,$dot)) : "") { - case ".xls": - $content_type="application/excel"; - break; - case ".hqx": - $content_type="application/macbinhex40"; - break; - case ".doc": - case ".dot": - case ".wrd": - $content_type="application/msword"; - break; - case ".pdf": - $content_type="application/pdf"; - break; - case ".pgp": - $content_type="application/pgp"; - break; - case ".ps": - case ".eps": - case ".ai": - $content_type="application/postscript"; - break; - case ".ppt": - $content_type="application/powerpoint"; - break; - case ".rtf": - $content_type="application/rtf"; - break; - case ".tgz": - case ".gtar": - $content_type="application/x-gtar"; - break; - case ".gz": - $content_type="application/x-gzip"; - break; - case ".php": - case ".php3": - $content_type="application/x-httpd-php"; - break; - case ".js": - $content_type="application/x-javascript"; - break; - case ".ppd": - case ".psd": - $content_type="application/x-photoshop"; - break; - case ".swf": - case ".swc": - case ".rf": - $content_type="application/x-shockwave-flash"; - break; - case ".tar": - $content_type="application/x-tar"; - break; - case ".zip": - $content_type="application/zip"; - break; - case ".mid": - case ".midi": - case ".kar": - $content_type="audio/midi"; - break; - case ".mp2": - case ".mp3": - case ".mpga": - $content_type="audio/mpeg"; - break; - case ".ra": - $content_type="audio/x-realaudio"; - break; - case ".wav": - $content_type="audio/wav"; - break; - case ".bmp": - $content_type="image/bitmap"; - break; - case ".gif": - $content_type="image/gif"; - break; - case ".iff": - $content_type="image/iff"; - break; - case ".jb2": - $content_type="image/jb2"; - break; - case ".jpg": - case ".jpe": - case ".jpeg": - $content_type="image/jpeg"; - break; - case ".jpx": - $content_type="image/jpx"; - break; - case ".png": - $content_type="image/png"; - break; - case ".tif": - case ".tiff": - $content_type="image/tiff"; - break; - case ".wbmp": - $content_type="image/vnd.wap.wbmp"; - break; - case ".xbm": - $content_type="image/xbm"; - break; - case ".css": - $content_type="text/css"; - break; - case ".txt": - $content_type="text/plain"; - break; - case ".htm": - case ".html": - $content_type="text/html"; - break; - case ".xml": - $content_type="text/xml"; - break; - case ".mpg": - case ".mpe": - case ".mpeg": - $content_type="video/mpeg"; - break; - case ".qt": - case ".mov": - $content_type="video/quicktime"; - break; - case ".avi": - $content_type="video/x-ms-video"; - break; - case ".eml": - $content_type="message/rfc822"; - break; - default: - $content_type="application/octet-stream"; - break; - } - break; - default: - return($content_type." is not a supported automatic content type detection method"); - } - break; - default: - return($content_type." is not a supported file content type"); - } - } else - $content_type="application/octet-stream"; - $definition=array( - "Content-Type"=>$content_type, - "NAME"=>$name - ); - if (IsSet($file["FileName"])) { - if (GetType($length=@filesize($file["FileName"]))!="integer") { - $error="it was not possible to determine the length of the file ".$file["FileName"]; - if(IsSet($php_errormsg) - && strlen($php_errormsg)) - $error.=": ".$php_errormsg; - if(!file_exists($file["FileName"])) - $error="it was not possible to access the file ".$file["FileName"]; - return($error); - } - $definition["FILENAME"]=$file["FileName"]; - $definition["Content-Length"]=$length; - } elseif(IsSet($file["Data"])) - $definition["Content-Length"]=strlen($definition["DATA"]=$file["Data"]); - else - return("it was not specified a valid file name"); - return(""); - } - - public Function ConnectFromProxy($arguments, &$headers) - { - if(!$this->PutLine('CONNECT '.$this->host_name.':'.($this->host_port ? $this->host_port : 443).' HTTP/1.0') - || (strlen($this->user_agent) - && !$this->PutLine('User-Agent: '.$this->user_agent)) - || (strlen($this->accept) - && !$this->PutLine('Accept: '.$this->accept)) - || (IsSet($arguments['Headers']['Proxy-Authorization']) - && !$this->PutLine('Proxy-Authorization: '.$arguments['Headers']['Proxy-Authorization'])) - || !$this->PutLine('')) - { - $this->Disconnect(); - return($this->error); - } - $this->state = "ConnectSent"; - if(strlen($error=$this->ReadReplyHeadersResponse($headers))) - return($error); - $proxy_authorization=""; - while (!strcmp($this->response_status, "100")) { - $this->state="ConnectSent"; - if(strlen($error=$this->ReadReplyHeadersResponse($headers))) - return($error); - } - switch ($this->response_status) { - case "200": - if (!@stream_socket_enable_crypto($this->connection, 1, STREAM_CRYPTO_METHOD_SSLv23_CLIENT)) { - $this->SetPHPError('it was not possible to start a SSL encrypted connection via this proxy', $php_errormsg, HTTP_CLIENT_ERROR_COMMUNICATION_FAILURE); - $this->Disconnect(); - return($this->error); - } - $this->state = "Connected"; - break; - case "407": - if(strlen($error=$this->Authenticate($headers, -1, $proxy_authorization, $this->proxy_request_user, $this->proxy_request_password, $this->proxy_request_realm, $this->proxy_request_workstation))) - return($error); - break; - default: - return($this->SetError("unable to send request via proxy", HTTP_CLIENT_ERROR_PROTOCOL_FAILURE)); - } - return(""); - } - - public Function SendRequest($arguments) - { - if(strlen($this->error)) - return($this->error); - if(IsSet($arguments["ProxyUser"])) - $this->proxy_request_user=$arguments["ProxyUser"]; - elseif(IsSet($this->proxy_user)) - $this->proxy_request_user=$this->proxy_user; - if(IsSet($arguments["ProxyPassword"])) - $this->proxy_request_password=$arguments["ProxyPassword"]; - elseif(IsSet($this->proxy_password)) - $this->proxy_request_password=$this->proxy_password; - if(IsSet($arguments["ProxyRealm"])) - $this->proxy_request_realm=$arguments["ProxyRealm"]; - elseif(IsSet($this->proxy_realm)) - $this->proxy_request_realm=$this->proxy_realm; - if(IsSet($arguments["ProxyWorkstation"])) - $this->proxy_request_workstation=$arguments["ProxyWorkstation"]; - elseif(IsSet($this->proxy_workstation)) - $this->proxy_request_workstation=$this->proxy_workstation; - switch ($this->state) { - case "Disconnected": - return($this->SetError("connection was not yet established", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - case "Connected": - $connect = 0; - break; - case "ConnectedToProxy": - if(strlen($error = $this->ConnectFromProxy($arguments, $headers))) - return($error); - $connect = 1; - break; - default: - return($this->SetError("can not send request in the current connection state", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - } - if(IsSet($arguments["RequestMethod"])) - $this->request_method=$arguments["RequestMethod"]; - if(IsSet($arguments["User-Agent"])) - $this->user_agent=$arguments["User-Agent"]; - if(!IsSet($arguments["Headers"]["User-Agent"]) - && strlen($this->user_agent)) - $arguments["Headers"]["User-Agent"]=$this->user_agent; - if(IsSet($arguments["KeepAlive"])) - $this->keep_alive=intval($arguments["KeepAlive"]); - if(!IsSet($arguments["Headers"]["Connection"]) - && $this->keep_alive) - $arguments["Headers"]["Connection"]='Keep-Alive'; - if(IsSet($arguments["Accept"])) - $this->user_agent=$arguments["Accept"]; - if(!IsSet($arguments["Headers"]["Accept"]) - && strlen($this->accept)) - $arguments["Headers"]["Accept"]=$this->accept; - if(strlen($this->request_method)==0) - return($this->SetError("it was not specified a valid request method", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - if(IsSet($arguments["RequestURI"])) - $this->request_uri=$arguments["RequestURI"]; - if(strlen($this->request_uri)==0 - || substr($this->request_uri,0,1)!="/") - return($this->SetError("it was not specified a valid request URI", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - $this->request_arguments=$arguments; - $this->request_headers=(IsSet($arguments["Headers"]) ? $arguments["Headers"] : array()); - $body_length=0; - $this->request_body=""; - $get_body=1; - if($this->request_method=="POST" - || $this->request_method=="PUT") - { - if (IsSet($arguments['StreamRequest'])) { - $get_body = 0; - $this->request_headers["Transfer-Encoding"]="chunked"; - } elseif(IsSet($arguments["PostFiles"]) - || ($this->force_multipart_form_post - && IsSet($arguments["PostValues"]))) - { - $boundary="--".md5(uniqid(time())); - $this->request_headers["Content-Type"]="multipart/form-data; boundary=".$boundary.(IsSet($arguments["CharSet"]) ? "; charset=".$arguments["CharSet"] : ""); - $post_parts=array(); - if (IsSet($arguments["PostValues"])) { - $values=$arguments["PostValues"]; - if(GetType($values)!="array") - return($this->SetError("it was not specified a valid POST method values array", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - for (Reset($values),$value=0;$value$headers,"DATA"=>$data); - $body_length+=strlen($headers)+strlen($data)+strlen("\r\n"); - } - } - $body_length+=strlen("--".$boundary."--\r\n"); - $files=(IsSet($arguments["PostFiles"]) ? $arguments["PostFiles"] : array()); - Reset($files); - $end=(GetType($input=Key($files))!="string"); - for (;!$end;) { - if(strlen($error=$this->GetFileDefinition($files[$input],$definition))) - return("3 ".$error); - $headers="--".$boundary."\r\nContent-Disposition: form-data; name=\"".$input."\"; filename=\"".$definition["NAME"]."\"\r\nContent-Type: ".$definition["Content-Type"]."\r\n\r\n"; - $part=count($post_parts); - $post_parts[$part]=array("HEADERS"=>$headers); - if (IsSet($definition["FILENAME"])) { - $post_parts[$part]["FILENAME"]=$definition["FILENAME"]; - $data=""; - } else - $data=$definition["DATA"]; - $post_parts[$part]["DATA"]=$data; - $body_length+=strlen($headers)+$definition["Content-Length"]+strlen("\r\n"); - Next($files); - $end=(GetType($input=Key($files))!="string"); - } - $get_body=0; - } elseif (IsSet($arguments["PostValues"])) { - $values=$arguments["PostValues"]; - if(GetType($values)!="array") - return($this->SetError("it was not specified a valid POST method values array", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - for (Reset($values),$value=0;$value0) - $this->request_body.="&"; - $this->request_body.=UrlEncode($k)."=".UrlEncode($values[$k][$v]); - } - } else { - if($value>0) - $this->request_body.="&"; - $this->request_body.=UrlEncode($k)."=".UrlEncode($values[$k]); - } - } - $this->request_headers["Content-Type"]="application/x-www-form-urlencoded".(IsSet($arguments["CharSet"]) ? "; charset=".$arguments["CharSet"] : ""); - $get_body=0; - } - } - if($get_body - && (IsSet($arguments["Body"]) - || IsSet($arguments["BodyStream"]))) - { - if(IsSet($arguments["Body"])) - $this->request_body=$arguments["Body"]; - else { - $stream=$arguments["BodyStream"]; - $this->request_body=""; - for ($part=0; $partrequest_body.=$stream[$part]["Data"]; - elseif (IsSet($stream[$part]["File"])) { - if(!($file=@fopen($stream[$part]["File"],"rb"))) - return($this->SetPHPError("could not open upload file ".$stream[$part]["File"], $php_errormsg, HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE)); - while (!feof($file)) { - if (GetType($block=@fread($file,$this->file_buffer_length))!="string") { - $error=$this->SetPHPError("could not read body stream file ".$stream[$part]["File"], $php_errormsg, HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE); - fclose($file); - return($error); - } - $this->request_body.=$block; - } - fclose($file); - } else - return("5 it was not specified a valid file or data body stream element at position ".$part); - } - } - if(!IsSet($this->request_headers["Content-Type"])) - $this->request_headers["Content-Type"]="application/octet-stream".(IsSet($arguments["CharSet"]) ? "; charset=".$arguments["CharSet"] : ""); - } - if(IsSet($arguments["AuthUser"])) - $this->request_user=$arguments["AuthUser"]; - elseif(IsSet($this->user)) - $this->request_user=$this->user; - if(IsSet($arguments["AuthPassword"])) - $this->request_password=$arguments["AuthPassword"]; - elseif(IsSet($this->password)) - $this->request_password=$this->password; - if(IsSet($arguments["AuthRealm"])) - $this->request_realm=$arguments["AuthRealm"]; - elseif(IsSet($this->realm)) - $this->request_realm=$this->realm; - if(IsSet($arguments["AuthWorkstation"])) - $this->request_workstation=$arguments["AuthWorkstation"]; - elseif(IsSet($this->workstation)) - $this->request_workstation=$this->workstation; - if(strlen($this->proxy_host_name)==0 - || $connect) - $request_uri=$this->request_uri; - else { - switch (strtolower($this->protocol)) { - case "http": - $default_port=80; - break; - case "https": - $default_port=443; - break; - } - $request_uri=strtolower($this->protocol)."://".$this->host_name.(($this->host_port==0 || $this->host_port==$default_port) ? "" : ":".$this->host_port).$this->request_uri; - } - if ($this->use_curl) { - $version=(GetType($v=curl_version())=="array" ? (IsSet($v["version"]) ? $v["version"] : "0.0.0") : (preg_match("/^libcurl\\/([0-9]+\\.[0-9]+\\.[0-9]+)/",$v,$m) ? $m[1] : "0.0.0")); - $curl_version=100000*intval($this->Tokenize($version,"."))+1000*intval($this->Tokenize("."))+intval($this->Tokenize("")); - $protocol_version=($curl_version<713002 ? "1.0" : $this->protocol_version); - } else - $protocol_version=$this->protocol_version; - $this->request=$this->request_method." ".$request_uri." HTTP/".$protocol_version; - if($body_length - || ($body_length=strlen($this->request_body))) - $this->request_headers["Content-Length"]=$body_length; - for ($headers=array(),$host_set=0,Reset($this->request_headers),$header=0;$headerrequest_headers);Next($this->request_headers),$header++) { - $header_name=Key($this->request_headers); - $header_value=$this->request_headers[$header_name]; - if (GetType($header_value)=="array") { - for(Reset($header_value),$value=0;$valuerequest_headers))=="host") { - $this->request_host=strtolower($header_value); - $host_set=1; - } - } - if (!$host_set) { - $headers[]="Host: ".$this->host_name; - $this->request_host=strtolower($this->host_name); - } - if (count($this->cookies)) { - $cookies=array(); - $this->PickCookies($cookies,0); - if(strtolower($this->protocol)=="https") - $this->PickCookies($cookies,1); - if (count($cookies)) { - $h=count($headers); - $headers[$h]="Cookie:"; - for (Reset($cookies),$cookie=0;$cookieuse_curl) { - if(IsSet($arguments['StreamRequest'])) - return($this->SetError("Streaming request data is not supported when using Curl", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - if($body_length - && strlen($this->request_body)==0) - { - for ($request_body="",$success=1,$part=0;$partSetPHPError("could not open upload file ".$post_parts[$part]["FILENAME"], $php_errormsg, HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE); - $success=0; - break; - } - while (!feof($file)) { - if (GetType($block=@fread($file,$this->file_buffer_length))!="string") { - $this->SetPHPError("could not read upload file", $php_errormsg, HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE); - $success=0; - break; - } - $request_body.=$block; - } - fclose($file); - if(!$success) - break; - } - $request_body.="\r\n"; - } - $request_body.="--".$boundary."--\r\n"; - } else - $request_body=$this->request_body; - curl_setopt($this->connection,CURLOPT_HEADER,1); - curl_setopt($this->connection,CURLOPT_RETURNTRANSFER,1); - if($this->timeout) - curl_setopt($this->connection,CURLOPT_TIMEOUT,$this->timeout); - curl_setopt($this->connection,CURLOPT_SSL_VERIFYPEER,0); - curl_setopt($this->connection,CURLOPT_SSL_VERIFYHOST,0); - $request=$this->request."\r\n".implode("\r\n",$headers)."\r\n\r\n".$request_body; - curl_setopt($this->connection,CURLOPT_CUSTOMREQUEST,$request); - if($this->debug) - $this->OutputDebug("C ".$request); - if (!($success=(strlen($this->response=curl_exec($this->connection))!=0))) { - $error=curl_error($this->connection); - $this->SetError("Could not execute the request".(strlen($error) ? ": ".$error : ""), HTTP_CLIENT_ERROR_PROTOCOL_FAILURE); - } - } else { - if (($success=$this->PutLine($this->request))) { - for ($header=0;$headerPutLine($headers[$header])) - break; - } - if($success - && ($success=$this->PutLine(""))) - { - if(IsSet($arguments['StreamRequest'])) - $next_state = "SendingRequestBody"; - elseif ($body_length) { - if(strlen($this->request_body)) - $success=$this->PutData($this->request_body); - else { - for ($part=0;$partPutData($post_parts[$part]["HEADERS"])) - || !($success=$this->PutData($post_parts[$part]["DATA"]))) - break; - if (IsSet($post_parts[$part]["FILENAME"])) { - if (!($file=@fopen($post_parts[$part]["FILENAME"],"rb"))) { - $this->SetPHPError("could not open upload file ".$post_parts[$part]["FILENAME"], $php_errormsg, HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE); - $success=0; - break; - } - while (!feof($file)) { - if (GetType($block=@fread($file,$this->file_buffer_length))!="string") { - $this->SetPHPError("could not read upload file", $php_errormsg, HTTP_CLIENT_ERROR_CANNOT_ACCESS_LOCAL_FILE); - $success=0; - break; - } - if(!($success=$this->PutData($block))) - break; - } - fclose($file); - if(!$success) - break; - } - if(!($success=$this->PutLine(""))) - break; - } - if($success) - $success=$this->PutLine("--".$boundary."--"); - } - if($success) - $sucess=$this->FlushData(); - } - } - } - } - if(!$success) - return($this->SetError("could not send the HTTP request: ".$this->error, $this->error_code)); - $this->state=$next_state; - return(""); - } - - public Function SetCookie($name, $value, $expires="" , $path="/" , $domain="" , $secure=0, $verbatim=0) - { - if(strlen($this->error)) - return($this->error); - if(strlen($name)==0) - return($this->SetError("it was not specified a valid cookie name", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - if(strlen($path)==0 - || strcmp($path[0],"/")) - return($this->SetError($path." is not a valid path for setting cookie ".$name, HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - if($domain=="" - || !strpos($domain,".",$domain[0]=="." ? 1 : 0)) - return($this->SetError($domain." is not a valid domain for setting cookie ".$name, HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - $domain=strtolower($domain); - if(!strcmp($domain[0],".")) - $domain=substr($domain,1); - if (!$verbatim) { - $name=$this->CookieEncode($name,1); - $value=$this->CookieEncode($value,0); - } - $secure=intval($secure); - $this->cookies[$secure][$domain][$path][$name]=array( - "name"=>$name, - "value"=>$value, - "domain"=>$domain, - "path"=>$path, - "expires"=>$expires, - "secure"=>$secure - ); - return(""); - } - - public Function SendRequestBody($data, $end_of_data) - { - if(strlen($this->error)) - return($this->error); - switch ($this->state) { - case "Disconnected": - return($this->SetError("connection was not yet established", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - case "Connected": - case "ConnectedToProxy": - return($this->SetError("request was not sent", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - case "SendingRequestBody": - break; - case "RequestSent": - return($this->SetError("request body was already sent", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - default: - return($this->SetError("can not send the request body in the current connection state", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - } - $length = strlen($data); - if ($length) { - $size = dechex($length)."\r\n"; - if(!$this->PutData($size) - || !$this->PutData($data)) - return($this->error); - } - if ($end_of_data) { - $size = "0\r\n"; - if(!$this->PutData($size)) - return($this->error); - $this->state = "RequestSent"; - } - return(""); - } - - public Function ReadReplyHeadersResponse(&$headers) - { - $headers=array(); - if(strlen($this->error)) - return($this->error); - switch ($this->state) { - case "Disconnected": - return($this->SetError("connection was not yet established", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - case "Connected": - return($this->SetError("request was not sent", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - case "ConnectedToProxy": - return($this->SetError("connection from the remote server from the proxy was not yet established", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - case "SendingRequestBody": - return($this->SetError("request body data was not completely sent", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - case "ConnectSent": - $connect = 1; - break; - case "RequestSent": - $connect = 0; - break; - default: - return($this->SetError("can not get request headers in the current connection state", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - } - $this->content_length=$this->read_length=$this->read_response=$this->remaining_chunk=0; - $this->content_length_set=$this->chunked=$this->last_chunk_read=$chunked=0; - $this->force_close = $this->connection_close=0; - for ($this->response_status="";;) { - $line=$this->GetLine(); - if(GetType($line)!="string") - return($this->SetError("could not read request reply: ".$this->error, $this->error_code)); - if (strlen($this->response_status)==0) { - if(!preg_match($match="/^http\\/[0-9]+\\.[0-9]+[ \t]+([0-9]+)[ \t]*(.*)\$/i",$line,$matches)) - return($this->SetError("it was received an unexpected HTTP response status", HTTP_CLIENT_ERROR_PROTOCOL_FAILURE)); - $this->response_status=$matches[1]; - $this->response_message=$matches[2]; - } - if ($line=="") { - if(strlen($this->response_status)==0) - return($this->SetError("it was not received HTTP response status", HTTP_CLIENT_ERROR_PROTOCOL_FAILURE)); - $this->state=($connect ? "GotConnectHeaders" : "GotReplyHeaders"); - break; - } - $header_name=strtolower($this->Tokenize($line,":")); - $header_value=Trim(Chop($this->Tokenize("\r\n"))); - if (IsSet($headers[$header_name])) { - if(GetType($headers[$header_name])=="string") - $headers[$header_name]=array($headers[$header_name]); - $headers[$header_name][]=$header_value; - } else - $headers[$header_name]=$header_value; - if (!$connect) { - switch ($header_name) { - case "content-length": - $this->content_length=intval($headers[$header_name]); - $this->content_length_set=1; - break; - case "transfer-encoding": - $encoding=$this->Tokenize($header_value,"; \t"); - if(!$this->use_curl - && !strcmp($encoding,"chunked")) - $chunked=1; - break; - case "set-cookie": - if ($this->support_cookies) { - if(GetType($headers[$header_name])=="array") - $cookie_headers=$headers[$header_name]; - else - $cookie_headers=array($headers[$header_name]); - for ($cookie=0;$cookieTokenize($cookie_headers[$cookie],"=")); - $cookie_value=$this->Tokenize(";"); - $domain=$this->request_host; - $path="/"; - $expires=""; - $secure=0; - while (($name = strtolower(trim(UrlDecode($this->Tokenize("=")))))!="") { - $value=UrlDecode($this->Tokenize(";")); - switch ($name) { - case "domain": - $domain=$value; - break; - case "path": - $path=$value; - break; - case "expires": - if (preg_match("/^((Mon|Monday|Tue|Tuesday|Wed|Wednesday|Thu|Thursday|Fri|Friday|Sat|Saturday|Sun|Sunday), )?([0-9]{2})\\-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\-([0-9]{2,4}) ([0-9]{2})\\:([0-9]{2})\\:([0-9]{2}) GMT\$/",$value,$matches)) { - $year=intval($matches[5]); - if($year<1900) - $year+=($year<70 ? 2000 : 1900); - $expires="$year-".$this->months[$matches[4]]."-".$matches[3]." ".$matches[6].":".$matches[7].":".$matches[8]; - } - break; - case "secure": - $secure=1; - break; - } - } - if(strlen($this->SetCookie($cookie_name, $cookie_value, $expires, $path , $domain, $secure, 1))) - $this->error=""; - } - } - break; - case "connection": - $this->force_close = $this->connection_close=!strcmp(strtolower($header_value),"close"); - break; - } - } - } - $this->chunked=$chunked; - if($this->content_length_set) - $this->connection_close=0; - return(""); - } - - public Function Redirect(&$headers) - { - if ($this->follow_redirect) { - if(!IsSet($headers["location"]) - || (GetType($headers["location"])!="array" - && strlen($location=$headers["location"])==0) - || (GetType($headers["location"])=="array" - && strlen($location=$headers["location"][0])==0)) - return($this->SetError("it was received a redirect without location URL", HTTP_CLIENT_ERROR_PROTOCOL_FAILURE)); - if (strcmp($location[0],"/")) { - if(!($location_arguments=@parse_url($location))) - return($this->SetError("the server did not return a valid redirection location URL", HTTP_CLIENT_ERROR_PROTOCOL_FAILURE)); - if(!IsSet($location_arguments["scheme"])) - $location=((GetType($end=strrpos($this->request_uri,"/"))=="integer" && $end>1) ? substr($this->request_uri,0,$end) : "")."/".$location; - } - if(!strcmp($location[0],"/")) - $location=$this->protocol."://".$this->host_name.($this->host_port ? ":".$this->host_port : "").$location; - $error=$this->GetRequestArguments($location,$arguments); - if(strlen($error)) - return($this->SetError("could not process redirect url: ".$error, HTTP_CLIENT_ERROR_PROTOCOL_FAILURE)); - $arguments["RequestMethod"]="GET"; - if(strlen($error=$this->Close())==0 - && strlen($error=$this->Open($arguments))==0 - && strlen($error=$this->SendRequest($arguments))==0) - { - $this->redirection_level++; - if ($this->redirection_level>$this->redirection_limit) { - $error="it was exceeded the limit of request redirections"; - $this->error_code = HTTP_CLIENT_ERROR_PROTOCOL_FAILURE; - } else - $error=$this->ReadReplyHeaders($headers); - $this->redirection_level--; - } - if(strlen($error)) - return($this->SetError($error, $this->error_code)); - } - return(""); - } - - public Function Authenticate(&$headers, $proxy, &$proxy_authorization, &$user, &$password, &$realm, &$workstation) - { - if ($proxy) { - $authenticate_header="proxy-authenticate"; - $authorization_header="Proxy-Authorization"; - $authenticate_status="407"; - $authentication_mechanism=$this->proxy_authentication_mechanism; - } else { - $authenticate_header="www-authenticate"; - $authorization_header="Authorization"; - $authenticate_status="401"; - $authentication_mechanism=$this->authentication_mechanism; - } - if (IsSet($headers[$authenticate_header])) { - if(function_exists("class_exists") - && !class_exists("sasl_client_class")) - return($this->SetError("the SASL client class needs to be loaded to be able to authenticate".($proxy ? " with the proxy server" : "")." and access this site", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - if(GetType($headers[$authenticate_header])=="array") - $authenticate=$headers[$authenticate_header]; - else - $authenticate=array($headers[$authenticate_header]); - for ($response="", $mechanisms=array(),$m=0;$mTokenize($authenticate[$m]," "); - $response=$this->Tokenize(""); - if (strlen($authentication_mechanism)) { - if (!strcmp($authentication_mechanism,$mechanism)) { - $mechanisms[]=$mechanism; - break; - } - } else - $mechanisms[]=$mechanism; - } - $sasl=new sasl_client_class; - if(IsSet($user)) - $sasl->SetCredential("user",$user); - if(IsSet($password)) - $sasl->SetCredential("password",$password); - if(IsSet($realm)) - $sasl->SetCredential("realm",$realm); - if(IsSet($workstation)) - $sasl->SetCredential("workstation",$workstation); - $sasl->SetCredential("uri",$this->request_uri); - $sasl->SetCredential("method",$this->request_method); - $sasl->SetCredential("session",$this->session); - do { - $status=$sasl->Start($mechanisms,$message,$interactions); - } while ($status==SASL_INTERACT); - switch ($status) { - case SASL_CONTINUE: - break; - case SASL_NOMECH: - return($this->SetError(($proxy ? "proxy " : "")."authentication error: ".(strlen($authentication_mechanism) ? "authentication mechanism ".$authentication_mechanism." may not be used: " : "").$sasl->error, HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - default: - return($this->SetError("Could not start the SASL ".($proxy ? "proxy " : "")."authentication client: ".$sasl->error, HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - } - if ($proxy >= 0) { - for (;;) { - if(strlen($error=$this->ReadReplyBody($body,$this->file_buffer_length))) - return($error); - if(strlen($body)==0) - break; - } - } - $authorization_value=$sasl->mechanism.(IsSet($message) ? " ".($sasl->encode_response ? base64_encode($message) : $message) : ""); - $request_arguments=$this->request_arguments; - $arguments=$request_arguments; - $arguments["Headers"][$authorization_header]=$authorization_value; - if(!$proxy - && strlen($proxy_authorization)) - $arguments["Headers"]["Proxy-Authorization"]=$proxy_authorization; - if(strlen($error=$this->Close()) - || strlen($error=$this->Open($arguments))) - return($this->SetError($error, $this->error_code)); - $authenticated=0; - if (IsSet($message)) { - if ($proxy < 0) { - if(strlen($error=$this->ConnectFromProxy($arguments, $headers))) - return($this->SetError($error, $this->error_code)); - } else { - if(strlen($error=$this->SendRequest($arguments)) - || strlen($error=$this->ReadReplyHeadersResponse($headers))) - return($this->SetError($error, $this->error_code)); - } - if(!IsSet($headers[$authenticate_header])) - $authenticate=array(); - elseif(GetType($headers[$authenticate_header])=="array") - $authenticate=$headers[$authenticate_header]; - else - $authenticate=array($headers[$authenticate_header]); - for ($mechanism=0;$mechanismTokenize($authenticate[$mechanism]," "),$sasl->mechanism)) { - $response=$this->Tokenize(""); - break; - } - } - switch ($this->response_status) { - case $authenticate_status: - break; - case "301": - case "302": - case "303": - case "307": - if($proxy >= 0) - return($this->Redirect($headers)); - default: - if (intval($this->response_status/100)==2) { - if($proxy) - $proxy_authorization=$authorization_value; - $authenticated=1; - break; - } - if($proxy - && !strcmp($this->response_status,"401")) - { - $proxy_authorization=$authorization_value; - $authenticated=1; - break; - } - return($this->SetError(($proxy ? "proxy " : "")."authentication error: ".$this->response_status." ".$this->response_message, HTTP_CLIENT_ERROR_PROTOCOL_FAILURE)); - } - } - for (;!$authenticated;) { - do { - $status=$sasl->Step($response,$message,$interactions); - } while ($status==SASL_INTERACT); - switch ($status) { - case SASL_CONTINUE: - $authorization_value=$sasl->mechanism.(IsSet($message) ? " ".($sasl->encode_response ? base64_encode($message) : $message) : ""); - $arguments=$request_arguments; - $arguments["Headers"][$authorization_header]=$authorization_value; - if(!$proxy - && strlen($proxy_authorization)) - $arguments["Headers"]["Proxy-Authorization"]=$proxy_authorization; - if ($proxy < 0) { - if(strlen($error=$this->ConnectFromProxy($arguments, $headers))) - return($this->SetError($error, $this->error_code)); - } else { - if(strlen($error=$this->SendRequest($arguments)) - || strlen($error=$this->ReadReplyHeadersResponse($headers))) - return($this->SetError($error, $this->error_code)); - } - switch ($this->response_status) { - case $authenticate_status: - if(GetType($headers[$authenticate_header])=="array") - $authenticate=$headers[$authenticate_header]; - else - $authenticate=array($headers[$authenticate_header]); - for ($response="",$mechanism=0;$mechanismTokenize($authenticate[$mechanism]," "),$sasl->mechanism)) { - $response=$this->Tokenize(""); - break; - } - } - if ($proxy >= 0) { - for (;;) { - if(strlen($error=$this->ReadReplyBody($body,$this->file_buffer_length))) - return($error); - if(strlen($body)==0) - break; - } - } - $this->state="Connected"; - break; - case "301": - case "302": - case "303": - case "307": - if($proxy >= 0) - return($this->Redirect($headers)); - default: - if (intval($this->response_status/100)==2) { - if($proxy) - $proxy_authorization=$authorization_value; - $authenticated=1; - break; - } - if($proxy - && !strcmp($this->response_status,"401")) - { - $proxy_authorization=$authorization_value; - $authenticated=1; - break; - } - return($this->SetError(($proxy ? "proxy " : "")."authentication error: ".$this->response_status." ".$this->response_message)); - } - break; - default: - return($this->SetError("Could not process the SASL ".($proxy ? "proxy " : "")."authentication step: ".$sasl->error, HTTP_CLIENT_ERROR_PROTOCOL_FAILURE)); - } - } - } - return(""); - } - - public Function ReadReplyHeaders(&$headers) - { - if(strlen($error=$this->ReadReplyHeadersResponse($headers))) - return($error); - $proxy_authorization=""; - while (!strcmp($this->response_status, "100")) { - $this->state="RequestSent"; - if(strlen($error=$this->ReadReplyHeadersResponse($headers))) - return($error); - } - switch ($this->response_status) { - case "301": - case "302": - case "303": - case "307": - if(strlen($error=$this->Redirect($headers))) - return($error); - break; - case "407": - if(strlen($error=$this->Authenticate($headers, 1, $proxy_authorization, $this->proxy_request_user, $this->proxy_request_password, $this->proxy_request_realm, $this->proxy_request_workstation))) - return($error); - if(strcmp($this->response_status,"401")) - return(""); - case "401": - return($this->Authenticate($headers, 0, $proxy_authorization, $this->request_user, $this->request_password, $this->request_realm, $this->request_workstation)); - } - return(""); - } - - public Function ReadReplyBody(&$body,$length) - { - $body=""; - if(strlen($this->error)) - return($this->error); - switch ($this->state) { - case "Disconnected": - return($this->SetError("connection was not yet established", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - case "Connected": - case "ConnectedToProxy": - return($this->SetError("request was not sent", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - case "RequestSent": - if(($error=$this->ReadReplyHeaders($headers))!="") - return($error); - break; - case "GotReplyHeaders": - break; - case 'ResponseReceived': - $body = ''; - return(''); - default: - return($this->SetError("can not get request headers in the current connection state", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - } - if($this->content_length_set) - $length=min($this->content_length-$this->read_length,$length); - $body = ''; - if ($length>0) { - if(!$this->EndOfInput() - && ($body=$this->ReadBytes($length))=="") - { - if(strlen($this->error)) - return($this->SetError("could not get the request reply body: ".$this->error, $this->error_code)); - } - $this->read_length+=strlen($body); - if($this->EndOfInput()) - $this->state = 'ResponseReceived'; - } - return(""); - } - - public Function ReadWholeReplyBody(&$body) - { - $body = ''; - for (;;) { - if(strlen($error = $this->ReadReplyBody($block, $this->file_buffer_length))) - return($error); - if(strlen($block) == 0) - return(''); - $body .= $block; - } - } - - public Function SaveCookies(&$cookies, $domain='', $secure_only=0, $persistent_only=0) - { - $now=gmdate("Y-m-d H-i-s"); - $cookies=array(); - for ($secure_cookies=0,Reset($this->cookies);$secure_cookiescookies);Next($this->cookies),$secure_cookies++) { - $secure=Key($this->cookies); - if(!$secure_only - || $secure) - { - for ($cookie_domain=0,Reset($this->cookies[$secure]);$cookie_domaincookies[$secure]);Next($this->cookies[$secure]),$cookie_domain++) { - $domain_pattern=Key($this->cookies[$secure]); - $match=strlen($domain)-strlen($domain_pattern); - if(strlen($domain)==0 - || ($match>=0 - && !strcmp($domain_pattern,substr($domain,$match)) - && ($match==0 - || $domain_pattern[0]=="." - || $domain[$match-1]=="."))) - { - for (Reset($this->cookies[$secure][$domain_pattern]),$path_part=0;$path_partcookies[$secure][$domain_pattern]);Next($this->cookies[$secure][$domain_pattern]),$path_part++) { - $path=Key($this->cookies[$secure][$domain_pattern]); - for (Reset($this->cookies[$secure][$domain_pattern][$path]),$cookie=0;$cookiecookies[$secure][$domain_pattern][$path]);Next($this->cookies[$secure][$domain_pattern][$path]),$cookie++) { - $cookie_name=Key($this->cookies[$secure][$domain_pattern][$path]); - $expires=$this->cookies[$secure][$domain_pattern][$path][$cookie_name]["expires"]; - if((!$persistent_only - && strlen($expires)==0) - || (strlen($expires) - && strcmp($now,$expires)<0)) - $cookies[$secure][$domain_pattern][$path][$cookie_name]=$this->cookies[$secure][$domain_pattern][$path][$cookie_name]; - } - } - } - } - } - } - } - - public Function SavePersistentCookies(&$cookies, $domain='', $secure_only=0) - { - $this->SaveCookies($cookies, $domain, $secure_only, 1); - } - - public Function GetPersistentCookies(&$cookies, $domain='', $secure_only=0) - { - $this->SavePersistentCookies($cookies, $domain, $secure_only); - } - - public Function RestoreCookies($cookies, $clear=1) - { - $new_cookies=($clear ? array() : $this->cookies); - for ($secure_cookies=0, Reset($cookies); $secure_cookiesSetError("invalid cookie secure value type (".serialize($secure).")", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - for ($cookie_domain=0,Reset($cookies[$secure]);$cookie_domainSetError("invalid cookie domain value type (".serialize($domain_pattern).")", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - for (Reset($cookies[$secure][$domain_pattern]),$path_part=0;$path_partSetError("invalid cookie path value type (".serialize($path).")", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - for (Reset($cookies[$secure][$domain_pattern][$path]),$cookie=0;$cookieSetError("invalid cookie expiry value type (".serialize($expires).")", HTTP_CLIENT_ERROR_INVALID_PARAMETERS)); - $new_cookies[$secure][$domain_pattern][$path][$cookie_name]=array( - "name"=>$cookie_name, - "value"=>$value, - "domain"=>$domain_pattern, - "path"=>$path, - "expires"=>$expires, - "secure"=>$secure - ); - } - } - } - } - $this->cookies=$new_cookies; - return(""); - } -}; diff --git a/core/src/core/classes/interface.AjxpGroupPathProvider.php b/core/src/core/classes/interface.AjxpGroupPathProvider.php deleted file mode 100644 index 5f82b57611..0000000000 --- a/core/src/core/classes/interface.AjxpGroupPathProvider.php +++ /dev/null @@ -1,43 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * @package Pydio - * @subpackage Core - */ -interface AjxpGroupPathProvider -{ - /** - * @abstract - * @return String - */ - public function getGroupPath(); - - - /** - * @abstract - * @param String $groupPath - * @return void - */ - public function setGroupPath($groupPath); - -} diff --git a/core/src/core/classes/interface.AjxpWrapper.php b/core/src/core/classes/interface.AjxpWrapper.php deleted file mode 100644 index 148ff8da18..0000000000 --- a/core/src/core/classes/interface.AjxpWrapper.php +++ /dev/null @@ -1,216 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Defines the methods that an access driver must implement to be considered as a file wrapper. - * @package Pydio - * @subpackage Core - * @interface AjxpWrapper - */ -interface AjxpWrapper -{ - /** - * Get a "usable" reference to a file : the real file or a tmp copy. - * - * @param string $path - * @param bool $persistent - */ - public static function getRealFSReference($path, $persistent = false); - - /** - * Read a file (by chunks) and copy the data directly inside the given stream. - * - * @param string $path - * @param resource $stream - */ - public static function copyFileInStream($path, $stream); - - /** - * Chmod implementation for this type of access. - * - * @param string $path - * @param number $chmodValue - */ - public static function changeMode($path, $chmodValue); - - /** - * Describe whether the current wrapper operates on a remote server or not. - * @static - * @abstract - * @return boolean - */ - public static function isRemote(); - - /** - * Describe whether the current wrapper can rewind a stream or not. - * @param String $url Url of the resource - * @static - * @abstract - * @return boolean - */ - public static function isSeekable($url); - - /** - * - * - * @return bool - */ - public function dir_closedir(); - - /** - * Enter description here... - * - * @param string $path - * @param int $options - * @return bool - */ - public function dir_opendir($path , $options); - - /** - * Enter description here... - * - * @return string - */ - public function dir_readdir(); - - /** - * Enter description here... - * - * @return bool - */ - public function dir_rewinddir(); - - /** - * Enter description here... - * - * @param string $path - * @param int $mode - * @param int $options - * @return bool - */ - public function mkdir($path , $mode , $options); - - /** - * Enter description here... - * - * @param string $path_from - * @param string $path_to - * @return bool - */ - public function rename($path_from , $path_to); - - /** - * Enter description here... - * - * @param string $path - * @param int $options - * @return bool - */ - public function rmdir($path , $options); - - /** - * Enter description here... - * - */ - public function stream_close(); - - /** - * Enter description here... - * - * @return bool - */ - public function stream_eof(); - - /** - * Enter description here... - * - * @return bool - */ - public function stream_flush(); - - /** - * Enter description here... - * - * @param string $path - * @param string $mode - * @param int $options - * @param string &$opened_path - * @return bool - */ - public function stream_open($path , $mode , $options , &$opened_path); - - /** - * Enter description here... - * - * @param int $count - * @return string - */ - public function stream_read($count); - - /** - * Enter description here... - * - * @param int $offset - * @param int $whence = SEEK_SET - * @return bool - */ - public function stream_seek($offset , $whence = SEEK_SET); - - /** - * Enter description here... - * - * @return array - */ - public function stream_stat(); - - /** - * Enter description here... - * - * @return int - */ - public function stream_tell(); - - /** - * Enter description here... - * - * @param string $data - * @return int - */ - public function stream_write($data); - - /** - * Enter description here... - * - * @param string $path - * @return bool - */ - public function unlink($path); - - /** - * Enter description here... - * - * @param string $path - * @param int $flags - * @return array - */ - public function url_stat($path , $flags); -} diff --git a/core/src/core/classes/interface.AjxpWrapperProvider.php b/core/src/core/classes/interface.AjxpWrapperProvider.php deleted file mode 100644 index 118c9018e7..0000000000 --- a/core/src/core/classes/interface.AjxpWrapperProvider.php +++ /dev/null @@ -1,75 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Interface must be implemented for access drivers that can be accessed via a wrapper protocol. - * @package Pydio - * @subpackage Core - * @interface AjxpWrapperProvider - */ -interface AjxpWrapperProvider -{ - /** - * Convert a path (from the repository root) to a fully - * qualified ajaxplorer url like ajxp.protocol://repoId/path/to/node - * @param String $path - * @return String - */ - public function getResourceUrl($path); - - /** - * Creates a directory - * @param String $path - * @param String $newDirName - */ - public function mkDir($path, $newDirName); - - /** - * Creates an empty file - * @param String $path - * @param String $newDirName - */ - public function createEmptyFile($path, $newDirName); - - /** - * @param String $from - * @param String $to - * @param Boolean $copy - */ - public function nodeChanged(&$from, &$to, $copy = false); - - /** - * @param String $node - * @param null $newSize - * @return - */ - public function nodeWillChange($node, $newSize = null); - - /** - * @param $nodePath - * @param $nodeName - * @param $isLeaf - * @param $lsOptions - * @return mixed - */ - public function filterNodeName($nodePath, $nodeName, &$isLeaf, $lsOptions); -} diff --git a/core/src/core/classes/interface.SqlTableProvider.php b/core/src/core/classes/interface.SqlTableProvider.php deleted file mode 100644 index b3b7769148..0000000000 --- a/core/src/core/classes/interface.SqlTableProvider.php +++ /dev/null @@ -1,40 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Interface SqlTableProvider - * Plugins that need to have their SQL tables installed at startup - * must implement this interface. - * @package Pydio - * @subpackage Core - * @interface SqlTableProvider - */ -interface SqlTableProvider{ - - /** - * Install SQL table using a dibi driver data - * @param $param array("SQL_DRIVER" => $dibiDriverData) - * @return mixed - */ - public function installSQLTables($param); - -} \ No newline at end of file diff --git a/core/src/core/classes/packer/class.JavaScriptPacker.php b/core/src/core/classes/packer/class.JavaScriptPacker.php deleted file mode 100644 index 98d34ebb7f..0000000000 --- a/core/src/core/classes/packer/class.JavaScriptPacker.php +++ /dev/null @@ -1,777 +0,0 @@ -pack(); - * - * or - * - * $myPacker = new JavaScriptPacker($script, 'Normal', true, false); - * $packed = $myPacker->pack(); - * - * or (default values) - * - * $myPacker = new JavaScriptPacker($script); - * $packed = $myPacker->pack(); - * - * - * params of the constructor : - * $script: the JavaScript to pack, string. - * $encoding: level of encoding, int or string : - * 0,10,62,95 or 'None', 'Numeric', 'Normal', 'High ASCII'. - * default: 62. - * $fastDecode: include the fast decoder in the packed result, boolean. - * default : true. - * $specialChars: if you are flagged your private and local variables - * in the script, boolean. - * default: false. - * - * The pack() method return the compressed JavasScript, as a string. - * - * see http://dean.edwards.name/packer/usage/ for more information. - * - * Notes : - * # need PHP 5 . Tested with PHP 5.1.2, 5.1.3, 5.1.4, 5.2.3 - * - * # The packed result may be different than with the Dean Edwards - * version, but with the same length. The reason is that the PHP - * function usort to sort array don't necessarily preserve the - * original order of two equal member. The Javascript sort function - * in fact preserve this order (but that's not require by the - * ECMAScript standard). So the encoded keywords order can be - * different in the two results. - * - * # Be careful with the 'High ASCII' Level encoding if you use - * UTF-8 in your files... - */ - - -class JavaScriptPacker -{ - // constants - const IGNORE = '$1'; - - // validate parameters - private $_script = ''; - private $_encoding = 62; - private $_fastDecode = true; - private $_specialChars = false; - - private $LITERAL_ENCODING = array( - 'None' => 0, - 'Numeric' => 10, - 'Normal' => 62, - 'High ASCII' => 95 - ); - - public function __construct($_script, $_encoding = 62, $_fastDecode = true, $_specialChars = false) - { - $this->_script = $_script . "\n"; - if (array_key_exists($_encoding, $this->LITERAL_ENCODING)) - $_encoding = $this->LITERAL_ENCODING[$_encoding]; - $this->_encoding = min((int) $_encoding, 95); - $this->_fastDecode = $_fastDecode; - $this->_specialChars = $_specialChars; - } - - public function pack() - { - $this->_addParser('_basicCompression'); - if ($this->_specialChars) - $this->_addParser('_encodeSpecialChars'); - if ($this->_encoding) - $this->_addParser('_encodeKeywords'); - - // go! - return $this->_pack($this->_script); - } - - // apply all parsing routines - private function _pack($script) - { - for ($i = 0; isset($this->_parsers[$i]); $i++) { - $script = call_user_func(array(&$this,$this->_parsers[$i]), $script); - } - return $script; - } - - // keep a list of parsing functions, they'll be executed all at once - private $_parsers = array(); - private function _addParser($parser) - { - $this->_parsers[] = $parser; - } - - // zero encoding - just removal of white space and comments - private function _basicCompression($script) - { - $parser = new ParseMaster(); - // make safe - $parser->escapeChar = '\\'; - // protect strings - $parser->add('/\'[^\'\\n\\r]*\'/', self::IGNORE); - $parser->add('/"[^"\\n\\r]*"/', self::IGNORE); - // remove comments - $parser->add('/\\/\\/[^\\n\\r]*[\\n\\r]/', ' '); - $parser->add('/\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\//', ' '); - // protect regular expressions - $parser->add('/\\s+(\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?)/', '$2'); // IGNORE - $parser->add('/[^\\w\\x24\\/\'"*)\\?:]\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?/', self::IGNORE); - // remove: ;;; doSomething(); - if ($this->_specialChars) $parser->add('/;;;[^\\n\\r]+[\\n\\r]/'); - // remove redundant semi-colons - $parser->add('/\\(;;\\)/', self::IGNORE); // protect for (;;) loops - $parser->add('/;+\\s*([};])/', '$2'); - // apply the above - $script = $parser->exec($script); - - // remove white-space - $parser->add('/(\\b|\\x24)\\s+(\\b|\\x24)/', '$2 $3'); - $parser->add('/([+\\-])\\s+([+\\-])/', '$2 $3'); - $parser->add('/\\s+/', ''); - // done - return $parser->exec($script); - } - - private function _encodeSpecialChars($script) - { - $parser = new ParseMaster(); - // replace: $name -> n, $$name -> na - $parser->add('/((\\x24+)([a-zA-Z$_]+))(\\d*)/', - array('fn' => '_replace_name') - ); - // replace: _name -> _0, double-underscore (__name) is ignored - $regexp = '/\\b_[A-Za-z\\d]\\w*/'; - // build the word list - $keywords = $this->_analyze($script, $regexp, '_encodePrivate'); - // quick ref - $encoded = $keywords['encoded']; - - $parser->add($regexp, - array( - 'fn' => '_replace_encoded', - 'data' => $encoded - ) - ); - return $parser->exec($script); - } - - private function _encodeKeywords($script) - { - // escape high-ascii values already in the script (i.e. in strings) - if ($this->_encoding > 62) - $script = $this->_escape95($script); - // create the parser - $parser = new ParseMaster(); - $encode = $this->_getEncoder($this->_encoding); - // for high-ascii, don't encode single character low-ascii - $regexp = ($this->_encoding > 62) ? '/\\w\\w+/' : '/\\w+/'; - // build the word list - $keywords = $this->_analyze($script, $regexp, $encode); - $encoded = $keywords['encoded']; - - // encode - $parser->add($regexp, - array( - 'fn' => '_replace_encoded', - 'data' => $encoded - ) - ); - if (empty($script)) return $script; - else { - //$res = $parser->exec($script); - //$res = $this->_bootStrap($res, $keywords); - //return $res; - return $this->_bootStrap($parser->exec($script), $keywords); - } - } - - private function _analyze($script, $regexp, $encode) - { - // analyse - // retreive all words in the script - $all = array(); - preg_match_all($regexp, $script, $all); - $_sorted = array(); // list of words sorted by frequency - $_encoded = array(); // dictionary of word->encoding - $_protected = array(); // instances of "protected" words - $all = $all[0]; // simulate the javascript comportement of global match - if (!empty($all)) { - $unsorted = array(); // same list, not sorted - $protected = array(); // "protected" words (dictionary of word->"word") - $value = array(); // dictionary of charCode->encoding (eg. 256->ff) - $this->_count = array(); // word->count - $i = count($all); $j = 0; //$word = null; - // count the occurrences - used for sorting later - do { - --$i; - $word = '$' . $all[$i]; - if (!isset($this->_count[$word])) { - $this->_count[$word] = 0; - $unsorted[$j] = $word; - // make a dictionary of all of the protected words in this script - // these are words that might be mistaken for encoding - //if (is_string($encode) && method_exists($this, $encode)) - $values[$j] = call_user_func(array(&$this, $encode), $j); - $protected['$' . $values[$j]] = $j++; - } - // increment the word counter - $this->_count[$word]++; - } while ($i > 0); - // prepare to sort the word list, first we must protect - // words that are also used as codes. we assign them a code - // equivalent to the word itself. - // e.g. if "do" falls within our encoding range - // then we store keywords["do"] = "do"; - // this avoids problems when decoding - $i = count($unsorted); - do { - $word = $unsorted[--$i]; - if (isset($protected[$word]) /*!= null*/) { - $_sorted[$protected[$word]] = substr($word, 1); - $_protected[$protected[$word]] = true; - $this->_count[$word] = 0; - } - } while ($i); - - // sort the words by frequency - // Note: the javascript and php version of sort can be different : - // in php manual, usort : - // " If two members compare as equal, - // their order in the sorted array is undefined." - // so the final packed script is different of the Dean's javascript version - // but equivalent. - // the ECMAscript standard does not guarantee this behaviour, - // and thus not all browsers (e.g. Mozilla versions dating back to at - // least 2003) respect this. - usort($unsorted, array(&$this, '_sortWords')); - $j = 0; - // because there are "protected" words in the list - // we must add the sorted words around them - do { - if (!isset($_sorted[$i])) - $_sorted[$i] = substr($unsorted[$j++], 1); - $_encoded[$_sorted[$i]] = $values[$i]; - } while (++$i < count($unsorted)); - } - return array( - 'sorted' => $_sorted, - 'encoded' => $_encoded, - 'protected' => $_protected); - } - - private $_count = array(); - private function _sortWords($match1, $match2) - { - return $this->_count[$match2] - $this->_count[$match1]; - } - - // build the boot function used for loading and decoding - private function _bootStrap($packed, $keywords) - { - $ENCODE = $this->_safeRegExp('$encode\\($count\\)'); - - // $packed: the packed script - $packed = "'" . $this->_escape($packed) . "'"; - - // $ascii: base for encoding - $ascii = min(count($keywords['sorted']), $this->_encoding); - if ($ascii == 0) $ascii = 1; - - // $count: number of words contained in the script - $count = count($keywords['sorted']); - - // $keywords: list of words contained in the script - foreach ($keywords['protected'] as $i=>$value) { - $keywords['sorted'][$i] = ''; - } - // convert from a string to an array - ksort($keywords['sorted']); - $keywords = "'" . implode('|',$keywords['sorted']) . "'.split('|')"; - - $encode = ($this->_encoding > 62) ? '_encode95' : $this->_getEncoder($ascii); - $encode = $this->_getJSFunction($encode); - $encode = preg_replace('/_encoding/','$ascii', $encode); - $encode = preg_replace('/arguments\\.callee/','$encode', $encode); - $inline = '\\$count' . ($ascii > 10 ? '.toString(\\$ascii)' : ''); - - // $decode: code snippet to speed up decoding - if ($this->_fastDecode) { - // create the decoder - $decode = $this->_getJSFunction('_decodeBody'); - if ($this->_encoding > 62) - $decode = preg_replace('/\\\\w/', '[\\xa1-\\xff]', $decode); - // perform the encoding inline for lower ascii values - elseif ($ascii < 36) - $decode = preg_replace($ENCODE, $inline, $decode); - // special case: when $count==0 there are no keywords. I want to keep - // the basic shape of the unpacking funcion so i'll frig the code... - if ($count == 0) - $decode = preg_replace($this->_safeRegExp('($count)\\s*=\\s*1'), '$1=0', $decode, 1); - } - - // boot function - $unpack = $this->_getJSFunction('_unpack'); - if ($this->_fastDecode) { - // insert the decoder - $this->buffer = $decode; - $unpack = preg_replace_callback('/\\{/', array(&$this, '_insertFastDecode'), $unpack, 1); - } - $unpack = preg_replace('/"/', "'", $unpack); - if ($this->_encoding > 62) { // high-ascii - // get rid of the word-boundaries for regexp matches - $unpack = preg_replace('/\'\\\\\\\\b\'\s*\\+|\\+\s*\'\\\\\\\\b\'/', '', $unpack); - } - if ($ascii > 36 || $this->_encoding > 62 || $this->_fastDecode) { - // insert the encode function - $this->buffer = $encode; - $unpack = preg_replace_callback('/\\{/', array(&$this, '_insertFastEncode'), $unpack, 1); - } else { - // perform the encoding inline - $unpack = preg_replace($ENCODE, $inline, $unpack); - } - // pack the boot function too - $unpackPacker = new JavaScriptPacker($unpack, 0, false, true); - $unpack = $unpackPacker->pack(); - - // arguments - $params = array($packed, $ascii, $count, $keywords); - if ($this->_fastDecode) { - $params[] = 0; - $params[] = '{}'; - } - $params = implode(',', $params); - - // the whole thing - return 'eval(' . $unpack . '(' . $params . "))\n"; - } - - private $buffer; - private function _insertFastDecode($match) - { - return '{' . $this->buffer . ';'; - } - private function _insertFastEncode($match) - { - return '{$encode=' . $this->buffer . ';'; - } - - // mmm.. ..which one do i need ?? - private function _getEncoder($ascii) - { - return $ascii > 10 ? $ascii > 36 ? $ascii > 62 ? - '_encode95' : '_encode62' : '_encode36' : '_encode10'; - } - - // zero encoding - // characters: 0123456789 - private function _encode10($charCode) - { - return $charCode; - } - - // inherent base36 support - // characters: 0123456789abcdefghijklmnopqrstuvwxyz - private function _encode36($charCode) - { - return base_convert($charCode, 10, 36); - } - - // hitch a ride on base36 and add the upper case alpha characters - // characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ - private function _encode62($charCode) - { - $res = ''; - if ($charCode >= $this->_encoding) { - $res = $this->_encode62((int) ($charCode / $this->_encoding)); - } - $charCode = $charCode % $this->_encoding; - - if ($charCode > 35) - return $res . chr($charCode + 29); - else - return $res . base_convert($charCode, 10, 36); - } - - // use high-ascii values - // characters: ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ - private function _encode95($charCode) - { - $res = ''; - if ($charCode >= $this->_encoding) - $res = $this->_encode95($charCode / $this->_encoding); - - return $res . chr(($charCode % $this->_encoding) + 161); - } - - private function _safeRegExp($string) - { - return '/'.preg_replace('/\$/', '\\\$', $string).'/'; - } - - private function _encodePrivate($charCode) - { - return "_" . $charCode; - } - - // protect characters used by the parser - private function _escape($script) - { - return preg_replace('/([\\\\\'])/', '\\\$1', $script); - } - - // protect high-ascii characters already in the script - private function _escape95($script) - { - return preg_replace_callback( - '/[\\xa1-\\xff]/', - array(&$this, '_escape95Bis'), - $script - ); - } - private function _escape95Bis($match) - { - return '\x'.((string) dechex(ord($match))); - } - - - private function _getJSFunction($aName) - { - if (defined('self::JSFUNCTION'.$aName)) - return constant('self::JSFUNCTION'.$aName); - else - return ''; - } - - // JavaScript Functions used. - // Note : In Dean's version, these functions are converted - // with 'String(aFunctionName);'. - // This internal conversion complete the original code, ex : - // 'while (aBool) anAction();' is converted to - // 'while (aBool) { anAction(); }'. - // The JavaScript functions below are corrected. - - // unpacking function - this is the boot strap function - // data extracted from this packing routine is passed to - // this function when decoded in the target - // NOTE ! : without the ';' final. - const JSFUNCTION_unpack = - -'function($packed, $ascii, $count, $keywords, $encode, $decode) { - while ($count--) { - if ($keywords[$count]) { - $packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]); - } - } - return $packed; -}'; -/* -'function($packed, $ascii, $count, $keywords, $encode, $decode) { - while ($count--) - if ($keywords[$count]) - $packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]); - return $packed; -}'; -*/ - - // code-snippet inserted into the unpacker to speed up decoding - const JSFUNCTION_decodeBody = -//_decode = function() { -// does the browser support String.replace where the -// replacement value is a function? - -' if (!\'\'.replace(/^/, String)) { - // decode all the values we need - while ($count--) { - $decode[$encode($count)] = $keywords[$count] || $encode($count); - } - // global replacement function - $keywords = [function ($encoded) {return $decode[$encoded]}]; - // generic match - $encode = function () {return \'\\\\w+\'}; - // reset the loop counter - we are now doing a global replace - $count = 1; - } -'; -//}; -/* -' if (!\'\'.replace(/^/, String)) { - // decode all the values we need - while ($count--) $decode[$encode($count)] = $keywords[$count] || $encode($count); - // global replacement function - $keywords = [function ($encoded) {return $decode[$encoded]}]; - // generic match - $encode = function () {return\'\\\\w+\'}; - // reset the loop counter - we are now doing a global replace - $count = 1; - }'; -*/ - - // zero encoding - // characters: 0123456789 - const JSFUNCTION_encode10 = -'function($charCode) { - return $charCode; -}';//;'; - - // inherent base36 support - // characters: 0123456789abcdefghijklmnopqrstuvwxyz - const JSFUNCTION_encode36 = -'function($charCode) { - return $charCode.toString(36); -}';//;'; - - // hitch a ride on base36 and add the upper case alpha characters - // characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ - const JSFUNCTION_encode62 = -'function($charCode) { - return ($charCode < _encoding ? \'\' : arguments.callee(parseInt($charCode / _encoding))) + - (($charCode = $charCode % _encoding) > 35 ? String.fromCharCode($charCode + 29) : $charCode.toString(36)); -}'; - - // use high-ascii values - // characters: ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ - const JSFUNCTION_encode95 = -'function($charCode) { - return ($charCode < _encoding ? \'\' : arguments.callee($charCode / _encoding)) + - String.fromCharCode($charCode % _encoding + 161); -}'; - -} - - -class ParseMaster -{ - public $ignoreCase = false; - public $escapeChar = ''; - - // constants - const EXPRESSION = 0; - const REPLACEMENT = 1; - const LENGTH = 2; - - // used to determine nesting levels - private $GROUPS = '/\\(/';//g - private $SUB_REPLACE = '/\\$\\d/'; - private $INDEXED = '/^\\$\\d+$/'; - private $TRIM = '/([\'"])\\1\\.(.*)\\.\\1\\1$/'; - private $ESCAPE = '/\\\./';//g - private $QUOTE = '/\'/'; - private $DELETED = '/\\x01[^\\x01]*\\x01/';//g - - public function add($expression, $replacement = '') - { - // count the number of sub-expressions - // - add one because each pattern is itself a sub-expression - $length = 1 + preg_match_all($this->GROUPS, $this->_internalEscape((string) $expression), $out); - - // treat only strings $replacement - if (is_string($replacement)) { - // does the pattern deal with sub-expressions? - if (preg_match($this->SUB_REPLACE, $replacement)) { - // a simple lookup? (e.g. "$2") - if (preg_match($this->INDEXED, $replacement)) { - // store the index (used for fast retrieval of matched strings) - $replacement = (int) (substr($replacement, 1)) - 1; - } else { // a complicated lookup (e.g. "Hello $2 $1") - // build a function to do the lookup - $quote = preg_match($this->QUOTE, $this->_internalEscape($replacement)) - ? '"' : "'"; - $replacement = array( - 'fn' => '_backReferences', - 'data' => array( - 'replacement' => $replacement, - 'length' => $length, - 'quote' => $quote - ) - ); - } - } - } - // pass the modified arguments - if (!empty($expression)) $this->_add($expression, $replacement, $length); - else $this->_add('/^$/', $replacement, $length); - } - - public function exec($string) - { - // execute the global replacement - $this->_escaped = array(); - - // simulate the _patterns.toSTring of Dean - $regexp = '/'; - foreach ($this->_patterns as $reg) { - $regexp .= '(' . substr($reg[self::EXPRESSION], 1, -1) . ')|'; - } - $regexp = substr($regexp, 0, -1) . '/'; - $regexp .= ($this->ignoreCase) ? 'i' : ''; - - $string = $this->_escape($string, $this->escapeChar); - $string = preg_replace_callback( - $regexp, - array( - &$this, - '_replacement' - ), - $string - ); - $string = $this->_unescape($string, $this->escapeChar); - - return preg_replace($this->DELETED, '', $string); - } - - public function reset() - { - // clear the patterns collection so that this object may be re-used - $this->_patterns = array(); - } - - // private - private $_escaped = array(); // escaped characters - private $_patterns = array(); // patterns stored by index - - // create and add a new pattern to the patterns collection - private function _add() - { - $arguments = func_get_args(); - $this->_patterns[] = $arguments; - } - - // this is the global replace function (it's quite complicated) - private function _replacement($arguments) - { - if (empty($arguments)) return ''; - - $i = 1; $j = 0; - // loop through the patterns - while (isset($this->_patterns[$j])) { - $pattern = $this->_patterns[$j++]; - // do we have a result? - if (isset($arguments[$i]) && ($arguments[$i] != '')) { - $replacement = $pattern[self::REPLACEMENT]; - - if (is_array($replacement) && isset($replacement['fn'])) { - - if (isset($replacement['data'])) $this->buffer = $replacement['data']; - return call_user_func(array(&$this, $replacement['fn']), $arguments, $i); - - } elseif (is_int($replacement)) { - return $arguments[$replacement + $i]; - - } - $delete = ($this->escapeChar == '' || - strpos($arguments[$i], $this->escapeChar) === false) - ? '' : "\x01" . $arguments[$i] . "\x01"; - return $delete . $replacement; - - // skip over references to sub-expressions - } else { - $i += $pattern[self::LENGTH]; - } - } - } - - private function _backReferences($match, $offset) - { - $replacement = $this->buffer['replacement']; - $quote = $this->buffer['quote']; - $i = $this->buffer['length']; - while ($i) { - $replacement = str_replace('$'.$i--, $match[$offset + $i], $replacement); - } - return $replacement; - } - - private function _replace_name($match, $offset) - { - $length = strlen($match[$offset + 2]); - $start = $length - max($length - strlen($match[$offset + 3]), 0); - return substr($match[$offset + 1], $start, $length) . $match[$offset + 4]; - } - - private function _replace_encoded($match, $offset) - { - return $this->buffer[$match[$offset]]; - } - - - // php : we cannot pass additional data to preg_replace_callback, - // and we cannot use &$this in create_function, so let's go to lower level - private $buffer; - - // encode escaped characters - private function _escape($string, $escapeChar) - { - if ($escapeChar) { - $this->buffer = $escapeChar; - return preg_replace_callback( - '/\\' . $escapeChar . '(.)' .'/', - array(&$this, '_escapeBis'), - $string - ); - - } else { - return $string; - } - } - private function _escapeBis($match) - { - $this->_escaped[] = $match[1]; - return $this->buffer; - } - - // decode escaped characters - private function _unescape($string, $escapeChar) - { - if ($escapeChar) { - $regexp = '/'.'\\'.$escapeChar.'/'; - $this->buffer = array('escapeChar'=> $escapeChar, 'i' => 0); - return preg_replace_callback - ( - $regexp, - array(&$this, '_unescapeBis'), - $string - ); - - } else { - return $string; - } - } - private function _unescapeBis() - { - if (isset($this->_escaped[$this->buffer['i']]) - && $this->_escaped[$this->buffer['i']] != '') - { - $temp = $this->_escaped[$this->buffer['i']]; - } else { - $temp = ''; - } - $this->buffer['i']++; - return $this->buffer['escapeChar'] . $temp; - } - - private function _internalEscape($string) - { - return preg_replace($this->ESCAPE, '', $string); - } -} diff --git a/core/src/core/classes/packer/index.html b/core/src/core/classes/packer/index.html deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_AuthBackendBasic.php b/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_AuthBackendBasic.php deleted file mode 100644 index 1aeb5559f3..0000000000 --- a/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_AuthBackendBasic.php +++ /dev/null @@ -1,133 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - - -class AJXP_Sabre_AuthBackendBasic extends Sabre\DAV\Auth\Backend\AbstractBasic -{ - protected $currentUser; - private $repositoryId; - - /** - * Utilitary method to detect basic header. - * @return bool - */ - public static function detectBasicHeader() - { - if(isSet($_SERVER["PHP_AUTH_USER"])) return true; - if(isSet($_SERVER["HTTP_AUTHORIZATION"])) $value = $_SERVER["HTTP_AUTHORIZATION"]; - if(!isSet($value) && isSet($_SERVER["REDIRECT_HTTP_AUTHORIZATION"])) $value = $_SERVER["HTTP_AUTHORIZATION"]; - if(!isSet($value)) return false; - return (strpos(strtolower($value),'basic') ===0) ; - } - - public function __construct($repositoryId) - { - $this->repositoryId = $repositoryId; - } - - - protected function validateUserPass($username, $password) - { - // Warning, this can only work if TRANSMIT_CLEAR_PASS is true; - return AuthService::checkPassword($username, $password, false, -1); - } - - public function authenticate(Sabre\DAV\Server $server, $realm) - { - $auth = new Sabre\HTTP\BasicAuth(); - $auth->setHTTPRequest($server->httpRequest); - $auth->setHTTPResponse($server->httpResponse); - $auth->setRealm($realm); - $userpass = $auth->getUserPass(); - if (!$userpass) { - $auth->requireLogin(); - throw new Sabre\DAV\Exception\NotAuthenticated('No basic authentication headers were found'); - } - - // Authenticates the user - //AJXP_Logger::info(__CLASS__,"authenticate",$userpass[0]); - - $confDriver = ConfService::getConfStorageImpl(); - $userObject = $confDriver->createUserObject($userpass[0]); - $webdavData = $userObject->getPref("AJXP_WEBDAV_DATA"); - if (empty($webdavData) || !isset($webdavData["ACTIVE"]) || $webdavData["ACTIVE"] !== true) { - AJXP_Logger::warning(__CLASS__, "Login failed", array("user" => $userpass[0], "error" => "WebDAV user not found or disabled")); - throw new Sabre\DAV\Exception\NotAuthenticated(); - } - // check if there are cached credentials. prevents excessive authentication calls to external - // auth mechanism. - $cachedPasswordValid = 0; - $secret = (defined("AJXP_SECRET_KEY")? AJXP_SECRET_KEY:"\1CDAFx¨op#"); - $encryptedPass = md5($userpass[1].$secret.date('YmdHi')); - if (isSet($webdavData["TMP_PASS"]) && ($encryptedPass == $webdavData["TMP_PASS"])) { - $cachedPasswordValid = true; - //AJXP_Logger::debug("Using Cached Password"); - } - - - if (!$cachedPasswordValid && (!$this->validateUserPass($userpass[0],$userpass[1]))) { - AJXP_Logger::warning(__CLASS__, "Login failed", array("user" => $userpass[0], "error" => "Invalid WebDAV user or password")); - $auth->requireLogin(); - throw new Sabre\DAV\Exception\NotAuthenticated('Username or password does not match'); - } - $this->currentUser = $userpass[0]; - - $res = AuthService::logUser($this->currentUser, $userpass[1], true); - if ($res < 1) { - throw new Sabre\DAV\Exception\NotAuthenticated(); - } - $this->updateCurrentUserRights(AuthService::getLoggedUser()); - if (ConfService::getCoreConf("SESSION_SET_CREDENTIALS", "auth")) { - AJXP_Safe::storeCredentials($this->currentUser, $userpass[1]); - } - if(isSet($this->repositoryId) && ConfService::getRepositoryById($this->repositoryId)->getOption("AJXP_WEBDAV_DISABLED") === true){ - throw new Sabre\DAV\Exception\NotAuthenticated('You are not allowed to access this workspace'); - } - ConfService::switchRootDir($this->repositoryId); - // the method used here will invalidate the cached password every minute on the minute - if (!$cachedPasswordValid) { - $webdavData["TMP_PASS"] = $encryptedPass; - $userObject->setPref("AJXP_WEBDAV_DATA", $webdavData); - $userObject->save("user"); - AuthService::updateUser($userObject); - } - - return true; - } - - - /** - * @param AbstractAjxpUser $user - * @return bool - */ - protected function updateCurrentUserRights($user) - { - if ($this->repositoryId == null) { - return true; - } - if (!$user->canSwitchTo($this->repositoryId)) { - throw new Sabre\DAV\Exception\NotAuthenticated(); - } - } - - -} diff --git a/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_AuthBackendDigest.php b/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_AuthBackendDigest.php deleted file mode 100644 index 1324ae9494..0000000000 --- a/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_AuthBackendDigest.php +++ /dev/null @@ -1,118 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * @package Pydio - * @subpackage SabreDav - */ -class AJXP_Sabre_AuthBackendDigest extends Sabre\DAV\Auth\Backend\AbstractDigest -{ - protected $currentUser; - private $secretKey; - private $repositoryId; - - public function __construct($repositoryId) - { - $this->repositoryId = $repositoryId; - if (defined('AJXP_SAFE_SECRET_KEY')) { - $this->secretKey = AJXP_SAFE_SECRET_KEY; - } else { - $this->secretKey = "\1CDAFx¨op#"; - } - } - - public function getDigestHash($realm, $username) - { - if (!AuthService::userExists($username)) { - return false; - } - $confDriver = ConfService::getConfStorageImpl(); - $user = $confDriver->createUserObject($username); - $webdavData = $user->getPref("AJXP_WEBDAV_DATA"); - if (empty($webdavData) || !isset($webdavData["ACTIVE"]) || $webdavData["ACTIVE"] !== true || (!isSet($webdavData["PASS"]) && !isset($webdavData["HA1"]) ) ) { - return false; - } - if (isSet($webdavData["HA1"])) { - return $webdavData["HA1"]; - } else { - $pass = $this->_decodePassword($webdavData["PASS"], $username); - return md5("{$username}:{$realm}:{$pass}"); - } - - } - - public function authenticate(Sabre\DAV\Server $server, $realm) - { - //AJXP_Logger::debug("Try authentication on $realm", $server); - - try { - $success = parent::authenticate($server, $realm); - } - catch(Exception $e) { - $success = 0; - $errmsg = $e->getMessage(); - if ($errmsg != "No digest authentication headers were found") - $success = false; - } - if ($success) { - $res = AuthService::logUser($this->currentUser, null, true); - if ($res < 1) { - throw new Sabre\DAV\Exception\NotAuthenticated(); - } - $this->updateCurrentUserRights(AuthService::getLoggedUser()); - if (ConfService::getCoreConf("SESSION_SET_CREDENTIALS", "auth")) { - $webdavData = AuthService::getLoggedUser()->getPref("AJXP_WEBDAV_DATA"); - AJXP_Safe::storeCredentials($this->currentUser, $this->_decodePassword($webdavData["PASS"], $this->currentUser)); - } - } - else { - if ($success === false) { - AJXP_Logger::warning(__CLASS__, "Login failed", array("user" => $this->currentUser, "error" => "Invalid WebDAV user or password")); - } - throw new Sabre\DAV\Exception\NotAuthenticated($errmsg); - } - ConfService::switchRootDir($this->repositoryId); - return true; - } - - protected function updateCurrentUserRights($user) - { - if ($this->repositoryId == null) { - return true; - } - if (!$user->canSwitchTo($this->repositoryId)) { - throw new Sabre\DAV\Exception\NotAuthenticated(); - } - } - - private function _decodePassword($encoded, $user) - { - if (function_exists('mcrypt_decrypt')) { - $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND); - $encoded = trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($user.$this->secretKey), base64_decode($encoded), MCRYPT_MODE_ECB, $iv), "\0"); - } - return $encoded; - } - - - -} diff --git a/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_BrowserPlugin.php b/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_BrowserPlugin.php deleted file mode 100755 index 097db6bb0c..0000000000 --- a/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_BrowserPlugin.php +++ /dev/null @@ -1,66 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * @package Pydio - * @subpackage SabreDav - */ -class AJXP_Sabre_BrowserPlugin extends Sabre\DAV\Browser\Plugin -{ - - protected $repositoryLabel; - - public function __construct($currentRepositoryLabel = null) - { - parent::__construct(false, true); - $this->repositoryLabel = $currentRepositoryLabel; - } - - public function generateDirectoryIndex($path) - { - $html = parent::generateDirectoryIndex($path); - $html = str_replace("image/vnd.microsoft.icon", "image/png", $html); - - $title = ConfService::getCoreConf("APPLICATION_TITLE"); - $html = preg_replace("/(.*)<\/title>/i", '<title>'.$title.'', $html); - - $repoString = ""; - if (!empty($this->repositoryLabel)) { - $repoString = " - ".$this->repositoryLabel."

    Index of ".$this->escapeHTML($path)."/

    "; - } - $html = preg_replace("/

    (.*)<\/h1>/i", "

    ".$title.$repoString, $html); - - $html = str_replace("h1 { font-size: 150% }", "h1 { font-size: 150% } \n h2 { font-size: 115% }", $html); - - return $html; - - } - - public function getLocalAssetPath($name) - { - if ($name != "favicon.ico") { - return parent::getLocalAssetPath($name); - } - return AJXP_INSTALL_PATH."/plugins/gui.ajax/res/themes/orbit/images/html-folder.png"; - } - -} diff --git a/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_Collection.php b/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_Collection.php deleted file mode 100755 index 497a3f35ac..0000000000 --- a/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_Collection.php +++ /dev/null @@ -1,194 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * @package Pydio - * @subpackage SabreDav - */ -class AJXP_Sabre_Collection extends AJXP_Sabre_Node implements Sabre\DAV\ICollection -{ - - protected $children; - - /** - * Creates a new file in the directory - * - * Data will either be supplied as a stream resource, or in certain cases - * as a string. Keep in mind that you may have to support either. - * - * After succesful creation of the file, you may choose to return the ETag - * of the new file here. - * - * The returned ETag must be surrounded by double-quotes (The quotes should - * be part of the actual string). - * - * If you cannot accurately determine the ETag, you should not return it. - * If you don't store the file exactly as-is (you're transforming it - * somehow) you should also not return an ETag. - * - * This means that if a subsequent GET to this new file does not exactly - * return the same contents of what was submitted here, you are strongly - * recommended to omit the ETag. - * - * @param string $name Name of the file - * @param resource|string $data Initial payload - * @return null|string - */ - public function createFile($name, $data = null) - { - try { - $name = ltrim($name, "/"); - AJXP_Logger::debug("CREATE FILE $name"); - - AJXP_Controller::findActionAndApply("mkfile", array( - "dir" => $this->path, - "filename" => $name - ), array()); - - if ( $data != null && is_file($this->getUrl()."/".$name)) { - - $p = $this->path."/".$name; - $this->getAccessDriver()->nodeWillChange($p, intval($_SERVER["CONTENT_LENGTH"])); - //AJXP_Logger::debug("Should now copy stream or string in ".$this->getUrl()."/".$name); - if (is_resource($data)) { - $stream = fopen($this->getUrl()."/".$name, "w"); - stream_copy_to_stream($data, $stream); - fclose($stream); - } else if (is_string($data)) { - file_put_contents($data, $this->getUrl()."/".$name); - } - - $toto = null; - $this->getAccessDriver()->nodeChanged($toto, $p); - - } - $node = new AJXP_Sabre_NodeLeaf($this->path."/".$name, $this->repository, $this->getAccessDriver()); - if (isSet($this->children)) { - $this->children = null; - } - return $node->getETag(); - - } catch (Exception $e) { - AJXP_Logger::debug("Error ".$e->getMessage(), $e->getTraceAsString()); - return null; - } - - - } - - /** - * Creates a new subdirectory - * - * @param string $name - * @return void - */ - public function createDirectory($name) - { - if (isSet($this->children)) { - $this->children = null; - } - - AJXP_Controller::findActionAndApply("mkdir", array( - "dir" => $this->path, - "dirname" => $name - ), array()); - - } - - /** - * Returns a specific child node, referenced by its name - * - * @param string $name - * @throws Sabre\DAV\Exception\NotFound - * @return Sabre\DAV\INode - */ - public function getChild($name) - { - foreach ($this->getChildren() as $child) { - - if ($child->getName()==$name) return $child; - - } - throw new Sabre\DAV\Exception\NotFound('File not found: ' . $name); - - } - - /** - * Returns an array with all the child nodes - * - * @return Sabre\DAV\INode[] - */ - public function getChildren() - { - if (isSet($this->children)) { - return $this->children; - } - - - $contents = array(); - $errors = array(); - - $nodes = scandir($this->getUrl()); - - foreach ($nodes as $file) { - if ($file == "." || $file == "..") { - continue; - } - // This function will perform the is_dir() call and update $isLeaf variable. - $isLeaf = ""; - if (!$this->getAccessDriver()->filterNodeName($this->getUrl(), $file, $isLeaf, array("d"=>true, "f"=>true, "z"=>true))){ - continue; - } - if ( !$isLeaf ) { - // Add collection without any children - $contents[] = new AJXP_Sabre_Collection($this->path."/".$file, $this->repository, $this->getAccessDriver()); - } else { - // Add files without content - $contents[] = new AJXP_Sabre_NodeLeaf($this->path."/".$file, $this->repository, $this->getAccessDriver()); - } - } - $this->children = $contents; - - $ajxpNode = new AJXP_Node($this->getUrl()); - AJXP_Controller::applyHook("node.read", array(&$ajxpNode)); - - return $contents; - - } - - /** - * Checks if a child-node with the specified name exists - * - * @param string $name - * @return bool - */ - public function childExists($name) - { - foreach ($this->getChildren() as $child) { - - if ($child->getName()==$name) return true; - - } - return false; - - } -} diff --git a/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_Node.php b/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_Node.php deleted file mode 100755 index cc1243321a..0000000000 --- a/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_Node.php +++ /dev/null @@ -1,243 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * @package Pydio - * @subpackage SabreDav - */ -class AJXP_Sabre_Node implements Sabre\DAV\INode, Sabre\DAV\IProperties -{ - - /** - * @var Repository - */ - protected $repository; - /** - * @var AjxpWrapperProvider - */ - protected $accessDriver; - - /** - * @var String - */ - protected $url; - protected $path; - - public function __construct($path, $repository, $accessDriver = null) - { - $this->repository = $repository; - $this->path = $path; - if ($accessDriver != null) { - $this->accessDriver = $accessDriver; - } - } - - /** - * @param Repository $repository - */ - public function updateRepository($repository){ - $this->repository = $repository; - } - - /** - * @return AjxpWrapperProvider - * @throws \Sabre\DAV\Exception\NotFound - */ - public function getAccessDriver() - { - if (!isset($this->accessDriver)) { - //$RID = $this->repository->getId(); - //ConfService::switchRootDir($RID); - ConfService::getConfStorageImpl(); - $this->accessDriver = ConfService::loadDriverForRepository($this->repository); - if (!$this->accessDriver instanceof AjxpWrapperProvider) { - throw new Sabre\DAV\Exception\NotFound( $this->repository->getId() ); - } - $this->accessDriver->detectStreamWrapper(true); - } - return $this->accessDriver; - } - - /** - * @return String - */ - public function getUrl() - { - if (!isSet($this->url)) { - $this->url = $this->getAccessDriver()->getResourceUrl($this->path); - } - return $this->url; - } - - /** - * Deleted the current node - * - * @return void - */ - public function delete() - { - ob_start(); - try { - AJXP_Controller::findActionAndApply("delete", array( - "dir" => dirname($this->path), - "file_0" => $this->path - ), array()); - } catch (Exception $e) { - - } - ob_get_flush(); - $this->putResourceData(array()); - - } - - /** - * Returns the name of the node. - * - * This is used to generate the url. - * - * @return string - */ - public function getName() - { - return basename($this->getUrl()); - } - - /** - * Renames the node - * - * @param string $name The new name - * @return void - */ - public function setName($name) - { - $data = $this->getResourceData(); - ob_start(); - AJXP_Controller::findActionAndApply("rename", array( - "filename_new" => $name, - "dir" => dirname($this->path), - "file" => $this->path - ), array()); - ob_get_flush(); - $this->putResourceData(array()); - $this->putResourceData($data, dirname($this->getUrl())."/".$name); - } - - /** - * Returns the last modification time, as a unix timestamp - * - * @return int - */ - public function getLastModified() - { - return filemtime($this->getUrl()); - } - - - /** - * Updates properties on this node, - * - * @param array $properties - * @see Sabre\DAV\IProperties::updateProperties - * @return bool|array - */ - public function updateProperties($properties) - { - $resourceData = $this->getResourceData(); - - foreach ($properties as $propertyName=>$propertyValue) { - - // If it was null, we need to delete the property - if (is_null($propertyValue)) { - if (isset($resourceData['properties'][$propertyName])) { - unset($resourceData['properties'][$propertyName]); - } - } else { - $resourceData['properties'][$propertyName] = $propertyValue; - } - - } - - //AJXP_Logger::debug("Saving Data", $resourceData); - $this->putResourceData($resourceData); - return true; - } - - /** - * Returns a list of properties for this nodes.; - * - * The properties list is a list of propertynames the client requested, encoded as xmlnamespace#tagName, for example: http://www.example.org/namespace#author - * If the array is empty, all properties should be returned - * - * @param array $properties - * @return array - */ - public function getProperties($properties) - { - $resourceData = $this->getResourceData(); - - // if the array was empty, we need to return everything - if (!$properties) return $resourceData['properties']; - - $props = array(); - foreach ($properties as $property) { - if (isset($resourceData['properties'][$property])) $props[$property] = $resourceData['properties'][$property]; - } - - return $props; - - } - - protected function putResourceData($array, $newURL = null) - { - $metaStore = $this->getMetastore(); - if ($metaStore != false) { - $metaStore->setMetadata(new AJXP_Node(($newURL!=null?$newURL:$this->getUrl())), "SABRE_DAV", $array, false, AJXP_METADATA_SCOPE_GLOBAL); - } - - } - - - protected function getResourceData() - { - $metaStore = $this->getMetastore(); - $data = array(); - if ($metaStore != false) { - $data = $metaStore->retrieveMetadata(new AJXP_Node($this->getUrl()), "SABRE_DAV", false, AJXP_METADATA_SCOPE_GLOBAL); - } - if (!isset($data['properties'])) $data['properties'] = array(); - return $data; - } - - - /** - * @return MetaStoreProvider|bool - */ - protected function getMetastore() - { - $metaStore = AJXP_PluginsService::getInstance()->getUniqueActivePluginForType("metastore"); - if($metaStore === false) return false; - $metaStore->initMeta($this->getAccessDriver()); - return $metaStore; - } - - -} diff --git a/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_NodeLeaf.php b/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_NodeLeaf.php deleted file mode 100755 index 1bc9ca0cd4..0000000000 --- a/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_NodeLeaf.php +++ /dev/null @@ -1,174 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * @package AjaXplorer - * @subpackage SabreDav - */ -class AJXP_Sabre_NodeLeaf extends AJXP_Sabre_Node implements Sabre\DAV\IFile -{ - - /** - * Updates the data - * - * The data argument is a readable stream resource. - * - * After a succesful put operation, you may choose to return an ETag. The - * etag must always be surrounded by double-quotes. These quotes must - * appear in the actual string you're returning. - * - * Clients may use the ETag from a PUT request to later on make sure that - * when they update the file, the contents haven't changed in the mean - * time. - * - * If you don't plan to store the file byte-by-byte, and you return a - * different object on a subsequent GET you are strongly recommended to not - * return an ETag, and just return null. - * - * @param resource $data - * @return string|null - */ - public function put($data) - { - // Warning, passed by ref - $p = $this->path; - - if (!AuthService::getLoggedUser()->canWrite($this->repository->getId())) { - throw new \Sabre\DAV\Exception\Forbidden() ; - } - $this->getAccessDriver()->nodeWillChange($p, intval($_SERVER["CONTENT_LENGTH"])); - - $stream = fopen($this->getUrl(), "w"); - stream_copy_to_stream($data, $stream); - fclose($stream); - - $toto = null; - $this->getAccessDriver()->nodeChanged($toto, $p); - - return $this->getETag(); - } - - /** - * Returns the data - * - * This method may either return a string or a readable stream resource - * - * @return mixed - */ - public function get() - { - $ajxpNode = new AJXP_Node($this->getUrl()); - AJXP_Controller::applyHook("node.read", array(&$ajxpNode)); - return fopen($this->getUrl(), "r"); - } - - /** - * Returns the mime-type for a file - * - * If null is returned, we'll assume application/octet-stream - * - * @return void|string - */ - public function getContentType() - { - //Get mimetype with fileinfo PECL extension - $fp = fopen($this->getUrl(), "r"); - $fileMime = null; - if (class_exists("finfo")) { - $finfo = new finfo(FILEINFO_MIME); - $fileMime = $finfo->buffer(fread($fp, 100)); - } elseif (function_exists("mime_content_type")) { - $fileMime = @mime_content_type($fp); - } else { - $fileExt = substr(strrchr(basename($this->getUrl()), '.'), 1); - if(empty($fileExt)) - $fileMime = "application/octet-stream"; - else { - $regex = "/^([\w\+\-\.\/]+)\s+(\w+\s)*($fileExt\s)/i"; - $lines = file( AJXP_CONF_PATH ."/mime.types"); - foreach ($lines as $line) { - if(substr($line, 0, 1) == '#') - continue; // skip comments - $line = rtrim($line) . " "; - if(!preg_match($regex, $line, $matches)) - continue; // no match to the extension - $fileMime = $matches[1]; - } - } - } - fclose($fp); - return $fileMime; - /* - if ( $this->options->useMimeExts && ezcBaseFeatures::hasExtensionSupport( 'fileinfo' ) ) { - $fInfo = new fInfo( FILEINFO_MIME ); - $mimeType = $fInfo->file( $this->getUrl() ); - - // The documentation tells to do this, but it does not work with a - // current version of pecl/fileinfo - // $fInfo->close(); - - return $mimeType; - } - - // Check if extension ext/mime-magic is usable. - if ( $this->options->useMimeExts && - ezcBaseFeatures::hasExtensionSupport( 'mime_magic' ) && - ( $mimeType = mime_content_type( $this->getUrl() ) ) !== false ) - { - return $mimeType; - } - */ - } - - /** - * Returns the ETag for a file - * - * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. - * - * Return null if the ETag can not effectively be determined - * - * @return String|null - */ - public function getETag() - { - clearstatcache(); - return '"'.md5( - $this->path - . $this->getSize() - . date( 'c', $this->getLastModified() ) - ).'"'; - } - - /** - * Returns the size of the node, in bytes - * - * @return int - */ - public function getSize() - { - return filesize($this->getUrl()); - } - - - - -} diff --git a/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_RootCollection.php b/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_RootCollection.php deleted file mode 100755 index ec523a08e5..0000000000 --- a/core/src/core/classes/sabredav/ajaxplorer/class.AJXP_Sabre_RootCollection.php +++ /dev/null @@ -1,60 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * @package Pydio - * @subpackage SabreDav - */ -class AJXP_Sabre_RootCollection extends Sabre\DAV\SimpleCollection -{ - - public function getChildren() - { - $this->children = array(); - $u = AuthService::getLoggedUser(); - if ($u != null) { - $repos = ConfService::getAccessibleRepositories($u); - // Refilter to make sure the driver is an AjxpWebdavProvider - foreach ($repos as $repository) { - $accessType = $repository->getAccessType(); - $driver = AJXP_PluginsService::getInstance()->getPluginByTypeName("access", $accessType); - if (is_a($driver, "AjxpWrapperProvider") && $repository->getOption("AJXP_WEBDAV_DISABLED") !== true) { - $this->children[$repository->getSlug()] = new Sabre\DAV\SimpleCollection($repository->getSlug()); - } - } - } - return $this->children; - } - - public function childExists($name) - { - $c = $this->getChildren(); - return array_key_exists($name, $c); - } - - public function getChild($name) - { - $c = $this->getChildren(); - return $c[$name]; - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Backend/AbstractBackend.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Backend/AbstractBackend.php deleted file mode 100644 index c83409eab1..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Backend/AbstractBackend.php +++ /dev/null @@ -1,155 +0,0 @@ - array( - * '{DAV:}displayname' => null, - * ), - * 424 => array( - * '{DAV:}owner' => null, - * ) - * ) - * - * In this example it was forbidden to update {DAV:}displayname. - * (403 Forbidden), which in turn also caused {DAV:}owner to fail - * (424 Failed Dependency) because the request needs to be atomic. - * - * @param mixed $calendarId - * @param array $mutations - * @return bool|array - */ - public function updateCalendar($calendarId, array $mutations) { - - return false; - - } - - /** - * Performs a calendar-query on the contents of this calendar. - * - * The calendar-query is defined in RFC4791 : CalDAV. Using the - * calendar-query it is possible for a client to request a specific set of - * object, based on contents of iCalendar properties, date-ranges and - * iCalendar component types (VTODO, VEVENT). - * - * This method should just return a list of (relative) urls that match this - * query. - * - * The list of filters are specified as an array. The exact array is - * documented by \Sabre\CalDAV\CalendarQueryParser. - * - * Note that it is extremely likely that getCalendarObject for every path - * returned from this method will be called almost immediately after. You - * may want to anticipate this to speed up these requests. - * - * This method provides a default implementation, which parses *all* the - * iCalendar objects in the specified calendar. - * - * This default may well be good enough for personal use, and calendars - * that aren't very large. But if you anticipate high usage, big calendars - * or high loads, you are strongly adviced to optimize certain paths. - * - * The best way to do so is override this method and to optimize - * specifically for 'common filters'. - * - * Requests that are extremely common are: - * * requests for just VEVENTS - * * requests for just VTODO - * * requests with a time-range-filter on either VEVENT or VTODO. - * - * ..and combinations of these requests. It may not be worth it to try to - * handle every possible situation and just rely on the (relatively - * easy to use) CalendarQueryValidator to handle the rest. - * - * Note that especially time-range-filters may be difficult to parse. A - * time-range filter specified on a VEVENT must for instance also handle - * recurrence rules correctly. - * A good example of how to interprete all these filters can also simply - * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct - * as possible, so it gives you a good idea on what type of stuff you need - * to think of. - * - * @param mixed $calendarId - * @param array $filters - * @return array - */ - public function calendarQuery($calendarId, array $filters) { - - $result = array(); - $objects = $this->getCalendarObjects($calendarId); - - $validator = new \Sabre\CalDAV\CalendarQueryValidator(); - - foreach($objects as $object) { - - if ($this->validateFilterForObject($object, $filters)) { - $result[] = $object['uri']; - } - - } - - return $result; - - } - - /** - * This method validates if a filters (as passed to calendarQuery) matches - * the given object. - * - * @param array $object - * @param array $filters - * @return bool - */ - protected function validateFilterForObject(array $object, array $filters) { - - // Unfortunately, setting the 'calendardata' here is optional. If - // it was excluded, we actually need another call to get this as - // well. - if (!isset($object['calendardata'])) { - $object = $this->getCalendarObject($object['calendarid'], $object['uri']); - } - - $data = is_resource($object['calendardata'])?stream_get_contents($object['calendardata']):$object['calendardata']; - $vObject = VObject\Reader::read($data); - - $validator = new CalDAV\CalendarQueryValidator(); - return $validator->validate($vObject, $filters); - - } - - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Backend/BackendInterface.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Backend/BackendInterface.php deleted file mode 100644 index 0dc31e5f4a..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Backend/BackendInterface.php +++ /dev/null @@ -1,233 +0,0 @@ - array( - * '{DAV:}displayname' => null, - * ), - * 424 => array( - * '{DAV:}owner' => null, - * ) - * ) - * - * In this example it was forbidden to update {DAV:}displayname. - * (403 Forbidden), which in turn also caused {DAV:}owner to fail - * (424 Failed Dependency) because the request needs to be atomic. - * - * @param mixed $calendarId - * @param array $mutations - * @return bool|array - */ - public function updateCalendar($calendarId, array $mutations); - - /** - * Delete a calendar and all it's objects - * - * @param mixed $calendarId - * @return void - */ - public function deleteCalendar($calendarId); - - /** - * Returns all calendar objects within a calendar. - * - * Every item contains an array with the following keys: - * * id - unique identifier which will be used for subsequent updates - * * calendardata - The iCalendar-compatible calendar data - * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string. - * * lastmodified - a timestamp of the last modification time - * * etag - An arbitrary string, surrounded by double-quotes. (e.g.: - * ' "abcdef"') - * * calendarid - The calendarid as it was passed to this function. - * * size - The size of the calendar objects, in bytes. - * - * Note that the etag is optional, but it's highly encouraged to return for - * speed reasons. - * - * The calendardata is also optional. If it's not returned - * 'getCalendarObject' will be called later, which *is* expected to return - * calendardata. - * - * If neither etag or size are specified, the calendardata will be - * used/fetched to determine these numbers. If both are specified the - * amount of times this is needed is reduced by a great degree. - * - * @param mixed $calendarId - * @return array - */ - public function getCalendarObjects($calendarId); - - /** - * Returns information from a single calendar object, based on it's object - * uri. - * - * The returned array must have the same keys as getCalendarObjects. The - * 'calendardata' object is required here though, while it's not required - * for getCalendarObjects. - * - * This method must return null if the object did not exist. - * - * @param mixed $calendarId - * @param string $objectUri - * @return array|null - */ - public function getCalendarObject($calendarId,$objectUri); - - /** - * Creates a new calendar object. - * - * It is possible return an etag from this function, which will be used in - * the response to this PUT request. Note that the ETag must be surrounded - * by double-quotes. - * - * However, you should only really return this ETag if you don't mangle the - * calendar-data. If the result of a subsequent GET to this object is not - * the exact same as this request body, you should omit the ETag. - * - * @param mixed $calendarId - * @param string $objectUri - * @param string $calendarData - * @return string|null - */ - public function createCalendarObject($calendarId,$objectUri,$calendarData); - - /** - * Updates an existing calendarobject, based on it's uri. - * - * It is possible return an etag from this function, which will be used in - * the response to this PUT request. Note that the ETag must be surrounded - * by double-quotes. - * - * However, you should only really return this ETag if you don't mangle the - * calendar-data. If the result of a subsequent GET to this object is not - * the exact same as this request body, you should omit the ETag. - * - * @param mixed $calendarId - * @param string $objectUri - * @param string $calendarData - * @return string|null - */ - public function updateCalendarObject($calendarId,$objectUri,$calendarData); - - /** - * Deletes an existing calendar object. - * - * @param mixed $calendarId - * @param string $objectUri - * @return void - */ - public function deleteCalendarObject($calendarId,$objectUri); - - /** - * Performs a calendar-query on the contents of this calendar. - * - * The calendar-query is defined in RFC4791 : CalDAV. Using the - * calendar-query it is possible for a client to request a specific set of - * object, based on contents of iCalendar properties, date-ranges and - * iCalendar component types (VTODO, VEVENT). - * - * This method should just return a list of (relative) urls that match this - * query. - * - * The list of filters are specified as an array. The exact array is - * documented by Sabre\CalDAV\CalendarQueryParser. - * - * Note that it is extremely likely that getCalendarObject for every path - * returned from this method will be called almost immediately after. You - * may want to anticipate this to speed up these requests. - * - * This method provides a default implementation, which parses *all* the - * iCalendar objects in the specified calendar. - * - * This default may well be good enough for personal use, and calendars - * that aren't very large. But if you anticipate high usage, big calendars - * or high loads, you are strongly adviced to optimize certain paths. - * - * The best way to do so is override this method and to optimize - * specifically for 'common filters'. - * - * Requests that are extremely common are: - * * requests for just VEVENTS - * * requests for just VTODO - * * requests with a time-range-filter on either VEVENT or VTODO. - * - * ..and combinations of these requests. It may not be worth it to try to - * handle every possible situation and just rely on the (relatively - * easy to use) CalendarQueryValidator to handle the rest. - * - * Note that especially time-range-filters may be difficult to parse. A - * time-range filter specified on a VEVENT must for instance also handle - * recurrence rules correctly. - * A good example of how to interprete all these filters can also simply - * be found in Sabre\CalDAV\CalendarQueryFilter. This class is as correct - * as possible, so it gives you a good idea on what type of stuff you need - * to think of. - * - * @param mixed $calendarId - * @param array $filters - * @return array - */ - public function calendarQuery($calendarId, array $filters); - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Backend/NotificationSupport.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Backend/NotificationSupport.php deleted file mode 100644 index 96533ad6ab..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Backend/NotificationSupport.php +++ /dev/null @@ -1,47 +0,0 @@ - 'displayname', - '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description', - '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone', - '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder', - '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor', - ); - - /** - * Creates the backend - * - * @param \PDO $pdo - * @param string $calendarTableName - * @param string $calendarObjectTableName - */ - public function __construct(\PDO $pdo, $calendarTableName = 'calendars', $calendarObjectTableName = 'calendarobjects') { - - $this->pdo = $pdo; - $this->calendarTableName = $calendarTableName; - $this->calendarObjectTableName = $calendarObjectTableName; - - } - - /** - * Returns a list of calendars for a principal. - * - * Every project is an array with the following keys: - * * id, a unique id that will be used by other functions to modify the - * calendar. This can be the same as the uri or a database key. - * * uri, which the basename of the uri with which the calendar is - * accessed. - * * principaluri. The owner of the calendar. Almost always the same as - * principalUri passed to this method. - * - * Furthermore it can contain webdav properties in clark notation. A very - * common one is '{DAV:}displayname'. - * - * @param string $principalUri - * @return array - */ - public function getCalendarsForUser($principalUri) { - - $fields = array_values($this->propertyMap); - $fields[] = 'id'; - $fields[] = 'uri'; - $fields[] = 'ctag'; - $fields[] = 'components'; - $fields[] = 'principaluri'; - $fields[] = 'transparent'; - - // Making fields a comma-delimited list - $fields = implode(', ', $fields); - $stmt = $this->pdo->prepare("SELECT " . $fields . " FROM ".$this->calendarTableName." WHERE principaluri = ? ORDER BY calendarorder ASC"); - $stmt->execute(array($principalUri)); - - $calendars = array(); - while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - - $components = array(); - if ($row['components']) { - $components = explode(',',$row['components']); - } - - $calendar = array( - 'id' => $row['id'], - 'uri' => $row['uri'], - 'principaluri' => $row['principaluri'], - '{' . CalDAV\Plugin::NS_CALENDARSERVER . '}getctag' => $row['ctag']?$row['ctag']:'0', - '{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Property\SupportedCalendarComponentSet($components), - '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' => new CalDAV\Property\ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'), - ); - - - foreach($this->propertyMap as $xmlName=>$dbName) { - $calendar[$xmlName] = $row[$dbName]; - } - - $calendars[] = $calendar; - - } - - return $calendars; - - } - - /** - * Creates a new calendar for a principal. - * - * If the creation was a success, an id must be returned that can be used to reference - * this calendar in other methods, such as updateCalendar - * - * @param string $principalUri - * @param string $calendarUri - * @param array $properties - * @return string - */ - public function createCalendar($principalUri, $calendarUri, array $properties) { - - $fieldNames = array( - 'principaluri', - 'uri', - 'ctag', - 'transparent', - ); - $values = array( - ':principaluri' => $principalUri, - ':uri' => $calendarUri, - ':ctag' => 1, - ':transparent' => 0, - ); - - // Default value - $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; - $fieldNames[] = 'components'; - if (!isset($properties[$sccs])) { - $values[':components'] = 'VEVENT,VTODO'; - } else { - if (!($properties[$sccs] instanceof CalDAV\Property\SupportedCalendarComponentSet)) { - throw new DAV\Exception('The ' . $sccs . ' property must be of type: \Sabre\CalDAV\Property\SupportedCalendarComponentSet'); - } - $values[':components'] = implode(',',$properties[$sccs]->getValue()); - } - $transp = '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp'; - if (isset($properties[$transp])) { - $values[':transparent'] = $properties[$transp]->getValue()==='transparent'; - } - - foreach($this->propertyMap as $xmlName=>$dbName) { - if (isset($properties[$xmlName])) { - - $values[':' . $dbName] = $properties[$xmlName]; - $fieldNames[] = $dbName; - } - } - - $stmt = $this->pdo->prepare("INSERT INTO ".$this->calendarTableName." (".implode(', ', $fieldNames).") VALUES (".implode(', ',array_keys($values)).")"); - $stmt->execute($values); - - return $this->pdo->lastInsertId(); - - } - - /** - * Updates properties for a calendar. - * - * The mutations array uses the propertyName in clark-notation as key, - * and the array value for the property value. In the case a property - * should be deleted, the property value will be null. - * - * This method must be atomic. If one property cannot be changed, the - * entire operation must fail. - * - * If the operation was successful, true can be returned. - * If the operation failed, false can be returned. - * - * Deletion of a non-existent property is always successful. - * - * Lastly, it is optional to return detailed information about any - * failures. In this case an array should be returned with the following - * structure: - * - * array( - * 403 => array( - * '{DAV:}displayname' => null, - * ), - * 424 => array( - * '{DAV:}owner' => null, - * ) - * ) - * - * In this example it was forbidden to update {DAV:}displayname. - * (403 Forbidden), which in turn also caused {DAV:}owner to fail - * (424 Failed Dependency) because the request needs to be atomic. - * - * @param string $calendarId - * @param array $mutations - * @return bool|array - */ - public function updateCalendar($calendarId, array $mutations) { - - $newValues = array(); - $result = array( - 200 => array(), // Ok - 403 => array(), // Forbidden - 424 => array(), // Failed Dependency - ); - - $hasError = false; - - foreach($mutations as $propertyName=>$propertyValue) { - - switch($propertyName) { - case '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' : - $fieldName = 'transparent'; - $newValues[$fieldName] = $propertyValue->getValue()==='transparent'; - break; - default : - // Checking the property map - if (!isset($this->propertyMap[$propertyName])) { - // We don't know about this property. - $hasError = true; - $result[403][$propertyName] = null; - unset($mutations[$propertyName]); - continue; - } - - $fieldName = $this->propertyMap[$propertyName]; - $newValues[$fieldName] = $propertyValue; - } - - } - - // If there were any errors we need to fail the request - if ($hasError) { - // Properties has the remaining properties - foreach($mutations as $propertyName=>$propertyValue) { - $result[424][$propertyName] = null; - } - - // Removing unused statuscodes for cleanliness - foreach($result as $status=>$properties) { - if (is_array($properties) && count($properties)===0) unset($result[$status]); - } - - return $result; - - } - - // Success - - // Now we're generating the sql query. - $valuesSql = array(); - foreach($newValues as $fieldName=>$value) { - $valuesSql[] = $fieldName . ' = ?'; - } - $valuesSql[] = 'ctag = ctag + 1'; - - $stmt = $this->pdo->prepare("UPDATE " . $this->calendarTableName . " SET " . implode(', ',$valuesSql) . " WHERE id = ?"); - $newValues['id'] = $calendarId; - $stmt->execute(array_values($newValues)); - - return true; - - } - - /** - * Delete a calendar and all it's objects - * - * @param string $calendarId - * @return void - */ - public function deleteCalendar($calendarId) { - - $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?'); - $stmt->execute(array($calendarId)); - - $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarTableName.' WHERE id = ?'); - $stmt->execute(array($calendarId)); - - } - - /** - * Returns all calendar objects within a calendar. - * - * Every item contains an array with the following keys: - * * id - unique identifier which will be used for subsequent updates - * * calendardata - The iCalendar-compatible calendar data - * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string. - * * lastmodified - a timestamp of the last modification time - * * etag - An arbitrary string, surrounded by double-quotes. (e.g.: - * ' "abcdef"') - * * calendarid - The calendarid as it was passed to this function. - * * size - The size of the calendar objects, in bytes. - * - * Note that the etag is optional, but it's highly encouraged to return for - * speed reasons. - * - * The calendardata is also optional. If it's not returned - * 'getCalendarObject' will be called later, which *is* expected to return - * calendardata. - * - * If neither etag or size are specified, the calendardata will be - * used/fetched to determine these numbers. If both are specified the - * amount of times this is needed is reduced by a great degree. - * - * @param string $calendarId - * @return array - */ - public function getCalendarObjects($calendarId) { - - $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?'); - $stmt->execute(array($calendarId)); - - $result = array(); - foreach($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) { - $result[] = array( - 'id' => $row['id'], - 'uri' => $row['uri'], - 'lastmodified' => $row['lastmodified'], - 'etag' => '"' . $row['etag'] . '"', - 'calendarid' => $row['calendarid'], - 'size' => (int)$row['size'], - ); - } - - return $result; - - } - - /** - * Returns information from a single calendar object, based on it's object - * uri. - * - * The returned array must have the same keys as getCalendarObjects. The - * 'calendardata' object is required here though, while it's not required - * for getCalendarObjects. - * - * This method must return null if the object did not exist. - * - * @param string $calendarId - * @param string $objectUri - * @return array|null - */ - public function getCalendarObject($calendarId,$objectUri) { - - $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, calendardata FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?'); - $stmt->execute(array($calendarId, $objectUri)); - $row = $stmt->fetch(\PDO::FETCH_ASSOC); - - if(!$row) return null; - - return array( - 'id' => $row['id'], - 'uri' => $row['uri'], - 'lastmodified' => $row['lastmodified'], - 'etag' => '"' . $row['etag'] . '"', - 'calendarid' => $row['calendarid'], - 'size' => (int)$row['size'], - 'calendardata' => $row['calendardata'], - ); - - } - - - /** - * Creates a new calendar object. - * - * It is possible return an etag from this function, which will be used in - * the response to this PUT request. Note that the ETag must be surrounded - * by double-quotes. - * - * However, you should only really return this ETag if you don't mangle the - * calendar-data. If the result of a subsequent GET to this object is not - * the exact same as this request body, you should omit the ETag. - * - * @param mixed $calendarId - * @param string $objectUri - * @param string $calendarData - * @return string|null - */ - public function createCalendarObject($calendarId,$objectUri,$calendarData) { - - $extraData = $this->getDenormalizedData($calendarData); - - $stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarObjectTableName.' (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence) VALUES (?,?,?,?,?,?,?,?,?)'); - $stmt->execute(array( - $calendarId, - $objectUri, - $calendarData, - time(), - $extraData['etag'], - $extraData['size'], - $extraData['componentType'], - $extraData['firstOccurence'], - $extraData['lastOccurence'], - )); - $stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?'); - $stmt->execute(array($calendarId)); - - return '"' . $extraData['etag'] . '"'; - - } - - /** - * Updates an existing calendarobject, based on it's uri. - * - * It is possible return an etag from this function, which will be used in - * the response to this PUT request. Note that the ETag must be surrounded - * by double-quotes. - * - * However, you should only really return this ETag if you don't mangle the - * calendar-data. If the result of a subsequent GET to this object is not - * the exact same as this request body, you should omit the ETag. - * - * @param mixed $calendarId - * @param string $objectUri - * @param string $calendarData - * @return string|null - */ - public function updateCalendarObject($calendarId,$objectUri,$calendarData) { - - $extraData = $this->getDenormalizedData($calendarData); - - $stmt = $this->pdo->prepare('UPDATE '.$this->calendarObjectTableName.' SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ? WHERE calendarid = ? AND uri = ?'); - $stmt->execute(array($calendarData,time(), $extraData['etag'], $extraData['size'], $extraData['componentType'], $extraData['firstOccurence'], $extraData['lastOccurence'] ,$calendarId,$objectUri)); - $stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?'); - $stmt->execute(array($calendarId)); - - return '"' . $extraData['etag'] . '"'; - - } - - /** - * Parses some information from calendar objects, used for optimized - * calendar-queries. - * - * Returns an array with the following keys: - * * etag - * * size - * * componentType - * * firstOccurence - * * lastOccurence - * - * @param string $calendarData - * @return array - */ - protected function getDenormalizedData($calendarData) { - - $vObject = VObject\Reader::read($calendarData); - $componentType = null; - $component = null; - $firstOccurence = null; - $lastOccurence = null; - foreach($vObject->getComponents() as $component) { - if ($component->name!=='VTIMEZONE') { - $componentType = $component->name; - break; - } - } - if (!$componentType) { - throw new \Sabre\DAV\Exception\BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component'); - } - if ($componentType === 'VEVENT') { - $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp(); - // Finding the last occurence is a bit harder - if (!isset($component->RRULE)) { - if (isset($component->DTEND)) { - $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp(); - } elseif (isset($component->DURATION)) { - $endDate = clone $component->DTSTART->getDateTime(); - $endDate->add(VObject\DateTimeParser::parse($component->DURATION->getValue())); - $lastOccurence = $endDate->getTimeStamp(); - } elseif (!$component->DTSTART->hasTime()) { - $endDate = clone $component->DTSTART->getDateTime(); - $endDate->modify('+1 day'); - $lastOccurence = $endDate->getTimeStamp(); - } else { - $lastOccurence = $firstOccurence; - } - } else { - $it = new VObject\RecurrenceIterator($vObject, (string)$component->UID); - $maxDate = new \DateTime(self::MAX_DATE); - if ($it->isInfinite()) { - $lastOccurence = $maxDate->getTimeStamp(); - } else { - $end = $it->getDtEnd(); - while($it->valid() && $end < $maxDate) { - $end = $it->getDtEnd(); - $it->next(); - - } - $lastOccurence = $end->getTimeStamp(); - } - - } - } - - return array( - 'etag' => md5($calendarData), - 'size' => strlen($calendarData), - 'componentType' => $componentType, - 'firstOccurence' => $firstOccurence, - 'lastOccurence' => $lastOccurence, - ); - - } - - /** - * Deletes an existing calendar object. - * - * @param string $calendarId - * @param string $objectUri - * @return void - */ - public function deleteCalendarObject($calendarId,$objectUri) { - - $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?'); - $stmt->execute(array($calendarId,$objectUri)); - $stmt = $this->pdo->prepare('UPDATE '. $this->calendarTableName .' SET ctag = ctag + 1 WHERE id = ?'); - $stmt->execute(array($calendarId)); - - } - - /** - * Performs a calendar-query on the contents of this calendar. - * - * The calendar-query is defined in RFC4791 : CalDAV. Using the - * calendar-query it is possible for a client to request a specific set of - * object, based on contents of iCalendar properties, date-ranges and - * iCalendar component types (VTODO, VEVENT). - * - * This method should just return a list of (relative) urls that match this - * query. - * - * The list of filters are specified as an array. The exact array is - * documented by \Sabre\CalDAV\CalendarQueryParser. - * - * Note that it is extremely likely that getCalendarObject for every path - * returned from this method will be called almost immediately after. You - * may want to anticipate this to speed up these requests. - * - * This method provides a default implementation, which parses *all* the - * iCalendar objects in the specified calendar. - * - * This default may well be good enough for personal use, and calendars - * that aren't very large. But if you anticipate high usage, big calendars - * or high loads, you are strongly adviced to optimize certain paths. - * - * The best way to do so is override this method and to optimize - * specifically for 'common filters'. - * - * Requests that are extremely common are: - * * requests for just VEVENTS - * * requests for just VTODO - * * requests with a time-range-filter on a VEVENT. - * - * ..and combinations of these requests. It may not be worth it to try to - * handle every possible situation and just rely on the (relatively - * easy to use) CalendarQueryValidator to handle the rest. - * - * Note that especially time-range-filters may be difficult to parse. A - * time-range filter specified on a VEVENT must for instance also handle - * recurrence rules correctly. - * A good example of how to interprete all these filters can also simply - * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct - * as possible, so it gives you a good idea on what type of stuff you need - * to think of. - * - * This specific implementation (for the PDO) backend optimizes filters on - * specific components, and VEVENT time-ranges. - * - * @param string $calendarId - * @param array $filters - * @return array - */ - public function calendarQuery($calendarId, array $filters) { - - $result = array(); - $validator = new \Sabre\CalDAV\CalendarQueryValidator(); - - $componentType = null; - $requirePostFilter = true; - $timeRange = null; - - // if no filters were specified, we don't need to filter after a query - if (!$filters['prop-filters'] && !$filters['comp-filters']) { - $requirePostFilter = false; - } - - // Figuring out if there's a component filter - if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) { - $componentType = $filters['comp-filters'][0]['name']; - - // Checking if we need post-filters - if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) { - $requirePostFilter = false; - } - // There was a time-range filter - if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) { - $timeRange = $filters['comp-filters'][0]['time-range']; - - // If start time OR the end time is not specified, we can do a - // 100% accurate mysql query. - if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) { - $requirePostFilter = false; - } - } - - } - - if ($requirePostFilter) { - $query = "SELECT uri, calendardata FROM ".$this->calendarObjectTableName." WHERE calendarid = :calendarid"; - } else { - $query = "SELECT uri FROM ".$this->calendarObjectTableName." WHERE calendarid = :calendarid"; - } - - $values = array( - 'calendarid' => $calendarId, - ); - - if ($componentType) { - $query.=" AND componenttype = :componenttype"; - $values['componenttype'] = $componentType; - } - - if ($timeRange && $timeRange['start']) { - $query.=" AND lastoccurence > :startdate"; - $values['startdate'] = $timeRange['start']->getTimeStamp(); - } - if ($timeRange && $timeRange['end']) { - $query.=" AND firstoccurence < :enddate"; - $values['enddate'] = $timeRange['end']->getTimeStamp(); - } - - $stmt = $this->pdo->prepare($query); - $stmt->execute($values); - - $result = array(); - while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - if ($requirePostFilter) { - if (!$this->validateFilterForObject($row, $filters)) { - continue; - } - } - $result[] = $row['uri']; - - } - - return $result; - - } -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Backend/SharingSupport.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Backend/SharingSupport.php deleted file mode 100644 index 4538fa5b58..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Backend/SharingSupport.php +++ /dev/null @@ -1,243 +0,0 @@ -caldavBackend = $caldavBackend; - $this->calendarInfo = $calendarInfo; - - } - - /** - * Returns the name of the calendar - * - * @return string - */ - public function getName() { - - return $this->calendarInfo['uri']; - - } - - /** - * Updates properties such as the display name and description - * - * @param array $mutations - * @return array - */ - public function updateProperties($mutations) { - - return $this->caldavBackend->updateCalendar($this->calendarInfo['id'],$mutations); - - } - - /** - * Returns the list of properties - * - * @param array $requestedProperties - * @return array - */ - public function getProperties($requestedProperties) { - - $response = array(); - - foreach($requestedProperties as $prop) switch($prop) { - - case '{urn:ietf:params:xml:ns:caldav}supported-calendar-data' : - $response[$prop] = new Property\SupportedCalendarData(); - break; - case '{urn:ietf:params:xml:ns:caldav}supported-collation-set' : - $response[$prop] = new Property\SupportedCollationSet(); - break; - case '{DAV:}owner' : - $response[$prop] = new DAVACL\Property\Principal(DAVACL\Property\Principal::HREF,$this->calendarInfo['principaluri']); - break; - default : - if (isset($this->calendarInfo[$prop])) $response[$prop] = $this->calendarInfo[$prop]; - break; - - } - return $response; - - } - - /** - * Returns a calendar object - * - * The contained calendar objects are for example Events or Todo's. - * - * @param string $name - * @return \Sabre\CalDAV\ICalendarObject - */ - public function getChild($name) { - - $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name); - - if (!$obj) throw new DAV\Exception\NotFound('Calendar object not found'); - - $obj['acl'] = $this->getACL(); - // Removing the irrelivant - foreach($obj['acl'] as $key=>$acl) { - if ($acl['privilege'] === '{' . Plugin::NS_CALDAV . '}read-free-busy') { - unset($obj['acl'][$key]); - } - } - - return new CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); - - } - - /** - * Returns the full list of calendar objects - * - * @return array - */ - public function getChildren() { - - $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']); - $children = array(); - foreach($objs as $obj) { - $obj['acl'] = $this->getACL(); - // Removing the irrelivant - foreach($obj['acl'] as $key=>$acl) { - if ($acl['privilege'] === '{' . Plugin::NS_CALDAV . '}read-free-busy') { - unset($obj['acl'][$key]); - } - } - $children[] = new CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); - } - return $children; - - } - - /** - * Checks if a child-node exists. - * - * @param string $name - * @return bool - */ - public function childExists($name) { - - $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name); - if (!$obj) - return false; - else - return true; - - } - - /** - * Creates a new directory - * - * We actually block this, as subdirectories are not allowed in calendars. - * - * @param string $name - * @return void - */ - public function createDirectory($name) { - - throw new DAV\Exception\MethodNotAllowed('Creating collections in calendar objects is not allowed'); - - } - - /** - * Creates a new file - * - * The contents of the new file must be a valid ICalendar string. - * - * @param string $name - * @param resource $calendarData - * @return string|null - */ - public function createFile($name,$calendarData = null) { - - if (is_resource($calendarData)) { - $calendarData = stream_get_contents($calendarData); - } - return $this->caldavBackend->createCalendarObject($this->calendarInfo['id'],$name,$calendarData); - - } - - /** - * Deletes the calendar. - * - * @return void - */ - public function delete() { - - $this->caldavBackend->deleteCalendar($this->calendarInfo['id']); - - } - - /** - * Renames the calendar. Note that most calendars use the - * {DAV:}displayname to display a name to display a name. - * - * @param string $newName - * @return void - */ - public function setName($newName) { - - throw new DAV\Exception\MethodNotAllowed('Renaming calendars is not yet supported'); - - } - - /** - * Returns the last modification date as a unix timestamp. - * - * @return void - */ - public function getLastModified() { - - return null; - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->calendarInfo['principaluri']; - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - return array( - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->getOwner(), - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}write', - 'principal' => $this->getOwner(), - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->getOwner() . '/calendar-proxy-write', - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}write', - 'principal' => $this->getOwner() . '/calendar-proxy-write', - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->getOwner() . '/calendar-proxy-read', - 'protected' => true, - ), - array( - 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy', - 'principal' => '{DAV:}authenticated', - 'protected' => true, - ), - - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See \Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - $default = DAVACL\Plugin::getDefaultSupportedPrivilegeSet(); - - // We need to inject 'read-free-busy' in the tree, aggregated under - // {DAV:}read. - foreach($default['aggregates'] as &$agg) { - - if ($agg['privilege'] !== '{DAV:}read') continue; - - $agg['aggregates'][] = array( - 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy', - ); - - } - return $default; - - } - - /** - * Performs a calendar-query on the contents of this calendar. - * - * The calendar-query is defined in RFC4791 : CalDAV. Using the - * calendar-query it is possible for a client to request a specific set of - * object, based on contents of iCalendar properties, date-ranges and - * iCalendar component types (VTODO, VEVENT). - * - * This method should just return a list of (relative) urls that match this - * query. - * - * The list of filters are specified as an array. The exact array is - * documented by Sabre\CalDAV\CalendarQueryParser. - * - * @param array $filters - * @return array - */ - public function calendarQuery(array $filters) { - - return $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/CalendarObject.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/CalendarObject.php deleted file mode 100644 index 2b9d2877e2..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/CalendarObject.php +++ /dev/null @@ -1,279 +0,0 @@ -caldavBackend = $caldavBackend; - - if (!isset($objectData['calendarid'])) { - throw new \InvalidArgumentException('The objectData argument must contain a \'calendarid\' property'); - } - if (!isset($objectData['uri'])) { - throw new \InvalidArgumentException('The objectData argument must contain an \'uri\' property'); - } - - $this->calendarInfo = $calendarInfo; - $this->objectData = $objectData; - - } - - /** - * Returns the uri for this object - * - * @return string - */ - public function getName() { - - return $this->objectData['uri']; - - } - - /** - * Returns the ICalendar-formatted object - * - * @return string - */ - public function get() { - - // Pre-populating the 'calendardata' is optional, if we don't have it - // already we fetch it from the backend. - if (!isset($this->objectData['calendardata'])) { - $this->objectData = $this->caldavBackend->getCalendarObject($this->objectData['calendarid'], $this->objectData['uri']); - } - return $this->objectData['calendardata']; - - } - - /** - * Updates the ICalendar-formatted object - * - * @param string|resource $calendarData - * @return string - */ - public function put($calendarData) { - - if (is_resource($calendarData)) { - $calendarData = stream_get_contents($calendarData); - } - $etag = $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'],$this->objectData['uri'],$calendarData); - $this->objectData['calendardata'] = $calendarData; - $this->objectData['etag'] = $etag; - - return $etag; - - } - - /** - * Deletes the calendar object - * - * @return void - */ - public function delete() { - - $this->caldavBackend->deleteCalendarObject($this->calendarInfo['id'],$this->objectData['uri']); - - } - - /** - * Returns the mime content-type - * - * @return string - */ - public function getContentType() { - - return 'text/calendar; charset=utf-8'; - - } - - /** - * Returns an ETag for this object. - * - * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. - * - * @return string - */ - public function getETag() { - - if (isset($this->objectData['etag'])) { - return $this->objectData['etag']; - } else { - return '"' . md5($this->get()). '"'; - } - - } - - /** - * Returns the last modification date as a unix timestamp - * - * @return int - */ - public function getLastModified() { - - return $this->objectData['lastmodified']; - - } - - /** - * Returns the size of this object in bytes - * - * @return int - */ - public function getSize() { - - if (array_key_exists('size',$this->objectData)) { - return $this->objectData['size']; - } else { - return strlen($this->get()); - } - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->calendarInfo['principaluri']; - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - // An alternative acl may be specified in the object data. - if (isset($this->objectData['acl'])) { - return $this->objectData['acl']; - } - - // The default ACL - return array( - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->calendarInfo['principaluri'], - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}write', - 'principal' => $this->calendarInfo['principaluri'], - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}write', - 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read', - 'protected' => true, - ), - - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new \Sabre\DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See \Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - return null; - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/CalendarQueryParser.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/CalendarQueryParser.php deleted file mode 100644 index 0a3472408a..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/CalendarQueryParser.php +++ /dev/null @@ -1,298 +0,0 @@ -dom = $dom; - $this->xpath = new \DOMXPath($dom); - $this->xpath->registerNameSpace('cal',Plugin::NS_CALDAV); - $this->xpath->registerNameSpace('dav','urn:DAV'); - - } - - /** - * Parses the request. - * - * @return void - */ - public function parse() { - - $filterNode = null; - - $filter = $this->xpath->query('/cal:calendar-query/cal:filter'); - if ($filter->length !== 1) { - throw new \Sabre\DAV\Exception\BadRequest('Only one filter element is allowed'); - } - - $compFilters = $this->parseCompFilters($filter->item(0)); - if (count($compFilters)!==1) { - throw new \Sabre\DAV\Exception\BadRequest('There must be exactly 1 top-level comp-filter.'); - } - - $this->filters = $compFilters[0]; - $this->requestedProperties = array_keys(\Sabre\DAV\XMLUtil::parseProperties($this->dom->firstChild)); - - $expand = $this->xpath->query('/cal:calendar-query/dav:prop/cal:calendar-data/cal:expand'); - if ($expand->length>0) { - $this->expand = $this->parseExpand($expand->item(0)); - } - - - } - - /** - * Parses all the 'comp-filter' elements from a node - * - * @param \DOMElement $parentNode - * @return array - */ - protected function parseCompFilters(\DOMElement $parentNode) { - - $compFilterNodes = $this->xpath->query('cal:comp-filter', $parentNode); - $result = array(); - - for($ii=0; $ii < $compFilterNodes->length; $ii++) { - - $compFilterNode = $compFilterNodes->item($ii); - - $compFilter = array(); - $compFilter['name'] = $compFilterNode->getAttribute('name'); - $compFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $compFilterNode)->length>0; - $compFilter['comp-filters'] = $this->parseCompFilters($compFilterNode); - $compFilter['prop-filters'] = $this->parsePropFilters($compFilterNode); - $compFilter['time-range'] = $this->parseTimeRange($compFilterNode); - - if ($compFilter['time-range'] && !in_array($compFilter['name'],array( - 'VEVENT', - 'VTODO', - 'VJOURNAL', - 'VFREEBUSY', - 'VALARM', - ))) { - throw new \Sabre\DAV\Exception\BadRequest('The time-range filter is not defined for the ' . $compFilter['name'] . ' component'); - }; - - $result[] = $compFilter; - - } - - return $result; - - } - - /** - * Parses all the prop-filter elements from a node - * - * @param \DOMElement $parentNode - * @return array - */ - protected function parsePropFilters(\DOMElement $parentNode) { - - $propFilterNodes = $this->xpath->query('cal:prop-filter', $parentNode); - $result = array(); - - for ($ii=0; $ii < $propFilterNodes->length; $ii++) { - - $propFilterNode = $propFilterNodes->item($ii); - $propFilter = array(); - $propFilter['name'] = $propFilterNode->getAttribute('name'); - $propFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $propFilterNode)->length>0; - $propFilter['param-filters'] = $this->parseParamFilters($propFilterNode); - $propFilter['text-match'] = $this->parseTextMatch($propFilterNode); - $propFilter['time-range'] = $this->parseTimeRange($propFilterNode); - - $result[] = $propFilter; - - } - - return $result; - - } - - /** - * Parses the param-filter element - * - * @param \DOMElement $parentNode - * @return array - */ - protected function parseParamFilters(\DOMElement $parentNode) { - - $paramFilterNodes = $this->xpath->query('cal:param-filter', $parentNode); - $result = array(); - - for($ii=0;$ii<$paramFilterNodes->length;$ii++) { - - $paramFilterNode = $paramFilterNodes->item($ii); - $paramFilter = array(); - $paramFilter['name'] = $paramFilterNode->getAttribute('name'); - $paramFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $paramFilterNode)->length>0; - $paramFilter['text-match'] = $this->parseTextMatch($paramFilterNode); - - $result[] = $paramFilter; - - } - - return $result; - - } - - /** - * Parses the text-match element - * - * @param \DOMElement $parentNode - * @return array|null - */ - protected function parseTextMatch(\DOMElement $parentNode) { - - $textMatchNodes = $this->xpath->query('cal:text-match', $parentNode); - - if ($textMatchNodes->length === 0) - return null; - - $textMatchNode = $textMatchNodes->item(0); - $negateCondition = $textMatchNode->getAttribute('negate-condition'); - $negateCondition = $negateCondition==='yes'; - $collation = $textMatchNode->getAttribute('collation'); - if (!$collation) $collation = 'i;ascii-casemap'; - - return array( - 'negate-condition' => $negateCondition, - 'collation' => $collation, - 'value' => $textMatchNode->nodeValue - ); - - } - - /** - * Parses the time-range element - * - * @param \DOMElement $parentNode - * @return array|null - */ - protected function parseTimeRange(\DOMElement $parentNode) { - - $timeRangeNodes = $this->xpath->query('cal:time-range', $parentNode); - if ($timeRangeNodes->length === 0) { - return null; - } - - $timeRangeNode = $timeRangeNodes->item(0); - - if ($start = $timeRangeNode->getAttribute('start')) { - $start = VObject\DateTimeParser::parseDateTime($start); - } else { - $start = null; - } - if ($end = $timeRangeNode->getAttribute('end')) { - $end = VObject\DateTimeParser::parseDateTime($end); - } else { - $end = null; - } - - if (!is_null($start) && !is_null($end) && $end <= $start) { - throw new \Sabre\DAV\Exception\BadRequest('The end-date must be larger than the start-date in the time-range filter'); - } - - return array( - 'start' => $start, - 'end' => $end, - ); - - } - - /** - * Parses the CALDAV:expand element - * - * @param \DOMElement $parentNode - * @return void - */ - protected function parseExpand(\DOMElement $parentNode) { - - $start = $parentNode->getAttribute('start'); - if(!$start) { - throw new \Sabre\DAV\Exception\BadRequest('The "start" attribute is required for the CALDAV:expand element'); - } - $start = VObject\DateTimeParser::parseDateTime($start); - - $end = $parentNode->getAttribute('end'); - if(!$end) { - throw new \Sabre\DAV\Exception\BadRequest('The "end" attribute is required for the CALDAV:expand element'); - } - - $end = VObject\DateTimeParser::parseDateTime($end); - - if ($end <= $start) { - throw new \Sabre\DAV\Exception\BadRequest('The end-date must be larger than the start-date in the expand element.'); - } - - return array( - 'start' => $start, - 'end' => $end, - ); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/CalendarQueryValidator.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/CalendarQueryValidator.php deleted file mode 100644 index 494aed1a7d..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/CalendarQueryValidator.php +++ /dev/null @@ -1,392 +0,0 @@ -name !== $filters['name']) { - return false; - } - - return - $this->validateCompFilters($vObject, $filters['comp-filters']) && - $this->validatePropFilters($vObject, $filters['prop-filters']); - - - } - - /** - * This method checks the validity of comp-filters. - * - * A list of comp-filters needs to be specified. Also the parent of the - * component we're checking should be specified, not the component to check - * itself. - * - * @param VObject\Component $parent - * @param array $filters - * @return bool - */ - protected function validateCompFilters(VObject\Component $parent, array $filters) { - - foreach($filters as $filter) { - - $isDefined = isset($parent->$filter['name']); - - if ($filter['is-not-defined']) { - - if ($isDefined) { - return false; - } else { - continue; - } - - } - if (!$isDefined) { - return false; - } - - if ($filter['time-range']) { - foreach($parent->$filter['name'] as $subComponent) { - if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) { - continue 2; - } - } - return false; - } - - if (!$filter['comp-filters'] && !$filter['prop-filters']) { - continue; - } - - // If there are sub-filters, we need to find at least one component - // for which the subfilters hold true. - foreach($parent->$filter['name'] as $subComponent) { - - if ( - $this->validateCompFilters($subComponent, $filter['comp-filters']) && - $this->validatePropFilters($subComponent, $filter['prop-filters'])) { - // We had a match, so this comp-filter succeeds - continue 2; - } - - } - - // If we got here it means there were sub-comp-filters or - // sub-prop-filters and there was no match. This means this filter - // needs to return false. - return false; - - } - - // If we got here it means we got through all comp-filters alive so the - // filters were all true. - return true; - - } - - /** - * This method checks the validity of prop-filters. - * - * A list of prop-filters needs to be specified. Also the parent of the - * property we're checking should be specified, not the property to check - * itself. - * - * @param VObject\Component $parent - * @param array $filters - * @return bool - */ - protected function validatePropFilters(VObject\Component $parent, array $filters) { - - foreach($filters as $filter) { - - $isDefined = isset($parent->$filter['name']); - - if ($filter['is-not-defined']) { - - if ($isDefined) { - return false; - } else { - continue; - } - - } - if (!$isDefined) { - return false; - } - - if ($filter['time-range']) { - foreach($parent->$filter['name'] as $subComponent) { - if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) { - continue 2; - } - } - return false; - } - - if (!$filter['param-filters'] && !$filter['text-match']) { - continue; - } - - // If there are sub-filters, we need to find at least one property - // for which the subfilters hold true. - foreach($parent->$filter['name'] as $subComponent) { - - if( - $this->validateParamFilters($subComponent, $filter['param-filters']) && - (!$filter['text-match'] || $this->validateTextMatch($subComponent, $filter['text-match'])) - ) { - // We had a match, so this prop-filter succeeds - continue 2; - } - - } - - // If we got here it means there were sub-param-filters or - // text-match filters and there was no match. This means the - // filter needs to return false. - return false; - - } - - // If we got here it means we got through all prop-filters alive so the - // filters were all true. - return true; - - } - - /** - * This method checks the validity of param-filters. - * - * A list of param-filters needs to be specified. Also the parent of the - * parameter we're checking should be specified, not the parameter to check - * itself. - * - * @param VObject\Property $parent - * @param array $filters - * @return bool - */ - protected function validateParamFilters(VObject\Property $parent, array $filters) { - - foreach($filters as $filter) { - - $isDefined = isset($parent[$filter['name']]); - - if ($filter['is-not-defined']) { - - if ($isDefined) { - return false; - } else { - continue; - } - - } - if (!$isDefined) { - return false; - } - - if (!$filter['text-match']) { - continue; - } - - if (version_compare(VObject\Version::VERSION, '3.0.0beta1', '>=')) { - - // If there are sub-filters, we need to find at least one parameter - // for which the subfilters hold true. - foreach($parent[$filter['name']]->getParts() as $subParam) { - - if($this->validateTextMatch($subParam,$filter['text-match'])) { - // We had a match, so this param-filter succeeds - continue 2; - } - - } - - } else { - - // If there are sub-filters, we need to find at least one parameter - // for which the subfilters hold true. - foreach($parent[$filter['name']] as $subParam) { - - if($this->validateTextMatch($subParam,$filter['text-match'])) { - // We had a match, so this param-filter succeeds - continue 2; - } - - } - - } - - // If we got here it means there was a text-match filter and there - // were no matches. This means the filter needs to return false. - return false; - - } - - // If we got here it means we got through all param-filters alive so the - // filters were all true. - return true; - - } - - /** - * This method checks the validity of a text-match. - * - * A single text-match should be specified as well as the specific property - * or parameter we need to validate. - * - * @param VObject\Node|string $check Value to check against. - * @param array $textMatch - * @return bool - */ - protected function validateTextMatch($check, array $textMatch) { - - if ($check instanceof VObject\Node) { - $check = (string)$check; - } - - $isMatching = \Sabre\DAV\StringUtil::textMatch($check, $textMatch['value'], $textMatch['collation']); - - return ($textMatch['negate-condition'] xor $isMatching); - - } - - /** - * Validates if a component matches the given time range. - * - * This is all based on the rules specified in rfc4791, which are quite - * complex. - * - * @param VObject\Node $component - * @param DateTime $start - * @param DateTime $end - * @return bool - */ - protected function validateTimeRange(VObject\Node $component, $start, $end) { - - if (is_null($start)) { - $start = new DateTime('1900-01-01'); - } - if (is_null($end)) { - $end = new DateTime('3000-01-01'); - } - - switch($component->name) { - - case 'VEVENT' : - case 'VTODO' : - case 'VJOURNAL' : - - return $component->isInTimeRange($start, $end); - - case 'VALARM' : - - // If the valarm is wrapped in a recurring event, we need to - // expand the recursions, and validate each. - // - // Our datamodel doesn't easily allow us to do this straight - // in the VALARM component code, so this is a hack, and an - // expensive one too. - if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) { - - // Fire up the iterator! - $it = new VObject\RecurrenceIterator($component->parent->parent, (string)$component->parent->UID); - while($it->valid()) { - $expandedEvent = $it->getEventObject(); - - // We need to check from these expanded alarms, which - // one is the first to trigger. Based on this, we can - // determine if we can 'give up' expanding events. - $firstAlarm = null; - if ($expandedEvent->VALARM !== null) { - foreach($expandedEvent->VALARM as $expandedAlarm) { - - $effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime(); - if ($expandedAlarm->isInTimeRange($start, $end)) { - return true; - } - - if ((string)$expandedAlarm->TRIGGER['VALUE'] === 'DATE-TIME') { - // This is an alarm with a non-relative trigger - // time, likely created by a buggy client. The - // implication is that every alarm in this - // recurring event trigger at the exact same - // time. It doesn't make sense to traverse - // further. - } else { - // We store the first alarm as a means to - // figure out when we can stop traversing. - if (!$firstAlarm || $effectiveTrigger < $firstAlarm) { - $firstAlarm = $effectiveTrigger; - } - } - } - } - if (is_null($firstAlarm)) { - // No alarm was found. - // - // Or technically: No alarm that will change for - // every instance of the recurrence was found, - // which means we can assume there was no match. - return false; - } - if ($firstAlarm > $end) { - return false; - } - $it->next(); - } - return false; - } else { - return $component->isInTimeRange($start, $end); - } - - case 'VFREEBUSY' : - throw new \Sabre\DAV\Exception\NotImplemented('time-range filters are currently not supported on ' . $component->name . ' components'); - - case 'COMPLETED' : - case 'CREATED' : - case 'DTEND' : - case 'DTSTAMP' : - case 'DTSTART' : - case 'DUE' : - case 'LAST-MODIFIED' : - return ($start <= $component->getDateTime() && $end >= $component->getDateTime()); - - - - default : - throw new \Sabre\DAV\Exception\BadRequest('You cannot create a time-range filter on a ' . $component->name . ' component'); - - } - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/CalendarRootNode.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/CalendarRootNode.php deleted file mode 100644 index 4f72ad4444..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/CalendarRootNode.php +++ /dev/null @@ -1,77 +0,0 @@ -caldavBackend = $caldavBackend; - - } - - /** - * Returns the nodename - * - * We're overriding this, because the default will be the 'principalPrefix', - * and we want it to be Sabre\CalDAV\Plugin::CALENDAR_ROOT - * - * @return string - */ - public function getName() { - - return Plugin::CALENDAR_ROOT; - - } - - /** - * This method returns a node for a principal. - * - * The passed array contains principal information, and is guaranteed to - * at least contain a uri item. Other properties may or may not be - * supplied by the authentication backend. - * - * @param array $principal - * @return \Sabre\DAV\INode - */ - public function getChildForPrincipal(array $principal) { - - return new UserCalendars($this->caldavBackend, $principal); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Exception/InvalidComponentType.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Exception/InvalidComponentType.php deleted file mode 100644 index f2a64e33f2..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Exception/InvalidComponentType.php +++ /dev/null @@ -1,35 +0,0 @@ -ownerDocument; - - $np = $doc->createElementNS(CalDAV\Plugin::NS_CALDAV,'cal:supported-calendar-component'); - $errorNode->appendChild($np); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/ICSExportPlugin.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/ICSExportPlugin.php deleted file mode 100644 index bba7fbd5ad..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/ICSExportPlugin.php +++ /dev/null @@ -1,142 +0,0 @@ -server = $server; - $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90); - - } - - /** - * 'beforeMethod' event handles. This event handles intercepts GET requests ending - * with ?export - * - * @param string $method - * @param string $uri - * @return bool - */ - public function beforeMethod($method, $uri) { - - if ($method!='GET') return; - if ($this->server->httpRequest->getQueryString()!='export') return; - - // splitting uri - list($uri) = explode('?',$uri,2); - - $node = $this->server->tree->getNodeForPath($uri); - - if (!($node instanceof Calendar)) return; - - // Checking ACL, if available. - if ($aclPlugin = $this->server->getPlugin('acl')) { - $aclPlugin->checkPrivileges($uri, '{DAV:}read'); - } - - $this->server->httpResponse->setHeader('Content-Type','text/calendar'); - $this->server->httpResponse->sendStatus(200); - - $nodes = $this->server->getPropertiesForPath($uri, array( - '{' . Plugin::NS_CALDAV . '}calendar-data', - ),1); - - $this->server->httpResponse->sendBody($this->generateICS($nodes)); - - // Returning false to break the event chain - return false; - - } - - /** - * Merges all calendar objects, and builds one big ics export - * - * @param array $nodes - * @return string - */ - public function generateICS(array $nodes) { - - $calendar = new VObject\Component\VCalendar(); - $calendar->version = '2.0'; - if (DAV\Server::$exposeVersion) { - $calendar->prodid = '-//SabreDAV//SabreDAV ' . DAV\Version::VERSION . '//EN'; - } else { - $calendar->prodid = '-//SabreDAV//SabreDAV//EN'; - } - $calendar->calscale = 'GREGORIAN'; - - $collectedTimezones = array(); - - $timezones = array(); - $objects = array(); - - foreach($nodes as $node) { - - if (!isset($node[200]['{' . Plugin::NS_CALDAV . '}calendar-data'])) { - continue; - } - $nodeData = $node[200]['{' . Plugin::NS_CALDAV . '}calendar-data']; - - $nodeComp = VObject\Reader::read($nodeData); - - foreach($nodeComp->children() as $child) { - - switch($child->name) { - case 'VEVENT' : - case 'VTODO' : - case 'VJOURNAL' : - $objects[] = $child; - break; - - // VTIMEZONE is special, because we need to filter out the duplicates - case 'VTIMEZONE' : - // Naively just checking tzid. - if (in_array((string)$child->TZID, $collectedTimezones)) continue; - - $timezones[] = $child; - $collectedTimezones[] = $child->TZID; - break; - - } - - } - - } - - foreach($timezones as $tz) $calendar->add($tz); - foreach($objects as $obj) $calendar->add($obj); - - return $calendar->serialize(); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/ICalendar.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/ICalendar.php deleted file mode 100644 index 0f0547046f..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/ICalendar.php +++ /dev/null @@ -1,36 +0,0 @@ -caldavBackend = $caldavBackend; - $this->principalUri = $principalUri; - - } - - /** - * Returns all notifications for a principal - * - * @return array - */ - public function getChildren() { - - $children = array(); - $notifications = $this->caldavBackend->getNotificationsForPrincipal($this->principalUri); - - foreach($notifications as $notification) { - - $children[] = new Node( - $this->caldavBackend, - $this->principalUri, - $notification - ); - } - - return $children; - - } - - /** - * Returns the name of this object - * - * @return string - */ - public function getName() { - - return 'notifications'; - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->principalUri; - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - return array( - array( - 'principal' => $this->getOwner(), - 'privilege' => '{DAV:}read', - 'protected' => true, - ), - array( - 'principal' => $this->getOwner(), - 'privilege' => '{DAV:}write', - 'protected' => true, - ) - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's as an array argument. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new DAV\Exception\NotImplemented('Updating ACLs is not implemented here'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - return null; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Notifications/ICollection.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Notifications/ICollection.php deleted file mode 100644 index 26e13b2119..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Notifications/ICollection.php +++ /dev/null @@ -1,24 +0,0 @@ -caldavBackend = $caldavBackend; - $this->principalUri = $principalUri; - $this->notification = $notification; - - } - - /** - * Returns the path name for this notification - * - * @return id - */ - public function getName() { - - return $this->notification->getId() . '.xml'; - - } - - /** - * Returns the etag for the notification. - * - * The etag must be surrounded by litteral double-quotes. - * - * @return string - */ - public function getETag() { - - return $this->notification->getETag(); - - } - - /** - * This method must return an xml element, using the - * Sabre\CalDAV\Notifications\INotificationType classes. - * - * @return INotificationType - */ - public function getNotificationType() { - - return $this->notification; - - } - - /** - * Deletes this notification - * - * @return void - */ - public function delete() { - - $this->caldavBackend->deleteNotification($this->getOwner(), $this->notification); - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->principalUri; - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - return array( - array( - 'principal' => $this->getOwner(), - 'privilege' => '{DAV:}read', - 'protected' => true, - ), - array( - 'principal' => $this->getOwner(), - 'privilege' => '{DAV:}write', - 'protected' => true, - ) - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's as an array argument. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new DAV\Exception\NotImplemented('Updating ACLs is not implemented here'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - return null; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Notifications/Notification/Invite.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Notifications/Notification/Invite.php deleted file mode 100644 index 8d6974d459..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Notifications/Notification/Invite.php +++ /dev/null @@ -1,324 +0,0 @@ -$value) { - if (!property_exists($this, $key)) { - throw new \InvalidArgumentException('Unknown option: ' . $key); - } - $this->$key = $value; - } - - } - - /** - * Serializes the notification as a single property. - * - * You should usually just encode the single top-level element of the - * notification. - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $node) { - - $prop = $node->ownerDocument->createElement('cs:invite-notification'); - $node->appendChild($prop); - - } - - /** - * This method serializes the entire notification, as it is used in the - * response body. - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serializeBody(DAV\Server $server, \DOMElement $node) { - - $doc = $node->ownerDocument; - - $dt = $doc->createElement('cs:dtstamp'); - $this->dtStamp->setTimezone(new \DateTimezone('GMT')); - $dt->appendChild($doc->createTextNode($this->dtStamp->format('Ymd\\THis\\Z'))); - $node->appendChild($dt); - - $prop = $doc->createElement('cs:invite-notification'); - $node->appendChild($prop); - - $uid = $doc->createElement('cs:uid'); - $uid->appendChild( $doc->createTextNode($this->id) ); - $prop->appendChild($uid); - - $href = $doc->createElement('d:href'); - $href->appendChild( $doc->createTextNode( $this->href ) ); - $prop->appendChild($href); - - $nodeName = null; - switch($this->type) { - - case SharingPlugin::STATUS_ACCEPTED : - $nodeName = 'cs:invite-accepted'; - break; - case SharingPlugin::STATUS_DECLINED : - $nodeName = 'cs:invite-declined'; - break; - case SharingPlugin::STATUS_DELETED : - $nodeName = 'cs:invite-deleted'; - break; - case SharingPlugin::STATUS_NORESPONSE : - $nodeName = 'cs:invite-noresponse'; - break; - - } - $prop->appendChild( - $doc->createElement($nodeName) - ); - $hostHref = $doc->createElement('d:href', $server->getBaseUri() . $this->hostUrl); - $hostUrl = $doc->createElement('cs:hosturl'); - $hostUrl->appendChild($hostHref); - $prop->appendChild($hostUrl); - - $access = $doc->createElement('cs:access'); - if ($this->readOnly) { - $access->appendChild($doc->createElement('cs:read')); - } else { - $access->appendChild($doc->createElement('cs:read-write')); - } - $prop->appendChild($access); - - $organizerUrl = $doc->createElement('cs:organizer'); - // If the organizer contains a 'mailto:' part, it means it should be - // treated as absolute. - if (strtolower(substr($this->organizer,0,7))==='mailto:') { - $organizerHref = new DAV\Property\Href($this->organizer, false); - } else { - $organizerHref = new DAV\Property\Href($this->organizer, true); - } - $organizerHref->serialize($server, $organizerUrl); - - if ($this->commonName) { - $commonName = $doc->createElement('cs:common-name'); - $commonName->appendChild($doc->createTextNode($this->commonName)); - $organizerUrl->appendChild($commonName); - - $commonNameOld = $doc->createElement('cs:organizer-cn'); - $commonNameOld->appendChild($doc->createTextNode($this->commonName)); - $prop->appendChild($commonNameOld); - - } - if ($this->firstName) { - $firstName = $doc->createElement('cs:first-name'); - $firstName->appendChild($doc->createTextNode($this->firstName)); - $organizerUrl->appendChild($firstName); - - $firstNameOld = $doc->createElement('cs:organizer-first'); - $firstNameOld->appendChild($doc->createTextNode($this->firstName)); - $prop->appendChild($firstNameOld); - } - if ($this->lastName) { - $lastName = $doc->createElement('cs:last-name'); - $lastName->appendChild($doc->createTextNode($this->lastName)); - $organizerUrl->appendChild($lastName); - - $lastNameOld = $doc->createElement('cs:organizer-last'); - $lastNameOld->appendChild($doc->createTextNode($this->lastName)); - $prop->appendChild($lastNameOld); - } - $prop->appendChild($organizerUrl); - - if ($this->summary) { - $summary = $doc->createElement('cs:summary'); - $summary->appendChild($doc->createTextNode($this->summary)); - $prop->appendChild($summary); - } - if ($this->supportedComponents) { - - $xcomp = $doc->createElement('cal:supported-calendar-component-set'); - $this->supportedComponents->serialize($server, $xcomp); - $prop->appendChild($xcomp); - - } - - } - - /** - * Returns a unique id for this notification - * - * This is just the base url. This should generally be some kind of unique - * id. - * - * @return string - */ - public function getId() { - - return $this->id; - - } - - /** - * Returns the ETag for this notification. - * - * The ETag must be surrounded by literal double-quotes. - * - * @return string - */ - public function getETag() { - - return $this->etag; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Notifications/Notification/InviteReply.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Notifications/Notification/InviteReply.php deleted file mode 100644 index e40751346b..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Notifications/Notification/InviteReply.php +++ /dev/null @@ -1,218 +0,0 @@ -$value) { - if (!property_exists($this, $key)) { - throw new \InvalidArgumentException('Unknown option: ' . $key); - } - $this->$key = $value; - } - - } - - /** - * Serializes the notification as a single property. - * - * You should usually just encode the single top-level element of the - * notification. - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $node) { - - $prop = $node->ownerDocument->createElement('cs:invite-reply'); - $node->appendChild($prop); - - } - - /** - * This method serializes the entire notification, as it is used in the - * response body. - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serializeBody(DAV\Server $server, \DOMElement $node) { - - $doc = $node->ownerDocument; - - $dt = $doc->createElement('cs:dtstamp'); - $this->dtStamp->setTimezone(new \DateTimezone('GMT')); - $dt->appendChild($doc->createTextNode($this->dtStamp->format('Ymd\\THis\\Z'))); - $node->appendChild($dt); - - $prop = $doc->createElement('cs:invite-reply'); - $node->appendChild($prop); - - $uid = $doc->createElement('cs:uid'); - $uid->appendChild($doc->createTextNode($this->id)); - $prop->appendChild($uid); - - $inReplyTo = $doc->createElement('cs:in-reply-to'); - $inReplyTo->appendChild( $doc->createTextNode($this->inReplyTo) ); - $prop->appendChild($inReplyTo); - - $href = $doc->createElement('d:href'); - $href->appendChild( $doc->createTextNode($this->href) ); - $prop->appendChild($href); - - $nodeName = null; - switch($this->type) { - - case SharingPlugin::STATUS_ACCEPTED : - $nodeName = 'cs:invite-accepted'; - break; - case SharingPlugin::STATUS_DECLINED : - $nodeName = 'cs:invite-declined'; - break; - - } - $prop->appendChild( - $doc->createElement($nodeName) - ); - $hostHref = $doc->createElement('d:href', $server->getBaseUri() . $this->hostUrl); - $hostUrl = $doc->createElement('cs:hosturl'); - $hostUrl->appendChild($hostHref); - $prop->appendChild($hostUrl); - - if ($this->summary) { - $summary = $doc->createElement('cs:summary'); - $summary->appendChild($doc->createTextNode($this->summary)); - $prop->appendChild($summary); - } - - } - - /** - * Returns a unique id for this notification - * - * This is just the base url. This should generally be some kind of unique - * id. - * - * @return string - */ - public function getId() { - - return $this->id; - - } - - /** - * Returns the ETag for this notification. - * - * The ETag must be surrounded by literal double-quotes. - * - * @return string - */ - public function getETag() { - - return $this->etag; - - } -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Notifications/Notification/SystemStatus.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Notifications/Notification/SystemStatus.php deleted file mode 100644 index 608892dab0..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Notifications/Notification/SystemStatus.php +++ /dev/null @@ -1,182 +0,0 @@ -id = $id; - $this->type = $type; - $this->description = $description; - $this->href = $href; - $this->etag = $etag; - - } - - /** - * Serializes the notification as a single property. - * - * You should usually just encode the single top-level element of the - * notification. - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $node) { - - switch($this->type) { - case self::TYPE_LOW : - $type = 'low'; - break; - case self::TYPE_MEDIUM : - $type = 'medium'; - break; - default : - case self::TYPE_HIGH : - $type = 'high'; - break; - } - - $prop = $node->ownerDocument->createElement('cs:systemstatus'); - $prop->setAttribute('type', $type); - - $node->appendChild($prop); - - } - - /** - * This method serializes the entire notification, as it is used in the - * response body. - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serializeBody(DAV\Server $server, \DOMElement $node) { - - switch($this->type) { - case self::TYPE_LOW : - $type = 'low'; - break; - case self::TYPE_MEDIUM : - $type = 'medium'; - break; - default : - case self::TYPE_HIGH : - $type = 'high'; - break; - } - - $prop = $node->ownerDocument->createElement('cs:systemstatus'); - $prop->setAttribute('type', $type); - - if ($this->description) { - $text = $node->ownerDocument->createTextNode($this->description); - $desc = $node->ownerDocument->createElement('cs:description'); - $desc->appendChild($text); - $prop->appendChild($desc); - } - if ($this->href) { - $text = $node->ownerDocument->createTextNode($this->href); - $href = $node->ownerDocument->createElement('d:href'); - $href->appendChild($text); - $prop->appendChild($href); - } - - $node->appendChild($prop); - - } - - /** - * Returns a unique id for this notification - * - * This is just the base url. This should generally be some kind of unique - * id. - * - * @return string - */ - public function getId() { - - return $this->id; - - } - - /* - * Returns the ETag for this notification. - * - * The ETag must be surrounded by literal double-quotes. - * - * @return string - */ - public function getETag() { - - return $this->etag; - - } -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Plugin.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Plugin.php deleted file mode 100644 index 610929388c..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Plugin.php +++ /dev/null @@ -1,1338 +0,0 @@ -imipHandler = $imipHandler; - - } - - /** - * Use this method to tell the server this plugin defines additional - * HTTP methods. - * - * This method is passed a uri. It should only return HTTP methods that are - * available for the specified uri. - * - * @param string $uri - * @return array - */ - public function getHTTPMethods($uri) { - - // The MKCALENDAR is only available on unmapped uri's, whose - // parents extend IExtendedCollection - list($parent, $name) = DAV\URLUtil::splitPath($uri); - - $node = $this->server->tree->getNodeForPath($parent); - - if ($node instanceof DAV\IExtendedCollection) { - try { - $node->getChild($name); - } catch (DAV\Exception\NotFound $e) { - return array('MKCALENDAR'); - } - } - return array(); - - } - - /** - * Returns a list of features for the DAV: HTTP header. - * - * @return array - */ - public function getFeatures() { - - return array('calendar-access', 'calendar-proxy'); - - } - - /** - * Returns a plugin name. - * - * Using this name other plugins will be able to access other plugins - * using DAV\Server::getPlugin - * - * @return string - */ - public function getPluginName() { - - return 'caldav'; - - } - - /** - * Returns a list of reports this plugin supports. - * - * This will be used in the {DAV:}supported-report-set property. - * Note that you still need to subscribe to the 'report' event to actually - * implement them - * - * @param string $uri - * @return array - */ - public function getSupportedReportSet($uri) { - - $node = $this->server->tree->getNodeForPath($uri); - - $reports = array(); - if ($node instanceof ICalendar || $node instanceof ICalendarObject) { - $reports[] = '{' . self::NS_CALDAV . '}calendar-multiget'; - $reports[] = '{' . self::NS_CALDAV . '}calendar-query'; - } - if ($node instanceof ICalendar) { - $reports[] = '{' . self::NS_CALDAV . '}free-busy-query'; - } - return $reports; - - } - - /** - * Initializes the plugin - * - * @param DAV\Server $server - * @return void - */ - public function initialize(DAV\Server $server) { - - $this->server = $server; - - $server->subscribeEvent('unknownMethod',array($this,'unknownMethod')); - //$server->subscribeEvent('unknownMethod',array($this,'unknownMethod2'),1000); - $server->subscribeEvent('report',array($this,'report')); - $server->subscribeEvent('beforeGetProperties',array($this,'beforeGetProperties')); - $server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel')); - $server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction')); - $server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent')); - $server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile')); - $server->subscribeEvent('beforeMethod', array($this,'beforeMethod')); - - $server->xmlNamespaces[self::NS_CALDAV] = 'cal'; - $server->xmlNamespaces[self::NS_CALENDARSERVER] = 'cs'; - - $server->propertyMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre\\CalDAV\\Property\\SupportedCalendarComponentSet'; - $server->propertyMap['{' . self::NS_CALDAV . '}schedule-calendar-transp'] = 'Sabre\\CalDAV\\Property\\ScheduleCalendarTransp'; - - $server->resourceTypeMapping['\\Sabre\\CalDAV\\ICalendar'] = '{urn:ietf:params:xml:ns:caldav}calendar'; - $server->resourceTypeMapping['\\Sabre\\CalDAV\\Schedule\\IOutbox'] = '{urn:ietf:params:xml:ns:caldav}schedule-outbox'; - $server->resourceTypeMapping['\\Sabre\\CalDAV\\Principal\\IProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read'; - $server->resourceTypeMapping['\\Sabre\\CalDAV\\Principal\\IProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write'; - $server->resourceTypeMapping['\\Sabre\\CalDAV\\Notifications\\ICollection'] = '{' . self::NS_CALENDARSERVER . '}notification'; - - array_push($server->protectedProperties, - - '{' . self::NS_CALDAV . '}supported-calendar-component-set', - '{' . self::NS_CALDAV . '}supported-calendar-data', - '{' . self::NS_CALDAV . '}max-resource-size', - '{' . self::NS_CALDAV . '}min-date-time', - '{' . self::NS_CALDAV . '}max-date-time', - '{' . self::NS_CALDAV . '}max-instances', - '{' . self::NS_CALDAV . '}max-attendees-per-instance', - '{' . self::NS_CALDAV . '}calendar-home-set', - '{' . self::NS_CALDAV . '}supported-collation-set', - '{' . self::NS_CALDAV . '}calendar-data', - - // scheduling extension - '{' . self::NS_CALDAV . '}schedule-inbox-URL', - '{' . self::NS_CALDAV . '}schedule-outbox-URL', - '{' . self::NS_CALDAV . '}calendar-user-address-set', - '{' . self::NS_CALDAV . '}calendar-user-type', - - // CalendarServer extensions - '{' . self::NS_CALENDARSERVER . '}getctag', - '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for', - '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for', - '{' . self::NS_CALENDARSERVER . '}notification-URL', - '{' . self::NS_CALENDARSERVER . '}notificationtype' - - ); - } - - /** - * This function handles support for the MKCALENDAR method - * - * @param string $method - * @param string $uri - * @return bool - */ - public function unknownMethod($method, $uri) { - - switch ($method) { - case 'MKCALENDAR' : - $this->httpMkCalendar($uri); - // false is returned to stop the propagation of the - // unknownMethod event. - return false; - case 'POST' : - - // Checking if this is a text/calendar content type - $contentType = $this->server->httpRequest->getHeader('Content-Type'); - if (strpos($contentType, 'text/calendar')!==0) { - return; - } - - // Checking if we're talking to an outbox - try { - $node = $this->server->tree->getNodeForPath($uri); - } catch (DAV\Exception\NotFound $e) { - return; - } - if (!$node instanceof Schedule\IOutbox) - return; - - $this->outboxRequest($node, $uri); - return false; - - } - - } - - /** - * This functions handles REPORT requests specific to CalDAV - * - * @param string $reportName - * @param \DOMNode $dom - * @return bool - */ - public function report($reportName,$dom) { - - switch($reportName) { - case '{'.self::NS_CALDAV.'}calendar-multiget' : - $this->calendarMultiGetReport($dom); - return false; - case '{'.self::NS_CALDAV.'}calendar-query' : - $this->calendarQueryReport($dom); - return false; - case '{'.self::NS_CALDAV.'}free-busy-query' : - $this->freeBusyQueryReport($dom); - return false; - - } - - - } - - /** - * This function handles the MKCALENDAR HTTP method, which creates - * a new calendar. - * - * @param string $uri - * @return void - */ - public function httpMkCalendar($uri) { - - // Due to unforgivable bugs in iCal, we're completely disabling MKCALENDAR support - // for clients matching iCal in the user agent - //$ua = $this->server->httpRequest->getHeader('User-Agent'); - //if (strpos($ua,'iCal/')!==false) { - // throw new \Sabre\DAV\Exception\Forbidden('iCal has major bugs in it\'s RFC3744 support. Therefore we are left with no other choice but disabling this feature.'); - //} - - $body = $this->server->httpRequest->getBody(true); - $properties = array(); - - if ($body) { - - $dom = DAV\XMLUtil::loadDOMDocument($body); - - foreach($dom->firstChild->childNodes as $child) { - - if (DAV\XMLUtil::toClarkNotation($child)!=='{DAV:}set') continue; - foreach(DAV\XMLUtil::parseProperties($child,$this->server->propertyMap) as $k=>$prop) { - $properties[$k] = $prop; - } - - } - } - - $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar'); - - $this->server->createCollection($uri,$resourceType,$properties); - - $this->server->httpResponse->sendStatus(201); - $this->server->httpResponse->setHeader('Content-Length',0); - } - - /** - * beforeGetProperties - * - * This method handler is invoked before any after properties for a - * resource are fetched. This allows us to add in any CalDAV specific - * properties. - * - * @param string $path - * @param DAV\INode $node - * @param array $requestedProperties - * @param array $returnedProperties - * @return void - */ - public function beforeGetProperties($path, DAV\INode $node, &$requestedProperties, &$returnedProperties) { - - if ($node instanceof DAVACL\IPrincipal) { - - // calendar-home-set property - $calHome = '{' . self::NS_CALDAV . '}calendar-home-set'; - if (in_array($calHome,$requestedProperties)) { - $principalId = $node->getName(); - $calendarHomePath = self::CALENDAR_ROOT . '/' . $principalId . '/'; - - unset($requestedProperties[array_search($calHome, $requestedProperties)]); - $returnedProperties[200][$calHome] = new DAV\Property\Href($calendarHomePath); - - } - - // schedule-outbox-URL property - $scheduleProp = '{' . self::NS_CALDAV . '}schedule-outbox-URL'; - if (in_array($scheduleProp,$requestedProperties)) { - $principalId = $node->getName(); - $outboxPath = self::CALENDAR_ROOT . '/' . $principalId . '/outbox'; - - unset($requestedProperties[array_search($scheduleProp, $requestedProperties)]); - $returnedProperties[200][$scheduleProp] = new DAV\Property\Href($outboxPath); - - } - - // calendar-user-address-set property - $calProp = '{' . self::NS_CALDAV . '}calendar-user-address-set'; - if (in_array($calProp,$requestedProperties)) { - - $addresses = $node->getAlternateUriSet(); - $addresses[] = $this->server->getBaseUri() . DAV\URLUtil::encodePath($node->getPrincipalUrl() . '/'); - unset($requestedProperties[array_search($calProp, $requestedProperties)]); - $returnedProperties[200][$calProp] = new DAV\Property\HrefList($addresses, false); - - } - - // These two properties are shortcuts for ical to easily find - // other principals this principal has access to. - $propRead = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for'; - $propWrite = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for'; - if (in_array($propRead,$requestedProperties) || in_array($propWrite,$requestedProperties)) { - - $aclPlugin = $this->server->getPlugin('acl'); - $membership = $aclPlugin->getPrincipalMembership($path); - $readList = array(); - $writeList = array(); - - foreach($membership as $group) { - - $groupNode = $this->server->tree->getNodeForPath($group); - - // If the node is either ap proxy-read or proxy-write - // group, we grab the parent principal and add it to the - // list. - if ($groupNode instanceof Principal\IProxyRead) { - list($readList[]) = DAV\URLUtil::splitPath($group); - } - if ($groupNode instanceof Principal\IProxyWrite) { - list($writeList[]) = DAV\URLUtil::splitPath($group); - } - - } - if (in_array($propRead,$requestedProperties)) { - unset($requestedProperties[$propRead]); - $returnedProperties[200][$propRead] = new DAV\Property\HrefList($readList); - } - if (in_array($propWrite,$requestedProperties)) { - unset($requestedProperties[$propWrite]); - $returnedProperties[200][$propWrite] = new DAV\Property\HrefList($writeList); - } - - } - - // notification-URL property - $notificationUrl = '{' . self::NS_CALENDARSERVER . '}notification-URL'; - if (($index = array_search($notificationUrl, $requestedProperties)) !== false) { - $principalId = $node->getName(); - $calendarHomePath = 'calendars/' . $principalId . '/notifications/'; - unset($requestedProperties[$index]); - $returnedProperties[200][$notificationUrl] = new DAV\Property\Href($calendarHomePath); - } - - } // instanceof IPrincipal - - if ($node instanceof Notifications\INode) { - - $propertyName = '{' . self::NS_CALENDARSERVER . '}notificationtype'; - if (($index = array_search($propertyName, $requestedProperties)) !== false) { - - $returnedProperties[200][$propertyName] = - $node->getNotificationType(); - - unset($requestedProperties[$index]); - - } - - } // instanceof Notifications_INode - - - if ($node instanceof ICalendarObject) { - // The calendar-data property is not supposed to be a 'real' - // property, but in large chunks of the spec it does act as such. - // Therefore we simply expose it as a property. - $calDataProp = '{' . Plugin::NS_CALDAV . '}calendar-data'; - if (in_array($calDataProp, $requestedProperties)) { - unset($requestedProperties[$calDataProp]); - $val = $node->get(); - if (is_resource($val)) - $val = stream_get_contents($val); - - // Taking out \r to not screw up the xml output - $returnedProperties[200][$calDataProp] = str_replace("\r","", $val); - - } - } - - } - - /** - * This function handles the calendar-multiget REPORT. - * - * This report is used by the client to fetch the content of a series - * of urls. Effectively avoiding a lot of redundant requests. - * - * @param \DOMNode $dom - * @return void - */ - public function calendarMultiGetReport($dom) { - - $properties = array_keys(DAV\XMLUtil::parseProperties($dom->firstChild)); - $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href'); - - $xpath = new \DOMXPath($dom); - $xpath->registerNameSpace('cal',Plugin::NS_CALDAV); - $xpath->registerNameSpace('dav','urn:DAV'); - - $expand = $xpath->query('/cal:calendar-multiget/dav:prop/cal:calendar-data/cal:expand'); - if ($expand->length>0) { - $expandElem = $expand->item(0); - $start = $expandElem->getAttribute('start'); - $end = $expandElem->getAttribute('end'); - if(!$start || !$end) { - throw new DAV\Exception\BadRequest('The "start" and "end" attributes are required for the CALDAV:expand element'); - } - $start = VObject\DateTimeParser::parseDateTime($start); - $end = VObject\DateTimeParser::parseDateTime($end); - - if ($end <= $start) { - throw new DAV\Exception\BadRequest('The end-date must be larger than the start-date in the expand element.'); - } - - $expand = true; - - } else { - - $expand = false; - - } - - foreach($hrefElems as $elem) { - $uri = $this->server->calculateUri($elem->nodeValue); - list($objProps) = $this->server->getPropertiesForPath($uri,$properties); - - if ($expand && isset($objProps[200]['{' . self::NS_CALDAV . '}calendar-data'])) { - $vObject = VObject\Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']); - $vObject->expand($start, $end); - $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); - } - - $propertyList[]=$objProps; - - } - - $prefer = $this->server->getHTTPPRefer(); - - $this->server->httpResponse->sendStatus(207); - $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->server->httpResponse->setHeader('Vary','Brief,Prefer'); - $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList, $prefer['return-minimal'])); - - } - - /** - * This function handles the calendar-query REPORT - * - * This report is used by clients to request calendar objects based on - * complex conditions. - * - * @param \DOMNode $dom - * @return void - */ - public function calendarQueryReport($dom) { - - $parser = new CalendarQueryParser($dom); - $parser->parse(); - - $node = $this->server->tree->getNodeForPath($this->server->getRequestUri()); - $depth = $this->server->getHTTPDepth(0); - - // The default result is an empty array - $result = array(); - - // The calendarobject was requested directly. In this case we handle - // this locally. - if ($depth == 0 && $node instanceof ICalendarObject) { - - $requestedCalendarData = true; - $requestedProperties = $parser->requestedProperties; - - if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) { - - // We always retrieve calendar-data, as we need it for filtering. - $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data'; - - // If calendar-data wasn't explicitly requested, we need to remove - // it after processing. - $requestedCalendarData = false; - } - - $properties = $this->server->getPropertiesForPath( - $this->server->getRequestUri(), - $requestedProperties, - 0 - ); - - // This array should have only 1 element, the first calendar - // object. - $properties = current($properties); - - // If there wasn't any calendar-data returned somehow, we ignore - // this. - if (isset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) { - - $validator = new CalendarQueryValidator(); - - $vObject = VObject\Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); - if ($validator->validate($vObject,$parser->filters)) { - - // If the client didn't require the calendar-data property, - // we won't give it back. - if (!$requestedCalendarData) { - unset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); - } else { - if ($parser->expand) { - $vObject->expand($parser->expand['start'], $parser->expand['end']); - $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); - } - } - - $result = array($properties); - - } - - } - - } - // If we're dealing with a calendar, the calendar itself is responsible - // for the calendar-query. - if ($node instanceof ICalendar && $depth = 1) { - - $nodePaths = $node->calendarQuery($parser->filters); - - foreach($nodePaths as $path) { - - list($properties) = - $this->server->getPropertiesForPath($this->server->getRequestUri() . '/' . $path, $parser->requestedProperties); - - if ($parser->expand) { - // We need to do some post-processing - $vObject = VObject\Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); - $vObject->expand($parser->expand['start'], $parser->expand['end']); - $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); - } - - $result[] = $properties; - - } - - } - - $prefer = $this->server->getHTTPPRefer(); - - $this->server->httpResponse->sendStatus(207); - $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->server->httpResponse->setHeader('Vary','Brief,Prefer'); - $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result, $prefer['return-minimal'])); - - } - - /** - * This method is responsible for parsing the request and generating the - * response for the CALDAV:free-busy-query REPORT. - * - * @param \DOMNode $dom - * @return void - */ - protected function freeBusyQueryReport(\DOMNode $dom) { - - $start = null; - $end = null; - - foreach($dom->firstChild->childNodes as $childNode) { - - $clark = DAV\XMLUtil::toClarkNotation($childNode); - if ($clark == '{' . self::NS_CALDAV . '}time-range') { - $start = $childNode->getAttribute('start'); - $end = $childNode->getAttribute('end'); - break; - } - - } - if ($start) { - $start = VObject\DateTimeParser::parseDateTime($start); - } - if ($end) { - $end = VObject\DateTimeParser::parseDateTime($end); - } - - if (!$start && !$end) { - throw new DAV\Exception\BadRequest('The freebusy report must have a time-range filter'); - } - $acl = $this->server->getPlugin('acl'); - - if (!$acl) { - throw new DAV\Exception('The ACL plugin must be loaded for free-busy queries to work'); - } - $uri = $this->server->getRequestUri(); - $acl->checkPrivileges($uri,'{' . self::NS_CALDAV . '}read-free-busy'); - - $calendar = $this->server->tree->getNodeForPath($uri); - if (!$calendar instanceof ICalendar) { - throw new DAV\Exception\NotImplemented('The free-busy-query REPORT is only implemented on calendars'); - } - - // Doing a calendar-query first, to make sure we get the most - // performance. - $urls = $calendar->calendarQuery(array( - 'name' => 'VCALENDAR', - 'comp-filters' => array( - array( - 'name' => 'VEVENT', - 'comp-filters' => array(), - 'prop-filters' => array(), - 'is-not-defined' => false, - 'time-range' => array( - 'start' => $start, - 'end' => $end, - ), - ), - ), - 'prop-filters' => array(), - 'is-not-defined' => false, - 'time-range' => null, - )); - - $objects = array_map(function($url) use ($calendar) { - $obj = $calendar->getChild($url)->get(); - return $obj; - }, $urls); - - $generator = new VObject\FreeBusyGenerator(); - $generator->setObjects($objects); - $generator->setTimeRange($start, $end); - $result = $generator->getResult(); - $result = $result->serialize(); - - $this->server->httpResponse->sendStatus(200); - $this->server->httpResponse->setHeader('Content-Type', 'text/calendar'); - $this->server->httpResponse->setHeader('Content-Length', strlen($result)); - $this->server->httpResponse->sendBody($result); - - } - - /** - * This method is triggered before a file gets updated with new content. - * - * This plugin uses this method to ensure that CalDAV objects receive - * valid calendar data. - * - * @param string $path - * @param DAV\IFile $node - * @param resource $data - * @return void - */ - public function beforeWriteContent($path, DAV\IFile $node, &$data) { - - if (!$node instanceof ICalendarObject) - return; - - $this->validateICalendar($data, $path); - - } - - /** - * This method is triggered before a new file is created. - * - * This plugin uses this method to ensure that newly created calendar - * objects contain valid calendar data. - * - * @param string $path - * @param resource $data - * @param DAV\ICollection $parentNode - * @return void - */ - public function beforeCreateFile($path, &$data, DAV\ICollection $parentNode) { - - if (!$parentNode instanceof Calendar) - return; - - $this->validateICalendar($data, $path); - - } - - /** - * This event is triggered before any HTTP request is handled. - * - * We use this to intercept GET calls to notification nodes, and return the - * proper response. - * - * @param string $method - * @param string $path - * @return void - */ - public function beforeMethod($method, $path) { - - if ($method!=='GET') return; - - try { - $node = $this->server->tree->getNodeForPath($path); - } catch (DAV\Exception\NotFound $e) { - return; - } - - if (!$node instanceof Notifications\INode) - return; - - if (!$this->server->checkPreconditions(true)) return false; - $dom = new \DOMDocument('1.0', 'UTF-8'); - - $dom->formatOutput = true; - - $root = $dom->createElement('cs:notification'); - foreach($this->server->xmlNamespaces as $namespace => $prefix) { - $root->setAttribute('xmlns:' . $prefix, $namespace); - } - - $dom->appendChild($root); - $node->getNotificationType()->serializeBody($this->server, $root); - - $this->server->httpResponse->setHeader('Content-Type','application/xml'); - $this->server->httpResponse->setHeader('ETag',$node->getETag()); - $this->server->httpResponse->sendStatus(200); - $this->server->httpResponse->sendBody($dom->saveXML()); - - return false; - - } - - /** - * Checks if the submitted iCalendar data is in fact, valid. - * - * An exception is thrown if it's not. - * - * @param resource|string $data - * @param string $path - * @return void - */ - protected function validateICalendar(&$data, $path) { - - // If it's a stream, we convert it to a string first. - if (is_resource($data)) { - $data = stream_get_contents($data); - } - - // Converting the data to unicode, if needed. - $data = DAV\StringUtil::ensureUTF8($data); - - try { - - $vobj = VObject\Reader::read($data); - - } catch (VObject\ParseException $e) { - - throw new DAV\Exception\UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage()); - - } - - if ($vobj->name !== 'VCALENDAR') { - throw new DAV\Exception\UnsupportedMediaType('This collection can only support iCalendar objects.'); - } - - // Get the Supported Components for the target calendar - list($parentPath,$object) = DAV\URLUtil::splitPath($path); - $calendarProperties = $this->server->getProperties($parentPath,array('{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set')); - $supportedComponents = $calendarProperties['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set']->getValue(); - - $foundType = null; - $foundUID = null; - foreach($vobj->getComponents() as $component) { - switch($component->name) { - case 'VTIMEZONE' : - continue 2; - case 'VEVENT' : - case 'VTODO' : - case 'VJOURNAL' : - if (is_null($foundType)) { - $foundType = $component->name; - if (!in_array($foundType, $supportedComponents)) { - throw new Exception\InvalidComponentType('This calendar only supports ' . implode(', ', $supportedComponents) . '. We found a ' . $foundType); - } - if (!isset($component->UID)) { - throw new DAV\Exception\BadRequest('Every ' . $component->name . ' component must have an UID'); - } - $foundUID = (string)$component->UID; - } else { - if ($foundType !== $component->name) { - throw new DAV\Exception\BadRequest('A calendar object must only contain 1 component. We found a ' . $component->name . ' as well as a ' . $foundType); - } - if ($foundUID !== (string)$component->UID) { - throw new DAV\Exception\BadRequest('Every ' . $component->name . ' in this object must have identical UIDs'); - } - } - break; - default : - throw new DAV\Exception\BadRequest('You are not allowed to create components of type: ' . $component->name . ' here'); - - } - } - if (!$foundType) - throw new DAV\Exception\BadRequest('iCalendar object must contain at least 1 of VEVENT, VTODO or VJOURNAL'); - - } - - /** - * This method handles POST requests to the schedule-outbox. - * - * Currently, two types of requests are support: - * * FREEBUSY requests from RFC 6638 - * * Simple iTIP messages from draft-desruisseaux-caldav-sched-04 - * - * The latter is from an expired early draft of the CalDAV scheduling - * extensions, but iCal depends on a feature from that spec, so we - * implement it. - * - * @param Schedule\IOutbox $outboxNode - * @param string $outboxUri - * @return void - */ - public function outboxRequest(Schedule\IOutbox $outboxNode, $outboxUri) { - - // Parsing the request body - try { - $vObject = VObject\Reader::read($this->server->httpRequest->getBody(true)); - } catch (VObject\ParseException $e) { - throw new DAV\Exception\BadRequest('The request body must be a valid iCalendar object. Parse error: ' . $e->getMessage()); - } - - // The incoming iCalendar object must have a METHOD property, and a - // component. The combination of both determines what type of request - // this is. - $componentType = null; - foreach($vObject->getComponents() as $component) { - if ($component->name !== 'VTIMEZONE') { - $componentType = $component->name; - break; - } - } - if (is_null($componentType)) { - throw new DAV\Exception\BadRequest('We expected at least one VTODO, VJOURNAL, VFREEBUSY or VEVENT component'); - } - - // Validating the METHOD - $method = strtoupper((string)$vObject->METHOD); - if (!$method) { - throw new DAV\Exception\BadRequest('A METHOD property must be specified in iTIP messages'); - } - - // So we support two types of requests: - // - // REQUEST with a VFREEBUSY component - // REQUEST, REPLY, ADD, CANCEL on VEVENT components - - $acl = $this->server->getPlugin('acl'); - - if ($componentType === 'VFREEBUSY' && $method === 'REQUEST') { - - $acl && $acl->checkPrivileges($outboxUri,'{' . Plugin::NS_CALDAV . '}schedule-query-freebusy'); - $this->handleFreeBusyRequest($outboxNode, $vObject); - - } elseif ($componentType === 'VEVENT' && in_array($method, array('REQUEST','REPLY','ADD','CANCEL'))) { - - $acl && $acl->checkPrivileges($outboxUri,'{' . Plugin::NS_CALDAV . '}schedule-post-vevent'); - $this->handleEventNotification($outboxNode, $vObject); - - } else { - - throw new DAV\Exception\NotImplemented('SabreDAV supports only VFREEBUSY (REQUEST) and VEVENT (REQUEST, REPLY, ADD, CANCEL)'); - - } - - } - - /** - * This method handles the REQUEST, REPLY, ADD and CANCEL methods for - * VEVENT iTip messages. - * - * @return void - */ - protected function handleEventNotification(Schedule\IOutbox $outboxNode, VObject\Component $vObject) { - - $originator = $this->server->httpRequest->getHeader('Originator'); - $recipients = $this->server->httpRequest->getHeader('Recipient'); - - if (!$originator) { - throw new DAV\Exception\BadRequest('The Originator: header must be specified when making POST requests'); - } - if (!$recipients) { - throw new DAV\Exception\BadRequest('The Recipient: header must be specified when making POST requests'); - } - - $recipients = explode(',',$recipients); - foreach($recipients as $k=>$recipient) { - - $recipient = trim($recipient); - if (!preg_match('/^mailto:(.*)@(.*)$/i', $recipient)) { - throw new DAV\Exception\BadRequest('Recipients must start with mailto: and must be valid email address'); - } - $recipient = substr($recipient, 7); - $recipients[$k] = $recipient; - } - - // We need to make sure that 'originator' matches one of the email - // addresses of the selected principal. - $principal = $outboxNode->getOwner(); - $props = $this->server->getProperties($principal,array( - '{' . self::NS_CALDAV . '}calendar-user-address-set', - )); - - $addresses = array(); - if (isset($props['{' . self::NS_CALDAV . '}calendar-user-address-set'])) { - $addresses = $props['{' . self::NS_CALDAV . '}calendar-user-address-set']->getHrefs(); - } - - $found = false; - foreach($addresses as $address) { - - // Trimming the / on both sides, just in case.. - if (rtrim(strtolower($originator),'/') === rtrim(strtolower($address),'/')) { - $found = true; - break; - } - - } - - if (!$found) { - throw new DAV\Exception\Forbidden('The addresses specified in the Originator header did not match any addresses in the owners calendar-user-address-set header'); - } - - // If the Originator header was a url, and not a mailto: address.. - // we're going to try to pull the mailto: from the vobject body. - if (strtolower(substr($originator,0,7)) !== 'mailto:') { - $originator = (string)$vObject->VEVENT->ORGANIZER; - - } - if (strtolower(substr($originator,0,7)) !== 'mailto:') { - throw new DAV\Exception\Forbidden('Could not find mailto: address in both the Orignator header, and the ORGANIZER property in the VEVENT'); - } - $originator = substr($originator,7); - - $result = $this->iMIPMessage($originator, $recipients, $vObject, $principal); - $this->server->httpResponse->sendStatus(200); - $this->server->httpResponse->setHeader('Content-Type','application/xml'); - $this->server->httpResponse->sendBody($this->generateScheduleResponse($result)); - - } - - /** - * Sends an iMIP message by email. - * - * This method must return an array with status codes per recipient. - * This should look something like: - * - * array( - * 'user1@example.org' => '2.0;Success' - * ) - * - * Formatting for this status code can be found at: - * https://tools.ietf.org/html/rfc5545#section-3.8.8.3 - * - * A list of valid status codes can be found at: - * https://tools.ietf.org/html/rfc5546#section-3.6 - * - * @param string $originator - * @param array $recipients - * @param VObject\Component $vObject - * @param string $principal Principal url - * @return array - */ - protected function iMIPMessage($originator, array $recipients, VObject\Component $vObject, $principal) { - - if (!$this->imipHandler) { - $resultStatus = '5.2;This server does not support this operation'; - } else { - $this->imipHandler->sendMessage($originator, $recipients, $vObject, $principal); - $resultStatus = '2.0;Success'; - } - - $result = array(); - foreach($recipients as $recipient) { - $result[$recipient] = $resultStatus; - } - - return $result; - - } - - /** - * Generates a schedule-response XML body - * - * The recipients array is a key->value list, containing email addresses - * and iTip status codes. See the iMIPMessage method for a description of - * the value. - * - * @param array $recipients - * @return string - */ - public function generateScheduleResponse(array $recipients) { - - $dom = new \DOMDocument('1.0','utf-8'); - $dom->formatOutput = true; - $xscheduleResponse = $dom->createElement('cal:schedule-response'); - $dom->appendChild($xscheduleResponse); - - foreach($this->server->xmlNamespaces as $namespace=>$prefix) { - - $xscheduleResponse->setAttribute('xmlns:' . $prefix, $namespace); - - } - - foreach($recipients as $recipient=>$status) { - $xresponse = $dom->createElement('cal:response'); - - $xrecipient = $dom->createElement('cal:recipient'); - $xrecipient->appendChild($dom->createTextNode($recipient)); - $xresponse->appendChild($xrecipient); - - $xrequestStatus = $dom->createElement('cal:request-status'); - $xrequestStatus->appendChild($dom->createTextNode($status)); - $xresponse->appendChild($xrequestStatus); - - $xscheduleResponse->appendChild($xresponse); - - } - - return $dom->saveXML(); - - } - - /** - * This method is responsible for parsing a free-busy query request and - * returning it's result. - * - * @param Schedule\IOutbox $outbox - * @param string $request - * @return string - */ - protected function handleFreeBusyRequest(Schedule\IOutbox $outbox, VObject\Component $vObject) { - - $vFreeBusy = $vObject->VFREEBUSY; - $organizer = $vFreeBusy->organizer; - - $organizer = (string)$organizer; - - // Validating if the organizer matches the owner of the inbox. - $owner = $outbox->getOwner(); - - $caldavNS = '{' . Plugin::NS_CALDAV . '}'; - - $uas = $caldavNS . 'calendar-user-address-set'; - $props = $this->server->getProperties($owner,array($uas)); - - if (empty($props[$uas]) || !in_array($organizer, $props[$uas]->getHrefs())) { - throw new DAV\Exception\Forbidden('The organizer in the request did not match any of the addresses for the owner of this inbox'); - } - - if (!isset($vFreeBusy->ATTENDEE)) { - throw new DAV\Exception\BadRequest('You must at least specify 1 attendee'); - } - - $attendees = array(); - foreach($vFreeBusy->ATTENDEE as $attendee) { - $attendees[]= (string)$attendee; - } - - - if (!isset($vFreeBusy->DTSTART) || !isset($vFreeBusy->DTEND)) { - throw new DAV\Exception\BadRequest('DTSTART and DTEND must both be specified'); - } - - $startRange = $vFreeBusy->DTSTART->getDateTime(); - $endRange = $vFreeBusy->DTEND->getDateTime(); - - $results = array(); - foreach($attendees as $attendee) { - $results[] = $this->getFreeBusyForEmail($attendee, $startRange, $endRange, $vObject); - } - - $dom = new \DOMDocument('1.0','utf-8'); - $dom->formatOutput = true; - $scheduleResponse = $dom->createElement('cal:schedule-response'); - foreach($this->server->xmlNamespaces as $namespace=>$prefix) { - - $scheduleResponse->setAttribute('xmlns:' . $prefix,$namespace); - - } - $dom->appendChild($scheduleResponse); - - foreach($results as $result) { - $response = $dom->createElement('cal:response'); - - $recipient = $dom->createElement('cal:recipient'); - $recipientHref = $dom->createElement('d:href'); - - $recipientHref->appendChild($dom->createTextNode($result['href'])); - $recipient->appendChild($recipientHref); - $response->appendChild($recipient); - - $reqStatus = $dom->createElement('cal:request-status'); - $reqStatus->appendChild($dom->createTextNode($result['request-status'])); - $response->appendChild($reqStatus); - - if (isset($result['calendar-data'])) { - - $calendardata = $dom->createElement('cal:calendar-data'); - $calendardata->appendChild($dom->createTextNode(str_replace("\r\n","\n",$result['calendar-data']->serialize()))); - $response->appendChild($calendardata); - - } - $scheduleResponse->appendChild($response); - } - - $this->server->httpResponse->sendStatus(200); - $this->server->httpResponse->setHeader('Content-Type','application/xml'); - $this->server->httpResponse->sendBody($dom->saveXML()); - - } - - /** - * Returns free-busy information for a specific address. The returned - * data is an array containing the following properties: - * - * calendar-data : A VFREEBUSY VObject - * request-status : an iTip status code. - * href: The principal's email address, as requested - * - * The following request status codes may be returned: - * * 2.0;description - * * 3.7;description - * - * @param string $email address - * @param \DateTime $start - * @param \DateTime $end - * @param VObject\Component $request - * @return array - */ - protected function getFreeBusyForEmail($email, \DateTime $start, \DateTime $end, VObject\Component $request) { - - $caldavNS = '{' . Plugin::NS_CALDAV . '}'; - - $aclPlugin = $this->server->getPlugin('acl'); - if (substr($email,0,7)==='mailto:') $email = substr($email,7); - - $result = $aclPlugin->principalSearch( - array('{http://sabredav.org/ns}email-address' => $email), - array( - '{DAV:}principal-URL', $caldavNS . 'calendar-home-set', - '{http://sabredav.org/ns}email-address', - ) - ); - - if (!count($result)) { - return array( - 'request-status' => '3.7;Could not find principal', - 'href' => 'mailto:' . $email, - ); - } - - if (!isset($result[0][200][$caldavNS . 'calendar-home-set'])) { - return array( - 'request-status' => '3.7;No calendar-home-set property found', - 'href' => 'mailto:' . $email, - ); - } - $homeSet = $result[0][200][$caldavNS . 'calendar-home-set']->getHref(); - - // Grabbing the calendar list - $objects = array(); - foreach($this->server->tree->getNodeForPath($homeSet)->getChildren() as $node) { - if (!$node instanceof ICalendar) { - continue; - } - $aclPlugin->checkPrivileges($homeSet . $node->getName() ,$caldavNS . 'read-free-busy'); - - // Getting the list of object uris within the time-range - $urls = $node->calendarQuery(array( - 'name' => 'VCALENDAR', - 'comp-filters' => array( - array( - 'name' => 'VEVENT', - 'comp-filters' => array(), - 'prop-filters' => array(), - 'is-not-defined' => false, - 'time-range' => array( - 'start' => $start, - 'end' => $end, - ), - ), - ), - 'prop-filters' => array(), - 'is-not-defined' => false, - 'time-range' => null, - )); - - $calObjects = array_map(function($url) use ($node) { - $obj = $node->getChild($url)->get(); - return $obj; - }, $urls); - - $objects = array_merge($objects,$calObjects); - - } - - $vcalendar = new VObject\Component\VCalendar(); - $vcalendar->VERSION = '2.0'; - $vcalendar->METHOD = 'REPLY'; - $vcalendar->CALSCALE = 'GREGORIAN'; - $vcalendar->PRODID = '-//SabreDAV//SabreDAV ' . DAV\Version::VERSION . '//EN'; - - $generator = new VObject\FreeBusyGenerator(); - $generator->setObjects($objects); - $generator->setTimeRange($start, $end); - $generator->setBaseObject($vcalendar); - - $result = $generator->getResult(); - - $vcalendar->VFREEBUSY->ATTENDEE = 'mailto:' . $email; - $vcalendar->VFREEBUSY->UID = (string)$request->VFREEBUSY->UID; - $vcalendar->VFREEBUSY->ORGANIZER = clone $request->VFREEBUSY->ORGANIZER; - - return array( - 'calendar-data' => $result, - 'request-status' => '2.0;Success', - 'href' => 'mailto:' . $email, - ); - } - - /** - * This method is used to generate HTML output for the - * DAV\Browser\Plugin. This allows us to generate an interface users - * can use to create new calendars. - * - * @param DAV\INode $node - * @param string $output - * @return bool - */ - public function htmlActionsPanel(DAV\INode $node, &$output) { - - if (!$node instanceof UserCalendars) - return; - - $output.= '
    -

    Create new calendar

    - -
    -
    - -
    - '; - - return false; - - } - - /** - * This method allows us to intercept the 'mkcalendar' sabreAction. This - * action enables the user to create new calendars from the browser plugin. - * - * @param string $uri - * @param string $action - * @param array $postVars - * @return bool - */ - public function browserPostAction($uri, $action, array $postVars) { - - if ($action!=='mkcalendar') - return; - - $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar'); - $properties = array(); - if (isset($postVars['{DAV:}displayname'])) { - $properties['{DAV:}displayname'] = $postVars['{DAV:}displayname']; - } - $this->server->createCollection($uri . '/' . $postVars['name'],$resourceType,$properties); - return false; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Principal/Collection.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Principal/Collection.php deleted file mode 100644 index 8a747a644b..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Principal/Collection.php +++ /dev/null @@ -1,32 +0,0 @@ -principalBackend, $principalInfo); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Principal/IProxyRead.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Principal/IProxyRead.php deleted file mode 100644 index 548411fa86..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Principal/IProxyRead.php +++ /dev/null @@ -1,19 +0,0 @@ -principalInfo = $principalInfo; - $this->principalBackend = $principalBackend; - - } - - /** - * Returns this principals name. - * - * @return string - */ - public function getName() { - - return 'calendar-proxy-read'; - - } - - /** - * Returns the last modification time - * - * @return null - */ - public function getLastModified() { - - return null; - - } - - /** - * Deletes the current node - * - * @throws DAV\Exception\Forbidden - * @return void - */ - public function delete() { - - throw new DAV\Exception\Forbidden('Permission denied to delete node'); - - } - - /** - * Renames the node - * - * @throws DAV\Exception\Forbidden - * @param string $name The new name - * @return void - */ - public function setName($name) { - - throw new DAV\Exception\Forbidden('Permission denied to rename file'); - - } - - - /** - * Returns a list of alternative urls for a principal - * - * This can for example be an email address, or ldap url. - * - * @return array - */ - public function getAlternateUriSet() { - - return array(); - - } - - /** - * Returns the full principal url - * - * @return string - */ - public function getPrincipalUrl() { - - return $this->principalInfo['uri'] . '/' . $this->getName(); - - } - - /** - * Returns the list of group members - * - * If this principal is a group, this function should return - * all member principal uri's for the group. - * - * @return array - */ - public function getGroupMemberSet() { - - return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); - - } - - /** - * Returns the list of groups this principal is member of - * - * If this principal is a member of a (list of) groups, this function - * should return a list of principal uri's for it's members. - * - * @return array - */ - public function getGroupMembership() { - - return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); - - } - - /** - * Sets a list of group members - * - * If this principal is a group, this method sets all the group members. - * The list of members is always overwritten, never appended to. - * - * This method should throw an exception if the members could not be set. - * - * @param array $principals - * @return void - */ - public function setGroupMemberSet(array $principals) { - - $this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals); - - } - - /** - * Returns the displayname - * - * This should be a human readable name for the principal. - * If none is available, return the nodename. - * - * @return string - */ - public function getDisplayName() { - - return $this->getName(); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Principal/ProxyWrite.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Principal/ProxyWrite.php deleted file mode 100644 index 02cd81f709..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Principal/ProxyWrite.php +++ /dev/null @@ -1,180 +0,0 @@ -principalInfo = $principalInfo; - $this->principalBackend = $principalBackend; - - } - - /** - * Returns this principals name. - * - * @return string - */ - public function getName() { - - return 'calendar-proxy-write'; - - } - - /** - * Returns the last modification time - * - * @return null - */ - public function getLastModified() { - - return null; - - } - - /** - * Deletes the current node - * - * @throws DAV\Exception\Forbidden - * @return void - */ - public function delete() { - - throw new DAV\Exception\Forbidden('Permission denied to delete node'); - - } - - /** - * Renames the node - * - * @throws DAV\Exception\Forbidden - * @param string $name The new name - * @return void - */ - public function setName($name) { - - throw new DAV\Exception\Forbidden('Permission denied to rename file'); - - } - - - /** - * Returns a list of alternative urls for a principal - * - * This can for example be an email address, or ldap url. - * - * @return array - */ - public function getAlternateUriSet() { - - return array(); - - } - - /** - * Returns the full principal url - * - * @return string - */ - public function getPrincipalUrl() { - - return $this->principalInfo['uri'] . '/' . $this->getName(); - - } - - /** - * Returns the list of group members - * - * If this principal is a group, this function should return - * all member principal uri's for the group. - * - * @return array - */ - public function getGroupMemberSet() { - - return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); - - } - - /** - * Returns the list of groups this principal is member of - * - * If this principal is a member of a (list of) groups, this function - * should return a list of principal uri's for it's members. - * - * @return array - */ - public function getGroupMembership() { - - return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); - - } - - /** - * Sets a list of group members - * - * If this principal is a group, this method sets all the group members. - * The list of members is always overwritten, never appended to. - * - * This method should throw an exception if the members could not be set. - * - * @param array $principals - * @return void - */ - public function setGroupMemberSet(array $principals) { - - $this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals); - - } - - /** - * Returns the displayname - * - * This should be a human readable name for the principal. - * If none is available, return the nodename. - * - * @return string - */ - public function getDisplayName() { - - return $this->getName(); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Principal/User.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Principal/User.php deleted file mode 100644 index 6f3ccb8689..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Principal/User.php +++ /dev/null @@ -1,134 +0,0 @@ -principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/' . $name); - if (!$principal) { - throw new DAV\Exception\NotFound('Node with name ' . $name . ' was not found'); - } - if ($name === 'calendar-proxy-read') - return new ProxyRead($this->principalBackend, $this->principalProperties); - - if ($name === 'calendar-proxy-write') - return new ProxyWrite($this->principalBackend, $this->principalProperties); - - throw new DAV\Exception\NotFound('Node with name ' . $name . ' was not found'); - - } - - /** - * Returns an array with all the child nodes - * - * @return DAV\INode[] - */ - public function getChildren() { - - $r = array(); - if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-read')) { - $r[] = new ProxyRead($this->principalBackend, $this->principalProperties); - } - if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-write')) { - $r[] = new ProxyWrite($this->principalBackend, $this->principalProperties); - } - - return $r; - - } - - /** - * Returns whether or not the child node exists - * - * @param string $name - * @return bool - */ - public function childExists($name) { - - try { - $this->getChild($name); - return true; - } catch (DAV\Exception\NotFound $e) { - return false; - } - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - $acl = parent::getACL(); - $acl[] = array( - 'privilege' => '{DAV:}read', - 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-read', - 'protected' => true, - ); - $acl[] = array( - 'privilege' => '{DAV:}read', - 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-write', - 'protected' => true, - ); - return $acl; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Property/AllowedSharingModes.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Property/AllowedSharingModes.php deleted file mode 100644 index ca8312d408..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Property/AllowedSharingModes.php +++ /dev/null @@ -1,74 +0,0 @@ -canBeShared = $canBeShared; - $this->canBePublished = $canBePublished; - - } - - /** - * Serializes the property in a DOMDocument - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $node) { - - $doc = $node->ownerDocument; - if ($this->canBeShared) { - $xcomp = $doc->createElement('cs:can-be-shared'); - $node->appendChild($xcomp); - } - if ($this->canBePublished) { - $xcomp = $doc->createElement('cs:can-be-published'); - $node->appendChild($xcomp); - } - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Property/Invite.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Property/Invite.php deleted file mode 100644 index 86ef69e803..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Property/Invite.php +++ /dev/null @@ -1,227 +0,0 @@ -users = $users; - $this->organizer = $organizer; - - } - - /** - * Returns the list of users, as it was passed to the constructor. - * - * @return array - */ - public function getValue() { - - return $this->users; - - } - - /** - * Serializes the property in a DOMDocument - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $node) { - - $doc = $node->ownerDocument; - - if (!is_null($this->organizer)) { - - $xorganizer = $doc->createElement('cs:organizer'); - - $href = $doc->createElement('d:href'); - $href->appendChild($doc->createTextNode($this->organizer['href'])); - $xorganizer->appendChild($href); - - if (isset($this->organizer['commonName']) && $this->organizer['commonName']) { - $commonName = $doc->createElement('cs:common-name'); - $commonName->appendChild($doc->createTextNode($this->organizer['commonName'])); - $xorganizer->appendChild($commonName); - } - if (isset($this->organizer['firstName']) && $this->organizer['firstName']) { - $firstName = $doc->createElement('cs:first-name'); - $firstName->appendChild($doc->createTextNode($this->organizer['firstName'])); - $xorganizer->appendChild($firstName); - } - if (isset($this->organizer['lastName']) && $this->organizer['lastName']) { - $lastName = $doc->createElement('cs:last-name'); - $lastName->appendChild($doc->createTextNode($this->organizer['lastName'])); - $xorganizer->appendChild($lastName); - } - - $node->appendChild($xorganizer); - - - } - - foreach($this->users as $user) { - - $xuser = $doc->createElement('cs:user'); - - $href = $doc->createElement('d:href'); - $href->appendChild($doc->createTextNode($user['href'])); - $xuser->appendChild($href); - - if (isset($user['commonName']) && $user['commonName']) { - $commonName = $doc->createElement('cs:common-name'); - $commonName->appendChild($doc->createTextNode($user['commonName'])); - $xuser->appendChild($commonName); - } - - switch($user['status']) { - - case SharingPlugin::STATUS_ACCEPTED : - $status = $doc->createElement('cs:invite-accepted'); - $xuser->appendChild($status); - break; - case SharingPlugin::STATUS_DECLINED : - $status = $doc->createElement('cs:invite-declined'); - $xuser->appendChild($status); - break; - case SharingPlugin::STATUS_NORESPONSE : - $status = $doc->createElement('cs:invite-noresponse'); - $xuser->appendChild($status); - break; - case SharingPlugin::STATUS_INVALID : - $status = $doc->createElement('cs:invite-invalid'); - $xuser->appendChild($status); - break; - - } - - $xaccess = $doc->createElement('cs:access'); - - if ($user['readOnly']) { - $xaccess->appendChild( - $doc->createElement('cs:read') - ); - } else { - $xaccess->appendChild( - $doc->createElement('cs:read-write') - ); - } - $xuser->appendChild($xaccess); - - if (isset($user['summary']) && $user['summary']) { - $summary = $doc->createElement('cs:summary'); - $summary->appendChild($doc->createTextNode($user['summary'])); - $xuser->appendChild($summary); - } - - $node->appendChild($xuser); - - } - - - } - - /** - * Unserializes the property. - * - * This static method should return a an instance of this object. - * - * @param \DOMElement $prop - * @return DAV\IProperty - */ - static function unserialize(\DOMElement $prop) { - - $xpath = new \DOMXPath($prop->ownerDocument); - $xpath->registerNamespace('cs', CalDAV\Plugin::NS_CALENDARSERVER); - $xpath->registerNamespace('d', 'urn:DAV'); - - $users = array(); - - foreach($xpath->query('cs:user', $prop) as $user) { - - $status = null; - if ($xpath->evaluate('boolean(cs:invite-accepted)', $user)) { - $status = SharingPlugin::STATUS_ACCEPTED; - } elseif ($xpath->evaluate('boolean(cs:invite-declined)', $user)) { - $status = SharingPlugin::STATUS_DECLINED; - } elseif ($xpath->evaluate('boolean(cs:invite-noresponse)', $user)) { - $status = SharingPlugin::STATUS_NORESPONSE; - } elseif ($xpath->evaluate('boolean(cs:invite-invalid)', $user)) { - $status = SharingPlugin::STATUS_INVALID; - } else { - throw new DAV\Exception('Every cs:user property must have one of cs:invite-accepted, cs:invite-declined, cs:invite-noresponse or cs:invite-invalid'); - } - $users[] = array( - 'href' => $xpath->evaluate('string(d:href)', $user), - 'commonName' => $xpath->evaluate('string(cs:common-name)', $user), - 'readOnly' => $xpath->evaluate('boolean(cs:access/cs:read)', $user), - 'summary' => $xpath->evaluate('string(cs:summary)', $user), - 'status' => $status, - ); - - } - - return new self($users); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Property/ScheduleCalendarTransp.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Property/ScheduleCalendarTransp.php deleted file mode 100644 index 7f12d75852..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Property/ScheduleCalendarTransp.php +++ /dev/null @@ -1,102 +0,0 @@ -value = $value; - - } - - /** - * Returns the current value - * - * @return string - */ - public function getValue() { - - return $this->value; - - } - - /** - * Serializes the property in a DOMDocument - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $node) { - - $doc = $node->ownerDocument; - switch($this->value) { - case self::TRANSPARENT : - $xval = $doc->createElement('cal:transparent'); - break; - case self::OPAQUE : - $xval = $doc->createElement('cal:opaque'); - break; - } - - $node->appendChild($xval); - - } - - /** - * Unserializes the DOMElement back into a Property class. - * - * @param \DOMElement $node - * @return ScheduleCalendarTransp - */ - static function unserialize(\DOMElement $node) { - - $value = null; - foreach($node->childNodes as $childNode) { - switch(DAV\XMLUtil::toClarkNotation($childNode)) { - case '{' . CalDAV\Plugin::NS_CALDAV . '}opaque' : - $value = self::OPAQUE; - break; - case '{' . CalDAV\Plugin::NS_CALDAV . '}transparent' : - $value = self::TRANSPARENT; - break; - } - } - if (is_null($value)) - return null; - - return new self($value); - - } -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php deleted file mode 100644 index 2538fa45d9..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php +++ /dev/null @@ -1,88 +0,0 @@ -components = $components; - - } - - /** - * Returns the list of supported components - * - * @return array - */ - public function getValue() { - - return $this->components; - - } - - /** - * Serializes the property in a DOMDocument - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $node) { - - $doc = $node->ownerDocument; - foreach($this->components as $component) { - - $xcomp = $doc->createElement('cal:comp'); - $xcomp->setAttribute('name',$component); - $node->appendChild($xcomp); - - } - - } - - /** - * Unserializes the DOMElement back into a Property class. - * - * @param \DOMElement $node - * @return Property_SupportedCalendarComponentSet - */ - static function unserialize(\DOMElement $node) { - - $components = array(); - foreach($node->childNodes as $childNode) { - if (DAV\XMLUtil::toClarkNotation($childNode)==='{' . CalDAV\Plugin::NS_CALDAV . '}comp') { - $components[] = $childNode->getAttribute('name'); - } - } - return new self($components); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Property/SupportedCalendarData.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Property/SupportedCalendarData.php deleted file mode 100644 index efab80a915..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Property/SupportedCalendarData.php +++ /dev/null @@ -1,40 +0,0 @@ -ownerDocument; - - $prefix = isset($server->xmlNamespaces[Plugin::NS_CALDAV])?$server->xmlNamespaces[Plugin::NS_CALDAV]:'cal'; - - $caldata = $doc->createElement($prefix . ':calendar-data'); - $caldata->setAttribute('content-type','text/calendar'); - $caldata->setAttribute('version','2.0'); - - $node->appendChild($caldata); - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Property/SupportedCollationSet.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Property/SupportedCollationSet.php deleted file mode 100644 index 63c37fe5ae..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Property/SupportedCollationSet.php +++ /dev/null @@ -1,45 +0,0 @@ -ownerDocument; - - $prefix = $node->lookupPrefix('urn:ietf:params:xml:ns:caldav'); - if (!$prefix) $prefix = 'cal'; - - $node->appendChild( - $doc->createElement($prefix . ':supported-collation','i;ascii-casemap') - ); - $node->appendChild( - $doc->createElement($prefix . ':supported-collation','i;octet') - ); - $node->appendChild( - $doc->createElement($prefix . ':supported-collation','i;unicode-casemap') - ); - - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Schedule/IMip.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Schedule/IMip.php deleted file mode 100644 index b2b0d98b1b..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Schedule/IMip.php +++ /dev/null @@ -1,111 +0,0 @@ -senderEmail = $senderEmail; - - } - - /** - * Sends one or more iTip messages through email. - * - * @param string $originator Originator Email - * @param array $recipients Array of email addresses - * @param VObject\Component $vObject - * @param string $principal Principal Url of the originator - * @return void - */ - public function sendMessage($originator, array $recipients, VObject\Component $vObject, $principal) { - - foreach($recipients as $recipient) { - - $to = $recipient; - $replyTo = $originator; - $subject = 'SabreDAV iTIP message'; - - switch(strtoupper($vObject->METHOD)) { - case 'REPLY' : - $subject = 'Response for: ' . $vObject->VEVENT->SUMMARY; - break; - case 'REQUEST' : - $subject = 'Invitation for: ' .$vObject->VEVENT->SUMMARY; - break; - case 'CANCEL' : - $subject = 'Cancelled event: ' . $vObject->VEVENT->SUMMARY; - break; - } - - $headers = array(); - $headers[] = 'Reply-To: ' . $replyTo; - $headers[] = 'From: ' . $this->senderEmail; - $headers[] = 'Content-Type: text/calendar; method=' . (string)$vObject->method . '; charset=utf-8'; - if (DAV\Server::$exposeVersion) { - $headers[] = 'X-Sabre-Version: ' . DAV\Version::VERSION . '-' . DAV\Version::STABILITY; - } - - $vcalBody = $vObject->serialize(); - - $this->mail($to, $subject, $vcalBody, $headers); - - } - - } - - // @codeCoverageIgnoreStart - // This is deemed untestable in a reasonable manner - - /** - * This function is reponsible for sending the actual email. - * - * @param string $to Recipient email address - * @param string $subject Subject of the email - * @param string $body iCalendar body - * @param array $headers List of headers - * @return void - */ - protected function mail($to, $subject, $body, array $headers) { - - - mail($to, $subject, $body, implode("\r\n", $headers)); - - } - - // @codeCoverageIgnoreEnd - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Schedule/IOutbox.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Schedule/IOutbox.php deleted file mode 100644 index 7341eaa85f..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Schedule/IOutbox.php +++ /dev/null @@ -1,16 +0,0 @@ -principalUri = $principalUri; - - } - - /** - * Returns the name of the node. - * - * This is used to generate the url. - * - * @return string - */ - public function getName() { - - return 'outbox'; - - } - - /** - * Returns an array with all the child nodes - * - * @return \Sabre\DAV\INode[] - */ - public function getChildren() { - - return array(); - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->principalUri; - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - return array( - array( - 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-query-freebusy', - 'principal' => $this->getOwner(), - 'protected' => true, - ), - array( - 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-post-vevent', - 'principal' => $this->getOwner(), - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->getOwner(), - 'protected' => true, - ), - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new DAV\Exception\MethodNotAllowed('You\'re not allowed to update the ACL'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - $default = DAVACL\Plugin::getDefaultSupportedPrivilegeSet(); - $default['aggregates'][] = array( - 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-query-freebusy', - ); - $default['aggregates'][] = array( - 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-post-vevent', - ); - - return $default; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/ShareableCalendar.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/ShareableCalendar.php deleted file mode 100644 index cabf7eb955..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/ShareableCalendar.php +++ /dev/null @@ -1,72 +0,0 @@ -caldavBackend->updateShares($this->calendarInfo['id'], $add, $remove); - - } - - /** - * Returns the list of people whom this calendar is shared with. - * - * Every element in this array should have the following properties: - * * href - Often a mailto: address - * * commonName - Optional, for example a first + last name - * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants. - * * readOnly - boolean - * * summary - Optional, a description for the share - * - * @return array - */ - public function getShares() { - - return $this->caldavBackend->getShares($this->calendarInfo['id']); - - } - - /** - * Marks this calendar as published. - * - * Publishing a calendar should automatically create a read-only, public, - * subscribable calendar. - * - * @param bool $value - * @return void - */ - public function setPublishStatus($value) { - - $this->caldavBackend->setPublishStatus($this->calendarInfo['id'], $value); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/SharedCalendar.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/SharedCalendar.php deleted file mode 100644 index 79eda43abb..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/SharedCalendar.php +++ /dev/null @@ -1,116 +0,0 @@ -calendarInfo['{http://calendarserver.org/ns/}shared-url']; - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->calendarInfo['{http://sabredav.org/ns}owner-principal']; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - // The top-level ACL only contains access information for the true - // owner of the calendar, so we need to add the information for the - // sharee. - $acl = parent::getACL(); - $acl[] = array( - 'privilege' => '{DAV:}read', - 'principal' => $this->calendarInfo['principaluri'], - 'protected' => true, - ); - if (!$this->calendarInfo['{http://sabredav.org/ns}read-only']) { - $acl[] = array( - 'privilege' => '{DAV:}write', - 'principal' => $this->calendarInfo['principaluri'], - 'protected' => true, - ); - } - return $acl; - - } - - /** - * Returns the list of people whom this calendar is shared with. - * - * Every element in this array should have the following properties: - * * href - Often a mailto: address - * * commonName - Optional, for example a first + last name - * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants. - * * readOnly - boolean - * * summary - Optional, a description for the share - * - * @return array - */ - public function getShares() { - - return $this->caldavBackend->getShares($this->calendarInfo['id']); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/SharingPlugin.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/SharingPlugin.php deleted file mode 100644 index e869cb278b..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/SharingPlugin.php +++ /dev/null @@ -1,526 +0,0 @@ -server = $server; - $server->resourceTypeMapping['Sabre\\CalDAV\\ISharedCalendar'] = '{' . Plugin::NS_CALENDARSERVER . '}shared'; - - array_push( - $this->server->protectedProperties, - '{' . Plugin::NS_CALENDARSERVER . '}invite', - '{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes', - '{' . Plugin::NS_CALENDARSERVER . '}shared-url' - ); - - $this->server->subscribeEvent('beforeGetProperties', array($this, 'beforeGetProperties')); - $this->server->subscribeEvent('afterGetProperties', array($this, 'afterGetProperties')); - $this->server->subscribeEvent('updateProperties', array($this, 'updateProperties')); - $this->server->subscribeEvent('unknownMethod', array($this,'unknownMethod')); - - } - - /** - * This event is triggered when properties are requested for a certain - * node. - * - * This allows us to inject any properties early. - * - * @param string $path - * @param DAV\INode $node - * @param array $requestedProperties - * @param array $returnedProperties - * @return void - */ - public function beforeGetProperties($path, DAV\INode $node, &$requestedProperties, &$returnedProperties) { - - if ($node instanceof IShareableCalendar) { - if (($index = array_search('{' . Plugin::NS_CALENDARSERVER . '}invite', $requestedProperties))!==false) { - - unset($requestedProperties[$index]); - $returnedProperties[200]['{' . Plugin::NS_CALENDARSERVER . '}invite'] = - new Property\Invite( - $node->getShares() - ); - - } - - } - - if ($node instanceof ISharedCalendar) { - - if (($index = array_search('{' . Plugin::NS_CALENDARSERVER . '}shared-url', $requestedProperties))!==false) { - - unset($requestedProperties[$index]); - $returnedProperties[200]['{' . Plugin::NS_CALENDARSERVER . '}shared-url'] = - new DAV\Property\Href( - $node->getSharedUrl() - ); - - } - // The 'invite' property is slightly different for the 'shared' - // instance of the calendar, as it also contains the owner - // information. - if (($index = array_search('{' . Plugin::NS_CALENDARSERVER . '}invite', $requestedProperties))!==false) { - - unset($requestedProperties[$index]); - - // Fetching owner information - $props = $this->server->getPropertiesForPath($node->getOwner(), array( - '{http://sabredav.org/ns}email-address', - '{DAV:}displayname', - ), 1); - - $ownerInfo = array( - 'href' => $node->getOwner(), - ); - - if (isset($props[0][200])) { - - // We're mapping the internal webdav properties to the - // elements caldav-sharing expects. - if (isset($props[0][200]['{http://sabredav.org/ns}email-address'])) { - $ownerInfo['href'] = 'mailto:' . $props[0][200]['{http://sabredav.org/ns}email-address']; - } - if (isset($props[0][200]['{DAV:}displayname'])) { - $ownerInfo['commonName'] = $props[0][200]['{DAV:}displayname']; - } - - } - - $returnedProperties[200]['{' . Plugin::NS_CALENDARSERVER . '}invite'] = - new Property\Invite( - $node->getShares(), - $ownerInfo - ); - - } - - - } - - } - - /** - * This method is triggered *after* all properties have been retrieved. - * This allows us to inject the correct resourcetype for calendars that - * have been shared. - * - * @param string $path - * @param array $properties - * @param DAV\INode $node - * @return void - */ - public function afterGetProperties($path, &$properties, DAV\INode $node) { - - if ($node instanceof IShareableCalendar) { - if (isset($properties[200]['{DAV:}resourcetype'])) { - if (count($node->getShares())>0) { - $properties[200]['{DAV:}resourcetype']->add( - '{' . Plugin::NS_CALENDARSERVER . '}shared-owner' - ); - } - } - $propName = '{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes'; - if (array_key_exists($propName, $properties[404])) { - unset($properties[404][$propName]); - $properties[200][$propName] = new Property\AllowedSharingModes(true,false); - } - - } - - } - - /** - * This method is trigged when a user attempts to update a node's - * properties. - * - * A previous draft of the sharing spec stated that it was possible to use - * PROPPATCH to remove 'shared-owner' from the resourcetype, thus unsharing - * the calendar. - * - * Even though this is no longer in the current spec, we keep this around - * because OS X 10.7 may still make use of this feature. - * - * @param array $mutations - * @param array $result - * @param DAV\INode $node - * @return void - */ - public function updateProperties(array &$mutations, array &$result, DAV\INode $node) { - - if (!$node instanceof IShareableCalendar) - return; - - if (!isset($mutations['{DAV:}resourcetype'])) { - return; - } - - // Only doing something if shared-owner is indeed not in the list. - if($mutations['{DAV:}resourcetype']->is('{' . Plugin::NS_CALENDARSERVER . '}shared-owner')) return; - - $shares = $node->getShares(); - $remove = array(); - foreach($shares as $share) { - $remove[] = $share['href']; - } - $node->updateShares(array(), $remove); - - // We're marking this update as 200 OK - $result[200]['{DAV:}resourcetype'] = null; - - // Removing it from the mutations list - unset($mutations['{DAV:}resourcetype']); - - } - - /** - * This event is triggered when the server didn't know how to handle a - * certain request. - * - * We intercept this to handle POST requests on calendars. - * - * @param string $method - * @param string $uri - * @return null|bool - */ - public function unknownMethod($method, $uri) { - - if ($method!=='POST') { - return; - } - - // Only handling xml - $contentType = $this->server->httpRequest->getHeader('Content-Type'); - if (strpos($contentType,'application/xml')===false && strpos($contentType,'text/xml')===false) - return; - - // Making sure the node exists - try { - $node = $this->server->tree->getNodeForPath($uri); - } catch (DAV\Exception\NotFound $e) { - return; - } - - $requestBody = $this->server->httpRequest->getBody(true); - - // If this request handler could not deal with this POST request, it - // will return 'null' and other plugins get a chance to handle the - // request. - // - // However, we already requested the full body. This is a problem, - // because a body can only be read once. This is why we preemptively - // re-populated the request body with the existing data. - $this->server->httpRequest->setBody($requestBody); - - $dom = DAV\XMLUtil::loadDOMDocument($requestBody); - - $documentType = DAV\XMLUtil::toClarkNotation($dom->firstChild); - - switch($documentType) { - - // Dealing with the 'share' document, which modified invitees on a - // calendar. - case '{' . Plugin::NS_CALENDARSERVER . '}share' : - - // We can only deal with IShareableCalendar objects - if (!$node instanceof IShareableCalendar) { - return; - } - - // Getting ACL info - $acl = $this->server->getPlugin('acl'); - - // If there's no ACL support, we allow everything - if ($acl) { - $acl->checkPrivileges($uri, '{DAV:}write'); - } - - $mutations = $this->parseShareRequest($dom); - - $node->updateShares($mutations[0], $mutations[1]); - - $this->server->httpResponse->sendStatus(200); - // Adding this because sending a response body may cause issues, - // and I wanted some type of indicator the response was handled. - $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well'); - - // Breaking the event chain - return false; - - // The invite-reply document is sent when the user replies to an - // invitation of a calendar share. - case '{'. Plugin::NS_CALENDARSERVER.'}invite-reply' : - - // This only works on the calendar-home-root node. - if (!$node instanceof UserCalendars) { - return; - } - - // Getting ACL info - $acl = $this->server->getPlugin('acl'); - - // If there's no ACL support, we allow everything - if ($acl) { - $acl->checkPrivileges($uri, '{DAV:}write'); - } - - $message = $this->parseInviteReplyRequest($dom); - - $url = $node->shareReply( - $message['href'], - $message['status'], - $message['calendarUri'], - $message['inReplyTo'], - $message['summary'] - ); - - $this->server->httpResponse->sendStatus(200); - // Adding this because sending a response body may cause issues, - // and I wanted some type of indicator the response was handled. - $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well'); - - if ($url) { - $dom = new \DOMDocument('1.0', 'UTF-8'); - $dom->formatOutput = true; - - $root = $dom->createElement('cs:shared-as'); - foreach($this->server->xmlNamespaces as $namespace => $prefix) { - $root->setAttribute('xmlns:' . $prefix, $namespace); - } - - $dom->appendChild($root); - $href = new DAV\Property\Href($url); - - $href->serialize($this->server, $root); - $this->server->httpResponse->setHeader('Content-Type','application/xml'); - $this->server->httpResponse->sendBody($dom->saveXML()); - - } - - // Breaking the event chain - return false; - - case '{' . Plugin::NS_CALENDARSERVER . '}publish-calendar' : - - // We can only deal with IShareableCalendar objects - if (!$node instanceof IShareableCalendar) { - return; - } - - // Getting ACL info - $acl = $this->server->getPlugin('acl'); - - // If there's no ACL support, we allow everything - if ($acl) { - $acl->checkPrivileges($uri, '{DAV:}write'); - } - - $node->setPublishStatus(true); - - // iCloud sends back the 202, so we will too. - $this->server->httpResponse->sendStatus(202); - - // Adding this because sending a response body may cause issues, - // and I wanted some type of indicator the response was handled. - $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well'); - - // Breaking the event chain - return false; - - case '{' . Plugin::NS_CALENDARSERVER . '}unpublish-calendar' : - - // We can only deal with IShareableCalendar objects - if (!$node instanceof IShareableCalendar) { - return; - } - - // Getting ACL info - $acl = $this->server->getPlugin('acl'); - - // If there's no ACL support, we allow everything - if ($acl) { - $acl->checkPrivileges($uri, '{DAV:}write'); - } - - $node->setPublishStatus(false); - - $this->server->httpResponse->sendStatus(200); - - // Adding this because sending a response body may cause issues, - // and I wanted some type of indicator the response was handled. - $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well'); - - // Breaking the event chain - return false; - - } - - - - } - - /** - * Parses the 'share' POST request. - * - * This method returns an array, containing two arrays. - * The first array is a list of new sharees. Every element is a struct - * containing a: - * * href element. (usually a mailto: address) - * * commonName element (often a first and lastname, but can also be - * false) - * * readOnly (true or false) - * * summary (A description of the share, can also be false) - * - * The second array is a list of sharees that are to be removed. This is - * just a simple array with 'hrefs'. - * - * @param \DOMDocument $dom - * @return array - */ - protected function parseShareRequest(\DOMDocument $dom) { - - $xpath = new \DOMXPath($dom); - $xpath->registerNamespace('cs', Plugin::NS_CALENDARSERVER); - $xpath->registerNamespace('d', 'urn:DAV'); - - $set = array(); - $elems = $xpath->query('cs:set'); - - for($i=0; $i < $elems->length; $i++) { - - $xset = $elems->item($i); - $set[] = array( - 'href' => $xpath->evaluate('string(d:href)', $xset), - 'commonName' => $xpath->evaluate('string(cs:common-name)', $xset), - 'summary' => $xpath->evaluate('string(cs:summary)', $xset), - 'readOnly' => $xpath->evaluate('boolean(cs:read)', $xset)!==false - ); - - } - - $remove = array(); - $elems = $xpath->query('cs:remove'); - - for($i=0; $i < $elems->length; $i++) { - - $xremove = $elems->item($i); - $remove[] = $xpath->evaluate('string(d:href)', $xremove); - - } - - return array($set, $remove); - - } - - /** - * Parses the 'invite-reply' POST request. - * - * This method returns an array, containing the following properties: - * * href - The sharee who is replying - * * status - One of the self::STATUS_* constants - * * calendarUri - The url of the shared calendar - * * inReplyTo - The unique id of the share invitation. - * * summary - Optional description of the reply. - * - * @param \DOMDocument $dom - * @return array - */ - protected function parseInviteReplyRequest(\DOMDocument $dom) { - - $xpath = new \DOMXPath($dom); - $xpath->registerNamespace('cs', Plugin::NS_CALENDARSERVER); - $xpath->registerNamespace('d', 'urn:DAV'); - - $hostHref = $xpath->evaluate('string(cs:hosturl/d:href)'); - if (!$hostHref) { - throw new DAV\Exception\BadRequest('The {' . Plugin::NS_CALENDARSERVER . '}hosturl/{DAV:}href element is required'); - } - - return array( - 'href' => $xpath->evaluate('string(d:href)'), - 'calendarUri' => $this->server->calculateUri($hostHref), - 'inReplyTo' => $xpath->evaluate('string(cs:in-reply-to)'), - 'summary' => $xpath->evaluate('string(cs:summary)'), - 'status' => $xpath->evaluate('boolean(cs:invite-accepted)')?self::STATUS_ACCEPTED:self::STATUS_DECLINED - ); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/UserCalendars.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/UserCalendars.php deleted file mode 100644 index 6e700eb048..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/UserCalendars.php +++ /dev/null @@ -1,342 +0,0 @@ -caldavBackend = $caldavBackend; - $this->principalInfo = $principalInfo; - - } - - /** - * Returns the name of this object - * - * @return string - */ - public function getName() { - - list(,$name) = DAV\URLUtil::splitPath($this->principalInfo['uri']); - return $name; - - } - - /** - * Updates the name of this object - * - * @param string $name - * @return void - */ - public function setName($name) { - - throw new DAV\Exception\Forbidden(); - - } - - /** - * Deletes this object - * - * @return void - */ - public function delete() { - - throw new DAV\Exception\Forbidden(); - - } - - /** - * Returns the last modification date - * - * @return int - */ - public function getLastModified() { - - return null; - - } - - /** - * Creates a new file under this object. - * - * This is currently not allowed - * - * @param string $filename - * @param resource $data - * @return void - */ - public function createFile($filename, $data=null) { - - throw new DAV\Exception\MethodNotAllowed('Creating new files in this collection is not supported'); - - } - - /** - * Creates a new directory under this object. - * - * This is currently not allowed. - * - * @param string $filename - * @return void - */ - public function createDirectory($filename) { - - throw new DAV\Exception\MethodNotAllowed('Creating new collections in this collection is not supported'); - - } - - /** - * Returns a single calendar, by name - * - * @param string $name - * @todo needs optimizing - * @return Calendar - */ - public function getChild($name) { - - foreach($this->getChildren() as $child) { - if ($name==$child->getName()) - return $child; - - } - throw new DAV\Exception\NotFound('Calendar with name \'' . $name . '\' could not be found'); - - } - - /** - * Checks if a calendar exists. - * - * @param string $name - * @todo needs optimizing - * @return bool - */ - public function childExists($name) { - - foreach($this->getChildren() as $child) { - if ($name==$child->getName()) - return true; - - } - return false; - - } - - /** - * Returns a list of calendars - * - * @return array - */ - public function getChildren() { - - $calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']); - $objs = array(); - foreach($calendars as $calendar) { - if ($this->caldavBackend instanceof Backend\SharingSupport) { - if (isset($calendar['{http://calendarserver.org/ns/}shared-url'])) { - $objs[] = new SharedCalendar($this->caldavBackend, $calendar); - } else { - $objs[] = new ShareableCalendar($this->caldavBackend, $calendar); - } - } else { - $objs[] = new Calendar($this->caldavBackend, $calendar); - } - } - $objs[] = new Schedule\Outbox($this->principalInfo['uri']); - - // We're adding a notifications node, if it's supported by the backend. - if ($this->caldavBackend instanceof Backend\NotificationSupport) { - $objs[] = new Notifications\Collection($this->caldavBackend, $this->principalInfo['uri']); - } - return $objs; - - } - - /** - * Creates a new calendar - * - * @param string $name - * @param array $resourceType - * @param array $properties - * @return void - */ - public function createExtendedCollection($name, array $resourceType, array $properties) { - - $isCalendar = false; - foreach($resourceType as $rt) { - switch ($rt) { - case '{DAV:}collection' : - case '{http://calendarserver.org/ns/}shared-owner' : - // ignore - break; - case '{urn:ietf:params:xml:ns:caldav}calendar' : - $isCalendar = true; - break; - default : - throw new DAV\Exception\InvalidResourceType('Unknown resourceType: ' . $rt); - } - } - if (!$isCalendar) { - throw new DAV\Exception\InvalidResourceType('You can only create calendars in this collection'); - } - $this->caldavBackend->createCalendar($this->principalInfo['uri'], $name, $properties); - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->principalInfo['uri']; - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - return array( - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->principalInfo['uri'], - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}write', - 'principal' => $this->principalInfo['uri'], - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}write', - 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-read', - 'protected' => true, - ), - - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - return null; - - } - - /** - * This method is called when a user replied to a request to share. - * - * This method should return the url of the newly created calendar if the - * share was accepted. - * - * @param string href The sharee who is replying (often a mailto: address) - * @param int status One of the SharingPlugin::STATUS_* constants - * @param string $calendarUri The url to the calendar thats being shared - * @param string $inReplyTo The unique id this message is a response to - * @param string $summary A description of the reply - * @return null|string - */ - public function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null) { - - if (!$this->caldavBackend instanceof Backend\SharingSupport) { - throw new DAV\Exception\NotImplemented('Sharing support is not implemented by this backend.'); - } - - return $this->caldavBackend->shareReply($href, $status, $calendarUri, $inReplyTo, $summary); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Version.php b/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Version.php deleted file mode 100644 index f30fc20ea3..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CalDAV/Version.php +++ /dev/null @@ -1,24 +0,0 @@ -carddavBackend = $carddavBackend; - $this->addressBookInfo = $addressBookInfo; - - } - - /** - * Returns the name of the addressbook - * - * @return string - */ - public function getName() { - - return $this->addressBookInfo['uri']; - - } - - /** - * Returns a card - * - * @param string $name - * @return \ICard - */ - public function getChild($name) { - - $obj = $this->carddavBackend->getCard($this->addressBookInfo['id'],$name); - if (!$obj) throw new DAV\Exception\NotFound('Card not found'); - return new Card($this->carddavBackend,$this->addressBookInfo,$obj); - - } - - /** - * Returns the full list of cards - * - * @return array - */ - public function getChildren() { - - $objs = $this->carddavBackend->getCards($this->addressBookInfo['id']); - $children = array(); - foreach($objs as $obj) { - $children[] = new Card($this->carddavBackend,$this->addressBookInfo,$obj); - } - return $children; - - } - - /** - * Creates a new directory - * - * We actually block this, as subdirectories are not allowed in addressbooks. - * - * @param string $name - * @return void - */ - public function createDirectory($name) { - - throw new DAV\Exception\MethodNotAllowed('Creating collections in addressbooks is not allowed'); - - } - - /** - * Creates a new file - * - * The contents of the new file must be a valid VCARD. - * - * This method may return an ETag. - * - * @param string $name - * @param resource $vcardData - * @return string|null - */ - public function createFile($name,$vcardData = null) { - - if (is_resource($vcardData)) { - $vcardData = stream_get_contents($vcardData); - } - // Converting to UTF-8, if needed - $vcardData = DAV\StringUtil::ensureUTF8($vcardData); - - return $this->carddavBackend->createCard($this->addressBookInfo['id'],$name,$vcardData); - - } - - /** - * Deletes the entire addressbook. - * - * @return void - */ - public function delete() { - - $this->carddavBackend->deleteAddressBook($this->addressBookInfo['id']); - - } - - /** - * Renames the addressbook - * - * @param string $newName - * @return void - */ - public function setName($newName) { - - throw new DAV\Exception\MethodNotAllowed('Renaming addressbooks is not yet supported'); - - } - - /** - * Returns the last modification date as a unix timestamp. - * - * @return void - */ - public function getLastModified() { - - return null; - - } - - /** - * Updates properties on this node, - * - * The properties array uses the propertyName in clark-notation as key, - * and the array value for the property value. In the case a property - * should be deleted, the property value will be null. - * - * This method must be atomic. If one property cannot be changed, the - * entire operation must fail. - * - * If the operation was successful, true can be returned. - * If the operation failed, false can be returned. - * - * Deletion of a non-existent property is always successful. - * - * Lastly, it is optional to return detailed information about any - * failures. In this case an array should be returned with the following - * structure: - * - * array( - * 403 => array( - * '{DAV:}displayname' => null, - * ), - * 424 => array( - * '{DAV:}owner' => null, - * ) - * ) - * - * In this example it was forbidden to update {DAV:}displayname. - * (403 Forbidden), which in turn also caused {DAV:}owner to fail - * (424 Failed Dependency) because the request needs to be atomic. - * - * @param array $mutations - * @return bool|array - */ - public function updateProperties($mutations) { - - return $this->carddavBackend->updateAddressBook($this->addressBookInfo['id'], $mutations); - - } - - /** - * Returns a list of properties for this nodes. - * - * The properties list is a list of propertynames the client requested, - * encoded in clark-notation {xmlnamespace}tagname - * - * If the array is empty, it means 'all properties' were requested. - * - * @param array $properties - * @return array - */ - public function getProperties($properties) { - - $response = array(); - foreach($properties as $propertyName) { - - if (isset($this->addressBookInfo[$propertyName])) { - - $response[$propertyName] = $this->addressBookInfo[$propertyName]; - - } - - } - - return $response; - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->addressBookInfo['principaluri']; - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - return array( - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->addressBookInfo['principaluri'], - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}write', - 'principal' => $this->addressBookInfo['principaluri'], - 'protected' => true, - ), - - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - return null; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CardDAV/AddressBookQueryParser.php b/core/src/core/classes/sabredav/lib/Sabre/CardDAV/AddressBookQueryParser.php deleted file mode 100644 index 3277d98b09..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CardDAV/AddressBookQueryParser.php +++ /dev/null @@ -1,221 +0,0 @@ -dom = $dom; - - $this->xpath = new \DOMXPath($dom); - $this->xpath->registerNameSpace('card',Plugin::NS_CARDDAV); - - } - - /** - * Parses the request. - * - * @return void - */ - public function parse() { - - $filterNode = null; - - $limit = $this->xpath->evaluate('number(/card:addressbook-query/card:limit/card:nresults)'); - if (is_nan($limit)) $limit = null; - - $filter = $this->xpath->query('/card:addressbook-query/card:filter'); - - // According to the CardDAV spec there needs to be exactly 1 filter - // element. However, KDE 4.8.2 contains a bug that will encode 0 filter - // elements, so this is a workaround for that. - // - // See: https://bugs.kde.org/show_bug.cgi?id=300047 - if ($filter->length === 0) { - $test = null; - $filter = null; - } elseif ($filter->length === 1) { - $filter = $filter->item(0); - $test = $this->xpath->evaluate('string(@test)', $filter); - } else { - throw new DAV\Exception\BadRequest('Only one filter element is allowed'); - } - - if (!$test) $test = self::TEST_ANYOF; - if ($test !== self::TEST_ANYOF && $test !== self::TEST_ALLOF) { - throw new DAV\Exception\BadRequest('The test attribute must either hold "anyof" or "allof"'); - } - - $propFilters = array(); - - $propFilterNodes = $this->xpath->query('card:prop-filter', $filter); - for($ii=0; $ii < $propFilterNodes->length; $ii++) { - - $propFilters[] = $this->parsePropFilterNode($propFilterNodes->item($ii)); - - - } - - $this->filters = $propFilters; - $this->limit = $limit; - $this->requestedProperties = array_keys(DAV\XMLUtil::parseProperties($this->dom->firstChild)); - $this->test = $test; - - } - - /** - * Parses the prop-filter xml element - * - * @param \DOMElement $propFilterNode - * @return array - */ - protected function parsePropFilterNode(\DOMElement $propFilterNode) { - - $propFilter = array(); - $propFilter['name'] = $propFilterNode->getAttribute('name'); - $propFilter['test'] = $propFilterNode->getAttribute('test'); - if (!$propFilter['test']) $propFilter['test'] = 'anyof'; - - $propFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $propFilterNode)->length>0; - - $paramFilterNodes = $this->xpath->query('card:param-filter', $propFilterNode); - - $propFilter['param-filters'] = array(); - - - for($ii=0;$ii<$paramFilterNodes->length;$ii++) { - - $propFilter['param-filters'][] = $this->parseParamFilterNode($paramFilterNodes->item($ii)); - - } - $propFilter['text-matches'] = array(); - $textMatchNodes = $this->xpath->query('card:text-match', $propFilterNode); - - for($ii=0;$ii<$textMatchNodes->length;$ii++) { - - $propFilter['text-matches'][] = $this->parseTextMatchNode($textMatchNodes->item($ii)); - - } - - return $propFilter; - - } - - /** - * Parses the param-filter element - * - * @param \DOMElement $paramFilterNode - * @return array - */ - public function parseParamFilterNode(\DOMElement $paramFilterNode) { - - $paramFilter = array(); - $paramFilter['name'] = $paramFilterNode->getAttribute('name'); - $paramFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $paramFilterNode)->length>0; - $paramFilter['text-match'] = null; - - $textMatch = $this->xpath->query('card:text-match', $paramFilterNode); - if ($textMatch->length>0) { - $paramFilter['text-match'] = $this->parseTextMatchNode($textMatch->item(0)); - } - - return $paramFilter; - - } - - /** - * Text match - * - * @param \DOMElement $textMatchNode - * @return array - */ - public function parseTextMatchNode(\DOMElement $textMatchNode) { - - $matchType = $textMatchNode->getAttribute('match-type'); - if (!$matchType) $matchType = 'contains'; - - if (!in_array($matchType, array('contains', 'equals', 'starts-with', 'ends-with'))) { - throw new DAV\Exception\BadRequest('Unknown match-type: ' . $matchType); - } - - $negateCondition = $textMatchNode->getAttribute('negate-condition'); - $negateCondition = $negateCondition==='yes'; - $collation = $textMatchNode->getAttribute('collation'); - if (!$collation) $collation = 'i;unicode-casemap'; - - return array( - 'negate-condition' => $negateCondition, - 'collation' => $collation, - 'match-type' => $matchType, - 'value' => $textMatchNode->nodeValue - ); - - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CardDAV/AddressBookRoot.php b/core/src/core/classes/sabredav/lib/Sabre/CardDAV/AddressBookRoot.php deleted file mode 100644 index 789abbc5d8..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CardDAV/AddressBookRoot.php +++ /dev/null @@ -1,80 +0,0 @@ -carddavBackend = $carddavBackend; - parent::__construct($principalBackend, $principalPrefix); - - } - - /** - * Returns the name of the node - * - * @return string - */ - public function getName() { - - return Plugin::ADDRESSBOOK_ROOT; - - } - - /** - * This method returns a node for a principal. - * - * The passed array contains principal information, and is guaranteed to - * at least contain a uri item. Other properties may or may not be - * supplied by the authentication backend. - * - * @param array $principal - * @return \Sabre\DAV\INode - */ - public function getChildForPrincipal(array $principal) { - - return new UserAddressBooks($this->carddavBackend, $principal['uri']); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CardDAV/Backend/AbstractBackend.php b/core/src/core/classes/sabredav/lib/Sabre/CardDAV/Backend/AbstractBackend.php deleted file mode 100644 index 46909efef1..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CardDAV/Backend/AbstractBackend.php +++ /dev/null @@ -1,18 +0,0 @@ -pdo = $pdo; - $this->addressBooksTableName = $addressBooksTableName; - $this->cardsTableName = $cardsTableName; - - } - - /** - * Returns the list of addressbooks for a specific user. - * - * @param string $principalUri - * @return array - */ - public function getAddressBooksForUser($principalUri) { - - $stmt = $this->pdo->prepare('SELECT id, uri, displayname, principaluri, description, ctag FROM '.$this->addressBooksTableName.' WHERE principaluri = ?'); - $stmt->execute(array($principalUri)); - - $addressBooks = array(); - - foreach($stmt->fetchAll() as $row) { - - $addressBooks[] = array( - 'id' => $row['id'], - 'uri' => $row['uri'], - 'principaluri' => $row['principaluri'], - '{DAV:}displayname' => $row['displayname'], - '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'], - '{http://calendarserver.org/ns/}getctag' => $row['ctag'], - '{' . CardDAV\Plugin::NS_CARDDAV . '}supported-address-data' => - new CardDAV\Property\SupportedAddressData(), - ); - - } - - return $addressBooks; - - } - - - /** - * Updates an addressbook's properties - * - * See Sabre\DAV\IProperties for a description of the mutations array, as - * well as the return value. - * - * @param mixed $addressBookId - * @param array $mutations - * @see Sabre\DAV\IProperties::updateProperties - * @return bool|array - */ - public function updateAddressBook($addressBookId, array $mutations) { - - $updates = array(); - - foreach($mutations as $property=>$newValue) { - - switch($property) { - case '{DAV:}displayname' : - $updates['displayname'] = $newValue; - break; - case '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' : - $updates['description'] = $newValue; - break; - default : - // If any unsupported values were being updated, we must - // let the entire request fail. - return false; - } - - } - - // No values are being updated? - if (!$updates) { - return false; - } - - $query = 'UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 '; - foreach($updates as $key=>$value) { - $query.=', `' . $key . '` = :' . $key . ' '; - } - $query.=' WHERE id = :addressbookid'; - - $stmt = $this->pdo->prepare($query); - $updates['addressbookid'] = $addressBookId; - - $stmt->execute($updates); - - return true; - - } - - /** - * Creates a new address book - * - * @param string $principalUri - * @param string $url Just the 'basename' of the url. - * @param array $properties - * @return void - */ - public function createAddressBook($principalUri, $url, array $properties) { - - $values = array( - 'displayname' => null, - 'description' => null, - 'principaluri' => $principalUri, - 'uri' => $url, - ); - - foreach($properties as $property=>$newValue) { - - switch($property) { - case '{DAV:}displayname' : - $values['displayname'] = $newValue; - break; - case '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' : - $values['description'] = $newValue; - break; - default : - throw new DAV\Exception\BadRequest('Unknown property: ' . $property); - } - - } - - $query = 'INSERT INTO ' . $this->addressBooksTableName . ' (uri, displayname, description, principaluri, ctag) VALUES (:uri, :displayname, :description, :principaluri, 1)'; - $stmt = $this->pdo->prepare($query); - $stmt->execute($values); - - } - - /** - * Deletes an entire addressbook and all its contents - * - * @param int $addressBookId - * @return void - */ - public function deleteAddressBook($addressBookId) { - - $stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ?'); - $stmt->execute(array($addressBookId)); - - $stmt = $this->pdo->prepare('DELETE FROM ' . $this->addressBooksTableName . ' WHERE id = ?'); - $stmt->execute(array($addressBookId)); - - } - - /** - * Returns all cards for a specific addressbook id. - * - * This method should return the following properties for each card: - * * carddata - raw vcard data - * * uri - Some unique url - * * lastmodified - A unix timestamp - * - * It's recommended to also return the following properties: - * * etag - A unique etag. This must change every time the card changes. - * * size - The size of the card in bytes. - * - * If these last two properties are provided, less time will be spent - * calculating them. If they are specified, you can also ommit carddata. - * This may speed up certain requests, especially with large cards. - * - * @param mixed $addressbookId - * @return array - */ - public function getCards($addressbookId) { - - $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM ' . $this->cardsTableName . ' WHERE addressbookid = ?'); - $stmt->execute(array($addressbookId)); - - return $stmt->fetchAll(\PDO::FETCH_ASSOC); - - - } - - /** - * Returns a specfic card. - * - * The same set of properties must be returned as with getCards. The only - * exception is that 'carddata' is absolutely required. - * - * @param mixed $addressBookId - * @param string $cardUri - * @return array - */ - public function getCard($addressBookId, $cardUri) { - - $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ? LIMIT 1'); - $stmt->execute(array($addressBookId, $cardUri)); - - $result = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - return (count($result)>0?$result[0]:false); - - } - - /** - * Creates a new card. - * - * The addressbook id will be passed as the first argument. This is the - * same id as it is returned from the getAddressbooksForUser method. - * - * The cardUri is a base uri, and doesn't include the full path. The - * cardData argument is the vcard body, and is passed as a string. - * - * It is possible to return an ETag from this method. This ETag is for the - * newly created resource, and must be enclosed with double quotes (that - * is, the string itself must contain the double quotes). - * - * You should only return the ETag if you store the carddata as-is. If a - * subsequent GET request on the same card does not have the same body, - * byte-by-byte and you did return an ETag here, clients tend to get - * confused. - * - * If you don't return an ETag, you can just return null. - * - * @param mixed $addressBookId - * @param string $cardUri - * @param string $cardData - * @return string|null - */ - public function createCard($addressBookId, $cardUri, $cardData) { - - $stmt = $this->pdo->prepare('INSERT INTO ' . $this->cardsTableName . ' (carddata, uri, lastmodified, addressbookid) VALUES (?, ?, ?, ?)'); - - $result = $stmt->execute(array($cardData, $cardUri, time(), $addressBookId)); - - $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?'); - $stmt2->execute(array($addressBookId)); - - return '"' . md5($cardData) . '"'; - - } - - /** - * Updates a card. - * - * The addressbook id will be passed as the first argument. This is the - * same id as it is returned from the getAddressbooksForUser method. - * - * The cardUri is a base uri, and doesn't include the full path. The - * cardData argument is the vcard body, and is passed as a string. - * - * It is possible to return an ETag from this method. This ETag should - * match that of the updated resource, and must be enclosed with double - * quotes (that is: the string itself must contain the actual quotes). - * - * You should only return the ETag if you store the carddata as-is. If a - * subsequent GET request on the same card does not have the same body, - * byte-by-byte and you did return an ETag here, clients tend to get - * confused. - * - * If you don't return an ETag, you can just return null. - * - * @param mixed $addressBookId - * @param string $cardUri - * @param string $cardData - * @return string|null - */ - public function updateCard($addressBookId, $cardUri, $cardData) { - - $stmt = $this->pdo->prepare('UPDATE ' . $this->cardsTableName . ' SET carddata = ?, lastmodified = ? WHERE uri = ? AND addressbookid =?'); - $stmt->execute(array($cardData, time(), $cardUri, $addressBookId)); - - $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?'); - $stmt2->execute(array($addressBookId)); - - return '"' . md5($cardData) . '"'; - - } - - /** - * Deletes a card - * - * @param mixed $addressBookId - * @param string $cardUri - * @return bool - */ - public function deleteCard($addressBookId, $cardUri) { - - $stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ?'); - $stmt->execute(array($addressBookId, $cardUri)); - - $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?'); - $stmt2->execute(array($addressBookId)); - - return $stmt->rowCount()===1; - - } -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CardDAV/Card.php b/core/src/core/classes/sabredav/lib/Sabre/CardDAV/Card.php deleted file mode 100644 index cc65f76008..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CardDAV/Card.php +++ /dev/null @@ -1,260 +0,0 @@ -carddavBackend = $carddavBackend; - $this->addressBookInfo = $addressBookInfo; - $this->cardData = $cardData; - - } - - /** - * Returns the uri for this object - * - * @return string - */ - public function getName() { - - return $this->cardData['uri']; - - } - - /** - * Returns the VCard-formatted object - * - * @return string - */ - public function get() { - - // Pre-populating 'carddata' is optional. If we don't yet have it - // already, we fetch it from the backend. - if (!isset($this->cardData['carddata'])) { - $this->cardData = $this->carddavBackend->getCard($this->addressBookInfo['id'], $this->cardData['uri']); - } - return $this->cardData['carddata']; - - } - - /** - * Updates the VCard-formatted object - * - * @param string $cardData - * @return string|null - */ - public function put($cardData) { - - if (is_resource($cardData)) - $cardData = stream_get_contents($cardData); - - // Converting to UTF-8, if needed - $cardData = DAV\StringUtil::ensureUTF8($cardData); - - $etag = $this->carddavBackend->updateCard($this->addressBookInfo['id'],$this->cardData['uri'],$cardData); - $this->cardData['carddata'] = $cardData; - $this->cardData['etag'] = $etag; - - return $etag; - - } - - /** - * Deletes the card - * - * @return void - */ - public function delete() { - - $this->carddavBackend->deleteCard($this->addressBookInfo['id'],$this->cardData['uri']); - - } - - /** - * Returns the mime content-type - * - * @return string - */ - public function getContentType() { - - return 'text/x-vcard; charset=utf-8'; - - } - - /** - * Returns an ETag for this object - * - * @return string - */ - public function getETag() { - - if (isset($this->cardData['etag'])) { - return $this->cardData['etag']; - } else { - $data = $this->get(); - if (is_string($data)) { - return '"' . md5($data) . '"'; - } else { - // We refuse to calculate the md5 if it's a stream. - return null; - } - } - - } - - /** - * Returns the last modification date as a unix timestamp - * - * @return int - */ - public function getLastModified() { - - return isset($this->cardData['lastmodified'])?$this->cardData['lastmodified']:null; - - } - - /** - * Returns the size of this object in bytes - * - * @return int - */ - public function getSize() { - - if (array_key_exists('size', $this->cardData)) { - return $this->cardData['size']; - } else { - return strlen($this->get()); - } - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->addressBookInfo['principaluri']; - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - return array( - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->addressBookInfo['principaluri'], - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}write', - 'principal' => $this->addressBookInfo['principaluri'], - 'protected' => true, - ), - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - return null; - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/CardDAV/IAddressBook.php b/core/src/core/classes/sabredav/lib/Sabre/CardDAV/IAddressBook.php deleted file mode 100644 index e9e990cbdd..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CardDAV/IAddressBook.php +++ /dev/null @@ -1,20 +0,0 @@ -subscribeEvent('beforeGetProperties', array($this, 'beforeGetProperties')); - $server->subscribeEvent('afterGetProperties', array($this, 'afterGetProperties')); - $server->subscribeEvent('updateProperties', array($this, 'updateProperties')); - $server->subscribeEvent('report', array($this,'report')); - $server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel')); - $server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction')); - $server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent')); - $server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile')); - - /* Namespaces */ - $server->xmlNamespaces[self::NS_CARDDAV] = 'card'; - - /* Mapping Interfaces to {DAV:}resourcetype values */ - $server->resourceTypeMapping['Sabre\\CardDAV\\IAddressBook'] = '{' . self::NS_CARDDAV . '}addressbook'; - $server->resourceTypeMapping['Sabre\\CardDAV\\IDirectory'] = '{' . self::NS_CARDDAV . '}directory'; - - /* Adding properties that may never be changed */ - $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-address-data'; - $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}max-resource-size'; - $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}addressbook-home-set'; - $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-collation-set'; - - $server->propertyMap['{http://calendarserver.org/ns/}me-card'] = 'Sabre\\DAV\\Property\\Href'; - - $this->server = $server; - - } - - /** - * Returns a list of supported features. - * - * This is used in the DAV: header in the OPTIONS and PROPFIND requests. - * - * @return array - */ - public function getFeatures() { - - return array('addressbook'); - - } - - /** - * Returns a list of reports this plugin supports. - * - * This will be used in the {DAV:}supported-report-set property. - * Note that you still need to subscribe to the 'report' event to actually - * implement them - * - * @param string $uri - * @return array - */ - public function getSupportedReportSet($uri) { - - $node = $this->server->tree->getNodeForPath($uri); - if ($node instanceof IAddressBook || $node instanceof ICard) { - return array( - '{' . self::NS_CARDDAV . '}addressbook-multiget', - '{' . self::NS_CARDDAV . '}addressbook-query', - ); - } - return array(); - - } - - - /** - * Adds all CardDAV-specific properties - * - * @param string $path - * @param DAV\INode $node - * @param array $requestedProperties - * @param array $returnedProperties - * @return void - */ - public function beforeGetProperties($path, DAV\INode $node, array &$requestedProperties, array &$returnedProperties) { - - if ($node instanceof DAVACL\IPrincipal) { - - // calendar-home-set property - $addHome = '{' . self::NS_CARDDAV . '}addressbook-home-set'; - if (in_array($addHome,$requestedProperties)) { - $principalId = $node->getName(); - $addressbookHomePath = self::ADDRESSBOOK_ROOT . '/' . $principalId . '/'; - unset($requestedProperties[array_search($addHome, $requestedProperties)]); - $returnedProperties[200][$addHome] = new DAV\Property\Href($addressbookHomePath); - } - - $directories = '{' . self::NS_CARDDAV . '}directory-gateway'; - if ($this->directories && in_array($directories, $requestedProperties)) { - unset($requestedProperties[array_search($directories, $requestedProperties)]); - $returnedProperties[200][$directories] = new DAV\Property\HrefList($this->directories); - } - - } - - if ($node instanceof ICard) { - - // The address-data property is not supposed to be a 'real' - // property, but in large chunks of the spec it does act as such. - // Therefore we simply expose it as a property. - $addressDataProp = '{' . self::NS_CARDDAV . '}address-data'; - if (in_array($addressDataProp, $requestedProperties)) { - unset($requestedProperties[$addressDataProp]); - $val = $node->get(); - if (is_resource($val)) - $val = stream_get_contents($val); - - $returnedProperties[200][$addressDataProp] = $val; - - } - } - - if ($node instanceof UserAddressBooks) { - - $meCardProp = '{http://calendarserver.org/ns/}me-card'; - if (in_array($meCardProp, $requestedProperties)) { - - $props = $this->server->getProperties($node->getOwner(), array('{http://sabredav.org/ns}vcard-url')); - if (isset($props['{http://sabredav.org/ns}vcard-url'])) { - - $returnedProperties[200][$meCardProp] = new DAV\Property\Href( - $props['{http://sabredav.org/ns}vcard-url'] - ); - $pos = array_search($meCardProp, $requestedProperties); - unset($requestedProperties[$pos]); - - } - - } - - } - - } - - /** - * This event is triggered when a PROPPATCH method is executed - * - * @param array $mutations - * @param array $result - * @param DAV\INode $node - * @return bool - */ - public function updateProperties(&$mutations, &$result, DAV\INode $node) { - - if (!$node instanceof UserAddressBooks) { - return true; - } - - $meCard = '{http://calendarserver.org/ns/}me-card'; - - // The only property we care about - if (!isset($mutations[$meCard])) - return true; - - $value = $mutations[$meCard]; - unset($mutations[$meCard]); - - if ($value instanceof DAV\Property\IHref) { - $value = $value->getHref(); - $value = $this->server->calculateUri($value); - } elseif (!is_null($value)) { - $result[400][$meCard] = null; - return false; - } - - $innerResult = $this->server->updateProperties( - $node->getOwner(), - array( - '{http://sabredav.org/ns}vcard-url' => $value, - ) - ); - - $closureResult = false; - foreach($innerResult as $status => $props) { - if (is_array($props) && array_key_exists('{http://sabredav.org/ns}vcard-url', $props)) { - $result[$status][$meCard] = null; - $closureResult = ($status>=200 && $status<300); - } - - } - - return $result; - - } - - /** - * This functions handles REPORT requests specific to CardDAV - * - * @param string $reportName - * @param \DOMNode $dom - * @return bool - */ - public function report($reportName,$dom) { - - switch($reportName) { - case '{'.self::NS_CARDDAV.'}addressbook-multiget' : - $this->addressbookMultiGetReport($dom); - return false; - case '{'.self::NS_CARDDAV.'}addressbook-query' : - $this->addressBookQueryReport($dom); - return false; - default : - return; - - } - - - } - - /** - * This function handles the addressbook-multiget REPORT. - * - * This report is used by the client to fetch the content of a series - * of urls. Effectively avoiding a lot of redundant requests. - * - * @param \DOMNode $dom - * @return void - */ - public function addressbookMultiGetReport($dom) { - - $properties = array_keys(DAV\XMLUtil::parseProperties($dom->firstChild)); - - $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href'); - $propertyList = array(); - - foreach($hrefElems as $elem) { - - $uri = $this->server->calculateUri($elem->nodeValue); - list($propertyList[]) = $this->server->getPropertiesForPath($uri,$properties); - - } - - $prefer = $this->server->getHTTPPRefer(); - - $this->server->httpResponse->sendStatus(207); - $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->server->httpResponse->setHeader('Vary','Brief,Prefer'); - $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList, $prefer['return-minimal'])); - - } - - /** - * This method is triggered before a file gets updated with new content. - * - * This plugin uses this method to ensure that Card nodes receive valid - * vcard data. - * - * @param string $path - * @param DAV\IFile $node - * @param resource $data - * @return void - */ - public function beforeWriteContent($path, DAV\IFile $node, &$data) { - - if (!$node instanceof ICard) - return; - - $this->validateVCard($data); - - } - - /** - * This method is triggered before a new file is created. - * - * This plugin uses this method to ensure that Card nodes receive valid - * vcard data. - * - * @param string $path - * @param resource $data - * @param DAV\ICollection $parentNode - * @return void - */ - public function beforeCreateFile($path, &$data, DAV\ICollection $parentNode) { - - if (!$parentNode instanceof IAddressBook) - return; - - $this->validateVCard($data); - - } - - /** - * Checks if the submitted iCalendar data is in fact, valid. - * - * An exception is thrown if it's not. - * - * @param resource|string $data - * @return void - */ - protected function validateVCard(&$data) { - - // If it's a stream, we convert it to a string first. - if (is_resource($data)) { - $data = stream_get_contents($data); - } - - // Converting the data to unicode, if needed. - $data = DAV\StringUtil::ensureUTF8($data); - - try { - - $vobj = VObject\Reader::read($data); - - } catch (VObject\ParseException $e) { - - throw new DAV\Exception\UnsupportedMediaType('This resource only supports valid vcard data. Parse error: ' . $e->getMessage()); - - } - - if ($vobj->name !== 'VCARD') { - throw new DAV\Exception\UnsupportedMediaType('This collection can only support vcard objects.'); - } - - if (!isset($vobj->UID)) { - // No UID in vcards is invalid, but we'll just add it in anyway. - $vobj->add('UID', DAV\UUIDUtil::getUUID()); - $data = $vobj->serialize(); - } - - } - - - /** - * This function handles the addressbook-query REPORT - * - * This report is used by the client to filter an addressbook based on a - * complex query. - * - * @param \DOMNode $dom - * @return void - */ - protected function addressbookQueryReport($dom) { - - $query = new AddressBookQueryParser($dom); - $query->parse(); - - $depth = $this->server->getHTTPDepth(0); - - if ($depth==0) { - $candidateNodes = array( - $this->server->tree->getNodeForPath($this->server->getRequestUri()) - ); - } else { - $candidateNodes = $this->server->tree->getChildren($this->server->getRequestUri()); - } - - $validNodes = array(); - foreach($candidateNodes as $node) { - - if (!$node instanceof ICard) - continue; - - $blob = $node->get(); - if (is_resource($blob)) { - $blob = stream_get_contents($blob); - } - - if (!$this->validateFilters($blob, $query->filters, $query->test)) { - continue; - } - - $validNodes[] = $node; - - if ($query->limit && $query->limit <= count($validNodes)) { - // We hit the maximum number of items, we can stop now. - break; - } - - } - - $result = array(); - foreach($validNodes as $validNode) { - - if ($depth==0) { - $href = $this->server->getRequestUri(); - } else { - $href = $this->server->getRequestUri() . '/' . $validNode->getName(); - } - - list($result[]) = $this->server->getPropertiesForPath($href, $query->requestedProperties, 0); - - } - - $prefer = $this->server->getHTTPPRefer(); - - $this->server->httpResponse->sendStatus(207); - $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->server->httpResponse->setHeader('Vary','Brief,Prefer'); - $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result, $prefer['return-minimal'])); - - } - - /** - * Validates if a vcard makes it throught a list of filters. - * - * @param string $vcardData - * @param array $filters - * @param string $test anyof or allof (which means OR or AND) - * @return bool - */ - public function validateFilters($vcardData, array $filters, $test) { - - $vcard = VObject\Reader::read($vcardData); - - if (!$filters) return true; - - foreach($filters as $filter) { - - $isDefined = isset($vcard->{$filter['name']}); - if ($filter['is-not-defined']) { - if ($isDefined) { - $success = false; - } else { - $success = true; - } - } elseif ((!$filter['param-filters'] && !$filter['text-matches']) || !$isDefined) { - - // We only need to check for existence - $success = $isDefined; - - } else { - - $vProperties = $vcard->select($filter['name']); - - $results = array(); - if ($filter['param-filters']) { - $results[] = $this->validateParamFilters($vProperties, $filter['param-filters'], $filter['test']); - } - if ($filter['text-matches']) { - $texts = array(); - foreach($vProperties as $vProperty) - $texts[] = $vProperty->getValue(); - - $results[] = $this->validateTextMatches($texts, $filter['text-matches'], $filter['test']); - } - - if (count($results)===1) { - $success = $results[0]; - } else { - if ($filter['test'] === 'anyof') { - $success = $results[0] || $results[1]; - } else { - $success = $results[0] && $results[1]; - } - } - - } // else - - // There are two conditions where we can already determine whether - // or not this filter succeeds. - if ($test==='anyof' && $success) { - return true; - } - if ($test==='allof' && !$success) { - return false; - } - - } // foreach - - // If we got all the way here, it means we haven't been able to - // determine early if the test failed or not. - // - // This implies for 'anyof' that the test failed, and for 'allof' that - // we succeeded. Sounds weird, but makes sense. - return $test==='allof'; - - } - - /** - * Validates if a param-filter can be applied to a specific property. - * - * @todo currently we're only validating the first parameter of the passed - * property. Any subsequence parameters with the same name are - * ignored. - * @param array $vProperties - * @param array $filters - * @param string $test - * @return bool - */ - protected function validateParamFilters(array $vProperties, array $filters, $test) { - - foreach($filters as $filter) { - - $isDefined = false; - foreach($vProperties as $vProperty) { - $isDefined = isset($vProperty[$filter['name']]); - if ($isDefined) break; - } - - if ($filter['is-not-defined']) { - if ($isDefined) { - $success = false; - } else { - $success = true; - } - - // If there's no text-match, we can just check for existence - } elseif (!$filter['text-match'] || !$isDefined) { - - $success = $isDefined; - - } else { - - $success = false; - foreach($vProperties as $vProperty) { - // If we got all the way here, we'll need to validate the - // text-match filter. - $success = DAV\StringUtil::textMatch($vProperty[$filter['name']]->getValue(), $filter['text-match']['value'], $filter['text-match']['collation'], $filter['text-match']['match-type']); - if ($success) break; - } - if ($filter['text-match']['negate-condition']) { - $success = !$success; - } - - } // else - - // There are two conditions where we can already determine whether - // or not this filter succeeds. - if ($test==='anyof' && $success) { - return true; - } - if ($test==='allof' && !$success) { - return false; - } - - } - - // If we got all the way here, it means we haven't been able to - // determine early if the test failed or not. - // - // This implies for 'anyof' that the test failed, and for 'allof' that - // we succeeded. Sounds weird, but makes sense. - return $test==='allof'; - - } - - /** - * Validates if a text-filter can be applied to a specific property. - * - * @param array $texts - * @param array $filters - * @param string $test - * @return bool - */ - protected function validateTextMatches(array $texts, array $filters, $test) { - - foreach($filters as $filter) { - - $success = false; - foreach($texts as $haystack) { - $success = DAV\StringUtil::textMatch($haystack, $filter['value'], $filter['collation'], $filter['match-type']); - - // Breaking on the first match - if ($success) break; - } - if ($filter['negate-condition']) { - $success = !$success; - } - - if ($success && $test==='anyof') - return true; - - if (!$success && $test=='allof') - return false; - - - } - - // If we got all the way here, it means we haven't been able to - // determine early if the test failed or not. - // - // This implies for 'anyof' that the test failed, and for 'allof' that - // we succeeded. Sounds weird, but makes sense. - return $test==='allof'; - - } - - /** - * This event is triggered after webdav-properties have been retrieved. - * - * @return bool - */ - public function afterGetProperties($uri, &$properties) { - - // If the request was made using the SOGO connector, we must rewrite - // the content-type property. By default SabreDAV will send back - // text/x-vcard; charset=utf-8, but for SOGO we must strip that last - // part. - if (!isset($properties[200]['{DAV:}getcontenttype'])) - return; - - if (strpos($this->server->httpRequest->getHeader('User-Agent'),'Thunderbird')===false) { - return; - } - - if (strpos($properties[200]['{DAV:}getcontenttype'],'text/x-vcard')===0) { - $properties[200]['{DAV:}getcontenttype'] = 'text/x-vcard'; - } - - } - - /** - * This method is used to generate HTML output for the - * Sabre\DAV\Browser\Plugin. This allows us to generate an interface users - * can use to create new calendars. - * - * @param DAV\INode $node - * @param string $output - * @return bool - */ - public function htmlActionsPanel(DAV\INode $node, &$output) { - - if (!$node instanceof UserAddressBooks) - return; - - $output.= '
    -

    Create new address book

    - -
    -
    - -
    - '; - - return false; - - } - - /** - * This method allows us to intercept the 'mkcalendar' sabreAction. This - * action enables the user to create new calendars from the browser plugin. - * - * @param string $uri - * @param string $action - * @param array $postVars - * @return bool - */ - public function browserPostAction($uri, $action, array $postVars) { - - if ($action!=='mkaddressbook') - return; - - $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:carddav}addressbook'); - $properties = array(); - if (isset($postVars['{DAV:}displayname'])) { - $properties['{DAV:}displayname'] = $postVars['{DAV:}displayname']; - } - $this->server->createCollection($uri . '/' . $postVars['name'],$resourceType,$properties); - return false; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CardDAV/Property/SupportedAddressData.php b/core/src/core/classes/sabredav/lib/Sabre/CardDAV/Property/SupportedAddressData.php deleted file mode 100644 index 9d8dd2e6de..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CardDAV/Property/SupportedAddressData.php +++ /dev/null @@ -1,72 +0,0 @@ - 'text/vcard', 'version' => '3.0'), - // array('contentType' => 'text/vcard', 'version' => '4.0'), - ); - } - - $this->supportedData = $supportedData; - - } - - /** - * Serializes the property in a DOMDocument - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $node) { - - $doc = $node->ownerDocument; - - $prefix = - isset($server->xmlNamespaces[CardDAV\Plugin::NS_CARDDAV]) ? - $server->xmlNamespaces[CardDAV\Plugin::NS_CARDDAV] : - 'card'; - - foreach($this->supportedData as $supported) { - - $caldata = $doc->createElementNS(CardDAV\Plugin::NS_CARDDAV, $prefix . ':address-data-type'); - $caldata->setAttribute('content-type',$supported['contentType']); - $caldata->setAttribute('version',$supported['version']); - $node->appendChild($caldata); - - } - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CardDAV/UserAddressBooks.php b/core/src/core/classes/sabredav/lib/Sabre/CardDAV/UserAddressBooks.php deleted file mode 100644 index b4af861474..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CardDAV/UserAddressBooks.php +++ /dev/null @@ -1,260 +0,0 @@ -carddavBackend = $carddavBackend; - $this->principalUri = $principalUri; - - } - - /** - * Returns the name of this object - * - * @return string - */ - public function getName() { - - list(,$name) = DAV\URLUtil::splitPath($this->principalUri); - return $name; - - } - - /** - * Updates the name of this object - * - * @param string $name - * @return void - */ - public function setName($name) { - - throw new DAV\Exception\MethodNotAllowed(); - - } - - /** - * Deletes this object - * - * @return void - */ - public function delete() { - - throw new DAV\Exception\MethodNotAllowed(); - - } - - /** - * Returns the last modification date - * - * @return int - */ - public function getLastModified() { - - return null; - - } - - /** - * Creates a new file under this object. - * - * This is currently not allowed - * - * @param string $filename - * @param resource $data - * @return void - */ - public function createFile($filename, $data=null) { - - throw new DAV\Exception\MethodNotAllowed('Creating new files in this collection is not supported'); - - } - - /** - * Creates a new directory under this object. - * - * This is currently not allowed. - * - * @param string $filename - * @return void - */ - public function createDirectory($filename) { - - throw new DAV\Exception\MethodNotAllowed('Creating new collections in this collection is not supported'); - - } - - /** - * Returns a single calendar, by name - * - * @param string $name - * @todo needs optimizing - * @return \AddressBook - */ - public function getChild($name) { - - foreach($this->getChildren() as $child) { - if ($name==$child->getName()) - return $child; - - } - throw new DAV\Exception\NotFound('Addressbook with name \'' . $name . '\' could not be found'); - - } - - /** - * Returns a list of addressbooks - * - * @return array - */ - public function getChildren() { - - $addressbooks = $this->carddavBackend->getAddressbooksForUser($this->principalUri); - $objs = array(); - foreach($addressbooks as $addressbook) { - $objs[] = new AddressBook($this->carddavBackend, $addressbook); - } - return $objs; - - } - - /** - * Creates a new addressbook - * - * @param string $name - * @param array $resourceType - * @param array $properties - * @return void - */ - public function createExtendedCollection($name, array $resourceType, array $properties) { - - if (!in_array('{'.Plugin::NS_CARDDAV.'}addressbook',$resourceType) || count($resourceType)!==2) { - throw new DAV\Exception\InvalidResourceType('Unknown resourceType for this collection'); - } - $this->carddavBackend->createAddressBook($this->principalUri, $name, $properties); - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->principalUri; - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - return array( - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->principalUri, - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}write', - 'principal' => $this->principalUri, - 'protected' => true, - ), - - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - return null; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CardDAV/VCFExportPlugin.php b/core/src/core/classes/sabredav/lib/Sabre/CardDAV/VCFExportPlugin.php deleted file mode 100644 index 3f91a30127..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CardDAV/VCFExportPlugin.php +++ /dev/null @@ -1,108 +0,0 @@ -server = $server; - $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90); - - } - - /** - * 'beforeMethod' event handles. This event handles intercepts GET requests ending - * with ?export - * - * @param string $method - * @param string $uri - * @return bool - */ - public function beforeMethod($method, $uri) { - - if ($method!='GET') return; - if ($this->server->httpRequest->getQueryString()!='export') return; - - // splitting uri - list($uri) = explode('?',$uri,2); - - $node = $this->server->tree->getNodeForPath($uri); - - if (!($node instanceof IAddressBook)) return; - - // Checking ACL, if available. - if ($aclPlugin = $this->server->getPlugin('acl')) { - $aclPlugin->checkPrivileges($uri, '{DAV:}read'); - } - - $this->server->httpResponse->setHeader('Content-Type','text/directory'); - $this->server->httpResponse->sendStatus(200); - - $nodes = $this->server->getPropertiesForPath($uri, array( - '{' . Plugin::NS_CARDDAV . '}address-data', - ),1); - - $this->server->httpResponse->sendBody($this->generateVCF($nodes)); - - // Returning false to break the event chain - return false; - - } - - /** - * Merges all vcard objects, and builds one big vcf export - * - * @param array $nodes - * @return string - */ - public function generateVCF(array $nodes) { - - $output = ""; - - foreach($nodes as $node) { - - if (!isset($node[200]['{' . Plugin::NS_CARDDAV . '}address-data'])) { - continue; - } - $nodeData = $node[200]['{' . Plugin::NS_CARDDAV . '}address-data']; - - // Parsing this node so VObject can clean up the output. - $output .= - VObject\Reader::read($nodeData)->serialize(); - - } - - return $output; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/CardDAV/Version.php b/core/src/core/classes/sabredav/lib/Sabre/CardDAV/Version.php deleted file mode 100644 index 00221941b3..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/CardDAV/Version.php +++ /dev/null @@ -1,26 +0,0 @@ -currentUser; - } - - - /** - * Authenticates the user based on the current request. - * - * If authentication is successful, true must be returned. - * If authentication fails, an exception must be thrown. - * - * @param DAV\Server $server - * @param string $realm - * @throws DAV\Exception\NotAuthenticated - * @return bool - */ - public function authenticate(DAV\Server $server, $realm) { - - $auth = new HTTP\BasicAuth(); - $auth->setHTTPRequest($server->httpRequest); - $auth->setHTTPResponse($server->httpResponse); - $auth->setRealm($realm); - $userpass = $auth->getUserPass(); - if (!$userpass) { - $auth->requireLogin(); - throw new DAV\Exception\NotAuthenticated('No basic authentication headers were found'); - } - - // Authenticates the user - if (!$this->validateUserPass($userpass[0],$userpass[1])) { - $auth->requireLogin(); - throw new DAV\Exception\NotAuthenticated('Username or password does not match'); - } - $this->currentUser = $userpass[0]; - return true; - } - - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Auth/Backend/AbstractDigest.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Auth/Backend/AbstractDigest.php deleted file mode 100644 index dc00438c95..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Auth/Backend/AbstractDigest.php +++ /dev/null @@ -1,101 +0,0 @@ -setHTTPRequest($server->httpRequest); - $digest->setHTTPResponse($server->httpResponse); - - $digest->setRealm($realm); - $digest->init(); - - $username = $digest->getUsername(); - - // No username was given - if (!$username) { - $digest->requireLogin(); - throw new DAV\Exception\NotAuthenticated('No digest authentication headers were found'); - } - - $hash = $this->getDigestHash($realm, $username); - // If this was false, the user account didn't exist - if ($hash===false || is_null($hash)) { - $digest->requireLogin(); - throw new DAV\Exception\NotAuthenticated('The supplied username was not on file'); - } - if (!is_string($hash)) { - throw new DAV\Exception('The returned value from getDigestHash must be a string or null'); - } - - // If this was false, the password or part of the hash was incorrect. - if (!$digest->validateA1($hash)) { - $digest->requireLogin(); - throw new DAV\Exception\NotAuthenticated('Incorrect username'); - } - - $this->currentUser = $username; - return true; - - } - - /** - * Returns the currently logged in username. - * - * @return string|null - */ - public function getCurrentUser() { - - return $this->currentUser; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Auth/Backend/Apache.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Auth/Backend/Apache.php deleted file mode 100644 index 66fdd91e1e..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Auth/Backend/Apache.php +++ /dev/null @@ -1,63 +0,0 @@ -httpRequest->getRawServerValue('REMOTE_USER'); - if (is_null($remoteUser)) { - throw new DAV\Exception('We did not receive the $_SERVER[REMOTE_USER] property. This means that apache might have been misconfigured'); - } - - $this->remoteUser = $remoteUser; - return true; - - } - - /** - * Returns information about the currently logged in user. - * - * If nobody is currently logged in, this method should return null. - * - * @return array|null - */ - public function getCurrentUser() { - - return $this->remoteUser; - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Auth/Backend/BackendInterface.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Auth/Backend/BackendInterface.php deleted file mode 100644 index b8d04e2e11..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Auth/Backend/BackendInterface.php +++ /dev/null @@ -1,36 +0,0 @@ -loadFile($filename); - - } - - /** - * Loads an htdigest-formatted file. This method can be called multiple times if - * more than 1 file is used. - * - * @param string $filename - * @return void - */ - public function loadFile($filename) { - - foreach(file($filename,FILE_IGNORE_NEW_LINES) as $line) { - - if (substr_count($line, ":") !== 2) - throw new DAV\Exception('Malformed htdigest file. Every line should contain 2 colons'); - - list($username,$realm,$A1) = explode(':',$line); - - if (!preg_match('/^[a-zA-Z0-9]{32}$/', $A1)) - throw new DAV\Exception('Malformed htdigest file. Invalid md5 hash'); - - $this->users[$realm . ':' . $username] = $A1; - - } - - } - - /** - * Returns a users' information - * - * @param string $realm - * @param string $username - * @return string - */ - public function getDigestHash($realm, $username) { - - return isset($this->users[$realm . ':' . $username])?$this->users[$realm . ':' . $username]:false; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Auth/Backend/PDO.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Auth/Backend/PDO.php deleted file mode 100644 index f153d8429b..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Auth/Backend/PDO.php +++ /dev/null @@ -1,65 +0,0 @@ -pdo = $pdo; - $this->tableName = $tableName; - - } - - /** - * Returns the digest hash for a user. - * - * @param string $realm - * @param string $username - * @return string|null - */ - public function getDigestHash($realm,$username) { - - $stmt = $this->pdo->prepare('SELECT username, digesta1 FROM '.$this->tableName.' WHERE username = ?'); - $stmt->execute(array($username)); - $result = $stmt->fetchAll(); - - if (!count($result)) return; - - return $result[0]['digesta1']; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Auth/Plugin.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Auth/Plugin.php deleted file mode 100644 index dbebc20f05..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Auth/Plugin.php +++ /dev/null @@ -1,112 +0,0 @@ -authBackend = $authBackend; - $this->realm = $realm; - - } - - /** - * Initializes the plugin. This function is automatically called by the server - * - * @param DAV\Server $server - * @return void - */ - public function initialize(DAV\Server $server) { - - $this->server = $server; - $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'),10); - - } - - /** - * Returns a plugin name. - * - * Using this name other plugins will be able to access other plugins - * using DAV\Server::getPlugin - * - * @return string - */ - public function getPluginName() { - - return 'auth'; - - } - - /** - * Returns the current users' principal uri. - * - * If nobody is logged in, this will return null. - * - * @return string|null - */ - public function getCurrentUser() { - - $userInfo = $this->authBackend->getCurrentUser(); - if (!$userInfo) return null; - - return $userInfo; - - } - - /** - * This method is called before any HTTP method and forces users to be authenticated - * - * @param string $method - * @param string $uri - * @throws Sabre\DAV\Exception\NotAuthenticated - * @return bool - */ - public function beforeMethod($method, $uri) { - - $this->authBackend->authenticate($this->server,$this->realm); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/GuessContentType.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/GuessContentType.php deleted file mode 100644 index 9fd47b9304..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/GuessContentType.php +++ /dev/null @@ -1,99 +0,0 @@ - 'image/jpeg', - 'gif' => 'image/gif', - 'png' => 'image/png', - - // groupware - 'ics' => 'text/calendar', - 'vcf' => 'text/x-vcard', - - // text - 'txt' => 'text/plain', - - ); - - /** - * Initializes the plugin - * - * @param DAV\Server $server - * @return void - */ - public function initialize(DAV\Server $server) { - - // Using a relatively low priority (200) to allow other extensions - // to set the content-type first. - $server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties'),200); - - } - - /** - * Handler for teh afterGetProperties event - * - * @param string $path - * @param array $properties - * @return void - */ - public function afterGetProperties($path, &$properties) { - - if (array_key_exists('{DAV:}getcontenttype', $properties[404])) { - - list(, $fileName) = DAV\URLUtil::splitPath($path); - $contentType = $this->getContentType($fileName); - - if ($contentType) { - $properties[200]['{DAV:}getcontenttype'] = $contentType; - unset($properties[404]['{DAV:}getcontenttype']); - } - - } - - } - - /** - * Simple method to return the contenttype - * - * @param string $fileName - * @return string - */ - protected function getContentType($fileName) { - - // Just grabbing the extension - $extension = strtolower(substr($fileName,strrpos($fileName,'.')+1)); - if (isset($this->extensionMap[$extension])) - return $this->extensionMap[$extension]; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/MapGetToPropFind.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/MapGetToPropFind.php deleted file mode 100644 index 881c063b90..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/MapGetToPropFind.php +++ /dev/null @@ -1,57 +0,0 @@ -server = $server; - $this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor')); - } - - /** - * This method intercepts GET requests to non-files, and changes it into an HTTP PROPFIND request - * - * @param string $method - * @param string $uri - * @return bool - */ - public function httpGetInterceptor($method, $uri) { - - if ($method!='GET') return true; - - $node = $this->server->tree->getNodeForPath($uri); - if ($node instanceof DAV\IFile) return; - - $this->server->invokeMethod('PROPFIND',$uri); - return false; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/Plugin.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/Plugin.php deleted file mode 100644 index 751c229654..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/Plugin.php +++ /dev/null @@ -1,491 +0,0 @@ - 'icons/file', - 'Sabre\\DAV\\ICollection' => 'icons/collection', - 'Sabre\\DAVACL\\IPrincipal' => 'icons/principal', - 'Sabre\\CalDAV\\ICalendar' => 'icons/calendar', - 'Sabre\\CardDAV\\IAddressBook' => 'icons/addressbook', - 'Sabre\\CardDAV\\ICard' => 'icons/card', - ); - - /** - * The file extension used for all icons - * - * @var string - */ - public $iconExtension = '.png'; - - /** - * reference to server class - * - * @var Sabre\DAV\Server - */ - protected $server; - - /** - * enablePost turns on the 'actions' panel, which allows people to create - * folders and upload files straight from a browser. - * - * @var bool - */ - protected $enablePost = true; - - /** - * By default the browser plugin will generate a favicon and other images. - * To turn this off, set this property to false. - * - * @var bool - */ - protected $enableAssets = true; - - /** - * Creates the object. - * - * By default it will allow file creation and uploads. - * Specify the first argument as false to disable this - * - * @param bool $enablePost - * @param bool $enableAssets - */ - public function __construct($enablePost=true, $enableAssets = true) { - - $this->enablePost = $enablePost; - $this->enableAssets = $enableAssets; - - } - - /** - * Initializes the plugin and subscribes to events - * - * @param DAV\Server $server - * @return void - */ - public function initialize(DAV\Server $server) { - - $this->server = $server; - $this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor')); - $this->server->subscribeEvent('onHTMLActionsPanel', array($this, 'htmlActionsPanel'),200); - if ($this->enablePost) $this->server->subscribeEvent('unknownMethod',array($this,'httpPOSTHandler')); - } - - /** - * This method intercepts GET requests to collections and returns the html - * - * @param string $method - * @param string $uri - * @return bool - */ - public function httpGetInterceptor($method, $uri) { - - if ($method !== 'GET') return true; - - // We're not using straight-up $_GET, because we want everything to be - // unit testable. - $getVars = array(); - parse_str($this->server->httpRequest->getQueryString(), $getVars); - - if (isset($getVars['sabreAction']) && $getVars['sabreAction'] === 'asset' && isset($getVars['assetName'])) { - $this->serveAsset($getVars['assetName']); - return false; - } - - try { - $node = $this->server->tree->getNodeForPath($uri); - } catch (DAV\Exception\NotFound $e) { - // We're simply stopping when the file isn't found to not interfere - // with other plugins. - return; - } - if ($node instanceof DAV\IFile) - return; - - $this->server->httpResponse->sendStatus(200); - $this->server->httpResponse->setHeader('Content-Type','text/html; charset=utf-8'); - - $this->server->httpResponse->sendBody( - $this->generateDirectoryIndex($uri) - ); - - return false; - - } - - /** - * Handles POST requests for tree operations. - * - * @param string $method - * @param string $uri - * @return bool - */ - public function httpPOSTHandler($method, $uri) { - - if ($method!='POST') return; - $contentType = $this->server->httpRequest->getHeader('Content-Type'); - list($contentType) = explode(';', $contentType); - if ($contentType !== 'application/x-www-form-urlencoded' && - $contentType !== 'multipart/form-data') { - return; - } - $postVars = $this->server->httpRequest->getPostVars(); - - if (!isset($postVars['sabreAction'])) - return; - - if ($this->server->broadcastEvent('onBrowserPostAction', array($uri, $postVars['sabreAction'], $postVars))) { - - switch($postVars['sabreAction']) { - - case 'mkcol' : - if (isset($postVars['name']) && trim($postVars['name'])) { - // Using basename() because we won't allow slashes - list(, $folderName) = DAV\URLUtil::splitPath(trim($postVars['name'])); - $this->server->createDirectory($uri . '/' . $folderName); - } - break; - case 'put' : - if ($_FILES) $file = current($_FILES); - else break; - - list(, $newName) = DAV\URLUtil::splitPath(trim($file['name'])); - if (isset($postVars['name']) && trim($postVars['name'])) - $newName = trim($postVars['name']); - - // Making sure we only have a 'basename' component - list(, $newName) = DAV\URLUtil::splitPath($newName); - - if (is_uploaded_file($file['tmp_name'])) { - $this->server->createFile($uri . '/' . $newName, fopen($file['tmp_name'],'r')); - } - break; - - } - - } - $this->server->httpResponse->setHeader('Location',$this->server->httpRequest->getUri()); - $this->server->httpResponse->sendStatus(302); - return false; - - } - - /** - * Escapes a string for html. - * - * @param string $value - * @return string - */ - public function escapeHTML($value) { - - return htmlspecialchars($value,ENT_QUOTES,'UTF-8'); - - } - - /** - * Generates the html directory index for a given url - * - * @param string $path - * @return string - */ - public function generateDirectoryIndex($path) { - - $version = ''; - if (DAV\Server::$exposeVersion) { - $version = DAV\Version::VERSION ."-". DAV\Version::STABILITY; - } - - $html = " - - Index for " . $this->escapeHTML($path) . "/ - SabreDAV " . $version . " - - "; - - if ($this->enableAssets) { - $html.=''; - } - - $html .= " - -

    Index for " . $this->escapeHTML($path) . "/

    - - - "; - - $files = $this->server->getPropertiesForPath($path,array( - '{DAV:}displayname', - '{DAV:}resourcetype', - '{DAV:}getcontenttype', - '{DAV:}getcontentlength', - '{DAV:}getlastmodified', - ),1); - - $parent = $this->server->tree->getNodeForPath($path); - - - if ($path) { - - list($parentUri) = DAV\URLUtil::splitPath($path); - $fullPath = DAV\URLUtil::encodePath($this->server->getBaseUri() . $parentUri); - - $icon = $this->enableAssets?'Parent':''; - $html.= " - - - - - - "; - - } - - foreach($files as $file) { - - // This is the current directory, we can skip it - if (rtrim($file['href'],'/')==$path) continue; - - list(, $name) = DAV\URLUtil::splitPath($file['href']); - - $type = null; - - - if (isset($file[200]['{DAV:}resourcetype'])) { - $type = $file[200]['{DAV:}resourcetype']->getValue(); - - // resourcetype can have multiple values - if (!is_array($type)) $type = array($type); - - foreach($type as $k=>$v) { - - // Some name mapping is preferred - switch($v) { - case '{DAV:}collection' : - $type[$k] = 'Collection'; - break; - case '{DAV:}principal' : - $type[$k] = 'Principal'; - break; - case '{urn:ietf:params:xml:ns:carddav}addressbook' : - $type[$k] = 'Addressbook'; - break; - case '{urn:ietf:params:xml:ns:caldav}calendar' : - $type[$k] = 'Calendar'; - break; - case '{urn:ietf:params:xml:ns:caldav}schedule-inbox' : - $type[$k] = 'Schedule Inbox'; - break; - case '{urn:ietf:params:xml:ns:caldav}schedule-outbox' : - $type[$k] = 'Schedule Outbox'; - break; - case '{http://calendarserver.org/ns/}calendar-proxy-read' : - $type[$k] = 'Proxy-Read'; - break; - case '{http://calendarserver.org/ns/}calendar-proxy-write' : - $type[$k] = 'Proxy-Write'; - break; - } - - } - $type = implode(', ', $type); - } - - // If no resourcetype was found, we attempt to use - // the contenttype property - if (!$type && isset($file[200]['{DAV:}getcontenttype'])) { - $type = $file[200]['{DAV:}getcontenttype']; - } - if (!$type) $type = 'Unknown'; - - $size = isset($file[200]['{DAV:}getcontentlength'])?(int)$file[200]['{DAV:}getcontentlength']:''; - $lastmodified = isset($file[200]['{DAV:}getlastmodified'])?$file[200]['{DAV:}getlastmodified']->getTime()->format(\DateTime::ATOM):''; - - $fullPath = DAV\URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path?$path . '/':'') . $name,'/')); - - $displayName = isset($file[200]['{DAV:}displayname'])?$file[200]['{DAV:}displayname']:$name; - - $displayName = $this->escapeHTML($displayName); - $type = $this->escapeHTML($type); - - $icon = ''; - - if ($this->enableAssets) { - $node = $this->server->tree->getNodeForPath(($path?$path.'/':'') . $name); - foreach(array_reverse($this->iconMap) as $class=>$iconName) { - - if ($node instanceof $class) { - $icon = ''; - break; - } - - - } - - } - - $html.= " - - - - - - "; - - } - - $html.= ""; - - $output = ''; - - if ($this->enablePost) { - $this->server->broadcastEvent('onHTMLActionsPanel',array($parent, &$output)); - } - - $html.=$output; - - $html.= "
    NameTypeSizeLast modified

    $icon..[parent]
    $icon{$displayName}{$type}{$size}{$lastmodified}

    -
    Generated by SabreDAV " . $version . " (c)2007-2014 http://sabre.io/
    - - "; - - return $html; - - } - - /** - * This method is used to generate the 'actions panel' output for - * collections. - * - * This specifically generates the interfaces for creating new files, and - * creating new directories. - * - * @param DAV\INode $node - * @param mixed $output - * @return void - */ - public function htmlActionsPanel(DAV\INode $node, &$output) { - - if (!$node instanceof DAV\ICollection) - return; - - // We also know fairly certain that if an object is a non-extended - // SimpleCollection, we won't need to show the panel either. - if (get_class($node)==='Sabre\\DAV\\SimpleCollection') - return; - - $output.= '
    -

    Create new folder

    - - Name:
    - -
    -
    -

    Upload file

    - - Name (optional):
    - File:
    - -
    - '; - - } - - /** - * This method takes a path/name of an asset and turns it into url - * suiteable for http access. - * - * @param string $assetName - * @return string - */ - protected function getAssetUrl($assetName) { - - return $this->server->getBaseUri() . '?sabreAction=asset&assetName=' . urlencode($assetName); - - } - - /** - * This method returns a local pathname to an asset. - * - * @param string $assetName - * @return string - */ - protected function getLocalAssetPath($assetName) { - - $assetDir = __DIR__ . '/assets/'; - $path = $assetDir . $assetName; - - // Making sure people aren't trying to escape from the base path. - if (strpos(realpath($path), realpath($assetDir)) === 0) { - return $path; - } - throw new DAV\Exception\Forbidden('Path does not exist, or escaping from the base path was detected'); - } - - /** - * This method reads an asset from disk and generates a full http response. - * - * @param string $assetName - * @return void - */ - protected function serveAsset($assetName) { - - $assetPath = $this->getLocalAssetPath($assetName); - if (!file_exists($assetPath)) { - throw new DAV\Exception\NotFound('Could not find an asset with this name'); - } - // Rudimentary mime type detection - switch(strtolower(substr($assetPath,strpos($assetPath,'.')+1))) { - - case 'ico' : - $mime = 'image/vnd.microsoft.icon'; - break; - - case 'png' : - $mime = 'image/png'; - break; - - default: - $mime = 'application/octet-stream'; - break; - - } - - $this->server->httpResponse->setHeader('Content-Type', $mime); - $this->server->httpResponse->setHeader('Content-Length', filesize($assetPath)); - $this->server->httpResponse->setHeader('Cache-Control', 'public, max-age=1209600'); - $this->server->httpResponse->sendStatus(200); - $this->server->httpResponse->sendBody(fopen($assetPath,'r')); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/favicon.ico b/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/favicon.ico deleted file mode 100644 index 2b2c10a22c..0000000000 Binary files a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/favicon.ico and /dev/null differ diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/addressbook.png b/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/addressbook.png deleted file mode 100644 index c9acc84172..0000000000 Binary files a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/addressbook.png and /dev/null differ diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/calendar.png b/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/calendar.png deleted file mode 100644 index 3ecd6a800a..0000000000 Binary files a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/calendar.png and /dev/null differ diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/card.png b/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/card.png deleted file mode 100644 index 2ce954866d..0000000000 Binary files a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/card.png and /dev/null differ diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/collection.png b/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/collection.png deleted file mode 100644 index 156fa64fd5..0000000000 Binary files a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/collection.png and /dev/null differ diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/file.png b/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/file.png deleted file mode 100644 index 3b98551cec..0000000000 Binary files a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/file.png and /dev/null differ diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/parent.png b/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/parent.png deleted file mode 100644 index 156fa64fd5..0000000000 Binary files a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/parent.png and /dev/null differ diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/principal.png b/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/principal.png deleted file mode 100644 index f8988f828e..0000000000 Binary files a/core/src/core/classes/sabredav/lib/Sabre/DAV/Browser/assets/icons/principal.png and /dev/null differ diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Client.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Client.php deleted file mode 100644 index 705b321955..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Client.php +++ /dev/null @@ -1,575 +0,0 @@ -$validSetting = $settings[$validSetting]; - } - } - - if (isset($settings['authType'])) { - $this->authType = $settings['authType']; - } else { - $this->authType = self::AUTH_BASIC | self::AUTH_DIGEST; - } - - $this->propertyMap['{DAV:}resourcetype'] = 'Sabre\\DAV\\Property\\ResourceType'; - - } - - /** - * Add trusted root certificates to the webdav client. - * - * The parameter certificates should be a absolute path to a file - * which contains all trusted certificates - * - * @param string $certificates - */ - public function addTrustedCertificates($certificates) { - $this->trustedCertificates = $certificates; - } - - /** - * Enables/disables SSL peer verification - * - * @param boolean $value - */ - public function setVerifyPeer($value) { - $this->verifyPeer = $value; - } - - /** - * Does a PROPFIND request - * - * The list of requested properties must be specified as an array, in clark - * notation. - * - * The returned array will contain a list of filenames as keys, and - * properties as values. - * - * The properties array will contain the list of properties. Only properties - * that are actually returned from the server (without error) will be - * returned, anything else is discarded. - * - * Depth should be either 0 or 1. A depth of 1 will cause a request to be - * made to the server to also return all child resources. - * - * @param string $url - * @param array $properties - * @param int $depth - * @return array - */ - public function propFind($url, array $properties, $depth = 0) { - - $body = '' . "\n"; - $body.= '' . "\n"; - $body.= ' ' . "\n"; - - foreach($properties as $property) { - - list( - $namespace, - $elementName - ) = XMLUtil::parseClarkNotation($property); - - if ($namespace === 'DAV:') { - $body.=' ' . "\n"; - } else { - $body.=" \n"; - } - - } - - $body.= ' ' . "\n"; - $body.= ''; - - $response = $this->request('PROPFIND', $url, $body, array( - 'Depth' => $depth, - 'Content-Type' => 'application/xml' - )); - - $result = $this->parseMultiStatus($response['body']); - - // If depth was 0, we only return the top item - if ($depth===0) { - reset($result); - $result = current($result); - return isset($result[200])?$result[200]:array(); - } - - $newResult = array(); - foreach($result as $href => $statusList) { - - $newResult[$href] = isset($statusList[200])?$statusList[200]:array(); - - } - - return $newResult; - - } - - /** - * Updates a list of properties on the server - * - * The list of properties must have clark-notation properties for the keys, - * and the actual (string) value for the value. If the value is null, an - * attempt is made to delete the property. - * - * @todo Must be building the request using the DOM, and does not yet - * support complex properties. - * @param string $url - * @param array $properties - * @return void - */ - public function propPatch($url, array $properties) { - - $body = '' . "\n"; - $body.= '' . "\n"; - - foreach($properties as $propName => $propValue) { - - list( - $namespace, - $elementName - ) = XMLUtil::parseClarkNotation($propName); - - if ($propValue === null) { - - $body.="\n"; - - if ($namespace === 'DAV:') { - $body.=' ' . "\n"; - } else { - $body.=" \n"; - } - - $body.="\n"; - - } else { - - $body.="\n"; - if ($namespace === 'DAV:') { - $body.=' '; - } else { - $body.=" "; - } - // Shitty.. i know - $body.=htmlspecialchars($propValue, ENT_NOQUOTES, 'UTF-8'); - if ($namespace === 'DAV:') { - $body.='' . "\n"; - } else { - $body.="\n"; - } - $body.="\n"; - - } - - } - - $body.= ''; - - $this->request('PROPPATCH', $url, $body, array( - 'Content-Type' => 'application/xml' - )); - - } - - /** - * Performs an HTTP options request - * - * This method returns all the features from the 'DAV:' header as an array. - * If there was no DAV header, or no contents this method will return an - * empty array. - * - * @return array - */ - public function options() { - - $result = $this->request('OPTIONS'); - if (!isset($result['headers']['dav'])) { - return array(); - } - - $features = explode(',', $result['headers']['dav']); - foreach($features as &$v) { - $v = trim($v); - } - return $features; - - } - - /** - * Performs an actual HTTP request, and returns the result. - * - * If the specified url is relative, it will be expanded based on the base - * url. - * - * The returned array contains 3 keys: - * * body - the response body - * * httpCode - a HTTP code (200, 404, etc) - * * headers - a list of response http headers. The header names have - * been lowercased. - * - * @param string $method - * @param string $url - * @param string $body - * @param array $headers - * @return array - */ - public function request($method, $url = '', $body = null, $headers = array()) { - - $url = $this->getAbsoluteUrl($url); - - $curlSettings = array( - CURLOPT_RETURNTRANSFER => true, - // Return headers as part of the response - CURLOPT_HEADER => true, - - // For security we cast this to a string. If somehow an array could - // be passed here, it would be possible for an attacker to use @ to - // post local files. - CURLOPT_POSTFIELDS => (string)$body, - // Automatically follow redirects - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_MAXREDIRS => 5, - ); - - if($this->verifyPeer !== null) { - $curlSettings[CURLOPT_SSL_VERIFYPEER] = $this->verifyPeer; - } - - if($this->trustedCertificates) { - $curlSettings[CURLOPT_CAINFO] = $this->trustedCertificates; - } - - switch ($method) { - case 'HEAD' : - - // do not read body with HEAD requests (this is necessary because cURL does not ignore the body with HEAD - // requests when the Content-Length header is given - which in turn is perfectly valid according to HTTP - // specs...) cURL does unfortunately return an error in this case ("transfer closed transfer closed with - // ... bytes remaining to read") this can be circumvented by explicitly telling cURL to ignore the - // response body - $curlSettings[CURLOPT_NOBODY] = true; - $curlSettings[CURLOPT_CUSTOMREQUEST] = 'HEAD'; - break; - - default: - $curlSettings[CURLOPT_CUSTOMREQUEST] = $method; - break; - - } - - // Adding HTTP headers - $nHeaders = array(); - foreach($headers as $key=>$value) { - - $nHeaders[] = $key . ': ' . $value; - - } - $curlSettings[CURLOPT_HTTPHEADER] = $nHeaders; - - if ($this->proxy) { - $curlSettings[CURLOPT_PROXY] = $this->proxy; - } - - if ($this->userName && $this->authType) { - $curlType = 0; - if ($this->authType & self::AUTH_BASIC) { - $curlType |= CURLAUTH_BASIC; - } - if ($this->authType & self::AUTH_DIGEST) { - $curlType |= CURLAUTH_DIGEST; - } - $curlSettings[CURLOPT_HTTPAUTH] = $curlType; - $curlSettings[CURLOPT_USERPWD] = $this->userName . ':' . $this->password; - } - - list( - $response, - $curlInfo, - $curlErrNo, - $curlError - ) = $this->curlRequest($url, $curlSettings); - - $headerBlob = substr($response, 0, $curlInfo['header_size']); - $response = substr($response, $curlInfo['header_size']); - - // In the case of 100 Continue, or redirects we'll have multiple lists - // of headers for each separate HTTP response. We can easily split this - // because they are separated by \r\n\r\n - $headerBlob = explode("\r\n\r\n", trim($headerBlob, "\r\n")); - - // We only care about the last set of headers - $headerBlob = $headerBlob[count($headerBlob)-1]; - - // Splitting headers - $headerBlob = explode("\r\n", $headerBlob); - - $headers = array(); - foreach($headerBlob as $header) { - $parts = explode(':', $header, 2); - if (count($parts)==2) { - $headers[strtolower(trim($parts[0]))] = trim($parts[1]); - } - } - - $response = array( - 'body' => $response, - 'statusCode' => $curlInfo['http_code'], - 'headers' => $headers - ); - - if ($curlErrNo) { - throw new Exception('[CURL] Error while making request: ' . $curlError . ' (error code: ' . $curlErrNo . ')'); - } - - if ($response['statusCode']>=400) { - switch ($response['statusCode']) { - case 400 : - throw new Exception\BadRequest('Bad request'); - case 401 : - throw new Exception\NotAuthenticated('Not authenticated'); - case 402 : - throw new Exception\PaymentRequired('Payment required'); - case 403 : - throw new Exception\Forbidden('Forbidden'); - case 404: - throw new Exception\NotFound('Resource not found.'); - case 405 : - throw new Exception\MethodNotAllowed('Method not allowed'); - case 409 : - throw new Exception\Conflict('Conflict'); - case 412 : - throw new Exception\PreconditionFailed('Precondition failed'); - case 416 : - throw new Exception\RequestedRangeNotSatisfiable('Requested Range Not Satisfiable'); - case 500 : - throw new Exception('Internal server error'); - case 501 : - throw new Exception\NotImplemented('Not Implemented'); - case 507 : - throw new Exception\InsufficientStorage('Insufficient storage'); - default: - throw new Exception('HTTP error response. (errorcode ' . $response['statusCode'] . ')'); - } - } - - return $response; - - } - - /** - * Wrapper for all curl functions. - * - * The only reason this was split out in a separate method, is so it - * becomes easier to unittest. - * - * @param string $url - * @param array $settings - * @return array - */ - // @codeCoverageIgnoreStart - protected function curlRequest($url, $settings) { - - $curl = curl_init($url); - curl_setopt_array($curl, $settings); - - return array( - curl_exec($curl), - curl_getinfo($curl), - curl_errno($curl), - curl_error($curl) - ); - - } - // @codeCoverageIgnoreEnd - - /** - * Returns the full url based on the given url (which may be relative). All - * urls are expanded based on the base url as given by the server. - * - * @param string $url - * @return string - */ - protected function getAbsoluteUrl($url) { - - // If the url starts with http:// or https://, the url is already absolute. - if (preg_match('/^http(s?):\/\//', $url)) { - return $url; - } - - // If the url starts with a slash, we must calculate the url based off - // the root of the base url. - if (strpos($url,'/') === 0) { - $parts = parse_url($this->baseUri); - return $parts['scheme'] . '://' . $parts['host'] . (isset($parts['port'])?':' . $parts['port']:'') . $url; - } - - // Otherwise... - return $this->baseUri . $url; - - } - - /** - * Parses a WebDAV multistatus response body - * - * This method returns an array with the following structure - * - * array( - * 'url/to/resource' => array( - * '200' => array( - * '{DAV:}property1' => 'value1', - * '{DAV:}property2' => 'value2', - * ), - * '404' => array( - * '{DAV:}property1' => null, - * '{DAV:}property2' => null, - * ), - * ) - * 'url/to/resource2' => array( - * .. etc .. - * ) - * ) - * - * - * @param string $body xml body - * @return array - */ - public function parseMultiStatus($body) { - - $body = XMLUtil::convertDAVNamespace($body); - - // Fixes an XXE vulnerability on PHP versions older than 5.3.23 or - // 5.4.13. - $previous = libxml_disable_entity_loader(true); - $responseXML = simplexml_load_string($body, null, LIBXML_NOBLANKS | LIBXML_NOCDATA); - libxml_disable_entity_loader($previous); - - if ($responseXML===false) { - throw new \InvalidArgumentException('The passed data is not valid XML'); - } - - $responseXML->registerXPathNamespace('d', 'urn:DAV'); - - $propResult = array(); - - foreach($responseXML->xpath('d:response') as $response) { - $response->registerXPathNamespace('d', 'urn:DAV'); - $href = $response->xpath('d:href'); - $href = (string)$href[0]; - - $properties = array(); - - foreach($response->xpath('d:propstat') as $propStat) { - - $propStat->registerXPathNamespace('d', 'urn:DAV'); - $status = $propStat->xpath('d:status'); - list($httpVersion, $statusCode, $message) = explode(' ', (string)$status[0],3); - - // Only using the propertymap for results with status 200. - $propertyMap = $statusCode==='200' ? $this->propertyMap : array(); - - $properties[$statusCode] = XMLUtil::parseProperties(dom_import_simplexml($propStat), $propertyMap); - - } - - $propResult[$href] = $properties; - - } - - return $propResult; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Collection.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Collection.php deleted file mode 100644 index 0090a4d6ed..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Collection.php +++ /dev/null @@ -1,110 +0,0 @@ -getChildren() as $child) { - - if ($child->getName()==$name) return $child; - - } - throw new Exception\NotFound('File not found: ' . $name); - - } - - /** - * Checks is a child-node exists. - * - * It is generally a good idea to try and override this. Usually it can be optimized. - * - * @param string $name - * @return bool - */ - public function childExists($name) { - - try { - - $this->getChild($name); - return true; - - } catch(Exception\NotFound $e) { - - return false; - - } - - } - - /** - * Creates a new file in the directory - * - * Data will either be supplied as a stream resource, or in certain cases - * as a string. Keep in mind that you may have to support either. - * - * After succesful creation of the file, you may choose to return the ETag - * of the new file here. - * - * The returned ETag must be surrounded by double-quotes (The quotes should - * be part of the actual string). - * - * If you cannot accurately determine the ETag, you should not return it. - * If you don't store the file exactly as-is (you're transforming it - * somehow) you should also not return an ETag. - * - * This means that if a subsequent GET to this new file does not exactly - * return the same contents of what was submitted here, you are strongly - * recommended to omit the ETag. - * - * @param string $name Name of the file - * @param resource|string $data Initial payload - * @return null|string - */ - public function createFile($name, $data = null) { - - throw new Exception\Forbidden('Permission denied to create file (filename ' . $name . ')'); - - } - - /** - * Creates a new subdirectory - * - * @param string $name - * @throws Exception\Forbidden - * @return void - */ - public function createDirectory($name) { - - throw new Exception\Forbidden('Permission denied to create directory'); - - } - - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception.php deleted file mode 100644 index 22a319e9f7..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception.php +++ /dev/null @@ -1,64 +0,0 @@ -lock) { - $error = $errorNode->ownerDocument->createElementNS('DAV:','d:no-conflicting-lock'); - $errorNode->appendChild($error); - if (!is_object($this->lock)) var_dump($this->lock); - $error->appendChild($errorNode->ownerDocument->createElementNS('DAV:','d:href',$this->lock->uri)); - } - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/FileNotFound.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/FileNotFound.php deleted file mode 100644 index aa4844cb9b..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/FileNotFound.php +++ /dev/null @@ -1,19 +0,0 @@ -ownerDocument->createElementNS('DAV:','d:valid-resourcetype'); - $errorNode->appendChild($error); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/LengthRequired.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/LengthRequired.php deleted file mode 100644 index 9487686dc4..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/LengthRequired.php +++ /dev/null @@ -1,30 +0,0 @@ -message = 'The locktoken supplied does not match any locks on this entity'; - - } - - /** - * This method allows the exception to include additional information into the WebDAV error response - * - * @param DAV\Server $server - * @param \DOMElement $errorNode - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $errorNode) { - - $error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-matches-request-uri'); - $errorNode->appendChild($error); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/Locked.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/Locked.php deleted file mode 100644 index 2bee1b02f1..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/Locked.php +++ /dev/null @@ -1,73 +0,0 @@ -lock = $lock; - - } - - /** - * Returns the HTTP statuscode for this exception - * - * @return int - */ - public function getHTTPCode() { - - return 423; - - } - - /** - * This method allows the exception to include additional information into the WebDAV error response - * - * @param DAV\Server $server - * @param \DOMElement $errorNode - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $errorNode) { - - if ($this->lock) { - $error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-submitted'); - $errorNode->appendChild($error); - - $href = $errorNode->ownerDocument->createElementNS('DAV:','d:href'); - $href->appendChild($errorNode->ownerDocument->createTextNode($this->lock->uri)); - $error->appendChild( - $href - ); - } - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/MethodNotAllowed.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/MethodNotAllowed.php deleted file mode 100644 index 05970cfa85..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/MethodNotAllowed.php +++ /dev/null @@ -1,45 +0,0 @@ -getAllowedMethods($server->getRequestUri()); - - return array( - 'Allow' => strtoupper(implode(', ',$methods)), - ); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/NotAuthenticated.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/NotAuthenticated.php deleted file mode 100644 index c082d489bb..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/NotAuthenticated.php +++ /dev/null @@ -1,30 +0,0 @@ -header = $header; - - } - - /** - * Returns the HTTP statuscode for this exception - * - * @return int - */ - public function getHTTPCode() { - - return 412; - - } - - /** - * This method allows the exception to include additional information into the WebDAV error response - * - * @param DAV\Server $server - * @param \DOMElement $errorNode - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $errorNode) { - - if ($this->header) { - $prop = $errorNode->ownerDocument->createElement('s:header'); - $prop->nodeValue = $this->header; - $errorNode->appendChild($prop); - } - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/ReportNotSupported.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/ReportNotSupported.php deleted file mode 100644 index 8e32096e0f..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/ReportNotSupported.php +++ /dev/null @@ -1,32 +0,0 @@ -ownerDocument->createElementNS('DAV:','d:supported-report'); - $errorNode->appendChild($error); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php deleted file mode 100644 index 25002be6ae..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php +++ /dev/null @@ -1,31 +0,0 @@ - - * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/). - * @license http://sabre.io/license/ Modified BSD License - */ -class ServiceUnavailable extends DAV\Exception { - - /** - * Returns the HTTP statuscode for this exception - * - * @return int - */ - public function getHTTPCode() { - - return 503; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/UnsupportedMediaType.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/UnsupportedMediaType.php deleted file mode 100644 index 46eea60df6..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Exception/UnsupportedMediaType.php +++ /dev/null @@ -1,28 +0,0 @@ -path . '/' . $name; - file_put_contents($newPath,$data); - - } - - /** - * Creates a new subdirectory - * - * @param string $name - * @return void - */ - public function createDirectory($name) { - - $newPath = $this->path . '/' . $name; - mkdir($newPath); - - } - - /** - * Returns a specific child node, referenced by its name - * - * This method must throw DAV\Exception\NotFound if the node does not - * exist. - * - * @param string $name - * @throws DAV\Exception\NotFound - * @return DAV\INode - */ - public function getChild($name) { - - $path = $this->path . '/' . $name; - - if (!file_exists($path)) throw new DAV\Exception\NotFound('File with name ' . $path . ' could not be located'); - - if (is_dir($path)) { - - return new Directory($path); - - } else { - - return new File($path); - - } - - } - - /** - * Returns an array with all the child nodes - * - * @return DAV\INode[] - */ - public function getChildren() { - - $nodes = array(); - foreach(scandir($this->path) as $node) if($node!='.' && $node!='..') $nodes[] = $this->getChild($node); - return $nodes; - - } - - /** - * Checks if a child exists. - * - * @param string $name - * @return bool - */ - public function childExists($name) { - - $path = $this->path . '/' . $name; - return file_exists($path); - - } - - /** - * Deletes all files in this directory, and then itself - * - * @return void - */ - public function delete() { - - foreach($this->getChildren() as $child) $child->delete(); - rmdir($this->path); - - } - - /** - * Returns available diskspace information - * - * @return array - */ - public function getQuotaInfo() { - - return array( - disk_total_space($this->path)-disk_free_space($this->path), - disk_free_space($this->path) - ); - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/FS/File.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/FS/File.php deleted file mode 100644 index d10370fae0..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/FS/File.php +++ /dev/null @@ -1,91 +0,0 @@ -path,$data); - - } - - /** - * Returns the data - * - * @return string - */ - public function get() { - - return fopen($this->path,'r'); - - } - - /** - * Delete the current file - * - * @return void - */ - public function delete() { - - unlink($this->path); - - } - - /** - * Returns the size of the node, in bytes - * - * @return int - */ - public function getSize() { - - return filesize($this->path); - - } - - /** - * Returns the ETag for a file - * - * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. - * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. - * - * Return null if the ETag can not effectively be determined - * - * @return mixed - */ - public function getETag() { - - return null; - - } - - /** - * Returns the mime-type for a file - * - * If null is returned, we'll assume application/octet-stream - * - * @return mixed - */ - public function getContentType() { - - return null; - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/FS/Node.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/FS/Node.php deleted file mode 100644 index 605fa3c826..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/FS/Node.php +++ /dev/null @@ -1,82 +0,0 @@ -path = $path; - - } - - - - /** - * Returns the name of the node - * - * @return string - */ - public function getName() { - - list(, $name) = DAV\URLUtil::splitPath($this->path); - return $name; - - } - - /** - * Renames the node - * - * @param string $name The new name - * @return void - */ - public function setName($name) { - - list($parentPath, ) = DAV\URLUtil::splitPath($this->path); - list(, $newName) = DAV\URLUtil::splitPath($name); - - $newPath = $parentPath . '/' . $newName; - rename($this->path,$newPath); - - $this->path = $newPath; - - } - - - - /** - * Returns the last modification time, as a unix timestamp - * - * @return int - */ - public function getLastModified() { - - return filemtime($this->path); - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/FSExt/Directory.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/FSExt/Directory.php deleted file mode 100644 index da3d2cc69b..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/FSExt/Directory.php +++ /dev/null @@ -1,159 +0,0 @@ -path . '/' . $name; - file_put_contents($newPath,$data); - - return '"' . md5_file($newPath) . '"'; - - } - - /** - * Creates a new subdirectory - * - * @param string $name - * @return void - */ - public function createDirectory($name) { - - // We're not allowing dots - if ($name=='.' || $name=='..') throw new DAV\Exception\Forbidden('Permission denied to . and ..'); - $newPath = $this->path . '/' . $name; - mkdir($newPath); - - } - - /** - * Returns a specific child node, referenced by its name - * - * This method must throw Sabre\DAV\Exception\NotFound if the node does not - * exist. - * - * @param string $name - * @throws DAV\Exception\NotFound - * @return DAV\INode - */ - public function getChild($name) { - - $path = $this->path . '/' . $name; - - if (!file_exists($path)) throw new DAV\Exception\NotFound('File could not be located'); - if ($name=='.' || $name=='..') throw new DAV\Exception\Forbidden('Permission denied to . and ..'); - - if (is_dir($path)) { - - return new Directory($path); - - } else { - - return new File($path); - - } - - } - - /** - * Checks if a child exists. - * - * @param string $name - * @return bool - */ - public function childExists($name) { - - if ($name=='.' || $name=='..') - throw new DAV\Exception\Forbidden('Permission denied to . and ..'); - - $path = $this->path . '/' . $name; - return file_exists($path); - - } - - /** - * Returns an array with all the child nodes - * - * @return DAV\INode[] - */ - public function getChildren() { - - $nodes = array(); - foreach(scandir($this->path) as $node) if($node!='.' && $node!='..' && $node!='.sabredav') $nodes[] = $this->getChild($node); - return $nodes; - - } - - /** - * Deletes all files in this directory, and then itself - * - * @return bool - */ - public function delete() { - - // Deleting all children - foreach($this->getChildren() as $child) $child->delete(); - - // Removing resource info, if its still around - if (file_exists($this->path . '/.sabredav')) unlink($this->path . '/.sabredav'); - - // Removing the directory itself - rmdir($this->path); - - return parent::delete(); - - } - - /** - * Returns available diskspace information - * - * @return array - */ - public function getQuotaInfo() { - - return array( - disk_total_space($this->path)-disk_free_space($this->path), - disk_free_space($this->path) - ); - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/FSExt/File.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/FSExt/File.php deleted file mode 100644 index 6588fad7eb..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/FSExt/File.php +++ /dev/null @@ -1,146 +0,0 @@ -path,$data); - return '"' . md5_file($this->path) . '"'; - - } - - /** - * Updates the file based on a range specification. - * - * The first argument is the data, which is either a readable stream - * resource or a string. - * - * The second argument is the type of update we're doing. - * This is either: - * * 1. append - * * 2. update based on a start byte - * * 3. update based on an end byte - *; - * The third argument is the start or end byte. - * - * After a successful put operation, you may choose to return an ETag. The - * etag must always be surrounded by double-quotes. These quotes must - * appear in the actual string you're returning. - * - * Clients may use the ETag from a PUT request to later on make sure that - * when they update the file, the contents haven't changed in the mean - * time. - * - * @param resource|string $data - * @param int $rangeType - * @param int $offset - * @return string|null - */ - public function patch($data, $rangeType, $offset = null) { - - switch($rangeType) { - case 1 : - $f = fopen($this->path, 'a'); - break; - case 2 : - $f = fopen($this->path, 'c'); - fseek($f,$offset); - break; - case 3 : - $f = fopen($this->path, 'c'); - fseek($f, $offset, SEEK_END); - break; - } - if (is_string($data)) { - fwrite($f, $data); - } else { - stream_copy_to_stream($data,$f); - } - fclose($f); - return '"' . md5_file($this->path) . '"'; - - } - - /** - * Returns the data - * - * @return resource - */ - public function get() { - - return fopen($this->path,'r'); - - } - - /** - * Delete the current file - * - * @return bool - */ - public function delete() { - - unlink($this->path); - return parent::delete(); - - } - - /** - * Returns the ETag for a file - * - * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. - * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. - * - * Return null if the ETag can not effectively be determined - * - * @return string|null - */ - public function getETag() { - - return '"' . md5_file($this->path). '"'; - - } - - /** - * Returns the mime-type for a file - * - * If null is returned, we'll assume application/octet-stream - * - * @return string|null - */ - public function getContentType() { - - return null; - - } - - /** - * Returns the size of the file, in bytes - * - * @return int - */ - public function getSize() { - - return filesize($this->path); - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/FSExt/Node.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/FSExt/Node.php deleted file mode 100644 index 0e11582f34..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/FSExt/Node.php +++ /dev/null @@ -1,214 +0,0 @@ -getResourceData(); - - foreach($properties as $propertyName=>$propertyValue) { - - // If it was null, we need to delete the property - if (is_null($propertyValue)) { - if (isset($resourceData['properties'][$propertyName])) { - unset($resourceData['properties'][$propertyName]); - } - } else { - $resourceData['properties'][$propertyName] = $propertyValue; - } - - } - - $this->putResourceData($resourceData); - return true; - } - - /** - * Returns a list of properties for this nodes.; - * - * The properties list is a list of propertynames the client requested, encoded as xmlnamespace#tagName, for example: http://www.example.org/namespace#author - * If the array is empty, all properties should be returned - * - * @param array $properties - * @return array - */ - function getProperties($properties) { - - $resourceData = $this->getResourceData(); - - // if the array was empty, we need to return everything - if (!$properties) return $resourceData['properties']; - - $props = array(); - foreach($properties as $property) { - if (isset($resourceData['properties'][$property])) $props[$property] = $resourceData['properties'][$property]; - } - - return $props; - - } - - /** - * Returns the path to the resource file - * - * @return string - */ - protected function getResourceInfoPath() { - - list($parentDir) = DAV\URLUtil::splitPath($this->path); - return $parentDir . '/.sabredav'; - - } - - /** - * Returns all the stored resource information - * - * @return array - */ - protected function getResourceData() { - - $path = $this->getResourceInfoPath(); - if (!file_exists($path)) return array('properties' => array()); - - // opening up the file, and creating a shared lock - $handle = fopen($path,'r'); - flock($handle,LOCK_SH); - $data = ''; - - // Reading data until the eof - while(!feof($handle)) { - $data.=fread($handle,8192); - } - - // We're all good - fclose($handle); - - // Unserializing and checking if the resource file contains data for this file - $data = unserialize($data); - if (!isset($data[$this->getName()])) { - return array('properties' => array()); - } - - $data = $data[$this->getName()]; - if (!isset($data['properties'])) $data['properties'] = array(); - return $data; - - } - - /** - * Updates the resource information - * - * @param array $newData - * @return void - */ - protected function putResourceData(array $newData) { - - $path = $this->getResourceInfoPath(); - - // opening up the file, and creating a shared lock - $handle = fopen($path,'a+'); - flock($handle,LOCK_EX); - $data = ''; - - rewind($handle); - - // Reading data until the eof - while(!feof($handle)) { - $data.=fread($handle,8192); - } - - // Unserializing and checking if the resource file contains data for this file - $data = unserialize($data); - $data[$this->getName()] = $newData; - ftruncate($handle,0); - rewind($handle); - - fwrite($handle,serialize($data)); - fclose($handle); - - } - - /** - * Renames the node - * - * @param string $name The new name - * @return void - */ - public function setName($name) { - - list($parentPath, ) = DAV\URLUtil::splitPath($this->path); - list(, $newName) = DAV\URLUtil::splitPath($name); - $newPath = $parentPath . '/' . $newName; - - // We're deleting the existing resourcedata, and recreating it - // for the new path. - $resourceData = $this->getResourceData(); - $this->deleteResourceData(); - - rename($this->path,$newPath); - $this->path = $newPath; - $this->putResourceData($resourceData); - - - } - - /** - * @return bool - */ - public function deleteResourceData() { - - // When we're deleting this node, we also need to delete any resource information - $path = $this->getResourceInfoPath(); - if (!file_exists($path)) return true; - - // opening up the file, and creating a shared lock - $handle = fopen($path,'a+'); - flock($handle,LOCK_EX); - $data = ''; - - rewind($handle); - - // Reading data until the eof - while(!feof($handle)) { - $data.=fread($handle,8192); - } - - // Unserializing and checking if the resource file contains data for this file - $data = unserialize($data); - if (isset($data[$this->getName()])) unset($data[$this->getName()]); - ftruncate($handle,0); - rewind($handle); - fwrite($handle,serialize($data)); - fclose($handle); - - return true; - } - - public function delete() { - - return $this->deleteResourceData(); - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/File.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/File.php deleted file mode 100644 index af8ce735fd..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/File.php +++ /dev/null @@ -1,85 +0,0 @@ - array( - * '{DAV:}displayname' => null, - * ), - * 424 => array( - * '{DAV:}owner' => null, - * ) - * ) - * - * In this example it was forbidden to update {DAV:}displayname. - * (403 Forbidden), which in turn also caused {DAV:}owner to fail - * (424 Failed Dependency) because the request needs to be atomic. - * - * @param array $mutations - * @return bool|array - */ - function updateProperties($mutations); - - /** - * Returns a list of properties for this nodes. - * - * The properties list is a list of propertynames the client requested, - * encoded in clark-notation {xmlnamespace}tagname - * - * If the array is empty, it means 'all properties' were requested. - * - * Note that it's fine to liberally give properties back, instead of - * conforming to the list of requested properties. - * The Server class will filter out the extra. - * - * @param array $properties - * @return void - */ - function getProperties($properties); - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/IQuota.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/IQuota.php deleted file mode 100644 index 988df3d062..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/IQuota.php +++ /dev/null @@ -1,27 +0,0 @@ -dataDir = $dataDir; - - } - - protected function getFileNameForUri($uri) { - - return $this->dataDir . '/sabredav_' . md5($uri) . '.locks'; - - } - - - /** - * Returns a list of Sabre\DAV\Locks\LockInfo objects - * - * This method should return all the locks for a particular uri, including - * locks that might be set on a parent uri. - * - * If returnChildLocks is set to true, this method should also look for - * any locks in the subtree of the uri for locks. - * - * @param string $uri - * @param bool $returnChildLocks - * @return array - */ - public function getLocks($uri, $returnChildLocks) { - - $lockList = array(); - $currentPath = ''; - - foreach(explode('/',$uri) as $uriPart) { - - // weird algorithm that can probably be improved, but we're traversing the path top down - if ($currentPath) $currentPath.='/'; - $currentPath.=$uriPart; - - $uriLocks = $this->getData($currentPath); - - foreach($uriLocks as $uriLock) { - - // Unless we're on the leaf of the uri-tree we should ignore locks with depth 0 - if($uri==$currentPath || $uriLock->depth!=0) { - $uriLock->uri = $currentPath; - $lockList[] = $uriLock; - } - - } - - } - - // Checking if we can remove any of these locks - foreach($lockList as $k=>$lock) { - if (time() > $lock->timeout + $lock->created) unset($lockList[$k]); - } - return $lockList; - - } - - /** - * Locks a uri - * - * @param string $uri - * @param LockInfo $lockInfo - * @return bool - */ - public function lock($uri, LockInfo $lockInfo) { - - // We're making the lock timeout 30 minutes - $lockInfo->timeout = 1800; - $lockInfo->created = time(); - - $locks = $this->getLocks($uri,false); - foreach($locks as $k=>$lock) { - if ($lock->token == $lockInfo->token) unset($locks[$k]); - } - $locks[] = $lockInfo; - $this->putData($uri,$locks); - return true; - - } - - /** - * Removes a lock from a uri - * - * @param string $uri - * @param LockInfo $lockInfo - * @return bool - */ - public function unlock($uri, LockInfo $lockInfo) { - - $locks = $this->getLocks($uri,false); - foreach($locks as $k=>$lock) { - - if ($lock->token == $lockInfo->token) { - - unset($locks[$k]); - $this->putData($uri,$locks); - return true; - - } - } - return false; - - } - - /** - * Returns the stored data for a uri - * - * @param string $uri - * @return array - */ - protected function getData($uri) { - - $path = $this->getFilenameForUri($uri); - if (!file_exists($path)) return array(); - - // opening up the file, and creating a shared lock - $handle = fopen($path,'r'); - flock($handle,LOCK_SH); - $data = ''; - - // Reading data until the eof - while(!feof($handle)) { - $data.=fread($handle,8192); - } - - // We're all good - fclose($handle); - - // Unserializing and checking if the resource file contains data for this file - $data = unserialize($data); - if (!$data) return array(); - return $data; - - } - - /** - * Updates the lock information - * - * @param string $uri - * @param array $newData - * @return void - */ - protected function putData($uri,array $newData) { - - $path = $this->getFileNameForUri($uri); - - // opening up the file, and creating a shared lock - $handle = fopen($path,'a+'); - flock($handle,LOCK_EX); - ftruncate($handle,0); - rewind($handle); - - fwrite($handle,serialize($newData)); - fclose($handle); - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Locks/Backend/File.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Locks/Backend/File.php deleted file mode 100644 index 9ac7e06b29..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Locks/Backend/File.php +++ /dev/null @@ -1,183 +0,0 @@ -locksFile = $locksFile; - - } - - /** - * Returns a list of Sabre\DAV\Locks\LockInfo objects - * - * This method should return all the locks for a particular uri, including - * locks that might be set on a parent uri. - * - * If returnChildLocks is set to true, this method should also look for - * any locks in the subtree of the uri for locks. - * - * @param string $uri - * @param bool $returnChildLocks - * @return array - */ - public function getLocks($uri, $returnChildLocks) { - - $newLocks = array(); - - $locks = $this->getData(); - - foreach($locks as $lock) { - - if ($lock->uri === $uri || - //deep locks on parents - ($lock->depth!=0 && strpos($uri, $lock->uri . '/')===0) || - - // locks on children - ($returnChildLocks && (strpos($lock->uri, $uri . '/')===0)) ) { - - $newLocks[] = $lock; - - } - - } - - // Checking if we can remove any of these locks - foreach($newLocks as $k=>$lock) { - if (time() > $lock->timeout + $lock->created) unset($newLocks[$k]); - } - return $newLocks; - - } - - /** - * Locks a uri - * - * @param string $uri - * @param LockInfo $lockInfo - * @return bool - */ - public function lock($uri, LockInfo $lockInfo) { - - // We're making the lock timeout 30 minutes - $lockInfo->timeout = 1800; - $lockInfo->created = time(); - $lockInfo->uri = $uri; - - $locks = $this->getData(); - - foreach($locks as $k=>$lock) { - if ( - ($lock->token == $lockInfo->token) || - (time() > $lock->timeout + $lock->created) - ) { - unset($locks[$k]); - } - } - $locks[] = $lockInfo; - $this->putData($locks); - return true; - - } - - /** - * Removes a lock from a uri - * - * @param string $uri - * @param LockInfo $lockInfo - * @return bool - */ - public function unlock($uri, LockInfo $lockInfo) { - - $locks = $this->getData(); - foreach($locks as $k=>$lock) { - - if ($lock->token == $lockInfo->token) { - - unset($locks[$k]); - $this->putData($locks); - return true; - - } - } - return false; - - } - - /** - * Loads the lockdata from the filesystem. - * - * @return array - */ - protected function getData() { - - if (!file_exists($this->locksFile)) return array(); - - // opening up the file, and creating a shared lock - $handle = fopen($this->locksFile,'r'); - flock($handle,LOCK_SH); - - // Reading data until the eof - $data = stream_get_contents($handle); - - // We're all good - fclose($handle); - - // Unserializing and checking if the resource file contains data for this file - $data = unserialize($data); - if (!$data) return array(); - return $data; - - } - - /** - * Saves the lockdata - * - * @param array $newData - * @return void - */ - protected function putData(array $newData) { - - // opening up the file, and creating an exclusive lock - $handle = fopen($this->locksFile,'a+'); - flock($handle,LOCK_EX); - - // We can only truncate and rewind once the lock is acquired. - ftruncate($handle,0); - rewind($handle); - - fwrite($handle,serialize($newData)); - fclose($handle); - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Locks/Backend/PDO.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Locks/Backend/PDO.php deleted file mode 100644 index ebaeef860c..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Locks/Backend/PDO.php +++ /dev/null @@ -1,167 +0,0 @@ -pdo = $pdo; - $this->tableName = $tableName; - - } - - /** - * Returns a list of Sabre\DAV\Locks\LockInfo objects - * - * This method should return all the locks for a particular uri, including - * locks that might be set on a parent uri. - * - * If returnChildLocks is set to true, this method should also look for - * any locks in the subtree of the uri for locks. - * - * @param string $uri - * @param bool $returnChildLocks - * @return array - */ - public function getLocks($uri, $returnChildLocks) { - - // NOTE: the following 10 lines or so could be easily replaced by - // pure sql. MySQL's non-standard string concatenation prevents us - // from doing this though. - $query = 'SELECT owner, token, timeout, created, scope, depth, uri FROM '.$this->tableName.' WHERE ((created + timeout) > CAST(? AS UNSIGNED INTEGER)) AND ((uri = ?)'; - $params = array(time(),$uri); - - // We need to check locks for every part in the uri. - $uriParts = explode('/',$uri); - - // We already covered the last part of the uri - array_pop($uriParts); - - $currentPath=''; - - foreach($uriParts as $part) { - - if ($currentPath) $currentPath.='/'; - $currentPath.=$part; - - $query.=' OR (depth!=0 AND uri = ?)'; - $params[] = $currentPath; - - } - - if ($returnChildLocks) { - - $query.=' OR (uri LIKE ?)'; - $params[] = $uri . '/%'; - - } - $query.=')'; - - $stmt = $this->pdo->prepare($query); - $stmt->execute($params); - $result = $stmt->fetchAll(); - - $lockList = array(); - foreach($result as $row) { - - $lockInfo = new LockInfo(); - $lockInfo->owner = $row['owner']; - $lockInfo->token = $row['token']; - $lockInfo->timeout = $row['timeout']; - $lockInfo->created = $row['created']; - $lockInfo->scope = $row['scope']; - $lockInfo->depth = $row['depth']; - $lockInfo->uri = $row['uri']; - $lockList[] = $lockInfo; - - } - - return $lockList; - - } - - /** - * Locks a uri - * - * @param string $uri - * @param LockInfo $lockInfo - * @return bool - */ - public function lock($uri, LockInfo $lockInfo) { - - // We're making the lock timeout 30 minutes - $lockInfo->timeout = 30*60; - $lockInfo->created = time(); - $lockInfo->uri = $uri; - - $locks = $this->getLocks($uri,false); - $exists = false; - foreach($locks as $lock) { - if ($lock->token == $lockInfo->token) $exists = true; - } - - if ($exists) { - $stmt = $this->pdo->prepare('UPDATE '.$this->tableName.' SET owner = ?, timeout = ?, scope = ?, depth = ?, uri = ?, created = ? WHERE token = ?'); - $stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token)); - } else { - $stmt = $this->pdo->prepare('INSERT INTO '.$this->tableName.' (owner,timeout,scope,depth,uri,created,token) VALUES (?,?,?,?,?,?,?)'); - $stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token)); - } - - return true; - - } - - - - /** - * Removes a lock from a uri - * - * @param string $uri - * @param LockInfo $lockInfo - * @return bool - */ - public function unlock($uri, LockInfo $lockInfo) { - - $stmt = $this->pdo->prepare('DELETE FROM '.$this->tableName.' WHERE uri = ? AND token = ?'); - $stmt->execute(array($uri,$lockInfo->token)); - - return $stmt->rowCount()===1; - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Locks/LockInfo.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Locks/LockInfo.php deleted file mode 100644 index 74bdb0f9c7..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Locks/LockInfo.php +++ /dev/null @@ -1,81 +0,0 @@ -addPlugin($lockPlugin); - * - * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/). - * @author Evert Pot (http://evertpot.com/) - * @license http://sabre.io/license/ Modified BSD License - */ -class Plugin extends DAV\ServerPlugin { - - /** - * locksBackend - * - * @var Backend\Backend\Interface - */ - protected $locksBackend; - - /** - * server - * - * @var Sabre\DAV\Server - */ - protected $server; - - /** - * __construct - * - * @param Backend\BackendInterface $locksBackend - */ - public function __construct(Backend\BackendInterface $locksBackend = null) { - - $this->locksBackend = $locksBackend; - - } - - /** - * Initializes the plugin - * - * This method is automatically called by the Server class after addPlugin. - * - * @param DAV\Server $server - * @return void - */ - public function initialize(DAV\Server $server) { - - $this->server = $server; - $server->subscribeEvent('unknownMethod',array($this,'unknownMethod')); - $server->subscribeEvent('beforeMethod',array($this,'beforeMethod'),50); - $server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties')); - - } - - /** - * Returns a plugin name. - * - * Using this name other plugins will be able to access other plugins - * using Sabre\DAV\Server::getPlugin - * - * @return string - */ - public function getPluginName() { - - return 'locks'; - - } - - /** - * This method is called by the Server if the user used an HTTP method - * the server didn't recognize. - * - * This plugin intercepts the LOCK and UNLOCK methods. - * - * @param string $method - * @param string $uri - * @return bool - */ - public function unknownMethod($method, $uri) { - - switch($method) { - - case 'LOCK' : $this->httpLock($uri); return false; - case 'UNLOCK' : $this->httpUnlock($uri); return false; - - } - - } - - /** - * This method is called after most properties have been found - * it allows us to add in any Lock-related properties - * - * @param string $path - * @param array $newProperties - * @return bool - */ - public function afterGetProperties($path, &$newProperties) { - - foreach($newProperties[404] as $propName=>$discard) { - - switch($propName) { - - case '{DAV:}supportedlock' : - $val = false; - if ($this->locksBackend) $val = true; - $newProperties[200][$propName] = new DAV\Property\SupportedLock($val); - unset($newProperties[404][$propName]); - break; - - case '{DAV:}lockdiscovery' : - $newProperties[200][$propName] = new DAV\Property\LockDiscovery($this->getLocks($path)); - unset($newProperties[404][$propName]); - break; - - } - - - } - return true; - - } - - - /** - * This method is called before the logic for any HTTP method is - * handled. - * - * This plugin uses that feature to intercept access to locked resources. - * - * @param string $method - * @param string $uri - * @return bool - */ - public function beforeMethod($method, $uri) { - - switch($method) { - - case 'DELETE' : - $lastLock = null; - if (!$this->validateLock($uri,$lastLock, true)) - throw new DAV\Exception\Locked($lastLock); - break; - case 'MKCOL' : - case 'PROPPATCH' : - case 'PUT' : - case 'PATCH' : - $lastLock = null; - if (!$this->validateLock($uri,$lastLock)) - throw new DAV\Exception\Locked($lastLock); - break; - case 'MOVE' : - $lastLock = null; - if (!$this->validateLock(array( - $uri, - $this->server->calculateUri($this->server->httpRequest->getHeader('Destination')), - ),$lastLock, true)) - throw new DAV\Exception\Locked($lastLock); - break; - case 'COPY' : - $lastLock = null; - if (!$this->validateLock( - $this->server->calculateUri($this->server->httpRequest->getHeader('Destination')), - $lastLock, true)) - throw new DAV\Exception\Locked($lastLock); - break; - } - - return true; - - } - - /** - * Use this method to tell the server this plugin defines additional - * HTTP methods. - * - * This method is passed a uri. It should only return HTTP methods that are - * available for the specified uri. - * - * @param string $uri - * @return array - */ - public function getHTTPMethods($uri) { - - if ($this->locksBackend) - return array('LOCK','UNLOCK'); - - return array(); - - } - - /** - * Returns a list of features for the HTTP OPTIONS Dav: header. - * - * In this case this is only the number 2. The 2 in the Dav: header - * indicates the server supports locks. - * - * @return array - */ - public function getFeatures() { - - return array(2); - - } - - /** - * Returns all lock information on a particular uri - * - * This function should return an array with Sabre\DAV\Locks\LockInfo objects. If there are no locks on a file, return an empty array. - * - * Additionally there is also the possibility of locks on parent nodes, so we'll need to traverse every part of the tree - * If the $returnChildLocks argument is set to true, we'll also traverse all the children of the object - * for any possible locks and return those as well. - * - * @param string $uri - * @param bool $returnChildLocks - * @return array - */ - public function getLocks($uri, $returnChildLocks = false) { - - $lockList = array(); - - if ($this->locksBackend) - $lockList = array_merge($lockList,$this->locksBackend->getLocks($uri, $returnChildLocks)); - - return $lockList; - - } - - /** - * Locks an uri - * - * The WebDAV lock request can be operated to either create a new lock on a file, or to refresh an existing lock - * If a new lock is created, a full XML body should be supplied, containing information about the lock such as the type - * of lock (shared or exclusive) and the owner of the lock - * - * If a lock is to be refreshed, no body should be supplied and there should be a valid If header containing the lock - * - * Additionally, a lock can be requested for a non-existent file. In these case we're obligated to create an empty file as per RFC4918:S7.3 - * - * @param string $uri - * @return void - */ - protected function httpLock($uri) { - - $lastLock = null; - if (!$this->validateLock($uri,$lastLock)) { - - // If the existing lock was an exclusive lock, we need to fail - if (!$lastLock || $lastLock->scope == LockInfo::EXCLUSIVE) { - //var_dump($lastLock); - throw new DAV\Exception\ConflictingLock($lastLock); - } - - } - - if ($body = $this->server->httpRequest->getBody(true)) { - // This is a new lock request - $lockInfo = $this->parseLockRequest($body); - $lockInfo->depth = $this->server->getHTTPDepth(); - $lockInfo->uri = $uri; - if($lastLock && $lockInfo->scope != LockInfo::SHARED) throw new DAV\Exception\ConflictingLock($lastLock); - - } elseif ($lastLock) { - - // This must have been a lock refresh - $lockInfo = $lastLock; - - // The resource could have been locked through another uri. - if ($uri!=$lockInfo->uri) $uri = $lockInfo->uri; - - } else { - - // There was neither a lock refresh nor a new lock request - throw new DAV\Exception\BadRequest('An xml body is required for lock requests'); - - } - - if ($timeout = $this->getTimeoutHeader()) $lockInfo->timeout = $timeout; - - $newFile = false; - - // If we got this far.. we should go check if this node actually exists. If this is not the case, we need to create it first - try { - $this->server->tree->getNodeForPath($uri); - - // We need to call the beforeWriteContent event for RFC3744 - // Edit: looks like this is not used, and causing problems now. - // - // See Issue 222 - // $this->server->broadcastEvent('beforeWriteContent',array($uri)); - - } catch (DAV\Exception\NotFound $e) { - - // It didn't, lets create it - $this->server->createFile($uri,fopen('php://memory','r')); - $newFile = true; - - } - - $this->lockNode($uri,$lockInfo); - - $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->server->httpResponse->setHeader('Lock-Token','token . '>'); - $this->server->httpResponse->sendStatus($newFile?201:200); - $this->server->httpResponse->sendBody($this->generateLockResponse($lockInfo)); - - } - - /** - * Unlocks a uri - * - * This WebDAV method allows you to remove a lock from a node. The client should provide a valid locktoken through the Lock-token http header - * The server should return 204 (No content) on success - * - * @param string $uri - * @return void - */ - protected function httpUnlock($uri) { - - $lockToken = $this->server->httpRequest->getHeader('Lock-Token'); - - // If the locktoken header is not supplied, we need to throw a bad request exception - if (!$lockToken) throw new DAV\Exception\BadRequest('No lock token was supplied'); - - $locks = $this->getLocks($uri); - - // Windows sometimes forgets to include < and > in the Lock-Token - // header - if ($lockToken[0]!=='<') $lockToken = '<' . $lockToken . '>'; - - foreach($locks as $lock) { - - if ('token . '>' == $lockToken) { - - $this->unlockNode($uri,$lock); - $this->server->httpResponse->setHeader('Content-Length','0'); - $this->server->httpResponse->sendStatus(204); - return; - - } - - } - - // If we got here, it means the locktoken was invalid - throw new DAV\Exception\LockTokenMatchesRequestUri(); - - } - - /** - * Locks a uri - * - * All the locking information is supplied in the lockInfo object. The object has a suggested timeout, but this can be safely ignored - * It is important that if the existing timeout is ignored, the property is overwritten, as this needs to be sent back to the client - * - * @param string $uri - * @param LockInfo $lockInfo - * @return bool - */ - public function lockNode($uri,LockInfo $lockInfo) { - - if (!$this->server->broadcastEvent('beforeLock',array($uri,$lockInfo))) return; - - if ($this->locksBackend) return $this->locksBackend->lock($uri,$lockInfo); - throw new DAV\Exception\MethodNotAllowed('Locking support is not enabled for this resource. No Locking backend was found so if you didn\'t expect this error, please check your configuration.'); - - } - - /** - * Unlocks a uri - * - * This method removes a lock from a uri. It is assumed all the supplied information is correct and verified - * - * @param string $uri - * @param LockInfo $lockInfo - * @return bool - */ - public function unlockNode($uri, LockInfo $lockInfo) { - - if (!$this->server->broadcastEvent('beforeUnlock',array($uri,$lockInfo))) return; - if ($this->locksBackend) return $this->locksBackend->unlock($uri,$lockInfo); - - } - - - /** - * Returns the contents of the HTTP Timeout header. - * - * The method formats the header into an integer. - * - * @return int - */ - public function getTimeoutHeader() { - - $header = $this->server->httpRequest->getHeader('Timeout'); - - if ($header) { - - if (stripos($header,'second-')===0) $header = (int)(substr($header,7)); - else if (strtolower($header)=='infinite') $header = LockInfo::TIMEOUT_INFINITE; - else throw new DAV\Exception\BadRequest('Invalid HTTP timeout header'); - - } else { - - $header = 0; - - } - - return $header; - - } - - /** - * Generates the response for successful LOCK requests - * - * @param LockInfo $lockInfo - * @return string - */ - protected function generateLockResponse(LockInfo $lockInfo) { - - $dom = new \DOMDocument('1.0','utf-8'); - $dom->formatOutput = true; - - $prop = $dom->createElementNS('DAV:','d:prop'); - $dom->appendChild($prop); - - $lockDiscovery = $dom->createElementNS('DAV:','d:lockdiscovery'); - $prop->appendChild($lockDiscovery); - - $lockObj = new DAV\Property\LockDiscovery(array($lockInfo),true); - $lockObj->serialize($this->server,$lockDiscovery); - - return $dom->saveXML(); - - } - - /** - * validateLock should be called when a write operation is about to happen - * It will check if the requested url is locked, and see if the correct lock tokens are passed - * - * @param mixed $urls List of relevant urls. Can be an array, a string or nothing at all for the current request uri - * @param mixed $lastLock This variable will be populated with the last checked lock object (Sabre\DAV\Locks\LockInfo) - * @param bool $checkChildLocks If set to true, this function will also look for any locks set on child resources of the supplied urls. This is needed for for example deletion of entire trees. - * @return bool - */ - protected function validateLock($urls = null,&$lastLock = null, $checkChildLocks = false) { - - if (is_null($urls)) { - $urls = array($this->server->getRequestUri()); - } elseif (is_string($urls)) { - $urls = array($urls); - } elseif (!is_array($urls)) { - throw new DAV\Exception('The urls parameter should either be null, a string or an array'); - } - - $conditions = $this->getIfConditions(); - - // We're going to loop through the urls and make sure all lock conditions are satisfied - foreach($urls as $url) { - - $locks = $this->getLocks($url, $checkChildLocks); - - // If there were no conditions, but there were locks, we fail - if (!$conditions && $locks) { - reset($locks); - $lastLock = current($locks); - return false; - } - - // If there were no locks or conditions, we go to the next url - if (!$locks && !$conditions) continue; - - foreach($conditions as $condition) { - - if (!$condition['uri']) { - $conditionUri = $this->server->getRequestUri(); - } else { - $conditionUri = $this->server->calculateUri($condition['uri']); - } - - // If the condition has a url, and it isn't part of the affected url at all, check the next condition - if ($conditionUri && strpos($url,$conditionUri)!==0) continue; - - // The tokens array contians arrays with 2 elements. 0=true/false for normal/not condition, 1=locktoken - // At least 1 condition has to be satisfied - foreach($condition['tokens'] as $conditionToken) { - - $etagValid = true; - $lockValid = true; - - // key 2 can contain an etag - if ($conditionToken[2]) { - - $uri = $conditionUri?$conditionUri:$this->server->getRequestUri(); - $node = $this->server->tree->getNodeForPath($uri); - $etagValid = $node instanceof DAV\IFile && $node->getETag()==$conditionToken[2]; - - } - - // key 1 can contain a lock token - if ($conditionToken[1]) { - - $lockValid = false; - // Match all the locks - foreach($locks as $lockIndex=>$lock) { - - $lockToken = 'opaquelocktoken:' . $lock->token; - - // Checking NOT - if (!$conditionToken[0] && $lockToken != $conditionToken[1]) { - - // Condition valid, onto the next - $lockValid = true; - break; - } - if ($conditionToken[0] && $lockToken == $conditionToken[1]) { - - $lastLock = $lock; - // Condition valid and lock matched - unset($locks[$lockIndex]); - $lockValid = true; - break; - - } - - } - - } - - // If, after checking both etags and locks they are stil valid, - // we can continue with the next condition. - if ($etagValid && $lockValid) continue 2; - } - // No conditions matched, so we fail - throw new DAV\Exception\PreconditionFailed('The tokens provided in the if header did not match','If'); - } - - // Conditions were met, we'll also need to check if all the locks are gone - if (count($locks)) { - - reset($locks); - - // There's still locks, we fail - $lastLock = current($locks); - return false; - - } - - - } - - // We got here, this means every condition was satisfied - return true; - - } - - /** - * This method is created to extract information from the WebDAV HTTP 'If:' header - * - * The If header can be quite complex, and has a bunch of features. We're using a regex to extract all relevant information - * The function will return an array, containing structs with the following keys - * - * * uri - the uri the condition applies to. If this is returned as an - * empty string, this implies it's referring to the request url. - * * tokens - The lock token. another 2 dimensional array containing 2 elements (0 = true/false.. If this is a negative condition its set to false, 1 = the actual token) - * * etag - an etag, if supplied - * - * @return array - */ - public function getIfConditions() { - - $header = $this->server->httpRequest->getHeader('If'); - if (!$header) return array(); - - $matches = array(); - - $regex = '/(?:\<(?P.*?)\>\s)?\((?PNot\s)?(?:\<(?P[^\>]*)\>)?(?:\s?)(?:\[(?P[^\]]*)\])?\)/im'; - preg_match_all($regex,$header,$matches,PREG_SET_ORDER); - - $conditions = array(); - - foreach($matches as $match) { - - $condition = array( - 'uri' => $match['uri'], - 'tokens' => array( - array($match['not']?0:1,$match['token'],isset($match['etag'])?$match['etag']:'') - ), - ); - - if (!$condition['uri'] && count($conditions)) $conditions[count($conditions)-1]['tokens'][] = array( - $match['not']?0:1, - $match['token'], - isset($match['etag'])?$match['etag']:'' - ); - else { - $conditions[] = $condition; - } - - } - - return $conditions; - - } - - /** - * Parses a webdav lock xml body, and returns a new Sabre\DAV\Locks\LockInfo object - * - * @param string $body - * @return DAV\Locks\LockInfo - */ - protected function parseLockRequest($body) { - - // Fixes an XXE vulnerability on PHP versions older than 5.3.23 or - // 5.4.13. - $previous = libxml_disable_entity_loader(true); - - - $xml = simplexml_load_string( - DAV\XMLUtil::convertDAVNamespace($body), - null, - LIBXML_NOWARNING); - libxml_disable_entity_loader($previous); - - $xml->registerXPathNamespace('d','urn:DAV'); - $lockInfo = new LockInfo(); - - $children = $xml->children("urn:DAV"); - $lockInfo->owner = (string)$children->owner; - - $lockInfo->token = DAV\UUIDUtil::getUUID(); - $lockInfo->scope = count($xml->xpath('d:lockscope/d:exclusive'))>0 ? LockInfo::EXCLUSIVE : LockInfo::SHARED; - - return $lockInfo; - - } - - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Mount/Plugin.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Mount/Plugin.php deleted file mode 100644 index 8376b03b06..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Mount/Plugin.php +++ /dev/null @@ -1,83 +0,0 @@ -server = $server; - $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90); - - } - - /** - * 'beforeMethod' event handles. This event handles intercepts GET requests ending - * with ?mount - * - * @param string $method - * @param string $uri - * @return bool - */ - public function beforeMethod($method, $uri) { - - if ($method!='GET') return; - if ($this->server->httpRequest->getQueryString()!='mount') return; - - $currentUri = $this->server->httpRequest->getAbsoluteUri(); - - // Stripping off everything after the ? - list($currentUri) = explode('?',$currentUri); - - $this->davMount($currentUri); - - // Returning false to break the event chain - return false; - - } - - /** - * Generates the davmount response - * - * @param string $uri absolute uri - * @return void - */ - public function davMount($uri) { - - $this->server->httpResponse->sendStatus(200); - $this->server->httpResponse->setHeader('Content-Type','application/davmount+xml'); - ob_start(); - echo '', "\n"; - echo "\n"; - echo " ", htmlspecialchars($uri, ENT_NOQUOTES, 'UTF-8'), "\n"; - echo ""; - $this->server->httpResponse->sendBody(ob_get_clean()); - - } - - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Node.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Node.php deleted file mode 100644 index 44e47be68f..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Node.php +++ /dev/null @@ -1,55 +0,0 @@ -rootNode = $rootNode; - - } - - /** - * Returns the INode object for the requested path - * - * @param string $path - * @return INode - */ - public function getNodeForPath($path) { - - $path = trim($path,'/'); - if (isset($this->cache[$path])) return $this->cache[$path]; - - // Is it the root node? - if (!strlen($path)) { - return $this->rootNode; - } - - // Attempting to fetch its parent - list($parentName, $baseName) = URLUtil::splitPath($path); - - // If there was no parent, we must simply ask it from the root node. - if ($parentName==="") { - $node = $this->rootNode->getChild($baseName); - } else { - // Otherwise, we recursively grab the parent and ask him/her. - $parent = $this->getNodeForPath($parentName); - - if (!($parent instanceof ICollection)) - throw new Exception\NotFound('Could not find node at path: ' . $path); - - $node = $parent->getChild($baseName); - - } - - $this->cache[$path] = $node; - return $node; - - } - - /** - * This function allows you to check if a node exists. - * - * @param string $path - * @return bool - */ - public function nodeExists($path) { - - try { - - // The root always exists - if ($path==='') return true; - - list($parent, $base) = URLUtil::splitPath($path); - - $parentNode = $this->getNodeForPath($parent); - if (!$parentNode instanceof ICollection) return false; - return $parentNode->childExists($base); - - } catch (Exception\NotFound $e) { - - return false; - - } - - } - - /** - * Returns a list of childnodes for a given path. - * - * @param string $path - * @return array - */ - public function getChildren($path) { - - $node = $this->getNodeForPath($path); - $children = $node->getChildren(); - foreach($children as $child) { - - $this->cache[trim($path,'/') . '/' . $child->getName()] = $child; - - } - return $children; - - } - - /** - * This method is called with every tree update - * - * Examples of tree updates are: - * * node deletions - * * node creations - * * copy - * * move - * * renaming nodes - * - * If Tree classes implement a form of caching, this will allow - * them to make sure caches will be expired. - * - * If a path is passed, it is assumed that the entire subtree is dirty - * - * @param string $path - * @return void - */ - public function markDirty($path) { - - // We don't care enough about sub-paths - // flushing the entire cache - $path = trim($path,'/'); - foreach($this->cache as $nodePath=>$node) { - if ($nodePath == $path || strpos($nodePath,$path.'/')===0) - unset($this->cache[$nodePath]); - - } - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/PartialUpdate/IFile.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/PartialUpdate/IFile.php deleted file mode 100644 index 9cfb47377b..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/PartialUpdate/IFile.php +++ /dev/null @@ -1,39 +0,0 @@ -addPlugin($patchPlugin); - * - * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/). - * @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/) - * @license http://sabre.io/license/ Modified BSD License - */ -class Plugin extends DAV\ServerPlugin { - - const RANGE_APPEND = 1; - const RANGE_START = 2; - const RANGE_END = 3; - - /** - * Reference to server - * - * @var Sabre\DAV\Server - */ - protected $server; - - /** - * Initializes the plugin - * - * This method is automatically called by the Server class after addPlugin. - * - * @param DAV\Server $server - * @return void - */ - public function initialize(DAV\Server $server) { - - $this->server = $server; - $server->subscribeEvent('unknownMethod',array($this,'unknownMethod')); - - } - - /** - * Returns a plugin name. - * - * Using this name other plugins will be able to access other plugins - * using DAV\Server::getPlugin - * - * @return string - */ - public function getPluginName() { - - return 'partialupdate'; - - } - - /** - * This method is called by the Server if the user used an HTTP method - * the server didn't recognize. - * - * This plugin intercepts the PATCH methods. - * - * @param string $method - * @param string $uri - * @return bool|null - */ - public function unknownMethod($method, $uri) { - - switch($method) { - - case 'PATCH': - return $this->httpPatch($uri); - - } - - } - - /** - * Use this method to tell the server this plugin defines additional - * HTTP methods. - * - * This method is passed a uri. It should only return HTTP methods that are - * available for the specified uri. - * - * We claim to support PATCH method (partial update) if and only if - * - the node exist - * - the node implements our partial update interface - * - * @param string $uri - * @return array - */ - public function getHTTPMethods($uri) { - - $tree = $this->server->tree; - if ($tree->nodeExists($uri)) { - $node = $tree->getNodeForPath($uri); - if ($node instanceof IFile || $node instanceof IPatchSupport) { - return array('PATCH'); - } - } - return array(); - - } - - /** - * Returns a list of features for the HTTP OPTIONS Dav: header. - * - * @return array - */ - public function getFeatures() { - - return array('sabredav-partialupdate'); - - } - - /** - * Patch an uri - * - * The WebDAV patch request can be used to modify only a part of an - * existing resource. If the resource does not exist yet and the first - * offset is not 0, the request fails - * - * @param string $uri - * @return void - */ - protected function httpPatch($uri) { - - // Get the node. Will throw a 404 if not found - $node = $this->server->tree->getNodeForPath($uri); - if (!$node instanceof IFile && !$node instanceof IPatchSupport) { - throw new DAV\Exception\MethodNotAllowed('The target resource does not support the PATCH method.'); - } - - $range = $this->getHTTPUpdateRange(); - - if (!$range) { - throw new DAV\Exception\BadRequest('No valid "X-Update-Range" found in the headers'); - } - - $contentType = strtolower( - $this->server->httpRequest->getHeader('Content-Type') - ); - - if ($contentType != 'application/x-sabredav-partialupdate') { - throw new DAV\Exception\UnsupportedMediaType('Unknown Content-Type header "' . $contentType . '"'); - } - - $len = $this->server->httpRequest->getHeader('Content-Length'); - if (!$len) throw new DAV\Exception\LengthRequired('A Content-Length header is required'); - - switch($range[0]) { - case self::RANGE_START : - // Calculate the end-range if it doesn't exist. - if (!$range[2]) { - $range[2] = $range[1] + $len - 1; - } else { - if ($range[2] < $range[1]) { - throw new DAV\Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[2] . ') is lower than the start offset (' . $range[1] . ')'); - } - if($range[2] - $range[1] + 1 != $len) { - throw new DAV\Exception\RequestedRangeNotSatisfiable('Actual data length (' . $len . ') is not consistent with begin (' . $range[1] . ') and end (' . $range[2] . ') offsets'); - } - } - break; - } - // Checking If-None-Match and related headers. - if (!$this->server->checkPreconditions()) return; - - if (!$this->server->broadcastEvent('beforeWriteContent',array($uri, $node, null))) - return; - - $body = $this->server->httpRequest->getBody(); - - - if ($node instanceof IPatchSupport) { - $etag = $node->patch($body, $range[0], isset($range[1])?$range[1]:null); - } else { - // The old interface - switch($range[0]) { - case self::RANGE_APPEND : - throw new DAV\Exception\NotImplemented('This node does not support the append syntax. Please upgrade it to IPatchSupport'); - case self::RANGE_START : - $etag = $node->putRange($body, $range[1]); - break; - case self::RANGE_END : - throw new DAV\Exception\NotImplemented('This node does not support the end-range syntax. Please upgrade it to IPatchSupport'); - break; - } - } - - $this->server->broadcastEvent('afterWriteContent',array($uri, $node)); - - $this->server->httpResponse->setHeader('Content-Length','0'); - if ($etag) $this->server->httpResponse->setHeader('ETag',$etag); - $this->server->httpResponse->sendStatus(204); - - return false; - - } - - /** - * Returns the HTTP custom range update header - * - * This method returns null if there is no well-formed HTTP range request - * header. It returns array(1) if it was an append request, array(2, - * $start, $end) if it's a start and end range, lastly it's array(3, - * $endoffset) if the offset was negative, and should be calculated from - * the end of the file. - * - * Examples: - * - * null - invalid - * array(1) - append - * array(2,10,15) - update bytes 10, 11, 12, 13, 14, 15 - * array(2,10,null) - update bytes 10 until the end of the patch body - * array(3,-5) - update from 5 bytes from the end of the file. - * - * @return array|null - */ - public function getHTTPUpdateRange() { - - $range = $this->server->httpRequest->getHeader('X-Update-Range'); - if (is_null($range)) return null; - - // Matching "Range: bytes=1234-5678: both numbers are optional - - if (!preg_match('/^(append)|(?:bytes=([0-9]+)-([0-9]*))|(?:bytes=(-[0-9]+))$/i',$range,$matches)) return null; - - if ($matches[1]==='append') { - return array(self::RANGE_APPEND); - } elseif (strlen($matches[2])>0) { - return array(self::RANGE_START, $matches[2], $matches[3]?:null); - } elseif ($matches[4]) { - return array(self::RANGE_END, $matches[4]); - } else { - return null; - } - - } -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Property.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Property.php deleted file mode 100644 index d0c265907c..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Property.php +++ /dev/null @@ -1,31 +0,0 @@ -time = $time; - } elseif (is_int($time) || ctype_digit($time)) { - $this->time = new \DateTime('@' . $time); - } else { - $this->time = new \DateTime($time); - } - - // Setting timezone to UTC - $this->time->setTimezone(new \DateTimeZone('UTC')); - - } - - /** - * serialize - * - * @param DAV\Server $server - * @param \DOMElement $prop - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $prop) { - - $doc = $prop->ownerDocument; - //$prop->setAttribute('xmlns:b','urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/'); - //$prop->setAttribute('b:dt','dateTime.rfc1123'); - $prop->nodeValue = HTTP\Util::toHTTPDate($this->time); - - } - - /** - * getTime - * - * @return \DateTime - */ - public function getTime() { - - return $this->time; - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/Href.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/Href.php deleted file mode 100644 index f0c162706c..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/Href.php +++ /dev/null @@ -1,99 +0,0 @@ -href = $href; - $this->autoPrefix = $autoPrefix; - - } - - /** - * Returns the uri - * - * @return string - */ - public function getHref() { - - return $this->href; - - } - - /** - * Serializes this property. - * - * It will additionally prepend the href property with the server's base uri. - * - * @param DAV\Server $server - * @param \DOMElement $dom - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $dom) { - - $prefix = $server->xmlNamespaces['DAV:']; - $elem = $dom->ownerDocument->createElement($prefix . ':href'); - - if ($this->autoPrefix) { - $value = $server->getBaseUri() . DAV\URLUtil::encodePath($this->href); - } else { - $value = $this->href; - } - $elem->appendChild($dom->ownerDocument->createTextNode($value)); - - $dom->appendChild($elem); - - } - - /** - * Unserializes this property from a DOM Element - * - * This method returns an instance of this class. - * It will only decode {DAV:}href values. For non-compatible elements null will be returned. - * - * @param \DOMElement $dom - * @return DAV\Property\Href - */ - static function unserialize(\DOMElement $dom) { - - if ($dom->firstChild && DAV\XMLUtil::toClarkNotation($dom->firstChild)==='{DAV:}href') { - return new self($dom->firstChild->textContent,false); - } - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/HrefList.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/HrefList.php deleted file mode 100644 index a5bad4ace3..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/HrefList.php +++ /dev/null @@ -1,105 +0,0 @@ -hrefs = $hrefs; - $this->autoPrefix = $autoPrefix; - - } - - /** - * Returns the uris - * - * @return array - */ - public function getHrefs() { - - return $this->hrefs; - - } - - /** - * Serializes this property. - * - * It will additionally prepend the href property with the server's base uri. - * - * @param DAV\Server $server - * @param \DOMElement $dom - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $dom) { - - $prefix = $server->xmlNamespaces['DAV:']; - - foreach($this->hrefs as $href) { - - $elem = $dom->ownerDocument->createElement($prefix . ':href'); - if ($this->autoPrefix) { - $value = $server->getBaseUri() . DAV\URLUtil::encodePath($href); - } else { - $value = $href; - } - $elem->appendChild($dom->ownerDocument->createTextNode($value)); - - $dom->appendChild($elem); - } - - } - - /** - * Unserializes this property from a DOM Element - * - * This method returns an instance of this class. - * It will only decode {DAV:}href values. - * - * @param \DOMElement $dom - * @return DAV\Property\HrefList - */ - static function unserialize(\DOMElement $dom) { - - $hrefs = array(); - foreach($dom->childNodes as $child) { - if (DAV\XMLUtil::toClarkNotation($child)==='{DAV:}href') { - $hrefs[] = $child->textContent; - } - } - return new self($hrefs, false); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/IHref.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/IHref.php deleted file mode 100644 index 268ab8d517..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/IHref.php +++ /dev/null @@ -1,25 +0,0 @@ -locks = $locks; - $this->revealLockToken = $revealLockToken; - - } - - /** - * serialize - * - * @param DAV\Server $server - * @param \DOMElement $prop - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $prop) { - - $doc = $prop->ownerDocument; - - foreach($this->locks as $lock) { - - $activeLock = $doc->createElementNS('DAV:','d:activelock'); - $prop->appendChild($activeLock); - - $lockScope = $doc->createElementNS('DAV:','d:lockscope'); - $activeLock->appendChild($lockScope); - - $lockScope->appendChild($doc->createElementNS('DAV:','d:' . ($lock->scope==DAV\Locks\LockInfo::EXCLUSIVE?'exclusive':'shared'))); - - $lockType = $doc->createElementNS('DAV:','d:locktype'); - $activeLock->appendChild($lockType); - - $lockType->appendChild($doc->createElementNS('DAV:','d:write')); - - /* {DAV:}lockroot */ - if (!self::$hideLockRoot) { - $lockRoot = $doc->createElementNS('DAV:','d:lockroot'); - $activeLock->appendChild($lockRoot); - $href = $doc->createElementNS('DAV:','d:href'); - $href->appendChild($doc->createTextNode($server->getBaseUri() . $lock->uri)); - $lockRoot->appendChild($href); - } - - $activeLock->appendChild($doc->createElementNS('DAV:','d:depth',($lock->depth == DAV\Server::DEPTH_INFINITY?'infinity':$lock->depth))); - $activeLock->appendChild($doc->createElementNS('DAV:','d:timeout','Second-' . $lock->timeout)); - - if ($this->revealLockToken) { - $lockToken = $doc->createElementNS('DAV:','d:locktoken'); - $activeLock->appendChild($lockToken); - $lockToken->appendChild($doc->createElementNS('DAV:','d:href','opaquelocktoken:' . $lock->token)); - } - - $activeLock->appendChild($doc->createElementNS('DAV:','d:owner',$lock->owner)); - - } - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/ResourceType.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/ResourceType.php deleted file mode 100644 index 68134f3f91..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/ResourceType.php +++ /dev/null @@ -1,127 +0,0 @@ -resourceType = array(); - elseif ($resourceType === DAV\Server::NODE_DIRECTORY) - $this->resourceType = array('{DAV:}collection'); - elseif (is_array($resourceType)) - $this->resourceType = $resourceType; - else - $this->resourceType = array($resourceType); - - } - - /** - * serialize - * - * @param DAV\Server $server - * @param \DOMElement $prop - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $prop) { - - $propName = null; - $rt = $this->resourceType; - - foreach($rt as $resourceType) { - if (preg_match('/^{([^}]*)}(.*)$/',$resourceType,$propName)) { - - if (isset($server->xmlNamespaces[$propName[1]])) { - $prop->appendChild($prop->ownerDocument->createElement($server->xmlNamespaces[$propName[1]] . ':' . $propName[2])); - } else { - $prop->appendChild($prop->ownerDocument->createElementNS($propName[1],'custom:' . $propName[2])); - } - - } - } - - } - - /** - * Returns the values in clark-notation - * - * For example array('{DAV:}collection') - * - * @return array - */ - public function getValue() { - - return $this->resourceType; - - } - - /** - * Checks if the principal contains a certain value - * - * @param string $type - * @return bool - */ - public function is($type) { - - return in_array($type, $this->resourceType); - - } - - /** - * Adds a resourcetype value to this property - * - * @param string $type - * @return void - */ - public function add($type) { - - $this->resourceType[] = $type; - $this->resourceType = array_unique($this->resourceType); - - } - - /** - * Unserializes a DOM element into a ResourceType property. - * - * @param \DOMElement $dom - * @return DAV\Property\ResourceType - */ - static public function unserialize(\DOMElement $dom) { - - $value = array(); - foreach($dom->childNodes as $child) { - - $value[] = DAV\XMLUtil::toClarkNotation($child); - - } - - return new self($value); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/Response.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/Response.php deleted file mode 100644 index 370abc26be..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/Response.php +++ /dev/null @@ -1,157 +0,0 @@ -href = $href; - $this->responseProperties = $responseProperties; - - } - - /** - * Returns the url - * - * @return string - */ - public function getHref() { - - return $this->href; - - } - - /** - * Returns the property list - * - * @return array - */ - public function getResponseProperties() { - - return $this->responseProperties; - - } - - /** - * serialize - * - * @param DAV\Server $server - * @param \DOMElement $dom - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $dom) { - - $document = $dom->ownerDocument; - $properties = $this->responseProperties; - - $xresponse = $document->createElement('d:response'); - $dom->appendChild($xresponse); - - $uri = DAV\URLUtil::encodePath($this->href); - - // Adding the baseurl to the beginning of the url - $uri = $server->getBaseUri() . $uri; - - $xresponse->appendChild($document->createElement('d:href',$uri)); - - // The properties variable is an array containing properties, grouped by - // HTTP status - foreach($properties as $httpStatus=>$propertyGroup) { - - // The 'href' is also in this array, and it's special cased. - // We will ignore it - if ($httpStatus=='href') continue; - - // If there are no properties in this group, we can also just carry on - if (!count($propertyGroup)) continue; - - $xpropstat = $document->createElement('d:propstat'); - $xresponse->appendChild($xpropstat); - - $xprop = $document->createElement('d:prop'); - $xpropstat->appendChild($xprop); - - $nsList = $server->xmlNamespaces; - - foreach($propertyGroup as $propertyName=>$propertyValue) { - - $propName = null; - preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName); - - // special case for empty namespaces - if ($propName[1]=='') { - - $currentProperty = $document->createElement($propName[2]); - $xprop->appendChild($currentProperty); - $currentProperty->setAttribute('xmlns',''); - - } else { - - if (!isset($nsList[$propName[1]])) { - $nsList[$propName[1]] = 'x' . count($nsList); - } - - // If the namespace was defined in the top-level xml namespaces, it means - // there was already a namespace declaration, and we don't have to worry about it. - if (isset($server->xmlNamespaces[$propName[1]])) { - $currentProperty = $document->createElement($nsList[$propName[1]] . ':' . $propName[2]); - } else { - $currentProperty = $document->createElementNS($propName[1],$nsList[$propName[1]].':' . $propName[2]); - } - $xprop->appendChild($currentProperty); - - } - - if (is_scalar($propertyValue)) { - $text = $document->createTextNode($propertyValue); - $currentProperty->appendChild($text); - } elseif ($propertyValue instanceof DAV\PropertyInterface) { - $propertyValue->serialize($server,$currentProperty); - } elseif (!is_null($propertyValue)) { - throw new DAV\Exception('Unknown property value type: ' . gettype($propertyValue) . ' for property: ' . $propertyName); - } - - } - - $xpropstat->appendChild($document->createElement('d:status',$server->httpResponse->getStatusMessage($httpStatus))); - - } - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/ResponseList.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/ResponseList.php deleted file mode 100644 index 9db6cbbf59..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/ResponseList.php +++ /dev/null @@ -1,59 +0,0 @@ -responses = $responses; - - } - - /** - * serialize - * - * @param DAV\Server $server - * @param \DOMElement $dom - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $dom) { - - foreach($this->responses as $response) { - $response->serialize($server, $dom); - } - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/SupportedLock.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/SupportedLock.php deleted file mode 100644 index 035c2f3301..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/SupportedLock.php +++ /dev/null @@ -1,78 +0,0 @@ -supportsLocks = $supportsLocks; - - } - - /** - * serialize - * - * @param DAV\Server $server - * @param \DOMElement $prop - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $prop) { - - $doc = $prop->ownerDocument; - - if (!$this->supportsLocks) return null; - - $lockEntry1 = $doc->createElement('d:lockentry'); - $lockEntry2 = $doc->createElement('d:lockentry'); - - $prop->appendChild($lockEntry1); - $prop->appendChild($lockEntry2); - - $lockScope1 = $doc->createElement('d:lockscope'); - $lockScope2 = $doc->createElement('d:lockscope'); - $lockType1 = $doc->createElement('d:locktype'); - $lockType2 = $doc->createElement('d:locktype'); - - $lockEntry1->appendChild($lockScope1); - $lockEntry1->appendChild($lockType1); - $lockEntry2->appendChild($lockScope2); - $lockEntry2->appendChild($lockType2); - - $lockScope1->appendChild($doc->createElement('d:exclusive')); - $lockScope2->appendChild($doc->createElement('d:shared')); - - $lockType1->appendChild($doc->createElement('d:write')); - $lockType2->appendChild($doc->createElement('d:write')); - - //$frag->appendXML(''); - //$frag->appendXML(''); - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/SupportedReportSet.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/SupportedReportSet.php deleted file mode 100644 index a8a90bb189..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Property/SupportedReportSet.php +++ /dev/null @@ -1,111 +0,0 @@ -addReport($reports); - - } - - /** - * Adds a report to this property - * - * The report must be a string in clark-notation. - * Multiple reports can be specified as an array. - * - * @param mixed $report - * @return void - */ - public function addReport($report) { - - if (!is_array($report)) $report = array($report); - - foreach($report as $r) { - - if (!preg_match('/^{([^}]*)}(.*)$/',$r)) - throw new DAV\Exception('Reportname must be in clark-notation'); - - $this->reports[] = $r; - - } - - } - - /** - * Returns the list of supported reports - * - * @return array - */ - public function getValue() { - - return $this->reports; - - } - - /** - * Serializes the node - * - * @param DAV\Server $server - * @param \DOMElement $prop - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $prop) { - - foreach($this->reports as $reportName) { - - $supportedReport = $prop->ownerDocument->createElement('d:supported-report'); - $prop->appendChild($supportedReport); - - $report = $prop->ownerDocument->createElement('d:report'); - $supportedReport->appendChild($report); - - preg_match('/^{([^}]*)}(.*)$/',$reportName,$matches); - - list(, $namespace, $element) = $matches; - - $prefix = isset($server->xmlNamespaces[$namespace])?$server->xmlNamespaces[$namespace]:null; - - if ($prefix) { - $report->appendChild($prop->ownerDocument->createElement($prefix . ':' . $element)); - } else { - $report->appendChild($prop->ownerDocument->createElementNS($namespace, 'x:' . $element)); - } - - } - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/PropertyInterface.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/PropertyInterface.php deleted file mode 100644 index 2fb0d7db6b..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/PropertyInterface.php +++ /dev/null @@ -1,21 +0,0 @@ - 'd', - 'http://sabredav.org/ns' => 's', - ); - - /** - * The propertymap can be used to map properties from - * requests to property classes. - * - * @var array - */ - public $propertyMap = array( - '{DAV:}resourcetype' => 'Sabre\\DAV\\Property\\ResourceType', - ); - - public $protectedProperties = array( - // RFC4918 - '{DAV:}getcontentlength', - '{DAV:}getetag', - '{DAV:}getlastmodified', - '{DAV:}lockdiscovery', - '{DAV:}supportedlock', - - // RFC4331 - '{DAV:}quota-available-bytes', - '{DAV:}quota-used-bytes', - - // RFC3744 - '{DAV:}supported-privilege-set', - '{DAV:}current-user-privilege-set', - '{DAV:}acl', - '{DAV:}acl-restrictions', - '{DAV:}inherited-acl-set', - - ); - - /** - * This is a flag that allow or not showing file, line and code - * of the exception in the returned XML - * - * @var bool - */ - public $debugExceptions = false; - - /** - * This property allows you to automatically add the 'resourcetype' value - * based on a node's classname or interface. - * - * The preset ensures that {DAV:}collection is automaticlly added for nodes - * implementing Sabre\DAV\ICollection. - * - * @var array - */ - public $resourceTypeMapping = array( - 'Sabre\\DAV\\ICollection' => '{DAV:}collection', - ); - - /** - * If this setting is turned off, SabreDAV's version number will be hidden - * from various places. - * - * Some people feel this is a good security measure. - * - * @var bool - */ - static public $exposeVersion = true; - - /** - * Sets up the server - * - * If a Sabre\DAV\Tree object is passed as an argument, it will - * use it as the directory tree. If a Sabre\DAV\INode is passed, it - * will create a Sabre\DAV\ObjectTree and use the node as the root. - * - * If nothing is passed, a Sabre\DAV\SimpleCollection is created in - * a Sabre\DAV\ObjectTree. - * - * If an array is passed, we automatically create a root node, and use - * the nodes in the array as top-level children. - * - * @param Tree|INode|array|null $treeOrNode The tree object - */ - public function __construct($treeOrNode = null) { - - if ($treeOrNode instanceof Tree) { - $this->tree = $treeOrNode; - } elseif ($treeOrNode instanceof INode) { - $this->tree = new ObjectTree($treeOrNode); - } elseif (is_array($treeOrNode)) { - - // If it's an array, a list of nodes was passed, and we need to - // create the root node. - foreach($treeOrNode as $node) { - if (!($node instanceof INode)) { - throw new Exception('Invalid argument passed to constructor. If you\'re passing an array, all the values must implement Sabre\\DAV\\INode'); - } - } - - $root = new SimpleCollection('root', $treeOrNode); - $this->tree = new ObjectTree($root); - - } elseif (is_null($treeOrNode)) { - $root = new SimpleCollection('root'); - $this->tree = new ObjectTree($root); - } else { - throw new Exception('Invalid argument passed to constructor. Argument must either be an instance of Sabre\\DAV\\Tree, Sabre\\DAV\\INode, an array or null'); - } - $this->httpResponse = new HTTP\Response(); - $this->httpRequest = new HTTP\Request(); - - } - - /** - * Starts the DAV Server - * - * @return void - */ - public function exec() { - - try { - - // If nginx (pre-1.2) is used as a proxy server, and SabreDAV as an - // origin, we must make sure we send back HTTP/1.0 if this was - // requested. - // This is mainly because nginx doesn't support Chunked Transfer - // Encoding, and this forces the webserver SabreDAV is running on, - // to buffer entire responses to calculate Content-Length. - $this->httpResponse->defaultHttpVersion = $this->httpRequest->getHTTPVersion(); - - $this->invokeMethod($this->httpRequest->getMethod(), $this->getRequestUri()); - - } catch (Exception $e) { - - try { - $this->broadcastEvent('exception', array($e)); - } catch (Exception $ignore) { - } - $DOM = new \DOMDocument('1.0','utf-8'); - $DOM->formatOutput = true; - - $error = $DOM->createElementNS('DAV:','d:error'); - $error->setAttribute('xmlns:s',self::NS_SABREDAV); - $DOM->appendChild($error); - - $h = function($v) { - - return htmlspecialchars($v, ENT_NOQUOTES, 'UTF-8'); - - }; - - $error->appendChild($DOM->createElement('s:exception',$h(get_class($e)))); - $error->appendChild($DOM->createElement('s:message',$h($e->getMessage()))); - if ($this->debugExceptions) { - $error->appendChild($DOM->createElement('s:file',$h($e->getFile()))); - $error->appendChild($DOM->createElement('s:line',$h($e->getLine()))); - $error->appendChild($DOM->createElement('s:code',$h($e->getCode()))); - $error->appendChild($DOM->createElement('s:stacktrace',$h($e->getTraceAsString()))); - - } - if (self::$exposeVersion) { - $error->appendChild($DOM->createElement('s:sabredav-version',$h(Version::VERSION))); - } - - if($e instanceof Exception) { - - $httpCode = $e->getHTTPCode(); - $e->serialize($this,$error); - $headers = $e->getHTTPHeaders($this); - - } else { - - $httpCode = 500; - $headers = array(); - - } - $headers['Content-Type'] = 'application/xml; charset=utf-8'; - - $this->httpResponse->sendStatus($httpCode); - $this->httpResponse->setHeaders($headers); - $this->httpResponse->sendBody($DOM->saveXML()); - - } - - } - - /** - * Sets the base server uri - * - * @param string $uri - * @return void - */ - public function setBaseUri($uri) { - - // If the baseUri does not end with a slash, we must add it - if ($uri[strlen($uri)-1]!=='/') - $uri.='/'; - - $this->baseUri = $uri; - - } - - /** - * Returns the base responding uri - * - * @return string - */ - public function getBaseUri() { - - if (is_null($this->baseUri)) $this->baseUri = $this->guessBaseUri(); - return $this->baseUri; - - } - - /** - * This method attempts to detect the base uri. - * Only the PATH_INFO variable is considered. - * - * If this variable is not set, the root (/) is assumed. - * - * @return string - */ - public function guessBaseUri() { - - $pathInfo = $this->httpRequest->getRawServerValue('PATH_INFO'); - $uri = $this->httpRequest->getRawServerValue('REQUEST_URI'); - - // If PATH_INFO is found, we can assume it's accurate. - if (!empty($pathInfo)) { - - // We need to make sure we ignore the QUERY_STRING part - if ($pos = strpos($uri,'?')) - $uri = substr($uri,0,$pos); - - // PATH_INFO is only set for urls, such as: /example.php/path - // in that case PATH_INFO contains '/path'. - // Note that REQUEST_URI is percent encoded, while PATH_INFO is - // not, Therefore they are only comparable if we first decode - // REQUEST_INFO as well. - $decodedUri = URLUtil::decodePath($uri); - - // A simple sanity check: - if(substr($decodedUri,strlen($decodedUri)-strlen($pathInfo))===$pathInfo) { - $baseUri = substr($decodedUri,0,strlen($decodedUri)-strlen($pathInfo)); - return rtrim($baseUri,'/') . '/'; - } - - throw new Exception('The REQUEST_URI ('. $uri . ') did not end with the contents of PATH_INFO (' . $pathInfo . '). This server might be misconfigured.'); - - } - - // The last fallback is that we're just going to assume the server root. - return '/'; - - } - - /** - * Adds a plugin to the server - * - * For more information, console the documentation of Sabre\DAV\ServerPlugin - * - * @param ServerPlugin $plugin - * @return void - */ - public function addPlugin(ServerPlugin $plugin) { - - $this->plugins[$plugin->getPluginName()] = $plugin; - $plugin->initialize($this); - - } - - /** - * Returns an initialized plugin by it's name. - * - * This function returns null if the plugin was not found. - * - * @param string $name - * @return ServerPlugin - */ - public function getPlugin($name) { - - if (isset($this->plugins[$name])) - return $this->plugins[$name]; - - // This is a fallback and deprecated. - foreach($this->plugins as $plugin) { - if (get_class($plugin)===$name) return $plugin; - } - - return null; - - } - - /** - * Returns all plugins - * - * @return array - */ - public function getPlugins() { - - return $this->plugins; - - } - - - /** - * Subscribe to an event. - * - * When the event is triggered, we'll call all the specified callbacks. - * It is possible to control the order of the callbacks through the - * priority argument. - * - * This is for example used to make sure that the authentication plugin - * is triggered before anything else. If it's not needed to change this - * number, it is recommended to ommit. - * - * @param string $event - * @param callback $callback - * @param int $priority - * @return void - */ - public function subscribeEvent($event, $callback, $priority = 100) { - - if (!isset($this->eventSubscriptions[$event])) { - $this->eventSubscriptions[$event] = array(); - } - while(isset($this->eventSubscriptions[$event][$priority])) $priority++; - $this->eventSubscriptions[$event][$priority] = $callback; - ksort($this->eventSubscriptions[$event]); - - } - - /** - * Broadcasts an event - * - * This method will call all subscribers. If one of the subscribers returns false, the process stops. - * - * The arguments parameter will be sent to all subscribers - * - * @param string $eventName - * @param array $arguments - * @return bool - */ - public function broadcastEvent($eventName,$arguments = array()) { - - if (isset($this->eventSubscriptions[$eventName])) { - - foreach($this->eventSubscriptions[$eventName] as $subscriber) { - - $result = call_user_func_array($subscriber,$arguments); - if ($result===false) return false; - - } - - } - - return true; - - } - - /** - * Handles a http request, and execute a method based on its name - * - * @param string $method - * @param string $uri - * @return void - */ - public function invokeMethod($method, $uri) { - - $method = strtoupper($method); - - if (!$this->broadcastEvent('beforeMethod',array($method, $uri))) return; - - // Make sure this is a HTTP method we support - $internalMethods = array( - 'OPTIONS', - 'GET', - 'HEAD', - 'DELETE', - 'PROPFIND', - 'MKCOL', - 'PUT', - 'PROPPATCH', - 'COPY', - 'MOVE', - 'REPORT' - ); - - if (in_array($method,$internalMethods)) { - - call_user_func(array($this,'http' . $method), $uri); - - } else { - - if ($this->broadcastEvent('unknownMethod',array($method, $uri))) { - // Unsupported method - throw new Exception\NotImplemented('There was no handler found for this "' . $method . '" method'); - } - - } - - } - - // {{{ HTTP Method implementations - - /** - * HTTP OPTIONS - * - * @param string $uri - * @return void - */ - protected function httpOptions($uri) { - - $methods = $this->getAllowedMethods($uri); - - $this->httpResponse->setHeader('Allow',strtoupper(implode(', ',$methods))); - $features = array('1','3', 'extended-mkcol'); - - foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures()); - - $this->httpResponse->setHeader('DAV',implode(', ',$features)); - $this->httpResponse->setHeader('MS-Author-Via','DAV'); - $this->httpResponse->setHeader('Accept-Ranges','bytes'); - if (self::$exposeVersion) { - $this->httpResponse->setHeader('X-Sabre-Version',Version::VERSION); - } - $this->httpResponse->setHeader('Content-Length',0); - $this->httpResponse->sendStatus(200); - - } - - /** - * HTTP GET - * - * This method simply fetches the contents of a uri, like normal - * - * @param string $uri - * @return bool - */ - protected function httpGet($uri) { - - $node = $this->tree->getNodeForPath($uri,0); - - if (!$this->checkPreconditions(true)) return false; - if (!$node instanceof IFile) throw new Exception\NotImplemented('GET is only implemented on File objects'); - - $body = $node->get(); - - // Converting string into stream, if needed. - if (is_string($body)) { - $stream = fopen('php://temp','r+'); - fwrite($stream,$body); - rewind($stream); - $body = $stream; - } - - /* - * TODO: getetag, getlastmodified, getsize should also be used using - * this method - */ - $httpHeaders = $this->getHTTPHeaders($uri); - - /* ContentType needs to get a default, because many webservers will otherwise - * default to text/html, and we don't want this for security reasons. - */ - if (!isset($httpHeaders['Content-Type'])) { - $httpHeaders['Content-Type'] = 'application/octet-stream'; - } - - - if (isset($httpHeaders['Content-Length'])) { - - $nodeSize = $httpHeaders['Content-Length']; - - // Need to unset Content-Length, because we'll handle that during figuring out the range - unset($httpHeaders['Content-Length']); - - } else { - $nodeSize = null; - } - - $this->httpResponse->setHeaders($httpHeaders); - - $range = $this->getHTTPRange(); - $ifRange = $this->httpRequest->getHeader('If-Range'); - $ignoreRangeHeader = false; - - // If ifRange is set, and range is specified, we first need to check - // the precondition. - if ($nodeSize && $range && $ifRange) { - - // if IfRange is parsable as a date we'll treat it as a DateTime - // otherwise, we must treat it as an etag. - try { - $ifRangeDate = new \DateTime($ifRange); - - // It's a date. We must check if the entity is modified since - // the specified date. - if (!isset($httpHeaders['Last-Modified'])) $ignoreRangeHeader = true; - else { - $modified = new \DateTime($httpHeaders['Last-Modified']); - if($modified > $ifRangeDate) $ignoreRangeHeader = true; - } - - } catch (\Exception $e) { - - // It's an entity. We can do a simple comparison. - if (!isset($httpHeaders['ETag'])) $ignoreRangeHeader = true; - elseif ($httpHeaders['ETag']!==$ifRange) $ignoreRangeHeader = true; - } - } - - // We're only going to support HTTP ranges if the backend provided a filesize - if (!$ignoreRangeHeader && $nodeSize && $range) { - - // Determining the exact byte offsets - if (!is_null($range[0])) { - - $start = $range[0]; - $end = $range[1]?$range[1]:$nodeSize-1; - if($start >= $nodeSize) - throw new Exception\RequestedRangeNotSatisfiable('The start offset (' . $range[0] . ') exceeded the size of the entity (' . $nodeSize . ')'); - - if($end < $start) throw new Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')'); - if($end >= $nodeSize) $end = $nodeSize-1; - - } else { - - $start = $nodeSize-$range[1]; - $end = $nodeSize-1; - - if ($start<0) $start = 0; - - } - - // New read/write stream - $newStream = fopen('php://temp','r+'); - - // stream_copy_to_stream() has a bug/feature: the `whence` argument - // is interpreted as SEEK_SET (count from absolute offset 0), while - // for a stream it should be SEEK_CUR (count from current offset). - // If a stream is nonseekable, the function fails. So we *emulate* - // the correct behaviour with fseek(): - if ($start > 0) { - if (($curOffs = ftell($body)) === false) $curOffs = 0; - fseek($body, $start - $curOffs, SEEK_CUR); - } - stream_copy_to_stream($body, $newStream, $end-$start+1); - rewind($newStream); - - $this->httpResponse->setHeader('Content-Length', $end-$start+1); - $this->httpResponse->setHeader('Content-Range','bytes ' . $start . '-' . $end . '/' . $nodeSize); - $this->httpResponse->sendStatus(206); - $this->httpResponse->sendBody($newStream); - - - } else { - - if ($nodeSize) $this->httpResponse->setHeader('Content-Length',$nodeSize); - $this->httpResponse->sendStatus(200); - $this->httpResponse->sendBody($body); - - } - - } - - /** - * HTTP HEAD - * - * This method is normally used to take a peak at a url, and only get the HTTP response headers, without the body - * This is used by clients to determine if a remote file was changed, so they can use a local cached version, instead of downloading it again - * - * @param string $uri - * @return void - */ - protected function httpHead($uri) { - - $node = $this->tree->getNodeForPath($uri); - /* This information is only collection for File objects. - * Ideally we want to throw 405 Method Not Allowed for every - * non-file, but MS Office does not like this - */ - if ($node instanceof IFile) { - $headers = $this->getHTTPHeaders($this->getRequestUri()); - if (!isset($headers['Content-Type'])) { - $headers['Content-Type'] = 'application/octet-stream'; - } - $this->httpResponse->setHeaders($headers); - } - $this->httpResponse->sendStatus(200); - - } - - /** - * HTTP Delete - * - * The HTTP delete method, deletes a given uri - * - * @param string $uri - * @return void - */ - protected function httpDelete($uri) { - - // Checking If-None-Match and related headers. - if (!$this->checkPreconditions()) return; - - if (!$this->broadcastEvent('beforeUnbind',array($uri))) return; - $this->tree->delete($uri); - $this->broadcastEvent('afterUnbind',array($uri)); - - $this->httpResponse->sendStatus(204); - $this->httpResponse->setHeader('Content-Length','0'); - - } - - - /** - * WebDAV PROPFIND - * - * This WebDAV method requests information about an uri resource, or a list of resources - * If a client wants to receive the properties for a single resource it will add an HTTP Depth: header with a 0 value - * If the value is 1, it means that it also expects a list of sub-resources (e.g.: files in a directory) - * - * The request body contains an XML data structure that has a list of properties the client understands - * The response body is also an xml document, containing information about every uri resource and the requested properties - * - * It has to return a HTTP 207 Multi-status status code - * - * @param string $uri - * @return void - */ - protected function httpPropfind($uri) { - - $requestedProperties = $this->parsePropFindRequest($this->httpRequest->getBody(true)); - - $depth = $this->getHTTPDepth(1); - // The only two options for the depth of a propfind is 0 or 1 - if ($depth!=0) $depth = 1; - - $newProperties = $this->getPropertiesForPath($uri,$requestedProperties,$depth); - - // This is a multi-status response - $this->httpResponse->sendStatus(207); - $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->httpResponse->setHeader('Vary','Brief,Prefer'); - - // Normally this header is only needed for OPTIONS responses, however.. - // iCal seems to also depend on these being set for PROPFIND. Since - // this is not harmful, we'll add it. - $features = array('1','3', 'extended-mkcol'); - foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures()); - $this->httpResponse->setHeader('DAV',implode(', ',$features)); - - $prefer = $this->getHTTPPrefer(); - $minimal = $prefer['return-minimal']; - - $data = $this->generateMultiStatus($newProperties, $minimal); - $this->httpResponse->sendBody($data); - - } - - /** - * WebDAV PROPPATCH - * - * This method is called to update properties on a Node. The request is an XML body with all the mutations. - * In this XML body it is specified which properties should be set/updated and/or deleted - * - * @param string $uri - * @return void - */ - protected function httpPropPatch($uri) { - - $newProperties = $this->parsePropPatchRequest($this->httpRequest->getBody(true)); - - $result = $this->updateProperties($uri, $newProperties); - - $prefer = $this->getHTTPPrefer(); - $this->httpResponse->setHeader('Vary','Brief,Prefer'); - - if ($prefer['return-minimal']) { - - // If return-minimal is specified, we only have to check if the - // request was succesful, and don't need to return the - // multi-status. - $ok = true; - foreach($result as $code=>$prop) { - if ((int)$code > 299) { - $ok = false; - } - } - - if ($ok) { - - $this->httpResponse->sendStatus(204); - return; - - } - - } - - $this->httpResponse->sendStatus(207); - $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - - $this->httpResponse->sendBody( - $this->generateMultiStatus(array($result)) - ); - - } - - /** - * HTTP PUT method - * - * This HTTP method updates a file, or creates a new one. - * - * If a new resource was created, a 201 Created status code should be returned. If an existing resource is updated, it's a 204 No Content - * - * @param string $uri - * @return bool - */ - protected function httpPut($uri) { - - $body = $this->httpRequest->getBody(); - - // Intercepting Content-Range - if ($this->httpRequest->getHeader('Content-Range')) { - /** - Content-Range is dangerous for PUT requests: PUT per definition - stores a full resource. draft-ietf-httpbis-p2-semantics-15 says - in section 7.6: - An origin server SHOULD reject any PUT request that contains a - Content-Range header field, since it might be misinterpreted as - partial content (or might be partial content that is being mistakenly - PUT as a full representation). Partial content updates are possible - by targeting a separately identified resource with state that - overlaps a portion of the larger resource, or by using a different - method that has been specifically defined for partial updates (for - example, the PATCH method defined in [RFC5789]). - This clarifies RFC2616 section 9.6: - The recipient of the entity MUST NOT ignore any Content-* - (e.g. Content-Range) headers that it does not understand or implement - and MUST return a 501 (Not Implemented) response in such cases. - OTOH is a PUT request with a Content-Range currently the only way to - continue an aborted upload request and is supported by curl, mod_dav, - Tomcat and others. Since some clients do use this feature which results - in unexpected behaviour (cf PEAR::HTTP_WebDAV_Client 1.0.1), we reject - all PUT requests with a Content-Range for now. - */ - - throw new Exception\NotImplemented('PUT with Content-Range is not allowed.'); - } - - // Intercepting the Finder problem - if (($expected = $this->httpRequest->getHeader('X-Expected-Entity-Length')) && $expected > 0) { - - /** - Many webservers will not cooperate well with Finder PUT requests, - because it uses 'Chunked' transfer encoding for the request body. - - The symptom of this problem is that Finder sends files to the - server, but they arrive as 0-length files in PHP. - - If we don't do anything, the user might think they are uploading - files successfully, but they end up empty on the server. Instead, - we throw back an error if we detect this. - - The reason Finder uses Chunked, is because it thinks the files - might change as it's being uploaded, and therefore the - Content-Length can vary. - - Instead it sends the X-Expected-Entity-Length header with the size - of the file at the very start of the request. If this header is set, - but we don't get a request body we will fail the request to - protect the end-user. - */ - - // Only reading first byte - $firstByte = fread($body,1); - if (strlen($firstByte)!==1) { - throw new Exception\Forbidden('This server is not compatible with OS/X finder. Consider using a different WebDAV client or webserver.'); - } - - // The body needs to stay intact, so we copy everything to a - // temporary stream. - - $newBody = fopen('php://temp','r+'); - fwrite($newBody,$firstByte); - stream_copy_to_stream($body, $newBody); - rewind($newBody); - - $body = $newBody; - - } - - // Checking If-None-Match and related headers. - if (!$this->checkPreconditions()) return; - - if ($this->tree->nodeExists($uri)) { - - $node = $this->tree->getNodeForPath($uri); - - // If the node is a collection, we'll deny it - if (!($node instanceof IFile)) throw new Exception\Conflict('PUT is not allowed on non-files.'); - if (!$this->broadcastEvent('beforeWriteContent',array($uri, $node, &$body))) return false; - - $etag = $node->put($body); - - $this->broadcastEvent('afterWriteContent',array($uri, $node)); - - $this->httpResponse->setHeader('Content-Length','0'); - if ($etag) $this->httpResponse->setHeader('ETag',$etag); - $this->httpResponse->sendStatus(204); - - } else { - - $etag = null; - // If we got here, the resource didn't exist yet. - if (!$this->createFile($this->getRequestUri(),$body,$etag)) { - // For one reason or another the file was not created. - return; - } - - $this->httpResponse->setHeader('Content-Length','0'); - if ($etag) $this->httpResponse->setHeader('ETag', $etag); - $this->httpResponse->sendStatus(201); - - } - - } - - - /** - * WebDAV MKCOL - * - * The MKCOL method is used to create a new collection (directory) on the server - * - * @param string $uri - * @return void - */ - protected function httpMkcol($uri) { - - $requestBody = $this->httpRequest->getBody(true); - - if ($requestBody) { - - $contentType = $this->httpRequest->getHeader('Content-Type'); - if (strpos($contentType,'application/xml')!==0 && strpos($contentType,'text/xml')!==0) { - - // We must throw 415 for unsupported mkcol bodies - throw new Exception\UnsupportedMediaType('The request body for the MKCOL request must have an xml Content-Type'); - - } - - $dom = XMLUtil::loadDOMDocument($requestBody); - if (XMLUtil::toClarkNotation($dom->firstChild)!=='{DAV:}mkcol') { - - // We must throw 415 for unsupported mkcol bodies - throw new Exception\UnsupportedMediaType('The request body for the MKCOL request must be a {DAV:}mkcol request construct.'); - - } - - $properties = array(); - foreach($dom->firstChild->childNodes as $childNode) { - - if (XMLUtil::toClarkNotation($childNode)!=='{DAV:}set') continue; - $properties = array_merge($properties, XMLUtil::parseProperties($childNode, $this->propertyMap)); - - } - if (!isset($properties['{DAV:}resourcetype'])) - throw new Exception\BadRequest('The mkcol request must include a {DAV:}resourcetype property'); - - $resourceType = $properties['{DAV:}resourcetype']->getValue(); - unset($properties['{DAV:}resourcetype']); - - } else { - - $properties = array(); - $resourceType = array('{DAV:}collection'); - - } - - $result = $this->createCollection($uri, $resourceType, $properties); - - if (is_array($result)) { - $this->httpResponse->sendStatus(207); - $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - - $this->httpResponse->sendBody( - $this->generateMultiStatus(array($result)) - ); - - } else { - $this->httpResponse->setHeader('Content-Length','0'); - $this->httpResponse->sendStatus(201); - } - - } - - /** - * WebDAV HTTP MOVE method - * - * This method moves one uri to a different uri. A lot of the actual request processing is done in getCopyMoveInfo - * - * @param string $uri - * @return bool - */ - protected function httpMove($uri) { - - $moveInfo = $this->getCopyAndMoveInfo(); - - // If the destination is part of the source tree, we must fail - if ($moveInfo['destination']==$uri) - throw new Exception\Forbidden('Source and destination uri are identical.'); - - if ($moveInfo['destinationExists']) { - - if (!$this->broadcastEvent('beforeUnbind',array($moveInfo['destination']))) return false; - $this->tree->delete($moveInfo['destination']); - $this->broadcastEvent('afterUnbind',array($moveInfo['destination'])); - - } - - if (!$this->broadcastEvent('beforeUnbind',array($uri))) return false; - if (!$this->broadcastEvent('beforeBind',array($moveInfo['destination']))) return false; - $this->tree->move($uri,$moveInfo['destination']); - $this->broadcastEvent('afterUnbind',array($uri)); - $this->broadcastEvent('afterBind',array($moveInfo['destination'])); - - // If a resource was overwritten we should send a 204, otherwise a 201 - $this->httpResponse->setHeader('Content-Length','0'); - $this->httpResponse->sendStatus($moveInfo['destinationExists']?204:201); - - } - - /** - * WebDAV HTTP COPY method - * - * This method copies one uri to a different uri, and works much like the MOVE request - * A lot of the actual request processing is done in getCopyMoveInfo - * - * @param string $uri - * @return bool - */ - protected function httpCopy($uri) { - - $copyInfo = $this->getCopyAndMoveInfo(); - // If the destination is part of the source tree, we must fail - if ($copyInfo['destination']==$uri) - throw new Exception\Forbidden('Source and destination uri are identical.'); - - if ($copyInfo['destinationExists']) { - if (!$this->broadcastEvent('beforeUnbind',array($copyInfo['destination']))) return false; - $this->tree->delete($copyInfo['destination']); - - } - if (!$this->broadcastEvent('beforeBind',array($copyInfo['destination']))) return false; - $this->tree->copy($uri,$copyInfo['destination']); - $this->broadcastEvent('afterBind',array($copyInfo['destination'])); - - // If a resource was overwritten we should send a 204, otherwise a 201 - $this->httpResponse->setHeader('Content-Length','0'); - $this->httpResponse->sendStatus($copyInfo['destinationExists']?204:201); - - } - - - - /** - * HTTP REPORT method implementation - * - * Although the REPORT method is not part of the standard WebDAV spec (it's from rfc3253) - * It's used in a lot of extensions, so it made sense to implement it into the core. - * - * @param string $uri - * @return void - */ - protected function httpReport($uri) { - - $body = $this->httpRequest->getBody(true); - $dom = XMLUtil::loadDOMDocument($body); - - $reportName = XMLUtil::toClarkNotation($dom->firstChild); - - if ($this->broadcastEvent('report',array($reportName,$dom, $uri))) { - - // If broadcastEvent returned true, it means the report was not supported - throw new Exception\ReportNotSupported(); - - } - - } - - // }}} - // {{{ HTTP/WebDAV protocol helpers - - /** - * Returns an array with all the supported HTTP methods for a specific uri. - * - * @param string $uri - * @return array - */ - public function getAllowedMethods($uri) { - - $methods = array( - 'OPTIONS', - 'GET', - 'HEAD', - 'DELETE', - 'PROPFIND', - 'PUT', - 'PROPPATCH', - 'COPY', - 'MOVE', - 'REPORT' - ); - - // The MKCOL is only allowed on an unmapped uri - try { - $this->tree->getNodeForPath($uri); - } catch (Exception\NotFound $e) { - $methods[] = 'MKCOL'; - } - - // We're also checking if any of the plugins register any new methods - foreach($this->plugins as $plugin) $methods = array_merge($methods, $plugin->getHTTPMethods($uri)); - array_unique($methods); - - return $methods; - - } - - /** - * Gets the uri for the request, keeping the base uri into consideration - * - * @return string - */ - public function getRequestUri() { - - return $this->calculateUri($this->httpRequest->getUri()); - - } - - /** - * Calculates the uri for a request, making sure that the base uri is stripped out - * - * @param string $uri - * @throws Exception\Forbidden A permission denied exception is thrown whenever there was an attempt to supply a uri outside of the base uri - * @return string - */ - public function calculateUri($uri) { - - if ($uri[0]!='/' && strpos($uri,'://')) { - - $uri = parse_url($uri,PHP_URL_PATH); - - } - - $uri = str_replace('//','/',$uri); - - if (strpos($uri,$this->getBaseUri())===0) { - - return trim(URLUtil::decodePath(substr($uri,strlen($this->getBaseUri()))),'/'); - - // A special case, if the baseUri was accessed without a trailing - // slash, we'll accept it as well. - } elseif ($uri.'/' === $this->getBaseUri()) { - - return ''; - - } else { - - throw new Exception\Forbidden('Requested uri (' . $uri . ') is out of base uri (' . $this->getBaseUri() . ')'); - - } - - } - - /** - * Returns the HTTP depth header - * - * This method returns the contents of the HTTP depth request header. If the depth header was 'infinity' it will return the Sabre\DAV\Server::DEPTH_INFINITY object - * It is possible to supply a default depth value, which is used when the depth header has invalid content, or is completely non-existent - * - * @param mixed $default - * @return int - */ - public function getHTTPDepth($default = self::DEPTH_INFINITY) { - - // If its not set, we'll grab the default - $depth = $this->httpRequest->getHeader('Depth'); - - if (is_null($depth)) return $default; - - if ($depth == 'infinity') return self::DEPTH_INFINITY; - - - // If its an unknown value. we'll grab the default - if (!ctype_digit($depth)) return $default; - - return (int)$depth; - - } - - /** - * Returns the HTTP range header - * - * This method returns null if there is no well-formed HTTP range request - * header or array($start, $end). - * - * The first number is the offset of the first byte in the range. - * The second number is the offset of the last byte in the range. - * - * If the second offset is null, it should be treated as the offset of the last byte of the entity - * If the first offset is null, the second offset should be used to retrieve the last x bytes of the entity - * - * @return array|null - */ - public function getHTTPRange() { - - $range = $this->httpRequest->getHeader('range'); - if (is_null($range)) return null; - - // Matching "Range: bytes=1234-5678: both numbers are optional - - if (!preg_match('/^bytes=([0-9]*)-([0-9]*)$/i',$range,$matches)) return null; - - if ($matches[1]==='' && $matches[2]==='') return null; - - return array( - $matches[1]!==''?$matches[1]:null, - $matches[2]!==''?$matches[2]:null, - ); - - } - - /** - * Returns the HTTP Prefer header information. - * - * The prefer header is defined in: - * http://tools.ietf.org/html/draft-snell-http-prefer-14 - * - * This method will return an array with options. - * - * Currently, the following options may be returned: - * array( - * 'return-asynch' => true, - * 'return-minimal' => true, - * 'return-representation' => true, - * 'wait' => 30, - * 'strict' => true, - * 'lenient' => true, - * ) - * - * This method also supports the Brief header, and will also return - * 'return-minimal' if the brief header was set to 't'. - * - * For the boolean options, false will be returned if the headers are not - * specified. For the integer options it will be 'null'. - * - * @return array - */ - public function getHTTPPrefer() { - - $result = array( - 'return-asynch' => false, - 'return-minimal' => false, - 'return-representation' => false, - 'wait' => null, - 'strict' => false, - 'lenient' => false, - ); - - if ($prefer = $this->httpRequest->getHeader('Prefer')) { - - $parameters = array_map('trim', - explode(',', $prefer) - ); - - foreach($parameters as $parameter) { - - // Right now our regex only supports the tokens actually - // specified in the draft. We may need to expand this if new - // tokens get registered. - if(!preg_match('/^(?P[a-z0-9-]+)(?:=(?P[0-9]+))?$/', $parameter, $matches)) { - continue; - } - - switch($matches['token']) { - - case 'return-asynch' : - case 'return-minimal' : - case 'return-representation' : - case 'strict' : - case 'lenient' : - $result[$matches['token']] = true; - break; - case 'wait' : - $result[$matches['token']] = $matches['value']; - break; - - } - - } - - } - - if ($this->httpRequest->getHeader('Brief')=='t') { - $result['return-minimal'] = true; - } - - return $result; - - } - - - /** - * Returns information about Copy and Move requests - * - * This function is created to help getting information about the source and the destination for the - * WebDAV MOVE and COPY HTTP request. It also validates a lot of information and throws proper exceptions - * - * The returned value is an array with the following keys: - * * destination - Destination path - * * destinationExists - Whether or not the destination is an existing url (and should therefore be overwritten) - * - * @return array - */ - public function getCopyAndMoveInfo() { - - // Collecting the relevant HTTP headers - if (!$this->httpRequest->getHeader('Destination')) throw new Exception\BadRequest('The destination header was not supplied'); - $destination = $this->calculateUri($this->httpRequest->getHeader('Destination')); - $overwrite = $this->httpRequest->getHeader('Overwrite'); - if (!$overwrite) $overwrite = 'T'; - if (strtoupper($overwrite)=='T') $overwrite = true; - elseif (strtoupper($overwrite)=='F') $overwrite = false; - // We need to throw a bad request exception, if the header was invalid - else throw new Exception\BadRequest('The HTTP Overwrite header should be either T or F'); - - list($destinationDir) = URLUtil::splitPath($destination); - - try { - $destinationParent = $this->tree->getNodeForPath($destinationDir); - if (!($destinationParent instanceof ICollection)) throw new Exception\UnsupportedMediaType('The destination node is not a collection'); - } catch (Exception\NotFound $e) { - - // If the destination parent node is not found, we throw a 409 - throw new Exception\Conflict('The destination node is not found'); - } - - try { - - $destinationNode = $this->tree->getNodeForPath($destination); - - // If this succeeded, it means the destination already exists - // we'll need to throw precondition failed in case overwrite is false - if (!$overwrite) throw new Exception\PreconditionFailed('The destination node already exists, and the overwrite header is set to false','Overwrite'); - - } catch (Exception\NotFound $e) { - - // Destination didn't exist, we're all good - $destinationNode = false; - - - - } - - // These are the three relevant properties we need to return - return array( - 'destination' => $destination, - 'destinationExists' => $destinationNode==true, - 'destinationNode' => $destinationNode, - ); - - } - - /** - * Returns a list of properties for a path - * - * This is a simplified version getPropertiesForPath. - * if you aren't interested in status codes, but you just - * want to have a flat list of properties. Use this method. - * - * @param string $path - * @param array $propertyNames - */ - public function getProperties($path, $propertyNames) { - - $result = $this->getPropertiesForPath($path,$propertyNames,0); - return $result[0][200]; - - } - - /** - * A kid-friendly way to fetch properties for a node's children. - * - * The returned array will be indexed by the path of the of child node. - * Only properties that are actually found will be returned. - * - * The parent node will not be returned. - * - * @param string $path - * @param array $propertyNames - * @return array - */ - public function getPropertiesForChildren($path, $propertyNames) { - - $result = array(); - foreach($this->getPropertiesForPath($path,$propertyNames,1) as $k=>$row) { - - // Skipping the parent path - if ($k === 0) continue; - - $result[$row['href']] = $row[200]; - - } - return $result; - - } - - /** - * Returns a list of HTTP headers for a particular resource - * - * The generated http headers are based on properties provided by the - * resource. The method basically provides a simple mapping between - * DAV property and HTTP header. - * - * The headers are intended to be used for HEAD and GET requests. - * - * @param string $path - * @return array - */ - public function getHTTPHeaders($path) { - - $propertyMap = array( - '{DAV:}getcontenttype' => 'Content-Type', - '{DAV:}getcontentlength' => 'Content-Length', - '{DAV:}getlastmodified' => 'Last-Modified', - '{DAV:}getetag' => 'ETag', - ); - - $properties = $this->getProperties($path,array_keys($propertyMap)); - - $headers = array(); - foreach($propertyMap as $property=>$header) { - if (!isset($properties[$property])) continue; - - if (is_scalar($properties[$property])) { - $headers[$header] = $properties[$property]; - - // GetLastModified gets special cased - } elseif ($properties[$property] instanceof Property\GetLastModified) { - $headers[$header] = HTTP\Util::toHTTPDate($properties[$property]->getTime()); - } - - } - - return $headers; - - } - - /** - * Returns a list of properties for a given path - * - * The path that should be supplied should have the baseUrl stripped out - * The list of properties should be supplied in Clark notation. If the list is empty - * 'allprops' is assumed. - * - * If a depth of 1 is requested child elements will also be returned. - * - * @param string $path - * @param array $propertyNames - * @param int $depth - * @return array - */ - public function getPropertiesForPath($path, $propertyNames = array(), $depth = 0) { - - if ($depth!=0) $depth = 1; - - $path = rtrim($path,'/'); - - // This event allows people to intercept these requests early on in the - // process. - // - // We're not doing anything with the result, but this can be helpful to - // pre-fetch certain expensive live properties. - $this->broadCastEvent('beforeGetPropertiesForPath', array($path, $propertyNames, $depth)); - - $returnPropertyList = array(); - - $parentNode = $this->tree->getNodeForPath($path); - $nodes = array( - $path => $parentNode - ); - if ($depth==1 && $parentNode instanceof ICollection) { - foreach($this->tree->getChildren($path) as $childNode) - $nodes[$path . '/' . $childNode->getName()] = $childNode; - } - - // If the propertyNames array is empty, it means all properties are requested. - // We shouldn't actually return everything we know though, and only return a - // sensible list. - $allProperties = count($propertyNames)==0; - - foreach($nodes as $myPath=>$node) { - - $currentPropertyNames = $propertyNames; - - $newProperties = array( - '200' => array(), - '404' => array(), - ); - - if ($allProperties) { - // Default list of propertyNames, when all properties were requested. - $currentPropertyNames = array( - '{DAV:}getlastmodified', - '{DAV:}getcontentlength', - '{DAV:}resourcetype', - '{DAV:}quota-used-bytes', - '{DAV:}quota-available-bytes', - '{DAV:}getetag', - '{DAV:}getcontenttype', - ); - } - - // If the resourceType was not part of the list, we manually add it - // and mark it for removal. We need to know the resourcetype in order - // to make certain decisions about the entry. - // WebDAV dictates we should add a / and the end of href's for collections - $removeRT = false; - if (!in_array('{DAV:}resourcetype',$currentPropertyNames)) { - $currentPropertyNames[] = '{DAV:}resourcetype'; - $removeRT = true; - } - - $result = $this->broadcastEvent('beforeGetProperties',array($myPath, $node, &$currentPropertyNames, &$newProperties)); - // If this method explicitly returned false, we must ignore this - // node as it is inaccessible. - if ($result===false) continue; - - if (count($currentPropertyNames) > 0) { - - if ($node instanceof IProperties) { - $nodeProperties = $node->getProperties($currentPropertyNames); - - // The getProperties method may give us too much, - // properties, in case the implementor was lazy. - // - // So as we loop through this list, we will only take the - // properties that were actually requested and discard the - // rest. - foreach($currentPropertyNames as $k=>$currentPropertyName) { - if (isset($nodeProperties[$currentPropertyName])) { - unset($currentPropertyNames[$k]); - $newProperties[200][$currentPropertyName] = $nodeProperties[$currentPropertyName]; - } - } - - } - - } - - foreach($currentPropertyNames as $prop) { - - if (isset($newProperties[200][$prop])) continue; - - switch($prop) { - case '{DAV:}getlastmodified' : if ($node->getLastModified()) $newProperties[200][$prop] = new Property\GetLastModified($node->getLastModified()); break; - case '{DAV:}getcontentlength' : - if ($node instanceof IFile) { - $size = $node->getSize(); - if (!is_null($size)) { - $newProperties[200][$prop] = (int)$node->getSize(); - } - } - break; - case '{DAV:}quota-used-bytes' : - if ($node instanceof IQuota) { - $quotaInfo = $node->getQuotaInfo(); - $newProperties[200][$prop] = $quotaInfo[0]; - } - break; - case '{DAV:}quota-available-bytes' : - if ($node instanceof IQuota) { - $quotaInfo = $node->getQuotaInfo(); - $newProperties[200][$prop] = $quotaInfo[1]; - } - break; - case '{DAV:}getetag' : if ($node instanceof IFile && $etag = $node->getETag()) $newProperties[200][$prop] = $etag; break; - case '{DAV:}getcontenttype' : if ($node instanceof IFile && $ct = $node->getContentType()) $newProperties[200][$prop] = $ct; break; - case '{DAV:}supported-report-set' : - $reports = array(); - foreach($this->plugins as $plugin) { - $reports = array_merge($reports, $plugin->getSupportedReportSet($myPath)); - } - $newProperties[200][$prop] = new Property\SupportedReportSet($reports); - break; - case '{DAV:}resourcetype' : - $newProperties[200]['{DAV:}resourcetype'] = new Property\ResourceType(); - foreach($this->resourceTypeMapping as $className => $resourceType) { - if ($node instanceof $className) $newProperties[200]['{DAV:}resourcetype']->add($resourceType); - } - break; - - } - - // If we were unable to find the property, we will list it as 404. - if (!$allProperties && !isset($newProperties[200][$prop])) $newProperties[404][$prop] = null; - - } - - $this->broadcastEvent('afterGetProperties',array(trim($myPath,'/'),&$newProperties, $node)); - - $newProperties['href'] = trim($myPath,'/'); - - // Its is a WebDAV recommendation to add a trailing slash to collectionnames. - // Apple's iCal also requires a trailing slash for principals (rfc 3744), though this is non-standard. - if ($myPath!='' && isset($newProperties[200]['{DAV:}resourcetype'])) { - $rt = $newProperties[200]['{DAV:}resourcetype']; - if ($rt->is('{DAV:}collection') || $rt->is('{DAV:}principal')) { - $newProperties['href'] .='/'; - } - } - - // If the resourcetype property was manually added to the requested property list, - // we will remove it again. - if ($removeRT) unset($newProperties[200]['{DAV:}resourcetype']); - - $returnPropertyList[] = $newProperties; - - } - - return $returnPropertyList; - - } - - /** - * This method is invoked by sub-systems creating a new file. - * - * Currently this is done by HTTP PUT and HTTP LOCK (in the Locks_Plugin). - * It was important to get this done through a centralized function, - * allowing plugins to intercept this using the beforeCreateFile event. - * - * This method will return true if the file was actually created - * - * @param string $uri - * @param resource $data - * @param string $etag - * @return bool - */ - public function createFile($uri,$data, &$etag = null) { - - list($dir,$name) = URLUtil::splitPath($uri); - - if (!$this->broadcastEvent('beforeBind',array($uri))) return false; - - $parent = $this->tree->getNodeForPath($dir); - if (!$parent instanceof ICollection) { - throw new Exception\Conflict('Files can only be created as children of collections'); - } - - if (!$this->broadcastEvent('beforeCreateFile',array($uri, &$data, $parent))) return false; - - $etag = $parent->createFile($name,$data); - $this->tree->markDirty($dir . '/' . $name); - - $this->broadcastEvent('afterBind',array($uri)); - $this->broadcastEvent('afterCreateFile',array($uri, $parent)); - - return true; - } - - /** - * This method is invoked by sub-systems creating a new directory. - * - * @param string $uri - * @return void - */ - public function createDirectory($uri) { - - $this->createCollection($uri,array('{DAV:}collection'),array()); - - } - - /** - * Use this method to create a new collection - * - * The {DAV:}resourcetype is specified using the resourceType array. - * At the very least it must contain {DAV:}collection. - * - * The properties array can contain a list of additional properties. - * - * @param string $uri The new uri - * @param array $resourceType The resourceType(s) - * @param array $properties A list of properties - * @return array|null - */ - public function createCollection($uri, array $resourceType, array $properties) { - - list($parentUri,$newName) = URLUtil::splitPath($uri); - - // Making sure {DAV:}collection was specified as resourceType - if (!in_array('{DAV:}collection', $resourceType)) { - throw new Exception\InvalidResourceType('The resourceType for this collection must at least include {DAV:}collection'); - } - - - // Making sure the parent exists - try { - - $parent = $this->tree->getNodeForPath($parentUri); - - } catch (Exception\NotFound $e) { - - throw new Exception\Conflict('Parent node does not exist'); - - } - - // Making sure the parent is a collection - if (!$parent instanceof ICollection) { - throw new Exception\Conflict('Parent node is not a collection'); - } - - - - // Making sure the child does not already exist - try { - $parent->getChild($newName); - - // If we got here.. it means there's already a node on that url, and we need to throw a 405 - throw new Exception\MethodNotAllowed('The resource you tried to create already exists'); - - } catch (Exception\NotFound $e) { - // This is correct - } - - - if (!$this->broadcastEvent('beforeBind',array($uri))) return; - - // There are 2 modes of operation. The standard collection - // creates the directory, and then updates properties - // the extended collection can create it directly. - if ($parent instanceof IExtendedCollection) { - - $parent->createExtendedCollection($newName, $resourceType, $properties); - - } else { - - // No special resourcetypes are supported - if (count($resourceType)>1) { - throw new Exception\InvalidResourceType('The {DAV:}resourcetype you specified is not supported here.'); - } - - $parent->createDirectory($newName); - $rollBack = false; - $exception = null; - $errorResult = null; - - if (count($properties)>0) { - - try { - - $errorResult = $this->updateProperties($uri, $properties); - if (!isset($errorResult[200])) { - $rollBack = true; - } - - } catch (Exception $e) { - - $rollBack = true; - $exception = $e; - - } - - } - - if ($rollBack) { - if (!$this->broadcastEvent('beforeUnbind',array($uri))) return; - $this->tree->delete($uri); - - // Re-throwing exception - if ($exception) throw $exception; - - return $errorResult; - } - - } - $this->tree->markDirty($parentUri); - $this->broadcastEvent('afterBind',array($uri)); - - } - - /** - * This method updates a resource's properties - * - * The properties array must be a list of properties. Array-keys are - * property names in clarknotation, array-values are it's values. - * If a property must be deleted, the value should be null. - * - * Note that this request should either completely succeed, or - * completely fail. - * - * The response is an array with statuscodes for keys, which in turn - * contain arrays with propertynames. This response can be used - * to generate a multistatus body. - * - * @param string $uri - * @param array $properties - * @return array - */ - public function updateProperties($uri, array $properties) { - - // we'll start by grabbing the node, this will throw the appropriate - // exceptions if it doesn't. - $node = $this->tree->getNodeForPath($uri); - - $result = array( - 200 => array(), - 403 => array(), - 424 => array(), - ); - $remainingProperties = $properties; - $hasError = false; - - // Running through all properties to make sure none of them are protected - if (!$hasError) foreach($properties as $propertyName => $value) { - if(in_array($propertyName, $this->protectedProperties)) { - $result[403][$propertyName] = null; - unset($remainingProperties[$propertyName]); - $hasError = true; - } - } - - if (!$hasError) { - // Allowing plugins to take care of property updating - $hasError = !$this->broadcastEvent('updateProperties',array( - &$remainingProperties, - &$result, - $node - )); - } - - // If the node is not an instance of Sabre\DAV\IProperties, every - // property is 403 Forbidden - if (!$hasError && count($remainingProperties) && !($node instanceof IProperties)) { - $hasError = true; - foreach($properties as $propertyName=> $value) { - $result[403][$propertyName] = null; - } - $remainingProperties = array(); - } - - // Only if there were no errors we may attempt to update the resource - if (!$hasError) { - - if (count($remainingProperties)>0) { - - $updateResult = $node->updateProperties($remainingProperties); - - if ($updateResult===true) { - // success - foreach($remainingProperties as $propertyName=>$value) { - $result[200][$propertyName] = null; - } - - } elseif ($updateResult===false) { - // The node failed to update the properties for an - // unknown reason - foreach($remainingProperties as $propertyName=>$value) { - $result[403][$propertyName] = null; - } - - } elseif (is_array($updateResult)) { - - // The node has detailed update information - // We need to merge the results with the earlier results. - foreach($updateResult as $status => $props) { - if (is_array($props)) { - if (!isset($result[$status])) - $result[$status] = array(); - - $result[$status] = array_merge($result[$status], $updateResult[$status]); - } - } - - } else { - throw new Exception('Invalid result from updateProperties'); - } - $remainingProperties = array(); - } - - } - - foreach($remainingProperties as $propertyName=>$value) { - // if there are remaining properties, it must mean - // there's a dependency failure - $result[424][$propertyName] = null; - } - - // Removing empty array values - foreach($result as $status=>$props) { - - if (count($props)===0) unset($result[$status]); - - } - $result['href'] = $uri; - return $result; - - } - - /** - * This method checks the main HTTP preconditions. - * - * Currently these are: - * * If-Match - * * If-None-Match - * * If-Modified-Since - * * If-Unmodified-Since - * - * The method will return true if all preconditions are met - * The method will return false, or throw an exception if preconditions - * failed. If false is returned the operation should be aborted, and - * the appropriate HTTP response headers are already set. - * - * Normally this method will throw 412 Precondition Failed for failures - * related to If-None-Match, If-Match and If-Unmodified Since. It will - * set the status to 304 Not Modified for If-Modified_since. - * - * If the $handleAsGET argument is set to true, it will also return 304 - * Not Modified for failure of the If-None-Match precondition. This is the - * desired behaviour for HTTP GET and HTTP HEAD requests. - * - * @param bool $handleAsGET - * @return bool - */ - public function checkPreconditions($handleAsGET = false) { - - $uri = $this->getRequestUri(); - $node = null; - $lastMod = null; - $etag = null; - - if ($ifMatch = $this->httpRequest->getHeader('If-Match')) { - - // If-Match contains an entity tag. Only if the entity-tag - // matches we are allowed to make the request succeed. - // If the entity-tag is '*' we are only allowed to make the - // request succeed if a resource exists at that url. - try { - $node = $this->tree->getNodeForPath($uri); - } catch (Exception\NotFound $e) { - throw new Exception\PreconditionFailed('An If-Match header was specified and the resource did not exist','If-Match'); - } - - // Only need to check entity tags if they are not * - if ($ifMatch!=='*') { - - // There can be multiple etags - $ifMatch = explode(',',$ifMatch); - $haveMatch = false; - foreach($ifMatch as $ifMatchItem) { - - // Stripping any extra spaces - $ifMatchItem = trim($ifMatchItem,' '); - - $etag = $node->getETag(); - if ($etag===$ifMatchItem) { - $haveMatch = true; - } else { - // Evolution has a bug where it sometimes prepends the " - // with a \. This is our workaround. - if (str_replace('\\"','"', $ifMatchItem) === $etag) { - $haveMatch = true; - } - } - - } - if (!$haveMatch) { - throw new Exception\PreconditionFailed('An If-Match header was specified, but none of the specified the ETags matched.','If-Match'); - } - } - } - - if ($ifNoneMatch = $this->httpRequest->getHeader('If-None-Match')) { - - // The If-None-Match header contains an etag. - // Only if the ETag does not match the current ETag, the request will succeed - // The header can also contain *, in which case the request - // will only succeed if the entity does not exist at all. - $nodeExists = true; - if (!$node) { - try { - $node = $this->tree->getNodeForPath($uri); - } catch (Exception\NotFound $e) { - $nodeExists = false; - } - } - if ($nodeExists) { - $haveMatch = false; - if ($ifNoneMatch==='*') $haveMatch = true; - else { - - // There might be multiple etags - $ifNoneMatch = explode(',', $ifNoneMatch); - $etag = $node->getETag(); - - foreach($ifNoneMatch as $ifNoneMatchItem) { - - // Stripping any extra spaces - $ifNoneMatchItem = trim($ifNoneMatchItem,' '); - - if ($etag===$ifNoneMatchItem) $haveMatch = true; - - } - - } - - if ($haveMatch) { - if ($handleAsGET) { - $this->httpResponse->sendStatus(304); - return false; - } else { - throw new Exception\PreconditionFailed('An If-None-Match header was specified, but the ETag matched (or * was specified).','If-None-Match'); - } - } - } - - } - - if (!$ifNoneMatch && ($ifModifiedSince = $this->httpRequest->getHeader('If-Modified-Since'))) { - - // The If-Modified-Since header contains a date. We - // will only return the entity if it has been changed since - // that date. If it hasn't been changed, we return a 304 - // header - // Note that this header only has to be checked if there was no If-None-Match header - // as per the HTTP spec. - $date = HTTP\Util::parseHTTPDate($ifModifiedSince); - - if ($date) { - if (is_null($node)) { - $node = $this->tree->getNodeForPath($uri); - } - $lastMod = $node->getLastModified(); - if ($lastMod) { - $lastMod = new \DateTime('@' . $lastMod); - if ($lastMod <= $date) { - $this->httpResponse->sendStatus(304); - $this->httpResponse->setHeader('Last-Modified', HTTP\Util::toHTTPDate($lastMod)); - return false; - } - } - } - } - - if ($ifUnmodifiedSince = $this->httpRequest->getHeader('If-Unmodified-Since')) { - - // The If-Unmodified-Since will allow allow the request if the - // entity has not changed since the specified date. - $date = HTTP\Util::parseHTTPDate($ifUnmodifiedSince); - - // We must only check the date if it's valid - if ($date) { - if (is_null($node)) { - $node = $this->tree->getNodeForPath($uri); - } - $lastMod = $node->getLastModified(); - if ($lastMod) { - $lastMod = new \DateTime('@' . $lastMod); - if ($lastMod > $date) { - throw new Exception\PreconditionFailed('An If-Unmodified-Since header was specified, but the entity has been changed since the specified date.','If-Unmodified-Since'); - } - } - } - - } - return true; - - } - - // }}} - // {{{ XML Readers & Writers - - - /** - * Generates a WebDAV propfind response body based on a list of nodes. - * - * If 'strip404s' is set to true, all 404 responses will be removed. - * - * @param array $fileProperties The list with nodes - * @param bool strip404s - * @return string - */ - public function generateMultiStatus(array $fileProperties, $strip404s = false) { - - $dom = new \DOMDocument('1.0','utf-8'); - //$dom->formatOutput = true; - $multiStatus = $dom->createElement('d:multistatus'); - $dom->appendChild($multiStatus); - - // Adding in default namespaces - foreach($this->xmlNamespaces as $namespace=>$prefix) { - - $multiStatus->setAttribute('xmlns:' . $prefix,$namespace); - - } - - foreach($fileProperties as $entry) { - - $href = $entry['href']; - unset($entry['href']); - - if ($strip404s && isset($entry[404])) { - unset($entry[404]); - } - - $response = new Property\Response($href,$entry); - $response->serialize($this,$multiStatus); - - } - - return $dom->saveXML(); - - } - - /** - * This method parses a PropPatch request - * - * PropPatch changes the properties for a resource. This method - * returns a list of properties. - * - * The keys in the returned array contain the property name (e.g.: {DAV:}displayname, - * and the value contains the property value. If a property is to be removed the value - * will be null. - * - * @param string $body xml body - * @return array list of properties in need of updating or deletion - */ - public function parsePropPatchRequest($body) { - - //We'll need to change the DAV namespace declaration to something else in order to make it parsable - $dom = XMLUtil::loadDOMDocument($body); - - $newProperties = array(); - - foreach($dom->firstChild->childNodes as $child) { - - if ($child->nodeType !== XML_ELEMENT_NODE) continue; - - $operation = XMLUtil::toClarkNotation($child); - - if ($operation!=='{DAV:}set' && $operation!=='{DAV:}remove') continue; - - $innerProperties = XMLUtil::parseProperties($child, $this->propertyMap); - - foreach($innerProperties as $propertyName=>$propertyValue) { - - if ($operation==='{DAV:}remove') { - $propertyValue = null; - } - - $newProperties[$propertyName] = $propertyValue; - - } - - } - - return $newProperties; - - } - - /** - * This method parses the PROPFIND request and returns its information - * - * This will either be a list of properties, or an empty array; in which case - * an {DAV:}allprop was requested. - * - * @param string $body - * @return array - */ - public function parsePropFindRequest($body) { - - // If the propfind body was empty, it means IE is requesting 'all' properties - if (!$body) return array(); - - $dom = XMLUtil::loadDOMDocument($body); - $elem = $dom->getElementsByTagNameNS('urn:DAV','propfind')->item(0); - return array_keys(XMLUtil::parseProperties($elem)); - - } - - // }}} - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/ServerPlugin.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/ServerPlugin.php deleted file mode 100644 index c393f43fbd..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/ServerPlugin.php +++ /dev/null @@ -1,90 +0,0 @@ -name = $name; - foreach($children as $child) { - - if (!($child instanceof INode)) throw new Exception('Only instances of Sabre\DAV\INode are allowed to be passed in the children argument'); - $this->addChild($child); - - } - - } - - /** - * Adds a new childnode to this collection - * - * @param INode $child - * @return void - */ - public function addChild(INode $child) { - - $this->children[$child->getName()] = $child; - - } - - /** - * Returns the name of the collection - * - * @return string - */ - public function getName() { - - return $this->name; - - } - - /** - * Returns a child object, by its name. - * - * This method makes use of the getChildren method to grab all the child nodes, and compares the name. - * Generally its wise to override this, as this can usually be optimized - * - * This method must throw Sabre\DAV\Exception\NotFound if the node does not - * exist. - * - * @param string $name - * @throws Exception\NotFound - * @return INode - */ - public function getChild($name) { - - if (isset($this->children[$name])) return $this->children[$name]; - throw new Exception\NotFound('File not found: ' . $name . ' in \'' . $this->getName() . '\''); - - } - - /** - * Returns a list of children for this collection - * - * @return array - */ - public function getChildren() { - - return array_values($this->children); - - } - - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/SimpleFile.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/SimpleFile.php deleted file mode 100644 index b7413fdded..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/SimpleFile.php +++ /dev/null @@ -1,121 +0,0 @@ -name = $name; - $this->contents = $contents; - $this->mimeType = $mimeType; - - } - - /** - * Returns the node name for this file. - * - * This name is used to construct the url. - * - * @return string - */ - public function getName() { - - return $this->name; - - } - - /** - * Returns the data - * - * This method may either return a string or a readable stream resource - * - * @return mixed - */ - public function get() { - - return $this->contents; - - } - - /** - * Returns the size of the file, in bytes. - * - * @return int - */ - public function getSize() { - - return strlen($this->contents); - - } - - /** - * Returns the ETag for a file - * - * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. - * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. - * - * Return null if the ETag can not effectively be determined - * @return string - */ - public function getETag() { - - return '"' . md5($this->contents) . '"'; - - } - - /** - * Returns the mime-type for a file - * - * If null is returned, we'll assume application/octet-stream - * @return string - */ - public function getContentType() { - - return $this->mimeType; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/StringUtil.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/StringUtil.php deleted file mode 100644 index c71575f490..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/StringUtil.php +++ /dev/null @@ -1,91 +0,0 @@ -dataDir = $dataDir; - - } - - /** - * Initialize the plugin - * - * This is called automatically be the Server class after this plugin is - * added with Sabre\DAV\Server::addPlugin() - * - * @param Server $server - * @return void - */ - public function initialize(Server $server) { - - $this->server = $server; - $server->subscribeEvent('beforeMethod',array($this,'beforeMethod')); - $server->subscribeEvent('beforeCreateFile',array($this,'beforeCreateFile')); - - } - - /** - * This method is called before any HTTP method handler - * - * This method intercepts any GET, DELETE, PUT and PROPFIND calls to - * filenames that are known to match the 'temporary file' regex. - * - * @param string $method - * @param string $uri - * @return bool - */ - public function beforeMethod($method, $uri) { - - if (!$tempLocation = $this->isTempFile($uri)) - return true; - - switch($method) { - case 'GET' : - return $this->httpGet($tempLocation); - case 'PUT' : - return $this->httpPut($tempLocation); - case 'PROPFIND' : - return $this->httpPropfind($tempLocation, $uri); - case 'DELETE' : - return $this->httpDelete($tempLocation); - } - return true; - - } - - /** - * This method is invoked if some subsystem creates a new file. - * - * This is used to deal with HTTP LOCK requests which create a new - * file. - * - * @param string $uri - * @param resource $data - * @return bool - */ - public function beforeCreateFile($uri,$data) { - - if ($tempPath = $this->isTempFile($uri)) { - - $hR = $this->server->httpResponse; - $hR->setHeader('X-Sabre-Temp','true'); - file_put_contents($tempPath,$data); - return false; - } - return true; - - } - - /** - * This method will check if the url matches the temporary file pattern - * if it does, it will return an path based on $this->dataDir for the - * temporary file storage. - * - * @param string $path - * @return boolean|string - */ - protected function isTempFile($path) { - - // We're only interested in the basename. - list(, $tempPath) = URLUtil::splitPath($path); - - foreach($this->temporaryFilePatterns as $tempFile) { - - if (preg_match($tempFile,$tempPath)) { - return $this->getDataDir() . '/sabredav_' . md5($path) . '.tempfile'; - } - - } - - return false; - - } - - - /** - * This method handles the GET method for temporary files. - * If the file doesn't exist, it will return false which will kick in - * the regular system for the GET method. - * - * @param string $tempLocation - * @return bool - */ - public function httpGet($tempLocation) { - - if (!file_exists($tempLocation)) return true; - - $hR = $this->server->httpResponse; - $hR->setHeader('Content-Type','application/octet-stream'); - $hR->setHeader('Content-Length',filesize($tempLocation)); - $hR->setHeader('X-Sabre-Temp','true'); - $hR->sendStatus(200); - $hR->sendBody(fopen($tempLocation,'r')); - return false; - - } - - /** - * This method handles the PUT method. - * - * @param string $tempLocation - * @return bool - */ - public function httpPut($tempLocation) { - - $hR = $this->server->httpResponse; - $hR->setHeader('X-Sabre-Temp','true'); - - $newFile = !file_exists($tempLocation); - - if (!$newFile && ($this->server->httpRequest->getHeader('If-None-Match'))) { - throw new Exception\PreconditionFailed('The resource already exists, and an If-None-Match header was supplied'); - } - - file_put_contents($tempLocation,$this->server->httpRequest->getBody()); - $hR->sendStatus($newFile?201:200); - return false; - - } - - /** - * This method handles the DELETE method. - * - * If the file didn't exist, it will return false, which will make the - * standard HTTP DELETE handler kick in. - * - * @param string $tempLocation - * @return bool - */ - public function httpDelete($tempLocation) { - - if (!file_exists($tempLocation)) return true; - - unlink($tempLocation); - $hR = $this->server->httpResponse; - $hR->setHeader('X-Sabre-Temp','true'); - $hR->sendStatus(204); - return false; - - } - - /** - * This method handles the PROPFIND method. - * - * It's a very lazy method, it won't bother checking the request body - * for which properties were requested, and just sends back a default - * set of properties. - * - * @param string $tempLocation - * @param string $uri - * @return bool - */ - public function httpPropfind($tempLocation, $uri) { - - if (!file_exists($tempLocation)) return true; - - $hR = $this->server->httpResponse; - $hR->setHeader('X-Sabre-Temp','true'); - $hR->sendStatus(207); - $hR->setHeader('Content-Type','application/xml; charset=utf-8'); - - $this->server->parsePropFindRequest($this->server->httpRequest->getBody(true)); - - $properties = array( - 'href' => $uri, - 200 => array( - '{DAV:}getlastmodified' => new Property\GetLastModified(filemtime($tempLocation)), - '{DAV:}getcontentlength' => filesize($tempLocation), - '{DAV:}resourcetype' => new Property\ResourceType(null), - '{'.Server::NS_SABREDAV.'}tempFile' => true, - - ), - ); - - $data = $this->server->generateMultiStatus(array($properties)); - $hR->sendBody($data); - return false; - - } - - - /** - * This method returns the directory where the temporary files should be stored. - * - * @return string - */ - protected function getDataDir() - { - return $this->dataDir; - } -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Tree.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Tree.php deleted file mode 100644 index ab94168b29..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Tree.php +++ /dev/null @@ -1,193 +0,0 @@ -getNodeForPath($path); - return true; - - } catch (Exception\NotFound $e) { - - return false; - - } - - } - - /** - * Copies a file from path to another - * - * @param string $sourcePath The source location - * @param string $destinationPath The full destination path - * @return void - */ - public function copy($sourcePath, $destinationPath) { - - $sourceNode = $this->getNodeForPath($sourcePath); - - // grab the dirname and basename components - list($destinationDir, $destinationName) = URLUtil::splitPath($destinationPath); - - $destinationParent = $this->getNodeForPath($destinationDir); - $this->copyNode($sourceNode,$destinationParent,$destinationName); - - $this->markDirty($destinationDir); - - } - - /** - * Moves a file from one location to another - * - * @param string $sourcePath The path to the file which should be moved - * @param string $destinationPath The full destination path, so not just the destination parent node - * @return int - */ - public function move($sourcePath, $destinationPath) { - - list($sourceDir, $sourceName) = URLUtil::splitPath($sourcePath); - list($destinationDir, $destinationName) = URLUtil::splitPath($destinationPath); - - if ($sourceDir===$destinationDir) { - $renameable = $this->getNodeForPath($sourcePath); - $renameable->setName($destinationName); - } else { - $this->copy($sourcePath,$destinationPath); - $this->getNodeForPath($sourcePath)->delete(); - } - $this->markDirty($sourceDir); - $this->markDirty($destinationDir); - - } - - /** - * Deletes a node from the tree - * - * @param string $path - * @return void - */ - public function delete($path) { - - $node = $this->getNodeForPath($path); - $node->delete(); - - list($parent) = URLUtil::splitPath($path); - $this->markDirty($parent); - - } - - /** - * Returns a list of childnodes for a given path. - * - * @param string $path - * @return array - */ - public function getChildren($path) { - - $node = $this->getNodeForPath($path); - return $node->getChildren(); - - } - - /** - * This method is called with every tree update - * - * Examples of tree updates are: - * * node deletions - * * node creations - * * copy - * * move - * * renaming nodes - * - * If Tree classes implement a form of caching, this will allow - * them to make sure caches will be expired. - * - * If a path is passed, it is assumed that the entire subtree is dirty - * - * @param string $path - * @return void - */ - public function markDirty($path) { - - - } - - /** - * copyNode - * - * @param INode $source - * @param ICollection $destinationParent - * @param string $destinationName - * @return void - */ - protected function copyNode(INode $source,ICollection $destinationParent,$destinationName = null) { - - if (!$destinationName) $destinationName = $source->getName(); - - if ($source instanceof IFile) { - - $data = $source->get(); - - // If the body was a string, we need to convert it to a stream - if (is_string($data)) { - $stream = fopen('php://temp','r+'); - fwrite($stream,$data); - rewind($stream); - $data = $stream; - } - $destinationParent->createFile($destinationName,$data); - $destination = $destinationParent->getChild($destinationName); - - } elseif ($source instanceof ICollection) { - - $destinationParent->createDirectory($destinationName); - - $destination = $destinationParent->getChild($destinationName); - foreach($source->getChildren() as $child) { - - $this->copyNode($child,$destination); - - } - - } - if ($source instanceof IProperties && $destination instanceof IProperties) { - - $props = $source->getProperties(array()); - $destination->updateProperties($props); - - } - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/Tree/Filesystem.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/Tree/Filesystem.php deleted file mode 100644 index a477725a5c..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/Tree/Filesystem.php +++ /dev/null @@ -1,133 +0,0 @@ -basePath = $basePath; - - } - - /** - * Returns a new node for the given path - * - * @param string $path - * @return DAV\FS\Node - */ - public function getNodeForPath($path) { - - $realPath = $this->getRealPath($path); - if (!file_exists($realPath)) { - throw new DAV\Exception\NotFound('File at location ' . $realPath . ' not found'); - } - if (is_dir($realPath)) { - return new DAV\FS\Directory($realPath); - } else { - return new DAV\FS\File($realPath); - } - - } - - /** - * Returns the real filesystem path for a webdav url. - * - * @param string $publicPath - * @return string - */ - protected function getRealPath($publicPath) { - - return rtrim($this->basePath,'/') . '/' . trim($publicPath,'/'); - - } - - /** - * Copies a file or directory. - * - * This method must work recursively and delete the destination - * if it exists - * - * @param string $source - * @param string $destination - * @return void - */ - public function copy($source,$destination) { - - $source = $this->getRealPath($source); - $destination = $this->getRealPath($destination); - $this->realCopy($source,$destination); - - } - - /** - * Used by self::copy - * - * @param string $source - * @param string $destination - * @return void - */ - protected function realCopy($source,$destination) { - - if (is_file($source)) { - copy($source,$destination); - } else { - mkdir($destination); - foreach(scandir($source) as $subnode) { - - if ($subnode=='.' || $subnode=='..') continue; - $this->realCopy($source.'/'.$subnode,$destination.'/'.$subnode); - - } - } - - } - - /** - * Moves a file or directory recursively. - * - * If the destination exists, delete it first. - * - * @param string $source - * @param string $destination - * @return void - */ - public function move($source,$destination) { - - $source = $this->getRealPath($source); - $destination = $this->getRealPath($destination); - rename($source,$destination); - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAV/URLUtil.php b/core/src/core/classes/sabredav/lib/Sabre/DAV/URLUtil.php deleted file mode 100644 index b7254e9a17..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAV/URLUtil.php +++ /dev/null @@ -1,124 +0,0 @@ - - * will be returned as: - * {http://www.example.org}myelem - * - * This format is used throughout the SabreDAV sourcecode. - * Elements encoded with the urn:DAV namespace will - * be returned as if they were in the DAV: namespace. This is to avoid - * compatibility problems. - * - * This function will return null if a nodetype other than an Element is passed. - * - * @param \DOMNode $dom - * @return string - */ - static function toClarkNotation(\DOMNode $dom) { - - if ($dom->nodeType !== XML_ELEMENT_NODE) return null; - - // Mapping back to the real namespace, in case it was dav - if ($dom->namespaceURI=='urn:DAV') $ns = 'DAV:'; else $ns = $dom->namespaceURI; - - // Mapping to clark notation - return '{' . $ns . '}' . $dom->localName; - - } - - /** - * Parses a clark-notation string, and returns the namespace and element - * name components. - * - * If the string was invalid, it will throw an InvalidArgumentException. - * - * @param string $str - * @throws InvalidArgumentException - * @return array - */ - static function parseClarkNotation($str) { - - if (!preg_match('/^{([^}]*)}(.*)$/',$str,$matches)) { - throw new \InvalidArgumentException('\'' . $str . '\' is not a valid clark-notation formatted string'); - } - - return array( - $matches[1], - $matches[2] - ); - - } - - /** - * This method takes an XML document (as string) and converts all instances of the - * DAV: namespace to urn:DAV - * - * This is unfortunately needed, because the DAV: namespace violates the xml namespaces - * spec, and causes the DOM to throw errors - * - * @param string $xmlDocument - * @return array|string|null - */ - static function convertDAVNamespace($xmlDocument) { - - // This is used to map the DAV: namespace to urn:DAV. This is needed, because the DAV: - // namespace is actually a violation of the XML namespaces specification, and will cause errors - return preg_replace("/xmlns(:[A-Za-z0-9_]*)?=(\"|\')DAV:(\\2)/","xmlns\\1=\\2urn:DAV\\2",$xmlDocument); - - } - - /** - * This method provides a generic way to load a DOMDocument for WebDAV use. - * - * This method throws a Sabre\DAV\Exception\BadRequest exception for any xml errors. - * It does not preserve whitespace, and it converts the DAV: namespace to urn:DAV. - * - * @param string $xml - * @throws Sabre\DAV\Exception\BadRequest - * @return DOMDocument - */ - static function loadDOMDocument($xml) { - - if (empty($xml)) - throw new Exception\BadRequest('Empty XML document sent'); - - // The BitKinex client sends xml documents as UTF-16. PHP 5.3.1 (and presumably lower) - // does not support this, so we must intercept this and convert to UTF-8. - if (substr($xml,0,12) === "\x3c\x00\x3f\x00\x78\x00\x6d\x00\x6c\x00\x20\x00") { - - // Note: the preceeding byte sequence is "]*)encoding="UTF-16"([^>]*)>|u','',$xml); - - } - - // Retaining old error setting - $oldErrorSetting = libxml_use_internal_errors(true); - // Fixes an XXE vulnerability on PHP versions older than 5.3.23 or - // 5.4.13. - $oldEntityLoaderSetting = libxml_disable_entity_loader(true); - - // Clearing any previous errors - libxml_clear_errors(); - - $dom = new \DOMDocument(); - - // We don't generally care about any whitespace - $dom->preserveWhiteSpace = false; - - $dom->loadXML(self::convertDAVNamespace($xml),LIBXML_NOWARNING | LIBXML_NOERROR); - - if ($error = libxml_get_last_error()) { - libxml_clear_errors(); - throw new Exception\BadRequest('The request body had an invalid XML body. (message: ' . $error->message . ', errorcode: ' . $error->code . ', line: ' . $error->line . ')'); - } - - // Restoring old mechanism for error handling - if ($oldErrorSetting===false) libxml_use_internal_errors(false); - if ($oldEntityLoaderSetting===false) libxml_disable_entity_loader(false); - - return $dom; - - } - - /** - * Parses all WebDAV properties out of a DOM Element - * - * Generally WebDAV properties are enclosed in {DAV:}prop elements. This - * method helps by going through all these and pulling out the actual - * propertynames, making them array keys and making the property values, - * well.. the array values. - * - * If no value was given (self-closing element) null will be used as the - * value. This is used in for example PROPFIND requests. - * - * Complex values are supported through the propertyMap argument. The - * propertyMap should have the clark-notation properties as it's keys, and - * classnames as values. - * - * When any of these properties are found, the unserialize() method will be - * (statically) called. The result of this method is used as the value. - * - * @param \DOMElement $parentNode - * @param array $propertyMap - * @return array - */ - static function parseProperties(\DOMElement $parentNode, array $propertyMap = array()) { - - $propList = array(); - foreach($parentNode->childNodes as $propNode) { - - if (self::toClarkNotation($propNode)!=='{DAV:}prop') continue; - - foreach($propNode->childNodes as $propNodeData) { - - /* If there are no elements in here, we actually get 1 text node, this special case is dedicated to netdrive */ - if ($propNodeData->nodeType != XML_ELEMENT_NODE) continue; - - $propertyName = self::toClarkNotation($propNodeData); - if (isset($propertyMap[$propertyName])) { - $propList[$propertyName] = call_user_func(array($propertyMap[$propertyName],'unserialize'),$propNodeData); - } else { - $propList[$propertyName] = $propNodeData->textContent; - } - } - - - } - return $propList; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/AbstractPrincipalCollection.php b/core/src/core/classes/sabredav/lib/Sabre/DAVACL/AbstractPrincipalCollection.php deleted file mode 100644 index a116236f31..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/AbstractPrincipalCollection.php +++ /dev/null @@ -1,155 +0,0 @@ -principalPrefix = $principalPrefix; - $this->principalBackend = $principalBackend; - - } - - /** - * This method returns a node for a principal. - * - * The passed array contains principal information, and is guaranteed to - * at least contain a uri item. Other properties may or may not be - * supplied by the authentication backend. - * - * @param array $principalInfo - * @return IPrincipal - */ - abstract function getChildForPrincipal(array $principalInfo); - - /** - * Returns the name of this collection. - * - * @return string - */ - public function getName() { - - list(,$name) = DAV\URLUtil::splitPath($this->principalPrefix); - return $name; - - } - - /** - * Return the list of users - * - * @return array - */ - public function getChildren() { - - if ($this->disableListing) - throw new DAV\Exception\MethodNotAllowed('Listing members of this collection is disabled'); - - $children = array(); - foreach($this->principalBackend->getPrincipalsByPrefix($this->principalPrefix) as $principalInfo) { - - $children[] = $this->getChildForPrincipal($principalInfo); - - - } - return $children; - - } - - /** - * Returns a child object, by its name. - * - * @param string $name - * @throws DAV\Exception\NotFound - * @return IPrincipal - */ - public function getChild($name) { - - $principalInfo = $this->principalBackend->getPrincipalByPath($this->principalPrefix . '/' . $name); - if (!$principalInfo) throw new DAV\Exception\NotFound('Principal with name ' . $name . ' not found'); - return $this->getChildForPrincipal($principalInfo); - - } - - /** - * This method is used to search for principals matching a set of - * properties. - * - * This search is specifically used by RFC3744's principal-property-search - * REPORT. You should at least allow searching on - * http://sabredav.org/ns}email-address. - * - * The actual search should be a unicode-non-case-sensitive search. The - * keys in searchProperties are the WebDAV property names, while the values - * are the property values to search on. - * - * If multiple properties are being searched on, the search should be - * AND'ed. - * - * This method should simply return a list of 'child names', which may be - * used to call $this->getChild in the future. - * - * @param array $searchProperties - * @return array - */ - public function searchPrincipals(array $searchProperties) { - - $result = $this->principalBackend->searchPrincipals($this->principalPrefix, $searchProperties); - $r = array(); - - foreach($result as $row) { - list(, $r[]) = DAV\URLUtil::splitPath($row); - } - - return $r; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Exception/AceConflict.php b/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Exception/AceConflict.php deleted file mode 100644 index 6ee9afd73b..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Exception/AceConflict.php +++ /dev/null @@ -1,35 +0,0 @@ -ownerDocument; - - $np = $doc->createElementNS('DAV:','d:no-ace-conflict'); - $errorNode->appendChild($np); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Exception/NeedPrivileges.php b/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Exception/NeedPrivileges.php deleted file mode 100644 index f7e4358839..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Exception/NeedPrivileges.php +++ /dev/null @@ -1,83 +0,0 @@ -uri = $uri; - $this->privileges = $privileges; - - parent::__construct('User did not have the required privileges (' . implode(',', $privileges) . ') for path "' . $uri . '"'); - - } - - /** - * Adds in extra information in the xml response. - * - * This method adds the {DAV:}need-privileges element as defined in rfc3744 - * - * @param DAV\Server $server - * @param \DOMElement $errorNode - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $errorNode) { - - $doc = $errorNode->ownerDocument; - - $np = $doc->createElementNS('DAV:','d:need-privileges'); - $errorNode->appendChild($np); - - foreach($this->privileges as $privilege) { - - $resource = $doc->createElementNS('DAV:','d:resource'); - $np->appendChild($resource); - - $resource->appendChild($doc->createElementNS('DAV:','d:href',$server->getBaseUri() . $this->uri)); - - $priv = $doc->createElementNS('DAV:','d:privilege'); - $resource->appendChild($priv); - - preg_match('/^{([^}]*)}(.*)$/',$privilege,$privilegeParts); - $priv->appendChild($doc->createElementNS($privilegeParts[1],'d:' . $privilegeParts[2])); - - - } - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Exception/NoAbstract.php b/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Exception/NoAbstract.php deleted file mode 100644 index ba6f76cdb5..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Exception/NoAbstract.php +++ /dev/null @@ -1,35 +0,0 @@ -ownerDocument; - - $np = $doc->createElementNS('DAV:','d:no-abstract'); - $errorNode->appendChild($np); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php b/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php deleted file mode 100644 index f61edef073..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php +++ /dev/null @@ -1,35 +0,0 @@ -ownerDocument; - - $np = $doc->createElementNS('DAV:','d:recognized-principal'); - $errorNode->appendChild($np); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Exception/NotSupportedPrivilege.php b/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Exception/NotSupportedPrivilege.php deleted file mode 100644 index 6d30698cd7..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Exception/NotSupportedPrivilege.php +++ /dev/null @@ -1,35 +0,0 @@ -ownerDocument; - - $np = $doc->createElementNS('DAV:','d:not-supported-privilege'); - $errorNode->appendChild($np); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/IACL.php b/core/src/core/classes/sabredav/lib/Sabre/DAVACL/IACL.php deleted file mode 100644 index 088ca3eec8..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/IACL.php +++ /dev/null @@ -1,74 +0,0 @@ -getChild in the future. - * - * @param array $searchProperties - * @return array - */ - function searchPrincipals(array $searchProperties); - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Plugin.php b/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Plugin.php deleted file mode 100644 index f9bf4bb441..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Plugin.php +++ /dev/null @@ -1,1402 +0,0 @@ - 'Display name', - '{http://sabredav.org/ns}email-address' => 'Email address', - ); - - /** - * Any principal uri's added here, will automatically be added to the list - * of ACL's. They will effectively receive {DAV:}all privileges, as a - * protected privilege. - * - * @var array - */ - public $adminPrincipals = array(); - - /** - * Returns a list of features added by this plugin. - * - * This list is used in the response of a HTTP OPTIONS request. - * - * @return array - */ - public function getFeatures() { - - return array('access-control', 'calendarserver-principal-property-search'); - - } - - /** - * Returns a list of available methods for a given url - * - * @param string $uri - * @return array - */ - public function getMethods($uri) { - - return array('ACL'); - - } - - /** - * Returns a plugin name. - * - * Using this name other plugins will be able to access other plugins - * using Sabre\DAV\Server::getPlugin - * - * @return string - */ - public function getPluginName() { - - return 'acl'; - - } - - /** - * Returns a list of reports this plugin supports. - * - * This will be used in the {DAV:}supported-report-set property. - * Note that you still need to subscribe to the 'report' event to actually - * implement them - * - * @param string $uri - * @return array - */ - public function getSupportedReportSet($uri) { - - return array( - '{DAV:}expand-property', - '{DAV:}principal-property-search', - '{DAV:}principal-search-property-set', - ); - - } - - - /** - * Checks if the current user has the specified privilege(s). - * - * You can specify a single privilege, or a list of privileges. - * This method will throw an exception if the privilege is not available - * and return true otherwise. - * - * @param string $uri - * @param array|string $privileges - * @param int $recursion - * @param bool $throwExceptions if set to false, this method won't throw exceptions. - * @throws Sabre\DAVACL\Exception\NeedPrivileges - * @return bool - */ - public function checkPrivileges($uri, $privileges, $recursion = self::R_PARENT, $throwExceptions = true) { - - if (!is_array($privileges)) $privileges = array($privileges); - - $acl = $this->getCurrentUserPrivilegeSet($uri); - - if (is_null($acl)) { - if ($this->allowAccessToNodesWithoutACL) { - return true; - } else { - if ($throwExceptions) - throw new Exception\NeedPrivileges($uri,$privileges); - else - return false; - - } - } - - $failed = array(); - foreach($privileges as $priv) { - - if (!in_array($priv, $acl)) { - $failed[] = $priv; - } - - } - - if ($failed) { - if ($throwExceptions) - throw new Exception\NeedPrivileges($uri,$failed); - else - return false; - } - return true; - - } - - /** - * Returns the standard users' principal. - * - * This is one authorative principal url for the current user. - * This method will return null if the user wasn't logged in. - * - * @return string|null - */ - public function getCurrentUserPrincipal() { - - $authPlugin = $this->server->getPlugin('auth'); - if (is_null($authPlugin)) return null; - /** @var $authPlugin Sabre\DAV\Auth\Plugin */ - - $userName = $authPlugin->getCurrentUser(); - if (!$userName) return null; - - return $this->defaultUsernamePath . '/' . $userName; - - } - - - /** - * Returns a list of principals that's associated to the current - * user, either directly or through group membership. - * - * @return array - */ - public function getCurrentUserPrincipals() { - - $currentUser = $this->getCurrentUserPrincipal(); - - if (is_null($currentUser)) return array(); - - return array_merge( - array($currentUser), - $this->getPrincipalMembership($currentUser) - ); - - } - - /** - * This array holds a cache for all the principals that are associated with - * a single principal. - * - * @var array - */ - protected $principalMembershipCache = array(); - - - /** - * Returns all the principal groups the specified principal is a member of. - * - * @param string $principal - * @return array - */ - public function getPrincipalMembership($mainPrincipal) { - - // First check our cache - if (isset($this->principalMembershipCache[$mainPrincipal])) { - return $this->principalMembershipCache[$mainPrincipal]; - } - - $check = array($mainPrincipal); - $principals = array(); - - while(count($check)) { - - $principal = array_shift($check); - - $node = $this->server->tree->getNodeForPath($principal); - if ($node instanceof IPrincipal) { - foreach($node->getGroupMembership() as $groupMember) { - - if (!in_array($groupMember, $principals)) { - - $check[] = $groupMember; - $principals[] = $groupMember; - - } - - } - - } - - } - - // Store the result in the cache - $this->principalMembershipCache[$mainPrincipal] = $principals; - - return $principals; - - } - - /** - * Returns the supported privilege structure for this ACL plugin. - * - * See RFC3744 for more details. Currently we default on a simple, - * standard structure. - * - * You can either get the list of privileges by a uri (path) or by - * specifying a Node. - * - * @param string|DAV\INode $node - * @return array - */ - public function getSupportedPrivilegeSet($node) { - - if (is_string($node)) { - $node = $this->server->tree->getNodeForPath($node); - } - - if ($node instanceof IACL) { - $result = $node->getSupportedPrivilegeSet(); - - if ($result) - return $result; - } - - return self::getDefaultSupportedPrivilegeSet(); - - } - - /** - * Returns a fairly standard set of privileges, which may be useful for - * other systems to use as a basis. - * - * @return array - */ - static function getDefaultSupportedPrivilegeSet() { - - return array( - 'privilege' => '{DAV:}all', - 'abstract' => true, - 'aggregates' => array( - array( - 'privilege' => '{DAV:}read', - 'aggregates' => array( - array( - 'privilege' => '{DAV:}read-acl', - 'abstract' => true, - ), - array( - 'privilege' => '{DAV:}read-current-user-privilege-set', - 'abstract' => true, - ), - ), - ), // {DAV:}read - array( - 'privilege' => '{DAV:}write', - 'aggregates' => array( - array( - 'privilege' => '{DAV:}write-acl', - 'abstract' => true, - ), - array( - 'privilege' => '{DAV:}write-properties', - 'abstract' => true, - ), - array( - 'privilege' => '{DAV:}write-content', - 'abstract' => true, - ), - array( - 'privilege' => '{DAV:}bind', - 'abstract' => true, - ), - array( - 'privilege' => '{DAV:}unbind', - 'abstract' => true, - ), - array( - 'privilege' => '{DAV:}unlock', - 'abstract' => true, - ), - ), - ), // {DAV:}write - ), - ); // {DAV:}all - - } - - /** - * Returns the supported privilege set as a flat list - * - * This is much easier to parse. - * - * The returned list will be index by privilege name. - * The value is a struct containing the following properties: - * - aggregates - * - abstract - * - concrete - * - * @param string|DAV\INode $node - * @return array - */ - final public function getFlatPrivilegeSet($node) { - - $privs = $this->getSupportedPrivilegeSet($node); - - $flat = array(); - $this->getFPSTraverse($privs, null, $flat); - - return $flat; - - } - - /** - * Traverses the privilege set tree for reordering - * - * This function is solely used by getFlatPrivilegeSet, and would have been - * a closure if it wasn't for the fact I need to support PHP 5.2. - * - * @param array $priv - * @param $concrete - * @param array $flat - * @return void - */ - final private function getFPSTraverse($priv, $concrete, &$flat) { - - $myPriv = array( - 'privilege' => $priv['privilege'], - 'abstract' => isset($priv['abstract']) && $priv['abstract'], - 'aggregates' => array(), - 'concrete' => isset($priv['abstract']) && $priv['abstract']?$concrete:$priv['privilege'], - ); - - if (isset($priv['aggregates'])) - foreach($priv['aggregates'] as $subPriv) $myPriv['aggregates'][] = $subPriv['privilege']; - - $flat[$priv['privilege']] = $myPriv; - - if (isset($priv['aggregates'])) { - - foreach($priv['aggregates'] as $subPriv) { - - $this->getFPSTraverse($subPriv, $myPriv['concrete'], $flat); - - } - - } - - } - - /** - * Returns the full ACL list. - * - * Either a uri or a DAV\INode may be passed. - * - * null will be returned if the node doesn't support ACLs. - * - * @param string|DAV\INode $node - * @return array - */ - public function getACL($node) { - - if (is_string($node)) { - $node = $this->server->tree->getNodeForPath($node); - } - if (!$node instanceof IACL) { - return null; - } - $acl = $node->getACL(); - foreach($this->adminPrincipals as $adminPrincipal) { - $acl[] = array( - 'principal' => $adminPrincipal, - 'privilege' => '{DAV:}all', - 'protected' => true, - ); - } - return $acl; - - } - - /** - * Returns a list of privileges the current user has - * on a particular node. - * - * Either a uri or a DAV\INode may be passed. - * - * null will be returned if the node doesn't support ACLs. - * - * @param string|DAV\INode $node - * @return array - */ - public function getCurrentUserPrivilegeSet($node) { - - if (is_string($node)) { - $node = $this->server->tree->getNodeForPath($node); - } - - $acl = $this->getACL($node); - - if (is_null($acl)) return null; - - $principals = $this->getCurrentUserPrincipals(); - - $collected = array(); - - foreach($acl as $ace) { - - $principal = $ace['principal']; - - switch($principal) { - - case '{DAV:}owner' : - $owner = $node->getOwner(); - if ($owner && in_array($owner, $principals)) { - $collected[] = $ace; - } - break; - - - // 'all' matches for every user - case '{DAV:}all' : - - // 'authenticated' matched for every user that's logged in. - // Since it's not possible to use ACL while not being logged - // in, this is also always true. - case '{DAV:}authenticated' : - $collected[] = $ace; - break; - - // 'unauthenticated' can never occur either, so we simply - // ignore these. - case '{DAV:}unauthenticated' : - break; - - default : - if (in_array($ace['principal'], $principals)) { - $collected[] = $ace; - } - break; - - } - - - } - - // Now we deduct all aggregated privileges. - $flat = $this->getFlatPrivilegeSet($node); - - $collected2 = array(); - while(count($collected)) { - - $current = array_pop($collected); - $collected2[] = $current['privilege']; - - foreach($flat[$current['privilege']]['aggregates'] as $subPriv) { - $collected2[] = $subPriv; - $collected[] = $flat[$subPriv]; - } - - } - - return array_values(array_unique($collected2)); - - } - - /** - * Principal property search - * - * This method can search for principals matching certain values in - * properties. - * - * This method will return a list of properties for the matched properties. - * - * @param array $searchProperties The properties to search on. This is a - * key-value list. The keys are property - * names, and the values the strings to - * match them on. - * @param array $requestedProperties This is the list of properties to - * return for every match. - * @param string $collectionUri The principal collection to search on. - * If this is ommitted, the standard - * principal collection-set will be used. - * @return array This method returns an array structure similar to - * Sabre\DAV\Server::getPropertiesForPath. Returned - * properties are index by a HTTP status code. - * - */ - public function principalSearch(array $searchProperties, array $requestedProperties, $collectionUri = null) { - - if (!is_null($collectionUri)) { - $uris = array($collectionUri); - } else { - $uris = $this->principalCollectionSet; - } - - $lookupResults = array(); - foreach($uris as $uri) { - - $principalCollection = $this->server->tree->getNodeForPath($uri); - if (!$principalCollection instanceof IPrincipalCollection) { - // Not a principal collection, we're simply going to ignore - // this. - continue; - } - - $results = $principalCollection->searchPrincipals($searchProperties); - foreach($results as $result) { - $lookupResults[] = rtrim($uri,'/') . '/' . $result; - } - - } - - $matches = array(); - - foreach($lookupResults as $lookupResult) { - - list($matches[]) = $this->server->getPropertiesForPath($lookupResult, $requestedProperties, 0); - - } - - return $matches; - - } - - /** - * Sets up the plugin - * - * This method is automatically called by the server class. - * - * @param DAV\Server $server - * @return void - */ - public function initialize(DAV\Server $server) { - - $this->server = $server; - $server->subscribeEvent('beforeGetProperties',array($this,'beforeGetProperties')); - - $server->subscribeEvent('beforeMethod', array($this,'beforeMethod'),20); - $server->subscribeEvent('beforeBind', array($this,'beforeBind'),20); - $server->subscribeEvent('beforeUnbind', array($this,'beforeUnbind'),20); - $server->subscribeEvent('updateProperties',array($this,'updateProperties')); - $server->subscribeEvent('beforeUnlock', array($this,'beforeUnlock'),20); - $server->subscribeEvent('report',array($this,'report')); - $server->subscribeEvent('unknownMethod', array($this, 'unknownMethod')); - - array_push($server->protectedProperties, - '{DAV:}alternate-URI-set', - '{DAV:}principal-URL', - '{DAV:}group-membership', - '{DAV:}principal-collection-set', - '{DAV:}current-user-principal', - '{DAV:}supported-privilege-set', - '{DAV:}current-user-privilege-set', - '{DAV:}acl', - '{DAV:}acl-restrictions', - '{DAV:}inherited-acl-set', - '{DAV:}owner', - '{DAV:}group' - ); - - // Automatically mapping nodes implementing IPrincipal to the - // {DAV:}principal resourcetype. - $server->resourceTypeMapping['Sabre\\DAVACL\\IPrincipal'] = '{DAV:}principal'; - - // Mapping the group-member-set property to the HrefList property - // class. - $server->propertyMap['{DAV:}group-member-set'] = 'Sabre\\DAV\\Property\\HrefList'; - - } - - - /* {{{ Event handlers */ - - /** - * Triggered before any method is handled - * - * @param string $method - * @param string $uri - * @return void - */ - public function beforeMethod($method, $uri) { - - $exists = $this->server->tree->nodeExists($uri); - - // If the node doesn't exists, none of these checks apply - if (!$exists) return; - - switch($method) { - - case 'GET' : - case 'HEAD' : - case 'OPTIONS' : - // For these 3 we only need to know if the node is readable. - $this->checkPrivileges($uri,'{DAV:}read'); - break; - - case 'PUT' : - case 'LOCK' : - case 'UNLOCK' : - // This method requires the write-content priv if the node - // already exists, and bind on the parent if the node is being - // created. - // The bind privilege is handled in the beforeBind event. - $this->checkPrivileges($uri,'{DAV:}write-content'); - break; - - - case 'PROPPATCH' : - $this->checkPrivileges($uri,'{DAV:}write-properties'); - break; - - case 'ACL' : - $this->checkPrivileges($uri,'{DAV:}write-acl'); - break; - - case 'COPY' : - case 'MOVE' : - // Copy requires read privileges on the entire source tree. - // If the target exists write-content normally needs to be - // checked, however, we're deleting the node beforehand and - // creating a new one after, so this is handled by the - // beforeUnbind event. - // - // The creation of the new node is handled by the beforeBind - // event. - // - // If MOVE is used beforeUnbind will also be used to check if - // the sourcenode can be deleted. - $this->checkPrivileges($uri,'{DAV:}read',self::R_RECURSIVE); - - break; - - } - - } - - /** - * Triggered before a new node is created. - * - * This allows us to check permissions for any operation that creates a - * new node, such as PUT, MKCOL, MKCALENDAR, LOCK, COPY and MOVE. - * - * @param string $uri - * @return void - */ - public function beforeBind($uri) { - - list($parentUri,$nodeName) = DAV\URLUtil::splitPath($uri); - $this->checkPrivileges($parentUri,'{DAV:}bind'); - - } - - /** - * Triggered before a node is deleted - * - * This allows us to check permissions for any operation that will delete - * an existing node. - * - * @param string $uri - * @return void - */ - public function beforeUnbind($uri) { - - list($parentUri,$nodeName) = DAV\URLUtil::splitPath($uri); - $this->checkPrivileges($parentUri,'{DAV:}unbind',self::R_RECURSIVEPARENTS); - - } - - /** - * Triggered before a node is unlocked. - * - * @param string $uri - * @param DAV\Locks\LockInfo $lock - * @TODO: not yet implemented - * @return void - */ - public function beforeUnlock($uri, DAV\Locks\LockInfo $lock) { - - - } - - /** - * Triggered before properties are looked up in specific nodes. - * - * @param string $uri - * @param DAV\INode $node - * @param array $requestedProperties - * @param array $returnedProperties - * @TODO really should be broken into multiple methods, or even a class. - * @return bool - */ - public function beforeGetProperties($uri, DAV\INode $node, &$requestedProperties, &$returnedProperties) { - - // Checking the read permission - if (!$this->checkPrivileges($uri,'{DAV:}read',self::R_PARENT,false)) { - - // User is not allowed to read properties - if ($this->hideNodesFromListings) { - return false; - } - - // Marking all requested properties as '403'. - foreach($requestedProperties as $key=>$requestedProperty) { - unset($requestedProperties[$key]); - $returnedProperties[403][$requestedProperty] = null; - } - return; - - } - - /* Adding principal properties */ - if ($node instanceof IPrincipal) { - - if (false !== ($index = array_search('{DAV:}alternate-URI-set', $requestedProperties))) { - - unset($requestedProperties[$index]); - $returnedProperties[200]['{DAV:}alternate-URI-set'] = new DAV\Property\HrefList($node->getAlternateUriSet()); - - } - if (false !== ($index = array_search('{DAV:}principal-URL', $requestedProperties))) { - - unset($requestedProperties[$index]); - $returnedProperties[200]['{DAV:}principal-URL'] = new DAV\Property\Href($node->getPrincipalUrl() . '/'); - - } - if (false !== ($index = array_search('{DAV:}group-member-set', $requestedProperties))) { - - unset($requestedProperties[$index]); - $returnedProperties[200]['{DAV:}group-member-set'] = new DAV\Property\HrefList($node->getGroupMemberSet()); - - } - if (false !== ($index = array_search('{DAV:}group-membership', $requestedProperties))) { - - unset($requestedProperties[$index]); - $returnedProperties[200]['{DAV:}group-membership'] = new DAV\Property\HrefList($node->getGroupMembership()); - - } - - if (false !== ($index = array_search('{DAV:}displayname', $requestedProperties))) { - - $returnedProperties[200]['{DAV:}displayname'] = $node->getDisplayName(); - - } - - } - if (false !== ($index = array_search('{DAV:}principal-collection-set', $requestedProperties))) { - - unset($requestedProperties[$index]); - $val = $this->principalCollectionSet; - // Ensuring all collections end with a slash - foreach($val as $k=>$v) $val[$k] = $v . '/'; - $returnedProperties[200]['{DAV:}principal-collection-set'] = new DAV\Property\HrefList($val); - - } - if (false !== ($index = array_search('{DAV:}current-user-principal', $requestedProperties))) { - - unset($requestedProperties[$index]); - if ($url = $this->getCurrentUserPrincipal()) { - $returnedProperties[200]['{DAV:}current-user-principal'] = new Property\Principal(Property\Principal::HREF, $url . '/'); - } else { - $returnedProperties[200]['{DAV:}current-user-principal'] = new Property\Principal(Property\Principal::UNAUTHENTICATED); - } - - } - if (false !== ($index = array_search('{DAV:}supported-privilege-set', $requestedProperties))) { - - unset($requestedProperties[$index]); - $returnedProperties[200]['{DAV:}supported-privilege-set'] = new Property\SupportedPrivilegeSet($this->getSupportedPrivilegeSet($node)); - - } - if (false !== ($index = array_search('{DAV:}current-user-privilege-set', $requestedProperties))) { - - if (!$this->checkPrivileges($uri, '{DAV:}read-current-user-privilege-set', self::R_PARENT, false)) { - $returnedProperties[403]['{DAV:}current-user-privilege-set'] = null; - unset($requestedProperties[$index]); - } else { - $val = $this->getCurrentUserPrivilegeSet($node); - if (!is_null($val)) { - unset($requestedProperties[$index]); - $returnedProperties[200]['{DAV:}current-user-privilege-set'] = new Property\CurrentUserPrivilegeSet($val); - } - } - - } - - /* The ACL property contains all the permissions */ - if (false !== ($index = array_search('{DAV:}acl', $requestedProperties))) { - - if (!$this->checkPrivileges($uri, '{DAV:}read-acl', self::R_PARENT, false)) { - - unset($requestedProperties[$index]); - $returnedProperties[403]['{DAV:}acl'] = null; - - } else { - - $acl = $this->getACL($node); - if (!is_null($acl)) { - unset($requestedProperties[$index]); - $returnedProperties[200]['{DAV:}acl'] = new Property\Acl($this->getACL($node)); - } - - } - - } - - /* The acl-restrictions property contains information on how privileges - * must behave. - */ - if (false !== ($index = array_search('{DAV:}acl-restrictions', $requestedProperties))) { - unset($requestedProperties[$index]); - $returnedProperties[200]['{DAV:}acl-restrictions'] = new Property\AclRestrictions(); - } - - /* Adding ACL properties */ - if ($node instanceof IACL) { - - if (false !== ($index = array_search('{DAV:}owner', $requestedProperties))) { - - unset($requestedProperties[$index]); - $returnedProperties[200]['{DAV:}owner'] = new DAV\Property\Href($node->getOwner() . '/'); - - } - - } - - } - - /** - * This method intercepts PROPPATCH methods and make sure the - * group-member-set is updated correctly. - * - * @param array $propertyDelta - * @param array $result - * @param DAV\INode $node - * @return bool - */ - public function updateProperties(&$propertyDelta, &$result, DAV\INode $node) { - - if (!array_key_exists('{DAV:}group-member-set', $propertyDelta)) - return; - - if (is_null($propertyDelta['{DAV:}group-member-set'])) { - $memberSet = array(); - } elseif ($propertyDelta['{DAV:}group-member-set'] instanceof DAV\Property\HrefList) { - $memberSet = array_map( - array($this->server,'calculateUri'), - $propertyDelta['{DAV:}group-member-set']->getHrefs() - ); - } else { - throw new DAV\Exception('The group-member-set property MUST be an instance of Sabre\DAV\Property\HrefList or null'); - } - - if (!($node instanceof IPrincipal)) { - $result[403]['{DAV:}group-member-set'] = null; - unset($propertyDelta['{DAV:}group-member-set']); - - // Returning false will stop the updateProperties process - return false; - } - - $node->setGroupMemberSet($memberSet); - // We must also clear our cache, just in case - - $this->principalMembershipCache = array(); - - $result[200]['{DAV:}group-member-set'] = null; - unset($propertyDelta['{DAV:}group-member-set']); - - } - - /** - * This method handles HTTP REPORT requests - * - * @param string $reportName - * @param \DOMNode $dom - * @return bool - */ - public function report($reportName, $dom) { - - switch($reportName) { - - case '{DAV:}principal-property-search' : - $this->principalPropertySearchReport($dom); - return false; - case '{DAV:}principal-search-property-set' : - $this->principalSearchPropertySetReport($dom); - return false; - case '{DAV:}expand-property' : - $this->expandPropertyReport($dom); - return false; - - } - - } - - /** - * This event is triggered for any HTTP method that is not known by the - * webserver. - * - * @param string $method - * @param string $uri - * @return bool - */ - public function unknownMethod($method, $uri) { - - if ($method!=='ACL') return; - - $this->httpACL($uri); - return false; - - } - - /** - * This method is responsible for handling the 'ACL' event. - * - * @param string $uri - * @return void - */ - public function httpACL($uri) { - - $body = $this->server->httpRequest->getBody(true); - $dom = DAV\XMLUtil::loadDOMDocument($body); - - $newAcl = - Property\Acl::unserialize($dom->firstChild) - ->getPrivileges(); - - // Normalizing urls - foreach($newAcl as $k=>$newAce) { - $newAcl[$k]['principal'] = $this->server->calculateUri($newAce['principal']); - } - - $node = $this->server->tree->getNodeForPath($uri); - - if (!($node instanceof IACL)) { - throw new DAV\Exception\MethodNotAllowed('This node does not support the ACL method'); - } - - $oldAcl = $this->getACL($node); - - $supportedPrivileges = $this->getFlatPrivilegeSet($node); - - /* Checking if protected principals from the existing principal set are - not overwritten. */ - foreach($oldAcl as $oldAce) { - - if (!isset($oldAce['protected']) || !$oldAce['protected']) continue; - - $found = false; - foreach($newAcl as $newAce) { - if ( - $newAce['privilege'] === $oldAce['privilege'] && - $newAce['principal'] === $oldAce['principal'] && - $newAce['protected'] - ) - $found = true; - } - - if (!$found) - throw new Exception\AceConflict('This resource contained a protected {DAV:}ace, but this privilege did not occur in the ACL request'); - - } - - foreach($newAcl as $newAce) { - - // Do we recognize the privilege - if (!isset($supportedPrivileges[$newAce['privilege']])) { - throw new Exception\NotSupportedPrivilege('The privilege you specified (' . $newAce['privilege'] . ') is not recognized by this server'); - } - - if ($supportedPrivileges[$newAce['privilege']]['abstract']) { - throw new Exception\NoAbstract('The privilege you specified (' . $newAce['privilege'] . ') is an abstract privilege'); - } - - // Looking up the principal - try { - $principal = $this->server->tree->getNodeForPath($newAce['principal']); - } catch (DAV\Exception\NotFound $e) { - throw new Exception\NotRecognizedPrincipal('The specified principal (' . $newAce['principal'] . ') does not exist'); - } - if (!($principal instanceof IPrincipal)) { - throw new Exception\NotRecognizedPrincipal('The specified uri (' . $newAce['principal'] . ') is not a principal'); - } - - } - $node->setACL($newAcl); - - } - - /* }}} */ - - /* Reports {{{ */ - - /** - * The expand-property report is defined in RFC3253 section 3-8. - * - * This report is very similar to a standard PROPFIND. The difference is - * that it has the additional ability to look at properties containing a - * {DAV:}href element, follow that property and grab additional elements - * there. - * - * Other rfc's, such as ACL rely on this report, so it made sense to put - * it in this plugin. - * - * @param \DOMElement $dom - * @return void - */ - protected function expandPropertyReport($dom) { - - $requestedProperties = $this->parseExpandPropertyReportRequest($dom->firstChild->firstChild); - $depth = $this->server->getHTTPDepth(0); - $requestUri = $this->server->getRequestUri(); - - $result = $this->expandProperties($requestUri,$requestedProperties,$depth); - - $dom = new \DOMDocument('1.0','utf-8'); - $dom->formatOutput = true; - $multiStatus = $dom->createElement('d:multistatus'); - $dom->appendChild($multiStatus); - - // Adding in default namespaces - foreach($this->server->xmlNamespaces as $namespace=>$prefix) { - - $multiStatus->setAttribute('xmlns:' . $prefix,$namespace); - - } - - foreach($result as $response) { - $response->serialize($this->server, $multiStatus); - } - - $xml = $dom->saveXML(); - $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->server->httpResponse->sendStatus(207); - $this->server->httpResponse->sendBody($xml); - - } - - /** - * This method is used by expandPropertyReport to parse - * out the entire HTTP request. - * - * @param \DOMElement $node - * @return array - */ - protected function parseExpandPropertyReportRequest($node) { - - $requestedProperties = array(); - do { - - if (DAV\XMLUtil::toClarkNotation($node)!=='{DAV:}property') continue; - - if ($node->firstChild) { - - $children = $this->parseExpandPropertyReportRequest($node->firstChild); - - } else { - - $children = array(); - - } - - $namespace = $node->getAttribute('namespace'); - if (!$namespace) $namespace = 'DAV:'; - - $propName = '{'.$namespace.'}' . $node->getAttribute('name'); - $requestedProperties[$propName] = $children; - - } while ($node = $node->nextSibling); - - return $requestedProperties; - - } - - /** - * This method expands all the properties and returns - * a list with property values - * - * @param array $path - * @param array $requestedProperties the list of required properties - * @param int $depth - * @return array - */ - protected function expandProperties($path, array $requestedProperties, $depth) { - - $foundProperties = $this->server->getPropertiesForPath($path, array_keys($requestedProperties), $depth); - - $result = array(); - - foreach($foundProperties as $node) { - - foreach($requestedProperties as $propertyName=>$childRequestedProperties) { - - // We're only traversing if sub-properties were requested - if(count($childRequestedProperties)===0) continue; - - // We only have to do the expansion if the property was found - // and it contains an href element. - if (!array_key_exists($propertyName,$node[200])) continue; - - if ($node[200][$propertyName] instanceof DAV\Property\IHref) { - $hrefs = array($node[200][$propertyName]->getHref()); - } elseif ($node[200][$propertyName] instanceof DAV\Property\HrefList) { - $hrefs = $node[200][$propertyName]->getHrefs(); - } - - $childProps = array(); - foreach($hrefs as $href) { - $childProps = array_merge($childProps, $this->expandProperties($href, $childRequestedProperties, 0)); - } - $node[200][$propertyName] = new DAV\Property\ResponseList($childProps); - - } - $result[] = new DAV\Property\Response($node['href'], $node); - - } - - return $result; - - } - - /** - * principalSearchPropertySetReport - * - * This method responsible for handing the - * {DAV:}principal-search-property-set report. This report returns a list - * of properties the client may search on, using the - * {DAV:}principal-property-search report. - * - * @param \DOMDocument $dom - * @return void - */ - protected function principalSearchPropertySetReport(\DOMDocument $dom) { - - $httpDepth = $this->server->getHTTPDepth(0); - if ($httpDepth!==0) { - throw new DAV\Exception\BadRequest('This report is only defined when Depth: 0'); - } - - if ($dom->firstChild->hasChildNodes()) - throw new DAV\Exception\BadRequest('The principal-search-property-set report element is not allowed to have child elements'); - - $dom = new \DOMDocument('1.0','utf-8'); - $dom->formatOutput = true; - $root = $dom->createElement('d:principal-search-property-set'); - $dom->appendChild($root); - // Adding in default namespaces - foreach($this->server->xmlNamespaces as $namespace=>$prefix) { - - $root->setAttribute('xmlns:' . $prefix,$namespace); - - } - - $nsList = $this->server->xmlNamespaces; - - foreach($this->principalSearchPropertySet as $propertyName=>$description) { - - $psp = $dom->createElement('d:principal-search-property'); - $root->appendChild($psp); - - $prop = $dom->createElement('d:prop'); - $psp->appendChild($prop); - - $propName = null; - preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName); - - $currentProperty = $dom->createElement($nsList[$propName[1]] . ':' . $propName[2]); - $prop->appendChild($currentProperty); - - $descriptionElem = $dom->createElement('d:description'); - $descriptionElem->setAttribute('xml:lang','en'); - $descriptionElem->appendChild($dom->createTextNode($description)); - $psp->appendChild($descriptionElem); - - - } - - $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->server->httpResponse->sendStatus(200); - $this->server->httpResponse->sendBody($dom->saveXML()); - - } - - /** - * principalPropertySearchReport - * - * This method is responsible for handing the - * {DAV:}principal-property-search report. This report can be used for - * clients to search for groups of principals, based on the value of one - * or more properties. - * - * @param \DOMDocument $dom - * @return void - */ - protected function principalPropertySearchReport(\DOMDocument $dom) { - - list($searchProperties, $requestedProperties, $applyToPrincipalCollectionSet) = $this->parsePrincipalPropertySearchReportRequest($dom); - - $uri = null; - if (!$applyToPrincipalCollectionSet) { - $uri = $this->server->getRequestUri(); - } - $result = $this->principalSearch($searchProperties, $requestedProperties, $uri); - - $prefer = $this->server->getHTTPPRefer(); - - $this->server->httpResponse->sendStatus(207); - $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->server->httpResponse->setHeader('Vary','Brief,Prefer'); - $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result, $prefer['return-minimal'])); - - } - - /** - * parsePrincipalPropertySearchReportRequest - * - * This method parses the request body from a - * {DAV:}principal-property-search report. - * - * This method returns an array with two elements: - * 1. an array with properties to search on, and their values - * 2. a list of propertyvalues that should be returned for the request. - * - * @param \DOMDocument $dom - * @return array - */ - protected function parsePrincipalPropertySearchReportRequest($dom) { - - $httpDepth = $this->server->getHTTPDepth(0); - if ($httpDepth!==0) { - throw new DAV\Exception\BadRequest('This report is only defined when Depth: 0'); - } - - $searchProperties = array(); - - $applyToPrincipalCollectionSet = false; - - // Parsing the search request - foreach($dom->firstChild->childNodes as $searchNode) { - - if (DAV\XMLUtil::toClarkNotation($searchNode) == '{DAV:}apply-to-principal-collection-set') { - $applyToPrincipalCollectionSet = true; - } - - if (DAV\XMLUtil::toClarkNotation($searchNode)!=='{DAV:}property-search') - continue; - - $propertyName = null; - $propertyValue = null; - - foreach($searchNode->childNodes as $childNode) { - - switch(DAV\XMLUtil::toClarkNotation($childNode)) { - - case '{DAV:}prop' : - $property = DAV\XMLUtil::parseProperties($searchNode); - reset($property); - $propertyName = key($property); - break; - - case '{DAV:}match' : - $propertyValue = $childNode->textContent; - break; - - } - - - } - - if (is_null($propertyName) || is_null($propertyValue)) - throw new DAV\Exception\BadRequest('Invalid search request. propertyname: ' . $propertyName . '. propertvvalue: ' . $propertyValue); - - $searchProperties[$propertyName] = $propertyValue; - - } - - return array($searchProperties, array_keys(DAV\XMLUtil::parseProperties($dom->firstChild)), $applyToPrincipalCollectionSet); - - } - - - /* }}} */ - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Principal.php b/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Principal.php deleted file mode 100644 index 89277f8508..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Principal.php +++ /dev/null @@ -1,281 +0,0 @@ -principalBackend = $principalBackend; - $this->principalProperties = $principalProperties; - - } - - /** - * Returns the full principal url - * - * @return string - */ - public function getPrincipalUrl() { - - return $this->principalProperties['uri']; - - } - - /** - * Returns a list of alternative urls for a principal - * - * This can for example be an email address, or ldap url. - * - * @return array - */ - public function getAlternateUriSet() { - - $uris = array(); - if (isset($this->principalProperties['{DAV:}alternate-URI-set'])) { - - $uris = $this->principalProperties['{DAV:}alternate-URI-set']; - - } - - if (isset($this->principalProperties['{http://sabredav.org/ns}email-address'])) { - $uris[] = 'mailto:' . $this->principalProperties['{http://sabredav.org/ns}email-address']; - } - - return array_unique($uris); - - } - - /** - * Returns the list of group members - * - * If this principal is a group, this function should return - * all member principal uri's for the group. - * - * @return array - */ - public function getGroupMemberSet() { - - return $this->principalBackend->getGroupMemberSet($this->principalProperties['uri']); - - } - - /** - * Returns the list of groups this principal is member of - * - * If this principal is a member of a (list of) groups, this function - * should return a list of principal uri's for it's members. - * - * @return array - */ - public function getGroupMembership() { - - return $this->principalBackend->getGroupMemberShip($this->principalProperties['uri']); - - } - - - /** - * Sets a list of group members - * - * If this principal is a group, this method sets all the group members. - * The list of members is always overwritten, never appended to. - * - * This method should throw an exception if the members could not be set. - * - * @param array $groupMembers - * @return void - */ - public function setGroupMemberSet(array $groupMembers) { - - $this->principalBackend->setGroupMemberSet($this->principalProperties['uri'], $groupMembers); - - } - - - /** - * Returns this principals name. - * - * @return string - */ - public function getName() { - - $uri = $this->principalProperties['uri']; - list(, $name) = DAV\URLUtil::splitPath($uri); - return $name; - - } - - /** - * Returns the name of the user - * - * @return string - */ - public function getDisplayName() { - - if (isset($this->principalProperties['{DAV:}displayname'])) { - return $this->principalProperties['{DAV:}displayname']; - } else { - return $this->getName(); - } - - } - - /** - * Returns a list of properties - * - * @param array $requestedProperties - * @return array - */ - public function getProperties($requestedProperties) { - - $newProperties = array(); - foreach($requestedProperties as $propName) { - - if (isset($this->principalProperties[$propName])) { - $newProperties[$propName] = $this->principalProperties[$propName]; - } - - } - - return $newProperties; - - } - - /** - * Updates this principals properties. - * - * @param array $mutations - * @see Sabre\DAV\IProperties::updateProperties - * @return bool|array - */ - public function updateProperties($mutations) { - - return $this->principalBackend->updatePrincipal($this->principalProperties['uri'], $mutations); - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->principalProperties['uri']; - - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - return array( - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->getPrincipalUrl(), - 'protected' => true, - ), - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new DAV\Exception\MethodNotAllowed('Updating ACLs is not allowed here'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - return null; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/PrincipalBackend/AbstractBackend.php b/core/src/core/classes/sabredav/lib/Sabre/DAVACL/PrincipalBackend/AbstractBackend.php deleted file mode 100644 index 984f9ad82f..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/PrincipalBackend/AbstractBackend.php +++ /dev/null @@ -1,18 +0,0 @@ - array( - * '{DAV:}prop1' => null, - * ), - * 201 => array( - * '{DAV:}prop2' => null, - * ), - * 403 => array( - * '{DAV:}prop3' => null, - * ), - * 424 => array( - * '{DAV:}prop4' => null, - * ), - * ); - * - * In this previous example prop1 was successfully updated or deleted, and - * prop2 was succesfully created. - * - * prop3 failed to update due to '403 Forbidden' and because of this prop4 - * also could not be updated with '424 Failed dependency'. - * - * This last example was actually incorrect. While 200 and 201 could appear - * in 1 response, if there's any error (403) the other properties should - * always fail with 423 (failed dependency). - * - * But anyway, if you don't want to scratch your head over this, just - * return true or false. - * - * @param string $path - * @param array $mutations - * @return array|bool - */ - function updatePrincipal($path, $mutations); - - /** - * This method is used to search for principals matching a set of - * properties. - * - * This search is specifically used by RFC3744's principal-property-search - * REPORT. You should at least allow searching on - * http://sabredav.org/ns}email-address. - * - * The actual search should be a unicode-non-case-sensitive search. The - * keys in searchProperties are the WebDAV property names, while the values - * are the property values to search on. - * - * If multiple properties are being searched on, the search should be - * AND'ed. - * - * This method should simply return an array with full principal uri's. - * - * If somebody attempted to search on a property the backend does not - * support, you should simply return 0 results. - * - * You can also just return 0 results if you choose to not support - * searching at all, but keep in mind that this may stop certain features - * from working. - * - * @param string $prefixPath - * @param array $searchProperties - * @return array - */ - function searchPrincipals($prefixPath, array $searchProperties); - - /** - * Returns the list of members for a group-principal - * - * @param string $principal - * @return array - */ - function getGroupMemberSet($principal); - - /** - * Returns the list of groups a principal is a member of - * - * @param string $principal - * @return array - */ - function getGroupMembership($principal); - - /** - * Updates the list of group members for a group principal. - * - * The principals should be passed as a list of uri's. - * - * @param string $principal - * @param array $members - * @return void - */ - function setGroupMemberSet($principal, array $members); - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/PrincipalBackend/PDO.php b/core/src/core/classes/sabredav/lib/Sabre/DAVACL/PrincipalBackend/PDO.php deleted file mode 100644 index 0921768c37..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/PrincipalBackend/PDO.php +++ /dev/null @@ -1,428 +0,0 @@ - array( - 'dbField' => 'displayname', - ), - - /** - * This property is actually used by the CardDAV plugin, where it gets - * mapped to {http://calendarserver.orgi/ns/}me-card. - * - * The reason we don't straight-up use that property, is because - * me-card is defined as a property on the users' addressbook - * collection. - */ - '{http://sabredav.org/ns}vcard-url' => array( - 'dbField' => 'vcardurl', - ), - /** - * This is the users' primary email-address. - */ - '{http://sabredav.org/ns}email-address' => array( - 'dbField' => 'email', - ), - ); - - /** - * Sets up the backend. - * - * @param PDO $pdo - * @param string $tableName - * @param string $groupMembersTableName - */ - public function __construct(\PDO $pdo, $tableName = 'principals', $groupMembersTableName = 'groupmembers') { - - $this->pdo = $pdo; - $this->tableName = $tableName; - $this->groupMembersTableName = $groupMembersTableName; - - } - - - /** - * Returns a list of principals based on a prefix. - * - * This prefix will often contain something like 'principals'. You are only - * expected to return principals that are in this base path. - * - * You are expected to return at least a 'uri' for every user, you can - * return any additional properties if you wish so. Common properties are: - * {DAV:}displayname - * {http://sabredav.org/ns}email-address - This is a custom SabreDAV - * field that's actualy injected in a number of other properties. If - * you have an email address, use this property. - * - * @param string $prefixPath - * @return array - */ - public function getPrincipalsByPrefix($prefixPath) { - - $fields = array( - 'uri', - ); - - foreach($this->fieldMap as $key=>$value) { - $fields[] = $value['dbField']; - } - $result = $this->pdo->query('SELECT '.implode(',', $fields).' FROM '. $this->tableName); - - $principals = array(); - - while($row = $result->fetch(\PDO::FETCH_ASSOC)) { - - // Checking if the principal is in the prefix - list($rowPrefix) = DAV\URLUtil::splitPath($row['uri']); - if ($rowPrefix !== $prefixPath) continue; - - $principal = array( - 'uri' => $row['uri'], - ); - foreach($this->fieldMap as $key=>$value) { - if ($row[$value['dbField']]) { - $principal[$key] = $row[$value['dbField']]; - } - } - $principals[] = $principal; - - } - - return $principals; - - } - - /** - * Returns a specific principal, specified by it's path. - * The returned structure should be the exact same as from - * getPrincipalsByPrefix. - * - * @param string $path - * @return array - */ - public function getPrincipalByPath($path) { - - $fields = array( - 'id', - 'uri', - ); - - foreach($this->fieldMap as $key=>$value) { - $fields[] = $value['dbField']; - } - $stmt = $this->pdo->prepare('SELECT '.implode(',', $fields).' FROM '. $this->tableName . ' WHERE uri = ?'); - $stmt->execute(array($path)); - - $row = $stmt->fetch(\PDO::FETCH_ASSOC); - if (!$row) return; - - $principal = array( - 'id' => $row['id'], - 'uri' => $row['uri'], - ); - foreach($this->fieldMap as $key=>$value) { - if ($row[$value['dbField']]) { - $principal[$key] = $row[$value['dbField']]; - } - } - return $principal; - - } - - /** - * Updates one ore more webdav properties on a principal. - * - * The list of mutations is supplied as an array. Each key in the array is - * a propertyname, such as {DAV:}displayname. - * - * Each value is the actual value to be updated. If a value is null, it - * must be deleted. - * - * This method should be atomic. It must either completely succeed, or - * completely fail. Success and failure can simply be returned as 'true' or - * 'false'. - * - * It is also possible to return detailed failure information. In that case - * an array such as this should be returned: - * - * array( - * 200 => array( - * '{DAV:}prop1' => null, - * ), - * 201 => array( - * '{DAV:}prop2' => null, - * ), - * 403 => array( - * '{DAV:}prop3' => null, - * ), - * 424 => array( - * '{DAV:}prop4' => null, - * ), - * ); - * - * In this previous example prop1 was successfully updated or deleted, and - * prop2 was succesfully created. - * - * prop3 failed to update due to '403 Forbidden' and because of this prop4 - * also could not be updated with '424 Failed dependency'. - * - * This last example was actually incorrect. While 200 and 201 could appear - * in 1 response, if there's any error (403) the other properties should - * always fail with 423 (failed dependency). - * - * But anyway, if you don't want to scratch your head over this, just - * return true or false. - * - * @param string $path - * @param array $mutations - * @return array|bool - */ - public function updatePrincipal($path, $mutations) { - - $updateAble = array(); - foreach($mutations as $key=>$value) { - - // We are not aware of this field, we must fail. - if (!isset($this->fieldMap[$key])) { - - $response = array( - 403 => array( - $key => null, - ), - 424 => array(), - ); - - // Adding the rest to the response as a 424 - foreach($mutations as $subKey=>$subValue) { - if ($subKey !== $key) { - $response[424][$subKey] = null; - } - } - return $response; - } - - $updateAble[$this->fieldMap[$key]['dbField']] = $value; - - } - - // No fields to update - $query = "UPDATE " . $this->tableName . " SET "; - - $first = true; - foreach($updateAble as $key => $value) { - if (!$first) { - $query.= ', '; - } - $first = false; - $query.= "$key = :$key "; - } - $query.='WHERE uri = :uri'; - $stmt = $this->pdo->prepare($query); - $updateAble['uri'] = $path; - $stmt->execute($updateAble); - - return true; - - } - - /** - * This method is used to search for principals matching a set of - * properties. - * - * This search is specifically used by RFC3744's principal-property-search - * REPORT. You should at least allow searching on - * http://sabredav.org/ns}email-address. - * - * The actual search should be a unicode-non-case-sensitive search. The - * keys in searchProperties are the WebDAV property names, while the values - * are the property values to search on. - * - * If multiple properties are being searched on, the search should be - * AND'ed. - * - * This method should simply return an array with full principal uri's. - * - * If somebody attempted to search on a property the backend does not - * support, you should simply return 0 results. - * - * You can also just return 0 results if you choose to not support - * searching at all, but keep in mind that this may stop certain features - * from working. - * - * @param string $prefixPath - * @param array $searchProperties - * @return array - */ - public function searchPrincipals($prefixPath, array $searchProperties) { - - $query = 'SELECT uri FROM ' . $this->tableName . ' WHERE 1=1 '; - $values = array(); - foreach($searchProperties as $property => $value) { - - switch($property) { - - case '{DAV:}displayname' : - $query.=' AND displayname LIKE ?'; - $values[] = '%' . $value . '%'; - break; - case '{http://sabredav.org/ns}email-address' : - $query.=' AND email LIKE ?'; - $values[] = '%' . $value . '%'; - break; - default : - // Unsupported property - return array(); - - } - - } - $stmt = $this->pdo->prepare($query); - $stmt->execute($values); - - $principals = array(); - while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - - // Checking if the principal is in the prefix - list($rowPrefix) = DAV\URLUtil::splitPath($row['uri']); - if ($rowPrefix !== $prefixPath) continue; - - $principals[] = $row['uri']; - - } - - return $principals; - - } - - /** - * Returns the list of members for a group-principal - * - * @param string $principal - * @return array - */ - public function getGroupMemberSet($principal) { - - $principal = $this->getPrincipalByPath($principal); - if (!$principal) throw new DAV\Exception('Principal not found'); - - $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM '.$this->groupMembersTableName.' AS groupmembers LEFT JOIN '.$this->tableName.' AS principals ON groupmembers.member_id = principals.id WHERE groupmembers.principal_id = ?'); - $stmt->execute(array($principal['id'])); - - $result = array(); - while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - $result[] = $row['uri']; - } - return $result; - - } - - /** - * Returns the list of groups a principal is a member of - * - * @param string $principal - * @return array - */ - public function getGroupMembership($principal) { - - $principal = $this->getPrincipalByPath($principal); - if (!$principal) throw new DAV\Exception('Principal not found'); - - $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM '.$this->groupMembersTableName.' AS groupmembers LEFT JOIN '.$this->tableName.' AS principals ON groupmembers.principal_id = principals.id WHERE groupmembers.member_id = ?'); - $stmt->execute(array($principal['id'])); - - $result = array(); - while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - $result[] = $row['uri']; - } - return $result; - - } - - /** - * Updates the list of group members for a group principal. - * - * The principals should be passed as a list of uri's. - * - * @param string $principal - * @param array $members - * @return void - */ - public function setGroupMemberSet($principal, array $members) { - - // Grabbing the list of principal id's. - $stmt = $this->pdo->prepare('SELECT id, uri FROM '.$this->tableName.' WHERE uri IN (? ' . str_repeat(', ? ', count($members)) . ');'); - $stmt->execute(array_merge(array($principal), $members)); - - $memberIds = array(); - $principalId = null; - - while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - if ($row['uri'] == $principal) { - $principalId = $row['id']; - } else { - $memberIds[] = $row['id']; - } - } - if (!$principalId) throw new DAV\Exception('Principal not found'); - - // Wiping out old members - $stmt = $this->pdo->prepare('DELETE FROM '.$this->groupMembersTableName.' WHERE principal_id = ?;'); - $stmt->execute(array($principalId)); - - foreach($memberIds as $memberId) { - - $stmt = $this->pdo->prepare('INSERT INTO '.$this->groupMembersTableName.' (principal_id, member_id) VALUES (?, ?);'); - $stmt->execute(array($principalId, $memberId)); - - } - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/PrincipalCollection.php b/core/src/core/classes/sabredav/lib/Sabre/DAVACL/PrincipalCollection.php deleted file mode 100644 index 3aadf399d3..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/PrincipalCollection.php +++ /dev/null @@ -1,33 +0,0 @@ -principalBackend, $principal); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Property/Acl.php b/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Property/Acl.php deleted file mode 100644 index e6a70ce911..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Property/Acl.php +++ /dev/null @@ -1,211 +0,0 @@ -privileges = $privileges; - $this->prefixBaseUrl = $prefixBaseUrl; - - } - - /** - * Returns the list of privileges for this property - * - * @return array - */ - public function getPrivileges() { - - return $this->privileges; - - } - - /** - * Serializes the property into a DOMElement - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $node) { - - $doc = $node->ownerDocument; - foreach($this->privileges as $ace) { - - $this->serializeAce($doc, $node, $ace, $server); - - } - - } - - /** - * Unserializes the {DAV:}acl xml element. - * - * @param \DOMElement $dom - * @return Acl - */ - static public function unserialize(\DOMElement $dom) { - - $privileges = array(); - $xaces = $dom->getElementsByTagNameNS('urn:DAV','ace'); - for($ii=0; $ii < $xaces->length; $ii++) { - - $xace = $xaces->item($ii); - $principal = $xace->getElementsByTagNameNS('urn:DAV','principal'); - if ($principal->length !== 1) { - throw new DAV\Exception\BadRequest('Each {DAV:}ace element must have one {DAV:}principal element'); - } - $principal = Principal::unserialize($principal->item(0)); - - switch($principal->getType()) { - case Principal::HREF : - $principal = $principal->getHref(); - break; - case Principal::AUTHENTICATED : - $principal = '{DAV:}authenticated'; - break; - case Principal::UNAUTHENTICATED : - $principal = '{DAV:}unauthenticated'; - break; - case Principal::ALL : - $principal = '{DAV:}all'; - break; - - } - - $protected = false; - - if ($xace->getElementsByTagNameNS('urn:DAV','protected')->length > 0) { - $protected = true; - } - - $grants = $xace->getElementsByTagNameNS('urn:DAV','grant'); - if ($grants->length < 1) { - throw new DAV\Exception\NotImplemented('Every {DAV:}ace element must have a {DAV:}grant element. {DAV:}deny is not yet supported'); - } - $grant = $grants->item(0); - - $xprivs = $grant->getElementsByTagNameNS('urn:DAV','privilege'); - for($jj=0; $jj<$xprivs->length; $jj++) { - - $xpriv = $xprivs->item($jj); - - $privilegeName = null; - - for ($kk=0;$kk<$xpriv->childNodes->length;$kk++) { - - $childNode = $xpriv->childNodes->item($kk); - if ($t = DAV\XMLUtil::toClarkNotation($childNode)) { - $privilegeName = $t; - break; - } - } - if (is_null($privilegeName)) { - throw new DAV\Exception\BadRequest('{DAV:}privilege elements must have a privilege element contained within them.'); - } - - $privileges[] = array( - 'principal' => $principal, - 'protected' => $protected, - 'privilege' => $privilegeName, - ); - - } - - } - - return new self($privileges); - - } - - /** - * Serializes a single access control entry. - * - * @param \DOMDocument $doc - * @param \DOMElement $node - * @param array $ace - * @param DAV\Server $server - * @return void - */ - private function serializeAce($doc,$node,$ace, DAV\Server $server) { - - $xace = $doc->createElementNS('DAV:','d:ace'); - $node->appendChild($xace); - - $principal = $doc->createElementNS('DAV:','d:principal'); - $xace->appendChild($principal); - switch($ace['principal']) { - case '{DAV:}authenticated' : - $principal->appendChild($doc->createElementNS('DAV:','d:authenticated')); - break; - case '{DAV:}unauthenticated' : - $principal->appendChild($doc->createElementNS('DAV:','d:unauthenticated')); - break; - case '{DAV:}all' : - $principal->appendChild($doc->createElementNS('DAV:','d:all')); - break; - default: - $principal->appendChild($doc->createElementNS('DAV:','d:href',($this->prefixBaseUrl?$server->getBaseUri():'') . $ace['principal'] . '/')); - } - - $grant = $doc->createElementNS('DAV:','d:grant'); - $xace->appendChild($grant); - - $privParts = null; - - preg_match('/^{([^}]*)}(.*)$/',$ace['privilege'],$privParts); - - $xprivilege = $doc->createElementNS('DAV:','d:privilege'); - $grant->appendChild($xprivilege); - - $xprivilege->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2])); - - if (isset($ace['protected']) && $ace['protected']) - $xace->appendChild($doc->createElement('d:protected')); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Property/AclRestrictions.php b/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Property/AclRestrictions.php deleted file mode 100644 index aa6fd17d6b..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Property/AclRestrictions.php +++ /dev/null @@ -1,34 +0,0 @@ -ownerDocument; - - $elem->appendChild($doc->createElementNS('DAV:','d:grant-only')); - $elem->appendChild($doc->createElementNS('DAV:','d:no-invert')); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php b/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php deleted file mode 100644 index e0501dbd60..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php +++ /dev/null @@ -1,124 +0,0 @@ -privileges = $privileges; - - } - - /** - * Serializes the property in the DOM - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $node) { - - $doc = $node->ownerDocument; - foreach($this->privileges as $privName) { - - $this->serializePriv($doc,$node,$privName); - - } - - } - - /** - * Returns true or false, whether the specified principal appears in the - * list. - * - * @return bool - */ - public function has($privilegeName) { - - return in_array($privilegeName, $this->privileges); - - } - - /** - * Serializes one privilege - * - * @param \DOMDocument $doc - * @param \DOMElement $node - * @param string $privName - * @return void - */ - protected function serializePriv($doc,$node,$privName) { - - $xp = $doc->createElementNS('DAV:','d:privilege'); - $node->appendChild($xp); - - $privParts = null; - preg_match('/^{([^}]*)}(.*)$/',$privName,$privParts); - - $xp->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2])); - - } - - /** - * Unserializes the {DAV:}current-user-privilege-set element. - * - * @param DOMElement $node - * @return CurrentUserPrivilegeSet - */ - static public function unserialize(\DOMElement $node) { - - $result = array(); - - $xprivs = $node->getElementsByTagNameNS('urn:DAV','privilege'); - - for($jj=0; $jj<$xprivs->length; $jj++) { - - $xpriv = $xprivs->item($jj); - - $privilegeName = null; - - for ($kk=0;$kk<$xpriv->childNodes->length;$kk++) { - - $childNode = $xpriv->childNodes->item($kk); - if ($t = DAV\XMLUtil::toClarkNotation($childNode)) { - $privilegeName = $t; - break; - } - } - - $result[] = $privilegeName; - - } - - return new self($result); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Property/Principal.php b/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Property/Principal.php deleted file mode 100644 index 6c644b024a..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Property/Principal.php +++ /dev/null @@ -1,161 +0,0 @@ -type = $type; - - if ($type===self::HREF && is_null($href)) { - throw new DAV\Exception('The href argument must be specified for the HREF principal type.'); - } - $this->href = $href; - - } - - /** - * Returns the principal type - * - * @return int - */ - public function getType() { - - return $this->type; - - } - - /** - * Returns the principal uri. - * - * @return string - */ - public function getHref() { - - return $this->href; - - } - - /** - * Serializes the property into a DOMElement. - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $node) { - - $prefix = $server->xmlNamespaces['DAV:']; - switch($this->type) { - - case self::UNAUTHENTICATED : - $node->appendChild( - $node->ownerDocument->createElement($prefix . ':unauthenticated') - ); - break; - case self::AUTHENTICATED : - $node->appendChild( - $node->ownerDocument->createElement($prefix . ':authenticated') - ); - break; - case self::HREF : - $href = $node->ownerDocument->createElement($prefix . ':href'); - $href->nodeValue = $server->getBaseUri() . DAV\URLUtil::encodePath($this->href); - $node->appendChild($href); - break; - - } - - } - - /** - * Deserializes a DOM element into a property object. - * - * @param \DOMElement $dom - * @return Principal - */ - static public function unserialize(\DOMElement $dom) { - - $parent = $dom->firstChild; - while(!DAV\XMLUtil::toClarkNotation($parent)) { - $parent = $parent->nextSibling; - } - - switch(DAV\XMLUtil::toClarkNotation($parent)) { - - case '{DAV:}unauthenticated' : - return new self(self::UNAUTHENTICATED); - case '{DAV:}authenticated' : - return new self(self::AUTHENTICATED); - case '{DAV:}href': - return new self(self::HREF, $parent->textContent); - case '{DAV:}all': - return new self(self::ALL); - default : - throw new DAV\Exception\BadRequest('Unexpected element (' . DAV\XMLUtil::toClarkNotation($parent) . '). Could not deserialize'); - - } - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Property/SupportedPrivilegeSet.php b/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Property/SupportedPrivilegeSet.php deleted file mode 100644 index 5f152d9e5e..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Property/SupportedPrivilegeSet.php +++ /dev/null @@ -1,94 +0,0 @@ -privileges = $privileges; - - } - - /** - * Serializes the property into a domdocument. - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $node) { - - $doc = $node->ownerDocument; - $this->serializePriv($doc, $node, $this->privileges); - - } - - /** - * Serializes a property - * - * This is a recursive function. - * - * @param \DOMDocument $doc - * @param \DOMElement $node - * @param array $privilege - * @return void - */ - private function serializePriv($doc,$node,$privilege) { - - $xsp = $doc->createElementNS('DAV:','d:supported-privilege'); - $node->appendChild($xsp); - - $xp = $doc->createElementNS('DAV:','d:privilege'); - $xsp->appendChild($xp); - - $privParts = null; - preg_match('/^{([^}]*)}(.*)$/',$privilege['privilege'],$privParts); - - $xp->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2])); - - if (isset($privilege['abstract']) && $privilege['abstract']) { - $xsp->appendChild($doc->createElementNS('DAV:','d:abstract')); - } - - if (isset($privilege['description'])) { - $xsp->appendChild($doc->createElementNS('DAV:','d:description',$privilege['description'])); - } - - if (isset($privilege['aggregates'])) { - foreach($privilege['aggregates'] as $subPrivilege) { - $this->serializePriv($doc,$xsp,$subPrivilege); - } - } - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Version.php b/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Version.php deleted file mode 100644 index 344e22d7bd..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/DAVACL/Version.php +++ /dev/null @@ -1,24 +0,0 @@ -httpRequest->getHeader('Authorization'); - $authHeader = explode(' ',$authHeader); - - if ($authHeader[0]!='AWS' || !isset($authHeader[1])) { - $this->errorCode = self::ERR_NOAWSHEADER; - return false; - } - - list($this->accessKey,$this->signature) = explode(':',$authHeader[1]); - - return true; - - } - - /** - * Returns the username for the request - * - * @return string - */ - public function getAccessKey() { - - return $this->accessKey; - - } - - /** - * Validates the signature based on the secretKey - * - * @param string $secretKey - * @return bool - */ - public function validate($secretKey) { - - $contentMD5 = $this->httpRequest->getHeader('Content-MD5'); - - if ($contentMD5) { - // We need to validate the integrity of the request - $body = $this->httpRequest->getBody(true); - $this->httpRequest->setBody($body,true); - - if ($contentMD5!=base64_encode(md5($body,true))) { - // content-md5 header did not match md5 signature of body - $this->errorCode = self::ERR_MD5CHECKSUMWRONG; - return false; - } - - } - - if (!$requestDate = $this->httpRequest->getHeader('x-amz-date')) - $requestDate = $this->httpRequest->getHeader('Date'); - - if (!$this->validateRFC2616Date($requestDate)) - return false; - - $amzHeaders = $this->getAmzHeaders(); - - $signature = base64_encode( - $this->hmacsha1($secretKey, - $this->httpRequest->getMethod() . "\n" . - $contentMD5 . "\n" . - $this->httpRequest->getHeader('Content-type') . "\n" . - $requestDate . "\n" . - $amzHeaders . - $this->httpRequest->getURI() - ) - ); - - if ($this->signature != $signature) { - - $this->errorCode = self::ERR_INVALIDSIGNATURE; - return false; - - } - - return true; - - } - - - /** - * Returns an HTTP 401 header, forcing login - * - * This should be called when username and password are incorrect, or not supplied at all - * - * @return void - */ - public function requireLogin() { - - $this->httpResponse->setHeader('WWW-Authenticate','AWS'); - $this->httpResponse->sendStatus(401); - - } - - /** - * Makes sure the supplied value is a valid RFC2616 date. - * - * If we would just use strtotime to get a valid timestamp, we have no way of checking if a - * user just supplied the word 'now' for the date header. - * - * This function also makes sure the Date header is within 15 minutes of the operating - * system date, to prevent replay attacks. - * - * @param string $dateHeader - * @return bool - */ - protected function validateRFC2616Date($dateHeader) { - - $date = Util::parseHTTPDate($dateHeader); - - // Unknown format - if (!$date) { - $this->errorCode = self::ERR_INVALIDDATEFORMAT; - return false; - } - - $min = new \DateTime('-15 minutes'); - $max = new \DateTime('+15 minutes'); - - // We allow 15 minutes around the current date/time - if ($date > $max || $date < $min) { - $this->errorCode = self::ERR_REQUESTTIMESKEWED; - return false; - } - - return $date; - - } - - /** - * Returns a list of AMZ headers - * - * @return string - */ - protected function getAmzHeaders() { - - $amzHeaders = array(); - $headers = $this->httpRequest->getHeaders(); - foreach($headers as $headerName => $headerValue) { - if (strpos(strtolower($headerName),'x-amz-')===0) { - $amzHeaders[strtolower($headerName)] = str_replace(array("\r\n"),array(' '),$headerValue) . "\n"; - } - } - ksort($amzHeaders); - - $headerStr = ''; - foreach($amzHeaders as $h=>$v) { - $headerStr.=$h.':'.$v; - } - - return $headerStr; - - } - - /** - * Generates an HMAC-SHA1 signature - * - * @param string $key - * @param string $message - * @return string - */ - private function hmacsha1($key, $message) { - - $blocksize=64; - if (strlen($key)>$blocksize) - $key=pack('H*', sha1($key)); - $key=str_pad($key,$blocksize,chr(0x00)); - $ipad=str_repeat(chr(0x36),$blocksize); - $opad=str_repeat(chr(0x5c),$blocksize); - $hmac = pack('H*',sha1(($key^$opad).pack('H*',sha1(($key^$ipad).$message)))); - return $hmac; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/HTTP/AbstractAuth.php b/core/src/core/classes/sabredav/lib/Sabre/HTTP/AbstractAuth.php deleted file mode 100644 index 1ddf412b7e..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/HTTP/AbstractAuth.php +++ /dev/null @@ -1,111 +0,0 @@ -httpResponse = new Response(); - $this->httpRequest = new Request(); - - } - - /** - * Sets an alternative HTTP response object - * - * @param Response $response - * @return void - */ - public function setHTTPResponse(Response $response) { - - $this->httpResponse = $response; - - } - - /** - * Sets an alternative HTTP request object - * - * @param Request $request - * @return void - */ - public function setHTTPRequest(Request $request) { - - $this->httpRequest = $request; - - } - - - /** - * Sets the realm - * - * The realm is often displayed in authentication dialog boxes - * Commonly an application name displayed here - * - * @param string $realm - * @return void - */ - public function setRealm($realm) { - - $this->realm = $realm; - - } - - /** - * Returns the realm - * - * @return string - */ - public function getRealm() { - - return $this->realm; - - } - - /** - * Returns an HTTP 401 header, forcing login - * - * This should be called when username and password are incorrect, or not supplied at all - * - * @return void - */ - abstract public function requireLogin(); - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/HTTP/BasicAuth.php b/core/src/core/classes/sabredav/lib/Sabre/HTTP/BasicAuth.php deleted file mode 100644 index 659964faa1..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/HTTP/BasicAuth.php +++ /dev/null @@ -1,67 +0,0 @@ -httpRequest->getRawServerValue('PHP_AUTH_USER')) && ($pass = $this->httpRequest->getRawServerValue('PHP_AUTH_PW'))) { - - return array($user,$pass); - - } - - // Most other webservers - $auth = $this->httpRequest->getHeader('Authorization'); - - // Apache could prefix environment variables with REDIRECT_ when urls - // are passed through mod_rewrite - if (!$auth) { - $auth = $this->httpRequest->getRawServerValue('REDIRECT_HTTP_AUTHORIZATION'); - } - - if (!$auth) return false; - - if (strpos(strtolower($auth),'basic')!==0) return false; - - return explode(':', base64_decode(substr($auth, 6)),2); - - } - - /** - * Returns an HTTP 401 header, forcing login - * - * This should be called when username and password are incorrect, or not supplied at all - * - * @return void - */ - public function requireLogin() { - - $this->httpResponse->setHeader('WWW-Authenticate','Basic realm="' . $this->realm . '"'); - $this->httpResponse->sendStatus(401); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/HTTP/DigestAuth.php b/core/src/core/classes/sabredav/lib/Sabre/HTTP/DigestAuth.php deleted file mode 100644 index aae6d84d61..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/HTTP/DigestAuth.php +++ /dev/null @@ -1,240 +0,0 @@ -nonce = uniqid(); - $this->opaque = md5($this->realm); - parent::__construct(); - - } - - /** - * Gathers all information from the headers - * - * This method needs to be called prior to anything else. - * - * @return void - */ - public function init() { - - $digest = $this->getDigest(); - $this->digestParts = $this->parseDigest($digest); - - } - - /** - * Sets the quality of protection value. - * - * Possible values are: - * Sabre\HTTP\DigestAuth::QOP_AUTH - * Sabre\HTTP\DigestAuth::QOP_AUTHINT - * - * Multiple values can be specified using logical OR. - * - * QOP_AUTHINT ensures integrity of the request body, but this is not - * supported by most HTTP clients. QOP_AUTHINT also requires the entire - * request body to be md5'ed, which can put strains on CPU and memory. - * - * @param int $qop - * @return void - */ - public function setQOP($qop) { - - $this->qop = $qop; - - } - - /** - * Validates the user. - * - * The A1 parameter should be md5($username . ':' . $realm . ':' . $password); - * - * @param string $A1 - * @return bool - */ - public function validateA1($A1) { - - $this->A1 = $A1; - return $this->validate(); - - } - - /** - * Validates authentication through a password. The actual password must be provided here. - * It is strongly recommended not store the password in plain-text and use validateA1 instead. - * - * @param string $password - * @return bool - */ - public function validatePassword($password) { - - $this->A1 = md5($this->digestParts['username'] . ':' . $this->realm . ':' . $password); - return $this->validate(); - - } - - /** - * Returns the username for the request - * - * @return string - */ - public function getUsername() { - - return $this->digestParts['username']; - - } - - /** - * Validates the digest challenge - * - * @return bool - */ - protected function validate() { - - $A2 = $this->httpRequest->getMethod() . ':' . $this->digestParts['uri']; - - if ($this->digestParts['qop']=='auth-int') { - // Making sure we support this qop value - if (!($this->qop & self::QOP_AUTHINT)) return false; - // We need to add an md5 of the entire request body to the A2 part of the hash - $body = $this->httpRequest->getBody(true); - $this->httpRequest->setBody($body,true); - $A2 .= ':' . md5($body); - } else { - - // We need to make sure we support this qop value - if (!($this->qop & self::QOP_AUTH)) return false; - } - - $A2 = md5($A2); - - $validResponse = md5("{$this->A1}:{$this->digestParts['nonce']}:{$this->digestParts['nc']}:{$this->digestParts['cnonce']}:{$this->digestParts['qop']}:{$A2}"); - - return $this->digestParts['response']==$validResponse; - - - } - - /** - * Returns an HTTP 401 header, forcing login - * - * This should be called when username and password are incorrect, or not supplied at all - * - * @return void - */ - public function requireLogin() { - - $qop = ''; - switch($this->qop) { - case self::QOP_AUTH : $qop = 'auth'; break; - case self::QOP_AUTHINT : $qop = 'auth-int'; break; - case self::QOP_AUTH | self::QOP_AUTHINT : $qop = 'auth,auth-int'; break; - } - - $this->httpResponse->setHeader('WWW-Authenticate','Digest realm="' . $this->realm . '",qop="'.$qop.'",nonce="' . $this->nonce . '",opaque="' . $this->opaque . '"'); - $this->httpResponse->sendStatus(401); - - } - - - /** - * This method returns the full digest string. - * - * It should be compatibile with mod_php format and other webservers. - * - * If the header could not be found, null will be returned - * - * @return mixed - */ - public function getDigest() { - - // mod_php - $digest = $this->httpRequest->getRawServerValue('PHP_AUTH_DIGEST'); - if ($digest) return $digest; - - // most other servers - $digest = $this->httpRequest->getHeader('Authorization'); - - // Apache could prefix environment variables with REDIRECT_ when urls - // are passed through mod_rewrite - if (!$digest) { - $digest = $this->httpRequest->getRawServerValue('REDIRECT_HTTP_AUTHORIZATION'); - } - - if ($digest && strpos(strtolower($digest),'digest')===0) { - return substr($digest,7); - } else { - return null; - } - - } - - - /** - * Parses the different pieces of the digest string into an array. - * - * This method returns false if an incomplete digest was supplied - * - * @param string $digest - * @return mixed - */ - protected function parseDigest($digest) { - - // protect against missing data - $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1); - $data = array(); - - preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $digest, $matches, PREG_SET_ORDER); - - foreach ($matches as $m) { - $data[$m[1]] = $m[2] ? $m[2] : $m[3]; - unset($needed_parts[$m[1]]); - } - - return $needed_parts ? false : $data; - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/HTTP/Request.php b/core/src/core/classes/sabredav/lib/Sabre/HTTP/Request.php deleted file mode 100644 index a71a52b421..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/HTTP/Request.php +++ /dev/null @@ -1,284 +0,0 @@ -_SERVER = $serverData; - else $this->_SERVER =& $_SERVER; - - if ($postData) $this->_POST = $postData; - else $this->_POST =& $_POST; - - } - - /** - * Returns the value for a specific http header. - * - * This method returns null if the header did not exist. - * - * @param string $name - * @return string - */ - public function getHeader($name) { - - $name = strtoupper(str_replace(array('-'),array('_'),$name)); - if (isset($this->_SERVER['HTTP_' . $name])) { - return $this->_SERVER['HTTP_' . $name]; - } - - // There's a few headers that seem to end up in the top-level - // server array. - switch($name) { - case 'CONTENT_TYPE' : - case 'CONTENT_LENGTH' : - if (isset($this->_SERVER[$name])) { - return $this->_SERVER[$name]; - } - break; - - } - return; - - } - - /** - * Returns all (known) HTTP headers. - * - * All headers are converted to lower-case, and additionally all underscores - * are automatically converted to dashes - * - * @return array - */ - public function getHeaders() { - - $hdrs = array(); - foreach($this->_SERVER as $key=>$value) { - - switch($key) { - case 'CONTENT_LENGTH' : - case 'CONTENT_TYPE' : - $hdrs[strtolower(str_replace('_','-',$key))] = $value; - break; - default : - if (strpos($key,'HTTP_')===0) { - $hdrs[substr(strtolower(str_replace('_','-',$key)),5)] = $value; - } - break; - } - - } - - return $hdrs; - - } - - /** - * Returns the HTTP request method - * - * This is for example POST or GET - * - * @return string - */ - public function getMethod() { - - return $this->_SERVER['REQUEST_METHOD']; - - } - - /** - * Returns the requested uri - * - * @return string - */ - public function getUri() { - - return $this->_SERVER['REQUEST_URI']; - - } - - /** - * Will return protocol + the hostname + the uri - * - * @return string - */ - public function getAbsoluteUri() { - - // Checking if the request was made through HTTPS. The last in line is for IIS - $protocol = isset($this->_SERVER['HTTPS']) && ($this->_SERVER['HTTPS']) && ($this->_SERVER['HTTPS']!='off'); - return ($protocol?'https':'http') . '://' . $this->getHeader('Host') . $this->getUri(); - - } - - /** - * Returns everything after the ? from the current url - * - * @return string - */ - public function getQueryString() { - - return isset($this->_SERVER['QUERY_STRING'])?$this->_SERVER['QUERY_STRING']:''; - - } - - /** - * Returns the HTTP request body body - * - * This method returns a readable stream resource. - * If the asString parameter is set to true, a string is sent instead. - * - * @param bool $asString - * @return resource - */ - public function getBody($asString = false) { - - if (is_null($this->body)) { - if (!is_null(self::$defaultInputStream)) { - $this->body = self::$defaultInputStream; - } else { - $this->body = fopen('php://input','r'); - self::$defaultInputStream = $this->body; - } - } - if ($asString) { - $body = stream_get_contents($this->body); - return $body; - } else { - return $this->body; - } - - } - - /** - * Sets the contents of the HTTP request body - * - * This method can either accept a string, or a readable stream resource. - * - * If the setAsDefaultInputStream is set to true, it means for this run of the - * script the supplied body will be used instead of php://input. - * - * @param mixed $body - * @param bool $setAsDefaultInputStream - * @return void - */ - public function setBody($body,$setAsDefaultInputStream = false) { - - if(is_resource($body)) { - $this->body = $body; - } else { - - $stream = fopen('php://temp','r+'); - fputs($stream,$body); - rewind($stream); - // String is assumed - $this->body = $stream; - } - if ($setAsDefaultInputStream) { - self::$defaultInputStream = $this->body; - } - - } - - /** - * Returns PHP's _POST variable. - * - * The reason this is in a method is so it can be subclassed and - * overridden. - * - * @return array - */ - public function getPostVars() { - - return $this->_POST; - - } - - /** - * Returns a specific item from the _SERVER array. - * - * Do not rely on this feature, it is for internal use only. - * - * @param string $field - * @return string - */ - public function getRawServerValue($field) { - - return isset($this->_SERVER[$field])?$this->_SERVER[$field]:null; - - } - - /** - * Returns the HTTP version specified within the request. - * - * @return string - */ - public function getHTTPVersion() { - - $protocol = $this->getRawServerValue('SERVER_PROTOCOL'); - if ($protocol==='HTTP/1.0') { - return '1.0'; - } else { - return '1.1'; - } - - } - -} - diff --git a/core/src/core/classes/sabredav/lib/Sabre/HTTP/Response.php b/core/src/core/classes/sabredav/lib/Sabre/HTTP/Response.php deleted file mode 100644 index a7fc0da126..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/HTTP/Response.php +++ /dev/null @@ -1,175 +0,0 @@ - 'Continue', - 101 => 'Switching Protocols', - 102 => 'Processing', - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authorative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - 207 => 'Multi-Status', // RFC 4918 - 208 => 'Already Reported', // RFC 5842 - 226 => 'IM Used', // RFC 3229 - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 306 => 'Reserved', - 307 => 'Temporary Redirect', - 400 => 'Bad request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Long', - 415 => 'Unsupported Media Type', - 416 => 'Requested Range Not Satisfiable', - 417 => 'Expectation Failed', - 418 => 'I\'m a teapot', // RFC 2324 - 422 => 'Unprocessable Entity', // RFC 4918 - 423 => 'Locked', // RFC 4918 - 424 => 'Failed Dependency', // RFC 4918 - 426 => 'Upgrade required', - 428 => 'Precondition required', // draft-nottingham-http-new-status - 429 => 'Too Many Requests', // draft-nottingham-http-new-status - 431 => 'Request Header Fields Too Large', // draft-nottingham-http-new-status - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version not supported', - 506 => 'Variant Also Negotiates', - 507 => 'Insufficient Storage', // RFC 4918 - 508 => 'Loop Detected', // RFC 5842 - 509 => 'Bandwidth Limit Exceeded', // non-standard - 510 => 'Not extended', - 511 => 'Network Authentication Required', // draft-nottingham-http-new-status - ); - - return 'HTTP/' . $httpVersion . ' ' . $code . ' ' . $msg[$code]; - - } - - // @codeCoverageIgnoreStart - // We cannot reasonably test header() related methods. - - /** - * Sends an HTTP status header to the client. - * - * @param int $code HTTP status code - * @return bool - */ - public function sendStatus($code) { - - if (!headers_sent()) - return header($this->getStatusMessage($code, $this->defaultHttpVersion)); - else return false; - - } - - /** - * Sets an HTTP header for the response - * - * @param string $name - * @param string $value - * @param bool $replace - * @return bool - */ - public function setHeader($name, $value, $replace = true) { - - $value = str_replace(array("\r","\n"),array('\r','\n'),$value); - if (!headers_sent()) - return header($name . ': ' . $value, $replace); - else return false; - - - } - // @codeCoverageIgnoreEnd - - /** - * Sets a bunch of HTTP Headers - * - * headersnames are specified as keys, value in the array value - * - * @param array $headers - * @return void - */ - public function setHeaders(array $headers) { - - foreach($headers as $key=>$value) - $this->setHeader($key, $value); - - } - - /** - * Sends the entire response body - * - * This method can accept either an open filestream, or a string. - * - * @param mixed $body - * @return void - */ - public function sendBody($body) { - - if (is_resource($body)) { - - file_put_contents('php://output', $body); - - } else { - - // We assume a string - echo $body; - - } - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/HTTP/Util.php b/core/src/core/classes/sabredav/lib/Sabre/HTTP/Util.php deleted file mode 100644 index 1472462538..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/HTTP/Util.php +++ /dev/null @@ -1,82 +0,0 @@ -= 0) - return new \DateTime('@' . $realDate, new \DateTimeZone('UTC')); - - } - - /** - * Transforms a DateTime object to HTTP's most common date format. - * - * We're serializing it as the RFC 1123 date, which, for HTTP must be - * specified as GMT. - * - * @param \DateTime $dateTime - * @return string - */ - static function toHTTPDate(\DateTime $dateTime) { - - // We need to clone it, as we don't want to affect the existing - // DateTime. - $dateTime = clone $dateTime; - $dateTime->setTimeZone(new \DateTimeZone('GMT')); - return $dateTime->format('D, d M Y H:i:s \G\M\T'); - - } - -} diff --git a/core/src/core/classes/sabredav/lib/Sabre/HTTP/Version.php b/core/src/core/classes/sabredav/lib/Sabre/HTTP/Version.php deleted file mode 100644 index 0e913835ce..0000000000 --- a/core/src/core/classes/sabredav/lib/Sabre/HTTP/Version.php +++ /dev/null @@ -1,24 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Autoload; - -/** - * ClassLoader implements a PSR-0 class loader - * - * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md - * - * $loader = new \Composer\Autoload\ClassLoader(); - * - * // register classes with namespaces - * $loader->add('Symfony\Component', __DIR__.'/component'); - * $loader->add('Symfony', __DIR__.'/framework'); - * - * // activate the autoloader - * $loader->register(); - * - * // to enable searching the include path (eg. for PEAR packages) - * $loader->setUseIncludePath(true); - * - * In this example, if you try to use a class in the Symfony\Component - * namespace or one of its children (Symfony\Component\Console for instance), - * the autoloader will first look for the class under the component/ - * directory, and it will then fallback to the framework/ directory if not - * found before giving up. - * - * This class is loosely based on the Symfony UniversalClassLoader. - * - * @author Fabien Potencier - * @author Jordi Boggiano - */ -class ClassLoader -{ - private $prefixes = array(); - private $fallbackDirs = array(); - private $useIncludePath = false; - private $classMap = array(); - - public function getPrefixes() - { - return $this->prefixes; - } - - public function getFallbackDirs() - { - return $this->fallbackDirs; - } - - public function getClassMap() - { - return $this->classMap; - } - - /** - * @param array $classMap Class to filename map - */ - public function addClassMap(array $classMap) - { - if ($this->classMap) { - $this->classMap = array_merge($this->classMap, $classMap); - } else { - $this->classMap = $classMap; - } - } - - /** - * Registers a set of classes, merging with any others previously set. - * - * @param string $prefix The classes prefix - * @param array|string $paths The location(s) of the classes - * @param bool $prepend Prepend the location(s) - */ - public function add($prefix, $paths, $prepend = false) - { - if (!$prefix) { - if ($prepend) { - $this->fallbackDirs = array_merge( - (array) $paths, - $this->fallbackDirs - ); - } else { - $this->fallbackDirs = array_merge( - $this->fallbackDirs, - (array) $paths - ); - } - - return; - } - if (!isset($this->prefixes[$prefix])) { - $this->prefixes[$prefix] = (array) $paths; - - return; - } - if ($prepend) { - $this->prefixes[$prefix] = array_merge( - (array) $paths, - $this->prefixes[$prefix] - ); - } else { - $this->prefixes[$prefix] = array_merge( - $this->prefixes[$prefix], - (array) $paths - ); - } - } - - /** - * Registers a set of classes, replacing any others previously set. - * - * @param string $prefix The classes prefix - * @param array|string $paths The location(s) of the classes - */ - public function set($prefix, $paths) - { - if (!$prefix) { - $this->fallbackDirs = (array) $paths; - - return; - } - $this->prefixes[$prefix] = (array) $paths; - } - - /** - * Turns on searching the include path for class files. - * - * @param bool $useIncludePath - */ - public function setUseIncludePath($useIncludePath) - { - $this->useIncludePath = $useIncludePath; - } - - /** - * Can be used to check if the autoloader uses the include path to check - * for classes. - * - * @return bool - */ - public function getUseIncludePath() - { - return $this->useIncludePath; - } - - /** - * Registers this instance as an autoloader. - * - * @param bool $prepend Whether to prepend the autoloader or not - */ - public function register($prepend = false) - { - spl_autoload_register(array($this, 'loadClass'), true, $prepend); - } - - /** - * Unregisters this instance as an autoloader. - */ - public function unregister() - { - spl_autoload_unregister(array($this, 'loadClass')); - } - - /** - * Loads the given class or interface. - * - * @param string $class The name of the class - * @return bool|null True, if loaded - */ - public function loadClass($class) - { - if ($file = $this->findFile($class)) { - include $file; - - return true; - } - } - - /** - * Finds the path to the file where the class is defined. - * - * @param string $class The name of the class - * - * @return string|null The path, if found - */ - public function findFile($class) - { - if ('\\' == $class[0]) { - $class = substr($class, 1); - } - - if (isset($this->classMap[$class])) { - return $this->classMap[$class]; - } - - if (false !== $pos = strrpos($class, '\\')) { - // namespaced class name - $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)) . DIRECTORY_SEPARATOR; - $className = substr($class, $pos + 1); - } else { - // PEAR-like class name - $classPath = null; - $className = $class; - } - - $classPath .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php'; - - foreach ($this->prefixes as $prefix => $dirs) { - if (0 === strpos($class, $prefix)) { - foreach ($dirs as $dir) { - if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) { - return $dir . DIRECTORY_SEPARATOR . $classPath; - } - } - } - } - - foreach ($this->fallbackDirs as $dir) { - if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) { - return $dir . DIRECTORY_SEPARATOR . $classPath; - } - } - - if ($this->useIncludePath && $file = stream_resolve_include_path($classPath)) { - return $file; - } - - return $this->classMap[$class] = false; - } -} diff --git a/core/src/core/classes/sabredav/vendor/composer/autoload_classmap.php b/core/src/core/classes/sabredav/vendor/composer/autoload_classmap.php deleted file mode 100644 index 4a9177d7fe..0000000000 --- a/core/src/core/classes/sabredav/vendor/composer/autoload_classmap.php +++ /dev/null @@ -1,9 +0,0 @@ - $vendorDir . '/sabre/vobject/lib/', - 'Sabre\\HTTP' => $baseDir . '/lib/', - 'Sabre\\DAVACL' => $baseDir . '/lib/', - 'Sabre\\DAV' => $baseDir . '/lib/', - 'Sabre\\CardDAV' => $baseDir . '/lib/', - 'Sabre\\CalDAV' => $baseDir . '/lib/', -); diff --git a/core/src/core/classes/sabredav/vendor/composer/autoload_real.php b/core/src/core/classes/sabredav/vendor/composer/autoload_real.php deleted file mode 100644 index dbb6a2f6f2..0000000000 --- a/core/src/core/classes/sabredav/vendor/composer/autoload_real.php +++ /dev/null @@ -1,43 +0,0 @@ - $path) { - $loader->add($namespace, $path); - } - - $classMap = require __DIR__ . '/autoload_classmap.php'; - if ($classMap) { - $loader->addClassMap($classMap); - } - - $loader->register(true); - - return $loader; - } -} diff --git a/core/src/core/classes/sabredav/vendor/composer/installed.json b/core/src/core/classes/sabredav/vendor/composer/installed.json deleted file mode 100644 index 9c2e87b949..0000000000 --- a/core/src/core/classes/sabredav/vendor/composer/installed.json +++ /dev/null @@ -1,51 +0,0 @@ -[ - { - "name": "sabre/vobject", - "version": "2.0.7", - "version_normalized": "2.0.7.0", - "source": { - "type": "git", - "url": "https://github.com/evert/sabre-vobject.git", - "reference": "2.0.7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/evert/sabre-vobject/zipball/2.0.7", - "reference": "2.0.7", - "shasum": "" - }, - "require": { - "php": ">=5.3.1" - }, - "time": "2013-03-05 12:16:55", - "bin": [ - "bin/vobjectvalidate.php" - ], - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-0": { - "Sabre\\VObject": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Evert Pot", - "email": "evert@rooftopsolutions.nl", - "homepage": "http://www.rooftopsolutions.nl/", - "role": "Developer" - } - ], - "description": "The VObject library for PHP allows you to easily parse and manipulate iCalendar and vCard objects", - "homepage": "https://github.com/evert/sabre-vobject", - "keywords": [ - "VObject", - "iCalendar", - "vCard" - ] - } -] diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/.travis.yml b/core/src/core/classes/sabredav/vendor/sabre/vobject/.travis.yml deleted file mode 100644 index 6bf8b75929..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: php -php: - - 5.3 - - 5.4 - - 5.5 - -script: phpunit --configuration tests/phpunit.xml - -before_script: composer install diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/ChangeLog b/core/src/core/classes/sabredav/vendor/sabre/vobject/ChangeLog deleted file mode 100644 index 2c8cd212c9..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/ChangeLog +++ /dev/null @@ -1,56 +0,0 @@ -2.0.7-stable (2013-03-05) - * Fixed: Microsoft re-uses their magic numbers for different timezones, - specifically id 2 for both Sarajevo and Lisbon). A workaround was added - to deal with this. - -2.0.6-stable (2013-02-17) - * Fixed: The reader now properly parses parameters without a value. - -2.0.5-stable (2012-11-05) - * Fixed: The FreeBusyGenerator is now properly using the factory methods - for creation of components and properties. - -2.0.4-stable (2012-11-02) - * Added: Known Lotus Notes / Domino timezone id's. - -2.0.3-stable (2012-10-29) - * Added: Support for 'GMT+????' format in TZID's. - * Added: Support for formats like SystemV/EST5EDT in TZID's. - * Fixed: RecurrenceIterator now repairs recurrence rules where UNTIL < DTSTART. - * Added: Support for BYHOUR in FREQ=DAILY (@hollodk). - * Added: Support for BYHOUR and BYDAY in FREQ=WEEKLY. - -2.0.2-stable (2012-10-06) - * Added: includes.php file, to load the entire library in one go. - * Fixed: A problem with determining alarm triggers for TODO's. - -2.0.1-stable (2012-09-22) - * Removed: Element class. It wasn't used. - * Added: Basic validation and repair methods for broken input data. - * Fixed: RecurrenceIterator could infinitely loop when an INTERVAL of 0 - was specified. - * Added: A cli script that can validate and automatically repair vcards - and iCalendar objects. - * Added: A new 'Compound' property, that can automatically split up parts - for properties such as N, ADR, ORG and CATEGORIES. - * Added: Splitter classes, that can split up large objects (such as exports) - into individual objects (thanks @DominikTO and @armin-hackmann). - * Added: VFREEBUSY component, which allows easily checking wether - timeslots are available. - * Added: The Reader class now has a 'FORGIVING' option, which allows it to - parse properties with incorrect characters in the name (at this time, it - just allows underscores). - * Added: Also added the 'IGNORE_INVALID_LINES' option, to completely - disregard any invalid lines. - * Fixed: A bug in Windows timezone-id mappings for times created in - Greenlands timezone (sorry Greenlanders! I do care!). - * Fixed: DTEND was not generated correctly for VFREEBUSY reports. - * Fixed: Parser is at least 25% faster with real-world data. - -2.0.0-stable (2012-08-08) - * VObject is now a separate project from SabreDAV. See the SabreDAV - changelog for version information before 2.0. - * New: VObject library now uses PHP 5.3 namespaces. - * New: It's possible to specify lists of parameters when constructing - properties. - * New: made it easier to construct the FreeBusyGenerator. diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/LICENSE b/core/src/core/classes/sabredav/vendor/sabre/vobject/LICENSE deleted file mode 100644 index b1801509f9..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (C) 2007-2013 Rooftop Solutions. - -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of the Sabre nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/README.md b/core/src/core/classes/sabredav/vendor/sabre/vobject/README.md deleted file mode 100644 index 160ea7a67c..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/README.md +++ /dev/null @@ -1,368 +0,0 @@ -# SabreTooth VObject library - -[![Build Status](https://secure.travis-ci.org/evert/sabre-vobject.png?branch=master)](http://travis-ci.org/evert/sabre-vobject) - -The VObject library allows you to easily parse and manipulate [iCalendar](https://tools.ietf.org/html/rfc5545) -and [vCard](https://tools.ietf.org/html/rfc6350) objects using PHP. -The goal of the VObject library is to create a very complete library, with an easy to use API. - -This project is a spin-off from [SabreDAV](http://code.google.com/p/sabredav/), where it has -been used for several years. The VObject library has 100% unittest coverage. - -# Installation - -VObject requires PHP 5.3, and should be installed using composer. -The general composer instructions can be found on the [composer website](http://getcomposer.org/doc/00-intro.md composer website). - -After that, just declare the vobject dependency as follows: - -``` -"require" : { - "sabre/vobject" : "2.0.*" -} -``` - -Then, run `composer.phar update` and you should be good. - -# Usage - -## Parsing - -For our example, we will be using the following vcard: - -``` -BEGIN:VCARD -VERSION:3.0 -PRODID:-//Sabre//Sabre VObject 2.0//EN -N:Planck;Max;;; -FN:Max Planck -EMAIL;TYPE=WORK:mplanck@example.org -item1.TEL;TYPE=CELL:(+49)3144435678 -item1.X-ABLabel:Private cell -item2.TEL;TYPE=WORK:(+49)5554564744 -item2.X-ABLabel:Work -END:VCARD -``` - - -If we want to just print out Max' full name, you can just use property access: - - -```php -use Sabre\VObject; - -$card = VObject\Reader::read($data); -echo $card->FN; -``` - -## Changing properties - -Creating properties is pretty similar. If we like to add his birthday, we just -set the property: - -```php -$card->BDAY = '1858-04-23'; -``` - -Note that in the previous example, we're actually updating any existing BDAY that -may already exist. If we want to add a new property, without overwriting the previous -we can do this with the `add` method. - -```php -$card->add('EMAIL','max@example.org'); -``` - -## Parameters - -If we want to also specify that this is max' home email addresses, we can do this with -a third parameter: - -``` -$card->add('EMAIL', 'max@example'org', array('type' => 'HOME')); -``` - -If we want to read out all the email addresses and their type, this would look something -like this: - -``` -foreach($card->EMAIL as $email) { - - echo $email['TYPE'], ' - ', $email; - -} -``` - -## Groups - -In our example, you can see that the TEL properties are prefixed. These are 'groups' and -allow you to group multiple related properties together. The group can be any user-defined -name. - -This particular example as generated by the OS X addressbook, which uses the `X-ABLabel` -to allow the user to specify custom labels for properties. OS X addressbook uses groups -to tie the label to the property. - -The VObject library simply ignores the group if you don't specify it, so this will work: - -```php -foreach($card->TEL as $tel) { - echo $tel, "\n"; -} -``` - -But if you would like to target a specific group + property, this is possible too: - -```php -echo $card->{'ITEM1.TEL'}; -``` - -So if you would like to show all the phone numbers, along with their custom label, the -following syntax is used: - -```php -foreach($card->TEL as $tel) { - - echo $card->{$tel->group . '.X-ABLABEL'}, ": "; - echo $tel, "\n"; - -} -``` - -## Serializing / Saving - -If you want to generate your updated VObject, you can simply call the serialize() method. - -```php -echo $card->serialize(); -``` - -## Components - -iCalendar, unlike vCards always have sub-components. Where vCards are often just a flat -list, iCalendar objects tend to have a tree-like structure. For the following paragraphs, -we will use the following object as the example: - -``` -BEGIN:VCALENDAR -VERSION:2.0 -PRODID:-//Sabre//Sabre VObject 2.0//EN -BEGIN:VEVENT -SUMMARY:Curiosity landing -DTSTART:20120806T051439Z -LOCATION:Mars -END:VEVENT -END:VCALENDAR -``` - -Since events, tasks and journals are always in a sub component, this is also how we -access them. - -```php -use Sabre\VObject; - -$calendar = VObject\Reader::read($data); -echo $calendar->VEVENT->SUMMARY; -``` - -Adding components to a calendar is done with a factory method: - -```php -$event = VObject\Component::create('VEVENT'); -$calendar->add($event); - -$event->SUMMARY = 'Curiosity launch'; -$event->DTSTART = '20111126T150202Z'; -$event->LOCATION = 'Cape Carnival'; -``` - -By the way.. cloning also works as expected, as the entire structure is cloned along with it: - -```php -$clonedEvent = clone $calendar->VEVENT[0]; -$calendar->add($clonedEvent); -``` - -## Date and time handling - -If you ever had to deal with iCalendar timezones, you know it can be complicated. -The way timezones are specified is flawed, which is something I may write an essay about some -day. VObject does its best to determine the correct timezone. Many standard formats -have been tested and verified, and special code has been implemented for special-casing -microsoft generated timezone information, and others. - -To get a real php `DateTime` object, you can request this as follows: - -```php -$event = $calendar->VEVENT; -$start = $event->DTSTART->getDateTime(); -echo $start->format(\DateTime::W3C); -``` - -To set the property with a DateTime object, you can use the following syntax: - -```php -$dateTime = new \DateTime('2012-08-07 23:53:00', new DateTimeZone('Europe/Amsterdam')); -$event->DTSTART->setDateTime($dateTime, VObject\Property\DateTime::DATE); -``` - -The second argument specifies the type of date you're setting. The following three -options exist: - -1. `LOCAL` This is a floating time, with no timezone information. This basically specifies that the event happens in whatever the timezone's currently in. This would be encoded as `DTSTART;VALUE=DATE-TIME:20120807235300` -2. `UTC` This specifies that the time should be encoded as a UTC time. This is encoded as `DTSTART;VALUE=DATE-TIME:20120807205300Z`. Note the extra Z and the fact that it's two hours 'earlier'. -3. `LOCALTZ` specifies that it's supposed to be encoded in its local timezone. For example `DTSTART;VALUE=DATE-TIME;TZID=Europe/Amsterdam:20120807235300`. -4. `DATE` This is a date-only, and does not contain the time. In this case this would be encoded as `DTSTART;VALUE=DATE:20120807`. - -A few important notes: - -* When a `TZID` is specified, there should also be a matching `VTIMEZONE` object with all the timezone information. VObject cannot currently automatically embed this. However, in reality other clients seem to do fine without this information. Yet, for completeness, this will be added in the future. -* As mentioned, the timezone-determination process may sometimes fail. Report any issues you find, and I'll be quick to add workarounds! - -## Recurrence rules - -Recurrence rules allow events to recur, for example for a weekly meeting, or an anniversary. -This is done with the `RRULE` property. The `RRULE` property allows for a LOT of different -rules. VObject only implements the ones that actually appear in calendar software. - -To read more about `RRULE` and all the options, check out [RFC5545](https://tools.ietf.org/html/rfc5545#section-3.8.5). -VObject supports the following options: - -1. `UNTIL` for an end date. -2. `INTERVAL` for for example "every 2 days". -3. `COUNT` to stop recurring after x items. -4. `FREQ=DAILY` to recur every day, and `BYDAY` to limit it to certain days. -5. `FREQ=WEEKLY` to recur every week, `BYDAY` to expand this to multiple weekdays in every week and `WKST` to specify on which day the week starts. -6. `FREQ=MONTHLY` to recur every month, `BYMONTHDAY` to expand this to certain days in a month, `BYDAY` to expand it to certain weekdays occuring in a month, and `BYSETPOS` to limit the last two expansions. -7. `FREQ=YEARLY` to recur every year, `BYMONTH` to expand that to certain months in a year, and `BYDAY` and `BYWEEKDAY` to expand the `BYMONTH` rule even further. - -VObject supports the `EXDATE` property for exclusions, but not yet the `RDATE` and `EXRULE` -properties. If you're interested in this, please file a github issue, as this will put it -on my radar. - -This is a bit of a complex subject to go in excruciating detail. The -[RFC](https://tools.ietf.org/html/rfc5545#section-3.8.5) has a lot of examples though. - -The hard part is not to write the RRULE, it is to expand them. The most complex and -hard-to-read code is hidden in this component. Dragons be here. - -So, if we have a meeting every 2nd monday of the month, this would be specified as such: - -``` -BEGIN:VCALENDAR - VERSION:2.0 - BEGIN:VEVENT - UID:1102c450-e0d7-11e1-9b23-0800200c9a66 - DTSTART:20120109T140000Z - RRULE:FREQ=MONTHLY;BYDAY=MO;BYSETPOS=2 - END:VEVENT -END:VCALENDAR -``` - -Note that normally it's not allowed to indent the object like this, but it does make -it easier to read. This is also the first time I added in a UID, which is required -for all VEVENT, VTODO and VJOURNAL objects! - -To figure out all the meetings for this year, we can use the following syntax: - -```php -use Sabre\VObject; - -$calendar = VObject\Reader::read($data); -$calendar->expand(new DateTime('2012-01-01'), new DateTime('2012-12-31')); -``` - -What the expand method does, is look at its inner events, and expand the recurring -rule. Our calendar now contains 12 events. The first will have its RRULE stripped, -and every subsequent VEVENT has the correct meeting date and a `RECURRENCE-ID` set. - -This results in something like this: - -``` -BEGIN:VCALENDAR - VERSION:2.0 - BEGIN:VEVENT - UID:1102c450-e0d7-11e1-9b23-0800200c9a66 - DTSTART:20120109T140000Z - END:VEVENT - BEGIN:VEVENT - UID:1102c450-e0d7-11e1-9b23-0800200c9a66 - RECURRENCE-ID:20120213T140000Z - DTSTART:20120213T140000Z - END:VEVENT - BEGIN:VEVENT - UID:1102c450-e0d7-11e1-9b23-0800200c9a66 - RECURRENCE-ID:20120312T140000Z - DTSTART:20120312T140000Z - END:VEVENT - ..etc.. -END:VCALENDAR -``` - -To show the list of dates, we would do this as such: - -```php -foreach($calendar->VEVENT as $event) { - echo $event->DTSTART->getDateTime()->format(\DateTime::ATOM); -} -``` - -In a recurring event, single instances can also be overriden. VObject also takes these -into consideration. The reason we needed to specify a start and end-date, is because -some recurrence rules can be 'never ending'. - -You should make sure you pick a sane date-range. Because if you pick a 50 year -time-range, for a daily recurring event; this would result in over 18K objects. - -# Free-busy report generation - -Some calendaring software can make use of FREEBUSY reports to show when people are -available. - -You can automatically generate these reports from calendars using the `FreeBusyGenerator`. - -Example based on our last event: - -```php -// We're giving it the calendar object. It's also possible to specify multiple objects, -// by setting them as an array. -// -// We must also specify a start and end date, because recurring events are expanded. -$fbGenerator = new VObject\FreeBusyGenerator( - new DateTime('2012-01-01'), - new DateTime('2012-12-31'), - $calendar -); - -// Grabbing the report -$freebusy = $fbGenerator->result(); - -// The freebusy report is another VCALENDAR object, so we can serialize it as usual: -echo $freebusy->serialize(); -``` - -The output of this script will look like this: - -``` -BEGIN:VCALENDAR -VERSION:2.0 -PRODID:-//Sabre//Sabre VObject 2.0//EN -CALSCALE:GREGORIAN -BEGIN:VFREEBUSY -DTSTART;VALUE=DATE-TIME:20111231T230000Z -DTEND;VALUE=DATE-TIME:20111231T230000Z -DTSTAMP;VALUE=DATE-TIME:20120808T131628Z -FREEBUSY;FBTYPE=BUSY:20120109T140000Z/20120109T140000Z -FREEBUSY;FBTYPE=BUSY:20120213T140000Z/20120213T140000Z -FREEBUSY;FBTYPE=BUSY:20120312T140000Z/20120312T140000Z -FREEBUSY;FBTYPE=BUSY:20120409T140000Z/20120409T140000Z -FREEBUSY;FBTYPE=BUSY:20120514T140000Z/20120514T140000Z -FREEBUSY;FBTYPE=BUSY:20120611T140000Z/20120611T140000Z -FREEBUSY;FBTYPE=BUSY:20120709T140000Z/20120709T140000Z -FREEBUSY;FBTYPE=BUSY:20120813T140000Z/20120813T140000Z -FREEBUSY;FBTYPE=BUSY:20120910T140000Z/20120910T140000Z -FREEBUSY;FBTYPE=BUSY:20121008T140000Z/20121008T140000Z -FREEBUSY;FBTYPE=BUSY:20121112T140000Z/20121112T140000Z -FREEBUSY;FBTYPE=BUSY:20121210T140000Z/20121210T140000Z -END:VFREEBUSY -END:VCALENDAR -``` diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/bin/vobjectvalidate.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/bin/vobjectvalidate.php deleted file mode 100644 index 84963a6901..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/bin/vobjectvalidate.php +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env php -$v) { - - if ($k===0) { - continue; - } - if (substr($v,0,2)==='--') { - switch($v) { - case '--repair' : - $repair = true; - break; - default : - throw new InvalidArgumentException('Unknown option: ' . $v); - break; - } - continue; - } - $posArgs[] = $v; - -} - -function help() { - - global $argv; - - fwrite(STDERR, <<children(); - -foreach($objects as $child) { - - switch($child->name) { - case 'VCALENDAR' : - fwrite(STDERR, "iCalendar: " . (string)$child->VERSION . "\n"); - break; - case 'VCARD' : - fwrite(STDERR, "vCard: " . (string)$child->VERSION . "\n"); - break; - default : - fwrite(STDERR, "This was an unknown object, but it did parse. It's likely that validation will give you unexpected results.\n"); - break; - } - - $options = 0; - if ($repair) $options |= Node::REPAIR; - - $warnings = $child->validate($options); - - if (!count($warnings)) { - fwrite(STDERR, "[GOOD NEWS] No warnings!\n"); - } else { - foreach($warnings as $warn) { - - fwrite(STDERR, $warn['message'] . "\n"); - - } - - } - - if ($repair) { - fwrite($output, $child->serialize()); - } - -} - diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/composer.json b/core/src/core/classes/sabredav/vendor/sabre/vobject/composer.json deleted file mode 100644 index f99ff1d1f2..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/composer.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "sabre/vobject", - "description" : "The VObject library for PHP allows you to easily parse and manipulate iCalendar and vCard objects", - "keywords" : [ "VObject", "iCalendar", "vCard" ], - "homepage" : "https://github.com/evert/sabre-vobject", - "license" : "BSD-3-Clause", - "require" : { - "php" : ">=5.3.1" - }, - "authors" : [ - { - "name" : "Evert Pot", - "email" : "evert@rooftopsolutions.nl", - "homepage" : "http://www.rooftopsolutions.nl/", - "role" : "Developer" - } - ], - "support" : { - "forum" : "https://groups.google.com/group/sabredav-discuss", - "source" : "https://github.com/evert/sabre-vobject" - }, - "autoload" : { - "psr-0" : { - "Sabre\\VObject" : "lib/" - } - }, - "bin" : [ - "bin/vobjectvalidate.php" - ] -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component.php deleted file mode 100644 index f3560d8671..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component.php +++ /dev/null @@ -1,405 +0,0 @@ - 'Sabre\\VObject\\Component\\VAlarm', - 'VCALENDAR' => 'Sabre\\VObject\\Component\\VCalendar', - 'VCARD' => 'Sabre\\VObject\\Component\\VCard', - 'VEVENT' => 'Sabre\\VObject\\Component\\VEvent', - 'VJOURNAL' => 'Sabre\\VObject\\Component\\VJournal', - 'VTODO' => 'Sabre\\VObject\\Component\\VTodo', - 'VFREEBUSY' => 'Sabre\\VObject\\Component\\VFreeBusy', - ); - - /** - * Creates the new component by name, but in addition will also see if - * there's a class mapped to the property name. - * - * @param string $name - * @param string $value - * @return Component - */ - static public function create($name, $value = null) { - - $name = strtoupper($name); - - if (isset(self::$classMap[$name])) { - return new self::$classMap[$name]($name, $value); - } else { - return new self($name, $value); - } - - } - - /** - * Creates a new component. - * - * By default this object will iterate over its own children, but this can - * be overridden with the iterator argument - * - * @param string $name - * @param ElementList $iterator - */ - public function __construct($name, ElementList $iterator = null) { - - $this->name = strtoupper($name); - if (!is_null($iterator)) $this->iterator = $iterator; - - } - - /** - * Turns the object back into a serialized blob. - * - * @return string - */ - public function serialize() { - - $str = "BEGIN:" . $this->name . "\r\n"; - - /** - * Gives a component a 'score' for sorting purposes. - * - * This is solely used by the childrenSort method. - * - * A higher score means the item will be lower in the list. - * To avoid score collisions, each "score category" has a reasonable - * space to accomodate elements. The $key is added to the $score to - * preserve the original relative order of elements. - * - * @param int $key - * @param array $array - * @return int - */ - $sortScore = function($key, $array) { - - if ($array[$key] instanceof Component) { - - // We want to encode VTIMEZONE first, this is a personal - // preference. - if ($array[$key]->name === 'VTIMEZONE') { - $score=300000000; - return $score+$key; - } else { - $score=400000000; - return $score+$key; - } - } else { - // Properties get encoded first - // VCARD version 4.0 wants the VERSION property to appear first - if ($array[$key] instanceof Property) { - if ($array[$key]->name === 'VERSION') { - $score=100000000; - return $score+$key; - } else { - // All other properties - $score=200000000; - return $score+$key; - } - } - } - - }; - - $tmp = $this->children; - uksort($this->children, function($a, $b) use ($sortScore, $tmp) { - - $sA = $sortScore($a, $tmp); - $sB = $sortScore($b, $tmp); - - if ($sA === $sB) return 0; - - return ($sA < $sB) ? -1 : 1; - - }); - - foreach($this->children as $child) $str.=$child->serialize(); - $str.= "END:" . $this->name . "\r\n"; - - return $str; - - } - - /** - * Adds a new component or element - * - * You can call this method with the following syntaxes: - * - * add(Node $node) - * add(string $name, $value, array $parameters = array()) - * - * The first version adds an Element - * The second adds a property as a string. - * - * @param mixed $item - * @param mixed $itemValue - * @return void - */ - public function add($item, $itemValue = null, array $parameters = array()) { - - if ($item instanceof Node) { - if (!is_null($itemValue)) { - throw new \InvalidArgumentException('The second argument must not be specified, when passing a VObject Node'); - } - $item->parent = $this; - $this->children[] = $item; - } elseif(is_string($item)) { - - $item = Property::create($item,$itemValue, $parameters); - $item->parent = $this; - $this->children[] = $item; - - } else { - - throw new \InvalidArgumentException('The first argument must either be a \\Sabre\\VObject\\Node or a string'); - - } - - } - - /** - * Returns an iterable list of children - * - * @return ElementList - */ - public function children() { - - return new ElementList($this->children); - - } - - /** - * Returns an array with elements that match the specified name. - * - * This function is also aware of MIME-Directory groups (as they appear in - * vcards). This means that if a property is grouped as "HOME.EMAIL", it - * will also be returned when searching for just "EMAIL". If you want to - * search for a property in a specific group, you can select on the entire - * string ("HOME.EMAIL"). If you want to search on a specific property that - * has not been assigned a group, specify ".EMAIL". - * - * Keys are retained from the 'children' array, which may be confusing in - * certain cases. - * - * @param string $name - * @return array - */ - public function select($name) { - - $group = null; - $name = strtoupper($name); - if (strpos($name,'.')!==false) { - list($group,$name) = explode('.', $name, 2); - } - - $result = array(); - foreach($this->children as $key=>$child) { - - if ( - strtoupper($child->name) === $name && - (is_null($group) || ( $child instanceof Property && strtoupper($child->group) === $group)) - ) { - - $result[$key] = $child; - - } - } - - reset($result); - return $result; - - } - - /** - * This method only returns a list of sub-components. Properties are - * ignored. - * - * @return array - */ - public function getComponents() { - - $result = array(); - foreach($this->children as $child) { - if ($child instanceof Component) { - $result[] = $child; - } - } - - return $result; - - } - - /** - * Validates the node for correctness. - * - * The following options are supported: - * - Node::REPAIR - If something is broken, and automatic repair may - * be attempted. - * - * An array is returned with warnings. - * - * Every item in the array has the following properties: - * * level - (number between 1 and 3 with severity information) - * * message - (human readable message) - * * node - (reference to the offending node) - * - * @param int $options - * @return array - */ - public function validate($options = 0) { - - $result = array(); - foreach($this->children as $child) { - $result = array_merge($result, $child->validate($options)); - } - return $result; - - } - - /* Magic property accessors {{{ */ - - /** - * Using 'get' you will either get a property or component, - * - * If there were no child-elements found with the specified name, - * null is returned. - * - * @param string $name - * @return Property - */ - public function __get($name) { - - $matches = $this->select($name); - if (count($matches)===0) { - return null; - } else { - $firstMatch = current($matches); - /** @var $firstMatch Property */ - $firstMatch->setIterator(new ElementList(array_values($matches))); - return $firstMatch; - } - - } - - /** - * This method checks if a sub-element with the specified name exists. - * - * @param string $name - * @return bool - */ - public function __isset($name) { - - $matches = $this->select($name); - return count($matches)>0; - - } - - /** - * Using the setter method you can add properties or subcomponents - * - * You can either pass a Component, Property - * object, or a string to automatically create a Property. - * - * If the item already exists, it will be removed. If you want to add - * a new item with the same name, always use the add() method. - * - * @param string $name - * @param mixed $value - * @return void - */ - public function __set($name, $value) { - - $matches = $this->select($name); - $overWrite = count($matches)?key($matches):null; - - if ($value instanceof Component || $value instanceof Property) { - $value->parent = $this; - if (!is_null($overWrite)) { - $this->children[$overWrite] = $value; - } else { - $this->children[] = $value; - } - } elseif (is_scalar($value)) { - $property = Property::create($name,$value); - $property->parent = $this; - if (!is_null($overWrite)) { - $this->children[$overWrite] = $property; - } else { - $this->children[] = $property; - } - } else { - throw new \InvalidArgumentException('You must pass a \\Sabre\\VObject\\Component, \\Sabre\\VObject\\Property or scalar type'); - } - - } - - /** - * Removes all properties and components within this component. - * - * @param string $name - * @return void - */ - public function __unset($name) { - - $matches = $this->select($name); - foreach($matches as $k=>$child) { - - unset($this->children[$k]); - $child->parent = null; - - } - - } - - /* }}} */ - - /** - * This method is automatically called when the object is cloned. - * Specifically, this will ensure all child elements are also cloned. - * - * @return void - */ - public function __clone() { - - foreach($this->children as $key=>$child) { - $this->children[$key] = clone $child; - $this->children[$key]->parent = $this; - } - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VAlarm.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VAlarm.php deleted file mode 100644 index f119fffe08..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VAlarm.php +++ /dev/null @@ -1,108 +0,0 @@ -TRIGGER; - if(!isset($trigger['VALUE']) || strtoupper($trigger['VALUE']) === 'DURATION') { - $triggerDuration = VObject\DateTimeParser::parseDuration($this->TRIGGER); - $related = (isset($trigger['RELATED']) && strtoupper($trigger['RELATED']) == 'END') ? 'END' : 'START'; - - $parentComponent = $this->parent; - if ($related === 'START') { - - if ($parentComponent->name === 'VTODO') { - $propName = 'DUE'; - } else { - $propName = 'DTSTART'; - } - - $effectiveTrigger = clone $parentComponent->$propName->getDateTime(); - $effectiveTrigger->add($triggerDuration); - } else { - if ($parentComponent->name === 'VTODO') { - $endProp = 'DUE'; - } elseif ($parentComponent->name === 'VEVENT') { - $endProp = 'DTEND'; - } else { - throw new \LogicException('time-range filters on VALARM components are only supported when they are a child of VTODO or VEVENT'); - } - - if (isset($parentComponent->$endProp)) { - $effectiveTrigger = clone $parentComponent->$endProp->getDateTime(); - $effectiveTrigger->add($triggerDuration); - } elseif (isset($parentComponent->DURATION)) { - $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime(); - $duration = VObject\DateTimeParser::parseDuration($parentComponent->DURATION); - $effectiveTrigger->add($duration); - $effectiveTrigger->add($triggerDuration); - } else { - $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime(); - $effectiveTrigger->add($triggerDuration); - } - } - } else { - $effectiveTrigger = $trigger->getDateTime(); - } - return $effectiveTrigger; - - } - - /** - * Returns true or false depending on if the event falls in the specified - * time-range. This is used for filtering purposes. - * - * The rules used to determine if an event falls within the specified - * time-range is based on the CalDAV specification. - * - * @param \DateTime $start - * @param \DateTime $end - * @return bool - */ - public function isInTimeRange(\DateTime $start, \DateTime $end) { - - $effectiveTrigger = $this->getEffectiveTriggerTime(); - - if (isset($this->DURATION)) { - $duration = VObject\DateTimeParser::parseDuration($this->DURATION); - $repeat = (string)$this->repeat; - if (!$repeat) { - $repeat = 1; - } - - $period = new \DatePeriod($effectiveTrigger, $duration, (int)$repeat); - - foreach($period as $occurrence) { - - if ($start <= $occurrence && $end > $occurrence) { - return true; - } - } - return false; - } else { - return ($start <= $effectiveTrigger && $end > $effectiveTrigger); - } - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VCalendar.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VCalendar.php deleted file mode 100644 index 8eeae8f22d..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VCalendar.php +++ /dev/null @@ -1,242 +0,0 @@ -children as $component) { - - if (!$component instanceof VObject\Component) - continue; - - if (isset($component->{'RECURRENCE-ID'})) - continue; - - if ($componentName && $component->name !== strtoupper($componentName)) - continue; - - if ($component->name === 'VTIMEZONE') - continue; - - $components[] = $component; - - } - - return $components; - - } - - /** - * If this calendar object, has events with recurrence rules, this method - * can be used to expand the event into multiple sub-events. - * - * Each event will be stripped from it's recurrence information, and only - * the instances of the event in the specified timerange will be left - * alone. - * - * In addition, this method will cause timezone information to be stripped, - * and normalized to UTC. - * - * This method will alter the VCalendar. This cannot be reversed. - * - * This functionality is specifically used by the CalDAV standard. It is - * possible for clients to request expand events, if they are rather simple - * clients and do not have the possibility to calculate recurrences. - * - * @param DateTime $start - * @param DateTime $end - * @return void - */ - public function expand(\DateTime $start, \DateTime $end) { - - $newEvents = array(); - - foreach($this->select('VEVENT') as $key=>$vevent) { - - if (isset($vevent->{'RECURRENCE-ID'})) { - unset($this->children[$key]); - continue; - } - - - if (!$vevent->rrule) { - unset($this->children[$key]); - if ($vevent->isInTimeRange($start, $end)) { - $newEvents[] = $vevent; - } - continue; - } - - $uid = (string)$vevent->uid; - if (!$uid) { - throw new \LogicException('Event did not have a UID!'); - } - - $it = new VObject\RecurrenceIterator($this, $vevent->uid); - $it->fastForward($start); - - while($it->valid() && $it->getDTStart() < $end) { - - if ($it->getDTEnd() > $start) { - - $newEvents[] = $it->getEventObject(); - - } - $it->next(); - - } - unset($this->children[$key]); - - } - - foreach($newEvents as $newEvent) { - - foreach($newEvent->children as $child) { - if ($child instanceof VObject\Property\DateTime && - $child->getDateType() == VObject\Property\DateTime::LOCALTZ) { - $child->setDateTime($child->getDateTime(),VObject\Property\DateTime::UTC); - } - } - - $this->add($newEvent); - - } - - // Removing all VTIMEZONE components - unset($this->VTIMEZONE); - - } - - /** - * Validates the node for correctness. - * An array is returned with warnings. - * - * Every item in the array has the following properties: - * * level - (number between 1 and 3 with severity information) - * * message - (human readable message) - * * node - (reference to the offending node) - * - * @return array - */ - /* - public function validate() { - - $warnings = array(); - - $version = $this->select('VERSION'); - if (count($version)!==1) { - $warnings[] = array( - 'level' => 1, - 'message' => 'The VERSION property must appear in the VCALENDAR component exactly 1 time', - 'node' => $this, - ); - } else { - if ((string)$this->VERSION !== '2.0') { - $warnings[] = array( - 'level' => 1, - 'message' => 'Only iCalendar version 2.0 as defined in rfc5545 is supported.', - 'node' => $this, - ); - } - } - $version = $this->select('PRODID'); - if (count($version)!==1) { - $warnings[] = array( - 'level' => 2, - 'message' => 'The PRODID property must appear in the VCALENDAR component exactly 1 time', - 'node' => $this, - ); - } - if (count($this->CALSCALE) > 1) { - $warnings[] = array( - 'level' => 2, - 'message' => 'The CALSCALE property must not be specified more than once.', - 'node' => $this, - ); - } - if (count($this->METHOD) > 1) { - $warnings[] = array( - 'level' => 2, - 'message' => 'The METHOD property must not be specified more than once.', - 'node' => $this, - ); - } - - $allowedComponents = array( - 'VEVENT', - 'VTODO', - 'VJOURNAL', - 'VFREEBUSY', - 'VTIMEZONE', - ); - $allowedProperties = array( - 'PRODID', - 'VERSION', - 'CALSCALE', - 'METHOD', - ); - $componentsFound = 0; - foreach($this->children as $child) { - if($child instanceof Component) { - $componentsFound++; - if (!in_array($child->name, $allowedComponents)) { - $warnings[] = array( - 'level' => 1, - 'message' => 'The ' . $child->name . " component is not allowed in the VCALENDAR component", - 'node' => $this, - ); - } - } - if ($child instanceof Property) { - if (!in_array($child->name, $allowedProperties)) { - $warnings[] = array( - 'level' => 2, - 'message' => 'The ' . $child->name . " property is not allowed in the VCALENDAR component", - 'node' => $this, - ); - } - } - } - - if ($componentsFound===0) { - $warnings[] = array( - 'level' => 1, - 'message' => 'An iCalendar object must have at least 1 component.', - 'node' => $this, - ); - } - - return array_merge( - $warnings, - parent::validate() - ); - - } - */ - -} - diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VCard.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VCard.php deleted file mode 100644 index 2e9432d1a7..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VCard.php +++ /dev/null @@ -1,105 +0,0 @@ -select('VERSION'); - if (count($version)!==1) { - $warnings[] = array( - 'level' => 1, - 'message' => 'The VERSION property must appear in the VCARD component exactly 1 time', - 'node' => $this, - ); - if ($options & self::REPAIR) { - $this->VERSION = self::DEFAULT_VERSION; - } - } else { - $version = (string)$this->VERSION; - if ($version!=='2.1' && $version!=='3.0' && $version!=='4.0') { - $warnings[] = array( - 'level' => 1, - 'message' => 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.', - 'node' => $this, - ); - if ($options & self::REPAIR) { - $this->VERSION = '4.0'; - } - } - - } - $fn = $this->select('FN'); - if (count($fn)!==1) { - $warnings[] = array( - 'level' => 1, - 'message' => 'The FN property must appear in the VCARD component exactly 1 time', - 'node' => $this, - ); - if (($options & self::REPAIR) && count($fn) === 0) { - // We're going to try to see if we can use the contents of the - // N property. - if (isset($this->N)) { - $value = explode(';', (string)$this->N); - if (isset($value[1]) && $value[1]) { - $this->FN = $value[1] . ' ' . $value[0]; - } else { - $this->FN = $value[0]; - } - - // Otherwise, the ORG property may work - } elseif (isset($this->ORG)) { - $this->FN = (string)$this->ORG; - } - - } - } - - return array_merge( - parent::validate($options), - $warnings - ); - - } - -} - diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VEvent.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VEvent.php deleted file mode 100644 index fc6331894f..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VEvent.php +++ /dev/null @@ -1,70 +0,0 @@ -RRULE) { - $it = new VObject\RecurrenceIterator($this); - $it->fastForward($start); - - // We fast-forwarded to a spot where the end-time of the - // recurrence instance exceeded the start of the requested - // time-range. - // - // If the starttime of the recurrence did not exceed the - // end of the time range as well, we have a match. - return ($it->getDTStart() < $end && $it->getDTEnd() > $start); - - } - - $effectiveStart = $this->DTSTART->getDateTime(); - if (isset($this->DTEND)) { - - // The DTEND property is considered non inclusive. So for a 3 day - // event in july, dtstart and dtend would have to be July 1st and - // July 4th respectively. - // - // See: - // http://tools.ietf.org/html/rfc5545#page-54 - $effectiveEnd = $this->DTEND->getDateTime(); - - } elseif (isset($this->DURATION)) { - $effectiveEnd = clone $effectiveStart; - $effectiveEnd->add( VObject\DateTimeParser::parseDuration($this->DURATION) ); - } elseif ($this->DTSTART->getDateType() == VObject\Property\DateTime::DATE) { - $effectiveEnd = clone $effectiveStart; - $effectiveEnd->modify('+1 day'); - } else { - $effectiveEnd = clone $effectiveStart; - } - return ( - ($start <= $effectiveEnd) && ($end > $effectiveStart) - ); - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VFreeBusy.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VFreeBusy.php deleted file mode 100644 index df48bce28d..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VFreeBusy.php +++ /dev/null @@ -1,68 +0,0 @@ -select('FREEBUSY') as $freebusy) { - - // We are only interested in FBTYPE=BUSY (the default), - // FBTYPE=BUSY-TENTATIVE or FBTYPE=BUSY-UNAVAILABLE. - if (isset($freebusy['FBTYPE']) && strtoupper(substr((string)$freebusy['FBTYPE'],0,4))!=='BUSY') { - continue; - } - - // The freebusy component can hold more than 1 value, separated by - // commas. - $periods = explode(',', (string)$freebusy); - - foreach($periods as $period) { - // Every period is formatted as [start]/[end]. The start is an - // absolute UTC time, the end may be an absolute UTC time, or - // duration (relative) value. - list($busyStart, $busyEnd) = explode('/', $period); - - $busyStart = VObject\DateTimeParser::parse($busyStart); - $busyEnd = VObject\DateTimeParser::parse($busyEnd); - if ($busyEnd instanceof \DateInterval) { - $tmp = clone $busyStart; - $tmp->add($busyEnd); - $busyEnd = $tmp; - } - - if($start < $busyEnd && $end > $busyStart) { - return false; - } - - } - - } - - return true; - - } - -} - diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VJournal.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VJournal.php deleted file mode 100644 index 2306123d98..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VJournal.php +++ /dev/null @@ -1,46 +0,0 @@ -DTSTART)?$this->DTSTART->getDateTime():null; - if ($dtstart) { - $effectiveEnd = clone $dtstart; - if ($this->DTSTART->getDateType() == VObject\Property\DateTime::DATE) { - $effectiveEnd->modify('+1 day'); - } - - return ($start <= $effectiveEnd && $end > $dtstart); - - } - return false; - - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VTodo.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VTodo.php deleted file mode 100644 index a2c6038dc4..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Component/VTodo.php +++ /dev/null @@ -1,68 +0,0 @@ -DTSTART)?$this->DTSTART->getDateTime():null; - $duration = isset($this->DURATION)?VObject\DateTimeParser::parseDuration($this->DURATION):null; - $due = isset($this->DUE)?$this->DUE->getDateTime():null; - $completed = isset($this->COMPLETED)?$this->COMPLETED->getDateTime():null; - $created = isset($this->CREATED)?$this->CREATED->getDateTime():null; - - if ($dtstart) { - if ($duration) { - $effectiveEnd = clone $dtstart; - $effectiveEnd->add($duration); - return $start <= $effectiveEnd && $end > $dtstart; - } elseif ($due) { - return - ($start < $due || $start <= $dtstart) && - ($end > $dtstart || $end >= $due); - } else { - return $start <= $dtstart && $end > $dtstart; - } - } - if ($due) { - return ($start < $due && $end >= $due); - } - if ($completed && $created) { - return - ($start <= $created || $start <= $completed) && - ($end >= $created || $end >= $completed); - } - if ($completed) { - return ($start <= $completed && $end >= $completed); - } - if ($created) { - return ($end > $created); - } - return true; - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/DateTimeParser.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/DateTimeParser.php deleted file mode 100644 index 31a7fc47d6..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/DateTimeParser.php +++ /dev/null @@ -1,181 +0,0 @@ -setTimeZone(new \DateTimeZone('UTC')); - return $date; - - } - - /** - * Parses an iCalendar (rfc5545) formatted date and returns a DateTime object - * - * @param string $date - * @return DateTime - */ - static public function parseDate($date) { - - // Format is YYYYMMDD - $result = preg_match('/^([1-4][0-9]{3})([0-1][0-9])([0-3][0-9])$/',$date,$matches); - - if (!$result) { - throw new \LogicException('The supplied iCalendar date value is incorrect: ' . $date); - } - - $date = new \DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3], new \DateTimeZone('UTC')); - return $date; - - } - - /** - * Parses an iCalendar (RFC5545) formatted duration value. - * - * This method will either return a DateTimeInterval object, or a string - * suitable for strtotime or DateTime::modify. - * - * @param string $duration - * @param bool $asString - * @return DateInterval|string - */ - static public function parseDuration($duration, $asString = false) { - - $result = preg_match('/^(?P\+|-)?P((?P\d+)W)?((?P\d+)D)?(T((?P\d+)H)?((?P\d+)M)?((?P\d+)S)?)?$/', $duration, $matches); - if (!$result) { - throw new \LogicException('The supplied iCalendar duration value is incorrect: ' . $duration); - } - - if (!$asString) { - $invert = false; - if ($matches['plusminus']==='-') { - $invert = true; - } - - - $parts = array( - 'week', - 'day', - 'hour', - 'minute', - 'second', - ); - foreach($parts as $part) { - $matches[$part] = isset($matches[$part])&&$matches[$part]?(int)$matches[$part]:0; - } - - - // We need to re-construct the $duration string, because weeks and - // days are not supported by DateInterval in the same string. - $duration = 'P'; - $days = $matches['day']; - if ($matches['week']) { - $days+=$matches['week']*7; - } - if ($days) - $duration.=$days . 'D'; - - if ($matches['minute'] || $matches['second'] || $matches['hour']) { - $duration.='T'; - - if ($matches['hour']) - $duration.=$matches['hour'].'H'; - - if ($matches['minute']) - $duration.=$matches['minute'].'M'; - - if ($matches['second']) - $duration.=$matches['second'].'S'; - - } - - if ($duration==='P') { - $duration = 'PT0S'; - } - $iv = new \DateInterval($duration); - if ($invert) $iv->invert = true; - - return $iv; - - } - - - - $parts = array( - 'week', - 'day', - 'hour', - 'minute', - 'second', - ); - - $newDur = ''; - foreach($parts as $part) { - if (isset($matches[$part]) && $matches[$part]) { - $newDur.=' '.$matches[$part] . ' ' . $part . 's'; - } - } - - $newDur = ($matches['plusminus']==='-'?'-':'+') . trim($newDur); - if ($newDur === '+') { $newDur = '+0 seconds'; }; - return $newDur; - - } - - /** - * Parses either a Date or DateTime, or Duration value. - * - * @param string $date - * @param DateTimeZone|string $referenceTZ - * @return DateTime|DateInterval - */ - static public function parse($date, $referenceTZ = null) { - - if ($date[0]==='P' || ($date[0]==='-' && $date[1]==='P')) { - return self::parseDuration($date); - } elseif (strlen($date)===8) { - return self::parseDate($date); - } else { - return self::parseDateTime($date, $referenceTZ); - } - - } - - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/ElementList.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/ElementList.php deleted file mode 100644 index 303faf40ae..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/ElementList.php +++ /dev/null @@ -1,172 +0,0 @@ -vevent where there's multiple VEVENT objects. - * - * @copyright Copyright (C) 2007-2013 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) - * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License - */ -class ElementList implements \Iterator, \Countable, \ArrayAccess { - - /** - * Inner elements - * - * @var array - */ - protected $elements = array(); - - /** - * Creates the element list. - * - * @param array $elements - */ - public function __construct(array $elements) { - - $this->elements = $elements; - - } - - /* {{{ Iterator interface */ - - /** - * Current position - * - * @var int - */ - private $key = 0; - - /** - * Returns current item in iteration - * - * @return Element - */ - public function current() { - - return $this->elements[$this->key]; - - } - - /** - * To the next item in the iterator - * - * @return void - */ - public function next() { - - $this->key++; - - } - - /** - * Returns the current iterator key - * - * @return int - */ - public function key() { - - return $this->key; - - } - - /** - * Returns true if the current position in the iterator is a valid one - * - * @return bool - */ - public function valid() { - - return isset($this->elements[$this->key]); - - } - - /** - * Rewinds the iterator - * - * @return void - */ - public function rewind() { - - $this->key = 0; - - } - - /* }}} */ - - /* {{{ Countable interface */ - - /** - * Returns the number of elements - * - * @return int - */ - public function count() { - - return count($this->elements); - - } - - /* }}} */ - - /* {{{ ArrayAccess Interface */ - - - /** - * Checks if an item exists through ArrayAccess. - * - * @param int $offset - * @return bool - */ - public function offsetExists($offset) { - - return isset($this->elements[$offset]); - - } - - /** - * Gets an item through ArrayAccess. - * - * @param int $offset - * @return mixed - */ - public function offsetGet($offset) { - - return $this->elements[$offset]; - - } - - /** - * Sets an item through ArrayAccess. - * - * @param int $offset - * @param mixed $value - * @return void - */ - public function offsetSet($offset,$value) { - - throw new \LogicException('You can not add new objects to an ElementList'); - - } - - /** - * Sets an item through ArrayAccess. - * - * This method just forwards the request to the inner iterator - * - * @param int $offset - * @return void - */ - public function offsetUnset($offset) { - - throw new \LogicException('You can not remove objects from an ElementList'); - - } - - /* }}} */ - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/FreeBusyGenerator.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/FreeBusyGenerator.php deleted file mode 100644 index 7a17f6ea6d..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/FreeBusyGenerator.php +++ /dev/null @@ -1,322 +0,0 @@ -setTimeRange($start, $end); - } - - if ($objects) { - $this->setObjects($objects); - } - - } - - /** - * Sets the VCALENDAR object. - * - * If this is set, it will not be generated for you. You are responsible - * for setting things like the METHOD, CALSCALE, VERSION, etc.. - * - * The VFREEBUSY object will be automatically added though. - * - * @param Component $vcalendar - * @return void - */ - public function setBaseObject(Component $vcalendar) { - - $this->baseObject = $vcalendar; - - } - - /** - * Sets the input objects - * - * You must either specify a valendar object as a strong, or as the parse - * Component. - * It's also possible to specify multiple objects as an array. - * - * @param mixed $objects - * @return void - */ - public function setObjects($objects) { - - if (!is_array($objects)) { - $objects = array($objects); - } - - $this->objects = array(); - foreach($objects as $object) { - - if (is_string($object)) { - $this->objects[] = Reader::read($object); - } elseif ($object instanceof Component) { - $this->objects[] = $object; - } else { - throw new \InvalidArgumentException('You can only pass strings or \\Sabre\\VObject\\Component arguments to setObjects'); - } - - } - - } - - /** - * Sets the time range - * - * Any freebusy object falling outside of this time range will be ignored. - * - * @param DateTime $start - * @param DateTime $end - * @return void - */ - public function setTimeRange(\DateTime $start = null, \DateTime $end = null) { - - $this->start = $start; - $this->end = $end; - - } - - /** - * Parses the input data and returns a correct VFREEBUSY object, wrapped in - * a VCALENDAR. - * - * @return Component - */ - public function getResult() { - - $busyTimes = array(); - - foreach($this->objects as $object) { - - foreach($object->getBaseComponents() as $component) { - - switch($component->name) { - - case 'VEVENT' : - - $FBTYPE = 'BUSY'; - if (isset($component->TRANSP) && (strtoupper($component->TRANSP) === 'TRANSPARENT')) { - break; - } - if (isset($component->STATUS)) { - $status = strtoupper($component->STATUS); - if ($status==='CANCELLED') { - break; - } - if ($status==='TENTATIVE') { - $FBTYPE = 'BUSY-TENTATIVE'; - } - } - - $times = array(); - - if ($component->RRULE) { - - $iterator = new RecurrenceIterator($object, (string)$component->uid); - if ($this->start) { - $iterator->fastForward($this->start); - } - - $maxRecurrences = 200; - - while($iterator->valid() && --$maxRecurrences) { - - $startTime = $iterator->getDTStart(); - if ($this->end && $startTime > $this->end) { - break; - } - $times[] = array( - $iterator->getDTStart(), - $iterator->getDTEnd(), - ); - - $iterator->next(); - - } - - } else { - - $startTime = $component->DTSTART->getDateTime(); - if ($this->end && $startTime > $this->end) { - break; - } - $endTime = null; - if (isset($component->DTEND)) { - $endTime = $component->DTEND->getDateTime(); - } elseif (isset($component->DURATION)) { - $duration = DateTimeParser::parseDuration((string)$component->DURATION); - $endTime = clone $startTime; - $endTime->add($duration); - } elseif ($component->DTSTART->getDateType() === Property\DateTime::DATE) { - $endTime = clone $startTime; - $endTime->modify('+1 day'); - } else { - // The event had no duration (0 seconds) - break; - } - - $times[] = array($startTime, $endTime); - - } - - foreach($times as $time) { - - if ($this->end && $time[0] > $this->end) break; - if ($this->start && $time[1] < $this->start) break; - - $busyTimes[] = array( - $time[0], - $time[1], - $FBTYPE, - ); - } - break; - - case 'VFREEBUSY' : - foreach($component->FREEBUSY as $freebusy) { - - $fbType = isset($freebusy['FBTYPE'])?strtoupper($freebusy['FBTYPE']):'BUSY'; - - // Skipping intervals marked as 'free' - if ($fbType==='FREE') - continue; - - $values = explode(',', $freebusy); - foreach($values as $value) { - list($startTime, $endTime) = explode('/', $value); - $startTime = DateTimeParser::parseDateTime($startTime); - - if (substr($endTime,0,1)==='P' || substr($endTime,0,2)==='-P') { - $duration = DateTimeParser::parseDuration($endTime); - $endTime = clone $startTime; - $endTime->add($duration); - } else { - $endTime = DateTimeParser::parseDateTime($endTime); - } - - if($this->start && $this->start > $endTime) continue; - if($this->end && $this->end < $startTime) continue; - $busyTimes[] = array( - $startTime, - $endTime, - $fbType - ); - - } - - - } - break; - - - - } - - - } - - } - - if ($this->baseObject) { - $calendar = $this->baseObject; - } else { - $calendar = Component::create('VCALENDAR'); - $calendar->version = '2.0'; - $calendar->prodid = '-//Sabre//Sabre VObject ' . Version::VERSION . '//EN'; - $calendar->calscale = 'GREGORIAN'; - } - - $vfreebusy = Component::create('VFREEBUSY'); - $calendar->add($vfreebusy); - - if ($this->start) { - $dtstart = Property::create('DTSTART'); - $dtstart->setDateTime($this->start,Property\DateTime::UTC); - $vfreebusy->add($dtstart); - } - if ($this->end) { - $dtend = Property::create('DTEND'); - $dtend->setDateTime($this->end,Property\DateTime::UTC); - $vfreebusy->add($dtend); - } - $dtstamp = Property::create('DTSTAMP'); - $dtstamp->setDateTime(new \DateTime('now'), Property\DateTime::UTC); - $vfreebusy->add($dtstamp); - - foreach($busyTimes as $busyTime) { - - $busyTime[0]->setTimeZone(new \DateTimeZone('UTC')); - $busyTime[1]->setTimeZone(new \DateTimeZone('UTC')); - - $prop = Property::create( - 'FREEBUSY', - $busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z') - ); - $prop['FBTYPE'] = $busyTime[2]; - $vfreebusy->add($prop); - - } - - return $calendar; - - } - -} - diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Node.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Node.php deleted file mode 100644 index 802abb8b1f..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Node.php +++ /dev/null @@ -1,187 +0,0 @@ -iterator)) - return $this->iterator; - - return new ElementList(array($this)); - - } - - /** - * Sets the overridden iterator - * - * Note that this is not actually part of the iterator interface - * - * @param ElementList $iterator - * @return void - */ - public function setIterator(ElementList $iterator) { - - $this->iterator = $iterator; - - } - - /* }}} */ - - /* {{{ Countable interface */ - - /** - * Returns the number of elements - * - * @return int - */ - public function count() { - - $it = $this->getIterator(); - return $it->count(); - - } - - /* }}} */ - - /* {{{ ArrayAccess Interface */ - - - /** - * Checks if an item exists through ArrayAccess. - * - * This method just forwards the request to the inner iterator - * - * @param int $offset - * @return bool - */ - public function offsetExists($offset) { - - $iterator = $this->getIterator(); - return $iterator->offsetExists($offset); - - } - - /** - * Gets an item through ArrayAccess. - * - * This method just forwards the request to the inner iterator - * - * @param int $offset - * @return mixed - */ - public function offsetGet($offset) { - - $iterator = $this->getIterator(); - return $iterator->offsetGet($offset); - - } - - /** - * Sets an item through ArrayAccess. - * - * This method just forwards the request to the inner iterator - * - * @param int $offset - * @param mixed $value - * @return void - */ - public function offsetSet($offset,$value) { - - $iterator = $this->getIterator(); - $iterator->offsetSet($offset,$value); - - // @codeCoverageIgnoreStart - // - // This method always throws an exception, so we ignore the closing - // brace - } - // @codeCoverageIgnoreEnd - - /** - * Sets an item through ArrayAccess. - * - * This method just forwards the request to the inner iterator - * - * @param int $offset - * @return void - */ - public function offsetUnset($offset) { - - $iterator = $this->getIterator(); - $iterator->offsetUnset($offset); - - // @codeCoverageIgnoreStart - // - // This method always throws an exception, so we ignore the closing - // brace - } - // @codeCoverageIgnoreEnd - - /* }}} */ - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Parameter.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Parameter.php deleted file mode 100644 index ddfb5b3fcf..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Parameter.php +++ /dev/null @@ -1,88 +0,0 @@ -name = strtoupper($name); - $this->value = $value; - - } - - /** - * Turns the object back into a serialized blob. - * - * @return string - */ - public function serialize() { - - if (is_null($this->value)) { - return $this->name; - } - $src = array( - '\\', - "\n", - ';', - ',', - ); - $out = array( - '\\\\', - '\n', - '\;', - '\,', - ); - - return $this->name . '=' . str_replace($src, $out, $this->value); - - } - - /** - * Called when this object is being cast to a string - * - * @return string - */ - public function __toString() { - - return $this->value; - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/ParseException.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/ParseException.php deleted file mode 100644 index f82613de35..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/ParseException.php +++ /dev/null @@ -1,12 +0,0 @@ - 'Sabre\\VObject\\Property\\DateTime', - 'CREATED' => 'Sabre\\VObject\\Property\\DateTime', - 'DTEND' => 'Sabre\\VObject\\Property\\DateTime', - 'DTSTAMP' => 'Sabre\\VObject\\Property\\DateTime', - 'DTSTART' => 'Sabre\\VObject\\Property\\DateTime', - 'DUE' => 'Sabre\\VObject\\Property\\DateTime', - 'EXDATE' => 'Sabre\\VObject\\Property\\MultiDateTime', - 'LAST-MODIFIED' => 'Sabre\\VObject\\Property\\DateTime', - 'RECURRENCE-ID' => 'Sabre\\VObject\\Property\\DateTime', - 'TRIGGER' => 'Sabre\\VObject\\Property\\DateTime', - 'N' => 'Sabre\\VObject\\Property\\Compound', - 'ORG' => 'Sabre\\VObject\\Property\\Compound', - 'ADR' => 'Sabre\\VObject\\Property\\Compound', - 'CATEGORIES' => 'Sabre\\VObject\\Property\\Compound', - ); - - /** - * Creates the new property by name, but in addition will also see if - * there's a class mapped to the property name. - * - * Parameters can be specified with the optional third argument. Parameters - * must be a key->value map of the parameter name, and value. If the value - * is specified as an array, it is assumed that multiple parameters with - * the same name should be added. - * - * @param string $name - * @param string $value - * @param array $parameters - * @return Property - */ - static public function create($name, $value = null, array $parameters = array()) { - - $name = strtoupper($name); - $shortName = $name; - $group = null; - if (strpos($shortName,'.')!==false) { - list($group, $shortName) = explode('.', $shortName); - } - - if (isset(self::$classMap[$shortName])) { - return new self::$classMap[$shortName]($name, $value, $parameters); - } else { - return new self($name, $value, $parameters); - } - - } - - /** - * Creates a new property object - * - * Parameters can be specified with the optional third argument. Parameters - * must be a key->value map of the parameter name, and value. If the value - * is specified as an array, it is assumed that multiple parameters with - * the same name should be added. - * - * @param string $name - * @param string $value - * @param array $parameters - */ - public function __construct($name, $value = null, array $parameters = array()) { - - if (!is_scalar($value) && !is_null($value)) { - throw new \InvalidArgumentException('The value argument must be scalar or null'); - } - - $name = strtoupper($name); - $group = null; - if (strpos($name,'.')!==false) { - list($group, $name) = explode('.', $name); - } - $this->name = $name; - $this->group = $group; - $this->setValue($value); - - foreach($parameters as $paramName => $paramValues) { - - if (!is_array($paramValues)) { - $paramValues = array($paramValues); - } - - foreach($paramValues as $paramValue) { - $this->add($paramName, $paramValue); - } - - } - - } - - /** - * Updates the internal value - * - * @param string $value - * @return void - */ - public function setValue($value) { - - $this->value = $value; - - } - - /** - * Turns the object back into a serialized blob. - * - * @return string - */ - public function serialize() { - - $str = $this->name; - if ($this->group) $str = $this->group . '.' . $this->name; - - foreach($this->parameters as $param) { - - $str.=';' . $param->serialize(); - - } - - $src = array( - '\\', - "\n", - ); - $out = array( - '\\\\', - '\n', - ); - $str.=':' . str_replace($src, $out, $this->value); - - $out = ''; - while(strlen($str)>0) { - if (strlen($str)>75) { - $out.= mb_strcut($str,0,75,'utf-8') . "\r\n"; - $str = ' ' . mb_strcut($str,75,strlen($str),'utf-8'); - } else { - $out.=$str . "\r\n"; - $str=''; - break; - } - } - - return $out; - - } - - /** - * Adds a new componenten or element - * - * You can call this method with the following syntaxes: - * - * add(Parameter $element) - * add(string $name, $value) - * - * The first version adds an Parameter - * The second adds a property as a string. - * - * @param mixed $item - * @param mixed $itemValue - * @return void - */ - public function add($item, $itemValue = null) { - - if ($item instanceof Parameter) { - if (!is_null($itemValue)) { - throw new \InvalidArgumentException('The second argument must not be specified, when passing a VObject'); - } - $item->parent = $this; - $this->parameters[] = $item; - } elseif(is_string($item)) { - - $parameter = new Parameter($item,$itemValue); - $parameter->parent = $this; - $this->parameters[] = $parameter; - - } else { - - throw new \InvalidArgumentException('The first argument must either be a Node a string'); - - } - - } - - /* ArrayAccess interface {{{ */ - - /** - * Checks if an array element exists - * - * @param mixed $name - * @return bool - */ - public function offsetExists($name) { - - if (is_int($name)) return parent::offsetExists($name); - - $name = strtoupper($name); - - foreach($this->parameters as $parameter) { - if ($parameter->name == $name) return true; - } - return false; - - } - - /** - * Returns a parameter, or parameter list. - * - * @param string $name - * @return Node - */ - public function offsetGet($name) { - - if (is_int($name)) return parent::offsetGet($name); - $name = strtoupper($name); - - $result = array(); - foreach($this->parameters as $parameter) { - if ($parameter->name == $name) - $result[] = $parameter; - } - - if (count($result)===0) { - return null; - } elseif (count($result)===1) { - return $result[0]; - } else { - $result[0]->setIterator(new ElementList($result)); - return $result[0]; - } - - } - - /** - * Creates a new parameter - * - * @param string $name - * @param mixed $value - * @return void - */ - public function offsetSet($name, $value) { - - if (is_int($name)) parent::offsetSet($name, $value); - - if (is_scalar($value)) { - if (!is_string($name)) - throw new \InvalidArgumentException('A parameter name must be specified. This means you cannot use the $array[]="string" to add parameters.'); - - $this->offsetUnset($name); - $parameter = new Parameter($name, $value); - $parameter->parent = $this; - $this->parameters[] = $parameter; - - } elseif ($value instanceof Parameter) { - if (!is_null($name)) - throw new \InvalidArgumentException('Don\'t specify a parameter name if you\'re passing a \\Sabre\\VObject\\Parameter. Add using $array[]=$parameterObject.'); - - $value->parent = $this; - $this->parameters[] = $value; - } else { - throw new \InvalidArgumentException('You can only add parameters to the property object'); - } - - } - - /** - * Removes one or more parameters with the specified name - * - * @param string $name - * @return void - */ - public function offsetUnset($name) { - - if (is_int($name)) parent::offsetUnset($name); - $name = strtoupper($name); - - foreach($this->parameters as $key=>$parameter) { - if ($parameter->name == $name) { - $parameter->parent = null; - unset($this->parameters[$key]); - } - - } - - } - - /* }}} */ - - /** - * Called when this object is being cast to a string - * - * @return string - */ - public function __toString() { - - return (string)$this->value; - - } - - /** - * This method is automatically called when the object is cloned. - * Specifically, this will ensure all child elements are also cloned. - * - * @return void - */ - public function __clone() { - - foreach($this->parameters as $key=>$child) { - $this->parameters[$key] = clone $child; - $this->parameters[$key]->parent = $this; - } - - } - - /** - * Validates the node for correctness. - * - * The following options are supported: - * - Node::REPAIR - If something is broken, and automatic repair may - * be attempted. - * - * An array is returned with warnings. - * - * Every item in the array has the following properties: - * * level - (number between 1 and 3 with severity information) - * * message - (human readable message) - * * node - (reference to the offending node) - * - * @param int $options - * @return array - */ - public function validate($options = 0) { - - $warnings = array(); - - // Checking if our value is UTF-8 - if (!StringUtil::isUTF8($this->value)) { - $warnings[] = array( - 'level' => 1, - 'message' => 'Property is not valid UTF-8!', - 'node' => $this, - ); - if ($options & self::REPAIR) { - $this->value = StringUtil::convertToUTF8($this->value); - } - } - - // Checking if the propertyname does not contain any invalid bytes. - if (!preg_match('/^([A-Z0-9-]+)$/', $this->name)) { - $warnings[] = array( - 'level' => 1, - 'message' => 'The propertyname: ' . $this->name . ' contains invalid characters. Only A-Z, 0-9 and - are allowed', - 'node' => $this, - ); - if ($options & self::REPAIR) { - // Uppercasing and converting underscores to dashes. - $this->name = strtoupper( - str_replace('_', '-', $this->name) - ); - // Removing every other invalid character - $this->name = preg_replace('/([^A-Z0-9-])/u', '', $this->name); - - } - - } - - // Validating inner parameters - foreach($this->parameters as $param) { - $warnings = array_merge($warnings, $param->validate($options)); - } - - return $warnings; - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Property/Compound.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Property/Compound.php deleted file mode 100644 index 8cd0dc27b3..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Property/Compound.php +++ /dev/null @@ -1,129 +0,0 @@ - ';', - 'ADR' => ';', - 'ORG' => ';', - 'CATEGORIES' => ',', - ); - - /** - * The currently used delimiter. - * - * @var string - */ - protected $delimiter = null; - - /** - * Get a compound value as an array. - * - * @param $name string - * @return array - */ - public function getParts() { - - if (is_null($this->value)) { - return array(); - } - - $delimiter = $this->getDelimiter(); - - // split by any $delimiter which is NOT prefixed by a slash. - // Note that this is not a a perfect solution. If a value is prefixed - // by two slashes, it should actually be split anyway. - // - // Hopefully we can fix this better in a future version, where we can - // break compatibility a bit. - $compoundValues = preg_split("/(?value); - - // remove slashes from any semicolon and comma left escaped in the single values - $compoundValues = array_map( - function($val) { - return strtr($val, array('\,' => ',', '\;' => ';')); - }, $compoundValues); - - return $compoundValues; - - } - - /** - * Returns the delimiter for this property. - * - * @return string - */ - public function getDelimiter() { - - if (!$this->delimiter) { - if (isset(self::$delimiterMap[$this->name])) { - $this->delimiter = self::$delimiterMap[$this->name]; - } else { - // To be a bit future proof, we are going to default the - // delimiter to ; - $this->delimiter = ';'; - } - } - return $this->delimiter; - - } - - /** - * Set a compound value as an array. - * - * - * @param $name string - * @return array - */ - public function setParts(array $values) { - - // add slashes to all semicolons and commas in the single values - $values = array_map( - function($val) { - return strtr($val, array(',' => '\,', ';' => '\;')); - }, $values); - - $this->setValue( - implode($this->getDelimiter(), $values) - ); - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Property/DateTime.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Property/DateTime.php deleted file mode 100644 index 9d900e6b53..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Property/DateTime.php +++ /dev/null @@ -1,233 +0,0 @@ -setValue($dt->format('Ymd\\THis')); - $this->offsetUnset('VALUE'); - $this->offsetUnset('TZID'); - $this->offsetSet('VALUE','DATE-TIME'); - break; - case self::UTC : - $dt->setTimeZone(new \DateTimeZone('UTC')); - $this->setValue($dt->format('Ymd\\THis\\Z')); - $this->offsetUnset('VALUE'); - $this->offsetUnset('TZID'); - $this->offsetSet('VALUE','DATE-TIME'); - break; - case self::LOCALTZ : - $this->setValue($dt->format('Ymd\\THis')); - $this->offsetUnset('VALUE'); - $this->offsetUnset('TZID'); - $this->offsetSet('VALUE','DATE-TIME'); - $this->offsetSet('TZID', $dt->getTimeZone()->getName()); - break; - case self::DATE : - $this->setValue($dt->format('Ymd')); - $this->offsetUnset('VALUE'); - $this->offsetUnset('TZID'); - $this->offsetSet('VALUE','DATE'); - break; - default : - throw new \InvalidArgumentException('You must pass a valid dateType constant'); - - } - $this->dateTime = $dt; - $this->dateType = $dateType; - - } - - /** - * Returns the current DateTime value. - * - * If no value was set, this method returns null. - * - * @return \DateTime|null - */ - public function getDateTime() { - - if ($this->dateTime) - return $this->dateTime; - - list( - $this->dateType, - $this->dateTime - ) = self::parseData($this->value, $this); - return $this->dateTime; - - } - - /** - * Returns the type of Date format. - * - * This method returns one of the format constants. If no date was set, - * this method will return null. - * - * @return int|null - */ - public function getDateType() { - - if ($this->dateType) - return $this->dateType; - - list( - $this->dateType, - $this->dateTime, - ) = self::parseData($this->value, $this); - return $this->dateType; - - } - - /** - * Parses the internal data structure to figure out what the current date - * and time is. - * - * The returned array contains two elements: - * 1. A 'DateType' constant (as defined on this class), or null. - * 2. A DateTime object (or null) - * - * @param string|null $propertyValue The string to parse (yymmdd or - * ymmddThhmmss, etc..) - * @param \Sabre\VObject\Property|null $property The instance of the - * property we're parsing. - * @return array - */ - static public function parseData($propertyValue, VObject\Property $property = null) { - - if (is_null($propertyValue)) { - return array(null, null); - } - - $date = '(?P[1-2][0-9]{3})(?P[0-1][0-9])(?P[0-3][0-9])'; - $time = '(?P[0-2][0-9])(?P[0-5][0-9])(?P[0-5][0-9])'; - $regex = "/^$date(T$time(?PZ)?)?$/"; - - if (!preg_match($regex, $propertyValue, $matches)) { - throw new \InvalidArgumentException($propertyValue . ' is not a valid \DateTime or Date string'); - } - - if (!isset($matches['hour'])) { - // Date-only - return array( - self::DATE, - new \DateTime($matches['year'] . '-' . $matches['month'] . '-' . $matches['date'] . ' 00:00:00', new \DateTimeZone('UTC')), - ); - } - - $dateStr = - $matches['year'] .'-' . - $matches['month'] . '-' . - $matches['date'] . ' ' . - $matches['hour'] . ':' . - $matches['minute'] . ':' . - $matches['second']; - - if (isset($matches['isutc'])) { - $dt = new \DateTime($dateStr,new \DateTimeZone('UTC')); - $dt->setTimeZone(new \DateTimeZone('UTC')); - return array( - self::UTC, - $dt - ); - } - - // Finding the timezone. - $tzid = $property['TZID']; - if (!$tzid) { - // This was a floating time string. This implies we use the - // timezone from date_default_timezone_set / date.timezone ini - // setting. - return array( - self::LOCAL, - new \DateTime($dateStr) - ); - } - - // To look up the timezone, we must first find the VCALENDAR component. - $root = $property; - while($root->parent) { - $root = $root->parent; - } - if ($root->name === 'VCALENDAR') { - $tz = VObject\TimeZoneUtil::getTimeZone((string)$tzid, $root); - } else { - $tz = VObject\TimeZoneUtil::getTimeZone((string)$tzid); - } - - $dt = new \DateTime($dateStr, $tz); - $dt->setTimeZone($tz); - - return array( - self::LOCALTZ, - $dt - ); - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Property/MultiDateTime.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Property/MultiDateTime.php deleted file mode 100644 index e05a0246f1..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Property/MultiDateTime.php +++ /dev/null @@ -1,168 +0,0 @@ -offsetUnset('VALUE'); - $this->offsetUnset('TZID'); - switch($dateType) { - - case DateTime::LOCAL : - $val = array(); - foreach($dt as $i) { - $val[] = $i->format('Ymd\\THis'); - } - $this->setValue(implode(',',$val)); - $this->offsetSet('VALUE','DATE-TIME'); - break; - case DateTime::UTC : - $val = array(); - foreach($dt as $i) { - $i->setTimeZone(new \DateTimeZone('UTC')); - $val[] = $i->format('Ymd\\THis\\Z'); - } - $this->setValue(implode(',',$val)); - $this->offsetSet('VALUE','DATE-TIME'); - break; - case DateTime::LOCALTZ : - $val = array(); - foreach($dt as $i) { - $val[] = $i->format('Ymd\\THis'); - } - $this->setValue(implode(',',$val)); - $this->offsetSet('VALUE','DATE-TIME'); - $this->offsetSet('TZID', $dt[0]->getTimeZone()->getName()); - break; - case DateTime::DATE : - $val = array(); - foreach($dt as $i) { - $val[] = $i->format('Ymd'); - } - $this->setValue(implode(',',$val)); - $this->offsetSet('VALUE','DATE'); - break; - default : - throw new \InvalidArgumentException('You must pass a valid dateType constant'); - - } - $this->dateTimes = $dt; - $this->dateType = $dateType; - - } - - /** - * Returns the current DateTime value. - * - * If no value was set, this method returns null. - * - * @return array|null - */ - public function getDateTimes() { - - if ($this->dateTimes) - return $this->dateTimes; - - $dts = array(); - - if (!$this->value) { - $this->dateTimes = null; - $this->dateType = null; - return null; - } - - foreach(explode(',',$this->value) as $val) { - list( - $type, - $dt - ) = DateTime::parseData($val, $this); - $dts[] = $dt; - $this->dateType = $type; - } - $this->dateTimes = $dts; - return $this->dateTimes; - - } - - /** - * Returns the type of Date format. - * - * This method returns one of the format constants. If no date was set, - * this method will return null. - * - * @return int|null - */ - public function getDateType() { - - if ($this->dateType) - return $this->dateType; - - if (!$this->value) { - $this->dateTimes = null; - $this->dateType = null; - return null; - } - - $dts = array(); - foreach(explode(',',$this->value) as $val) { - list( - $type, - $dt - ) = DateTime::parseData($val, $this); - $dts[] = $dt; - $this->dateType = $type; - } - $this->dateTimes = $dts; - return $this->dateType; - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Reader.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Reader.php deleted file mode 100644 index 2d9a14a296..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Reader.php +++ /dev/null @@ -1,223 +0,0 @@ -add($parsedLine); - - if ($nextLine===false) - throw new ParseException('Invalid VObject. Document ended prematurely.'); - - } - - // Checking component name of the 'END:' line. - if (substr($nextLine,4)!==$obj->name) { - throw new ParseException('Invalid VObject, expected: "END:' . $obj->name . '" got: "' . $nextLine . '"'); - } - next($lines); - - return $obj; - - } - - // Properties - //$result = preg_match('/(?P[A-Z0-9-]+)(?:;(?P^(?([^:^\"]|\"([^\"]*)\")*))?"; - $regex = "/^(?P$token)$parameters:(?P.*)$/i"; - - $result = preg_match($regex,$line,$matches); - - if (!$result) { - if ($options & self::OPTION_IGNORE_INVALID_LINES) { - return null; - } else { - throw new ParseException('Invalid VObject, line ' . ($lineNr+1) . ' did not follow the icalendar/vcard format'); - } - } - - $propertyName = strtoupper($matches['name']); - $propertyValue = preg_replace_callback('#(\\\\(\\\\|N|n))#',function($matches) { - if ($matches[2]==='n' || $matches[2]==='N') { - return "\n"; - } else { - return $matches[2]; - } - }, $matches['value']); - - $obj = Property::create($propertyName, $propertyValue); - - if ($matches['parameters']) { - - foreach(self::readParameters($matches['parameters']) as $param) { - $obj->add($param); - } - - } - - return $obj; - - - } - - /** - * Reads a parameter list from a property - * - * This method returns an array of Parameter - * - * @param string $parameters - * @return array - */ - static private function readParameters($parameters) { - - $token = '[A-Z0-9-]+'; - - $paramValue = '(?P[^\"^;]*|"[^"]*")'; - - $regex = "/(?<=^|;)(?P$token)(=$paramValue(?=$|;))?/i"; - preg_match_all($regex, $parameters, $matches, PREG_SET_ORDER); - - $params = array(); - foreach($matches as $match) { - - if (!isset($match['paramValue'])) { - - $value = null; - - } else { - - $value = $match['paramValue']; - - if (isset($value[0]) && $value[0]==='"') { - // Stripping quotes, if needed - $value = substr($value,1,strlen($value)-2); - } - - $value = preg_replace_callback('#(\\\\(\\\\|N|n|;|,))#',function($matches) { - if ($matches[2]==='n' || $matches[2]==='N') { - return "\n"; - } else { - return $matches[2]; - } - }, $value); - - } - - $params[] = new Parameter($match['paramName'], $value); - - } - - return $params; - - } - - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/RecurrenceIterator.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/RecurrenceIterator.php deleted file mode 100644 index 2f9ad5b6ac..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/RecurrenceIterator.php +++ /dev/null @@ -1,1112 +0,0 @@ - 0, - 'MO' => 1, - 'TU' => 2, - 'WE' => 3, - 'TH' => 4, - 'FR' => 5, - 'SA' => 6, - ); - - /** - * Mappings between the day number and english day name. - * - * @var array - */ - private $dayNames = array( - 0 => 'Sunday', - 1 => 'Monday', - 2 => 'Tuesday', - 3 => 'Wednesday', - 4 => 'Thursday', - 5 => 'Friday', - 6 => 'Saturday', - ); - - /** - * If the current iteration of the event is an overriden event, this - * property will hold the VObject - * - * @var Component - */ - private $currentOverriddenEvent; - - /** - * This property may contain the date of the next not-overridden event. - * This date is calculated sometimes a bit early, before overridden events - * are evaluated. - * - * @var DateTime - */ - private $nextDate; - - /** - * Creates the iterator - * - * You should pass a VCALENDAR component, as well as the UID of the event - * we're going to traverse. - * - * @param Component $vcal - * @param string|null $uid - */ - public function __construct(Component $vcal, $uid=null) { - - if (is_null($uid)) { - if ($vcal->name === 'VCALENDAR') { - throw new \InvalidArgumentException('If you pass a VCALENDAR object, you must pass a uid argument as well'); - } - $components = array($vcal); - $uid = (string)$vcal->uid; - } else { - $components = $vcal->select('VEVENT'); - } - foreach($components as $component) { - if ((string)$component->uid == $uid) { - if (isset($component->{'RECURRENCE-ID'})) { - $this->overriddenEvents[$component->DTSTART->getDateTime()->getTimeStamp()] = $component; - $this->overriddenDates[] = $component->{'RECURRENCE-ID'}->getDateTime(); - } else { - $this->baseEvent = $component; - } - } - } - if (!$this->baseEvent) { - throw new \InvalidArgumentException('Could not find a base event with uid: ' . $uid); - } - - $this->startDate = clone $this->baseEvent->DTSTART->getDateTime(); - - $this->endDate = null; - if (isset($this->baseEvent->DTEND)) { - $this->endDate = clone $this->baseEvent->DTEND->getDateTime(); - } else { - $this->endDate = clone $this->startDate; - if (isset($this->baseEvent->DURATION)) { - $this->endDate->add(DateTimeParser::parse($this->baseEvent->DURATION->value)); - } elseif ($this->baseEvent->DTSTART->getDateType()===Property\DateTime::DATE) { - $this->endDate->modify('+1 day'); - } - } - $this->currentDate = clone $this->startDate; - - $rrule = (string)$this->baseEvent->RRULE; - - $parts = explode(';', $rrule); - - // If no rrule was specified, we create a default setting - if (!$rrule) { - $this->frequency = 'daily'; - $this->count = 1; - } else foreach($parts as $part) { - - list($key, $value) = explode('=', $part, 2); - - switch(strtoupper($key)) { - - case 'FREQ' : - if (!in_array( - strtolower($value), - array('secondly','minutely','hourly','daily','weekly','monthly','yearly') - )) { - throw new \InvalidArgumentException('Unknown value for FREQ=' . strtoupper($value)); - - } - $this->frequency = strtolower($value); - break; - - case 'UNTIL' : - $this->until = DateTimeParser::parse($value); - - // In some cases events are generated with an UNTIL= - // parameter before the actual start of the event. - // - // Not sure why this is happening. We assume that the - // intention was that the event only recurs once. - // - // So we are modifying the parameter so our code doesn't - // break. - if($this->until < $this->baseEvent->DTSTART->getDateTime()) { - $this->until = $this->baseEvent->DTSTART->getDateTime(); - } - break; - - case 'COUNT' : - $this->count = (int)$value; - break; - - case 'INTERVAL' : - $this->interval = (int)$value; - if ($this->interval < 1) { - throw new \InvalidArgumentException('INTERVAL in RRULE must be a positive integer!'); - } - break; - - case 'BYSECOND' : - $this->bySecond = explode(',', $value); - break; - - case 'BYMINUTE' : - $this->byMinute = explode(',', $value); - break; - - case 'BYHOUR' : - $this->byHour = explode(',', $value); - break; - - case 'BYDAY' : - $this->byDay = explode(',', strtoupper($value)); - break; - - case 'BYMONTHDAY' : - $this->byMonthDay = explode(',', $value); - break; - - case 'BYYEARDAY' : - $this->byYearDay = explode(',', $value); - break; - - case 'BYWEEKNO' : - $this->byWeekNo = explode(',', $value); - break; - - case 'BYMONTH' : - $this->byMonth = explode(',', $value); - break; - - case 'BYSETPOS' : - $this->bySetPos = explode(',', $value); - break; - - case 'WKST' : - $this->weekStart = strtoupper($value); - break; - - } - - } - - // Parsing exception dates - if (isset($this->baseEvent->EXDATE)) { - foreach($this->baseEvent->EXDATE as $exDate) { - - foreach(explode(',', (string)$exDate) as $exceptionDate) { - - $this->exceptionDates[] = - DateTimeParser::parse($exceptionDate, $this->startDate->getTimeZone()); - - } - - } - - } - - } - - /** - * Returns the current item in the list - * - * @return DateTime - */ - public function current() { - - if (!$this->valid()) return null; - return clone $this->currentDate; - - } - - /** - * This method returns the startdate for the current iteration of the - * event. - * - * @return DateTime - */ - public function getDtStart() { - - if (!$this->valid()) return null; - return clone $this->currentDate; - - } - - /** - * This method returns the enddate for the current iteration of the - * event. - * - * @return DateTime - */ - public function getDtEnd() { - - if (!$this->valid()) return null; - $dtEnd = clone $this->currentDate; - $dtEnd->add( $this->startDate->diff( $this->endDate ) ); - return clone $dtEnd; - - } - - /** - * Returns a VEVENT object with the updated start and end date. - * - * Any recurrence information is removed, and this function may return an - * 'overridden' event instead. - * - * This method always returns a cloned instance. - * - * @return Component\VEvent - */ - public function getEventObject() { - - if ($this->currentOverriddenEvent) { - return clone $this->currentOverriddenEvent; - } - $event = clone $this->baseEvent; - unset($event->RRULE); - unset($event->EXDATE); - unset($event->RDATE); - unset($event->EXRULE); - - $event->DTSTART->setDateTime($this->getDTStart(), $event->DTSTART->getDateType()); - if (isset($event->DTEND)) { - $event->DTEND->setDateTime($this->getDtEnd(), $event->DTSTART->getDateType()); - } - if ($this->counter > 0) { - $event->{'RECURRENCE-ID'} = (string)$event->DTSTART; - } - - return $event; - - } - - /** - * Returns the current item number - * - * @return int - */ - public function key() { - - return $this->counter; - - } - - /** - * Whether or not there is a 'next item' - * - * @return bool - */ - public function valid() { - - if (!is_null($this->count)) { - return $this->counter < $this->count; - } - if (!is_null($this->until)) { - return $this->currentDate <= $this->until; - } - return true; - - } - - /** - * Resets the iterator - * - * @return void - */ - public function rewind() { - - $this->currentDate = clone $this->startDate; - $this->counter = 0; - - } - - /** - * This method allows you to quickly go to the next occurrence after the - * specified date. - * - * Note that this checks the current 'endDate', not the 'stardDate'. This - * means that if you forward to January 1st, the iterator will stop at the - * first event that ends *after* January 1st. - * - * @param DateTime $dt - * @return void - */ - public function fastForward(\DateTime $dt) { - - while($this->valid() && $this->getDTEnd() <= $dt) { - $this->next(); - } - - } - - /** - * Returns true if this recurring event never ends. - * - * @return bool - */ - public function isInfinite() { - - return !$this->count && !$this->until; - - } - - /** - * Goes on to the next iteration - * - * @return void - */ - public function next() { - - /* - if (!is_null($this->count) && $this->counter >= $this->count) { - $this->currentDate = null; - }*/ - - - $previousStamp = $this->currentDate->getTimeStamp(); - - while(true) { - - $this->currentOverriddenEvent = null; - - // If we have a next date 'stored', we use that - if ($this->nextDate) { - $this->currentDate = $this->nextDate; - $currentStamp = $this->currentDate->getTimeStamp(); - $this->nextDate = null; - } else { - - // Otherwise, we calculate it - switch($this->frequency) { - - case 'hourly' : - $this->nextHourly(); - break; - - case 'daily' : - $this->nextDaily(); - break; - - case 'weekly' : - $this->nextWeekly(); - break; - - case 'monthly' : - $this->nextMonthly(); - break; - - case 'yearly' : - $this->nextYearly(); - break; - - } - $currentStamp = $this->currentDate->getTimeStamp(); - - // Checking exception dates - foreach($this->exceptionDates as $exceptionDate) { - if ($this->currentDate == $exceptionDate) { - $this->counter++; - continue 2; - } - } - foreach($this->overriddenDates as $overriddenDate) { - if ($this->currentDate == $overriddenDate) { - continue 2; - } - } - - } - - // Checking overridden events - foreach($this->overriddenEvents as $index=>$event) { - if ($index > $previousStamp && $index <= $currentStamp) { - - // We're moving the 'next date' aside, for later use. - $this->nextDate = clone $this->currentDate; - - $this->currentDate = $event->DTSTART->getDateTime(); - $this->currentOverriddenEvent = $event; - - break; - } - } - - break; - - } - - /* - if (!is_null($this->until)) { - if($this->currentDate > $this->until) { - $this->currentDate = null; - } - }*/ - - $this->counter++; - - } - - /** - * Does the processing for advancing the iterator for hourly frequency. - * - * @return void - */ - protected function nextHourly() { - - if (!$this->byHour) { - $this->currentDate->modify('+' . $this->interval . ' hours'); - return; - } - } - - /** - * Does the processing for advancing the iterator for daily frequency. - * - * @return void - */ - protected function nextDaily() { - - if (!$this->byHour && !$this->byDay) { - $this->currentDate->modify('+' . $this->interval . ' days'); - return; - } - - if (isset($this->byHour)) { - $recurrenceHours = $this->getHours(); - } - - if (isset($this->byDay)) { - $recurrenceDays = $this->getDays(); - } - - do { - - if ($this->byHour) { - if ($this->currentDate->format('G') == '23') { - // to obey the interval rule - $this->currentDate->modify('+' . $this->interval-1 . ' days'); - } - - $this->currentDate->modify('+1 hours'); - - } else { - $this->currentDate->modify('+' . $this->interval . ' days'); - - } - - // Current day of the week - $currentDay = $this->currentDate->format('w'); - - // Current hour of the day - $currentHour = $this->currentDate->format('G'); - - } while (($this->byDay && !in_array($currentDay, $recurrenceDays)) || ($this->byHour && !in_array($currentHour, $recurrenceHours))); - - } - - /** - * Does the processing for advancing the iterator for weekly frequency. - * - * @return void - */ - protected function nextWeekly() { - - if (!$this->byHour && !$this->byDay) { - $this->currentDate->modify('+' . $this->interval . ' weeks'); - return; - } - - if ($this->byHour) { - $recurrenceHours = $this->getHours(); - } - - if ($this->byDay) { - $recurrenceDays = $this->getDays(); - } - - // First day of the week: - $firstDay = $this->dayMap[$this->weekStart]; - - do { - - if ($this->byHour) { - $this->currentDate->modify('+1 hours'); - } else { - $this->currentDate->modify('+1 days'); - } - - // Current day of the week - $currentDay = (int) $this->currentDate->format('w'); - - // Current hour of the day - $currentHour = (int) $this->currentDate->format('G'); - - // We need to roll over to the next week - if ($currentDay === $firstDay && (!$this->byHour || $currentHour == '0')) { - $this->currentDate->modify('+' . $this->interval-1 . ' weeks'); - - // We need to go to the first day of this week, but only if we - // are not already on this first day of this week. - if($this->currentDate->format('w') != $firstDay) { - $this->currentDate->modify('last ' . $this->dayNames[$this->dayMap[$this->weekStart]]); - } - } - - // We have a match - } while (($this->byDay && !in_array($currentDay, $recurrenceDays)) || ($this->byHour && !in_array($currentHour, $recurrenceHours))); - } - - /** - * Does the processing for advancing the iterator for monthly frequency. - * - * @return void - */ - protected function nextMonthly() { - - $currentDayOfMonth = $this->currentDate->format('j'); - if (!$this->byMonthDay && !$this->byDay) { - - // If the current day is higher than the 28th, rollover can - // occur to the next month. We Must skip these invalid - // entries. - if ($currentDayOfMonth < 29) { - $this->currentDate->modify('+' . $this->interval . ' months'); - } else { - $increase = 0; - do { - $increase++; - $tempDate = clone $this->currentDate; - $tempDate->modify('+ ' . ($this->interval*$increase) . ' months'); - } while ($tempDate->format('j') != $currentDayOfMonth); - $this->currentDate = $tempDate; - } - return; - } - - while(true) { - - $occurrences = $this->getMonthlyOccurrences(); - - foreach($occurrences as $occurrence) { - - // The first occurrence thats higher than the current - // day of the month wins. - if ($occurrence > $currentDayOfMonth) { - break 2; - } - - } - - // If we made it all the way here, it means there were no - // valid occurrences, and we need to advance to the next - // month. - $this->currentDate->modify('first day of this month'); - $this->currentDate->modify('+ ' . $this->interval . ' months'); - - // This goes to 0 because we need to start counting at hte - // beginning. - $currentDayOfMonth = 0; - - } - - $this->currentDate->setDate($this->currentDate->format('Y'), $this->currentDate->format('n'), $occurrence); - - } - - /** - * Does the processing for advancing the iterator for yearly frequency. - * - * @return void - */ - protected function nextYearly() { - - $currentMonth = $this->currentDate->format('n'); - $currentYear = $this->currentDate->format('Y'); - $currentDayOfMonth = $this->currentDate->format('j'); - - // No sub-rules, so we just advance by year - if (!$this->byMonth) { - - // Unless it was a leap day! - if ($currentMonth==2 && $currentDayOfMonth==29) { - - $counter = 0; - do { - $counter++; - // Here we increase the year count by the interval, until - // we hit a date that's also in a leap year. - // - // We could just find the next interval that's dividable by - // 4, but that would ignore the rule that there's no leap - // year every year that's dividable by a 100, but not by - // 400. (1800, 1900, 2100). So we just rely on the datetime - // functions instead. - $nextDate = clone $this->currentDate; - $nextDate->modify('+ ' . ($this->interval*$counter) . ' years'); - } while ($nextDate->format('n')!=2); - $this->currentDate = $nextDate; - - return; - - } - - // The easiest form - $this->currentDate->modify('+' . $this->interval . ' years'); - return; - - } - - $currentMonth = $this->currentDate->format('n'); - $currentYear = $this->currentDate->format('Y'); - $currentDayOfMonth = $this->currentDate->format('j'); - - $advancedToNewMonth = false; - - // If we got a byDay or getMonthDay filter, we must first expand - // further. - if ($this->byDay || $this->byMonthDay) { - - while(true) { - - $occurrences = $this->getMonthlyOccurrences(); - - foreach($occurrences as $occurrence) { - - // The first occurrence that's higher than the current - // day of the month wins. - // If we advanced to the next month or year, the first - // occurrence is always correct. - if ($occurrence > $currentDayOfMonth || $advancedToNewMonth) { - break 2; - } - - } - - // If we made it here, it means we need to advance to - // the next month or year. - $currentDayOfMonth = 1; - $advancedToNewMonth = true; - do { - - $currentMonth++; - if ($currentMonth>12) { - $currentYear+=$this->interval; - $currentMonth = 1; - } - } while (!in_array($currentMonth, $this->byMonth)); - - $this->currentDate->setDate($currentYear, $currentMonth, $currentDayOfMonth); - - } - - // If we made it here, it means we got a valid occurrence - $this->currentDate->setDate($currentYear, $currentMonth, $occurrence); - return; - - } else { - - // These are the 'byMonth' rules, if there are no byDay or - // byMonthDay sub-rules. - do { - - $currentMonth++; - if ($currentMonth>12) { - $currentYear+=$this->interval; - $currentMonth = 1; - } - } while (!in_array($currentMonth, $this->byMonth)); - $this->currentDate->setDate($currentYear, $currentMonth, $currentDayOfMonth); - - return; - - } - - } - - /** - * Returns all the occurrences for a monthly frequency with a 'byDay' or - * 'byMonthDay' expansion for the current month. - * - * The returned list is an array of integers with the day of month (1-31). - * - * @return array - */ - protected function getMonthlyOccurrences() { - - $startDate = clone $this->currentDate; - - $byDayResults = array(); - - // Our strategy is to simply go through the byDays, advance the date to - // that point and add it to the results. - if ($this->byDay) foreach($this->byDay as $day) { - - $dayName = $this->dayNames[$this->dayMap[substr($day,-2)]]; - - // Dayname will be something like 'wednesday'. Now we need to find - // all wednesdays in this month. - $dayHits = array(); - - $checkDate = clone $startDate; - $checkDate->modify('first day of this month'); - $checkDate->modify($dayName); - - do { - $dayHits[] = $checkDate->format('j'); - $checkDate->modify('next ' . $dayName); - } while ($checkDate->format('n') === $startDate->format('n')); - - // So now we have 'all wednesdays' for month. It is however - // possible that the user only really wanted the 1st, 2nd or last - // wednesday. - if (strlen($day)>2) { - $offset = (int)substr($day,0,-2); - - if ($offset>0) { - // It is possible that the day does not exist, such as a - // 5th or 6th wednesday of the month. - if (isset($dayHits[$offset-1])) { - $byDayResults[] = $dayHits[$offset-1]; - } - } else { - - // if it was negative we count from the end of the array - $byDayResults[] = $dayHits[count($dayHits) + $offset]; - } - } else { - // There was no counter (first, second, last wednesdays), so we - // just need to add the all to the list). - $byDayResults = array_merge($byDayResults, $dayHits); - - } - - } - - $byMonthDayResults = array(); - if ($this->byMonthDay) foreach($this->byMonthDay as $monthDay) { - - // Removing values that are out of range for this month - if ($monthDay > $startDate->format('t') || - $monthDay < 0-$startDate->format('t')) { - continue; - } - if ($monthDay>0) { - $byMonthDayResults[] = $monthDay; - } else { - // Negative values - $byMonthDayResults[] = $startDate->format('t') + 1 + $monthDay; - } - } - - // If there was just byDay or just byMonthDay, they just specify our - // (almost) final list. If both were provided, then byDay limits the - // list. - if ($this->byMonthDay && $this->byDay) { - $result = array_intersect($byMonthDayResults, $byDayResults); - } elseif ($this->byMonthDay) { - $result = $byMonthDayResults; - } else { - $result = $byDayResults; - } - $result = array_unique($result); - sort($result, SORT_NUMERIC); - - // The last thing that needs checking is the BYSETPOS. If it's set, it - // means only certain items in the set survive the filter. - if (!$this->bySetPos) { - return $result; - } - - $filteredResult = array(); - foreach($this->bySetPos as $setPos) { - - if ($setPos<0) { - $setPos = count($result)-($setPos+1); - } - if (isset($result[$setPos-1])) { - $filteredResult[] = $result[$setPos-1]; - } - } - - sort($filteredResult, SORT_NUMERIC); - return $filteredResult; - - } - - protected function getHours() - { - $recurrenceHours = array(); - foreach($this->byHour as $byHour) { - $recurrenceHours[] = $byHour; - } - - return $recurrenceHours; - } - - protected function getDays() - { - $recurrenceDays = array(); - foreach($this->byDay as $byDay) { - - // The day may be preceeded with a positive (+n) or - // negative (-n) integer. However, this does not make - // sense in 'weekly' so we ignore it here. - $recurrenceDays[] = $this->dayMap[substr($byDay,-2)]; - - } - - return $recurrenceDays; - } -} - diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Splitter/ICalendar.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Splitter/ICalendar.php deleted file mode 100644 index 8eed0cac40..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Splitter/ICalendar.php +++ /dev/null @@ -1,111 +0,0 @@ -children as $component) { - if (!$component instanceof VObject\Component) { - continue; - } - - // Get all timezones - if ($component->name === 'VTIMEZONE') { - $this->vtimezones[(string)$component->TZID] = $component; - continue; - } - - // Get component UID for recurring Events search - if($component->UID) { - $uid = (string)$component->UID; - } else { - // Generating a random UID - $uid = sha1(microtime()) . '-vobjectimport'; - } - - // Take care of recurring events - if (!array_key_exists($uid, $this->objects)) { - $this->objects[$uid] = VObject\Component::create('VCALENDAR'); - } - - $this->objects[$uid]->add(clone $component); - } - - } - - /** - * Every time getNext() is called, a new object will be parsed, until we - * hit the end of the stream. - * - * When the end is reached, null will be returned. - * - * @return Sabre\VObject\Component|null - */ - public function getNext() { - - if($object=array_shift($this->objects)) { - - // create our baseobject - $object->version = '2.0'; - $object->prodid = '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN'; - $object->calscale = 'GREGORIAN'; - - // add vtimezone information to obj (if we have it) - foreach ($this->vtimezones as $vtimezone) { - $object->add($vtimezone); - } - - return $object; - - } else { - - return null; - - } - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Splitter/SplitterInterface.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Splitter/SplitterInterface.php deleted file mode 100644 index 54b1ff3c5d..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Splitter/SplitterInterface.php +++ /dev/null @@ -1,39 +0,0 @@ -input = $input; - - } - - /** - * Every time getNext() is called, a new object will be parsed, until we - * hit the end of the stream. - * - * When the end is reached, null will be returned. - * - * @return Sabre\VObject\Component|null - */ - public function getNext() { - - $vcard = ''; - - do { - - if (feof($this->input)) { - return false; - } - - $line = fgets($this->input); - $vcard .= $line; - - } while(strtoupper(substr($line,0,4))!=="END:"); - - $object = VObject\Reader::read($vcard); - - if($object->name !== 'VCARD') { - throw new \InvalidArgumentException("Thats no vCard!", 1); - } - - return $object; - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/StringUtil.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/StringUtil.php deleted file mode 100644 index fe1854d6cc..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/StringUtil.php +++ /dev/null @@ -1,61 +0,0 @@ -'Australia/Darwin', - 'AUS Eastern Standard Time'=>'Australia/Sydney', - 'Afghanistan Standard Time'=>'Asia/Kabul', - 'Alaskan Standard Time'=>'America/Anchorage', - 'Arab Standard Time'=>'Asia/Riyadh', - 'Arabian Standard Time'=>'Asia/Dubai', - 'Arabic Standard Time'=>'Asia/Baghdad', - 'Argentina Standard Time'=>'America/Buenos_Aires', - 'Armenian Standard Time'=>'Asia/Yerevan', - 'Atlantic Standard Time'=>'America/Halifax', - 'Azerbaijan Standard Time'=>'Asia/Baku', - 'Azores Standard Time'=>'Atlantic/Azores', - 'Bangladesh Standard Time'=>'Asia/Dhaka', - 'Canada Central Standard Time'=>'America/Regina', - 'Cape Verde Standard Time'=>'Atlantic/Cape_Verde', - 'Caucasus Standard Time'=>'Asia/Yerevan', - 'Cen. Australia Standard Time'=>'Australia/Adelaide', - 'Central America Standard Time'=>'America/Guatemala', - 'Central Asia Standard Time'=>'Asia/Almaty', - 'Central Brazilian Standard Time'=>'America/Cuiaba', - 'Central Europe Standard Time'=>'Europe/Budapest', - 'Central European Standard Time'=>'Europe/Warsaw', - 'Central Pacific Standard Time'=>'Pacific/Guadalcanal', - 'Central Standard Time'=>'America/Chicago', - 'Central Standard Time (Mexico)'=>'America/Mexico_City', - 'China Standard Time'=>'Asia/Shanghai', - 'Dateline Standard Time'=>'Etc/GMT+12', - 'E. Africa Standard Time'=>'Africa/Nairobi', - 'E. Australia Standard Time'=>'Australia/Brisbane', - 'E. Europe Standard Time'=>'Europe/Minsk', - 'E. South America Standard Time'=>'America/Sao_Paulo', - 'Eastern Standard Time'=>'America/New_York', - 'Egypt Standard Time'=>'Africa/Cairo', - 'Ekaterinburg Standard Time'=>'Asia/Yekaterinburg', - 'FLE Standard Time'=>'Europe/Kiev', - 'Fiji Standard Time'=>'Pacific/Fiji', - 'GMT Standard Time'=>'Europe/London', - 'GTB Standard Time'=>'Europe/Istanbul', - 'Georgian Standard Time'=>'Asia/Tbilisi', - 'Greenland Standard Time'=>'America/Godthab', - 'Greenwich Standard Time'=>'Atlantic/Reykjavik', - 'Hawaiian Standard Time'=>'Pacific/Honolulu', - 'India Standard Time'=>'Asia/Calcutta', - 'Iran Standard Time'=>'Asia/Tehran', - 'Israel Standard Time'=>'Asia/Jerusalem', - 'Jordan Standard Time'=>'Asia/Amman', - 'Kamchatka Standard Time'=>'Asia/Kamchatka', - 'Korea Standard Time'=>'Asia/Seoul', - 'Magadan Standard Time'=>'Asia/Magadan', - 'Mauritius Standard Time'=>'Indian/Mauritius', - 'Mexico Standard Time'=>'America/Mexico_City', - 'Mexico Standard Time 2'=>'America/Chihuahua', - 'Mid-Atlantic Standard Time'=>'Etc/GMT-2', - 'Middle East Standard Time'=>'Asia/Beirut', - 'Montevideo Standard Time'=>'America/Montevideo', - 'Morocco Standard Time'=>'Africa/Casablanca', - 'Mountain Standard Time'=>'America/Denver', - 'Mountain Standard Time (Mexico)'=>'America/Chihuahua', - 'Myanmar Standard Time'=>'Asia/Rangoon', - 'N. Central Asia Standard Time'=>'Asia/Novosibirsk', - 'Namibia Standard Time'=>'Africa/Windhoek', - 'Nepal Standard Time'=>'Asia/Katmandu', - 'New Zealand Standard Time'=>'Pacific/Auckland', - 'Newfoundland Standard Time'=>'America/St_Johns', - 'North Asia East Standard Time'=>'Asia/Irkutsk', - 'North Asia Standard Time'=>'Asia/Krasnoyarsk', - 'Pacific SA Standard Time'=>'America/Santiago', - 'Pacific Standard Time'=>'America/Los_Angeles', - 'Pacific Standard Time (Mexico)'=>'America/Santa_Isabel', - 'Pakistan Standard Time'=>'Asia/Karachi', - 'Paraguay Standard Time'=>'America/Asuncion', - 'Romance Standard Time'=>'Europe/Paris', - 'Russian Standard Time'=>'Europe/Moscow', - 'SA Eastern Standard Time'=>'America/Cayenne', - 'SA Pacific Standard Time'=>'America/Bogota', - 'SA Western Standard Time'=>'America/La_Paz', - 'SE Asia Standard Time'=>'Asia/Bangkok', - 'Samoa Standard Time'=>'Pacific/Apia', - 'Singapore Standard Time'=>'Asia/Singapore', - 'South Africa Standard Time'=>'Africa/Johannesburg', - 'Sri Lanka Standard Time'=>'Asia/Colombo', - 'Syria Standard Time'=>'Asia/Damascus', - 'Taipei Standard Time'=>'Asia/Taipei', - 'Tasmania Standard Time'=>'Australia/Hobart', - 'Tokyo Standard Time'=>'Asia/Tokyo', - 'Tonga Standard Time'=>'Pacific/Tongatapu', - 'US Eastern Standard Time'=>'America/Indianapolis', - 'US Mountain Standard Time'=>'America/Phoenix', - 'UTC+12'=>'Etc/GMT-12', - 'UTC-02'=>'Etc/GMT+2', - 'UTC-11'=>'Etc/GMT+11', - 'Ulaanbaatar Standard Time'=>'Asia/Ulaanbaatar', - 'Venezuela Standard Time'=>'America/Caracas', - 'Vladivostok Standard Time'=>'Asia/Vladivostok', - 'W. Australia Standard Time'=>'Australia/Perth', - 'W. Central Africa Standard Time'=>'Africa/Lagos', - 'W. Europe Standard Time'=>'Europe/Berlin', - 'West Asia Standard Time'=>'Asia/Tashkent', - 'West Pacific Standard Time'=>'Pacific/Port_Moresby', - 'Yakutsk Standard Time'=>'Asia/Yakutsk', - - // Microsoft exchange timezones - // Source: - // http://msdn.microsoft.com/en-us/library/ms988620%28v=exchg.65%29.aspx - // - // Correct timezones deduced with help from: - // http://en.wikipedia.org/wiki/List_of_tz_database_time_zones - 'Universal Coordinated Time' => 'UTC', - 'Casablanca, Monrovia' => 'Africa/Casablanca', - 'Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London' => 'Europe/Lisbon', - 'Greenwich Mean Time; Dublin, Edinburgh, London' => 'Europe/London', - 'Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' => 'Europe/Berlin', - 'Belgrade, Pozsony, Budapest, Ljubljana, Prague' => 'Europe/Prague', - 'Brussels, Copenhagen, Madrid, Paris' => 'Europe/Paris', - 'Paris, Madrid, Brussels, Copenhagen' => 'Europe/Paris', - 'Prague, Central Europe' => 'Europe/Prague', - 'Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb' => 'Europe/Sarajevo', - 'West Central Africa' => 'Africa/Luanda', // This was a best guess - 'Athens, Istanbul, Minsk' => 'Europe/Athens', - 'Bucharest' => 'Europe/Bucharest', - 'Cairo' => 'Africa/Cairo', - 'Harare, Pretoria' => 'Africa/Harare', - 'Helsinki, Riga, Tallinn' => 'Europe/Helsinki', - 'Israel, Jerusalem Standard Time' => 'Asia/Jerusalem', - 'Baghdad' => 'Asia/Baghdad', - 'Arab, Kuwait, Riyadh' => 'Asia/Kuwait', - 'Moscow, St. Petersburg, Volgograd' => 'Europe/Moscow', - 'East Africa, Nairobi' => 'Africa/Nairobi', - 'Tehran' => 'Asia/Tehran', - 'Abu Dhabi, Muscat' => 'Asia/Muscat', // Best guess - 'Baku, Tbilisi, Yerevan' => 'Asia/Baku', - 'Kabul' => 'Asia/Kabul', - 'Ekaterinburg' => 'Asia/Yekaterinburg', - 'Islamabad, Karachi, Tashkent' => 'Asia/Karachi', - 'Kolkata, Chennai, Mumbai, New Delhi, India Standard Time' => 'Asia/Calcutta', - 'Kathmandu, Nepal' => 'Asia/Kathmandu', - 'Almaty, Novosibirsk, North Central Asia' => 'Asia/Almaty', - 'Astana, Dhaka' => 'Asia/Dhaka', - 'Sri Jayawardenepura, Sri Lanka' => 'Asia/Colombo', - 'Rangoon' => 'Asia/Rangoon', - 'Bangkok, Hanoi, Jakarta' => 'Asia/Bangkok', - 'Krasnoyarsk' => 'Asia/Krasnoyarsk', - 'Beijing, Chongqing, Hong Kong SAR, Urumqi' => 'Asia/Shanghai', - 'Irkutsk, Ulaan Bataar' => 'Asia/Irkutsk', - 'Kuala Lumpur, Singapore' => 'Asia/Singapore', - 'Perth, Western Australia' => 'Australia/Perth', - 'Taipei' => 'Asia/Taipei', - 'Osaka, Sapporo, Tokyo' => 'Asia/Tokyo', - 'Seoul, Korea Standard time' => 'Asia/Seoul', - 'Yakutsk' => 'Asia/Yakutsk', - 'Adelaide, Central Australia' => 'Australia/Adelaide', - 'Darwin' => 'Australia/Darwin', - 'Brisbane, East Australia' => 'Australia/Brisbane', - 'Canberra, Melbourne, Sydney, Hobart (year 2000 only)' => 'Australia/Sydney', - 'Guam, Port Moresby' => 'Pacific/Guam', - 'Hobart, Tasmania' => 'Australia/Hobart', - 'Vladivostok' => 'Asia/Vladivostok', - 'Magadan, Solomon Is., New Caledonia' => 'Asia/Magadan', - 'Auckland, Wellington' => 'Pacific/Auckland', - 'Fiji Islands, Kamchatka, Marshall Is.' => 'Pacific/Fiji', - 'Nuku\'alofa, Tonga' => 'Pacific/Tongatapu', - 'Azores' => 'Atlantic/Azores', - 'Cape Verde Is.' => 'Atlantic/Cape_Verde', - 'Mid-Atlantic' => 'America/Noronha', - 'Brasilia' => 'America/Sao_Paulo', // Best guess - 'Buenos Aires' => 'America/Argentina/Buenos_Aires', - 'Greenland' => 'America/Godthab', - 'Newfoundland' => 'America/St_Johns', - 'Atlantic Time (Canada)' => 'America/Halifax', - 'Caracas, La Paz' => 'America/Caracas', - 'Santiago' => 'America/Santiago', - 'Bogota, Lima, Quito' => 'America/Bogota', - 'Eastern Time (US & Canada)' => 'America/New_York', - 'Indiana (East)' => 'America/Indiana/Indianapolis', - 'Central America' => 'America/Guatemala', - 'Central Time (US & Canada)' => 'America/Chicago', - 'Mexico City, Tegucigalpa' => 'America/Mexico_City', - 'Saskatchewan' => 'America/Edmonton', - 'Arizona' => 'America/Phoenix', - 'Mountain Time (US & Canada)' => 'America/Denver', // Best guess - 'Pacific Time (US & Canada); Tijuana' => 'America/Los_Angeles', // Best guess - 'Alaska' => 'America/Anchorage', - 'Hawaii' => 'Pacific/Honolulu', - 'Midway Island, Samoa' => 'Pacific/Midway', - 'Eniwetok, Kwajalein, Dateline Time' => 'Pacific/Kwajalein', - - // The following list are timezone names that could be generated by - // Lotus / Domino - 'Dateline' => 'Etc/GMT-12', - 'Samoa' => 'Pacific/Apia', - 'Hawaiian' => 'Pacific/Honolulu', - 'Alaskan' => 'America/Anchorage', - 'Pacific' => 'America/Los_Angeles', - 'Pacific Standard Time' => 'America/Los_Angeles', - 'Mexico Standard Time 2' => 'America/Chihuahua', - 'Mountain' => 'America/Denver', - 'Mountain Standard Time' => 'America/Chihuahua', - 'US Mountain' => 'America/Phoenix', - 'Canada Central' => 'America/Edmonton', - 'Central America' => 'America/Guatemala', - 'Central' => 'America/Chicago', - 'Central Standard Time' => 'America/Mexico_City', - 'Mexico' => 'America/Mexico_City', - 'Eastern' => 'America/New_York', - 'SA Pacific' => 'America/Bogota', - 'US Eastern' => 'America/Indiana/Indianapolis', - 'Venezuela' => 'America/Caracas', - 'Atlantic' => 'America/Halifax', - 'Central Brazilian' => 'America/Manaus', - 'Pacific SA' => 'America/Santiago', - 'SA Western' => 'America/La_Paz', - 'Newfoundland' => 'America/St_Johns', - 'Argentina' => 'America/Argentina/Buenos_Aires', - 'E. South America' => 'America/Belem', - 'Greenland' => 'America/Godthab', - 'Montevideo' => 'America/Montevideo', - 'SA Eastern' => 'America/Belem', - 'Mid-Atlantic' => 'Etc/GMT-2', - 'Azores' => 'Atlantic/Azores', - 'Cape Verde' => 'Atlantic/Cape_Verde', - 'Greenwich' => 'Atlantic/Reykjavik', // No I'm serious.. Greenwich is not GMT. - 'Morocco' => 'Africa/Casablanca', - 'Central Europe' => 'Europe/Prague', - 'Central European' => 'Europe/Sarajevo', - 'Romance' => 'Europe/Paris', - 'W. Central Africa' => 'Africa/Lagos', // Best guess - 'W. Europe' => 'Europe/Amsterdam', - 'E. Europe' => 'Europe/Minsk', - 'Egypt' => 'Africa/Cairo', - 'FLE' => 'Europe/Helsinki', - 'GTB' => 'Europe/Athens', - 'Israel' => 'Asia/Jerusalem', - 'Jordan' => 'Asia/Amman', - 'Middle East' => 'Asia/Beirut', - 'Namibia' => 'Africa/Windhoek', - 'South Africa' => 'Africa/Harare', - 'Arab' => 'Asia/Kuwait', - 'Arabic' => 'Asia/Baghdad', - 'E. Africa' => 'Africa/Nairobi', - 'Georgian' => 'Asia/Tbilisi', - 'Russian' => 'Europe/Moscow', - 'Iran' => 'Asia/Tehran', - 'Arabian' => 'Asia/Muscat', - 'Armenian' => 'Asia/Yerevan', - 'Azerbijan' => 'Asia/Baku', - 'Caucasus' => 'Asia/Yerevan', - 'Mauritius' => 'Indian/Mauritius', - 'Afghanistan' => 'Asia/Kabul', - 'Ekaterinburg' => 'Asia/Yekaterinburg', - 'Pakistan' => 'Asia/Karachi', - 'West Asia' => 'Asia/Tashkent', - 'India' => 'Asia/Calcutta', - 'Sri Lanka' => 'Asia/Colombo', - 'Nepal' => 'Asia/Kathmandu', - 'Central Asia' => 'Asia/Dhaka', - 'N. Central Asia' => 'Asia/Almaty', - 'Myanmar' => 'Asia/Rangoon', - 'North Asia' => 'Asia/Krasnoyarsk', - 'SE Asia' => 'Asia/Bangkok', - 'China' => 'Asia/Shanghai', - 'North Asia East' => 'Asia/Irkutsk', - 'Singapore' => 'Asia/Singapore', - 'Taipei' => 'Asia/Taipei', - 'W. Australia' => 'Australia/Perth', - 'Korea' => 'Asia/Seoul', - 'Tokyo' => 'Asia/Tokyo', - 'Yakutsk' => 'Asia/Yakutsk', - 'AUS Central' => 'Australia/Darwin', - 'Cen. Australia' => 'Australia/Adelaide', - 'AUS Eastern' => 'Australia/Sydney', - 'E. Australia' => 'Australia/Brisbane', - 'Tasmania' => 'Australia/Hobart', - 'Vladivostok' => 'Asia/Vladivostok', - 'West Pacific' => 'Pacific/Guam', - 'Central Pacific' => 'Asia/Magadan', - 'Fiji' => 'Pacific/Fiji', - 'New Zealand' => 'Pacific/Auckland', - 'Tonga' => 'Pacific/Tongatapu', - ); - - /** - * List of microsoft exchange timezone ids. - * - * Source: http://msdn.microsoft.com/en-us/library/aa563018(loband).aspx - */ - public static $microsoftExchangeMap = array( - 0 => 'UTC', - 31 => 'Africa/Casablanca', - - // Insanely, id #2 is used for both Europe/Lisbon, and Europe/Sarajevo. - // I'm not even kidding.. We handle this special case in the - // getTimeZone method. - 2 => 'Europe/Lisbon', - 1 => 'Europe/London', - 4 => 'Europe/Berlin', - 6 => 'Europe/Prague', - 3 => 'Europe/Paris', - 69 => 'Africa/Luanda', // This was a best guess - 7 => 'Europe/Athens', - 5 => 'Europe/Bucharest', - 49 => 'Africa/Cairo', - 50 => 'Africa/Harare', - 59 => 'Europe/Helsinki', - 27 => 'Asia/Jerusalem', - 26 => 'Asia/Baghdad', - 74 => 'Asia/Kuwait', - 51 => 'Europe/Moscow', - 56 => 'Africa/Nairobi', - 25 => 'Asia/Tehran', - 24 => 'Asia/Muscat', // Best guess - 54 => 'Asia/Baku', - 48 => 'Asia/Kabul', - 58 => 'Asia/Yekaterinburg', - 47 => 'Asia/Karachi', - 23 => 'Asia/Calcutta', - 62 => 'Asia/Kathmandu', - 46 => 'Asia/Almaty', - 71 => 'Asia/Dhaka', - 66 => 'Asia/Colombo', - 61 => 'Asia/Rangoon', - 22 => 'Asia/Bangkok', - 64 => 'Asia/Krasnoyarsk', - 45 => 'Asia/Shanghai', - 63 => 'Asia/Irkutsk', - 21 => 'Asia/Singapore', - 73 => 'Australia/Perth', - 75 => 'Asia/Taipei', - 20 => 'Asia/Tokyo', - 72 => 'Asia/Seoul', - 70 => 'Asia/Yakutsk', - 19 => 'Australia/Adelaide', - 44 => 'Australia/Darwin', - 18 => 'Australia/Brisbane', - 76 => 'Australia/Sydney', - 43 => 'Pacific/Guam', - 42 => 'Australia/Hobart', - 68 => 'Asia/Vladivostok', - 41 => 'Asia/Magadan', - 17 => 'Pacific/Auckland', - 40 => 'Pacific/Fiji', - 67 => 'Pacific/Tongatapu', - 29 => 'Atlantic/Azores', - 53 => 'Atlantic/Cape_Verde', - 30 => 'America/Noronha', - 8 => 'America/Sao_Paulo', // Best guess - 32 => 'America/Argentina/Buenos_Aires', - 60 => 'America/Godthab', - 28 => 'America/St_Johns', - 9 => 'America/Halifax', - 33 => 'America/Caracas', - 65 => 'America/Santiago', - 35 => 'America/Bogota', - 10 => 'America/New_York', - 34 => 'America/Indiana/Indianapolis', - 55 => 'America/Guatemala', - 11 => 'America/Chicago', - 37 => 'America/Mexico_City', - 36 => 'America/Edmonton', - 38 => 'America/Phoenix', - 12 => 'America/Denver', // Best guess - 13 => 'America/Los_Angeles', // Best guess - 14 => 'America/Anchorage', - 15 => 'Pacific/Honolulu', - 16 => 'Pacific/Midway', - 39 => 'Pacific/Kwajalein', - ); - - /** - * This method will try to find out the correct timezone for an iCalendar - * date-time value. - * - * You must pass the contents of the TZID parameter, as well as the full - * calendar. - * - * If the lookup fails, this method will return the default PHP timezone - * (as configured using date_default_timezone_set, or the date.timezone ini - * setting). - * - * Alternatively, if $failIfUncertain is set to true, it will throw an - * exception if we cannot accurately determine the timezone. - * - * @param string $tzid - * @param Sabre\VObject\Component $vcalendar - * @return DateTimeZone - */ - static public function getTimeZone($tzid, Component $vcalendar = null, $failIfUncertain = false) { - - // First we will just see if the tzid is a support timezone identifier. - try { - return new \DateTimeZone($tzid); - } catch (\Exception $e) { - } - - // Next, we check if the tzid is somewhere in our tzid map. - if (isset(self::$map[$tzid])) { - return new \DateTimeZone(self::$map[$tzid]); - } - - // Maybe the author was hyper-lazy and just included an offset. We - // support it, but we aren't happy about it. - if (preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) { - return new \DateTimeZone('Etc/GMT' . $matches[1] . ltrim(substr($matches[2],0,2),'0')); - } - - if ($vcalendar) { - - // If that didn't work, we will scan VTIMEZONE objects - foreach($vcalendar->select('VTIMEZONE') as $vtimezone) { - - if ((string)$vtimezone->TZID === $tzid) { - - // Some clients add 'X-LIC-LOCATION' with the olson name. - if (isset($vtimezone->{'X-LIC-LOCATION'})) { - - $lic = (string)$vtimezone->{'X-LIC-LOCATION'}; - - // Libical generators may specify strings like - // "SystemV/EST5EDT". For those we must remove the - // SystemV part. - if (substr($lic,0,8)==='SystemV/') { - $lic = substr($lic,8); - } - - try { - return new \DateTimeZone($lic); - } catch (\Exception $e) { - } - - } - // Microsoft may add a magic number, which we also have an - // answer for. - if (isset($vtimezone->{'X-MICROSOFT-CDO-TZID'})) { - $cdoId = (int)$vtimezone->{'X-MICROSOFT-CDO-TZID'}->value; - - // 2 can mean both Europe/Lisbon and Europe/Sarajevo. - if ($cdoId===2 && strpos((string)$vtimezone->TZID, 'Sarajevo')!==false) { - return new \DateTimeZone('Europe/Sarajevo'); - } - - if (isset(self::$microsoftExchangeMap[$cdoId])) { - return new \DateTimeZone(self::$microsoftExchangeMap[$cdoId]); - } - } - - } - - } - - } - - if ($failIfUncertain) { - throw new \InvalidArgumentException('We were unable to determine the correct PHP timezone for tzid: ' . $tzid); - } - - // If we got all the way here, we default to UTC. - return new \DateTimeZone(date_default_timezone_get()); - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Version.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Version.php deleted file mode 100644 index 7fdf1fdcea..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/lib/Sabre/VObject/Version.php +++ /dev/null @@ -1,24 +0,0 @@ -assertEquals($outcome, $valarm->isInTimeRange($start, $end)); - - } - - public function timeRangeTestData() { - - $tests = array(); - - // Hard date and time - $valarm1 = Component::create('VALARM'); - $valarm1->TRIGGER = '20120312T130000Z'; - $valarm1->TRIGGER['VALUE'] = 'DATE-TIME'; - - $tests[] = array($valarm1, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-04-01 01:00:00'), true); - $tests[] = array($valarm1, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-03-10 01:00:00'), false); - - // Relation to start time of event - $valarm2 = Component::create('VALARM'); - $valarm2->TRIGGER = '-P1D'; - $valarm2->TRIGGER['VALUE'] = 'DURATION'; - - $vevent2 = Component::create('VEVENT'); - $vevent2->DTSTART = '20120313T130000Z'; - $vevent2->add($valarm2); - - $tests[] = array($valarm2, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-04-01 01:00:00'), true); - $tests[] = array($valarm2, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-03-10 01:00:00'), false); - - // Relation to end time of event - $valarm3 = Component::create('VALARM'); - $valarm3->TRIGGER = '-P1D'; - $valarm3->TRIGGER['VALUE'] = 'DURATION'; - $valarm3->TRIGGER['RELATED']= 'END'; - - $vevent3 = Component::create('VEVENT'); - $vevent3->DTSTART = '20120301T130000Z'; - $vevent3->DTEND = '20120401T130000Z'; - $vevent3->add($valarm3); - - $tests[] = array($valarm3, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), false); - $tests[] = array($valarm3, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), true); - - // Relation to end time of todo - $valarm4 = Component::create('VALARM'); - $valarm4->TRIGGER = '-P1D'; - $valarm4->TRIGGER['VALUE'] = 'DURATION'; - $valarm4->TRIGGER['RELATED']= 'END'; - - $vtodo4 = Component::create('VTODO'); - $vtodo4->DTSTART = '20120301T130000Z'; - $vtodo4->DUE = '20120401T130000Z'; - $vtodo4->add($valarm4); - - $tests[] = array($valarm4, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), false); - $tests[] = array($valarm4, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), true); - - // Relation to start time of event + repeat - $valarm5 = Component::create('VALARM'); - $valarm5->TRIGGER = '-P1D'; - $valarm5->TRIGGER['VALUE'] = 'DURATION'; - $valarm5->REPEAT = 10; - $valarm5->DURATION = 'P1D'; - - $vevent5 = Component::create('VEVENT'); - $vevent5->DTSTART = '20120301T130000Z'; - $vevent5->add($valarm5); - - $tests[] = array($valarm5, new DateTime('2012-03-09 01:00:00'), new DateTime('2012-03-10 01:00:00'), true); - - // Relation to start time of event + duration, but no repeat - $valarm6 = Component::create('VALARM'); - $valarm6->TRIGGER = '-P1D'; - $valarm6->TRIGGER['VALUE'] = 'DURATION'; - $valarm6->DURATION = 'P1D'; - - $vevent6 = Component::create('VEVENT'); - $vevent6->DTSTART = '20120313T130000Z'; - $vevent6->add($valarm6); - - $tests[] = array($valarm6, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-04-01 01:00:00'), true); - $tests[] = array($valarm6, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-03-10 01:00:00'), false); - - - // Relation to end time of event (DURATION instead of DTEND) - $valarm7 = Component::create('VALARM'); - $valarm7->TRIGGER = '-P1D'; - $valarm7->TRIGGER['VALUE'] = 'DURATION'; - $valarm7->TRIGGER['RELATED']= 'END'; - - $vevent7 = Component::create('VEVENT'); - $vevent7->DTSTART = '20120301T130000Z'; - $vevent7->DURATION = 'P30D'; - $vevent7->add($valarm7); - - $tests[] = array($valarm7, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), false); - $tests[] = array($valarm7, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), true); - - // Relation to end time of event (No DTEND or DURATION) - $valarm7 = Component::create('VALARM'); - $valarm7->TRIGGER = '-P1D'; - $valarm7->TRIGGER['VALUE'] = 'DURATION'; - $valarm7->TRIGGER['RELATED']= 'END'; - - $vevent7 = Component::create('VEVENT'); - $vevent7->DTSTART = '20120301T130000Z'; - $vevent7->add($valarm7); - - $tests[] = array($valarm7, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), true); - $tests[] = array($valarm7, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), false); - - - return $tests; - } - - /** - * @expectedException LogicException - */ - public function testInTimeRangeInvalidComponent() { - - $valarm = Component::create('VALARM'); - $valarm->TRIGGER = '-P1D'; - $valarm->TRIGGER['RELATED'] = 'END'; - - $vjournal = Component::create('VJOURNAL'); - $vjournal->add($valarm); - - $valarm->isInTimeRange(new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00')); - - } - - /** - * This bug was found and reported on the mailing list. - */ - public function testInTimeRangeBuggy() { - -$input = <<assertTrue($vobj->VTODO->VALARM->isInTimeRange(new \DateTime('2012-10-01 00:00:00'), new \DateTime('2012-11-01 00:00:00'))); - - } -} - diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Component/VCalendarTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Component/VCalendarTest.php deleted file mode 100644 index 1d7e0c6031..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Component/VCalendarTest.php +++ /dev/null @@ -1,244 +0,0 @@ -expand( - new \DateTime('2011-12-01'), - new \DateTime('2011-12-31') - ); - - // This will normalize the output - $output = VObject\Reader::read($output)->serialize(); - - $this->assertEquals($output, $vcal->serialize()); - - } - - public function expandData() { - - $tests = array(); - - // No data - $input = 'BEGIN:VCALENDAR -CALSCALE:GREGORIAN -VERSION:2.0 -END:VCALENDAR -'; - - $output = $input; - $tests[] = array($input,$output); - - - // Simple events - $input = 'BEGIN:VCALENDAR -CALSCALE:GREGORIAN -VERSION:2.0 -BEGIN:VEVENT -UID:bla -SUMMARY:InExpand -DTSTART;VALUE=DATE:20111202 -END:VEVENT -BEGIN:VEVENT -UID:bla2 -SUMMARY:NotInExpand -DTSTART;VALUE=DATE:20120101 -END:VEVENT -END:VCALENDAR -'; - - $output = 'BEGIN:VCALENDAR -CALSCALE:GREGORIAN -VERSION:2.0 -BEGIN:VEVENT -UID:bla -SUMMARY:InExpand -DTSTART;VALUE=DATE:20111202 -END:VEVENT -END:VCALENDAR -'; - - $tests[] = array($input, $output); - - // Removing timezone info - $input = 'BEGIN:VCALENDAR -CALSCALE:GREGORIAN -VERSION:2.0 -BEGIN:VTIMEZONE -TZID:Europe/Paris -END:VTIMEZONE -BEGIN:VEVENT -UID:bla4 -SUMMARY:RemoveTZ info -DTSTART;TZID=Europe/Paris:20111203T130102 -END:VEVENT -END:VCALENDAR -'; - - $output = 'BEGIN:VCALENDAR -CALSCALE:GREGORIAN -VERSION:2.0 -BEGIN:VEVENT -UID:bla4 -SUMMARY:RemoveTZ info -DTSTART;VALUE=DATE-TIME:20111203T120102Z -END:VEVENT -END:VCALENDAR -'; - - $tests[] = array($input, $output); - - // Recurrence rule - $input = 'BEGIN:VCALENDAR -CALSCALE:GREGORIAN -VERSION:2.0 -BEGIN:VEVENT -UID:bla6 -SUMMARY:Testing RRule -DTSTART:20111125T120000Z -DTEND:20111125T130000Z -RRULE:FREQ=WEEKLY -END:VEVENT -END:VCALENDAR -'; - - $output = 'BEGIN:VCALENDAR -CALSCALE:GREGORIAN -VERSION:2.0 -BEGIN:VEVENT -UID:bla6 -SUMMARY:Testing RRule -DTSTART;VALUE=DATE-TIME:20111202T120000Z -DTEND;VALUE=DATE-TIME:20111202T130000Z -RECURRENCE-ID:20111202T120000Z -END:VEVENT -BEGIN:VEVENT -UID:bla6 -SUMMARY:Testing RRule -DTSTART;VALUE=DATE-TIME:20111209T120000Z -DTEND;VALUE=DATE-TIME:20111209T130000Z -RECURRENCE-ID:20111209T120000Z -END:VEVENT -BEGIN:VEVENT -UID:bla6 -SUMMARY:Testing RRule -DTSTART;VALUE=DATE-TIME:20111216T120000Z -DTEND;VALUE=DATE-TIME:20111216T130000Z -RECURRENCE-ID:20111216T120000Z -END:VEVENT -BEGIN:VEVENT -UID:bla6 -SUMMARY:Testing RRule -DTSTART;VALUE=DATE-TIME:20111223T120000Z -DTEND;VALUE=DATE-TIME:20111223T130000Z -RECURRENCE-ID:20111223T120000Z -END:VEVENT -BEGIN:VEVENT -UID:bla6 -SUMMARY:Testing RRule -DTSTART;VALUE=DATE-TIME:20111230T120000Z -DTEND;VALUE=DATE-TIME:20111230T130000Z -RECURRENCE-ID:20111230T120000Z -END:VEVENT -END:VCALENDAR -'; - - // Recurrence rule + override - $input = 'BEGIN:VCALENDAR -CALSCALE:GREGORIAN -VERSION:2.0 -BEGIN:VEVENT -UID:bla6 -SUMMARY:Testing RRule2 -DTSTART:20111125T120000Z -DTEND:20111125T130000Z -RRULE:FREQ=WEEKLY -END:VEVENT -BEGIN:VEVENT -UID:bla6 -RECURRENCE-ID:20111209T120000Z -DTSTART:20111209T140000Z -DTEND:20111209T150000Z -SUMMARY:Override! -END:VEVENT -END:VCALENDAR -'; - - $output = 'BEGIN:VCALENDAR -CALSCALE:GREGORIAN -VERSION:2.0 -BEGIN:VEVENT -UID:bla6 -SUMMARY:Testing RRule2 -DTSTART;VALUE=DATE-TIME:20111202T120000Z -DTEND;VALUE=DATE-TIME:20111202T130000Z -RECURRENCE-ID:20111202T120000Z -END:VEVENT -BEGIN:VEVENT -UID:bla6 -RECURRENCE-ID:20111209T120000Z -DTSTART:20111209T140000Z -DTEND:20111209T150000Z -SUMMARY:Override! -END:VEVENT -BEGIN:VEVENT -UID:bla6 -SUMMARY:Testing RRule2 -DTSTART;VALUE=DATE-TIME:20111216T120000Z -DTEND;VALUE=DATE-TIME:20111216T130000Z -RECURRENCE-ID:20111216T120000Z -END:VEVENT -BEGIN:VEVENT -UID:bla6 -SUMMARY:Testing RRule2 -DTSTART;VALUE=DATE-TIME:20111223T120000Z -DTEND;VALUE=DATE-TIME:20111223T130000Z -RECURRENCE-ID:20111223T120000Z -END:VEVENT -BEGIN:VEVENT -UID:bla6 -SUMMARY:Testing RRule2 -DTSTART;VALUE=DATE-TIME:20111230T120000Z -DTEND;VALUE=DATE-TIME:20111230T130000Z -RECURRENCE-ID:20111230T120000Z -END:VEVENT -END:VCALENDAR -'; - - $tests[] = array($input, $output); - return $tests; - - } - - /** - * @expectedException LogicException - */ - public function testBrokenEventExpand() { - - $input = 'BEGIN:VCALENDAR -CALSCALE:GREGORIAN -VERSION:2.0 -BEGIN:VEVENT -RRULE:FREQ=WEEKLY -DTSTART;VALUE=DATE:20111202 -END:VEVENT -END:VCALENDAR -'; - $vcal = VObject\Reader::read($input); - $vcal->expand( - new \DateTime('2011-12-01'), - new \DateTime('2011-12-31') - ); - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Component/VCardTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Component/VCardTest.php deleted file mode 100644 index 584a007d95..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Component/VCardTest.php +++ /dev/null @@ -1,100 +0,0 @@ -validate(); - - $warnMsg = array(); - foreach($warnings as $warning) { - $warnMsg[] = $warning['message']; - } - - $this->assertEquals($expectedWarnings, $warnMsg); - - $vcard->validate(VObject\Component::REPAIR); - - $this->assertEquals( - $expectedRepairedOutput, - $vcard->serialize() - ); - - } - - public function validateData() { - - $tests = array(); - - // Correct - $tests[] = array( - "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nEND:VCARD\r\n", - array(), - "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nEND:VCARD\r\n", - ); - - // No VERSION - $tests[] = array( - "BEGIN:VCARD\r\nFN:John Doe\r\nEND:VCARD\r\n", - array( - 'The VERSION property must appear in the VCARD component exactly 1 time', - ), - "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nEND:VCARD\r\n", - ); - - // Unknown version - $tests[] = array( - "BEGIN:VCARD\r\nVERSION:2.2\r\nFN:John Doe\r\nEND:VCARD\r\n", - array( - 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.', - ), - "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nEND:VCARD\r\n", - ); - - // No FN - $tests[] = array( - "BEGIN:VCARD\r\nVERSION:4.0\r\nEND:VCARD\r\n", - array( - 'The FN property must appear in the VCARD component exactly 1 time', - ), - "BEGIN:VCARD\r\nVERSION:4.0\r\nEND:VCARD\r\n", - ); - // No FN, N fallback - $tests[] = array( - "BEGIN:VCARD\r\nVERSION:4.0\r\nN:Doe;John;;;;;\r\nEND:VCARD\r\n", - array( - 'The FN property must appear in the VCARD component exactly 1 time', - ), - "BEGIN:VCARD\r\nVERSION:4.0\r\nN:Doe;John;;;;;\r\nFN:John Doe\r\nEND:VCARD\r\n", - ); - // No FN, N fallback, no first name - $tests[] = array( - "BEGIN:VCARD\r\nVERSION:4.0\r\nN:Doe;;;;;;\r\nEND:VCARD\r\n", - array( - 'The FN property must appear in the VCARD component exactly 1 time', - ), - "BEGIN:VCARD\r\nVERSION:4.0\r\nN:Doe;;;;;;\r\nFN:Doe\r\nEND:VCARD\r\n", - ); - - // No FN, ORG fallback - $tests[] = array( - "BEGIN:VCARD\r\nVERSION:4.0\r\nORG:Acme Co.\r\nEND:VCARD\r\n", - array( - 'The FN property must appear in the VCARD component exactly 1 time', - ), - "BEGIN:VCARD\r\nVERSION:4.0\r\nORG:Acme Co.\r\nFN:Acme Co.\r\nEND:VCARD\r\n", - ); - return $tests; - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Component/VEventTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Component/VEventTest.php deleted file mode 100644 index 616da4ac76..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Component/VEventTest.php +++ /dev/null @@ -1,74 +0,0 @@ -assertEquals($outcome, $vevent->isInTimeRange($start, $end)); - - } - - public function timeRangeTestData() { - - $tests = array(); - - $vevent = new VEvent('VEVENT'); - $vevent->DTSTART = '20111223T120000Z'; - $tests[] = array($vevent, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); - $tests[] = array($vevent, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); - - $vevent2 = clone $vevent; - $vevent2->DTEND = '20111225T120000Z'; - $tests[] = array($vevent2, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); - $tests[] = array($vevent2, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); - - $vevent3 = clone $vevent; - $vevent3->DURATION = 'P1D'; - $tests[] = array($vevent3, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); - $tests[] = array($vevent3, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); - - $vevent4 = clone $vevent; - $vevent4->DTSTART = '20111225'; - $vevent4->DTSTART['VALUE'] = 'DATE'; - $tests[] = array($vevent4, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); - $tests[] = array($vevent4, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); - // Event with no end date should be treated as lasting the entire day. - $tests[] = array($vevent4, new \DateTime('2011-12-25 16:00:00'), new \DateTime('2011-12-25 17:00:00'), true); - - - $vevent5 = clone $vevent; - $vevent5->DURATION = 'P1D'; - $vevent5->RRULE = 'FREQ=YEARLY'; - $tests[] = array($vevent5, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); - $tests[] = array($vevent5, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); - $tests[] = array($vevent5, new \DateTime('2013-12-01'), new \DateTime('2013-12-31'), true); - - $vevent6 = clone $vevent; - $vevent6->DTSTART = '20111225'; - $vevent6->DTSTART['VALUE'] = 'DATE'; - $vevent6->DTEND = '20111225'; - $vevent6->DTEND['VALUE'] = 'DATE'; - - $tests[] = array($vevent6, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); - $tests[] = array($vevent6, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); - - // Added this test to ensure that recurrence rules with no DTEND also - // get checked for the entire day. - $vevent7 = clone $vevent; - $vevent7->DTSTART = '20120101'; - $vevent7->DTSTART['VALUE'] = 'DATE'; - $vevent7->RRULE = 'FREQ=MONTHLY'; - $tests[] = array($vevent7, new \DateTime('2012-02-01 15:00:00'), new \DateTime('2012-02-02'), true); - return $tests; - - } - -} - diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Component/VFreeBusyTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Component/VFreeBusyTest.php deleted file mode 100644 index 031c3c6848..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Component/VFreeBusyTest.php +++ /dev/null @@ -1,39 +0,0 @@ -VFREEBUSY; - - $tz = new \DateTimeZone('UTC'); - - $this->assertFalse($vfb->isFree(new \DateTime('2012-09-12 01:15:00', $tz), new \DateTime('2012-09-12 01:45:00', $tz))); - $this->assertFalse($vfb->isFree(new \DateTime('2012-09-12 08:05:00', $tz), new \DateTime('2012-09-12 08:10:00', $tz))); - $this->assertFalse($vfb->isFree(new \DateTime('2012-09-12 10:15:00', $tz), new \DateTime('2012-09-12 10:45:00', $tz))); - - // Checking whether the end time is treated as non-inclusive - $this->assertTrue($vfb->isFree(new \DateTime('2012-09-12 09:00:00', $tz), new \DateTime('2012-09-12 09:15:00', $tz))); - $this->assertTrue($vfb->isFree(new \DateTime('2012-09-12 09:45:00', $tz), new \DateTime('2012-09-12 10:00:00', $tz))); - $this->assertTrue($vfb->isFree(new \DateTime('2012-09-12 11:00:00', $tz), new \DateTime('2012-09-12 12:00:00', $tz))); - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Component/VJournalTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Component/VJournalTest.php deleted file mode 100644 index 46ecb992b1..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Component/VJournalTest.php +++ /dev/null @@ -1,41 +0,0 @@ -assertEquals($outcome, $vtodo->isInTimeRange($start, $end)); - - } - - public function timeRangeTestData() { - - $tests = array(); - - $vjournal = Component::create('VJOURNAL'); - $vjournal->DTSTART = '20111223T120000Z'; - $tests[] = array($vjournal, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); - $tests[] = array($vjournal, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); - - $vjournal2 = Component::create('VJOURNAL'); - $vjournal2->DTSTART = '20111223'; - $vjournal2->DTSTART['VALUE'] = 'DATE'; - $tests[] = array($vjournal2, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); - $tests[] = array($vjournal2, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); - - $vjournal3 = Component::create('VJOURNAL'); - $tests[] = array($vjournal3, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), false); - $tests[] = array($vjournal3, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); - - return $tests; - } - -} - diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Component/VTodoTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Component/VTodoTest.php deleted file mode 100644 index a84da5cdf9..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Component/VTodoTest.php +++ /dev/null @@ -1,67 +0,0 @@ -assertEquals($outcome, $vtodo->isInTimeRange($start, $end)); - - } - - public function timeRangeTestData() { - - $tests = array(); - - $vtodo = Component::create('VTODO'); - $vtodo->DTSTART = '20111223T120000Z'; - $tests[] = array($vtodo, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); - $tests[] = array($vtodo, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); - - $vtodo2 = clone $vtodo; - $vtodo2->DURATION = 'P1D'; - $tests[] = array($vtodo2, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); - $tests[] = array($vtodo2, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); - - $vtodo3 = clone $vtodo; - $vtodo3->DUE = '20111225'; - $tests[] = array($vtodo3, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); - $tests[] = array($vtodo3, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); - - $vtodo4 = Component::create('VTODO'); - $vtodo4->DUE = '20111225'; - $tests[] = array($vtodo4, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); - $tests[] = array($vtodo4, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); - - $vtodo5 = Component::create('VTODO'); - $vtodo5->COMPLETED = '20111225'; - $tests[] = array($vtodo5, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); - $tests[] = array($vtodo5, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); - - $vtodo6 = Component::create('VTODO'); - $vtodo6->CREATED = '20111225'; - $tests[] = array($vtodo6, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); - $tests[] = array($vtodo6, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); - - $vtodo7 = Component::create('VTODO'); - $vtodo7->CREATED = '20111225'; - $vtodo7->COMPLETED = '20111226'; - $tests[] = array($vtodo7, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); - $tests[] = array($vtodo7, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); - - $vtodo7 = Component::create('VTODO'); - $tests[] = array($vtodo7, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); - $tests[] = array($vtodo7, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), true); - - return $tests; - - } - -} - diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/ComponentTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/ComponentTest.php deleted file mode 100644 index 07000bda0b..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/ComponentTest.php +++ /dev/null @@ -1,413 +0,0 @@ -children[] = $sub; - - $sub = new Component('VTODO'); - $comp->children[] = $sub; - - $count = 0; - foreach($comp->children() as $key=>$subcomponent) { - - $count++; - $this->assertInstanceOf('Sabre\\VObject\\Component',$subcomponent); - - } - $this->assertEquals(2,$count); - $this->assertEquals(1,$key); - - } - - function testMagicGet() { - - $comp = new Component('VCALENDAR'); - - $sub = new Component('VEVENT'); - $comp->children[] = $sub; - - $sub = new Component('VTODO'); - $comp->children[] = $sub; - - $event = $comp->vevent; - $this->assertInstanceOf('Sabre\\VObject\\Component', $event); - $this->assertEquals('VEVENT', $event->name); - - $this->assertInternalType('null', $comp->vjournal); - - } - - function testMagicGetGroups() { - - $comp = new Component('VCARD'); - - $sub = new Property('GROUP1.EMAIL','1@1.com'); - $comp->children[] = $sub; - - $sub = new Property('GROUP2.EMAIL','2@2.com'); - $comp->children[] = $sub; - - $sub = new Property('EMAIL','3@3.com'); - $comp->children[] = $sub; - - $emails = $comp->email; - $this->assertEquals(3, count($emails)); - - $email1 = $comp->{"group1.email"}; - $this->assertEquals('EMAIL', $email1[0]->name); - $this->assertEquals('GROUP1', $email1[0]->group); - - $email3 = $comp->{".email"}; - $this->assertEquals('EMAIL', $email3[0]->name); - $this->assertEquals(null, $email3[0]->group); - - } - - function testMagicIsset() { - - $comp = new Component('VCALENDAR'); - - $sub = new Component('VEVENT'); - $comp->children[] = $sub; - - $sub = new Component('VTODO'); - $comp->children[] = $sub; - - $this->assertTrue(isset($comp->vevent)); - $this->assertTrue(isset($comp->vtodo)); - $this->assertFalse(isset($comp->vjournal)); - - } - - function testMagicSetScalar() { - - $comp = new Component('VCALENDAR'); - $comp->myProp = 'myValue'; - - $this->assertInstanceOf('Sabre\\VObject\\Property',$comp->MYPROP); - $this->assertEquals('myValue',$comp->MYPROP->value); - - - } - - function testMagicSetScalarTwice() { - - $comp = new Component('VCALENDAR'); - $comp->myProp = 'myValue'; - $comp->myProp = 'myValue'; - - $this->assertEquals(1,count($comp->children)); - $this->assertInstanceOf('Sabre\\VObject\\Property',$comp->MYPROP); - $this->assertEquals('myValue',$comp->MYPROP->value); - - } - - function testMagicSetComponent() { - - $comp = new Component('VCALENDAR'); - - // Note that 'myProp' is ignored here. - $comp->myProp = new Component('VEVENT'); - - $this->assertEquals(1, count($comp->children)); - - $this->assertEquals('VEVENT',$comp->VEVENT->name); - - } - - function testMagicSetTwice() { - - $comp = new Component('VCALENDAR'); - - $comp->VEVENT = new Component('VEVENT'); - $comp->VEVENT = new Component('VEVENT'); - - $this->assertEquals(1, count($comp->children)); - - $this->assertEquals('VEVENT',$comp->VEVENT->name); - - } - - function testArrayAccessGet() { - - $comp = new Component('VCALENDAR'); - - $event = new Component('VEVENT'); - $event->summary = 'Event 1'; - - $comp->add($event); - - $event2 = clone $event; - $event2->summary = 'Event 2'; - - $comp->add($event2); - - $this->assertEquals(2,count($comp->children())); - $this->assertTrue($comp->vevent[1] instanceof Component); - $this->assertEquals('Event 2', (string)$comp->vevent[1]->summary); - - } - - function testArrayAccessExists() { - - $comp = new Component('VCALENDAR'); - - $event = new Component('VEVENT'); - $event->summary = 'Event 1'; - - $comp->add($event); - - $event2 = clone $event; - $event2->summary = 'Event 2'; - - $comp->add($event2); - - $this->assertTrue(isset($comp->vevent[0])); - $this->assertTrue(isset($comp->vevent[1])); - - } - - /** - * @expectedException LogicException - */ - function testArrayAccessSet() { - - $comp = new Component('VCALENDAR'); - $comp['hey'] = 'hi there'; - - } - /** - * @expectedException LogicException - */ - function testArrayAccessUnset() { - - $comp = new Component('VCALENDAR'); - unset($comp[0]); - - } - - function testAddScalar() { - - $comp = new Component('VCALENDAR'); - - $comp->add('myprop','value'); - - $this->assertEquals(1, count($comp->children)); - - $this->assertTrue($comp->children[0] instanceof Property); - $this->assertEquals('MYPROP',$comp->children[0]->name); - $this->assertEquals('value',$comp->children[0]->value); - - } - - function testAddScalarParams() { - - $comp = Component::create('VCALENDAR'); - - $comp->add('myprop','value',array('param1'=>'value1')); - - $this->assertEquals(1, count($comp->children)); - - $this->assertTrue($comp->children[0] instanceof Property); - $this->assertEquals('MYPROP',$comp->children[0]->name); - $this->assertEquals('value',$comp->children[0]->value); - - $this->assertEquals(1, count($comp->children[0]->parameters)); - - $this->assertTrue($comp->children[0]->parameters[0] instanceof Parameter); - $this->assertEquals('PARAM1',$comp->children[0]->parameters[0]->name); - $this->assertEquals('value1',$comp->children[0]->parameters[0]->value); - - } - - - function testAddComponent() { - - $comp = new Component('VCALENDAR'); - - $comp->add(new Component('VEVENT')); - - $this->assertEquals(1, count($comp->children)); - - $this->assertEquals('VEVENT',$comp->VEVENT->name); - - } - - function testAddComponentTwice() { - - $comp = new Component('VCALENDAR'); - - $comp->add(new Component('VEVENT')); - $comp->add(new Component('VEVENT')); - - $this->assertEquals(2, count($comp->children)); - - $this->assertEquals('VEVENT',$comp->VEVENT->name); - - } - - /** - * @expectedException InvalidArgumentException - */ - function testAddArgFail() { - - $comp = new Component('VCALENDAR'); - $comp->add(new Component('VEVENT'),'hello'); - - } - - /** - * @expectedException InvalidArgumentException - */ - function testAddArgFail2() { - - $comp = new Component('VCALENDAR'); - $comp->add(array()); - - } - - /** - * @expectedException InvalidArgumentException - */ - function testAddArgFail3() { - - $comp = new Component('VCALENDAR'); - $comp->add('hello',array()); - - } - - /** - * @expectedException InvalidArgumentException - */ - function testMagicSetInvalid() { - - $comp = new Component('VCALENDAR'); - - // Note that 'myProp' is ignored here. - $comp->myProp = new \StdClass(); - - $this->assertEquals(1, count($comp->children)); - - $this->assertEquals('VEVENT',$comp->VEVENT->name); - - } - - function testMagicUnset() { - - $comp = new Component('VCALENDAR'); - $comp->add(new Component('VEVENT')); - - unset($comp->vevent); - - $this->assertEquals(array(), $comp->children); - - } - - - function testCount() { - - $comp = new Component('VCALENDAR'); - $this->assertEquals(1,$comp->count()); - - } - - function testChildren() { - - $comp = new Component('VCALENDAR'); - - // Note that 'myProp' is ignored here. - $comp->children = array( - new Component('VEVENT'), - new Component('VTODO') - ); - - $r = $comp->children(); - $this->assertTrue($r instanceof ElementList); - $this->assertEquals(2,count($r)); - } - - function testGetComponents() { - - $comp = new Component('VCALENDAR'); - - // Note that 'myProp' is ignored here. - $comp->children = array( - new Property('FOO','BAR'), - new Component('VTODO') - ); - - $r = $comp->getComponents(); - $this->assertInternalType('array', $r); - $this->assertEquals(1, count($r)); - $this->assertEquals('VTODO', $r[0]->name); - } - - function testSerialize() { - - $comp = new Component('VCALENDAR'); - $this->assertEquals("BEGIN:VCALENDAR\r\nEND:VCALENDAR\r\n", $comp->serialize()); - - } - - function testSerializeChildren() { - - $comp = new Component('VCALENDAR'); - $comp->children = array( - new Component('VEVENT'), - new Component('VTODO') - ); - - $str = $comp->serialize(); - - $this->assertEquals("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nEND:VEVENT\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n", $str); - - } - - function testSerializeOrderCompAndProp() { - - $comp = new Component('VCALENDAR'); - $comp->add(new Component('VEVENT')); - $comp->add('PROP1','BLABLA'); - $comp->add('VERSION','2.0'); - $comp->add(new Component('VTIMEZONE')); - - $str = $comp->serialize(); - - $this->assertEquals("BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPROP1:BLABLA\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", $str); - - } - - function testAnotherSerializeOrderProp() { - - $prop4s=array('1', '2', '3', '4', '5', '6', '7', '8', '9', '10'); - - $comp = new Component('VCARD'); - $comp->__set('SOMEPROP','FOO'); - $comp->__set('ANOTHERPROP','FOO'); - $comp->__set('THIRDPROP','FOO'); - foreach ($prop4s as $prop4) { - $comp->add('PROP4', 'FOO '.$prop4); - } - $comp->__set('PROPNUMBERFIVE', 'FOO'); - $comp->__set('PROPNUMBERSIX', 'FOO'); - $comp->__set('PROPNUMBERSEVEN', 'FOO'); - $comp->__set('PROPNUMBEREIGHT', 'FOO'); - $comp->__set('PROPNUMBERNINE', 'FOO'); - $comp->__set('PROPNUMBERTEN', 'FOO'); - $comp->__set('VERSION','2.0'); - $comp->__set('UID', 'FOO'); - - $str = $comp->serialize(); - - $this->assertEquals("BEGIN:VCARD\r\nVERSION:2.0\r\nSOMEPROP:FOO\r\nANOTHERPROP:FOO\r\nTHIRDPROP:FOO\r\nPROP4:FOO 1\r\nPROP4:FOO 2\r\nPROP4:FOO 3\r\nPROP4:FOO 4\r\nPROP4:FOO 5\r\nPROP4:FOO 6\r\nPROP4:FOO 7\r\nPROP4:FOO 8\r\nPROP4:FOO 9\r\nPROP4:FOO 10\r\nPROPNUMBERFIVE:FOO\r\nPROPNUMBERSIX:FOO\r\nPROPNUMBERSEVEN:FOO\r\nPROPNUMBEREIGHT:FOO\r\nPROPNUMBERNINE:FOO\r\nPROPNUMBERTEN:FOO\r\nUID:FOO\r\nEND:VCARD\r\n", $str); - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/DateTimeParserTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/DateTimeParserTest.php deleted file mode 100644 index 6ea2faed92..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/DateTimeParserTest.php +++ /dev/null @@ -1,153 +0,0 @@ -assertEquals('+1 weeks', DateTimeParser::parseDuration('P1W',true)); - $this->assertEquals('+5 days', DateTimeParser::parseDuration('P5D',true)); - $this->assertEquals('+5 days 3 hours 50 minutes 12 seconds', DateTimeParser::parseDuration('P5DT3H50M12S',true)); - $this->assertEquals('-1 weeks 50 minutes', DateTimeParser::parseDuration('-P1WT50M',true)); - $this->assertEquals('+50 days 3 hours 2 seconds', DateTimeParser::parseDuration('+P50DT3H2S',true)); - $this->assertEquals(new DateInterval('PT0S'), DateTimeParser::parseDuration('PT0S')); - - } - - function testParseICalendarDurationDateInterval() { - - $expected = new DateInterval('P7D'); - $this->assertEquals($expected, DateTimeParser::parseDuration('P1W')); - $this->assertEquals($expected, DateTimeParser::parse('P1W')); - - $expected = new DateInterval('PT3M'); - $expected->invert = true; - $this->assertEquals($expected, DateTimeParser::parseDuration('-PT3M')); - - } - - /** - * @expectedException LogicException - */ - function testParseICalendarDurationFail() { - - DateTimeParser::parseDuration('P1X',true); - - } - - function testParseICalendarDateTime() { - - $dateTime = DateTimeParser::parseDateTime('20100316T141405'); - - $compare = new DateTime('2010-03-16 14:14:05',new DateTimeZone('UTC')); - - $this->assertEquals($compare, $dateTime); - - } - - /** - * @depends testParseICalendarDateTime - * @expectedException LogicException - */ - function testParseICalendarDateTimeBadFormat() { - - $dateTime = DateTimeParser::parseDateTime('20100316T141405 '); - - } - - /** - * @depends testParseICalendarDateTime - */ - function testParseICalendarDateTimeUTC() { - - $dateTime = DateTimeParser::parseDateTime('20100316T141405Z'); - - $compare = new DateTime('2010-03-16 14:14:05',new DateTimeZone('UTC')); - $this->assertEquals($compare, $dateTime); - - } - - /** - * @depends testParseICalendarDateTime - */ - function testParseICalendarDateTimeUTC2() { - - $dateTime = DateTimeParser::parseDateTime('20101211T160000Z'); - - $compare = new DateTime('2010-12-11 16:00:00',new DateTimeZone('UTC')); - $this->assertEquals($compare, $dateTime); - - } - - /** - * @depends testParseICalendarDateTime - */ - function testParseICalendarDateTimeCustomTimeZone() { - - $dateTime = DateTimeParser::parseDateTime('20100316T141405', new DateTimeZone('Europe/Amsterdam')); - - $compare = new DateTime('2010-03-16 13:14:05',new DateTimeZone('UTC')); - $this->assertEquals($compare, $dateTime); - - } - - function testParseICalendarDate() { - - $dateTime = DateTimeParser::parseDate('20100316'); - - $expected = new DateTime('2010-03-16 00:00:00',new DateTimeZone('UTC')); - - $this->assertEquals($expected, $dateTime); - - $dateTime = DateTimeParser::parse('20100316'); - $this->assertEquals($expected, $dateTime); - - } - - /** - * TCheck if a date with year > 4000 will not throw an exception. iOS seems to use 45001231 in yearly recurring events - */ - function testParseICalendarDateGreaterThan4000() { - - $dateTime = DateTimeParser::parseDate('45001231'); - - $expected = new DateTime('4500-12-31 00:00:00',new DateTimeZone('UTC')); - - $this->assertEquals($expected, $dateTime); - - $dateTime = DateTimeParser::parse('45001231'); - $this->assertEquals($expected, $dateTime); - - } - - /** - * Check if a datetime with year > 4000 will not throw an exception. iOS seems to use 45001231T235959 in yearly recurring events - */ - function testParseICalendarDateTimeGreaterThan4000() { - - $dateTime = DateTimeParser::parseDateTime('45001231T235959'); - - $expected = new DateTime('4500-12-31 23:59:59',new DateTimeZone('UTC')); - - $this->assertEquals($expected, $dateTime); - - $dateTime = DateTimeParser::parse('45001231T235959'); - $this->assertEquals($expected, $dateTime); - - } - - /** - * @depends testParseICalendarDate - * @expectedException LogicException - */ - function testParseICalendarDateBadFormat() { - - $dateTime = DateTimeParser::parseDate('20100316T141405'); - - } -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/ElementListTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/ElementListTest.php deleted file mode 100644 index 84e1bcbe90..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/ElementListTest.php +++ /dev/null @@ -1,32 +0,0 @@ -$subcomponent) { - - $count++; - $this->assertInstanceOf('Sabre\\VObject\\Component',$subcomponent); - - } - $this->assertEquals(3,$count); - $this->assertEquals(2,$key); - - } - - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/EmClientTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/EmClientTest.php deleted file mode 100644 index 69d410fe7f..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/EmClientTest.php +++ /dev/null @@ -1,55 +0,0 @@ -VEVENT->DTSTART->getDateTime(); - $this->assertEquals(new \DateTime('2011-10-08 19:30:00', new \DateTimeZone('America/Chicago')), $dt); - - } - -} - diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/FreeBusyGeneratorTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/FreeBusyGeneratorTest.php deleted file mode 100644 index 1f79e0a47c..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/FreeBusyGeneratorTest.php +++ /dev/null @@ -1,246 +0,0 @@ -getInput() - ); - - $result = $gen->getResult(); - - $expected = array( - '20110101T120000Z/20110101T130000Z', - '20110101T130000Z/20110101T140000Z', - '20110101T180000Z/20110101T190000Z', - '20110101T190000Z/20110101T200000Z', - '20110102T000000Z/20110103T000000Z', - '20110101T210000Z/20110101T220000Z', - - '20110103T010000Z/20110103T020000Z', - '20110103T030000Z/20110103T040000Z', - '20110103T040000Z/20110103T050000Z', - '20110103T050000Z/20110103T060000Z', - - '20110101T220000Z/20110101T230000Z', - '20110101T230000Z/20110102T000000Z', - ); - - foreach($result->VFREEBUSY->FREEBUSY as $fb) { - - $this->assertContains((string)$fb, $expected); - - $k = array_search((string)$fb, $expected); - unset($expected[$k]); - - } - if (count($expected)>0) { - $this->fail('There were elements in the expected array that were not found in the output: ' . "\n" . print_r($expected,true) . "\n" . $result->serialize()); - - } - - } - - function testGeneratorBaseObject() { - - $obj = new Component('VCALENDAR'); - $obj->METHOD = 'PUBLISH'; - - $gen = new FreeBusyGenerator(); - $gen->setObjects(array()); - $gen->setBaseObject($obj); - - $result = $gen->getResult(); - - $this->assertEquals('PUBLISH', $result->METHOD->value); - - } - - /** - * @expectedException InvalidArgumentException - */ - function testInvalidArg() { - - $gen = new FreeBusyGenerator( - new \DateTime('2012-01-01'), - new \DateTime('2012-12-31'), - new \StdClass() - ); - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Issue153Test.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Issue153Test.php deleted file mode 100644 index 1cc14c1610..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Issue153Test.php +++ /dev/null @@ -1,14 +0,0 @@ -assertEquals('Test Benutzer', (string)$obj->fn); - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Issue154Test.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Issue154Test.php deleted file mode 100644 index ed9c7c3f43..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Issue154Test.php +++ /dev/null @@ -1,29 +0,0 @@ -VERSION = '3.0'; - $vcard->PHOTO = base64_encode('random_stuff'); - $vcard->PHOTO->add('BASE64',null); - $vcard->UID = 'foo-bar'; - - $result = $vcard->serialize(); - $expected = array( - "BEGIN:VCARD", - "VERSION:3.0", - "PHOTO;BASE64:" . base64_encode('random_stuff'), - "UID:foo-bar", - "END:VCARD", - "", - ); - - $this->assertEquals(implode("\r\n", $expected), $result); - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/ParameterTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/ParameterTest.php deleted file mode 100644 index 9171af35c6..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/ParameterTest.php +++ /dev/null @@ -1,36 +0,0 @@ -assertEquals('NAME',$param->name); - $this->assertEquals('value',$param->value); - - } - - function testCastToString() { - - $param = new Parameter('name','value'); - $this->assertEquals('value',$param->__toString()); - $this->assertEquals('value',(string)$param); - - } - - function testSerialize() { - - $param = new Parameter('name','value'); - $this->assertEquals('NAME=value',$param->serialize()); - - } - - function testSerializeEmpty() { - - $param = new Parameter('name',null); - $this->assertEquals('NAME',$param->serialize()); - - } -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Property/CompoundTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Property/CompoundTest.php deleted file mode 100644 index 5d8cdaabbd..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Property/CompoundTest.php +++ /dev/null @@ -1,59 +0,0 @@ -setParts($arr); - - $this->assertEquals('ABC\, Inc.;North American Division;Marketing\;Sales', $elem->value); - $this->assertEquals(3, count($elem->getParts())); - $parts = $elem->getParts(); - $this->assertEquals('Marketing;Sales', $parts[2]); - - } - - function testGetParts() { - - $str = 'ABC\, Inc.;North American Division;Marketing\;Sales'; - - $elem = new Compound('ORG', $str); - - $this->assertEquals(3, count($elem->getParts())); - $parts = $elem->getParts(); - $this->assertEquals('Marketing;Sales', $parts[2]); - } - - function testGetPartsDefaultDelimiter() { - - $str = 'Hi!;Hello!'; - - $elem = new Compound('X-FOO', $str); - - $this->assertEquals(array( - 'Hi!', - 'Hello!', - ), $elem->getParts()); - - } - - function testGetPartsNull() { - - $str = 'ABC\, Inc.;North American Division;Marketing\;Sales'; - - $elem = new Compound('ORG', null); - - $this->assertEquals(0, count($elem->getParts())); - - } -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Property/DateTimeTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Property/DateTimeTest.php deleted file mode 100644 index 9dc7e86100..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Property/DateTimeTest.php +++ /dev/null @@ -1,234 +0,0 @@ -setTimeZone($tz); - - $elem = new DateTime('DTSTART'); - $elem->setDateTime($dt); - - $this->assertEquals('19850704T013000', $elem->value); - $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']); - $this->assertEquals('DATE-TIME', (string)$elem['VALUE']); - - } - - function testSetDateTimeLOCAL() { - - $tz = new \DateTimeZone('Europe/Amsterdam'); - $dt = new \DateTime('1985-07-04 01:30:00', $tz); - $dt->setTimeZone($tz); - - $elem = new DateTime('DTSTART'); - $elem->setDateTime($dt, DateTime::LOCAL); - - $this->assertEquals('19850704T013000', $elem->value); - $this->assertNull($elem['TZID']); - $this->assertEquals('DATE-TIME', (string)$elem['VALUE']); - - } - - function testSetDateTimeUTC() { - - $tz = new \DateTimeZone('GMT'); - $dt = new \DateTime('1985-07-04 01:30:00', $tz); - $dt->setTimeZone($tz); - - $elem = new DateTime('DTSTART'); - $elem->setDateTime($dt, DateTime::UTC); - - $this->assertEquals('19850704T013000Z', $elem->value); - $this->assertNull($elem['TZID']); - $this->assertEquals('DATE-TIME', (string)$elem['VALUE']); - - } - - function testSetDateTimeLOCALTZ() { - - $tz = new \DateTimeZone('Europe/Amsterdam'); - $dt = new \DateTime('1985-07-04 01:30:00', $tz); - $dt->setTimeZone($tz); - - $elem = new DateTime('DTSTART'); - $elem->setDateTime($dt, DateTime::LOCALTZ); - - $this->assertEquals('19850704T013000', $elem->value); - $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']); - $this->assertEquals('DATE-TIME', (string)$elem['VALUE']); - - } - - function testSetDateTimeDATE() { - - $tz = new \DateTimeZone('Europe/Amsterdam'); - $dt = new \DateTime('1985-07-04 01:30:00', $tz); - $dt->setTimeZone($tz); - - $elem = new DateTime('DTSTART'); - $elem->setDateTime($dt, DateTime::DATE); - - $this->assertEquals('19850704', $elem->value); - $this->assertNull($elem['TZID']); - $this->assertEquals('DATE', (string)$elem['VALUE']); - - } - - /** - * @expectedException InvalidArgumentException - */ - function testSetDateTimeInvalid() { - - $tz = new \DateTimeZone('Europe/Amsterdam'); - $dt = new \DateTime('1985-07-04 01:30:00', $tz); - $dt->setTimeZone($tz); - - $elem = new DateTime('DTSTART'); - $elem->setDateTime($dt, 7); - - } - - function testGetDateTimeCached() { - - $tz = new \DateTimeZone('Europe/Amsterdam'); - $dt = new \DateTime('1985-07-04 01:30:00', $tz); - $dt->setTimeZone($tz); - - $elem = new DateTime('DTSTART'); - $elem->setDateTime($dt); - - $this->assertEquals($elem->getDateTime(), $dt); - - } - - function testGetDateTimeDateNULL() { - - $elem = new DateTime('DTSTART'); - $dt = $elem->getDateTime(); - - $this->assertNull($dt); - $this->assertNull($elem->getDateType()); - - } - - function testGetDateTimeDateDATE() { - - $elem = new DateTime('DTSTART','19850704'); - $dt = $elem->getDateTime(); - - $this->assertInstanceOf('DateTime', $dt); - $this->assertEquals('1985-07-04 00:00:00', $dt->format('Y-m-d H:i:s')); - $this->assertEquals(DateTime::DATE, $elem->getDateType()); - - } - - - function testGetDateTimeDateLOCAL() { - - $elem = new DateTime('DTSTART','19850704T013000'); - $dt = $elem->getDateTime(); - - $this->assertInstanceOf('DateTime', $dt); - $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); - $this->assertEquals(DateTime::LOCAL, $elem->getDateType()); - - } - - function testGetDateTimeDateUTC() { - - $elem = new DateTime('DTSTART','19850704T013000Z'); - $dt = $elem->getDateTime(); - - $this->assertInstanceOf('DateTime', $dt); - $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); - $this->assertEquals('UTC', $dt->getTimeZone()->getName()); - $this->assertEquals(DateTime::UTC, $elem->getDateType()); - - } - - function testGetDateTimeDateLOCALTZ() { - - $elem = new DateTime('DTSTART','19850704T013000'); - $elem['TZID'] = 'Europe/Amsterdam'; - - $dt = $elem->getDateTime(); - - $this->assertInstanceOf('DateTime', $dt); - $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); - $this->assertEquals('Europe/Amsterdam', $dt->getTimeZone()->getName()); - $this->assertEquals(DateTime::LOCALTZ, $elem->getDateType()); - - } - - /** - * @expectedException InvalidArgumentException - */ - function testGetDateTimeDateInvalid() { - - $elem = new DateTime('DTSTART','bla'); - $dt = $elem->getDateTime(); - - } - - function testGetDateTimeWeirdTZ() { - - $elem = new DateTime('DTSTART','19850704T013000'); - $elem['TZID'] = '/freeassociation.sourceforge.net/Tzfile/Europe/Amsterdam'; - - - $event = new Component('VEVENT'); - $event->add($elem); - - $timezone = new Component('VTIMEZONE'); - $timezone->TZID = '/freeassociation.sourceforge.net/Tzfile/Europe/Amsterdam'; - $timezone->{'X-LIC-LOCATION'} = 'Europe/Amsterdam'; - - $calendar = new Component('VCALENDAR'); - $calendar->add($event); - $calendar->add($timezone); - - $dt = $elem->getDateTime(); - - $this->assertInstanceOf('DateTime', $dt); - $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); - $this->assertEquals('Europe/Amsterdam', $dt->getTimeZone()->getName()); - $this->assertEquals(DateTime::LOCALTZ, $elem->getDateType()); - - } - - function testGetDateTimeBadTimeZone() { - - $default = date_default_timezone_get(); - date_default_timezone_set('Canada/Eastern'); - - $elem = new DateTime('DTSTART','19850704T013000'); - $elem['TZID'] = 'Moon'; - - - $event = new Component('VEVENT'); - $event->add($elem); - - $timezone = new Component('VTIMEZONE'); - $timezone->TZID = 'Moon'; - $timezone->{'X-LIC-LOCATION'} = 'Moon'; - - $calendar = new Component('VCALENDAR'); - $calendar->add($event); - $calendar->add($timezone); - - $dt = $elem->getDateTime(); - - $this->assertInstanceOf('DateTime', $dt); - $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); - $this->assertEquals('Canada/Eastern', $dt->getTimeZone()->getName()); - $this->assertEquals(DateTime::LOCALTZ, $elem->getDateType()); - date_default_timezone_set($default); - - } -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Property/MultiDateTimeTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Property/MultiDateTimeTest.php deleted file mode 100644 index eae3e2bb21..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Property/MultiDateTimeTest.php +++ /dev/null @@ -1,202 +0,0 @@ -setTimeZone($tz); - $dt2->setTimeZone($tz); - - $elem = new MultiDateTime('DTSTART'); - $elem->setDateTimes(array($dt1,$dt2)); - - $this->assertEquals('19850704T013000,19860704T013000', $elem->value); - $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']); - $this->assertEquals('DATE-TIME', (string)$elem['VALUE']); - - } - - function testSetDateTimeLOCAL() { - - $tz = new \DateTimeZone('Europe/Amsterdam'); - $dt1 = new \DateTime('1985-07-04 01:30:00', $tz); - $dt2 = new \DateTime('1986-07-04 01:30:00', $tz); - $dt1->setTimeZone($tz); - $dt2->setTimeZone($tz); - - $elem = new MultiDateTime('DTSTART'); - $elem->setDateTimes(array($dt1,$dt2), DateTime::LOCAL); - - $this->assertEquals('19850704T013000,19860704T013000', $elem->value); - $this->assertNull($elem['TZID']); - $this->assertEquals('DATE-TIME', (string)$elem['VALUE']); - - } - - function testSetDateTimeUTC() { - - $tz = new \DateTimeZone('GMT'); - $dt1 = new \DateTime('1985-07-04 01:30:00', $tz); - $dt2 = new \DateTime('1986-07-04 01:30:00', $tz); - $dt1->setTimeZone($tz); - $dt2->setTimeZone($tz); - - $elem = new MultiDateTime('DTSTART'); - $elem->setDateTimes(array($dt1,$dt2), DateTime::UTC); - - $this->assertEquals('19850704T013000Z,19860704T013000Z', $elem->value); - $this->assertNull($elem['TZID']); - $this->assertEquals('DATE-TIME', (string)$elem['VALUE']); - - } - - function testSetDateTimeLOCALTZ() { - - $tz = new \DateTimeZone('Europe/Amsterdam'); - $dt1 = new \DateTime('1985-07-04 01:30:00', $tz); - $dt2 = new \DateTime('1986-07-04 01:30:00', $tz); - $dt1->setTimeZone($tz); - $dt2->setTimeZone($tz); - - $elem = new MultiDateTime('DTSTART'); - $elem->setDateTimes(array($dt1,$dt2), DateTime::LOCALTZ); - - $this->assertEquals('19850704T013000,19860704T013000', $elem->value); - $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']); - $this->assertEquals('DATE-TIME', (string)$elem['VALUE']); - - } - - function testSetDateTimeDATE() { - - $tz = new \DateTimeZone('Europe/Amsterdam'); - $dt1 = new \DateTime('1985-07-04 01:30:00', $tz); - $dt2 = new \DateTime('1986-07-04 01:30:00', $tz); - $dt1->settimezone($tz); - $dt2->settimezone($tz); - - $elem = new MultiDateTime('DTSTART'); - $elem->setDateTimes(array($dt1,$dt2), DateTime::DATE); - - $this->assertEquals('19850704,19860704', $elem->value); - $this->assertNull($elem['TZID']); - $this->assertEquals('DATE', (string)$elem['VALUE']); - - } - - /** - * @expectedException InvalidArgumentException - */ - function testSetDateTimeInvalid() { - - $tz = new \DateTimeZone('Europe/Amsterdam'); - $dt = new \DateTime('1985-07-04 01:30:00', $tz); - $dt->setTimeZone($tz); - - $elem = new MultiDateTime('DTSTART'); - $elem->setDateTimes(array($dt), 7); - - } - - function testGetDateTimeCached() { - - $tz = new \DateTimeZone('Europe/Amsterdam'); - $dt1 = new \DateTime('1985-07-04 01:30:00', $tz); - $dt2 = new \DateTime('1986-07-04 01:30:00', $tz); - $dt1->settimezone($tz); - $dt2->settimezone($tz); - - $elem = new MultiDateTime('DTSTART'); - $elem->setDateTimes(array($dt1,$dt2)); - - $this->assertEquals($elem->getDateTimes(), array($dt1,$dt2)); - - } - - function testGetDateTimeDateNULL() { - - $elem = new MultiDateTime('DTSTART'); - $dt = $elem->getDateTimes(); - - $this->assertNull($dt); - $this->assertNull($elem->getDateType()); - - } - - function testGetDateTimeDateDATE() { - - $elem = new MultiDateTime('DTSTART','19850704,19860704'); - $dt = $elem->getDateTimes(); - - $this->assertEquals('1985-07-04 00:00:00', $dt[0]->format('Y-m-d H:i:s')); - $this->assertEquals('1986-07-04 00:00:00', $dt[1]->format('Y-m-d H:i:s')); - $this->assertEquals(DateTime::DATE, $elem->getDateType()); - - } - - function testGetDateTimeDateDATEReverse() { - - $elem = new MultiDateTime('DTSTART','19850704,19860704'); - - $this->assertEquals(DateTime::DATE, $elem->getDateType()); - - $dt = $elem->getDateTimes(); - $this->assertEquals('1985-07-04 00:00:00', $dt[0]->format('Y-m-d H:i:s')); - $this->assertEquals('1986-07-04 00:00:00', $dt[1]->format('Y-m-d H:i:s')); - - } - - - function testGetDateTimeDateLOCAL() { - - $elem = new DateTime('DTSTART','19850704T013000'); - $dt = $elem->getDateTime(); - - $this->assertInstanceOf('DateTime', $dt); - $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); - $this->assertEquals(DateTime::LOCAL, $elem->getDateType()); - - } - - function testGetDateTimeDateUTC() { - - $elem = new DateTime('DTSTART','19850704T013000Z'); - $dt = $elem->getDateTime(); - - $this->assertInstanceOf('DateTime', $dt); - $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); - $this->assertEquals('UTC', $dt->getTimeZone()->getName()); - $this->assertEquals(DateTime::UTC, $elem->getDateType()); - - } - - function testGetDateTimeDateLOCALTZ() { - - $elem = new DateTime('DTSTART','19850704T013000'); - $elem['TZID'] = 'Europe/Amsterdam'; - - $dt = $elem->getDateTime(); - - $this->assertInstanceOf('DateTime', $dt); - $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); - $this->assertEquals('Europe/Amsterdam', $dt->getTimeZone()->getName()); - $this->assertEquals(DateTime::LOCALTZ, $elem->getDateType()); - - } - - /** - * @expectedException InvalidArgumentException - */ - function testGetDateTimeDateInvalid() { - - $elem = new DateTime('DTSTART','bla'); - $dt = $elem->getDateTime(); - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/PropertyTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/PropertyTest.php deleted file mode 100644 index a2e258bda9..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/PropertyTest.php +++ /dev/null @@ -1,323 +0,0 @@ -assertEquals('PROPNAME', $property->name); - $this->assertEquals('propvalue', $property->value); - $this->assertEquals('propvalue', $property->__toString()); - $this->assertEquals('propvalue', (string)$property); - - } - - /** - * @expectedException InvalidArgumentException - */ - public function testCreateNonScalar() { - - $property = new Property('propname',array()); - - } - - public function testParameterExists() { - - $property = new Property('propname','propvalue'); - $property->parameters[] = new Parameter('paramname','paramvalue'); - - $this->assertTrue(isset($property['PARAMNAME'])); - $this->assertTrue(isset($property['paramname'])); - $this->assertFalse(isset($property['foo'])); - - } - - public function testParameterGet() { - - $property = new Property('propname','propvalue'); - $property->parameters[] = new Parameter('paramname','paramvalue'); - - $this->assertInstanceOf('Sabre\\VObject\\Parameter',$property['paramname']); - - } - - public function testParameterNotExists() { - - $property = new Property('propname','propvalue'); - $property->parameters[] = new Parameter('paramname','paramvalue'); - - $this->assertInternalType('null',$property['foo']); - - } - - public function testParameterMultiple() { - - $property = new Property('propname','propvalue'); - $property->parameters[] = new Parameter('paramname','paramvalue'); - $property->parameters[] = new Parameter('paramname','paramvalue'); - - $this->assertInstanceOf('Sabre\\VObject\\Parameter',$property['paramname']); - $this->assertEquals(2,count($property['paramname'])); - - } - - public function testSetParameterAsString() { - - $property = new Property('propname','propvalue'); - $property['paramname'] = 'paramvalue'; - - $this->assertEquals(1,count($property->parameters)); - $this->assertInstanceOf('Sabre\\VObject\\Parameter', $property->parameters[0]); - $this->assertEquals('PARAMNAME',$property->parameters[0]->name); - $this->assertEquals('paramvalue',$property->parameters[0]->value); - - } - - /** - * @expectedException InvalidArgumentException - */ - public function testSetParameterAsStringNoKey() { - - $property = new Property('propname','propvalue'); - $property[] = 'paramvalue'; - - } - - public function testSetParameterObject() { - - $property = new Property('propname','propvalue'); - $param = new Parameter('paramname','paramvalue'); - - $property[] = $param; - - $this->assertEquals(1,count($property->parameters)); - $this->assertEquals($param, $property->parameters[0]); - - } - - /** - * @expectedException InvalidArgumentException - */ - public function testSetParameterObjectWithKey() { - - $property = new Property('propname','propvalue'); - $param = new Parameter('paramname','paramvalue'); - - $property['key'] = $param; - - } - - - /** - * @expectedException InvalidArgumentException - */ - public function testSetParameterObjectRandomObject() { - - $property = new Property('propname','propvalue'); - $property[] = new \StdClass(); - - } - - public function testUnsetParameter() { - - $property = new Property('propname','propvalue'); - $param = new Parameter('paramname','paramvalue'); - $property->parameters[] = $param; - - unset($property['PARAMNAME']); - $this->assertEquals(0,count($property->parameters)); - - } - - public function testParamCount() { - - $property = new Property('propname','propvalue'); - $param = new Parameter('paramname','paramvalue'); - $property->parameters[] = $param; - $property->parameters[] = clone $param; - - $this->assertEquals(2,count($property->parameters)); - - } - - public function testSerialize() { - - $property = new Property('propname','propvalue'); - - $this->assertEquals("PROPNAME:propvalue\r\n",$property->serialize()); - - } - - public function testSerializeParam() { - - $property = new Property('propname','propvalue'); - $property->parameters[] = new Parameter('paramname','paramvalue'); - $property->parameters[] = new Parameter('paramname2','paramvalue2'); - - $this->assertEquals("PROPNAME;PARAMNAME=paramvalue;PARAMNAME2=paramvalue2:propvalue\r\n",$property->serialize()); - - } - - public function testSerializeNewLine() { - - $property = new Property('propname',"line1\nline2"); - - $this->assertEquals("PROPNAME:line1\\nline2\r\n",$property->serialize()); - - } - - public function testSerializeLongLine() { - - $value = str_repeat('!',200); - $property = new Property('propname',$value); - - $expected = "PROPNAME:" . str_repeat('!',66) . "\r\n " . str_repeat('!',74) . "\r\n " . str_repeat('!',60) . "\r\n"; - - $this->assertEquals($expected,$property->serialize()); - - } - - public function testSerializeUTF8LineFold() { - - $value = str_repeat('!',65) . "\xc3\xa4bla"; // inserted umlaut-a - $property = new Property('propname', $value); - $expected = "PROPNAME:" . str_repeat('!',65) . "\r\n \xc3\xa4bla\r\n"; - $this->assertEquals($expected, $property->serialize()); - - } - - public function testGetIterator() { - - $it = new ElementList(array()); - $property = new Property('propname','propvalue'); - $property->setIterator($it); - $this->assertEquals($it,$property->getIterator()); - - } - - - public function testGetIteratorDefault() { - - $property = new Property('propname','propvalue'); - $it = $property->getIterator(); - $this->assertTrue($it instanceof ElementList); - $this->assertEquals(1,count($it)); - - } - - function testAddScalar() { - - $property = new Property('EMAIL'); - - $property->add('myparam','value'); - - $this->assertEquals(1, count($property->parameters)); - - $this->assertTrue($property->parameters[0] instanceof Parameter); - $this->assertEquals('MYPARAM',$property->parameters[0]->name); - $this->assertEquals('value',$property->parameters[0]->value); - - } - - function testAddParameter() { - - $prop = new Property('EMAIL'); - - $prop->add(new Parameter('MYPARAM','value')); - - $this->assertEquals(1, count($prop->parameters)); - $this->assertEquals('MYPARAM',$prop['myparam']->name); - - } - - function testAddParameterTwice() { - - $prop = new Property('EMAIL'); - - $prop->add(new Parameter('MYPARAM', 'value1')); - $prop->add(new Parameter('MYPARAM', 'value2')); - - $this->assertEquals(2, count($prop->parameters)); - - $this->assertEquals('MYPARAM',$prop['MYPARAM']->name); - - } - - /** - * @expectedException InvalidArgumentException - */ - function testAddArgFail() { - - $prop = new Property('EMAIL'); - $prop->add(new Parameter('MPARAM'),'hello'); - - } - - /** - * @expectedException InvalidArgumentException - */ - function testAddArgFail2() { - - $property = new Property('EMAIL','value'); - $property->add(array()); - - } - - /** - * @expectedException InvalidArgumentException - */ - function testAddArgFail3() { - - $property = new Property('EMAIL','value'); - $property->add('hello',array()); - - } - - function testClone() { - - $property = new Property('EMAIL','value'); - $property['FOO'] = 'BAR'; - - $property2 = clone $property; - - $property['FOO'] = 'BAZ'; - $this->assertEquals('BAR', (string)$property2['FOO']); - - } - - function testCreateParams() { - - $property = Property::create('X-PROP', 'value', array( - 'param1' => 'value1', - 'param2' => array('value2', 'value3') - )); - - $this->assertEquals(1, count($property['PARAM1'])); - $this->assertEquals(2, count($property['PARAM2'])); - - } - - function testValidateNonUTF8() { - - $property = Property::create('X-PROP', "Bla\x00"); - $result = $property->validate(Property::REPAIR); - - $this->assertEquals('Property is not valid UTF-8!', $result[0]['message']); - $this->assertEquals('Bla', $property->value); - - } - - - function testValidateBadPropertyName() { - - $property = Property::create("X_*&PROP*", "Bla"); - $result = $property->validate(Property::REPAIR); - - $this->assertEquals($result[0]['message'], 'The propertyname: X_*&PROP* contains invalid characters. Only A-Z, 0-9 and - are allowed'); - $this->assertEquals('X-PROP', $property->name); - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/ReaderTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/ReaderTest.php deleted file mode 100644 index d5b22f8c3b..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/ReaderTest.php +++ /dev/null @@ -1,328 +0,0 @@ -assertInstanceOf('Sabre\\VObject\\Component', $result); - $this->assertEquals('VCALENDAR', $result->name); - $this->assertEquals(0, count($result->children)); - - } - - function testReadComponentUnixNewLine() { - - $data = "BEGIN:VCALENDAR\nEND:VCALENDAR"; - - $result = Reader::read($data); - - $this->assertInstanceOf('Sabre\\VObject\\Component', $result); - $this->assertEquals('VCALENDAR', $result->name); - $this->assertEquals(0, count($result->children)); - - } - - function testReadComponentMacNewLine() { - - $data = "BEGIN:VCALENDAR\rEND:VCALENDAR"; - - $result = Reader::read($data); - - $this->assertInstanceOf('Sabre\\VObject\\Component', $result); - $this->assertEquals('VCALENDAR', $result->name); - $this->assertEquals(0, count($result->children)); - - } - - function testReadComponentLineFold() { - - $data = "BEGIN:\r\n\tVCALENDAR\r\nE\r\n ND:VCALENDAR"; - - $result = Reader::read($data); - - $this->assertInstanceOf('Sabre\\VObject\\Component', $result); - $this->assertEquals('VCALENDAR', $result->name); - $this->assertEquals(0, count($result->children)); - - } - - /** - * @expectedException Sabre\VObject\ParseException - */ - function testReadCorruptComponent() { - - $data = "BEGIN:VCALENDAR\r\nEND:FOO"; - - $result = Reader::read($data); - - } - - function testReadProperty() { - - $data = "PROPNAME:propValue"; - $result = Reader::read($data); - - $this->assertInstanceOf('Sabre\\VObject\\Property', $result); - $this->assertEquals('PROPNAME', $result->name); - $this->assertEquals('propValue', $result->value); - - } - - function testReadPropertyWithNewLine() { - - $data = 'PROPNAME:Line1\\nLine2\\NLine3\\\\Not the 4th line!'; - $result = Reader::read($data); - - $this->assertInstanceOf('Sabre\\VObject\\Property', $result); - $this->assertEquals('PROPNAME', $result->name); - $this->assertEquals("Line1\nLine2\nLine3\\Not the 4th line!", $result->value); - - } - - function testReadMappedProperty() { - - $data = "DTSTART:20110529"; - $result = Reader::read($data); - - $this->assertInstanceOf('Sabre\\VObject\\Property\\DateTime', $result); - $this->assertEquals('DTSTART', $result->name); - $this->assertEquals('20110529', $result->value); - - } - - function testReadMappedPropertyGrouped() { - - $data = "foo.DTSTART:20110529"; - $result = Reader::read($data); - - $this->assertInstanceOf('Sabre\\VObject\\Property\\DateTime', $result); - $this->assertEquals('DTSTART', $result->name); - $this->assertEquals('20110529', $result->value); - - } - - - /** - * @expectedException Sabre\VObject\ParseException - */ - function testReadBrokenLine() { - - $data = "PROPNAME;propValue"; - $result = Reader::read($data); - - } - - function testReadPropertyInComponent() { - - $data = array( - "BEGIN:VCALENDAR", - "PROPNAME:propValue", - "END:VCALENDAR" - ); - - $result = Reader::read(implode("\r\n",$data)); - - $this->assertInstanceOf('Sabre\\VObject\\Component', $result); - $this->assertEquals('VCALENDAR', $result->name); - $this->assertEquals(1, count($result->children)); - $this->assertInstanceOf('Sabre\\VObject\\Property', $result->children[0]); - $this->assertEquals('PROPNAME', $result->children[0]->name); - $this->assertEquals('propValue', $result->children[0]->value); - - } - function testReadNestedComponent() { - - $data = array( - "BEGIN:VCALENDAR", - "BEGIN:VTIMEZONE", - "BEGIN:DAYLIGHT", - "END:DAYLIGHT", - "END:VTIMEZONE", - "END:VCALENDAR" - ); - - $result = Reader::read(implode("\r\n",$data)); - - $this->assertInstanceOf('Sabre\\VObject\\Component', $result); - $this->assertEquals('VCALENDAR', $result->name); - $this->assertEquals(1, count($result->children)); - $this->assertInstanceOf('Sabre\\VObject\\Component', $result->children[0]); - $this->assertEquals('VTIMEZONE', $result->children[0]->name); - $this->assertEquals(1, count($result->children[0]->children)); - $this->assertInstanceOf('Sabre\\VObject\\Component', $result->children[0]->children[0]); - $this->assertEquals('DAYLIGHT', $result->children[0]->children[0]->name); - - - } - - function testReadPropertyParameter() { - - $data = "PROPNAME;PARAMNAME=paramvalue:propValue"; - $result = Reader::read($data); - - $this->assertInstanceOf('Sabre\\VObject\\Property', $result); - $this->assertEquals('PROPNAME', $result->name); - $this->assertEquals('propValue', $result->value); - $this->assertEquals(1, count($result->parameters)); - $this->assertEquals('PARAMNAME', $result->parameters[0]->name); - $this->assertEquals('paramvalue', $result->parameters[0]->value); - - } - - function testReadPropertyNoValue() { - - $data = "PROPNAME;PARAMNAME:propValue"; - $result = Reader::read($data); - - $this->assertInstanceOf('Sabre\\VObject\\Property', $result); - $this->assertEquals('PROPNAME', $result->name); - $this->assertEquals('propValue', $result->value); - $this->assertEquals(1, count($result->parameters)); - $this->assertEquals('PARAMNAME', $result->parameters[0]->name); - - $this->assertNull($result->parameters[0]->value); - - } - - function testReadPropertyParameterExtraColon() { - - $data = "PROPNAME;PARAMNAME=paramvalue:propValue:anotherrandomstring"; - $result = Reader::read($data); - - $this->assertInstanceOf('Sabre\\VObject\\Property', $result); - $this->assertEquals('PROPNAME', $result->name); - $this->assertEquals('propValue:anotherrandomstring', $result->value); - $this->assertEquals(1, count($result->parameters)); - $this->assertEquals('PARAMNAME', $result->parameters[0]->name); - $this->assertEquals('paramvalue', $result->parameters[0]->value); - - } - - function testReadProperty2Parameters() { - - $data = "PROPNAME;PARAMNAME=paramvalue;PARAMNAME2=paramvalue2:propValue"; - $result = Reader::read($data); - - $this->assertInstanceOf('Sabre\\VObject\\Property', $result); - $this->assertEquals('PROPNAME', $result->name); - $this->assertEquals('propValue', $result->value); - $this->assertEquals(2, count($result->parameters)); - $this->assertEquals('PARAMNAME', $result->parameters[0]->name); - $this->assertEquals('paramvalue', $result->parameters[0]->value); - $this->assertEquals('PARAMNAME2', $result->parameters[1]->name); - $this->assertEquals('paramvalue2', $result->parameters[1]->value); - - } - - function testReadPropertyParameterQuoted() { - - $data = "PROPNAME;PARAMNAME=\"paramvalue\":propValue"; - $result = Reader::read($data); - - $this->assertInstanceOf('Sabre\\VObject\\Property', $result); - $this->assertEquals('PROPNAME', $result->name); - $this->assertEquals('propValue', $result->value); - $this->assertEquals(1, count($result->parameters)); - $this->assertEquals('PARAMNAME', $result->parameters[0]->name); - $this->assertEquals('paramvalue', $result->parameters[0]->value); - - } - function testReadPropertyParameterNewLines() { - - $data = "PROPNAME;PARAMNAME=paramvalue1\\nvalue2\\\\nvalue3:propValue"; - $result = Reader::read($data); - - $this->assertInstanceOf('Sabre\\VObject\\Property', $result); - $this->assertEquals('PROPNAME', $result->name); - $this->assertEquals('propValue', $result->value); - - $this->assertEquals(1, count($result->parameters)); - $this->assertEquals('PARAMNAME', $result->parameters[0]->name); - $this->assertEquals("paramvalue1\nvalue2\\nvalue3", $result->parameters[0]->value); - - } - - function testReadPropertyParameterQuotedColon() { - - $data = "PROPNAME;PARAMNAME=\"param:value\":propValue"; - $result = Reader::read($data); - - $this->assertInstanceOf('Sabre\\VObject\\Property', $result); - $this->assertEquals('PROPNAME', $result->name); - $this->assertEquals('propValue', $result->value); - $this->assertEquals(1, count($result->parameters)); - $this->assertEquals('PARAMNAME', $result->parameters[0]->name); - $this->assertEquals('param:value', $result->parameters[0]->value); - - } - - function testReadForgiving() { - - $data = array( - "BEGIN:VCALENDAR", - "X_PROP:propValue", - "END:VCALENDAR" - ); - - $caught = false; - try { - $result = Reader::read(implode("\r\n",$data)); - } catch (ParseException $e) { - $caught = true; - } - - $this->assertEquals(true, $caught); - - $result = Reader::read(implode("\r\n",$data), Reader::OPTION_FORGIVING); - - $expected = implode("\r\n", array( - "BEGIN:VCALENDAR", - "X_PROP:propValue", - "END:VCALENDAR", - "" - )); - - $this->assertEquals($expected, $result->serialize()); - - - } - - function testReadWithInvalidLine() { - - $data = array( - "BEGIN:VCALENDAR", - "DESCRIPTION:propValue", - "Yes, we've actually seen a file with non-idented property values on multiple lines", - "END:VCALENDAR" - ); - - $caught = false; - try { - $result = Reader::read(implode("\r\n",$data)); - } catch (ParseException $e) { - $caught = true; - } - - $this->assertEquals(true, $caught); - - $result = Reader::read(implode("\r\n",$data), Reader::OPTION_IGNORE_INVALID_LINES); - - $expected = implode("\r\n", array( - "BEGIN:VCALENDAR", - "DESCRIPTION:propValue", - "END:VCALENDAR", - "" - )); - - $this->assertEquals($expected, $result->serialize()); - - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorFifthTuesdayProblemTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorFifthTuesdayProblemTest.php deleted file mode 100644 index 069832a0fb..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorFifthTuesdayProblemTest.php +++ /dev/null @@ -1,44 +0,0 @@ -VEVENT->UID); - - while($it->valid()) { - $it->next(); - } - - // If we got here, it means we were successful. The bug that was in the - // system before would fail on the 5th tuesday of the month, if the 5th - // tuesday did not exist. - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorInfiniteLoopProblemTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorInfiniteLoopProblemTest.php deleted file mode 100644 index 670c39baee..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorInfiniteLoopProblemTest.php +++ /dev/null @@ -1,91 +0,0 @@ -DTSTART = '20090420T180000Z'; - $ev->RRULE = 'FREQ=WEEKLY;BYDAY=MO;UNTIL=20090704T205959Z;INTERVAL=1'; - - $this->assertFalse($ev->isInTimeRange(new DateTime('2012-01-01 12:00:00'),new DateTime('3000-01-01 00:00:00'))); - - } - - /** - * Different bug, also likely an infinite loop. - */ - function testYearlyByMonthLoop() { - - $ev = Component::create('VEVENT'); - $ev->UID = 'uuid'; - $ev->DTSTART = '20120101T154500'; - $ev->DTSTART['TZID'] = 'Europe/Berlin'; - $ev->RRULE = 'FREQ=YEARLY;INTERVAL=1;UNTIL=20120203T225959Z;BYMONTH=2;BYSETPOS=1;BYDAY=SU,MO,TU,WE,TH,FR,SA'; - $ev->DTEND = '20120101T164500'; - $ev->DTEND['TZID'] = 'Europe/Berlin'; - - // This recurrence rule by itself is a yearly rule that should happen - // every february. - // - // The BYDAY part expands this to every day of the month, but the - // BYSETPOS limits this to only the 1st day of the month. Very crazy - // way to specify this, and could have certainly been a lot easier. - $cal = Component::create('VCALENDAR'); - $cal->add($ev); - - $it = new RecurrenceIterator($cal,'uuid'); - $it->fastForward(new DateTime('2012-01-29 23:00:00', new DateTimeZone('UTC'))); - - $collect = array(); - - while($it->valid()) { - $collect[] = $it->getDTSTART(); - if ($it->getDTSTART() > new DateTime('2013-02-05 22:59:59', new DateTimeZone('UTC'))) { - break; - } - $it->next(); - - } - - $this->assertEquals( - array(new DateTime('2012-02-01 15:45:00', new DateTimeZone('Europe/Berlin'))), - $collect - ); - - } - - /** - * Something, somewhere produced an ics with an interval set to 0. Because - * this means we increase the current day (or week, month) by 0, this also - * results in an infinite loop. - * - * @expectedException InvalidArgumentException - * @return void - */ - function testZeroInterval() { - - $ev = Component::create('VEVENT'); - $ev->UID = 'uuid'; - $ev->DTSTART = '20120824T145700Z'; - $ev->RRULE = 'FREQ=YEARLY;INTERVAL=0'; - $cal = Component::create('VCALENDAR'); - $cal->add($ev); - - $it = new RecurrenceIterator($cal,'uuid'); - $it->fastForward(new DateTime('2013-01-01 23:00:00', new DateTimeZone('UTC'))); - - // if we got this far.. it means we are no longer infinitely looping - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorMinusOneProblemTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorMinusOneProblemTest.php deleted file mode 100644 index 2c17f9f6be..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorMinusOneProblemTest.php +++ /dev/null @@ -1,30 +0,0 @@ -VEVENT->UID); - - $this->assertTrue($it->valid()); - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorTest.php deleted file mode 100644 index 1811d8b6f9..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/RecurrenceIteratorTest.php +++ /dev/null @@ -1,1425 +0,0 @@ -UID = 'bla'; - $ev->RRULE = 'FREQ=DAILY;BYHOUR=10;BYMINUTE=5;BYSECOND=16;BYWEEKNO=32;BYYEARDAY=100,200'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-10-07'),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - $this->assertTrue($it->isInfinite()); - $this->assertEquals(array(10), $it->byHour); - $this->assertEquals(array(5), $it->byMinute); - $this->assertEquals(array(16), $it->bySecond); - $this->assertEquals(array(32), $it->byWeekNo); - $this->assertEquals(array(100,200), $it->byYearDay); - - } - - /** - * @expectedException InvalidArgumentException - * @depends testValues - */ - function testInvalidFreq() { - - $ev = new Component('VEVENT'); - $ev->RRULE = 'FREQ=SMONTHLY;INTERVAL=3;UNTIL=20111025T000000Z'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-10-07'),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - } - - /** - * @expectedException InvalidArgumentException - */ - function testVCalendarNoUID() { - - $vcal = new Component('VCALENDAR'); - $it = new RecurrenceIterator($vcal); - - } - - /** - * @expectedException InvalidArgumentException - */ - function testVCalendarInvalidUID() { - - $vcal = new Component('VCALENDAR'); - $it = new RecurrenceIterator($vcal,'foo'); - - } - - /** - * @depends testValues - */ - function testHourly() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=HOURLY;INTERVAL=3;UNTIL=20111025T000000Z'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-10-07 12:00:00', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,$ev->uid); - - $this->assertEquals('hourly', $it->frequency); - $this->assertEquals(3, $it->interval); - $this->assertEquals(new DateTime('2011-10-25', new DateTimeZone('UTC')), $it->until); - - // Max is to prevent overflow - $max = 12; - $result = array(); - foreach($it as $item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new DateTime('2011-10-07 12:00:00', $tz), - new DateTime('2011-10-07 15:00:00', $tz), - new DateTime('2011-10-07 18:00:00', $tz), - new DateTime('2011-10-07 21:00:00', $tz), - new DateTime('2011-10-08 00:00:00', $tz), - new DateTime('2011-10-08 03:00:00', $tz), - new DateTime('2011-10-08 06:00:00', $tz), - new DateTime('2011-10-08 09:00:00', $tz), - new DateTime('2011-10-08 12:00:00', $tz), - new DateTime('2011-10-08 15:00:00', $tz), - new DateTime('2011-10-08 18:00:00', $tz), - new DateTime('2011-10-08 21:00:00', $tz), - ), - $result - ); - - } - - /** - * @depends testValues - */ - function testDaily() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=DAILY;INTERVAL=3;UNTIL=20111025T000000Z'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,$ev->uid); - - $this->assertEquals('daily', $it->frequency); - $this->assertEquals(3, $it->interval); - $this->assertEquals(new DateTime('2011-10-25', new DateTimeZone('UTC')), $it->until); - - // Max is to prevent overflow - $max = 12; - $result = array(); - foreach($it as $item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new DateTime('2011-10-07', $tz), - new DateTime('2011-10-10', $tz), - new DateTime('2011-10-13', $tz), - new DateTime('2011-10-16', $tz), - new DateTime('2011-10-19', $tz), - new DateTime('2011-10-22', $tz), - new DateTime('2011-10-25', $tz), - ), - $result - ); - - } - - /** - * @depends testValues - */ - function testNoRRULE() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,$ev->uid); - - $this->assertEquals('daily', $it->frequency); - $this->assertEquals(1, $it->interval); - - // Max is to prevent overflow - $max = 12; - $result = array(); - foreach($it as $item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new DateTime('2011-10-07', $tz), - ), - $result - ); - - } - - /** - * @depends testValues - */ - function testDailyByDayByHour() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=DAILY;BYDAY=SA,SU;BYHOUR=6,7'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-10-08 06:00:00', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - $this->assertEquals('daily', $it->frequency); - $this->assertEquals(1, $it->interval); - $this->assertEquals(array('6','7'), $it->byHour); - $this->assertEquals(array('SA','SU'), $it->byDay); - - // Grabbing the next 12 items - $max = 12; - $result = array(); - foreach($it as $item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new datetime('2011-10-08 06:00:00', $tz), - new datetime('2011-10-08 07:00:00', $tz), - new datetime('2011-10-09 06:00:00', $tz), - new datetime('2011-10-09 07:00:00', $tz), - new datetime('2011-10-15 06:00:00', $tz), - new datetime('2011-10-15 07:00:00', $tz), - new datetime('2011-10-16 06:00:00', $tz), - new datetime('2011-10-16 07:00:00', $tz), - new datetime('2011-10-22 06:00:00', $tz), - new datetime('2011-10-22 07:00:00', $tz), - new datetime('2011-10-23 06:00:00', $tz), - new datetime('2011-10-23 07:00:00', $tz), - ), - $result - ); - - } - - /** - * @depends testValues - */ - function testDailyByHour() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=DAILY;INTERVAL=2;BYHOUR=10,11,12,13,14,15'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2012-10-11 12:00:00', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - $this->assertEquals('daily', $it->frequency); - $this->assertEquals(2, $it->interval); - $this->assertEquals(array('10','11','12','13','14','15'), $it->byHour); - - // Grabbing the next 12 items - $max = 12; - $result = array(); - foreach($it as $item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new datetime('2012-10-11 12:00:00', $tz), - new datetime('2012-10-11 13:00:00', $tz), - new datetime('2012-10-11 14:00:00', $tz), - new datetime('2012-10-11 15:00:00', $tz), - new datetime('2012-10-13 10:00:00', $tz), - new datetime('2012-10-13 11:00:00', $tz), - new datetime('2012-10-13 12:00:00', $tz), - new datetime('2012-10-13 13:00:00', $tz), - new datetime('2012-10-13 14:00:00', $tz), - new datetime('2012-10-13 15:00:00', $tz), - new datetime('2012-10-15 10:00:00', $tz), - new datetime('2012-10-15 11:00:00', $tz), - ), - $result - ); - - } - - /** - * @depends testValues - */ - function testDailyByDay() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=DAILY;INTERVAL=2;BYDAY=TU,WE,FR'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - $this->assertEquals('daily', $it->frequency); - $this->assertEquals(2, $it->interval); - $this->assertEquals(array('TU','WE','FR'), $it->byDay); - - // Grabbing the next 12 items - $max = 12; - $result = array(); - foreach($it as $item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new DateTime('2011-10-07', $tz), - new DateTime('2011-10-11', $tz), - new DateTime('2011-10-19', $tz), - new DateTime('2011-10-21', $tz), - new DateTime('2011-10-25', $tz), - new DateTime('2011-11-02', $tz), - new DateTime('2011-11-04', $tz), - new DateTime('2011-11-08', $tz), - new DateTime('2011-11-16', $tz), - new DateTime('2011-11-18', $tz), - new DateTime('2011-11-22', $tz), - new DateTime('2011-11-30', $tz), - ), - $result - ); - - } - - /** - * @depends testValues - */ - function testWeekly() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;COUNT=10'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - $this->assertEquals('weekly', $it->frequency); - $this->assertEquals(2, $it->interval); - $this->assertEquals(10, $it->count); - - // Max is to prevent overflow - $max = 12; - $result = array(); - foreach($it as $item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new DateTime('2011-10-07', $tz), - new DateTime('2011-10-21', $tz), - new DateTime('2011-11-04', $tz), - new DateTime('2011-11-18', $tz), - new DateTime('2011-12-02', $tz), - new DateTime('2011-12-16', $tz), - new DateTime('2011-12-30', $tz), - new DateTime('2012-01-13', $tz), - new DateTime('2012-01-27', $tz), - new DateTime('2012-02-10', $tz), - ), - $result - ); - - } - - /** - * @depends testValues - */ - function testWeeklyByDayByHour() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=MO;BYHOUR=8,9,10'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-10-07 08:00:00', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - $this->assertEquals('weekly', $it->frequency); - $this->assertEquals(2, $it->interval); - $this->assertEquals(array('TU','WE','FR'), $it->byDay); - $this->assertEquals(array('8','9','10'), $it->byHour); - $this->assertEquals('MO', $it->weekStart); - - // Grabbing the next 12 items - $max = 15; - $result = array(); - foreach($it as $item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new DateTime('2011-10-07 08:00:00', $tz), - new DateTime('2011-10-07 09:00:00', $tz), - new DateTime('2011-10-07 10:00:00', $tz), - new DateTime('2011-10-18 08:00:00', $tz), - new DateTime('2011-10-18 09:00:00', $tz), - new DateTime('2011-10-18 10:00:00', $tz), - new DateTime('2011-10-19 08:00:00', $tz), - new DateTime('2011-10-19 09:00:00', $tz), - new DateTime('2011-10-19 10:00:00', $tz), - new DateTime('2011-10-21 08:00:00', $tz), - new DateTime('2011-10-21 09:00:00', $tz), - new DateTime('2011-10-21 10:00:00', $tz), - new DateTime('2011-11-01 08:00:00', $tz), - new DateTime('2011-11-01 09:00:00', $tz), - new DateTime('2011-11-01 10:00:00', $tz), - ), - $result - ); - - } - - /** - * @depends testValues - */ - function testWeeklyByDaySpecificHour() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=SU'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-10-07 18:00:00', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - $this->assertEquals('weekly', $it->frequency); - $this->assertEquals(2, $it->interval); - $this->assertEquals(array('TU','WE','FR'), $it->byDay); - $this->assertEquals('SU', $it->weekStart); - - // Grabbing the next 12 items - $max = 12; - $result = array(); - foreach($it as $item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new DateTime('2011-10-07 18:00:00', $tz), - new DateTime('2011-10-18 18:00:00', $tz), - new DateTime('2011-10-19 18:00:00', $tz), - new DateTime('2011-10-21 18:00:00', $tz), - new DateTime('2011-11-01 18:00:00', $tz), - new DateTime('2011-11-02 18:00:00', $tz), - new DateTime('2011-11-04 18:00:00', $tz), - new DateTime('2011-11-15 18:00:00', $tz), - new DateTime('2011-11-16 18:00:00', $tz), - new DateTime('2011-11-18 18:00:00', $tz), - new DateTime('2011-11-29 18:00:00', $tz), - new DateTime('2011-11-30 18:00:00', $tz), - ), - $result - ); - - } - - /** - * @depends testValues - */ - function testWeeklyByDay() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=SU'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - $this->assertEquals('weekly', $it->frequency); - $this->assertEquals(2, $it->interval); - $this->assertEquals(array('TU','WE','FR'), $it->byDay); - $this->assertEquals('SU', $it->weekStart); - - // Grabbing the next 12 items - $max = 12; - $result = array(); - foreach($it as $item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new DateTime('2011-10-07', $tz), - new DateTime('2011-10-18', $tz), - new DateTime('2011-10-19', $tz), - new DateTime('2011-10-21', $tz), - new DateTime('2011-11-01', $tz), - new DateTime('2011-11-02', $tz), - new DateTime('2011-11-04', $tz), - new DateTime('2011-11-15', $tz), - new DateTime('2011-11-16', $tz), - new DateTime('2011-11-18', $tz), - new DateTime('2011-11-29', $tz), - new DateTime('2011-11-30', $tz), - ), - $result - ); - - } - - /** - * @depends testValues - */ - function testMonthly() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=3;COUNT=5'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-12-05', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - $this->assertEquals('monthly', $it->frequency); - $this->assertEquals(3, $it->interval); - $this->assertEquals(5, $it->count); - - $max = 14; - $result = array(); - foreach($it as $item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new DateTime('2011-12-05', $tz), - new DateTime('2012-03-05', $tz), - new DateTime('2012-06-05', $tz), - new DateTime('2012-09-05', $tz), - new DateTime('2012-12-05', $tz), - ), - $result - ); - - - } - - /** - * @depends testValues - */ - function testMonthlyEndOfMonth() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=2;COUNT=12'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-12-31', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - $this->assertEquals('monthly', $it->frequency); - $this->assertEquals(2, $it->interval); - $this->assertEquals(12, $it->count); - - $max = 14; - $result = array(); - foreach($it as $item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new DateTime('2011-12-31', $tz), - new DateTime('2012-08-31', $tz), - new DateTime('2012-10-31', $tz), - new DateTime('2012-12-31', $tz), - new DateTime('2013-08-31', $tz), - new DateTime('2013-10-31', $tz), - new DateTime('2013-12-31', $tz), - new DateTime('2014-08-31', $tz), - new DateTime('2014-10-31', $tz), - new DateTime('2014-12-31', $tz), - new DateTime('2015-08-31', $tz), - new DateTime('2015-10-31', $tz), - ), - $result - ); - - - } - - /** - * @depends testValues - */ - function testMonthlyByMonthDay() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=5;COUNT=9;BYMONTHDAY=1,31,-7'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-01-01', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - $this->assertEquals('monthly', $it->frequency); - $this->assertEquals(5, $it->interval); - $this->assertEquals(9, $it->count); - $this->assertEquals(array(1, 31, -7), $it->byMonthDay); - - $max = 14; - $result = array(); - foreach($it as $item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new DateTime('2011-01-01', $tz), - new DateTime('2011-01-25', $tz), - new DateTime('2011-01-31', $tz), - new DateTime('2011-06-01', $tz), - new DateTime('2011-06-24', $tz), - new DateTime('2011-11-01', $tz), - new DateTime('2011-11-24', $tz), - new DateTime('2012-04-01', $tz), - new DateTime('2012-04-24', $tz), - ), - $result - ); - - } - - /** - * @depends testValues - */ - function testMonthlyByDay() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=2;COUNT=16;BYDAY=MO,-2TU,+1WE,3TH'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-01-03', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - $this->assertEquals('monthly', $it->frequency); - $this->assertEquals(2, $it->interval); - $this->assertEquals(16, $it->count); - $this->assertEquals(array('MO','-2TU','+1WE','3TH'), $it->byDay); - - $max = 20; - $result = array(); - foreach($it as $k=>$item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new DateTime('2011-01-03', $tz), - new DateTime('2011-01-05', $tz), - new DateTime('2011-01-10', $tz), - new DateTime('2011-01-17', $tz), - new DateTime('2011-01-18', $tz), - new DateTime('2011-01-20', $tz), - new DateTime('2011-01-24', $tz), - new DateTime('2011-01-31', $tz), - new DateTime('2011-03-02', $tz), - new DateTime('2011-03-07', $tz), - new DateTime('2011-03-14', $tz), - new DateTime('2011-03-17', $tz), - new DateTime('2011-03-21', $tz), - new DateTime('2011-03-22', $tz), - new DateTime('2011-03-28', $tz), - new DateTime('2011-05-02', $tz), - ), - $result - ); - - } - - /** - * @depends testValues - */ - function testMonthlyByDayByMonthDay() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=MONTHLY;COUNT=10;BYDAY=MO;BYMONTHDAY=1'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-08-01', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - $this->assertEquals('monthly', $it->frequency); - $this->assertEquals(1, $it->interval); - $this->assertEquals(10, $it->count); - $this->assertEquals(array('MO'), $it->byDay); - $this->assertEquals(array(1), $it->byMonthDay); - - $max = 20; - $result = array(); - foreach($it as $k=>$item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new DateTime('2011-08-01', $tz), - new DateTime('2012-10-01', $tz), - new DateTime('2013-04-01', $tz), - new DateTime('2013-07-01', $tz), - new DateTime('2014-09-01', $tz), - new DateTime('2014-12-01', $tz), - new DateTime('2015-06-01', $tz), - new DateTime('2016-02-01', $tz), - new DateTime('2016-08-01', $tz), - new DateTime('2017-05-01', $tz), - ), - $result - ); - - } - - /** - * @depends testValues - */ - function testMonthlyByDayBySetPos() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=MONTHLY;COUNT=10;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=1,-1'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-01-03', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - $this->assertEquals('monthly', $it->frequency); - $this->assertEquals(1, $it->interval); - $this->assertEquals(10, $it->count); - $this->assertEquals(array('MO','TU','WE','TH','FR'), $it->byDay); - $this->assertEquals(array(1,-1), $it->bySetPos); - - $max = 20; - $result = array(); - foreach($it as $k=>$item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new DateTime('2011-01-03', $tz), - new DateTime('2011-01-31', $tz), - new DateTime('2011-02-01', $tz), - new DateTime('2011-02-28', $tz), - new DateTime('2011-03-01', $tz), - new DateTime('2011-03-31', $tz), - new DateTime('2011-04-01', $tz), - new DateTime('2011-04-29', $tz), - new DateTime('2011-05-02', $tz), - new DateTime('2011-05-31', $tz), - ), - $result - ); - - } - - /** - * @depends testValues - */ - function testYearly() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=YEARLY;COUNT=10;INTERVAL=3'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-01-01', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - $this->assertEquals('yearly', $it->frequency); - $this->assertEquals(3, $it->interval); - $this->assertEquals(10, $it->count); - - $max = 20; - $result = array(); - foreach($it as $k=>$item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new DateTime('2011-01-01', $tz), - new DateTime('2014-01-01', $tz), - new DateTime('2017-01-01', $tz), - new DateTime('2020-01-01', $tz), - new DateTime('2023-01-01', $tz), - new DateTime('2026-01-01', $tz), - new DateTime('2029-01-01', $tz), - new DateTime('2032-01-01', $tz), - new DateTime('2035-01-01', $tz), - new DateTime('2038-01-01', $tz), - ), - $result - ); - - } - - /** - * @depends testValues - */ - function testYearlyLeapYear() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=YEARLY;COUNT=3'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2012-02-29', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - $this->assertEquals('yearly', $it->frequency); - $this->assertEquals(3, $it->count); - - $max = 20; - $result = array(); - foreach($it as $k=>$item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new DateTime('2012-02-29', $tz), - new DateTime('2016-02-29', $tz), - new DateTime('2020-02-29', $tz), - ), - $result - ); - - } - - /** - * @depends testValues - */ - function testYearlyByMonth() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=YEARLY;COUNT=8;INTERVAL=4;BYMONTH=4,10'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-04-07', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - $this->assertEquals('yearly', $it->frequency); - $this->assertEquals(4, $it->interval); - $this->assertEquals(8, $it->count); - $this->assertEquals(array(4,10), $it->byMonth); - - $max = 20; - $result = array(); - foreach($it as $k=>$item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new DateTime('2011-04-07', $tz), - new DateTime('2011-10-07', $tz), - new DateTime('2015-04-07', $tz), - new DateTime('2015-10-07', $tz), - new DateTime('2019-04-07', $tz), - new DateTime('2019-10-07', $tz), - new DateTime('2023-04-07', $tz), - new DateTime('2023-10-07', $tz), - ), - $result - ); - - } - - /** - * @depends testValues - */ - function testYearlyByMonthByDay() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-04-04', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - $this->assertEquals('yearly', $it->frequency); - $this->assertEquals(5, $it->interval); - $this->assertEquals(8, $it->count); - $this->assertEquals(array(4,10), $it->byMonth); - $this->assertEquals(array('1MO','-1SU'), $it->byDay); - - $max = 20; - $result = array(); - foreach($it as $k=>$item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $tz = new DateTimeZone('UTC'); - - $this->assertEquals( - array( - new DateTime('2011-04-04', $tz), - new DateTime('2011-04-24', $tz), - new DateTime('2011-10-03', $tz), - new DateTime('2011-10-30', $tz), - new DateTime('2016-04-04', $tz), - new DateTime('2016-04-24', $tz), - new DateTime('2016-10-03', $tz), - new DateTime('2016-10-30', $tz), - ), - $result - ); - - } - - /** - * @depends testValues - */ - function testFastForward() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU'; - $dtStart = new Property\DateTime('DTSTART'); - $dtStart->setDateTime(new DateTime('2011-04-04', new DateTimeZone('UTC')),Property\DateTime::UTC); - - $ev->add($dtStart); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - // The idea is that we're fast-forwarding too far in the future, so - // there will be no results left. - $it->fastForward(new DateTime('2020-05-05', new DateTimeZone('UTC'))); - - $max = 20; - $result = array(); - while($item = $it->current()) { - - $result[] = $item; - $max--; - - if (!$max) break; - $it->next(); - - } - - $tz = new DateTimeZone('UTC'); - $this->assertEquals(array(), $result); - - } - - /** - * @depends testValues - */ - function testComplexExclusions() { - - $ev = new Component('VEVENT'); - $ev->UID = 'bla'; - $ev->RRULE = 'FREQ=YEARLY;COUNT=10'; - $dtStart = new Property\DateTime('DTSTART'); - - $tz = new DateTimeZone('Canada/Eastern'); - $dtStart->setDateTime(new DateTime('2011-01-01 13:50:20', $tz),Property\DateTime::LOCALTZ); - - $exDate1 = new Property\MultiDateTime('EXDATE'); - $exDate1->setDateTimes(array(new DateTime('2012-01-01 13:50:20', $tz), new DateTime('2014-01-01 13:50:20', $tz)), Property\DateTime::LOCALTZ); - $exDate2 = new Property\MultiDateTime('EXDATE'); - $exDate2->setDateTimes(array(new DateTime('2016-01-01 13:50:20', $tz)), Property\DateTime::LOCALTZ); - - $ev->add($dtStart); - $ev->add($exDate1); - $ev->add($exDate2); - - $vcal = Component::create('VCALENDAR'); - $vcal->add($ev); - - $it = new RecurrenceIterator($vcal,(string)$ev->uid); - - $this->assertEquals('yearly', $it->frequency); - $this->assertEquals(1, $it->interval); - $this->assertEquals(10, $it->count); - - $max = 20; - $result = array(); - foreach($it as $k=>$item) { - - $result[] = $item; - $max--; - - if (!$max) break; - - } - - $this->assertEquals( - array( - new DateTime('2011-01-01 13:50:20', $tz), - new DateTime('2013-01-01 13:50:20', $tz), - new DateTime('2015-01-01 13:50:20', $tz), - new DateTime('2017-01-01 13:50:20', $tz), - new DateTime('2018-01-01 13:50:20', $tz), - new DateTime('2019-01-01 13:50:20', $tz), - new DateTime('2020-01-01 13:50:20', $tz), - ), - $result - ); - - } - - /** - * @depends testValues - */ - function testOverridenEvent() { - - $vcal = Component::create('VCALENDAR'); - - $ev1 = Component::create('VEVENT'); - $ev1->UID = 'overridden'; - $ev1->RRULE = 'FREQ=DAILY;COUNT=10'; - $ev1->DTSTART = '20120107T120000Z'; - $ev1->SUMMARY = 'baseEvent'; - - $vcal->add($ev1); - - // ev2 overrides an event, and puts it on 2pm instead. - $ev2 = Component::create('VEVENT'); - $ev2->UID = 'overridden'; - $ev2->{'RECURRENCE-ID'} = '20120110T120000Z'; - $ev2->DTSTART = '20120110T140000Z'; - $ev2->SUMMARY = 'Event 2'; - - $vcal->add($ev2); - - // ev3 overrides an event, and puts it 2 days and 2 hours later - $ev3 = Component::create('VEVENT'); - $ev3->UID = 'overridden'; - $ev3->{'RECURRENCE-ID'} = '20120113T120000Z'; - $ev3->DTSTART = '20120115T140000Z'; - $ev3->SUMMARY = 'Event 3'; - - $vcal->add($ev3); - - $it = new RecurrenceIterator($vcal,'overridden'); - - $dates = array(); - $summaries = array(); - while($it->valid()) { - - $dates[] = $it->getDTStart(); - $summaries[] = (string)$it->getEventObject()->SUMMARY; - $it->next(); - - } - - $tz = new DateTimeZone('GMT'); - $this->assertEquals(array( - new DateTime('2012-01-07 12:00:00',$tz), - new DateTime('2012-01-08 12:00:00',$tz), - new DateTime('2012-01-09 12:00:00',$tz), - new DateTime('2012-01-10 14:00:00',$tz), - new DateTime('2012-01-11 12:00:00',$tz), - new DateTime('2012-01-12 12:00:00',$tz), - new DateTime('2012-01-14 12:00:00',$tz), - new DateTime('2012-01-15 12:00:00',$tz), - new DateTime('2012-01-15 14:00:00',$tz), - new DateTime('2012-01-16 12:00:00',$tz), - ), $dates); - - $this->assertEquals(array( - 'baseEvent', - 'baseEvent', - 'baseEvent', - 'Event 2', - 'baseEvent', - 'baseEvent', - 'baseEvent', - 'baseEvent', - 'Event 3', - 'baseEvent', - ), $summaries); - - } - - /** - * @depends testValues - */ - function testOverridenEvent2() { - - $vcal = Component::create('VCALENDAR'); - - $ev1 = Component::create('VEVENT'); - $ev1->UID = 'overridden'; - $ev1->RRULE = 'FREQ=WEEKLY;COUNT=3'; - $ev1->DTSTART = '20120112T120000Z'; - $ev1->SUMMARY = 'baseEvent'; - - $vcal->add($ev1); - - // ev2 overrides an event, and puts it 6 days earlier instead. - $ev2 = Component::create('VEVENT'); - $ev2->UID = 'overridden'; - $ev2->{'RECURRENCE-ID'} = '20120119T120000Z'; - $ev2->DTSTART = '20120113T120000Z'; - $ev2->SUMMARY = 'Override!'; - - $vcal->add($ev2); - - $it = new RecurrenceIterator($vcal,'overridden'); - - $dates = array(); - $summaries = array(); - while($it->valid()) { - - $dates[] = $it->getDTStart(); - $summaries[] = (string)$it->getEventObject()->SUMMARY; - $it->next(); - - } - - $tz = new DateTimeZone('GMT'); - $this->assertEquals(array( - new DateTime('2012-01-12 12:00:00',$tz), - new DateTime('2012-01-13 12:00:00',$tz), - new DateTime('2012-01-26 12:00:00',$tz), - - ), $dates); - - $this->assertEquals(array( - 'baseEvent', - 'Override!', - 'baseEvent', - ), $summaries); - - } - - /** - * @depends testValues - */ - function testOverridenEventNoValuesExpected() { - - $vcal = Component::create('VCALENDAR'); - - $ev1 = Component::create('VEVENT'); - $ev1->UID = 'overridden'; - $ev1->RRULE = 'FREQ=WEEKLY;COUNT=3'; - $ev1->DTSTART = '20120124T120000Z'; - $ev1->SUMMARY = 'baseEvent'; - - $vcal->add($ev1); - - // ev2 overrides an event, and puts it 6 days earlier instead. - $ev2 = Component::create('VEVENT'); - $ev2->UID = 'overridden'; - $ev2->{'RECURRENCE-ID'} = '20120131T120000Z'; - $ev2->DTSTART = '20120125T120000Z'; - $ev2->SUMMARY = 'Override!'; - - $vcal->add($ev2); - - $it = new RecurrenceIterator($vcal,'overridden'); - - $dates = array(); - $summaries = array(); - - // The reported problem was specifically related to the VCALENDAR - // expansion. In this parcitular case, we had to forward to the 28th of - // january. - $it->fastForward(new DateTime('2012-01-28 23:00:00')); - - // We stop the loop when it hits the 6th of februari. Normally this - // iterator would hit 24, 25 (overriden from 31) and 7 feb but because - // we 'filter' from the 28th till the 6th, we should get 0 results. - while($it->valid() && $it->getDTSTart() < new DateTime('2012-02-06 23:00:00')) { - - $dates[] = $it->getDTStart(); - $summaries[] = (string)$it->getEventObject()->SUMMARY; - $it->next(); - - } - - $this->assertEquals(array(), $dates); - $this->assertEquals(array(), $summaries); - - } -} - diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Splitter/ICalendarTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Splitter/ICalendarTest.php deleted file mode 100644 index 43613350f0..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Splitter/ICalendarTest.php +++ /dev/null @@ -1,283 +0,0 @@ -version = VObject\Version::VERSION; - } - - function createStream($data) { - - $stream = fopen('php://memory','r+'); - fwrite($stream, $data); - rewind($stream); - return $stream; - - } - - function testICalendarImportValidEvent() { - - $data = <<createStream($data); - - $objects = new ICalendar($tempFile); - - $return = ""; - while($object=$objects->getNext()) { - $return .= $object->serialize(); - } - $this->assertEquals(array(), VObject\Reader::read($return)->validate()); - } - - function testICalendarImportEndOfData() { - $data = <<createStream($data); - - $objects = new ICalendar($tempFile); - - $return = ""; - while($object=$objects->getNext()) { - $return .= $object->serialize(); - } - $this->assertNull($object=$objects->getNext()); - } - - /** - * @expectedException Sabre\VObject\ParseException - */ - function testICalendarImportInvalidEvent() { - $data = <<createStream($data); - - $objects = new ICalendar($tempFile); - } - - function testICalendarImportMultipleValidEvents() { - - $event[] = <<createStream($data); - - $objects = new ICalendar($tempFile); - - $return = ""; - $i = 0; - while($object=$objects->getNext()) { - - $expected = <<version//EN -CALSCALE:GREGORIAN -$event[$i] -END:VCALENDAR - -EOT; - - $return .= $object->serialize(); - $expected = str_replace("\n", "\r\n", $expected); - $this->assertEquals($expected, $object->serialize()); - $i++; - } - $this->assertEquals(array(), VObject\Reader::read($return)->validate()); - } - - function testICalendarImportEventWithoutUID() { - - $data = <<version//EN -CALSCALE:GREGORIAN -BEGIN:VEVENT -END:VEVENT -END:VCALENDAR - -EOT; - $tempFile = $this->createStream($data); - - $objects = new ICalendar($tempFile); - - $return = ""; - while($object=$objects->getNext()) { - $expected = str_replace("\n", "\r\n", $data); - $this->assertEquals($expected, $object->serialize()); - $return .= $object->serialize(); - } - - $this->assertEquals(array(), VObject\Reader::read($return)->validate()); - } - - function testICalendarImportMultipleVTIMEZONESAndMultipleValidEvents() { - - $timezones = <<createStream($data); - - $objects = new ICalendar($tempFile); - - $return = ""; - $i = 0; - while($object=$objects->getNext()) { - - $expected = <<version//EN -CALSCALE:GREGORIAN -$timezones -$event[$i] -END:VCALENDAR - -EOT; - $expected = str_replace("\n", "\r\n", $expected); - - $this->assertEquals($expected, $object->serialize()); - $return .= $object->serialize(); - $i++; - - } - - $this->assertEquals(array(), VObject\Reader::read($return)->validate()); - $this->assertEquals(array(), VObject\Reader::read($return)->validate()); - } - - function testICalendarImportWithOutVTIMEZONES() { - - $data = <<createStream($data); - - $objects = new ICalendar($tempFile); - - $return = ""; - while($object=$objects->getNext()) { - $return .= $object->serialize(); - } - - $this->assertEquals(array(), VObject\Reader::read($return)->validate()); - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Splitter/VCardTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Splitter/VCardTest.php deleted file mode 100644 index b6b41925fc..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/Splitter/VCardTest.php +++ /dev/null @@ -1,138 +0,0 @@ -createStream($data); - - $objects = new VCard($tempFile); - - $return = ""; - while($object=$objects->getNext()) { - $return .= $object->serialize(); - } - - VObject\Reader::read($return); - } - - function testVCardImportValidVCardsWithCategories() { - $data = <<createStream($data); - - $objects = new VCard($tempFile); - - $return = ""; - while($object=$objects->getNext()) { - $return .= $object->serialize(); - } - - VObject\Reader::read($return); - } - - function testVCardImportEndOfData() { - $data = <<createStream($data); - - $objects = new VCard($tempFile); - $object=$objects->getNext(); - - $this->assertFalse($object=$objects->getNext()); - - - } - - /** - * @expectedException InvalidArgumentException - */ - function testVCardImportCheckInvalidArgumentException() { - $data = <<createStream($data); - - $objects = new VCard($tempFile); - while($object=$objects->getNext()) { - $return .= $object->serialize(); - } - - } - - function testVCardImportMultipleValidVCards() { - $data = <<createStream($data); - - $objects = new VCard($tempFile); - - $return = ""; - while($object=$objects->getNext()) { - $return .= $object->serialize(); - } - - VObject\Reader::read($return); - } - - function testVCardImportVCardWithoutUID() { - $data = <<createStream($data); - - $objects = new VCard($tempFile); - - $return = ""; - while($object=$objects->getNext()) { - $return .= $object->serialize(); - } - - VObject\Reader::read($return); - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/StringUtilTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/StringUtilTest.php deleted file mode 100644 index 59a83d2945..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/StringUtilTest.php +++ /dev/null @@ -1,59 +0,0 @@ -assertEquals(false, $string); - - } - - function testIsUTF8() { - - $string = StringUtil::isUTF8('I 💚 SabreDAV'); - - $this->assertEquals(true, $string); - - } - - function testUTF8ControlChar() { - - $string = StringUtil::isUTF8(chr('0x00')); - - $this->assertEquals(false, $string); - - } - - function testConvertToUTF8nonUTF8() { - - $string = StringUtil::convertToUTF8(chr('0xbf')); - - $this->assertEquals(utf8_encode(chr('0xbf')), $string); - - } - - function testConvertToUTF8IsUTF8() { - - $string = StringUtil::convertToUTF8('I 💚 SabreDAV'); - - $this->assertEquals('I 💚 SabreDAV', $string); - - } - - function testConvertToUTF8ControlChar() { - - $string = StringUtil::convertToUTF8(chr(0x00)); - - $this->assertEquals('', $string); - - } - - - - - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/TimeZoneUtilTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/TimeZoneUtilTest.php deleted file mode 100644 index 7f76353d20..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/TimeZoneUtilTest.php +++ /dev/null @@ -1,297 +0,0 @@ -assertEquals($ex->getName(), $tz->getName()); - - } - - function testWetherMicrosoftIsStillInsane() { - - $vobj = <<assertEquals($ex->getName(), $tz->getName()); - - } - - function testUnknownExchangeId() { - - $vobj = <<assertEquals($ex->getName(), $tz->getName()); - - } - - function testWindowsTimeZone() { - - $tz = TimeZoneUtil::getTimeZone('Eastern Standard Time'); - $ex = new \DateTimeZone('America/New_York'); - $this->assertEquals($ex->getName(), $tz->getName()); - - } - - function testTimezoneOffset() { - - $tz = TimeZoneUtil::getTimeZone('GMT-0400', null, true); - $ex = new \DateTimeZone('Etc/GMT-4'); - $this->assertEquals($ex->getName(), $tz->getName()); - - } - - /** - * @expectedException InvalidArgumentException - */ - function testTimezoneFail() { - - $tz = TimeZoneUtil::getTimeZone('FooBar',null,true); - - } - - function testFallBack() { - - $vobj = <<assertEquals($ex->getName(), $tz->getName()); - - } - - function testLjubljanaBug() { - - $vobj = <<assertEquals($ex->getName(), $tz->getName()); - - } - - function testWeirdSystemVLICs() { - -$vobj = <<assertEquals($ex->getName(), $tz->getName()); - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/VersionTest.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/VersionTest.php deleted file mode 100644 index ae6855e854..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/VersionTest.php +++ /dev/null @@ -1,17 +0,0 @@ -assertEquals(-1, version_compare('0.9.0',$v)); - - $s = Version::STABILITY; - $this->assertTrue($s == 'alpha' || $s == 'beta' || $s =='stable'); - - } - -} diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/issue153.vcf b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/issue153.vcf deleted file mode 100644 index 5fb0fa297c..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/Sabre/VObject/issue153.vcf +++ /dev/null @@ -1,352 +0,0 @@ -BEGIN:VCARD -VERSION:3.0 -N:Benutzer;Test;;; -FN:Test Benutzer -PHOTO;BASE64: - /9j/4AAQSkZJRgABAQAAAQABAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQA - AAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAABQKADAAQAAAABAAABQAAAAAD/2wBD - AAIBAQIBAQICAQICAgICAwUDAwMDAwYEBAMFBwYHBwcGBgYHCAsJBwgKCAYGCQ0JCgsLDAwMBwkN - Dg0MDgsMDAv/2wBDAQICAgMCAwUDAwULCAYICwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL - CwsLCwsLCwsLCwsLCwsLCwsLCwv/wAARCAFAAUADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAA - AAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKB - kaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZn - aGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT - 1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcI - CQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAV - YnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6 - goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk - 5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD8J7JbO8tYo1tIFCDLOVG5qfdaVZRwmSOFWzyA - F4H1rLt5WViMhdp6HgmtKK8O3B+4Rhx6fSgBI9FtjaNN5aErwRjilSys7lFAt41xyTtqc2yJCVlY - 7eqgGqv2jyLcebjZnGPWncdzT0+w0u5eQXtrGiBcIyoPmNMXwpb/AGMTSRRbH6YAyPwqK21GKdfL - BAVfu+1SQX4jnjKFsp03dPypCKN9oEaKSkC7R0bGKpnSlSPdHErZOORXV3Ouy337sCLB6kpx+FY0 - t+VfyrgcbuCB1oAfoMemrcImq2sZX+I7ATXS618PdK1DRlvvDEaMq5LoV2nisx4LVrUfu5BOePau - m8EQS6PY3HmFXjljKhTzjOf1oA4mz8OxvMrLbW5RD8wbByKg1LRrRriRYY408w/KAMba1pRaWt/H - a6a7CVm2u7N8lUPEujzaRekzSK6tgqVNAGNBZJauY5Yon92GTRJp0ROY0Un0A4q3c2odkaYOMjii - KL7NIDGcj1NDAZBplmmWv1xnoFHStfS/DFpewqYoYm3DutZ8lv8AapdyOqk8EVteEbSe3KBSrDrQ - BT8S+HbawiiWGCAPjsuMnPesqHS4JSFlSMP7DitbXbvfrkkM2eGw3p+FMfTh5X+hr8w7t3oAhOhW - u8MkMZUY3fL0Heo9UsrN5FFrbxKmMBgoG41fWFra0Acjpzg9aoXjtgRoo29vagCoun27kbY059qn - bwykskYjRArdTT7GEl2UqMr2q/JtVU27iR15NADdK8DC/wBPle2iicxNg5ALH6Umm6FZ/a3ttQt4 - g2Cqnb0PbJ+tamn3j6ZCW0nILfeBORWVfO4dhLw7fMW7560AZuqeHf7MuTFcRpv6qVGVx70q2Eci - QwyW0SsPvOqjJrUtb6S9tHQKGeMZYuM8VUs7gRxbrncy9mWgB1x4QtTHvsQWkHJVhhax3tkhugHh - UkfeAXIFdPZ3v2uxkQ9G4jI6/j+tYun3r2Fy6yxeb2Py5IoAqXenJ5xaGNNvXH/1qcLSGeBdkSg9 - CcdaswC3be0pfexOMnpn2qaS1KQkQASKoydvLCgDNi09RKTNCuO2BxVjSobc6gqXMERQHkleDUsc - u9VADbG6qOWAp11bLbptkjlCkZRsde9AFi5sbO3kKfZYTnkHaOlVbuO2F5thtYcADjaKXUpHj8ku - Co2VDFL5wLeg696YFwQ2z7Qtlb8HJO0c1Zsr7T7a9kL6XazZ4CmMFRWfHdkEgjGRjPpU9raP5LSP - j5h2pAWdQ0+z1KdG+y21qvcRqBn8qXSvC+iTu63ssqyE/IAuR+NQwSrGm1g+c8E9qiSQW9wPNYYP - OR2oAW68GNa28k3lwGNHwvzDJGfSqM9nHBgm3j59QMVdmma4zIjsUBHy5OKp6o8s2BJjZjjAoAro - /nysbgYY9zWmLPCR+WQQwyaz4k2F/Pbft/GtKxvUeFN+B2x+NAEptsWpZSdo9etZe8su2X7pPFdU - LeOazKqVwevNYt7pw5EA5HIxQBQA8tAIeGz1NWIJvJlhW5OQBzjrUMR/eN9pwoXjB4qQ3ERJeYcy - 9P8AZoA0jf8AmybVxsHAFS6jp63ixmwjIwOfrWfaou12GcDpmt/w5qJhXc6hh2GM0AZkHiRpblVl - G0RjGMdxXQ+H/E0Rm+bjdw1crqEHm3EksY4Y9PTmq0cskc42qUOfpmgDovHOhLBOZ9O+aEnIUdRW - QZft1sgum/1Ywua3fDfiFDL5WoEPEwxzzirPizwTFPZC60kYUjcAp4NAHPSq91EoRS3061DHD9nb - 94Mkfw020v57GbcCRt4IIqzNcedIH2jc3JyOaAIYrRZmJxtNdB4fkGn2hluBgBR+NZ2n2X9ozAQD - 5qvaxGbKIRXkuFU4C96AMDxBKZdQkuEUkStuUegpNM1eWScAkqpHTHNPlwbjMzExZ4Pal1PS/s6+ - dY/6vuwPSgC9G8c0A+1xEknrnpUVxaeXNm2dVUfjVazvEZAEkMrccZzV1YYyBIhJP8SZ6fhQBSmV - 4JfMVT+96UJdSQdcMO4A6fjVmTUoJiqTOMJ/q+elRyQs0TtaxF0PVhzmgCzpd55r7YI2HHPTmrV0 - sDTF7gnJXGO4OKyNKgn80NbFhjoBzWjqdg6SISPmIBOaAKVnI1leyhsMJOD7CqOqRtZqotjiFulW - rhsSMshKH1ogsZbmF475TKifdf0oApabevHIAhCYOdxp0t59luS0I+995uxqpdRyWsrqmXGeCR/K - rVlZfaogqv8AvD/CaAIY42kV3K5zzn1p9jNLp6u/A80YPNWWsJNPAVpC4JAZT2HfFWJoVmVVjhVk - HTPrQBPoi2wsoo4APtBHL+tP1mS5uVEFxgJGNqH15plp5WmyBriMRsowM8UybXTNdbrpd6A/KKAD - xbJAGs44FIPlnd9c/wD16ynt/LiDW2SR2qa5vP7RnMs6BNuQMd6jhkAUb2K8+tADYp0fhj8w6itC - yQ3CFYeAOoqi8Uew+UMuf4u9T2NwIW+UgMetO4FmS6RJ1ik6HqxHAqC+gimUiA8DvjrU0kcE8ieY - itu+8c0+bShaWxksSZoM4b0SkBTgha0cq33Cuc1SvrrLFV6jpWqbuGe1HnnDdAKy7i3WSY7OT2NN - AMulWSV8ZDNzxV7SlbaFjClx69Kpww7W3ct7jpUtnNJHd5UjZnt1NIDdt7h7NQ7qGfpt7VR1XVEh - dhEpP94/4VpafexTy7ZlbBGDVHxFbQh1j04HaOTkdKAM5ZVlYso3E+tVp4w8gx0Bqd7QxNu+6D6V - DIoVySxAx2NAFyNmli2pjYBz61paW3lWrFS3BwP8/hWJbTBFJy2D6HgfWtiTWPsqxraBHyOeBg0A - RSoLSTdIepzz0606exTWyQGMXljORTNT1B7+ECZR5fHzDqapfbHjbFkTsIwSTQA43ptyyS44Paun - 8N64Z7Bre4YlZBtU5+7XLTQbjwN4Pb+IfWn2lw9uyrIw2Z5HpQBv3GirHc7LxWVZOVI71FNp7WDg - QYlIIGD6VvaPdi+tljb5yeAzcn8DT9YtbPSpVhDM87jJ3Htjnn6UAUIrJreD7Si7MDoKhv8AUxqt - pGt5GqIOr9zRfLM8ZFgZGtex2nGe4zWKN8rsDhYx2JpJ3Atx+HxcRSzWcpcL/CRwaj0zW1sQy3cS - nsFPSoYJpbIl7dm8tT8wzV7+0hqEO1Y4lQ9cqMn9KoCp9kW7kaaxU+Yx+5j5etWrb/RGxfr5bkdu - lW7KFILpfspDbVyc1fjNnrLtHqOYWP8AFjGfxpAc/e6Ql/GzW4AfqBWfpupS6Xer5vPlHmMjg10V - 5pp0u4JhYNGvAYHrUn2WLWrVo41AvSMRZAC/8CPr1oAvafdWOuNG+lqDekY+zg8MPXPX/wDXWZrF - tcWNw0erKElB4Rf4R6c1BpqyaBdbrnEcwyAc4x06H0rQS9a9jUTgOXPzMwycexoAw7u1jYb3zkU3 - Srtgdk54PFamv2C2pDQbWjcfKCeSa56aJld23YA6ZOKFqBrXGjjULuOKxKuZOTn+H/OKwr/ztOvs - uCrg7RgVLYapPbXAEW4EkHJNdBNBH4gtgyhFmXuw60AVpbT7VpiPJ94jLetQWsDRSIYz8mec1c0+ - 1nexdrw7GjJXk/epsFtDPG0bOdw+b5SaAKWsXA+14Y71FQi5S4RvlAC8A0y5hHmHarhvQ9BVGSQx - sUXPHX3oAmDCJ8rzgHg96gQ+ZGWbg9vahNRG7EnalkkF6hEXyD270MCWF3aEhdue1OsmNnMAih/r - VaBgAUY8561PaubdnMxJXseuKANhIY5Assp2v12itZtAgubEi2nb5xuKYHWubstQaO6SVzujTqpP - X8K2rXWLRF8xZJPMfjAzgUAcxcNiaRSpUocc96sW+yNgZCMVF4lvJdRvTOYkj52jbgZ98D6VWmlY - 2qCUnJOKaVwCzviibANwYc8Utkdl7tbKhjxmpUspvm8tgn16ipigSEG4G4pxu9TSA27GeFbRlGGm - P3cdhUN8GEP2hV3JjafrWfpU/wBmuAcZLA4/Sr1trkarJHcRmSEZO3uTQBmrcbZCLoDZ2x1qOHSi - yebJIAPQipp4kmbzI1EQJ6GtCxsoHP8Ap91GB2yDQBlSWO+M/ZsBHHzZ71XkfMIWNgGU9vSt3U9N - t9m21uonz0Iz/hVCfRkjg82FhtHDGgCuZ8EMjDZjBzSZ8pAwU7XbGT0pWtEjjAZgV4PFOml2QKqk - OoOcU1qBNYRSrdkrhw3BIrah8KwXoV/m3PyVzyDWNp999kccgZq/ea7PFAGgZlJ6EUgN23thpdi4 - V1Eucr7ev9K53V/ER1a/MkuWdBtG04zioLrXJ5wDK2XAxmqVqmZ2YPtHJ/GgDsvC3i0ppr2d2ish - yFAHIz706bRLNdOPnErKw4y3NcvZ3pjA8o4kB61o3OpSX9nbx3QIkU/MwoAj/sGaPzFjlWSJjk46 - ioYYwqssjIHHAHpWm4ESN9nYDIFZV+I7uVI1wrY5b1oAtafcvb3W4MM9Nx6U/VZpNRys54ToU4zW - KXaDKrJuC8cVdtpi1gzs43HNAD9N195bdYtRIUR4wD1NX2KuA9uThuSQelcsZwzq9xyzfezV/SdX - e3m8pXJhkPKkUAdYZk8RywjVVJES7U2cE/WtA+HDHohuY3Uxg7RF/GeaPBlxaawMW6rHKnAU9SOO - lX/FFv8A2bpzTQk+cpAAz93nrQBx+r4c5CODEOA3Y+wrKu5V1C1GFKznkk9K6Wzv49fs8Xf7y7DY - MhGNgrmtX0s2t66WknnKvUp0/WgCnbrJFdot0NwJxkDFdDYp86oMjjIArJivxbR7LuMyEjKitS21 - MW8auuW44H93/PFAG15aXdr5Uv7uULkA/wCFc+Yvstw0at8+eoq/p+rm6vRJMNwIx9KranYySXSy - WEZZHOCw7UARXFyj5STAk7ntWVf2gALLyfUVoataLbfLO2SO/Ws2c+VwhLK3QDpQBmz2xAyCG56d - 6uWPlnCkFcjoTzUBkMc/3cZpwn8oZkDFs8HsKALN1apDIHOeaiLkRkMOtSXE6yxAsRUcdxldswIJ - HANMCuJW8xQgOP51oacWPPGAeRUUOIZQzDhecd6mbIcbPusM0gLmq6bHPohlhDeZuH4c1zzF1+Rs - HByDXTae0s0IhjjZg3GPWqOs+HpLCTbNGyb+cHrQBZitjPEzW/LL97vinw2v2m2aORec9AKXQbsw - ygBBiX72TWxfaS8kiGFQAwz8vWkncDlbqNraT5cjb/n+lMGckx8kjOa1tU2TxkPkMpxyKyrhJ4Wa - KIDbTAkgvIp7URzgBwe/BpZYrd4vmZWNZ81x5cgBXDdzVlIvtUOGIBHpQA2aEROpR8DsB2q3bvG9 - iySzEsTkLnrVMqViCZzt7nrT7GBVuQRnODQA6Q+Sx80A4HApEJB3BAR9K19EmhkvCJ0ZsKe3tUc8 - Mc1yy7cpn6YoAzoUiclnYYY8AHpUl8zRxqpPy9qtC2tULgSMAvQ460lzIl9b7YiDt4GaAKMMQlJ5 - z9Kj8gIW5yKnS3Crlzhh6d6k0mbyZT565Q5z60ANtrRpPmhzWhbwy7DJcDhhwMdKlt7aK+gb+z33 - yKdxVuMCqaz5cqGYfWgB6yu8rBB8o6Gs/UpjGQXBGPTvVmSfyImyepqrqjbIw3WgCDz1ib9yOTg4 - NbVlNBJYvlVBHt1rBaPzQWU4IHSn2FwRJslJxQA6e3M0O4oAzdB6VXR2iKGQENGOK0ms1eAkFjF/ - BjrVGaAo371smgC7pety2kwl06Vo5AOWXmuwm+Itv4g8Ota30aWlySAJQfmkP/1zXIeG4Y5SVBB3 - evamXGly2tydwG0nKkHpQBZ86fRbpBLI252y4PGRWhO8Ml1IbJhHn+BTnNU9O1oRwvDqqhB2lHJP - 4U6awb+z4JdKbzdh5ZurDHtQBat5LaRHiaOP7QejEZKD/Oauy+FI7W3Bsroyhxkq3QH8q5a7ujM8 - nWOQnBqTR9burCT98xdR60AbbaHc6ZG3ymJsZC/3hVnw/fNIXt7hygHzZp2oeIBqCxzqfmCgEe3+ - RVdrmLVAEtf3bxfOW/ve36UAV7+7DXMu5Q4/Os2e3eRWkiAGOijtWrPodxfQmeNVAPOPWsppJIpi - JxsKcY9aAMwRyTSbpflx68VOYvOXb97OKtXAiZdzkqT0AGc037BIIRLHjsR60AVprZrZwGj4qTY0 - xyRj3PUVMJDduFfqvFRzxJCzrCzEr60ALEu+YI53c4qeGB7lGCnBU4FUopTBLvfk1at9R2sAMjNA - GtaXsnhy2FzPHvC46jgnNQ33imTXrkz3oVFAwo9Kfrtq03hAzEfJ5gyc81hWM5hhKrhgT0NPcByS - P5g2uVI98Vp6X4uuNGlyzCQIQR0bI7/1rNQxqW+05J7Y4qK5ZYUP2ZCW9TSA7SR9M8V30X9nMFZw - WfcNi5qPWPDtjo0pE7O03U/Mf055rmtFmN9E0DEox+atPWbiW7lSO8Ja4jQbcDC4A9PXFADYtM0+ - 6nc3u7aOm3IP6Vnak9tYt/xL/M445zTIbieOdmWNsE46cip42EkyC4hYx469KAFsrT7XEJgFPOT6 - 1s+H9PD3XlzxnL/MDtqn9pghgb7GjL/eJORWqfEnmrA9oFRoxjJ5BoAp6NqDW2pzRXtuyIAw3FMf - rVS4iF08pydmeCDxWvqeuC+Ro9qglcMw71mwReXD5aAlFJPPU0AZ0cEsbkSZKH15FD2xJJiJVj6c - VfnzLGEXAA71PFpDPaebE6/KOh60AYVws8TBgrFe57CmHUG25RVJA7AVozzSLbNvX5T1AHNY/m/Z - nPlqwDetAEtvqzJNu3FZBwQBjI96vPqkd3mRtokH31UYx+VZqWruxaFl+frkZxT1tvs1ujJgEH5m - PR/pQAXl2S371XAHI+Wkaf7VD8hGR2arKySylRccQ98DmiS0jifdsdgeODQBQd9x3IBx1xTYlBm3 - En86sXUAwPswKg9QeaBErIEj6nrQC0NHRtUjt0K3AHzDABGcVW1fTzJL51jyOpz0NVooispebBI4 - wK2YFEthk8qR07igDAgJil+TKtnnHFaP2h5yI3ZsgdSfaqd2P3im3BGM9aktsjmRgCOaAJZrMwR7 - 3A5PT0pdMvZtOning+byzuVDyh/A8VHczSzDPy7RwOKgiuHEewjKeoFAzp7TUNM8XXEw8RhYNQmP - 7ny18uNeOM7cCtMfDiS8uY0tDEYghyynjPbn864htP8ANhLIehzWzovxDvtFsDB9+PI4I/rQI0r3 - wNc6DO0N2VaQqW2q24YxmqFhYRgE/vkkDfMGBBP4GrSeJ7tZd6SxvIfmK4yQP84p0XiyC71gS65G - 00zAKGX5Qv4UAbFpd28WnIsBLsDzmub1+AXt1LJEoQqfu4xu+lbWsWgs4/NsCXjPIbqK5+5kklmE - rDD54BFAGb5cjybCrAnnB6ipEvXil2sM4GMVpFY7m4UNmNyOWJ4qteaM0BISVZe+RQBFHC2/zISg - B69KlIVhIHA3HuR70lqotlBulY5P4Vcls44k3u6N5oyoHb60wM6O1SRir5LemOKv2vhuW4iLg7VA - 6k4FTR2ax4aaVIwR3HWqGua5PcQm1WRBH6jqaQFzWbE2nhzynuIi+8HaHyKweJSEQEN6jpVcKyOw - cMVznOeKmtZvOPDKuOKAJbi0JYFf4eue9IW8sncfvdqnlvVFyFyu09abI0bysMZx0oArC4eCTcgb - juK2dNvE1N1M0ohljGQzc5A7cfSs6aweWAk7kTuapQysIT9mOSvG49aAOkvzLMxk06QNuG1l7j3r - PlnnJAuGJij+nNQ6XqT7wEYqyn5v9utLULaW7j321uiEjLqMkKKAIotbghb/AI8hKGPIBHNXLG6t - 7uzk3RLbKG/iP+Fc+8f2d1eFztzyD2q5p2oCFWRoxOX52nPFAGgLyC2lyZFKdB70r69buxRJBHjr - nvWVdeXLE7xE8fwnoPpVKZUnQPkBhwRmgDq7a9tLyARWiiWYngL1qG4gurJ28+NowO2a5a3v3smD - aa5WUd1HNbC6zI0KSX13JO7D5lbHFAE4V7pi0b5x1GazdUtXSM7v4iPw5rQ0/XrcXX75FgUdxzuq - /qFrp+sWRe3uDkc4BFAHLRDY42ycd6uPOXiiV+RGPlWnXOg3IQvEmIB/Ft6/jUUEZmMcgydvzECg - C1G2+Ly3YAvyM9qY88kaFcmmp807uwPJ4FS3do+Fzn5ulAFVrjbgS8Z4yah2C03SMffNWZdPknVA - iluQOnHWmX9pILvyY13HHK46UAVre7LSyOCTmtjSiy7VijLeZ0IqO08OzPIUiTI74Ga6bRP7O01F - h1KYJOv3V4BoA4zU1lExMrkbOAvpVcSifhjgrzmtjxPp7pO7SggOcqfUViy25hG5fSgC8rrLAojb - d7d6SexlEgwpRfTNV7e5LFBbKAwPNWHeX7TguxI7GmBPBExhaNVIJ6egqOVknO1fkx1J61aj1gLC - UEKlk4LVWvozC67kCFxkD1pAQ24e3uDLC3z9CR3H/wCqrczJdOGiOxvYc5/CocMYhtUBj3xU8Qjk - XbKPIZOjqclvzoAu2HiO60xPKvd7wY/1fGBWnJo8WuW6y6XIPMYZEAzuH9KxISonAuzuRzgk9qtR - 79KmMuhTt5cRyxznFADLzS2tMw6pAY5OoDEZ/Sm20TQQ74YwVQckGtMatB4kUpqreVIRw5+8aqXF - jc6bAsbD9yThWz94UAOmmjvrRCMJjOQRVS0sD9pLyABM5Of6Vdtrdn+RUGcZqO6uRBG0MuFI79KA - MfV7r7ZqDI7kohAVT6U2eJNimJQOuTnpSXFussrMvBz1pJov3YUsR9O9ABblRncQ3bAqY2EUwIiA - Vqr20ojfYqZx3q9bSKAGcYJPIoAoq7OCEQBffrRDGEcleM8nNPjuGkhHmbB74ApvmxltsuTnuDQA - +SFEjDwu5buD0qpLL5vMg2kEdOlXECMAyZGOMMePyprQRI5N0rt3BXO326UAV4b0Wt0pC5HrXS2W - qq9zE7jcO+OhFc81kbg7iMqeAFHSpLa8eymaNOUIwD6UAavjPQYYybq1bBmXcF9O39Kw4iXdDKcE - DAxW3q7NdWELISdiYIz71kz6ZNZNHI0cjqQfujIFAEtzAtu/7vODzmqlyzNyAo9vWp7uWSWJd+AM - jjGGqOWCSWRVVW2+uKAKskpWU5TP0p8c+ExsPPNTmCVD+5U/QrzRJHJGymeOQc45HFAFczh497KR - jirWlEsAudvII9znitEeBp7yAPZvEVPJUsP5ZqCO3j0yYDUNwliI6dOPpQBt/wDCR3Wj6eHFujvI - do3DIX9KoHXoL6J11CJYZAONlaWueIYtY8Nwx6ZHu2MdxVeTXKG0eaXKRuCeuBQB0mn+HRe2Yeze - MqRkFmwfyra0rwsIrRmvZICcgDLVw7xXFuFd2uEQfeAJAxUkkjSxh4J7gjPAErf40Abvjq1i0y4S - KByCdrfL+FUI7SR4Wc+WzMOCW5qhf3Mt9cCV2ZiihRk5qpdTSBgRI+R2DnFAFw2k6AqJZMjuD1qn - cxzyyAkPuiP3ieT/AJzV+01R7a2RpMZPVmGQ1WVuTqLDCptcfMBwRQBEkst/YMCSTH8vJqtJaoYQ - JPv1o+ZDZKAo+UnBpmrCBpRNp4/0crgZ9f8A9dAzCdGgkOynxSus2xjkj+L1qW5/fxYj+8D+NRWz - R4fzCd2O9Ai0lzI6mPaMOcZqW4uI7rbtJ3IMc1XScKqncQT0olPlKWfBz6UATKjSDcmdoFWtPCyR - kzckHiqUV0623lKVIPzHHWp7Ic/vSRz0zQBcCqdyT4J7YqC3uZdKv1a2UupO7B6H2NMglMUsmcnd - 0Lc4q3BmaMBiDjr60AWJRBfyb9P2RueWJ6KfQVLHqMdtcEysxJXayN0x0yKyWihWQBdwTOSdxHNb - zWEF5ErXhX7QQAMNge2f0oAnhs4rq2kksHwirkg9SfauXnJnmL3AbL9jXSRWh0N28x1cEfMqtnA/ - Cs+70+O9/fWRIb+76fhSTuBimbyyyKDgnipLk7AML1pZbCWO7Hnjn26U6ZykRL+veqAryuvm/Jwf - Sk3mo2AyHyCT6Ux5pLU5Gwg88gGkBPNAILUO3KmooyjL8ueegzTvPMsRjG4qBwKrW1sxJZzsIPGa - AJbmfp5q7MZx71NZawEi8qZSyHg4NRGLzCPtB3eme1R3Nutocodyd8UAaVtqEUDlI8/N3PaqV2Ht - X2x4lIOSwHFSWkEFyo+cD1BpbmNbNdkh20AMh1UiJ1c9RzWj/wAJa1vYiK1RmRvvetY5gDENxgnp - UlhN5TiI4O4845oAmu51lXzFDGQ8jnpTra4uJkBAOQavXvhG8tIhPawvJAfmY9gKE1COwgIiAZiO - 3rQBV866T52Qsw6YrXguZNTs0WSJ8IPnHr9KwZNamNumZSpPU4pbPxBeRy/uJjtXqfWgDodMtnXK - QjYeo3VnalpiXjMzXMKS9O9VV1ydCXkmLY/SorWwTVJTmQEt81AHTeCY49Mik+0SJKmOg71W1bxH - HLdgaXaSRNnjdzWapGlBBG2ec4GKtQ6yZD5hjLMvbIzQBfutWC2ajV4ywwN2OM/Sql/JY2kKGzU/ - McnBBqlf3Lam5e8lKMv3Yz2FU4VjgzsGQ3WgDa0ya0u7kxzgqCCcn1q43hizkEjRkOoXcAOua5Ka - 6Mc3ygEVb0nW57ac/ZC4Xuo5zQBBeZjcwuMxRn5fUUmnySx6kv2cgg98deK1LjT31pTLpymSVuWi - Xqv17U2GzFgFBUCVOo7igCTT7cnTp/ty5ZnyCvGOKz2uwimOY7geQB0FWY7tzu8xiqk8A96qOvmy - MSowOc0AVpkkgk3uAiP39KkjtonYtnO4cKOP1q1Z3K+X5V2N6OeM8gfWiewaxiKhDsAyJB2oAk0u - 1juAwniYshwoB61FLZfaJDv/AHWexpulXRNwpjkP7s8nu1Wd4uC7zfezxQBTjxZTHzlMigbdy8Up - YXEv7nPvk1aNqbhDhgARnFZMCvbzuWZgc/nQBo2l6qs63AJA6VIsiG4DI4jXP8XeqcbrK5JH3xkH - 0pWhWVR52CF6UAa8kUd7H8rD5f1p5txHAfNPasWRCjgh8D0BrV0a+DgCdfM3DaB9RigCml/JFPyB - 159xV+C/wfNHAbtUN9orxO3k5dhycfw1XmT7JarIjb1k6U2BcuNSVGDSAPu6be1QTXcO0CVSwbPA - 7VRtpftEmxW2Mx6HvUv2V1J2jkdaQBFJB5jBVYemetRyW6SqTKCfTFNllCHBX5vWkLBPvk4NADTG - 0ePKB5qdLN5NjycqvNQIpZAFVj71LsaJQBuGaAH3aCVwycKODUMsZgJjxv8AXIzUs0DpHhmBycjm - gOd37wdRjNAFETeTcARAbSeTViApfrhjufHXNJNCsUu18Z61Xit3Q5JxQBdW0MYKyn5hSf2BPIjS - 24I29T6f5xUMMrs5HOF71ooVmtMyu3ynAAzQBqeCfG7aaPsmuYkiYFG3HseKq67YQW2rSNpLCS0l - GQ5GSh74xWZc2SyxK4OZl5x7d/0rV0K+j+xPFOu4Pwpx0oAo3OnFreM7AR9Kp/2eYpxtyCx6VoXd - g2nSlQzMh6UxJdjqSpKgfN6mgCOLSZGkKyYw/wCn+c1YltRodoWA+Y8Z+taPhWz866DQqxLdmq34 - x0ZbS23yY3NgkUAcZcSyrjcc7zw3YU62meOeTazdOhrZ07TYLkYvSFVfmqveQWkDj7CW9zg0AZs9 - 8wbO3L8ZpvmGRsyZQDsO9WLu0EwZojwMc1DJCrsA5we1AFmGVZLc7Y1bA6nvU1gIyNzgxtnoKr7I - NgHO8dx0pJ3AYG3UnHegDRS+NpL5lsxh3dQverj38OtL/pKCKSPhWU/f+tYEt98xMnC9qgludrrJ - GzFl7DvQBq6pYNGdzHGO3aqS33kEBhlSME0+01z7OcXGXRupJ5H0q5fafFqNuJLLnofmGDRsBmJe - DzMEZGevpW7o8sN/bzLqTBML8oB71k/2YYh83FQRqbdtr7sDv60AX7jSo4ZsiVo067hj9anuNHey - jVizMj8gkdaqQyi+UxjO7O0A96tXDz6rEFucp5HygUANGEQKjDJGaqzWbzgyn5QOPY1p2xZtOaGN - VMo5BPoKqxa1NHHtmij+Q4xkUAUraZFiYScMOgNMf76CIZHf2q5KRq8arEjK4OTsGaki0oKwAEhP - uDmgCohEsqq/O6rrMNMj3AEdgfQmn3tqUgEcaYz1JFMtLdn0wpFGxYHhjQBa026M0XM2WQ/NnHzU - 6Yw6tCPt6rbpH0CdvzrPtrZ45ceU4cHk9qtzW6XLOjqwY9+1AEa+HWun8zR28xU5LAZx+VLaGSV9 - jrkr145amvEY4hGkjKMg5XoPY/571vaHFDr95HHqDMkoU4C9G+uKAOevoo5iSBjBxVYwLdRkL1Xt - XSeK/CdzpkjRMqyJ95SjbsD3rmJbUwoeuGOCfSgC9eWc9rcbbdA0KHPmhcq39Ka8e9DkBS5zk1X0 - /wAR3dvEtuTm3AwVzW/D4w0xIEivbOaSTAVWBAH40AYMu6CZDkFcHcTz6UrtkYlwVHIwOtb91olr - qtuRZSL5h5EX8VY97pc1jKAqZ2jB/wA/nQBRJhubjE4YOOnNMC+S+DzmrMkIA819wPTbjmqwfzcM - 4w3vQA9mbYwgIz/ENvSm2t+6jZsYKeTkVYjn/eqwGAOp9aeW+2sdkgVf5UAQLKY5MHGferNv+6IM - XT07CmyaeZIS1vtmkUdQKbZ+akOZoyqMe45oAvRzjUJPLLgSds8/zqyPDzwETagy4U8YwARWMbcw - NuDDePenPrbXEfkTn5hwrdqAO709LPSbbzlZdvqD0Ncnr/iufX793uWQrGdmFGBjpmstdQeFRHKx - 2Nn5f73+f61E7iLCxDnrjvQBaubtNypAxyRzg0q263DMsJIzzyc1mwyDeSD82e9XIGUIrSyBNw+X - 2+tAD3tSpcFvufrVZbdL2XbnDdjnGKnhs2nkYtcIEJ6461HMiJIApBVe5HWgB8mmtpzDzSrrkZYU - 65mRGYoBgirEkCStiJlC7c5IqjLNsYhtu0d6AKkshbAZcAdc81Gdwb5SD6cVZjYy5WXBVu/pWppn - h63urfdLdxR47MDk0AYjnhehxntVq11OVANuTj8q2/8AhBZ7mwkm00CYKQBtHXrWe+kTWS7J4zE+ - OQ1ACQX/ANrkC3DD0wODV280KQwM0jxheueKdZWcCrvkjYYHUHvRe6jFLapHtLKeDjg0AVrDQ5xd - xuhIUEMHx8pH1roZtH+2W+dPIbHDMOcms+81YNoqWltlFKhQD1HNP0e5udHsHFkcyMRkDoaALUPh - aa1n8yUgqRgjPOO/eq+reDkvHzoQYIB85JzzW5HBLqWmCSWQJM3UEdB3/Sk0S3uNPmIkBlgJyXAw - o/Ci4EHh3QYfDsfm3mHklGGLdFqS91HSYpvMw0jjkhTx/KqXjLUg8hihYiMn746H6Vg+QYxuV9vH - 1oA3xrem38TNe28rqp+VUyD+gpbTU7O6ylvEYoEBPzjDAjp2HeuUk1aeyfNqMH+8BTrvVhqEAMuP - O7n1oA3X1Q3U0klp5S7OGHFZt7rj4DwxlTJ6riqMTiDZsHTn6/WpbfU5EP8AxMVMqdFIOMfWgCZb - lpEO/GDgn9K6bwZpktjcC7lUsAMYPvj/AArBi0lrpc2sqbZsHbjkV20SvDp8UUZBcDp60AY+ueIZ - dIu3Frh0lbD+YNxAPXBPSqLrpuunyNPBSSM7mZyQpJ/KtWQ2uqvNDcjypQjAFjnJx0rhNYhntbvy - 7jcucgIe9AEUMOy5ImYgg4xViVVa4UFSoToc9a6DxZoEdqv2rTsHzDlx/dFcujFpG27vlPGe9AEi - anPpV359o7b143jqo/yP0rWs/FSavF9l1JltlB3tOerd+axl3XGfMXC9896iu7UbtyYIxg0AdTc2 - Vrqe3+zZxIF4Uj+I1S1Hwpexu0kts8aL7Vg2t9JZ8REjJ+UD+Guh0TxjeaW3/EwAuFAxh260AY8y - ujfLkBOCOuabHcqgCxYAbrz0rsbSysfHdzks1rO33Y0AwTWd4h+D2r6M5mmt0ER5D85P1oAxLfWZ - LSYrbnAb5eKnudVnyELFkHOcCqUmjzRzBWyD9K6W38JtLo6TtkLzmgDHtryGZiZUDZqDU1Vl3wp8 - g+9jsf8AOKmGnw2cpE8jFR1I7VdGjRXMQa0kdoSPmHrQBn6bYnWz5NydjgZVgORWeztBK8ZBJQld - x6nFdZ4ZtoNI1QPI7O+OB7VX8faO9rdC7ESrC4BJHqaAOcgUTtuORiraW0M9yiXLAIeoPc+1RWar - u6Haxq7e6ekEZkBGzGVz1ptgVprUw3ku3iJDgDPUYFEzAwZRN2CDgUw3JEkezD7+xolvytwn2pVV - RkADv060gLVlMk4aLIDHp7+1Vbu1+yzgThiHOOelElyIZl8v5CDkVtxWkGtaYs0bMblCcr/KgDCe - 3LzsN20L2HepUQJnHI9KsX+gT29pHKCd79qWw0u4aPcwU4796AL+meIr2G1aDSbiWHOMhR1qxZXz - xXBl1n/iYBBlg/FR6VZW1nciS9mdJADgYGO1Q3pIOOu5hz60AO1vxLDqluP7Pt47eJSQ2KzvtiSg - eWuPpU89gsfzH5cc+1ZaSpbXRZT8tAGjjz237gNuPwrc0O48uUPOM4GBXORXC3HmJD1bB/QVZivZ - fLwp+71oA6fVfEiwXC+UBGjfKTj14qZbi7gtJWjkY2zx5C9s4rnbCRdZiaOUkFQTke3P9KbYa1c6 - XcBARLEWxhzwBU2AotqzH5Ls5YdFPOKmiu1KgxfvCOqHrXTL4EXxLbl9MO6bGRkYzXPal4TuNLu2 - ju/3csfUD9KoDO19yChhO3OcqO1VoZEUbHVckZL9x3q09s8a5uDkZxUDWX2i4OzgHvQBLCwkwyEF - c4z6VNDZm7utkROCfwqCzAhuGRhhV/WtR5okjjkQ7ST2oAlSRtMdUjHzR1p2OuOI2Ly4kHQViS3K - iYBMsW5zSNF9klEjPnPSgC1dzm4uVKSMZd4JP41oeJPD8+r6ZHLbwmW5H3yCMqvr/Os6xu/tDfvU - CqSOfWuj0yf7OxLO2CAG9x6UAZs6vcIqSiVw3GQMisR7RVvpFkGFU46e1dN4c1hYmCXm0quDIO9c - 54quVl16drdDHGzZX6UAV5bTzWIi4Ws6/DQEoQSpI5q9BfywxkS7WU9OOlMa3F8hG7bj5sn86AKc - ErggKVA96lFwLcYHX3NQPAHnYD5e26pAnluA/JoAu6JevFqsEqs4YN0HQV39p8aL+CJVnWKWOP5c - OAf6VwCzrbxAIMMefpT48zEFD9RQB6hZ+PNE8YqsfiJFt5GOC0abcH6ioPF+i2/hiGK50xmuLOQ4 - AjO9s/T8a8wlzLIdxKkHIwcc1s6R43vdJi2xurxsdriQbto9RnpQBal1C1urtzcIVjfqu3FRMNM8 - zbpplViehyAKnuU0/X4N+ixtFdR/67e2fN+g4xzWPcWzWFyDL8gP3Qw+9+NAGhqulSWzpJHt/wBn - Bzj2NejeHLG28f8Ahox6/HsmA2DHBGO9eTrrksUTKSOD0Par+n/EnVdMRVsZYgpHIK9u9KwEvjn4 - eTeF9UY2Jie3HI+bJFc6b6eMkt909j2rsrTxpYa7bGHWYpXlc8Ord/yrOu/B8gEjQul3Ao6RjLL9 - cGhaAcu0skr7mK8HtTjEAcMMk881Zm0l7JXxg7uQBywqqzysygDBPr1qgHSWqzANL6UunXjWBOxW - KsaZcggbu4HSlindrf5ANxNIDqblPteiWrESNC2fujJ7Vd0bRY7KLfZswWYZYSdT2/pWJ4Q8ST21 - 1b2krIYj8pBFdd4k024ht0nsdpjA4AHNAHO6npkSs2SwPase6ieJcSYdenB+atGbWykgF9G2cHvi - qGqMxiWW0GFyCSRnFAFeSN4yGiLE9we1QXYEhzMo+bnAqaC9YzbpSGY8CoL/ACwDQ80AV1mxdJwQ - q9h1qd71WHU/QdqgDO0gJAyevFE4WI8dW60AafhzUHt5v3ZAzxVzXNFku/38Odg9KwbK4ELA4z+N - ddourgQKJsMv92gCr4Y8Qy6VGUmkdLcDjn5/8a6vS5tM8SWTG3kkaZeP3xIyfxrmPEuk/ZXF9akG - CY/LHj7tZy38tvcxSwnYw7DpQB0viLwrIigwhcHqAeKxDpbmcgJtKjOfStXRPHgjlEeuAzZ6bf4e - lajX+navE4gZIyQcFmxQBxd5ZPG+9iuDxmqitHGR5oO09M+tdDqmjNsDl90YPBHSsJ4N7uH7dOOt - MByxj+EkE/d5qwYGkUNu+VetUgxVz6gVNAryx7Y84J5PpSAeZWjG8A/Lg1sabqn2hF8wnniqPkK6 - qk/z/TilaEWo/cgqKANPSbRba8zM6MXGDzVPxHYPPOzOOVPy471R03XmSRXlQEHv6VstqaakgJKh - h0X1oA5jBjYrP8uTkA9TQ0qoxLHqPyrQ1+z6TMu104x65/8A1ViSsVc5GdwoAseWbkDyQWC01QVv - S+5WGcbe9OguTFZqIjhxnPHWnWTCO6LyKjPnpQBDfs4n3sMc8Y7VPBKWT922498U7X0RCjRnJmAL - KP4aq2rtA/ycBu5HXFAGkYg0GT8rY5J5qIw5jyMORxU28zwAou5jxj1pnktAzCUlT1xQBHFP/Z8w - dpNsg6ccj8a6jQPFNjqdqbfxJbvPM/yxTE/LF9c1zsNsJ1U3EYIP8VPe1iicCORsnnHTBoAtat4Z - mS92Wn79WBK7aw0ia3uXW4jdChxkjvW/Z+KLjTZFd4hKwyAc44qy+nwazpxEOPNdvMdx1UdTQBzb - AbSNyqGPf+lWvDPiW58IXDtZzOIpRiVVON4qS/0ePcG04/aYV4Z8YwaoPGJrgq2AqnAPY0AdVdww - eJLX7XoxSKfbnyRwzn61zGooyMzsreYpwQTyn+P/ANap9NvX0S4DQtzu7dhW/rel2viWzWfRiPtC - L88a/wAfuaAOQEvyDepIOOamtbFJZWKzrH7Gpk02QRBLgYYHkDtSTaf5LBgM7u1AEVxbS2aiSNfm - xw3St7RfiTLFZi2vUe4VRt44xWJDczTzoLoFgvO096bMomlkaJfI5ztFAG7Jqdlrcm2WNYHA+82C - KidbiCAoVLWzfKoHOawo1dyGO4bQcc9frWppOvSwQLDcDzQSOvbmgCjcWBQsqDYwOTmo44BdAZfG - OeuK1NYdZLjzCdu8dAKzpLYQt+6OKAK88ciXREQ3AY5/Ckmt3dlMoznPSrMU2zJxgD2zSSRmX5kY - gdiO9AFWO3KSDgqMjrXQ6fYuUAjG3HO7rWRawNeSDLYKnHPeunVG0bR4ruTnc20g96AHxn7ZbNA7 - qzgcVzup2s2mzOl0CAT8jYzvrb1TxpZ3tgr6fBFFL/EUqpp+pJqpxeqJAPulucfSgDDfcjgxAqSP - mB60xXXlZFBPXpV2+tms5W2oTnpk1nht0uZCAfTFAG9oOvCJBb6jueJj8qj+Grer6XFCqvHMvHTA - zmuajlMUmWHznoKvQ6tLDEPtKeZnsT0oAkaBVLGX7x54qOG6NvkEEA/rV2dYLi08y3fMhH3e4rMR - mkDLOMkHg9KALcN7vXI4Iq9ZyG5jw7An1rFuWMWMAopxTzqMkIxZAuOpINAD7ZAcg9F6VqaXdRFg - pX5h92sPzRbfKQdvr61c0+4MjDyxsYHkkUAdA2lvdQ+ZcDIPGOuawNY0wWNywjwVbocdK2E1ubTF - +T5gw5yM1Lc2kOqaX5kXMxG4nPT8KAOSUSKu5VGM03aZmRo22k9Tird26Fgp+6hwcVAZfNmCnBVu - mKAJp7N71FDcuOI8d6pJlLlt+d44PoK0dTZLKCI2HmCZQCd33c+1R6iqXKpJBu34+bPQGmBNpzND - bgH7zHjPapLiXMhEvzMRwarQXG+ILcfMP7w7VZjdHj+QgMOmaQCRF7AsVBZO2am2G5t2kIAJ9O1V - 2vzM21l+UU9Cjj5M8eh4NAAIXjUeRl8/pUa6k1hGFtWyG6n+lWYX25Y8dsUs9t5tkVkK7Tz7+tAE - 9l4hAj8q/RUf+Db0P1qZ/DUWrTO0paK9cfLGg+Qn61zc0SeYc53DgVr+HNfk0u623LgwSDaxHLY9 - QaYFa80a60G58vU1VmbqF5AFWdC1k6PqaTW6qyEbSD+FdRJd2s8IikZJbO46MTmRB7nr2/WsrxD4 - QjtohLo+9kHXPb0pAd6uh6Lrekm6hkkQSRgNtQfK/p+dc1f/AAsuGUnSWSVScgynbisHQfGFxpki - RKw8tRyD0z/nNWPFHji/1lFihkCxKMAocUAaNt8NNSt3bzYrYsnT5xTLvwZYQTIuqzlLh/vqigqP - xrk/7QuIwRHcXG4jnMpP9ary3kzhvtUkrSH7p3E0AdXqPgvT1vI47K4kfcCcYAx0/wAar2ngu2uW - ZIJX3pnjHFc3DqUikfPIGHU5PFb2ka3PDe7dPZGGzGW7/wCc0AX7LRLSzcxb3eXrhhxVG78JeVcA - bvvcVfEgudqaoyrOrbiV9Pwpmo311pMnmWmySH3w1AGRrXh6TRfLMq8yfcHGPxqxZ6fpmnmNddml - jlk5+RQRx/8ArqO51ptT3vMwWU9iOF/CsOZHnkIkYu3YnmgDo7qPTtPszcWTu5LcAr1ycVl6p4hk - 1BRbsCEXkCqEGqz20wEWGEZGAeRxVy+vRqV2JpUVJiACQMAUAZ0+mvaNuuz88hwAOmaktbt7C4Ub - c8jvW5rGkp/YUEsRM0nLSf7PFYogSWEF/lJ6CgDWcjXyuMhwOAO9Y09hLbSyKy9+pqzpM9xo90Jr - co2OMMM5ropr2PxBYGK7VVXBbIXG4jnrQByUI8xSADs6HPWpPLIjGxssvr3pxQmcqx+VGwFHenJI - gOF5oAW0jZB5nQnnH6Usnzjrg0rW2/8AeISD1x2pWR5VySNo60AQBX2EzHIXpSQJ5kjOOFpLgrtI - iLFvWi2Y3CFYuoNAEt4myTBBQ46Gq6OyHKjGTzSyyyXUm+/cnHc0+PY42RtuDcDigDS03UzdQlHG - WHFSw3/2CX99lo+hA64NUorOeyG9FJA68VJFaLqNu0hkIlXkgelAF3VtEjvNMF1pKOctyPTFc/bw - tGVeMfMRzW54f119M8yJ2IjlGzk9B/k1p6f4fsmi2xXsUmeP88U7gYV5Et3aQlWCsox+NR2eUnWG - 7bdvrZ1TRY7FXjuQsatzHJ7VkyeXbxnz38xl6NmkBFfiXR3MDKQjHI9xUMV0ijMnNdBZWbeJbUcC - SZU+U454rFu/DF7byNJcW0qxqeeOtAE0EcbI+4nax49qnKNY7CCG46Vjw3DRHO1gtaNrqPnBRKu1 - R0Y80AXYDHPAzlPmzzTWG2Evn8KafMMWIsFfamKxcAyjAHbNAFSeRJpOBg0xrXykVjyp6VLqFv5b - AqwTI6dal02ZZ5VjuMNGentQBJZxXFtFuUZDcitDSPFrwOYrkFkfj6Vl30l7p87RpKRDn92eoIqG - 31gRxk3qMzqRnmgC/wCJtIa2uzLYfMjgEj2rNs70woyIMjPLHtW7Y3y38gkUnGBke1R6p4dS/mNx - obeZgfvIVH3Pf3oAz7W3EmGzgrSSRqszF13+4/hqOOLdGSrk5HO0d6WCUxYaUMYhw4HegCM6TLcy - Ztkd0wckd6jtZZbPiI+aqnlem2tTStXNvcbYZyiSA4QcdMf41Y8Taf8A2dZieGMR7sAkc7s8H+dA - GVJqTT3AKtjIxtrStNVy/kyLuUj1rAlhG4NtKqOc/wB+l+2SpP8AcKMn3s07gdJdeHPtLRS2zpCr - csD171laro72bGSFWZRwzHpQdUe8hTDEMg5xU0N7Pcx7GVpIf4lzSAwlk2yAoevUDpWpa2hvYeTg - 0mo2UM8w8lPs4HUDvRpsFz9oYW6NKB07U0BbjvptGhkgJDRMu01VLRyyIYQSgA3HstVdVMiSlZyx - bPKiksbyS1hdWUmKQ5K0gJpt8UgAw69iKn0/UyJdrdOmKIPIvW/cyLEqj7p4zUEUIEr+blHXJBx1 - oAk1O28q6VoSFVhk1GbZQ25TzUlvcfakIucKAcAnqaWK1cyFkQlB70AJvJdNq5I4+tBcbCnCjv71 - LIVcAowVhxj0qO2t9zkXHKt0bsKAIpbPIHlKWUjk06wgaNiqIBzViF/kKKwBHA9aguI5oX3REk9j - TQErWypGPOGc/pTLTy47gMFyob5fetB7EmcG3G6N8hSTjNWRpgsws/y7ouWB70gKd5dGSRcfKnIP - HFXrHSYL61e4kfyVVcYA61lC7OrxurAKxbIHtUtxfC2sTDA/A49KAEazRmkEw+TqG9as+H7YSTeX - bvu7ccYrIt7qRdobPLc59K6jw9pf2KUXcJBVjuI/z9aALF88MsJh1AiRoPl54Iqt5GmXUG3ABx1x - 0/WneMbGfTryO8VB5d2N6qfTJHP5VBoNtFqUb/b28uU/d2d6AJLPV4dGtP8AQyokHGKgu/Fwu9wl - PXgj0pmpaSmnOxmYEdu5rOht2knZ4FX3oAimiju3AtlAznrVWSAW7OC2HQ/d7VdNjLaMjurbSeMC - s+4WS41BjyEB5zQBcgnk2ARnJbqKZcydmZt3fFVxB+9DRkjHfNWLh/KKGTp/6FQBGLg3C5PzFeBT - LeT5yEzlB0p1zb7wGtzt9RTNhWVQOHPWgDc0iUajbPbTgM5GE9aydTtPKk8sKcDrk9adZX5+0FLc - FZM/K1dPpmgReJLR2nOyZDhQT1z60AYWgXYtrvy5cFXBXA9+OtGpLceH9YIsZ3BwGI4+YHsaNR09 - 9C1ERTFTMjBgE6YyO9S+IoDqHlag5++RGPfGKALelpb+IbtA+Ldk+ZkXofxqHxFpn2Vpv7OXdGOW - 56Vk3GpCBQB8pB429a0bHXN8kX2gKY1ILju1AGakfmFfJXLN0/z+VdZYQG503yda5xyPp/8AqqXw - 2LKJJvsqbjIdwDL936Viarq8u9nhA8sNg88/TFAGrdeFbeWBHscSL/AM9DWRqnhObyS7KUYdfetH - wkx1Gdnm3rECAB6Vu674psYbIRxeZuHBJHWgDzZw2nybQMluDVnT9T2PsJK56Ve1OS1vJ/OhOfXj - pWVdWctu/mJhgTxQBeYrOS0xAxTojJHKHspCQ3GPSqaXCTuqpnf+lTQIJ5XRXwy0AaN7YxzWzT3I - /fSHp6VnS2LI8Yt13kj5ucAU17me4hYbvkHXJ5qvJfDMYDNlevqeaAJTAVJGBuHPFSWuoMN32iNW - UgjOelVo5vNUvg8HGKVollOIG4HNAGhb6dHewhrVy8gPK4qaFTZZRssT1GKzLWd7C5zDlS1a9rq5 - vU2uFAIznuaAK93po2GSIEjqefu1C8QZApc+uBxWnbQpeyCG1OB1cnjmi5sUuTlxgpTQFBAYCWEQ - bjrmmsHvDypH0qYqYGPlk56DPSnWFuz3BN2MCkB0niGK10bw/ExCyMxwhVskH8K5O98SPfWixqPm - AxkjBNEkkz2iQSzgqn3U54rPm4RkY4YEfhQBd0gPBMGnwc8fSpvElpFBIGU5Y4Ix0qjcanIkKBG5 - 7VGzPdIHvF3P9aAHpGtymc4Ira0fU5YYUG7KA5P0rAEgjOFjfHtVqzndD8ilFkGKAPTri4h1fRrW - DVAojmjwjdwPY/XNcJK6aTfubdjhDgc9a19PnbUYLW2upsRJ8o61S8WeH1sryKJ2AeRSUb1oApTX - TXpaQMWJGcdal8PSf6UTcj5WOKz5YW0zgTKZG44Bq4THLpSqj7LhWJdsdfSgDo9e16OGFba0ji3p - wZCBzXOoYZp2N2u0Mecd6Zp12cIbkfIBzTbwRG53W4wp5oAbeWVmgY2ZYeuTVC4SWFAzjdGO5qws - HmK28jaTVi1vhaR+XfRGeJhtVR69jz6dfwpgZEcrPcAp92pl2IzMxLuRwamfSJZCXtnRhnLgcFR6 - VWc7J9mNpbtikAW9w0MheQj5ea3NG1Y2sPmWhCvjuf5Vk7UadY48RseW960rDS11C3b7EMzL3oAt - 6hpn9pZu4GzGq7djH5g2PzpPDsMV/Y3Fveg/uVZl+vNJYRy2KhXfcB972q5aRw310/2eZLbcuCWH - X8qaA4yTeT845B4qaEqjZlVtzflV+80qY31z/Z8T3ENqMs8ZAAGcd6zoZMncEwH6H0pAdDpusLZQ - 7Rjc3ApkFoZJHmY4iAPXpms8R7oh/Gc5HtXQaALbUtGMN6ApPHrzQA/TvEdsdOWD92rRk8gcmud8 - QXkl1cZzlfapr3QP7NujGjfKTlSKzr2Jmdgx/wBX096AIkn8ucBQQjdat/bWMLZKOOnOOKzdjL0P - BoiXe2Cu7vQBpxC0KAyK2488Hiql3LskbaDtbpjrV+3tlubYC2TExGBVe+tJNOAF4PmHNAFO0meG - R1bI9jU0iK23zcbsdagWYO+xOH7mrkMWYcNgkUAQwKGA4JC5pzyFmPlEADt61asYIgSJWA3dOKv6 - zosFpdxPaBGVlG445BwKAMwuWADAbqs6eI/3hl++Pu1cj8NFyrRncAdxb0psElpY37NMhljD4YKe - poAsWmm/aIjKknlsvUnoalhtHLcbiueucA1Uu9UMs8wt4SsOfkUnkCrOmXcotj9rkV0HSLnmgDoD - 4JSXSzPNNFJhdwCkZX9a5+K9gD+XPgDdjNTpez6ZZywwPskcZbk/KK5qZ2llPmvvYnrQATr8zE5D - N1zxRbou7951anhZNYuUVFw7dvSp59IltXdZ1IZKAGvpLNGfLAfufaqDCSKUEkgdMkVd07VWs7oG - XLL0x60+7ePUjyCpByMUAV3bBGxsk1ZikV4gAMkHOKpzW5SUmN849qjjnlil3KODxj0oA6KykW7t - yJW8pk4BFdxrGhwax4TS5JWWaEBEY9QDn/CvNrPUfJmBcZDHLV0s2vsfDMwt2ZYy4z7cGgDHv9NK - yjfD+8bgYFUNRtTps4S6HlkjIBPU/wCcVeN86xKZmJlyMc5p/ifU5L/RYVmto9wJUyZ5oAy01Dfb - qZV2xnoKbfX6NEv2ZcHHWmPLFJYQx2ZLTL1U1EIJA+2bAJ6Y5oAIboyDb0PU1c8xLkBJLna4Hy44 - 5x06VAbZbdcyZ3elNBXeCRjnOaAG2808N5syYmJ7fx+5q7tW5QCZQso/iqsULT7rXLr6k4xVi0dX - +9kmgBlxpbI7SxqZAoGWz0p+i3txZ3AezJAHXjrWlZ26mFyzEnPC+vStzTLO3vZ1M8Yjwp6Hr0oA - 5/xFqyrIggQKrLlsdc96xpQZ5wySbu2DVnVYQ9/MJCSitxVOQFW4G1aAOm+H3iGPSbie1upBDBqC - CKRugwOfwrI8VWsenazNHZtvs0fEb/3h6j171Elg02N65x6Gt200i18VwwwXcjQ3Fou2NQMiTvye - 3WgDn4riKEhkfKf3h6+9aFlGLeyS8eT5DIMoDnv3FXZ9I0iwhJFxJLMpwY2ACg1TvvISzMs77S5w - EUcUAW9dH9qW6y6ZKBgcgdawoNOu7iWMmNiWOMDtT4Jxb5e1bKuMEHsfWpNM1ZrG4WWFmct0BHSg - CprWivp0u193mMeR6VHa2jmQbVH0zV3WNRkv5mkn5YnjFRJGBMjRMScdKANvR7OO1u4pS+SGGV68 - d61/GnhSHUYReQyqsZXiPI64rK0S5hRNzfePXvWr5w1KIwwucAccUAefW1q8kqiT+WK0RpdzFFuE - bFT0bHBqxrFj/Z87LjDZ/Km2ctw7Kgk3KO3SgDPQPuHmqNynv2rRs7hrhjDIcDqD6VPeafDfWbbC - UnUjav8AeHfn8qsaL4bl2pLcYWJT85PYdzQBq6dfjRtKX7QnmC4JQH07f1rIl0SztbsSrcoQnJQH - qaseJ7mBVT7PIXtDwrYwQ3esOO4RrxvLZmjI+90P5UAXrm881T9lHOeAOareXPH+8BKOB19Kb9rF - pcq0ILDPc8mp7m+S6k3fdKj7vWgB8Gtj7Oq3AZ3fCs7DmorqxQTbl+oAqJJlu4gJMKwIxT3kNq+H - G5/7o7D1zTA7Pwpd6NBrk5vQwMv3Pl+7UnjAwwXX7tFe3l5UjBbHvXP3GnCOxhuo2IL1G+qPcFYX - cknoT/n2pbgVZtGFxZvNbH5VOBk+vt+FZ8lrPakrcqyHGcEYzWidWS3lCxAlVPUdDWxf6pa6nLH/ - AGlH99QoI4wTwKbA45pHEirjk1asbxYZCsoDYH1rV17wyumSKVbeGG4Y6gVk/wBn7UdgCpPc0gLw - aEwtLKMDtWhoNykVwHdd8JGCjDIrDkSW1g2zOhVhkVLo+puSVlKlccYoA6Dxf4PbSLRb21wto7DG - W7ntj61mpKdXtxaOQvlfMCSBuJrqLfWIfEvhg2muKzQoN4CnBJHT9cVyU5hEjNbB0CHABPNAGTPa - fZriQONjqcZ6flUtqqB1SRmMr/dJzWlDaLrEUh1Qbnx+628ZNZE1s9nfctxEccjpQBO9tLcy7Zjw - vfNQ31q9oee3A75qe2Yyzby5OKiutRMsjKQDg4FG4EVvEyfM5xnsD1q5bbzKHBAB9KrCJN4YMd3p - V+wt8szRZUCnYDXsWSGPz7jGI+SMVVuvErXKEWuRk9QMYqXVyLXTUyRmRcmsSC4EAO8D2pAXxbma - IMR8w7+tVdRtkUAT9ew71as7wsF2nFGsKodDOMzHo/YU0rgULe7j098qW545Gaki1FIbwzeYyzfw - EdvyqkyGSfaw+bvRcQLayqyEnAyaQHR6gi6/pXnBER0IGFHzN15rnmlXyTGRuQHByeQau2GrS20G - 9OhO3H1//VWhf6RprXbXmnrMtuYsOjNk78DkfiDQBi2rpHIVQjb1otHPnBZAMAdRVUQiW6Bgyis2 - Buq29q2nXJjn/eDsycUAOLCG8yg9zkcVCzeVIZY+cenekN0LqYRSHAHA9aLMCOTy5BlTyPegCxa6 - ltkL2+ORzxjFWbTXpLSV3Y84+XFVJvLilKjgVFMpAyBxQBq6prEF7bQSzA+ZJ97jpVRGjDbUJAB+ - U+tUywlJUdE6VteHLK3kuoDqQZ0zyAcYFAG3feVo+io90u2d13R/LyR35rm77VZNSmzC5SEj5hnH - 14/Otu+hv/FN3gTWywW4KRqQM4/OsUeFZp5miaVAc9R0oAaXWa0EUWCIjuA9PeqEMbCYM3G77oAr - bi8Gz2YDmeLc3ygev61X1CxnnuTE8TvPb9fKXigDMuIJFlBdtzHnAPSrEF0IwDCm5hw2VNRzxTWt - 0BeKVMnTIxj8KZ/ahtgY49uT7UAX7VH1K63oERVOTxiuu0ex0nS7L7chJkm+R1kwwyPQZrh4JJDw - zbVbk4/OrNpefLsnyyg5UUAf/9k= -END:VCARD diff --git a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/bootstrap.php b/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/bootstrap.php deleted file mode 100644 index ee071ea759..0000000000 --- a/core/src/core/classes/sabredav/vendor/sabre/vobject/tests/bootstrap.php +++ /dev/null @@ -1,4 +0,0 @@ - - - Sabre/ - - - - - ../lib/ - - - diff --git a/core/src/core/classes/securimage/AHGBold.ttf b/core/src/core/classes/securimage/AHGBold.ttf deleted file mode 100644 index 764b23d76f..0000000000 Binary files a/core/src/core/classes/securimage/AHGBold.ttf and /dev/null differ diff --git a/core/src/core/classes/securimage/LICENSE.txt b/core/src/core/classes/securimage/LICENSE.txt deleted file mode 100644 index 9a749e6855..0000000000 --- a/core/src/core/classes/securimage/LICENSE.txt +++ /dev/null @@ -1,458 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -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 this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -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 -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "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 -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY 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 -LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS diff --git a/core/src/core/classes/securimage/README.FONT.txt b/core/src/core/classes/securimage/README.FONT.txt deleted file mode 100644 index d4770de5a0..0000000000 --- a/core/src/core/classes/securimage/README.FONT.txt +++ /dev/null @@ -1,12 +0,0 @@ -AHGBold.ttf is used by Securimage under the following license: - -Alte Haas Grotesk is a typeface that look like an helvetica printed in an old Muller-Brockmann Book. - -These fonts are freeware and can be distributed as long as they are -together with this text file. - -I would appreciate very much to see what you have done with it anyway. - -yann le coroller -www.yannlecoroller.com -yann@lecoroller.com \ No newline at end of file diff --git a/core/src/core/classes/securimage/README.txt b/core/src/core/classes/securimage/README.txt deleted file mode 100644 index b608018d49..0000000000 --- a/core/src/core/classes/securimage/README.txt +++ /dev/null @@ -1,88 +0,0 @@ -NAME: - - Securimage - A PHP class for creating and managing form CAPTCHA images - -VERSION: 2.0 BETA - -AUTHOR: - - Drew Phillips - -DOWNLOAD: - - The latest version can always be - found at http://www.phpcaptcha.org - -DOCUMENTATION: - - Online documentation of the class, methods, and variables can - be found at http://www.phpcaptcha.org/Securimage_Docs/ - -REQUIREMENTS: - PHP 4.3.0 - GD 2.0 - FreeType (recommended, required for TTF support) - -SYNOPSIS: - - require_once 'securimage.php'; - - $image = new Securimage(); - - $image->show(); - - // Code Validation - - $image = new Securimage(); - if ($image->check($_POST['code']) == true) { - echo "Correct!"; - } else { - echo "Sorry, wrong code."; - } - -DESCRIPTION: - - What is Securimage? - - Securimage is a PHP class that is used to generate and validate CAPTCHA images. - The classes uses an existing PHP session or creates its own if none is found to store the - CAPTCHA code. Variables within the class are used to control the style and display of the image. - The class supports TTF fonts and effects for strengthening the security of the image. - If TTF support is not available, GD fonts can be used as well, but certain options such as - transparent text and angled letters cannot be used. - - -COPYRIGHT: - Copyright (c) 2009 Drew Phillips. All rights reserved. - This software is released under the GNU Lesser General Public License. - - ----------------------------------------------------------------------------- - Flash code created for Securimage by Douglas Walsh (www.douglaswalsh.net) - Many thanks for releasing this to the project! - - ------------------------------------------------------------------------------ - Portions of Securimage contain code from Han-Kwang Nienhuys' PHP captcha - - Han-Kwang Nienhuys' PHP captcha - Copyright June 2007 - - This copyright message and attribution must be preserved upon - modification. Redistribution under other licenses is expressly allowed. - Other licenses include GPL 2 or higher, BSD, and non-free licenses. - The original, unrestricted version can be obtained from - http://www.lagom.nl/linux/hkcaptcha/ - - ------------------------------------------------------------------------------- - AHGBold.ttf (AlteHaasGroteskBold.ttf) font was created by Yann Le Coroller and is distributed as freeware - - Alte Haas Grotesk is a typeface that look like an helvetica printed in an old Muller-Brockmann Book. - - These fonts are freeware and can be distributed as long as they are - together with this text file. - - I would appreciate very much to see what you have done with it anyway. - - yann le coroller - www.yannlecoroller.com - yann@lecoroller.com - diff --git a/core/src/core/classes/securimage/gdfonts/automatic.gdf b/core/src/core/classes/securimage/gdfonts/automatic.gdf deleted file mode 100644 index 3eee7068f3..0000000000 Binary files a/core/src/core/classes/securimage/gdfonts/automatic.gdf and /dev/null differ diff --git a/core/src/core/classes/securimage/images/refresh.gif b/core/src/core/classes/securimage/images/refresh.gif deleted file mode 100644 index e3bb77668e..0000000000 Binary files a/core/src/core/classes/securimage/images/refresh.gif and /dev/null differ diff --git a/core/src/core/classes/securimage/securimage.php b/core/src/core/classes/securimage/securimage.php deleted file mode 100644 index e3bf0d5891..0000000000 --- a/core/src/core/classes/securimage/securimage.php +++ /dev/null @@ -1,1584 +0,0 @@ - - * File: securimage.php
    - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or any later version.

    - * - * This library 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 - * Lesser General Public License for more details.

    - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

    - * - * Any modifications to the library should be indicated clearly in the source code - * to inform users that the changes are not a part of the original software.

    - * - * If you found this script useful, please take a quick moment to rate it.
    - * http://www.hotscripts.com/rate/49400.html Thanks. - * - * @link http://www.phpcaptcha.org Securimage PHP CAPTCHA - * @link http://www.phpcaptcha.org/latest.zip Download Latest Version - * @link http://www.phpcaptcha.org/Securimage_Docs/ Online Documentation - * @copyright 2009 Drew Phillips - * @author Drew Phillips - * @version 2.0.1 BETA (December 6th, 2009) - * @package Securimage - * - */ - -/** - ChangeLog - - 2.0.1 - - Add support for browsers with cookies disabled (requires php5, sqlite) maps users to md5 hashed ip addresses and md5 hashed codes for security - - Add fallback to gd fonts if ttf support is not enabled or font file not found (Mike Challis http://www.642weather.com/weather/scripts.php) - - Check for previous definition of image type constants (Mike Challis) - - Fix mime type settings for audio output - - Fixed color allocation issues with multiple colors and background images, consolidate allocation to one function - - Ability to let codes expire after a given length of time - - Allow HTML color codes to be passed to Securimage_Color (suggested by Mike Challis) - - 2.0.0 - - Add mathematical distortion to characters (using code from HKCaptcha) - - Improved session support - - Added Securimage_Color class for easier color definitions - - Add distortion to audio output to prevent binary comparison attack (proposed by Sven "SavageTiger" Hagemann [insecurity.nl]) - - Flash button to stream mp3 audio (Douglas Walsh www.douglaswalsh.net) - - Audio output is mp3 format by default - - Change font to AlteHaasGrotesk by yann le coroller - - Some code cleanup - - 1.0.4 (unreleased) - - Ability to output audible codes in mp3 format to stream from flash - - 1.0.3.1 - - Error reading from wordlist in some cases caused words to be cut off 1 letter short - - 1.0.3 - - Removed shadow_text from code which could cause an undefined property error due to removal from previous version - - 1.0.2 - - Audible CAPTCHA Code wav files - - Create codes from a word list instead of random strings - - 1.0 - - Added the ability to use a selected character set, rather than a-z0-9 only. - - Added the multi-color text option to use different colors for each letter. - - Switched to automatic session handling instead of using files for code storage - - Added GD Font support if ttf support is not available. Can use internal GD fonts or load new ones. - - Added the ability to set line thickness - - Added option for drawing arced lines over letters - - Added ability to choose image type for output - - */ - -/** - * Output images in JPEG format - */ -if (!defined('SI_IMAGE_JPEG')) - define('SI_IMAGE_JPEG', 1); -/** - * Output images in PNG format - */ -if (!defined('SI_IMAGE_PNG')) - define('SI_IMAGE_PNG', 2); -/** - * Output images in GIF format (not recommended) - * Must have GD >= 2.0.28! - */ -if (!defined('SI_IMAGE_GIF')) - define('SI_IMAGE_GIF', 3); - -/** - * Securimage CAPTCHA Class. - * - * @package Securimage - * @subpackage classes - * - */ -class Securimage -{ - /** - * The desired width of the CAPTCHA image. - * - * @var int - */ - public $image_width; - - /** - * The desired width of the CAPTCHA image. - * - * @var int - */ - public $image_height; - - /** - * The image format for output.
    - * Valid options: SI_IMAGE_PNG, SI_IMAGE_JPG, SI_IMAGE_GIF - * - * @var int - */ - public $image_type; - - /** - * The length of the code to generate. - * - * @var int - */ - public $code_length; - - /** - * The character set for individual characters in the image.
    - * Letters are converted to uppercase.
    - * The font must support the letters or there may be problematic substitutions. - * - * @var string - */ - public $charset; - - /** - * Create codes using this word list - * - * @var string The path to the word list to use for creating CAPTCHA codes - */ - public $wordlist_file; - - /** - * Use wordlist of not - * - * @var bool true to use wordlist file, false to use random code - */ - public $use_wordlist = false; - - /** - * Note: Use of GD fonts is not recommended as many distortion features are not available
    - * The GD font to use.
    - * Internal gd fonts can be loaded by their number.
    - * Alternatively, a file path can be given and the font will be loaded from file. - * - * @var mixed - */ - public $gd_font_file; - - /** - * The approximate size of the font in pixels.
    - * This does not control the size of the font because that is determined by the GD font itself.
    - * This is used to aid the calculations of positioning used by this class.
    - * - * @var int - */ - public $gd_font_size; - - /** - * Use a gd font instead of TTF - * - * @var bool true for gd font, false for TTF - */ - public $use_gd_font; - - // Note: These font options below do not apply if you set $use_gd_font to true with the exception of $text_color - - /** - * The path to the TTF font file to load. - * - * @var string - */ - public $ttf_file; - - /** - * How much to distort image, higher = more distortion.
    - * Distortion is only available when using TTF fonts.
    - * - * @var float - */ - public $perturbation; - - /** - * The minimum angle in degrees, with 0 degrees being left-to-right reading text.
    - * Higher values represent a counter-clockwise rotation.
    - * For example, a value of 90 would result in bottom-to-top reading text.
    - * This value along with maximum angle distance do not need to be very high with perturbation - * - * @var int - */ - public $text_angle_minimum; - - /** - * The minimum angle in degrees, with 0 degrees being left-to-right reading text.
    - * Higher values represent a counter-clockwise rotation.
    - * For example, a value of 90 would result in bottom-to-top reading text. - * - * @var int - */ - public $text_angle_maximum; - - /** - * The X-Position on the image where letter drawing will begin.
    - * This value is in pixels from the left side of the image. - * - * @var int - * @deprecated 2.0 - */ - public $text_x_start; - - /** - * The background color for the image as a Securimage_Color.
    - * - * @var Securimage_Color - */ - public $image_bg_color; - - /** - * Scan this directory for gif, jpg, and png files to use as background images.
    - * A random image file will be picked each time.
    - * Change from null to the full path to your directory.
    - * i.e. var $background_directory = $_SERVER['DOCUMENT_ROOT'] . '/securimage/backgrounds'; - * Make sure not to pass a background image to the show function, otherwise this directive is ignored. - * - * @var string - */ - public $background_directory = null; //'./backgrounds'; - - /** - * The text color to use for drawing characters as a Securimage_Color.
    - * This value is ignored if $use_multi_text is set to true.
    - * Make sure this contrasts well with the background color or image.
    - * - * @see Securimage::$use_multi_text - * @var Securimage_Color - */ - public $text_color; - - /** - * Set to true to use multiple colors for each character. - * - * @see Securimage::$multi_text_color - * @var boolean - */ - public $use_multi_text; - - /** - * Array of Securimage_Colors which will be randomly selected for each letter.
    - * - * @var array - */ - public $multi_text_color; - - /** - * Set to true to make the characters appear transparent. - * - * @see Securimage::$text_transparency_percentage - * @var boolean - */ - public $use_transparent_text; - - /** - * The percentage of transparency, 0 to 100.
    - * A value of 0 is completely opaque, 100 is completely transparent (invisble) - * - * @see Securimage::$use_transparent_text - * @var int - */ - public $text_transparency_percentage; - - - // Line options - /** - * Draw vertical and horizontal lines on the image. - * - * @see Securimage::$line_color - * @see Securimage::$draw_lines_over_text - * @var boolean - */ - public $num_lines; - - /** - * Color of lines drawn over text - * - * @var string - */ - public $line_color; - - /** - * Draw the lines over the text.
    - * If fales lines will be drawn before putting the text on the image. - * - * @var boolean - */ - public $draw_lines_over_text; - - /** - * Text to write at the bottom corner of captcha image - * - * @since 2.0 - * @var string Signature text - */ - public $image_signature; - - /** - * Color to use for writing signature text - * - * @since 2.0 - * @var Securimage_Color - */ - public $signature_color; - - /** - * Full path to the WAV files to use to make the audio files, include trailing /.
    - * Name Files [A-Z0-9].wav - * - * @since 1.0.1 - * @var string - */ - public $audio_path; - - /** - * Type of audio file to generate (mp3 or wav) - * - * @var string - */ - public $audio_format; - - /** - * The session name to use if not the default. Blank for none - * - * @see http://php.net/session_name - * @since 2.0 - * @var string - */ - public $session_name = ''; - - /** - * The amount of time in seconds that a code remains valid.
    - * Any code older than this number will be considered invalid even if entered correctly.
    - * Any non-numeric or value less than 1 disables this functionality. - * - * @var int - */ - public $expiry_time; - - /** - * Path to the file to use for storing codes for users.
    - * THIS FILE MUST ABSOLUTELY NOT BE ACCESSIBLE FROM A WEB BROWSER!!
    - * Put this file in a directory below the web root or one that is restricted (i.e. an apache .htaccess file with deny from all)
    - * If you cannot meet those requirements your forms may not be completely protected.
    - * You could obscure the database file name but this is also not recommended. - * - * @var string - */ - public $sqlite_database; - - /** - * Use an SQLite database for storing codes as a backup to sessions.
    - * Note: Sessions will still be used - */ - public $use_sqlite_db; - - - //END USER CONFIGURATION - //There should be no need to edit below unless you really know what you are doing. - - /** - * The gd image resource. - * - * @access private - * @var resource - */ - public $im; - - /** - * Temporary image for rendering - * - * @access private - * @var resource - */ - public $tmpimg; - - /** - * Internal scale factor for anti-alias @hkcaptcha - * - * @access private - * @since 2.0 - * @var int - */ - public $iscale; // internal scale factor for anti-alias @hkcaptcha - - /** - * The background image resource - * - * @access private - * @var resource - */ - public $bgimg; - - /** - * The code generated by the script - * - * @access private - * @var string - */ - public $code; - - /** - * The code that was entered by the user - * - * @access private - * @var string - */ - public $code_entered; - - /** - * Whether or not the correct code was entered - * - * @access private - * @var boolean - */ - public $correct_code; - - /** - * Handle to SQLite database - * - * @access private - * @var resource - */ - public $sqlite_handle; - - /** - * Color resource for image line color - * - * @access private - * @var int - */ - public $gdlinecolor; - - /** - * Array of colors for multi colored codes - * - * @access private - * @var array - */ - public $gdmulticolor; - - /** - * Color resource for image font color - * - * @access private - * @var int - */ - public $gdtextcolor; - - /** - * Color resource for image signature color - * - * @access private - * @var int - */ - public $gdsignaturecolor; - - /** - * Color resource for image background color - * - * @access private - * @var int - */ - public $gdbgcolor; - - - /** - * Class constructor.
    - * Because the class uses sessions, this will attempt to start a session if there is no previous one.
    - * If you do not start a session before calling the class, the constructor must be called before any - * output is sent to the browser. - * - * - * $securimage = new Securimage(); - * - * - */ - public function __construct() - { - // Initialize session or attach to existing - if ( session_id() == '' ) { // no session has been started yet, which is needed for validation - if (trim($this->session_name) != '') { - session_name($this->session_name); // set session name if provided - } - session_start(); - } - - // Set Default Values - $this->image_width = 230; - $this->image_height = 80; - $this->image_type = SI_IMAGE_PNG; - - $this->code_length = 6; - $this->charset = 'ABCDEFGHKLMNPRSTUVWYZabcdefghklmnprstuvwyz23456789'; - $this->wordlist_file = './words/words.txt'; - $this->use_wordlist = false; - - $this->gd_font_file = 'gdfonts/automatic.gdf'; - $this->use_gd_font = false; - $this->gd_font_size = 24; - $this->text_x_start = 15; - - $this->ttf_file = './AHGBold.ttf'; - - $this->perturbation = 0.75; - $this->iscale = 5; - $this->text_angle_minimum = 0; - $this->text_angle_maximum = 0; - - $this->image_bg_color = new Securimage_Color(0xff, 0xff, 0xff); - $this->text_color = new Securimage_Color(0x3d, 0x3d, 0x3d); - $this->multi_text_color = array(new Securimage_Color(0x0, 0x20, 0xCC), - new Securimage_Color(0x0, 0x30, 0xEE), - new Securimage_color(0x0, 0x40, 0xCC), - new Securimage_Color(0x0, 0x50, 0xEE), - new Securimage_Color(0x0, 0x60, 0xCC)); - $this->use_multi_text = false; - - $this->use_transparent_text = false; - $this->text_transparency_percentage = 30; - - $this->num_lines = 10; - $this->line_color = new Securimage_Color(0x3d, 0x3d, 0x3d); - $this->draw_lines_over_text = true; - - $this->image_signature = ''; - $this->signature_color = new Securimage_Color(0x20, 0x50, 0xCC); - $this->signature_font = './AHGBold.ttf'; - - $this->audio_path = './audio/'; - $this->audio_format = 'mp3'; - $this->session_name = ''; - $this->expiry_time = 900; - - $this->sqlite_database = 'database/securimage.sqlite'; - $this->use_sqlite_db = false; - - $this->sqlite_handle = false; - } - - /** - * Generate a code and output the image to the browser. - * - * - * show('bg.jpg'); - * ?> - * - * - * @param string $background_image The path to an image to use as the background for the CAPTCHA - */ - public function show($background_image = "") - { - if ($background_image != "" && is_readable($background_image)) { - $this->bgimg = $background_image; - } - - $this->doImage(); - } - - /** - * Validate the code entered by the user. - * - * - * $code = $_POST['code']; - * if ($securimage->check($code) == false) { - * die("Sorry, the code entered did not match."); - * } else { - * $valid = true; - * } - * - * @param string $code The code the user entered - * @return boolean true if the code was correct, false if not - */ - public function check($code) - { - $this->code_entered = $code; - $this->validate(); - return $this->correct_code; - } - - /** - * Output audio file with HTTP headers to browser - * - * - * $sound = new Securimage(); - * $sound->audio_format = 'mp3'; - * $sound->outputAudioFile(); - * - * - * @since 2.0 - */ - public function outputAudioFile() - { - if (strtolower($this->audio_format) == 'wav') { - header('Content-type: audio/x-wav'); - $ext = 'wav'; - } else { - header('Content-type: audio/mpeg'); // default to mp3 - $ext = 'mp3'; - } - - header("Content-Disposition: attachment; filename=\"securimage_audio.{$ext}\""); - header('Cache-Control: no-store, no-cache, must-revalidate'); - header('Expires: Sun, 1 Jan 2000 12:00:00 GMT'); - header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT'); - - $audio = $this->getAudibleCode($ext); - - header('Content-Length: ' . strlen($audio)); - - echo $audio; - exit; - } - - /** - * Generate and output the image - * - * @access private - * - */ - public function doImage() - { - if ($this->use_gd_font == true) { - $this->iscale = 1; - } - if ($this->use_transparent_text == true || $this->bgimg != "") { - $this->im = imagecreatetruecolor($this->image_width, $this->image_height); - $this->tmpimg = imagecreatetruecolor($this->image_width * $this->iscale, $this->image_height * $this->iscale); - - } else { //no transparency - $this->im = imagecreate($this->image_width, $this->image_height); - $this->tmpimg = imagecreate($this->image_width * $this->iscale, $this->image_height * $this->iscale); - } - - $this->allocateColors(); - imagepalettecopy($this->tmpimg, $this->im); - - $this->setBackground(); - - $this->createCode(); - - if (!$this->draw_lines_over_text && $this->num_lines > 0) $this->drawLines(); - - $this->drawWord(); - if ($this->use_gd_font == false && is_readable($this->ttf_file)) $this->distortedCopy(); - - if ($this->draw_lines_over_text && $this->num_lines > 0) $this->drawLines(); - - if (trim($this->image_signature) != '') $this->addSignature(); - - $this->output(); - - } - - /** - * Allocate all colors that will be used in the CAPTCHA image - * - * @since 2.0.1 - * @access private - */ - public function allocateColors() - { - // allocate bg color first for imagecreate - $this->gdbgcolor = imagecolorallocate($this->im, $this->image_bg_color->r, $this->image_bg_color->g, $this->image_bg_color->b); - - $alpha = intval($this->text_transparency_percentage / 100 * 127); - - if ($this->use_transparent_text == true) { - $this->gdtextcolor = imagecolorallocatealpha($this->im, $this->text_color->r, $this->text_color->g, $this->text_color->b, $alpha); - $this->gdlinecolor = imagecolorallocatealpha($this->im, $this->line_color->r, $this->line_color->g, $this->line_color->b, $alpha); - } else { - $this->gdtextcolor = imagecolorallocate($this->im, $this->text_color->r, $this->text_color->g, $this->text_color->b); - $this->gdlinecolor = imagecolorallocate($this->im, $this->line_color->r, $this->line_color->g, $this->line_color->b); - } - - $this->gdsignaturecolor = imagecolorallocate($this->im, $this->signature_color->r, $this->signature_color->g, $this->signature_color->b); - - if ($this->use_multi_text == true) { - $this->gdmulticolor = array(); - - foreach ($this->multi_text_color as $color) { - if ($this->use_transparent_text == true) { - $this->gdmulticolor[] = imagecolorallocatealpha($this->im, $color->r, $color->g, $color->b, $alpha); - } else { - $this->gdmulticolor[] = imagecolorallocate($this->im, $color->r, $color->g, $color->b); - } - } - } - } - - /** - * Set the background of the CAPTCHA image - * - * @access private - * - */ - public function setBackground() - { - imagefilledrectangle($this->im, 0, 0, $this->image_width * $this->iscale, $this->image_height * $this->iscale, $this->gdbgcolor); - imagefilledrectangle($this->tmpimg, 0, 0, $this->image_width * $this->iscale, $this->image_height * $this->iscale, $this->gdbgcolor); - - if ($this->bgimg == '') { - if ($this->background_directory != null && is_dir($this->background_directory) && is_readable($this->background_directory)) { - $img = $this->getBackgroundFromDirectory(); - if ($img != false) { - $this->bgimg = $img; - } - } - } - - $dat = @getimagesize($this->bgimg); - if ($dat == false) { - return; - } - - switch ($dat[2]) { - case 1: $newim = @imagecreatefromgif($this->bgimg); break; - case 2: $newim = @imagecreatefromjpeg($this->bgimg); break; - case 3: $newim = @imagecreatefrompng($this->bgimg); break; - case 15: $newim = @imagecreatefromwbmp($this->bgimg); break; - case 16: $newim = @imagecreatefromxbm($this->bgimg); break; - default: return; - } - - if(!$newim) return; - - imagecopyresized($this->im, $newim, 0, 0, 0, 0, $this->image_width, $this->image_height, imagesx($newim), imagesy($newim)); - } - - /** - * Return the full path to a random gif, jpg, or png from the background directory. - * - * @access private - * @see Securimage::$background_directory - * @return mixed false if none found, string $path if found - */ - public function getBackgroundFromDirectory() - { - $images = array(); - - if ($dh = opendir($this->background_directory)) { - while (($file = readdir($dh)) !== false) { - if (preg_match('/(jpg|gif|png)$/i', $file)) $images[] = $file; - } - - closedir($dh); - - if (sizeof($images) > 0) { - return rtrim($this->background_directory, '/') . '/' . $images[rand(0, sizeof($images)-1)]; - } - } - - return false; - } - - /** - * Draw random curvy lines over the image
    - * Modified code from HKCaptcha - * - * @since 2.0 - * @access private - * - */ - public function drawLines() - { - for ($line = 0; $line < $this->num_lines; ++$line) { - $x = $this->image_width * (1 + $line) / ($this->num_lines + 1); - $x += (0.5 - $this->frand()) * $this->image_width / $this->num_lines; - $y = rand($this->image_height * 0.1, $this->image_height * 0.9); - - $theta = ($this->frand()-0.5) * M_PI * 0.7; - $w = $this->image_width; - $len = rand($w * 0.4, $w * 0.7); - $lwid = rand(0, 2); - - $k = $this->frand() * 0.6 + 0.2; - $k = $k * $k * 0.5; - $phi = $this->frand() * 6.28; - $step = 0.5; - $dx = $step * cos($theta); - $dy = $step * sin($theta); - $n = $len / $step; - $amp = 1.5 * $this->frand() / ($k + 5.0 / $len); - $x0 = $x - 0.5 * $len * cos($theta); - $y0 = $y - 0.5 * $len * sin($theta); - - $ldx = round(-$dy * $lwid); - $ldy = round($dx * $lwid); - - for ($i = 0; $i < $n; ++$i) { - $x = $x0 + $i * $dx + $amp * $dy * sin($k * $i * $step + $phi); - $y = $y0 + $i * $dy - $amp * $dx * sin($k * $i * $step + $phi); - imagefilledrectangle($this->im, $x, $y, $x + $lwid, $y + $lwid, $this->gdlinecolor); - } - } - } - - /** - * Draw the CAPTCHA code over the image - * - * @access private - * - */ - public function drawWord() - { - $width2 = $this->image_width * $this->iscale; - $height2 = $this->image_height * $this->iscale; - - if ($this->use_gd_font == true || !is_readable($this->ttf_file)) { - if (!is_int($this->gd_font_file)) { //is a file name - $font = @imageloadfont($this->gd_font_file); - if ($font == false) { - trigger_error("Failed to load GD Font file {$this->gd_font_file} ", E_USER_WARNING); - return; - } - } else { //gd font identifier - $font = $this->gd_font_file; - } - - imagestring($this->im, $font, $this->text_x_start, ($this->image_height / 2) - ($this->gd_font_size / 2), $this->code, $this->gdtextcolor); - } else { //ttf font - $font_size = $height2 * .35; - $bb = imagettfbbox($font_size, 0, $this->ttf_file, $this->code); - $tx = $bb[4] - $bb[0]; - $ty = $bb[5] - $bb[1]; - $x = floor($width2 / 2 - $tx / 2 - $bb[0]); - $y = round($height2 / 2 - $ty / 2 - $bb[1]); - - $strlen = strlen($this->code); - if (!is_array($this->multi_text_color)) $this->use_multi_text = false; - - - if ($this->use_multi_text == false && $this->text_angle_minimum == 0 && $this->text_angle_maximum == 0) { // no angled or multi-color characters - imagettftext($this->tmpimg, $font_size, 0, $x, $y, $this->gdtextcolor, $this->ttf_file, $this->code); - } else { - for ($i = 0; $i < $strlen; ++$i) { - $angle = rand($this->text_angle_minimum, $this->text_angle_maximum); - $y = rand($y - 5, $y + 5); - if ($this->use_multi_text == true) { - $font_color = $this->gdmulticolor[rand(0, sizeof($this->gdmulticolor) - 1)]; - } else { - $font_color = $this->gdtextcolor; - } - - $ch = $this->code{$i}; - - imagettftext($this->tmpimg, $font_size, $angle, $x, $y, $font_color, $this->ttf_file, $ch); - - // estimate character widths to increment $x without creating spaces that are too large or too small - // these are best estimates to align text but may vary between fonts - // for optimal character widths, do not use multiple text colors or character angles and the complete string will be written by imagettftext - if (strpos('abcdeghknopqsuvxyz', $ch) !== false) { - $min_x = $font_size - ($this->iscale * 6); - $max_x = $font_size - ($this->iscale * 6); - } else if (strpos('ilI1', $ch) !== false) { - $min_x = $font_size / 5; - $max_x = $font_size / 3; - } else if (strpos('fjrt', $ch) !== false) { - $min_x = $font_size - ($this->iscale * 12); - $max_x = $font_size - ($this->iscale * 12); - } else if ($ch == 'wm') { - $min_x = $font_size; - $max_x = $font_size + ($this->iscale * 3); - } else { // numbers, capitals or unicode - $min_x = $font_size + ($this->iscale * 2); - $max_x = $font_size + ($this->iscale * 5); - } - - $x += rand($min_x, $max_x); - } //for loop - } // angled or multi-color - } //else ttf font - //$this->im = $this->tmpimg; - //$this->output(); - } //function - - /** - * Warp text from temporary image onto final image.
    - * Modified for securimage - * - * @access private - * @since 2.0 - * @author Han-Kwang Nienhuys modified - * @copyright Han-Kwang Neinhuys - * - */ - public function distortedCopy() - { - $numpoles = 3; // distortion factor - - // make array of poles AKA attractor points - for ($i = 0; $i < $numpoles; ++$i) { - $px[$i] = rand($this->image_width * 0.3, $this->image_width * 0.7); - $py[$i] = rand($this->image_height * 0.3, $this->image_height * 0.7); - $rad[$i] = rand($this->image_width * 0.4, $this->image_width * 0.7); - $tmp = -$this->frand() * 0.15 - 0.15; - $amp[$i] = $this->perturbation * $tmp; - } - - $bgCol = imagecolorat($this->tmpimg, 0, 0); - $width2 = $this->iscale * $this->image_width; - $height2 = $this->iscale * $this->image_height; - - imagepalettecopy($this->im, $this->tmpimg); // copy palette to final image so text colors come across - - // loop over $img pixels, take pixels from $tmpimg with distortion field - for ($ix = 0; $ix < $this->image_width; ++$ix) { - for ($iy = 0; $iy < $this->image_height; ++$iy) { - $x = $ix; - $y = $iy; - - for ($i = 0; $i < $numpoles; ++$i) { - $dx = $ix - $px[$i]; - $dy = $iy - $py[$i]; - if ($dx == 0 && $dy == 0) continue; - - $r = sqrt($dx * $dx + $dy * $dy); - if ($r > $rad[$i]) continue; - - $rscale = $amp[$i] * sin(3.14 * $r / $rad[$i]); - $x += $dx * $rscale; - $y += $dy * $rscale; - } - - $c = $bgCol; - $x *= $this->iscale; - $y *= $this->iscale; - - if ($x >= 0 && $x < $width2 && $y >= 0 && $y < $height2) { - $c = imagecolorat($this->tmpimg, $x, $y); - } - - if ($c != $bgCol) { // only copy pixels of letters to preserve any background image - imagesetpixel($this->im, $ix, $iy, $c); - } - } - } - } - - /** - * Create a code and save to the session - * - * @access private - * @since 1.0.1 - * - */ - public function createCode() - { - $this->code = false; - - if ($this->use_wordlist && is_readable($this->wordlist_file)) { - $this->code = $this->readCodeFromFile(); - } - - if ($this->code == false) { - $this->code = $this->generateCode($this->code_length); - } - - $this->saveData(); - } - - /** - * Generate a code - * - * @access private - * @param int $len The code length - * @return string - */ - public function generateCode($len) - { - $code = ''; - - for ($i = 1, $cslen = strlen($this->charset); $i <= $len; ++$i) { - $code .= $this->charset{rand(0, $cslen - 1)}; - } - return $code; - } - - /** - * Reads a word list file to get a code - * - * @access private - * @since 1.0.2 - * @return mixed false on failure, a word on success - */ - public function readCodeFromFile() - { - $fp = @fopen($this->wordlist_file, 'rb'); - if (!$fp) return false; - - $fsize = filesize($this->wordlist_file); - if ($fsize < 32) return false; // too small of a list to be effective - - if ($fsize < 128) { - $max = $fsize; // still pretty small but changes the range of seeking - } else { - $max = 128; - } - - fseek($fp, rand(0, $fsize - $max), SEEK_SET); - $data = fread($fp, 128); // read a random 128 bytes from file - fclose($fp); - $data = preg_replace("/\r?\n/", "\n", $data); - - $start = strpos($data, "\n", rand(0, 100)) + 1; // random start position - $end = strpos($data, "\n", $start); // find end of word - - return strtolower(substr($data, $start, $end - $start)); // return substring in 128 bytes - } - - /** - * Output image to the browser - * - * @access private - * - */ - public function output() - { - header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); - header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT"); - header("Cache-Control: no-store, no-cache, must-revalidate"); - header("Cache-Control: post-check=0, pre-check=0", false); - header("Pragma: no-cache"); - - switch ($this->image_type) { - case SI_IMAGE_JPEG: - header("Content-Type: image/jpeg"); - imagejpeg($this->im, null, 90); - break; - - case SI_IMAGE_GIF: - header("Content-Type: image/gif"); - imagegif($this->im); - break; - - default: - header("Content-Type: image/png"); - imagepng($this->im); - break; - } - - imagedestroy($this->im); - exit; - } - - /** - * Get WAV or MP3 file data of the spoken code.
    - * This is appropriate for output to the browser as audio/x-wav or audio/mpeg - * - * @since 1.0.1 - * @return string WAV or MP3 data - * - */ - public function getAudibleCode($format = 'wav') - { - $letters = array(); - $code = $this->getCode(); - - if ($code == '') { - $this->createCode(); - $code = $this->getCode(); - } - - for ($i = 0; $i < strlen($code); ++$i) { - $letters[] = $code{$i}; - } - - if ($format == 'mp3') { - return $this->generateMP3($letters); - } else { - return $this->generateWAV($letters); - } - } - - /** - * Set the path to the audio directory.
    - * - * @since 1.0.4 - * @return bool true if the directory exists and is readble, false if not - */ - public function setAudioPath($audio_directory) - { - if (is_dir($audio_directory) && is_readable($audio_directory)) { - $this->audio_path = $audio_directory; - return true; - } else { - return false; - } - } - - /** - * Save the code in the session - * - * @access private - * - */ - public function saveData() - { - $_SESSION['securimage_code_value'] = strtolower($this->code); - $_SESSION['securimage_code_ctime'] = time(); - - $this->saveCodeToDatabase(); - } - - /** - * Validate the code to the user code - * - * @access private - * - */ - public function validate() - { - // retrieve code from session, if no code exists check sqlite database if supported. - - if (isset($_SESSION['securimage_code_value']) && trim($_SESSION['securimage_code_value']) != '') { - if ($this->isCodeExpired($_SESSION['securimage_code_ctime']) == false) { - $code = $_SESSION['securimage_code_value']; - } - } else if ($this->use_sqlite_db == true && function_exists('sqlite_open')) { // no code in session - may mean user has cookies turned off - $this->openDatabase(); - $code = $this->getCodeFromDatabase(); - } else { - // session code invalid or non-existant and code not found in sqlite db or sqlite is not available - $code = ''; - } - - $code = trim(strtolower($code)); - $code_entered = trim(strtolower($this->code_entered)); - $this->correct_code = false; - - if ($code != '') { - if ($code == $code_entered) { - $this->correct_code = true; - $_SESSION['securimage_code_value'] = ''; - $_SESSION['securimage_code_ctime'] = ''; - $this->clearCodeFromDatabase(); - } - } - } - - /** - * Get the captcha code - * - * @since 1.0.1 - * @return string - */ - public function getCode() - { - if (isset($_SESSION['securimage_code_value']) && !empty($_SESSION['securimage_code_value'])) { - return strtolower($_SESSION['securimage_code_value']); - } else { - if ($this->sqlite_handle == false) $this->openDatabase(); - - return $this->getCodeFromDatabase(); // attempt to get from database, returns empty string if sqlite is not available or disabled - } - } - - /** - * Check if the user entered code was correct - * - * @access private - * @return boolean - */ - public function checkCode() - { - return $this->correct_code; - } - - /** - * Generate a wav file by concatenating individual files - * - * @since 1.0.1 - * @access private - * @param array $letters Array of letters to build a file from - * @return string WAV file data - */ - public function generateWAV($letters) - { - $data_len = 0; - $files = array(); - $out_data = ''; - - foreach ($letters as $letter) { - $filename = $this->audio_path . strtoupper($letter) . '.wav'; - - $fp = fopen($filename, 'rb'); - - $file = array(); - - $data = fread($fp, filesize($filename)); // read file in - - $header = substr($data, 0, 36); - $body = substr($data, 44); - - - $data = unpack('NChunkID/VChunkSize/NFormat/NSubChunk1ID/VSubChunk1Size/vAudioFormat/vNumChannels/VSampleRate/VByteRate/vBlockAlign/vBitsPerSample', $header); - - $file['sub_chunk1_id'] = $data['SubChunk1ID']; - $file['bits_per_sample'] = $data['BitsPerSample']; - $file['channels'] = $data['NumChannels']; - $file['format'] = $data['AudioFormat']; - $file['sample_rate'] = $data['SampleRate']; - $file['size'] = $data['ChunkSize'] + 8; - $file['data'] = $body; - - if ( ($p = strpos($file['data'], 'LIST')) !== false) { - // If the LIST data is not at the end of the file, this will probably break your sound file - $info = substr($file['data'], $p + 4, 8); - $data = unpack('Vlength/Vjunk', $info); - $file['data'] = substr($file['data'], 0, $p); - $file['size'] = $file['size'] - (strlen($file['data']) - $p); - } - - $files[] = $file; - $data = null; - $header = null; - $body = null; - - $data_len += strlen($file['data']); - - fclose($fp); - } - - $out_data = ''; - for ($i = 0; $i < sizeof($files); ++$i) { - if ($i == 0) { // output header - $out_data .= pack('C4VC8', ord('R'), ord('I'), ord('F'), ord('F'), $data_len + 36, ord('W'), ord('A'), ord('V'), ord('E'), ord('f'), ord('m'), ord('t'), ord(' ')); - - $out_data .= pack('VvvVVvv', - 16, - $files[$i]['format'], - $files[$i]['channels'], - $files[$i]['sample_rate'], - $files[$i]['sample_rate'] * (($files[$i]['bits_per_sample'] * $files[$i]['channels']) / 8), - ($files[$i]['bits_per_sample'] * $files[$i]['channels']) / 8, - $files[$i]['bits_per_sample'] ); - - $out_data .= pack('C4', ord('d'), ord('a'), ord('t'), ord('a')); - - $out_data .= pack('V', $data_len); - } - - $out_data .= $files[$i]['data']; - } - - $this->scrambleAudioData($out_data, 'wav'); - return $out_data; - } - - /** - * Randomly modify the audio data to scramble sound and prevent binary recognition.
    - * Take care not to "break" the audio file by leaving the header data intact. - * - * @since 2.0 - * @access private - * @param $data Sound data in mp3 of wav format - */ - public function scrambleAudioData(&$data, $format) - { - if ($format == 'wav') { - $start = strpos($data, 'data') + 4; // look for "data" indicator - if ($start === false) $start = 44; // if not found assume 44 byte header - } else { // mp3 - $start = 4; // 4 byte (32 bit) frame header - } - - $start += rand(1, 64); // randomize starting offset - $datalen = strlen($data) - $start - 256; // leave last 256 bytes unchanged - - for ($i = $start; $i < $datalen; $i += 64) { - $ch = ord($data{$i}); - if ($ch < 9 || $ch > 119) continue; - - $data{$i} = chr($ch + rand(-8, 8)); - } - } - - /** - * Generate an mp3 file by concatenating individual files - * @since 1.0.4 - * @access private - * @param array $letters Array of letters to build a file from - * @return string MP3 file data - */ - public function generateMP3($letters) - { - $data_len = 0; - $files = array(); - $out_data = ''; - - foreach ($letters as $letter) { - $filename = $this->audio_path . strtoupper($letter) . '.mp3'; - - $fp = fopen($filename, 'rb'); - $data = fread($fp, filesize($filename)); // read file in - - $this->scrambleAudioData($data, 'mp3'); - $out_data .= $data; - - fclose($fp); - } - - - return $out_data; - } - - /** - * Generate random number less than 1 - * @since 2.0 - * @access private - * @return float - */ - public function frand() - { - return 0.0001*rand(0,9999); - } - - /** - * Print signature text on image - * - * @since 2.0 - * @access private - * - */ - public function addSignature() - { - if ($this->use_gd_font) { - imagestring($this->im, 5, $this->image_width - (strlen($this->image_signature) * 10), $this->image_height - 20, $this->image_signature, $this->gdsignaturecolor); - } else { - - $bbox = imagettfbbox(10, 0, $this->signature_font, $this->image_signature); - $textlen = $bbox[2] - $bbox[0]; - $x = $this->image_width - $textlen - 5; - $y = $this->image_height - 3; - - imagettftext($this->im, 10, 0, $x, $y, $this->gdsignaturecolor, $this->signature_font, $this->image_signature); - } - } - - /** - * Get hashed IP address of remote user - * - * @access private - * @since 2.0.1 - * @return string - */ - public function getIPHash() - { - return strtolower(md5($_SERVER['REMOTE_ADDR'])); - } - - /** - * Open SQLite database - * - * @access private - * @since 2.0.1 - * @return bool true if database was opened successfully - */ - public function openDatabase() - { - $this->sqlite_handle = false; - - if ($this->use_sqlite_db && function_exists('sqlite_open')) { - $this->sqlite_handle = sqlite_open($this->sqlite_database, 0666); - - if ($this->sqlite_handle !== false) { - $res = sqlite_query($this->sqlite_handle, "PRAGMA table_info(codes)"); - if (sqlite_num_rows($res) == 0) { - sqlite_query($this->sqlite_handle, "CREATE TABLE codes (iphash VARCHAR(32) PRIMARY KEY, code VARCHAR(32) NOT NULL, created INTEGER)"); - } - } - - return $this->sqlite_handle != false; - } - - return $this->sqlite_handle; - } - - /** - * Save captcha code to sqlite database - * - * @access private - * @since 2.0.1 - * @return bool true if code was saved, false if not - */ - public function saveCodeToDatabase() - { - $success = false; - - $this->openDatabase(); - - if ($this->use_sqlite_db && $this->sqlite_handle !== false) { - $ip = $this->getIPHash(); - $time = time(); - $code = $_SESSION['securimage_code_value']; // hash code for security - if cookies are disabled the session still exists at this point - $success = sqlite_query($this->sqlite_handle, "INSERT OR REPLACE INTO codes(iphash, code, created) VALUES('$ip', '$code', $time)"); - } - - return $success !== false; - } - - /** - * Get stored captcha code from sqlite database based on ip address hash - * - * @access private - * @since 2.0.1 - * @return string captcha code - */ - public function getCodeFromDatabase() - { - $code = ''; - - if ($this->use_sqlite_db && $this->sqlite_handle !== false) { - $ip = $this->getIPHash(); - - $res = sqlite_query($this->sqlite_handle, "SELECT * FROM codes WHERE iphash = '$ip'"); - if ($res && sqlite_num_rows($res) > 0) { - $res = sqlite_fetch_array($res); - - if ($this->isCodeExpired($res['created']) == false) { - $code = $res['code']; - } - } - } - - return $code; - } - - /** - * Delete a code from the database by ip address hash - * - * @access private - * @since 2.0.1 - */ - public function clearCodeFromDatabase() - { - if ($this->sqlite_handle !== false) { - $ip = $this->getIPHash(); - - sqlite_query($this->sqlite_handle, "DELETE FROM codes WHERE iphash = '$ip'"); - } - } - - /** - * Purge codes over a day old from database - * - * @access private - * @since 2.0.1 - */ - public function purgeOldCodesFromDatabase() - { - if ($this->use_sqlite_db && $this->sqlite_handle !== false) { - $now = time(); - $limit = (!is_numeric($this->expiry_time) || $this->expiry_time < 1) ? 86400 : $this->expiry_time; - - sqlite_query($this->sqlite_handle, "DELETE FROM codes WHERE $now - created > $limit"); - } - } - - /** - * Check a code to see if it is expired based on creation time - * - * @access private - * @since 2.0.1 - * @param $creation_time unix timestamp of code creation time - * @return bool true if code has expired, false if not - */ - public function isCodeExpired($creation_time) - { - $expired = true; - - if (!is_numeric($this->expiry_time) || $this->expiry_time < 1) { - $expired = false; - } else if (time() - $creation_time < $this->expiry_time) { - $expired = false; - } - - return $expired; - } - -} /* class Securimage */ - - -/** - * Color object for Securimage CAPTCHA - * - * @since 2.0 - * @package Securimage - * @subpackage classes - * - */ -class Securimage_Color -{ - /** - * Red component: 0-255 - * - * @var int - */ - public $r; - /** - * Green component: 0-255 - * - * @var int - */ - public $g; - /** - * Blue component: 0-255 - * - * @var int - */ - public $b; - - /** - * Create a new Securimage_Color object.
    - * Specify the red, green, and blue components using their HTML hex code equivalent.
    - * Example: The code for the HTML color #4A203C is:
    - * $color = new Securimage_Color(0x4A, 0x20, 0x3C); - * - * @param $red int|String Red component 0-255 - * @param $green int Green component 0-255 - * @param $blue int Blue component 0-255 - */ - public function __construct($red, $green = null, $blue = null) - { - if ($green == null && $blue == null && preg_match('/^#[a-f0-9]{3,6}$/i', $red)) { - $col = substr($red, 1); - if (strlen($col) == 3) { - $red = str_repeat(substr($col, 0, 1), 2); - $green = str_repeat(substr($col, 1, 1), 2); - $blue = str_repeat(substr($col, 2, 1), 2); - } else { - $red = substr($col, 0, 2); - $green = substr($col, 2, 2); - $blue = substr($col, 4, 2); - } - - $red = hexdec($red); - $green = hexdec($green); - $blue = hexdec($blue); - } else { - if ($red < 0) $red = 0; - if ($red > 255) $red = 255; - if ($green < 0) $green = 0; - if ($green > 255) $green = 255; - if ($blue < 0) $blue = 0; - if ($blue > 255) $blue = 255; - } - - $this->r = $red; - $this->g = $green; - $this->b = $blue; - } -} diff --git a/core/src/core/compat.php b/core/src/core/compat.php new file mode 100644 index 0000000000..290429d671 --- /dev/null +++ b/core/src/core/compat.php @@ -0,0 +1,30 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ + +defined('AJXP_EXEC') or die('Access not allowed'); + +class_alias("Pydio\\Access\\Core\\Filter\\AJXP_Permission", "AJXP_Permission", true); +class_alias("Pydio\\Access\\Core\\Filter\\AJXP_PermissionMask", "AJXP_PermissionMask", true); +class_alias("Pydio\\Access\\Core\\Model\\AJXP_Node", "AJXP_Node", true); +class_alias("Pydio\\Conf\\Core\\AJXP_Role", "AJXP_Role", true); +class_alias("Pydio\\Access\\Core\\Model\\Repository", "Repository", true); +class_alias("Pydio\\Access\\Core\\Filter\\ContentFilter", "ContentFilter", true); +class_alias("Pydio\\Notification\\Core\\Notification", "AJXP_Notification", true); \ No newline at end of file diff --git a/core/src/core/composer.json b/core/src/core/composer.json new file mode 100644 index 0000000000..02b5015e95 --- /dev/null +++ b/core/src/core/composer.json @@ -0,0 +1,26 @@ +{ + "name" : "pydio/core", + "autoload": { + "psr-4": { + "Pydio\\Core\\": "./src/pydio/Core", + "Pydio\\Tests\\": "./src/pydio/Tests" + } + }, + "require": { + "zendframework/zend-diactoros": "~1.0", + "guzzlehttp/guzzle": "~5", + "guzzlehttp/command": "~0.7", + "guzzlehttp/guzzle-services": "~0.5", + "gimler/guzzle-description-loader" : "*", + "commerceguys/guzzle-oauth2-plugin": "~2.0", + "nikic/fast-route":"~1.0", + "symfony/console":"^3.0", + "davegardnerisme/nsqphp": "dev-master", + "sabre/dav":"1.8.10", + "aws/aws-sdk-php": "^3.19.4", + "meenie/javascript-packer":"1.1", + "dapphp/securimage":"3.6.4", + "phpseclib/phpseclib":"2.0.3" + } + +} \ No newline at end of file diff --git a/core/src/core/doc/CREDITS-ONLY.txt b/core/src/core/doc/CREDITS-ONLY.txt new file mode 100644 index 0000000000..89fb292ca1 --- /dev/null +++ b/core/src/core/doc/CREDITS-ONLY.txt @@ -0,0 +1,57 @@ +

    Pydio Core License

    +Pydio Core 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. +Pydio Core 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 Pydio. If not, see . + +

    Contributors

    +Cyril Russo : public links generation +Panni (support for folder upload using jumploader, support for contextmenu in Opera) +German : panzaeron +Catalon : Joan +Other translators : see mention in translation files +Shorten.multi plugin by Miguelon +Ultreia many fixes +Thomas Nicot (UX or Orbit Theme): http://resetandplay.com/ +Others bugfixes and translations: +Tran The Cuong, Nicolas Pouliquen, Pär Strindevall, Martin Schaible, Lawrence Ho, +Arnold van Blanken, Marco De Pardi, Anael Mobilia, Dmitri Bosenko, Florian Vogt, +Gerrit Pannek, Max Ruman, Stefan Huber, Christian Foellmann, John Regan, Sylvain Mandon, +Gerald Me, Matthieu Simon, Florian Huwyler, Aaron Guggisberg, Stefan Wüthrich, Teki Imai, +Nicola Mustone, Mike Rhuner, Mike Smorul, Pablo Daniel Rey, Steve Ludovicy, Lukasz Lis. +Garnetius + +

    Third Party Library

    +
    Amazon S3
    + S3 Stream Wrapper is released under the BSD license, Copyright 2011 P'unk Avenue LLC http://punkave.com/ + +
    JavaScript
    + Prototype, Scriptaculous http://www.prototypejs.org/, http://script.aculo.us/ (MIT) + Modernizr http://modernizr.com (BSD / MIT) + Webfx widgets (selectable, sortable, xtree) : http://webfx.eae.net/ (Apache 2) + Chosen selector by Patrick Filler for Harvest (MIT): https://github.com/harvesthq/chosen + PDFJS library is provided by the Mozilla Foundation (Apache 2): http://mozilla.github.io/pdf.js/ + +
    Images and Fonts
    + Oxygen : http://www.oxygen-icons.org/ (Creative Common SA 3.0 / LGPL) + Font Awesome by Dave Gandy : http://fortawesome.github.com/Font-Awesome/ (SIL Open Font License) + Open Sans by Steve Matteson : http://www.opensans.com/ (Apache Licence v2) + Background Photos : from http://unsplash.com/, + Embedded photos credits: http://www.gratisography.com/, Nick West, Martin Staněk, Sonja Langford + Folders & Files icons in Orbit Theme, by Ionic : http://ionicons.com/ (MIT License) + +
    Core PHP Classes
    + Dibi ORM: Copyright (c) 2004, 2014 David Grudl (http://davidgrudl.com) All rights reserved. (New BSD) + SabreDAV Library http://code.google.com/p/sabredav (New BSD) + SECURIMAGE http://www.phpcaptcha.org/ (LGPL) + Pclzip : http://www.phpconcept.net/pclzip/ (LGPL) + +
    Plugins
    + PThumb.php http://www.phpclasses.org (LPGL) + OpenLayers http://www.openlayers.org (BSD) + Video-js http://www.videojs.com/ (LGPL) + Zend Lucene http://www.zend.com/ (New BSD) + svn_lib.inc (LGPL) + PhpMailer-Lite http://phpmailer.sourceforge.net (LGPL) + Mp3 Player : http://www.alsacreations.fr/mp3-dewplayer.html + SoundManager : http://www.schillmania.com/projects/soundmanager2/ (BSD) + CAS Driver (Apache 2): https://github.com/Jasig/phpCAS \ No newline at end of file diff --git a/core/src/core/doc/CREDITS.txt b/core/src/core/doc/CREDITS.txt index b22c3cae82..37f220609e 100644 --- a/core/src/core/doc/CREDITS.txt +++ b/core/src/core/doc/CREDITS.txt @@ -1,6 +1,6 @@
    Version __AJXP_VERSION__ - __AJXP_VERSION_DATE__ -Copyright 2007-2014 Abstrium SAS +Copyright 2007-2016 Abstrium SAS The latest code can be found at https://pydio.com/.
    ++ Getting Help ++ diff --git a/core/src/core/doc/PREVIOUS_RELEASES b/core/src/core/doc/PREVIOUS_RELEASES index 01c5d06ed6..3616a0129c 100644 --- a/core/src/core/doc/PREVIOUS_RELEASES +++ b/core/src/core/doc/PREVIOUS_RELEASES @@ -2347,7 +2347,7 @@ Rework "regexp" in listUsersPaginated / getUsersCount in class.sqlAuthDriver.php Use HTML base tag instead of the previous complicated "travel_path_to_root" mechanism... New access point for managing users self-service operations (currently reset password) Make custom sort function to make sure users are sorted (fix #279) -New variable AJXP_SANITIZE_FILENAME, to be less restrictive on the allowed characters than AJXP_SANITIZE_HTML_STRICT. Particularly, allow commas in filename. +New variable InputFilter::SANITIZE_FILENAME, to be less restrictive on the allowed characters than InputFilter::SANITIZE_HTML_STRICT. Particularly, allow commas in filename. Login: trim username and password to avoid trailing and ending white space problems Better API for get_my_feed. Ability to force recycle_bin creation in workspace share Add node.share.create hooks @@ -2564,7 +2564,7 @@ preg_replace(): The /e modifier is deprecated in PHP5.5 Fix #404 Fix #392, by adding PRE and POST callbacks to PclZip skipSecureToken actions were not taken into accounts in external files. -Disable at last native autocompletion on share dialog, by removing name attribute. Styling of the Minisite, customize Logo Always sanitize user names as AJXP_SANITIZE_EMAILCHARS Users dashboard, revert styling of FetchedResultPane, was ugly. To be improved still. +Disable at last native autocompletion on share dialog, by removing name attribute. Styling of the Minisite, customize Logo Always sanitize user names as InputFilter::SANITIZE_EMAILCHARS Users dashboard, revert styling of FetchedResultPane, was ugly. To be improved still. Detect if minisite logo is a binary or a real filepath Fix log.sql for ipv6 logging Add a paddingBottom when FilesList is in gridMode, to avoid hiding last line with scrollbar, fix #172 @@ -2861,7 +2861,7 @@ serialized data can contain null bytes, store them in blob (details) New functions in to be implemented by conf plugins (save/load/clearTemporaryKey, pruneTemporaryKeys) (details) Replace AJXP_REBASE if present in the html templates and passed in START_PARAMETERS (details) FS: Pass __AJXP_ZIP_FLAT__ to makeZip function to force all files to be at level 0 Fix tabulator et splitter resizing issues Download Cart. (details) -New variable AJXP_SANITIZE_FILENAME, to be less restrictive on the allowed characters than AJXP_SANITIZE_HTML_STRICT. Particularly, allow commas in filename. (details) +New variable InputFilter::SANITIZE_FILENAME, to be less restrictive on the allowed characters than InputFilter::SANITIZE_HTML_STRICT. Particularly, allow commas in filename. (details) New invisibleBar, and autoFoldOnEvent options on Splitter New tabsTips option on Tabulator Fix simpleTooltip positioning and style Rework layout here and there, implements the new behaviours, still to be tested and validated. (details) Expand and fix widgets, add a mode for horizontal tabulator positioning. (details) diff --git a/core/src/core/doc/api2.json b/core/src/core/doc/api2.json new file mode 100644 index 0000000000..d1015d58e1 --- /dev/null +++ b/core/src/core/doc/api2.json @@ -0,0 +1 @@ +{"swagger":"2.0","host":"localhost","schemes":["http"],"basePath":"/api/v2","info":{"version":"2.0.0","title":"Pydio API V2","description":"Access to a Pydio Server. Second version of the API."},"paths":{"/fs/{path}":{"get":{"x-pydio-action":"ls","description":"Get information about a node and its metadata. By default, it will return \nPydio \"primary\" metadata (stat, internal informations). Extended metadata can\nbe added by some plugins. \nFor collections (folders), pass the **children** parameter to list its content. \nTo access the actual content of the nodes, see the I/O API.\n","operationId":"getNodeInfos","parameters":[{"$ref":"#/parameters/pathParameter"},{"name":"meta","in":"query","description":"Level of precision for expected metadata","type":"string","enum":["minimal","standard","extended"],"required":true,"default":"standard"},{"name":"children","in":"query","description":"Load children if node is a collection","required":false,"type":"string","enum":["dfz","d","df","f","fz","z","dz"]},{"name":"recursive","in":"formData","description":"If requiring children, load grandchildren recursively","type":"boolean","required":false},{"name":"max_depth","in":"formData","description":"If requiring children recursively, stop at the given depth. If -1, no limit.","type":"integer","required":false,"default":-1},{"name":"max_nodes","in":"formData","description":"If requiring children recursively, stop at the given depth. If -1, no limit.","type":"integer","required":false,"default":-1},{"name":"remote_order","in":"formData","description":"Apply server-side sorting","type":"boolean","required":false,"default":false},{"name":"order_column","in":"formData","description":"Order column to use for server-side sorting","type":"string","required":false},{"name":"order_direction","in":"formData","description":"Order direction to use for server-side sorting (asc or desc)","type":"string","required":false},{"name":"page_position","in":"formData","description":"For a single file, try to detect the page position in the parent node listing.","type":"boolean","required":false,"default":false},{"name":"X-Indexation-Required","in":"header","description":"Invalidate current index and trigger a background indexation","type":"boolean","required":false}],"produces":["application/json","application/xml"],"responses":{"200":{"description":"Successful response","schema":{"$ref":"#/definitions/NodeList"}}},"tags":["File"]},"post":{"x-pydio-action":"fs_create_resource","description":"Create new resources or move/copy existing resources:\n+ Create a new folder (pass a path **with a trailing slash**), or a new empty file (no trailing slash).\n+ Copy a resource to a new destination: pass destination as {path}, and origin via copy_from parameter.\n+ Rename / Move a resource : basically a copy operation followed by a delete of original\n","operationId":"createNode","parameters":[{"$ref":"#/parameters/pathParameter"},{"name":"copy_source","in":"query","description":"If it's a move or a copy, indicated the path of the original node. Path must contain the original workspace Id, as it can be used for cross repository copy as well.","type":"string","required":false},{"name":"delete_source","in":"query","description":"If it's a move/rename, will remove original after copy operation","required":false,"type":"boolean"},{"name":"override","in":"query","description":"Ignore existing resource and override it","required":false,"type":"boolean","default":false},{"name":"recursive","in":"query","description":"For directories, create parents if necessary","required":false,"type":"boolean","default":false}],"produces":["application/json","application/xml"],"responses":{"200":{"description":"Successful response","schema":{"$ref":"#/definitions/PydioResponse"}}},"tags":["File"]},"patch":{"x-pydio-action":"fs_update_metadata","description":"Update existing resources metadata (see I/O for content modification). Basic metadata is provided by core plugins, but they can be extended by other plugins.\nFor example :\n`{\"core\": {\"chmod\": 777}}, {\"user_meta\":[{\"metaName\":\"metaValue\"}]}`\n`{\"bookmarks\":{\"bookmarked\": true}, \"locks\":{\"locked\":true}, \"meta.watch\":{\"watch\":true}}`\netc...\n","operationId":"updateNode","parameters":[{"$ref":"#/parameters/pathParameter"},{"name":"metadata","in":"formData","description":"Json-serialized metadata to update","type":"string","required":true}],"produces":["application/json","application/xml"],"responses":{"200":{"description":"Successful response","schema":{"$ref":"#/definitions/PydioResponse"}}},"tags":["File"]},"delete":{"x-pydio-action":"delete","description":"Delete existing resource\n","operationId":"deleteNode","parameters":[{"$ref":"#/parameters/pathParameter"}],"produces":["application/json","application/xml"],"responses":{"200":{"description":"Successful response","schema":{"$ref":"#/definitions/PydioResponse"}}},"tags":["File"]}},"/io/{path}":{"get":{"x-pydio-action":"download","description":"Get resource content. Depending on the attachment parameter, will try to either trigger a download, or send binary stream with appropriate headers. Depending on the active plugins, may be able to serve: + Plain text + MP3/Wav Stream + MP4 Stream + On-the-fly generated images + On-the-fly generated thumbnails for images or pdf\n","operationId":"download","parameters":[{"$ref":"#/parameters/pathParameter"},{"name":"attachment","in":"query","description":"if set, send back a force-download, otherwise use Accept header to try to find the best response Content-Type.","type":"boolean","required":false},{"name":"additional_parameters","in":"query","description":"some plugin can take more parameters to send various contents\nderived from main resource. For example, for images, you can pass\nget_thumb & dimension\n","type":"string","required":false}],"produces":["application/octet-stream"],"responses":{"200":{"description":"Successful Response","schema":{"type":"file"}}},"tags":["File"]},"put":{"x-pydio-action":"upload","description":"Create resource by posting to Input Stream\n","operationId":"uploadStream","parameters":[{"$ref":"#/parameters/pathParameter"},{"name":"inputStream","in":"body","description":"binary data","required":true,"schema":{"$ref":"#/definitions/InputStream"}}],"responses":{"200":{"description":"Successful Response","schema":{"$ref":"#/definitions/PydioResponse"}}},"tags":["File"]},"post":{"x-pydio-action":"upload","description":"Create resource by posting via form Data\n","operationId":"upload","consumes":["multipart/form-data"],"parameters":[{"$ref":"#/parameters/pathParameter"},{"name":"userfile_0","in":"formData","description":"File to upload","required":true,"type":"file"}],"responses":{"200":{"description":"Successful Response","schema":{"$ref":"#/definitions/PydioResponse"}}},"tags":["File"]},"patch":{"x-pydio-action":"upload","description":"Update existing resource via form Data\n","operationId":"appendData","consumes":["multipart/form-data"],"parameters":[{"$ref":"#/parameters/pathParameter"},{"name":"append","in":"formData","description":"wether to replace content or append at the end of the existing resource","required":true,"default":true,"type":"boolean"},{"name":"userfile_0","in":"formData","description":"File to upload","required":true,"type":"file"}],"responses":{"200":{"description":"Successful Response","schema":{"$ref":"#/definitions/PydioResponse"}}},"tags":["File"]}},"/tasks":{"get":{"description":"List tasks currently running on the server (and visible to the current user).\n","operationId":"listTasks","x-pydio-plugin":"core.tasks","x-pydio-action":"tasks_list","parameters":[{"name":"filter","in":"formData","description":"additional filters for task listing (JSON serialized)","type":"string","required":false},{"$ref":"#/parameters/workspaceIdFormParameter"},{"$ref":"#/parameters/nodeSelectionParameter"}],"responses":{"200":{"description":"Successful Response","schema":{"type":"array","items":{"$ref":"#/definitions/Task"}}}},"tags":["Task"]}},"/tasks/{taskId}":{"get":{"description":"Get information about a currently running task Id\n","operationId":"getTaskInfo","x-pydio-plugin":"core.tasks","x-pydio-action":"task_info","parameters":[{"name":"taskId","in":"path","description":"Task to monitor on the server","type":"string","required":true}],"responses":{"200":{"description":"Successful Response","schema":{"$ref":"#/definitions/Task"}}},"tags":["Task"]},"post":{"description":"Create a task on the server. This will generally trigger a server-side background \"Task\", which ID will be returned in the PydioResponse['tasks'] array\n","operationId":"createTask","x-pydio-plugin":"core.tasks","x-pydio-action":"task_create","consumes":["application/json"],"parameters":[{"name":"taskId","in":"path","description":"Task to launch on the server","type":"string","required":true},{"name":"request_body","in":"body","description":"JSON Task object","schema":{"$ref":"#/definitions/Task"}}],"responses":{"200":{"description":"Successful Response","schema":{"$ref":"#/definitions/Task"}}},"tags":["Task"]},"patch":{"description":"Update a task on the server.\n","operationId":"updateTask","x-pydio-plugin":"core.tasks","x-pydio-action":"task_update","consumes":["application/json"],"parameters":[{"name":"taskId","in":"path","description":"Task to update on the server","type":"string","required":true},{"name":"request_body","in":"body","description":"JSON Diff describing the patches to apply on the task object","schema":{"$ref":"#/definitions/Task"}}],"responses":{"200":{"description":"Successful Response","schema":{"$ref":"#/definitions/Task"}}},"tags":["Task"]},"delete":{"description":"Update a task on the server.\n","operationId":"deleteTask","x-pydio-plugin":"core.tasks","x-pydio-action":"task_delete","parameters":[{"name":"taskId","in":"path","description":"Task to delete on the server","type":"string","required":true}],"responses":{"200":{"description":"Successful Response","schema":{"$ref":"#/definitions/Task"}}},"tags":["Task"]}},"/user":{"get":{"description":"List accessible workspace for currently logged user. Alias for /state/?xPath=user/repositories\n","operationId":"userInfo","produces":["application/json"],"x-pydio-plugin":"core.conf","x-pydio-action":"user_state","responses":{"200":{"description":"Successful Response","schema":{"type":"object"}}},"tags":["User Account"]}},"/user/workspaces":{"get":{"description":"List accessible workspace for currently logged user. Alias for /state/?xPath=user/repositories\n","operationId":"userWorkspaces","produces":["application/json"],"x-pydio-plugin":"core.conf","x-pydio-action":"user_state","responses":{"200":{"description":"Successful Response","schema":{"type":"object"}}},"tags":["User Account"]}},"/user/preferences":{"get":{"description":"List accessible workspace for currently logged user. Alias for /state/?xPath=user/preferences\n","operationId":"userPreferences","produces":["application/json"],"x-pydio-plugin":"core.conf","x-pydio-action":"user_state","responses":{"200":{"description":"Successful Response","schema":{"type":"object"}}},"tags":["User Account"]}},"/workspaces/{workspaceId}":{"get":{"description":"Get information about the given workspace.\nInfo can be gathered via various plugins. Pass the expected metadata type via the X-Pydio-Ws-Info header. Currently supported values are quota|info|changes\n","operationId":"getWorkspaceInfo","produces":["application/json"],"parameters":[{"name":"X-Pydio-WS-Info","in":"header","required":true,"type":"string","enum":["quota","info"]},{"$ref":"#/parameters/workspaceIdParameter"}],"x-pydio-plugin":"core.conf","x-pydio-action":"load_repository_info","responses":{"200":{"description":"Successful Response","schema":{"type":"object","properties":{"USAGE":{"type":"integer"},"TOTAL":{"type":"integer"}}}}},"tags":["Workspace"]}},"/workspaces/{workspaceId}/changes/{sequenceId}":{"get":{"description":"Sends back all changes since a given sequence ID. \n\nThis plugin requires **meta.syncable** active on the workspace.\n","operationId":"changes","produces":["application/json"],"parameters":[{"name":"sequenceId","in":"path","description":"File to upload","required":true,"type":"integer"},{"$ref":"#/parameters/workspaceIdParameter"}],"x-pydio-plugin":"meta.syncable","x-pydio-action":"changes","responses":{"200":{"description":"Successful Response","schema":{"type":"array","items":{"type":"object","properties":{"seq_id":{"type":"integer"},"node":{"$ref":"#/definitions/Node"}}}}}},"tags":["Workspace"]}},"/admin/roles":{"get":{"x-pydio-action":"ls","description":"List roles\n","operationId":"getRoles","produces":["application/json","application/xml"],"responses":{"200":{"description":"A list of roles represented as standard nodes","schema":{"$ref":"#/definitions/NodeList"}}},"tags":["Provisioning"]}},"/admin/roles/{roleId}":{"get":{"x-pydio-action":"edit_role","description":"Get Role by Id\n","operationId":"getRole","parameters":[{"name":"roleId","in":"path","description":"Id of the role to load","type":"string","required":true},{"name":"load_fill_values","in":"query","description":"Load additional data to build a form for editing this role","type":"boolean","required":false}],"produces":["application/json"],"responses":{"200":{"description":"Successful response","schema":{"$ref":"#/definitions/Role"}}},"tags":["Provisioning"]},"delete":{"x-pydio-action":"delete","description":"Delete Role by Id\n","operationId":"deleteRole","parameters":[{"name":"roleId","in":"path","description":"Id of the role to delete","type":"string","required":true}],"produces":["application/json"],"responses":{"200":{"description":"Successful response","schema":{"$ref":"#/definitions/PydioResponse"}}},"tags":["Provisioning"]},"post":{"x-pydio-action":"create_role","description":"Create a new role with this ID\n","operationId":"createRole","parameters":[{"name":"roleId","in":"path","description":"Id of the role to load","type":"string","required":true},{"name":"role","in":"body","schema":{"$ref":"#/definitions/Role"},"description":"JSON description of the new role","required":true}],"produces":["application/json"],"consumes":["application/json"],"responses":{"200":{"description":"Successful response","schema":{"$ref":"#/definitions/Role"}}},"tags":["Provisioning"]},"patch":{"x-pydio-action":"post_json_role","description":"Update the role\n","operationId":"updateRole","parameters":[{"name":"roleId","in":"path","description":"Id of the role to load","type":"string","required":true},{"name":"role","in":"body","schema":{"$ref":"#/definitions/Role"},"description":"JSON description of the new role","required":true}],"produces":["application/json"],"consumes":["application/json"],"responses":{"200":{"description":"Successful response","schema":{"$ref":"#/definitions/Role"}}},"tags":["Provisioning"]}},"/admin/people/{path}":{"get":{"x-pydio-action":"ls","description":"List roles\n","operationId":"getPeople","produces":["application/json","application/xml"],"parameters":[{"$ref":"#/parameters/peopleParameterOptional"},{"name":"list","in":"query","description":"list children of the current resource","type":"boolean","default":true}],"responses":{"200":{"description":"A user/group or list of users and groups","schema":{"$ref":"#/definitions/NodeList"}}},"tags":["Provisioning"]},"delete":{"x-pydio-action":"people-delete-resource","description":"Delete Role by Id\n","operationId":"deletePeople","parameters":[{"$ref":"#/parameters/peopleParameter"}],"produces":["application/json"],"responses":{"200":{"description":"Successful response","schema":{"$ref":"#/definitions/PydioResponse"}}},"tags":["Provisioning"]},"post":{"x-pydio-action":"people-create-resource","description":"Create a new user or a new group with this path. To create a user, make sure to pass a userPass parameter. Otherwise it will create a group.\n","operationId":"createPeople","parameters":[{"$ref":"#/parameters/peopleParameter"},{"name":"resourceType","in":"formData","type":"string","enum":["user","group"],"description":"Wether it's a user or a group","required":true},{"name":"groupLabel","in":"formData","type":"string","description":"Label of the new group if we are creating a group","required":false},{"name":"userPass","in":"formData","type":"string","description":"Password of the new user if we are creating a user","required":false}],"produces":["application/json"],"consumes":["application/json"],"responses":{"200":{"description":"Successful response","schema":{"$ref":"#/definitions/PydioResponse"}}},"tags":["Provisioning"]},"patch":{"x-pydio-action":"people-patch-resource","description":"Update user or group specific data with this path. Use resourceType parameter to discriminate, and send a parameterName/parameterValue couple to be patched.\n\nAuthorized parameterName values are described below, along with the parameterValue corresponding specification: \n- For groups\n - groupLabel : relabel an existing group\n- For users\n - userPass: change user password\n - userProfile: update user profile\n - userLock: set/remove a lock on a user. Value must be a lockname:lockvalue string where lockvalue is \"true\" or \"fale\".\n - userRoles: Bunch update roles, eventually reorder them, as a JSON encoded array.\n - userAddRole: add a role to the user\n - userRemoveRole: remove a role currently applied to the user.\n - userPreferences: a JSON associative array of key/values to update.\n\nTo edit user/group permissions or parameters, use the role api instead, using the object specific role_id (AJXP_GRP_/groupPath or AJXP_USR_/userId).\n","operationId":"patchPeople","parameters":[{"$ref":"#/parameters/peopleParameter"},{"name":"resourceType","in":"query","type":"string","enum":["user","group"],"description":"Wether it's a user or a group","required":true},{"name":"patch-tuple","in":"body","schema":{"$ref":"#/definitions/PeoplePatch"},"description":"parameterName / parameterValue association","required":true}],"produces":["application/json"],"consumes":["application/json"],"responses":{"200":{"description":"Successful response","schema":{"$ref":"#/definitions/PydioResponse"}}},"tags":["Provisioning"]}},"/admin/workspaces":{"get":{"x-pydio-action":"ls","description":"List Workspaces\n","operationId":"adminListWorkspaces","produces":["application/json","application/xml"],"responses":{"200":{"description":"A list of workspaces represented as standard nodes","schema":{"$ref":"#/definitions/NodeList"}}},"tags":["Provisioning"]},"post":{"x-pydio-action":"post_repository","description":"NOT IMPLEMENTED YET - Create a workspace from scratch\n","operationId":"adminCreateWorkspace","parameters":[{"name":"payload","in":"body","description":"Repository details","schema":{"$ref":"#/definitions/AdminWorkspace"},"required":true}],"produces":["application/json","application/xml"],"responses":{"200":{"description":"Workspace object","schema":{"$ref":"#/definitions/AdminWorkspace"}}},"tags":["Provisioning"]}},"/admin/workspaces/{workspaceId}":{"get":{"x-pydio-action":"edit_repository","description":"Load detail of a workspace\n","operationId":"adminGetWorkspace","parameters":[{"name":"workspaceId","in":"path","description":"Id or Alias / Load detail of this workspace","type":"string","required":true},{"name":"load_fill_values","in":"query","description":"Load additional data to build a form for editing this role","type":"boolean","required":false}],"produces":["application/json","application/xml"],"responses":{"200":{"description":"Workspace object","schema":{"$ref":"#/definitions/AdminWorkspace"}}},"tags":["Provisioning"]},"delete":{"x-pydio-action":"delete","description":"Load detail of a workspace\n","operationId":"adminDeleteWorkspace","parameters":[{"name":"workspaceId","in":"path","description":"Id or Alias / Load detail of this workspace","type":"string","required":true}],"produces":["application/json","application/xml"],"responses":{"200":{"description":"Confirmation response","schema":{"$ref":"#/definitions/PydioResponse"}}},"tags":["Provisioning"]},"patch":{"x-pydio-action":"patch_repository","description":"NOT IMPLEMENTED YET - Edit details of a workspace\n","operationId":"adminEditWorkspace","parameters":[{"name":"workspaceId","in":"path","description":"Id or Alias / Update details for this workspace","type":"string","required":true},{"name":"payload","in":"body","description":"Repository details","schema":{"$ref":"#/definitions/AdminWorkspace"},"required":true}],"produces":["application/json","application/xml"],"responses":{"200":{"description":"Workspace object","schema":{"$ref":"#/definitions/AdminWorkspace"}}},"tags":["Provisioning"]}},"/admin/workspaces/{workspaceId}/features/{metaId}":{"post":{"x-pydio-action":"meta_source_add","description":"Add a metasource\n","operationId":"adminAddWorkspaceFeature","parameters":[{"name":"workspaceId","in":"path","description":"id or alias of this workspace","type":"string","required":true},{"name":"metaId","in":"path","description":"plugin id for new meta","type":"string","required":true},{"name":"parameters","in":"body","description":"Meta source parameters","schema":{"$ref":"#/definitions/MetaSourceParameters"},"required":true}],"produces":["application/json","application/xml"],"responses":{"200":{"description":"Workspace object","schema":{"$ref":"#/definitions/PydioResponse"},"examples":{"application/json":{"message":"Succesfully added metasource"}}}},"tags":["Provisioning"]},"patch":{"x-pydio-action":"meta_source_edit","description":"Edit a metasource\n","operationId":"adminEditWorkspaceFeature","parameters":[{"name":"workspaceId","in":"path","description":"id or alias of this workspace","type":"string","required":true},{"name":"metaId","in":"path","description":"plugin id for meta","type":"string","required":true},{"name":"parameters","in":"body","description":"Meta source parameters","schema":{"$ref":"#/definitions/MetaSourceParameters"},"required":true}],"produces":["application/json","application/xml"],"responses":{"200":{"description":"Workspace object","schema":{"$ref":"#/definitions/PydioResponse"}}},"tags":["Provisioning"]},"delete":{"x-pydio-action":"meta_source_delete","description":"Edit a metasource\n","operationId":"adminRemoveWorkspaceFeature","parameters":[{"name":"workspaceId","in":"path","description":"id or alias of this workspace","type":"string","required":true},{"name":"metaId","in":"path","description":"plugin id for meta","type":"string","required":true}],"produces":["application/json","application/xml"],"responses":{"200":{"description":"Workspace object","schema":{"$ref":"#/definitions/PydioResponse"}}},"tags":["Provisioning"]}}},"parameters":{"pathParameter":{"name":"path","in":"path","description":"Workspace id or alias + full path to the node, e.g. \"/my-files/path/to/node\"","required":true,"type":"string"},"workspaceIdParameter":{"name":"workspaceId","in":"path","description":"Id or Alias of the workspace","required":true,"type":"string"},"workspaceIdFormParameter":{"name":"workspaceId","in":"formData","description":"Id or Alias of the workspace","required":true,"type":"string"},"nodeSelectionParameter":{"name":"path","in":"formData","description":"One or more node pathes","required":false,"type":"array","items":{"type":"string"},"collectionFormat":"multi"},"peopleParameter":{"name":"path","in":"path","description":"User or group identifier, including full group path","required":true,"type":"string"},"peopleParameterOptional":{"name":"path","in":"path","description":"User or group identifier, including full group path (optional)","required":true,"type":"string"}},"definitions":{"PydioResponse":{"title":"PydioResponse","description":"Generic container for messages after successful operations or errors","type":"object","properties":{"message":{"type":"string"},"level":{"type":"string"},"errorCode":{"type":"integer"},"nodesDiff":{"$ref":"#/definitions/NodesDiff"},"bgAction":{"$ref":"#/definitions/BgAction"},"tasks":{"type":"array","items":{"$ref":"#/definitions/Task"}}},"example":{"message":"Operation completed successfuly"}},"Task":{"title":"Task","description":"Background operation started on the server. It's the client mission to check it on a regular basis.","type":"object","properties":{"id":{"type":"string"},"status":{"type":"integer"},"label":{"type":"string"},"description":{"type":"string"},"userId":{"type":"string"},"wsId":{"type":"string"},"actionName":{"type":"string"},"schedule":{"type":"object","properties":{"scheduleType":{"type":"string"},"scheduleValue":{"type":"string"}}},"parameters":{"type":"object"}}},"BgAction":{"title":"BgAction","description":"triggers a background action on the client side","type":"object","properties":{"actionName":{"type":"string"},"delay":{"type":"integer"}}},"Node":{"title":"Node","description":"A file or folder represented as a generic resource, including metadata and children. Properties before children are part of the \"standard\" metadat set, properties after are returned by the \"extended\" metadata set.","type":"object","properties":{"path":{"type":"string"},"is_leaf":{"type":"boolean"},"label":{"type":"string"},"ajxp_modiftime":{"type":"integer"},"bytesize":{"type":"integer"},"stat":{"type":"object"},"ajxp_relativetime":{"type":"string"},"ajxp_description":{"type":"string"},"icon":{"type":"string"},"filesize":{"type":"string"},"mimestring_id":{"type":"string"},"ajxp_readonly":{"type":"boolean"},"file_perms":{"type":"string"},"repo_has_recycle":{"type":"boolean"},"children":{"$ref":"#/definitions/NodeList"}}},"NodesDiff":{"title":"NodesDiff","description":"Description of node removed / added / updated in the backend","type":"object","properties":{"add":{"type":"array","items":{"$ref":"#/definitions/Node"}},"update":{"type":"array","description":"Nodes may have an additional attribute original_path","items":{"$ref":"#/definitions/Node"}},"remove":{"type":"array","items":{"type":"string"}}}},"NodeList":{"title":"NodeList","description":"List of Node objects","type":"object","properties":{"pagination":{"$ref":"#/definitions/PaginationData"},"data":{"type":"object","properties":{"node":{"$ref":"#/definitions/Node"},"children":{"type":"array","items":{"$ref":"#/definitions/Node"}}}}}},"PaginationData":{"title":"PaginationData","description":"Additional metadata attached to a NodeList for pagination. Could be sent through headers instead.","type":"object","properties":{"count":{"type":"integer","description":"total number of children"},"current":{"type":"integer","description":"current page"},"total":{"type":"integer","description":"total number of pages"},"dirs":{"type":"integer","description":"total number of \"collection\" childrens"},"remoteSort":{"type":"object","description":"additional attributes describing current server-side sorting"}}},"InputStream":{"title":"InputStream","description":"Simple binary stream","type":"string"},"Role":{"title":"Role","description":"Representation of a Role, central container of permissions, actions and parameters.","type":"object","properties":{"ACL":{"type":"object","description":"Key/value associating workspace IDs and rights strings (r/w)"},"MASKS":{"type":"object","description":"Folders permissions masks"},"PARAMETERS":{"type":"object","description":"Refined values of plugins parameters"},"ACTIONS":{"type":"object","description":"Enabled/disabled actions of plugins"},"APPLIES":{"type":"object","description":"Set of profiles on which this role automatically applies"}}},"PeoplePatch":{"title":"PeoplePatch","description":"a key / value tuple describing which parameter to patch","type":"object","properties":{"resourceType":{"type":"string","enum":["user","group"]},"parameterName":{"type":"string","enum":["groupLabel","userPass","userProfile","userLock","userRoles","userAddRole","userRemoveRole","userPreferences"]},"parameterValue":{"type":"string"}}},"AdminWorkspace":{"title":"Workspace Definition","description":"Parameters of a workspace, as seen by administrator","type":"object","required":["display","accessType"],"properties":{"id":{"type":"string","description":"Id of this workspace"},"slug":{"type":"string","description":"human readable identifier, computed from display"},"display":{"type":"string","description":"Label for this workspace"},"displayStringId":{"type":"string","description":"an i18n identifier to adapt the label to the user language"},"accessType":{"type":"string","description":"plugin name to be used as driver to access the storage. Resulting plugin id is \"access.accessType\"."},"writeable":{"type":"boolean","description":"wether this workspace/template is writeable or not (not writeable if defined in bootstrap php configs)."},"isTemplate":{"type":"boolean","description":"wether this is a template or a concrete workspace."},"groupPath":{"type":"string","description":"If this repository has a groupPath"},"parameters":{"type":"object","description":"a key/value object containing all driver parameters."},"features":{"type":"object","description":"The additional features parameters."},"mask":{"type":"object","description":"permission mask applied on workspace files and folders"},"info":{"type":"object","description":"additional informations provided by the server","properties":{"user":{"type":"integer","description":"computed number of users accessing this workspace"},"shares":{"type":"integer","description":"number of children shared from this workspace"}}}},"example":{"id":1,"securityScope":"USER","slug":"my-files","display":"My Files","displayStringId":432,"accessType":"fs","writeable":false,"isTemplate":false,"parameters":{"CREATION_TIME":1468593800,"USER_DESCRIPTION":476,"PATH":"AJXP_DATA_PATH/personal/AJXP_USER","CREATE":true,"RECYCLE_BIN":"recycle_bin","CHMOD_VALUE":"0600","DEFAULT_RIGHTS":"rw","PAGINATION_THRESHOLD":500,"PAGINATION_NUMBER":200},"features":{"metastore.serial":{"METADATA_FILE":".ajxp_meta","METADATA_FILE_LOCATION":"infolders"},"meta.user":{"meta_fields":"tags","meta_labels":"Tags","meta_visibility":"hidden"},"meta.filehasher":[],"meta.watch":[],"meta.syncable":{"REPO_SYNCABLE":true},"meta.exif":{"meta_fields":"COMPUTED_GPS.GPS_Latitude,COMPUTED_GPS.GPS_Longitude","meta_labels":"Latitude,Longitude"},"index.lucene":{"index_meta_fields":"tags","repository_specific_keywords":"AJXP_USER"}},"INFO":{"users":12,"shares":74}}},"MetaSourceParameters":{"title":"MetaSource","description":"A set of parameters for meta sources","type":"object","example":{"PLUGIN_PARAMETER_STRING":"parameterValue1","PLUGIN_PARAMETER_INT":1,"PLUGIN_PARAMETER_BOOL":true}}}} \ No newline at end of file diff --git a/core/src/core/doc/wopi.json b/core/src/core/doc/wopi.json new file mode 100644 index 0000000000..e7585bef22 --- /dev/null +++ b/core/src/core/doc/wopi.json @@ -0,0 +1 @@ +{"swagger":"2.0","host":"localhost","schemes":["http"],"basePath":"/wopi","info":{"version":"0.0.1","title":"Pydio WOPI V1","description":"Access to a Pydio File via WOPI protocol."},"paths":{"/files/{path}/contents":{"get":{"x-pydio-action":"download","description":"Get resource content.\n","operationId":"download","parameters":[{"$ref":"#/parameters/pathParameter"},{"name":"access_token","in":"query","description":"An access token that the host will use to determine whether the request is authorized.","type":"string","required":true},{"name":"X-WOPI-MaxExpectedSize","in":"header","description":"An integer specifying the upper bound of the expected size of the file being requested","type":"integer","required":false}],"produces":["application/octet-stream"],"responses":{"200":{"description":"Successful Response","schema":{"type":"file"}}},"tags":["File"]},"post":{"x-pydio-action":"upload","description":"Create resource by posting via form Data\n","operationId":"uploadStream","consumes":["multipart/form-data"],"parameters":[{"$ref":"#/parameters/pathParameter"},{"name":"inputStream","in":"body","description":"binary data","required":true,"schema":{"$ref":"#/definitions/InputStream"}}],"responses":{"200":{"description":"Successful Response"}},"tags":["File"]}},"/files/{path}":{"get":{"x-pydio-action":"ls","description":"Get information about a file.\n","operationId":"getNodeInfos","parameters":[{"$ref":"#/parameters/pathParameter"},{"name":"format","in":"query","description":"Format of the response.","type":"string","required":true,"default":"json"},{"name":"access_token","in":"query","description":"An access token that the host will use to determine whether the request is authorized.","type":"string","required":true},{"name":"X-WOPI-SessionContext","in":"header","description":"The value of the Session context parameter, if provided on the initial WOPI action URL using the sc parameter.","type":"string","required":false}],"produces":["application/json","application/xml"],"responses":{"200":{"description":"Successful response","schema":{"$ref":"#/definitions/NodeList"}}},"tags":["File"]}}},"parameters":{"pathParameter":{"name":"path","in":"path","description":"Workspace id or alias + full path to the node, e.g. \"/my-files/path/to/node\"","required":true,"type":"string"}},"definitions":{"InputStream":{"title":"InputStream","description":"Simple binary stream","type":"string"},"Node":{"title":"Node","description":"A file or folder represented as a generic resource, including metadata and children. Properties before children are part of the \"standard\" metadat set, properties after are returned by the \"extended\" metadata set.","type":"object","properties":{"path":{"type":"string"},"is_leaf":{"type":"boolean"},"label":{"type":"string"},"ajxp_modiftime":{"type":"integer"},"bytesize":{"type":"integer"},"stat":{"type":"object"},"ajxp_relativetime":{"type":"string"},"ajxp_description":{"type":"string"},"icon":{"type":"string"},"filesize":{"type":"string"},"mimestring_id":{"type":"string"},"ajxp_readonly":{"type":"boolean"},"file_perms":{"type":"string"},"repo_has_recycle":{"type":"boolean"},"children":{"$ref":"#/definitions/NodeList"}}},"NodeList":{"title":"NodeList","description":"List of Node objects","type":"object","properties":{"pagination":{"$ref":"#/definitions/PaginationData"},"data":{"type":"object","properties":{"node":{"$ref":"#/definitions/Node"},"children":{"type":"array","items":{"$ref":"#/definitions/Node"}}}}}},"PaginationData":{"title":"PaginationData","description":"Additional metadata attached to a NodeList for pagination. Could be sent through headers instead.","type":"object","properties":{"count":{"type":"integer","description":"total number of children"},"current":{"type":"integer","description":"current page"},"total":{"type":"integer","description":"total number of pages"},"dirs":{"type":"integer","description":"total number of \"collection\" childrens"},"remoteSort":{"type":"object","description":"additional attributes describing current server-side sorting"}}}}} \ No newline at end of file diff --git a/core/src/core/classes/index.html b/core/src/core/src/index.html similarity index 100% rename from core/src/core/classes/index.html rename to core/src/core/src/index.html diff --git a/core/src/core/src/lib/HttpClient.php b/core/src/core/src/lib/HttpClient.php new file mode 100644 index 0000000000..4d4620922a --- /dev/null +++ b/core/src/core/src/lib/HttpClient.php @@ -0,0 +1,501 @@ +cookies array ready for the next request + // Note: This currently ignores the cookie path (and time) completely. Time is not important, + // but path could possibly lead to security problems. + public $persist_referers = true; // For each request, sends path of last request as referer + public $debug = false; + public $handle_redirects = true; // Auaomtically redirect if Location or URI header is found + public $max_redirects = 5; + public $headers_only = false; // If true, stops receiving once headers have been read. + // Basic authorization variables + public $username; + public $password; + // Response vars + public $status; + public $headers = array(); + public $content = ''; + public $errormsg; + // Tracker variables + public $redirect_count = 0; + public $cookie_host = ''; + public $postFileName = "userfile"; + public $postFileData = array(); + public $postDataArray = array(); + + public $directForwarding = false; + /** + * @var bool|resource + */ + public $contentDestStream = false; + /** + * @var bool|callable + */ + public $eventListener = false; + + public $collectHeaders; + + public function __construct($host, $port=80) + { + $this->host = $host; + $this->port = $port; + } + public function get($path, $data = false) + { + $this->path = $path; + $this->method = 'GET'; + if ($data) { + $this->path .= '?'.$this->buildQueryString($data); + } + return $this->doRequest(); + } + public function post($path, $data) + { + $this->path = $path; + $this->method = 'POST'; + $this->postdata = $this->buildQueryString($data); + return $this->doRequest(); + } + public function postFile($path, $postData, $fileVarName, $fileData) + { + $this->path = $path; + $this->method = 'POST'; + $this->postFileData = $fileData; + $this->postDataArray = $postData; + $this->postFileName = $fileVarName; + $this->postdata = $this->buildQueryString($postData); + return $this->doRequest(); + } + + public function writeContentToStream($destStream) + { + $this->contentDestStream = $destStream; + } + + public function clearContentDestStream() + { + $this->contentDestStream = false; + } + + public function setEventListener($callback) + { + $this->eventListener = $callback; + } + + public function notify($eventName, $data = null) + { + if($this->eventListener == false) return; + call_user_func($this->eventListener, $eventName, $data); + } + + public function buildQueryString($data) + { + $querystring = ''; + if (is_array($data)) { + // Change data in to postable data + foreach ($data as $key => $val) { + if (is_array($val)) { + foreach ($val as $val2) { + $querystring .= urlencode($key).'='.urlencode($val2).'&'; + } + } else { + $querystring .= urlencode($key).'='.urlencode($val).'&'; + } + } + $querystring = substr($querystring, 0, -1); // Eliminate unnecessary & + } else { + $querystring = $data; + } + return $querystring; + } + public function doRequest() + { + // Performs the actual HTTP request, returning true or false depending on outcome + $this->notify("open"); + if (!$fp = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout)) { + // Set error message + switch ($errno) { + case -3: + $this->errormsg = 'Socket creation failed (-3)'; + break; + case -4: + $this->errormsg = 'DNS lookup failure (-4)'; + break; + case -5: + $this->errormsg = 'Connection refused or timed out (-5)'; + break; + default: + $this->errormsg = 'Connection failed on '.$this->host.'('.$errno.')'; + $this->errormsg .= ' '.$errstr; + $this->debug($this->errormsg); + } + $this->notify("error", $this->errormsg); + $this->notify("close"); + return false; + } + socket_set_timeout($fp, $this->timeout); + $request = $this->buildRequest(); + $this->debug('Request', $request); + @fwrite($fp, $request); + // Reset all the variables that should not persist between requests + $this->headers = array(); + $this->content = ''; + $this->errormsg = ''; + // Set a couple of flags + $inHeaders = true; + $atStart = true; + $parsedHeaders = false; + $totalReadSize = 0; + // Now start reading back the response + while (!feof($fp)) { + @set_time_limit(60); + $line = fgets($fp, 4096); + if ($atStart) { + // Deal with first line of returned data + $atStart = false; + if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/', $line, $m)) { + $this->errormsg = "Status code line invalid: ".htmlentities($line); + $this->debug($this->errormsg); + $this->notify("error", $this->errormsg); + $this->notify("close"); + return false; + } + $http_version = $m[1]; // not used + $this->status = $m[2]; + $status_string = $m[3]; // not used + $this->debug(trim($line)); + continue; + } + if ($inHeaders) { + if (trim($line) == '') { + $inHeaders = false; + $this->debug('Received Headers', $this->headers); + if (isSet($this->collectHeaders)) { + foreach ($this->headers as $hKey => $hValue) { + if (isSet($this->collectHeaders[$hKey])) { + if($hKey == "content-length" && $hValue == "0") continue; + $this->collectHeaders[$hKey] = $hValue; + Logger::debug("Setting $hKey", $this->collectHeaders); + } + } + } + if ($this->persist_cookies && isset($this->headers['set-cookie']) && $this->host == $this->cookie_host) { + $cookies = $this->headers['set-cookie']; + if (!is_array($cookies)) { + $cookies = array($cookies); + } + foreach ($cookies as $cookie) { + if (preg_match('/([^=]+)=([^;]+);/', $cookie, $m)) { + $this->cookies[$m[1]] = $m[2]; + } + } + // Record domain of cookies for security reasons + $this->cookie_host = $this->host; + } + // If $persist_referers, set the referer ready for the next request + if ($this->persist_referers) { + //$this->debug('Persisting referer: '.$this->getRequestURL()); + $this->referer = $this->getRequestURL(); + } + // Finally, if handle_redirects and a redirect is sent, do that + if ($this->handle_redirects) { + if (++$this->redirect_count >= $this->max_redirects) { + $this->errormsg = 'Number of redirects exceeded maximum ('.$this->max_redirects.')'; + $this->debug($this->errormsg); + $this->redirect_count = 0; + return false; + } + $location = isset($this->headers['location']) ? $this->headers['location'] : ''; + $uri = isset($this->headers['uri']) ? $this->headers['uri'] : ''; + if ($location || $uri) { + $url = parse_url($location.$uri); + // This will FAIL if redirect is to a different site + $this->debug("Should redirect! ", $url); + $data = array(); + if (isSet($url['query'])) { + parse_str($url["query"], $data); + } + $this->host = $url["host"]; + fclose($fp); + if (isSet($this->collectHeaders) && isSet($this->collectHeaders["ajxp-last-redirection"])) { + $this->collectHeaders["ajxp-last-redirection"] = $location.$uri; + } + return $this->get($url['path'], (!empty($data)?$data:false)); + } + } + + if ($this->headers_only) { + break; // Skip the rest of the input + } + continue; + } + if (!preg_match('/([^:]+):\\s*(.*)/', $line, $m)) { + // Skip to the next header + continue; + } + $key = strtolower(trim($m[1])); + $val = trim($m[2]); + if ($this->directForwarding) { + header($line, true); + continue; + } + + // Deal with the possibility of multiple headers of same name + if (isset($this->headers[$key])) { + if (is_array($this->headers[$key])) { + $this->headers[$key][] = $val; + } else { + $this->headers[$key] = array($this->headers[$key], $val); + } + } else { + $this->headers[$key] = $val; + } + continue; + } + + // We're not in the headers, so append the line to the contents + if ($this->directForwarding) { + print $line; + continue; + } + if ($this->contentDestStream===false) { + $this->content .= $line; + } else { + fwrite($this->contentDestStream, $line); + } + $totalReadSize += strlen($line); + $this->notify("data_read", $totalReadSize); + } + $this->notify("close"); + fclose($fp); + if ($this->directForwarding) { + return true; + } + // If data is compressed, uncompress it + if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] == 'gzip') { + $this->debug('Content is gzip encoded, unzipping it'); + if (!$this->headers_only) { + $this->content = substr($this->content, 10); // See http://www.php.net/manual/en/function.gzencode.php + $this->content = gzinflate($this->content); + } + } + // $this->debug("CONTENT : ".htmlentities($this->content)); + return true; + } + public function buildRequest() + { + $headers = array(); + $headers[] = "{$this->method} {$this->path} HTTP/1.0"; // Using 1.1 leads to all manner of problems, such as "chunked" encoding + $headers[] = "Host: {$this->host}"; + $headers[] = "User-Agent: {$this->user_agent}"; + $headers[] = "Accept: {$this->accept}"; + if ($this->use_gzip) { + $headers[] = "Accept-encoding: {$this->accept_encoding}"; + } + $headers[] = "Accept-language: {$this->accept_language}"; + if ($this->referer) { + $headers[] = "Referer: {$this->referer}"; + } + // Cookies + if ($this->cookies) { + $cookie = 'Cookie: '; + foreach ($this->cookies as $key => $value) { + $cookie .= "$key=$value; "; + } + $headers[] = $cookie; + } + // Basic authentication + if ($this->username && $this->password) { + $headers[] = 'Authorization: BASIC '.base64_encode($this->username.':'.$this->password); + } + if (!count($this->postFileData)) { + // If this is a POST, set the content type and length + if ($this->postdata) { + $headers[] = 'Content-Type: application/x-www-form-urlencoded'; + $headers[] = 'Content-Length: '.strlen($this->postdata); + } + $request = implode("\r\n", $headers)."\r\n\r\n".$this->postdata; + } else { + srand((double) microtime()*1000000); + $boundary = "----".substr(md5(rand(0,32000)),0,10); + $headers[] = "Content-Type: multipart/form-data; boundary=$boundary"; + $data = array(); + // attach post vars + $this->postDataArray["Filename"] = $this->postFileData["name"]; + foreach ($this->postDataArray as $index => $value) { + $data[]="--$boundary"; + $data[]= "content-disposition: form-data; name=\"".$index."\""; + $data[]= "\r\n".$value.""; + } + // and attach the file + //$data[]= "--$boundary"; + $content_file = join("", file($this->postFileData["tmp_name"])); + $data[]="--$boundary"; + $data[]="content-disposition: form-data; name=\"".$this->postFileName."\"; filename=\"".$this->postFileData["name"]."\""; + $data[]= "Content-Type: ".$this->postFileData['type']."\r\n"; + $data[]= "".$content_file.""; + $data[]="--$boundary--"; + //$headers[]= "Content-Length: " . strlen(implode("",$data)); + $data = implode("\r\n", $data); + $headers[]= "Content-Length: " . strlen($data); + $headers[] = "Cache-Control: no-cache"; + $headers[] = "Connection: Keep-Alive"; + $request = implode("\r\n", $headers)."\r\n\r\n".$data; + } + return $request; + } + public function getStatus() + { + return $this->status; + } + public function getContent() + { + return $this->content; + } + public function getHeaders() + { + return $this->headers; + } + public function getHeader($header) + { + $header = strtolower($header); + if (isset($this->headers[$header])) { + return $this->headers[$header]; + } else { + return false; + } + } + public function getError() + { + return $this->errormsg; + } + public function getCookies() + { + return $this->cookies; + } + public function getRequestURL() + { + $url = 'http://'.$this->host; + if ($this->port != 80) { + $url .= ':'.$this->port; + } + $url .= $this->path; + return $url; + } + // Setter methods + public function setUserAgent($string) + { + $this->user_agent = $string; + } + public function setAuthorization($username, $password) + { + $this->username = $username; + $this->password = $password; + } + public function setCookies($array) + { + $this->cookies = $array; + } + // Option setting methods + public function useGzip($boolean) + { + $this->use_gzip = $boolean; + } + public function setPersistCookies($boolean) + { + $this->persist_cookies = $boolean; + } + public function setPersistReferers($boolean) + { + $this->persist_referers = $boolean; + } + public function setHandleRedirects($boolean) + { + $this->handle_redirects = $boolean; + } + public function setMaxRedirects($num) + { + $this->max_redirects = $num; + } + public function setHeadersOnly($boolean, &$collectHeaders = null) + { + $this->headers_only = $boolean; + if ($collectHeaders != null) { + $this->collectHeaders = $collectHeaders; + } + } + public function setDebug($boolean) + { + $this->debug = $boolean; + } + // "Quick" static methods + public function quickGet($url) + { + $bits = parse_url($url); + $host = $bits['host']; + $port = isset($bits['port']) ? $bits['port'] : 80; + $path = isset($bits['path']) ? $bits['path'] : '/'; + if (isset($bits['query'])) { + $path .= '?'.$bits['query']; + } + $client = new HttpClient($host, $port); + if (!$client->get($path)) { + return false; + } else { + return $client->getContent(); + } + } + public function quickPost($url, $data) + { + $bits = parse_url($url); + $host = $bits['host']; + $port = isset($bits['port']) ? $bits['port'] : 80; + $path = isset($bits['path']) ? $bits['path'] : '/'; + $client = new HttpClient($host, $port); + if (!$client->post($path, $data)) { + return false; + } else { + return $client->getContent(); + } + } + public function debug($msg, $object = false) + { + if ($this->debug) { + $st = '
    HttpClient Debug: '.$msg; + if ($object) { + ob_start(); + print_r($object); + $content = htmlentities(ob_get_contents()); + ob_end_clean(); + $st .= '
    '.$content.'
    '; + } + $st .= '
    '; + Logger::debug($msg . ($object!==false?" - ".print_r($object, true):"")); + } + } +} diff --git a/core/src/core/classes/dibi/dibi.php b/core/src/core/src/lib/dibi/dibi.php similarity index 100% rename from core/src/core/classes/dibi/dibi.php rename to core/src/core/src/lib/dibi/dibi.php diff --git a/core/src/core/classes/dibi/drivers/DibiFirebirdDriver.php b/core/src/core/src/lib/dibi/drivers/DibiFirebirdDriver.php similarity index 100% rename from core/src/core/classes/dibi/drivers/DibiFirebirdDriver.php rename to core/src/core/src/lib/dibi/drivers/DibiFirebirdDriver.php diff --git a/core/src/core/classes/dibi/drivers/DibiMsSql2005Driver.php b/core/src/core/src/lib/dibi/drivers/DibiMsSql2005Driver.php similarity index 100% rename from core/src/core/classes/dibi/drivers/DibiMsSql2005Driver.php rename to core/src/core/src/lib/dibi/drivers/DibiMsSql2005Driver.php diff --git a/core/src/core/classes/dibi/drivers/DibiMsSql2005Reflector.php b/core/src/core/src/lib/dibi/drivers/DibiMsSql2005Reflector.php similarity index 100% rename from core/src/core/classes/dibi/drivers/DibiMsSql2005Reflector.php rename to core/src/core/src/lib/dibi/drivers/DibiMsSql2005Reflector.php diff --git a/core/src/core/classes/dibi/drivers/DibiMsSqlDriver.php b/core/src/core/src/lib/dibi/drivers/DibiMsSqlDriver.php similarity index 100% rename from core/src/core/classes/dibi/drivers/DibiMsSqlDriver.php rename to core/src/core/src/lib/dibi/drivers/DibiMsSqlDriver.php diff --git a/core/src/core/classes/dibi/drivers/DibiMsSqlReflector.php b/core/src/core/src/lib/dibi/drivers/DibiMsSqlReflector.php similarity index 100% rename from core/src/core/classes/dibi/drivers/DibiMsSqlReflector.php rename to core/src/core/src/lib/dibi/drivers/DibiMsSqlReflector.php diff --git a/core/src/core/classes/dibi/drivers/DibiMySqlDriver.php b/core/src/core/src/lib/dibi/drivers/DibiMySqlDriver.php similarity index 100% rename from core/src/core/classes/dibi/drivers/DibiMySqlDriver.php rename to core/src/core/src/lib/dibi/drivers/DibiMySqlDriver.php diff --git a/core/src/core/classes/dibi/drivers/DibiMySqlReflector.php b/core/src/core/src/lib/dibi/drivers/DibiMySqlReflector.php similarity index 100% rename from core/src/core/classes/dibi/drivers/DibiMySqlReflector.php rename to core/src/core/src/lib/dibi/drivers/DibiMySqlReflector.php diff --git a/core/src/core/classes/dibi/drivers/DibiMySqliDriver.php b/core/src/core/src/lib/dibi/drivers/DibiMySqliDriver.php similarity index 100% rename from core/src/core/classes/dibi/drivers/DibiMySqliDriver.php rename to core/src/core/src/lib/dibi/drivers/DibiMySqliDriver.php diff --git a/core/src/core/classes/dibi/drivers/DibiOdbcDriver.php b/core/src/core/src/lib/dibi/drivers/DibiOdbcDriver.php similarity index 100% rename from core/src/core/classes/dibi/drivers/DibiOdbcDriver.php rename to core/src/core/src/lib/dibi/drivers/DibiOdbcDriver.php diff --git a/core/src/core/classes/dibi/drivers/DibiOracleDriver.php b/core/src/core/src/lib/dibi/drivers/DibiOracleDriver.php similarity index 100% rename from core/src/core/classes/dibi/drivers/DibiOracleDriver.php rename to core/src/core/src/lib/dibi/drivers/DibiOracleDriver.php diff --git a/core/src/core/classes/dibi/drivers/DibiPdoDriver.php b/core/src/core/src/lib/dibi/drivers/DibiPdoDriver.php similarity index 100% rename from core/src/core/classes/dibi/drivers/DibiPdoDriver.php rename to core/src/core/src/lib/dibi/drivers/DibiPdoDriver.php diff --git a/core/src/core/classes/dibi/drivers/DibiPostgreDriver.php b/core/src/core/src/lib/dibi/drivers/DibiPostgreDriver.php similarity index 100% rename from core/src/core/classes/dibi/drivers/DibiPostgreDriver.php rename to core/src/core/src/lib/dibi/drivers/DibiPostgreDriver.php diff --git a/core/src/core/classes/dibi/drivers/DibiSqlite3Driver.php b/core/src/core/src/lib/dibi/drivers/DibiSqlite3Driver.php similarity index 100% rename from core/src/core/classes/dibi/drivers/DibiSqlite3Driver.php rename to core/src/core/src/lib/dibi/drivers/DibiSqlite3Driver.php diff --git a/core/src/core/classes/dibi/drivers/DibiSqliteDriver.php b/core/src/core/src/lib/dibi/drivers/DibiSqliteDriver.php similarity index 100% rename from core/src/core/classes/dibi/drivers/DibiSqliteDriver.php rename to core/src/core/src/lib/dibi/drivers/DibiSqliteDriver.php diff --git a/core/src/core/classes/dibi/drivers/DibiSqliteReflector.php b/core/src/core/src/lib/dibi/drivers/DibiSqliteReflector.php similarity index 100% rename from core/src/core/classes/dibi/drivers/DibiSqliteReflector.php rename to core/src/core/src/lib/dibi/drivers/DibiSqliteReflector.php diff --git a/core/src/core/classes/dibi/libs/Dibi.php b/core/src/core/src/lib/dibi/libs/Dibi.php similarity index 100% rename from core/src/core/classes/dibi/libs/Dibi.php rename to core/src/core/src/lib/dibi/libs/Dibi.php diff --git a/core/src/core/classes/dibi/libs/DibiConnection.php b/core/src/core/src/lib/dibi/libs/DibiConnection.php similarity index 100% rename from core/src/core/classes/dibi/libs/DibiConnection.php rename to core/src/core/src/lib/dibi/libs/DibiConnection.php diff --git a/core/src/core/classes/dibi/libs/DibiDataSource.php b/core/src/core/src/lib/dibi/libs/DibiDataSource.php similarity index 100% rename from core/src/core/classes/dibi/libs/DibiDataSource.php rename to core/src/core/src/lib/dibi/libs/DibiDataSource.php diff --git a/core/src/core/classes/dibi/libs/DibiDatabaseInfo.php b/core/src/core/src/lib/dibi/libs/DibiDatabaseInfo.php similarity index 100% rename from core/src/core/classes/dibi/libs/DibiDatabaseInfo.php rename to core/src/core/src/lib/dibi/libs/DibiDatabaseInfo.php diff --git a/core/src/core/classes/dibi/libs/DibiDateTime.php b/core/src/core/src/lib/dibi/libs/DibiDateTime.php similarity index 100% rename from core/src/core/classes/dibi/libs/DibiDateTime.php rename to core/src/core/src/lib/dibi/libs/DibiDateTime.php diff --git a/core/src/core/classes/dibi/libs/DibiEvent.php b/core/src/core/src/lib/dibi/libs/DibiEvent.php similarity index 100% rename from core/src/core/classes/dibi/libs/DibiEvent.php rename to core/src/core/src/lib/dibi/libs/DibiEvent.php diff --git a/core/src/core/classes/dibi/libs/DibiException.php b/core/src/core/src/lib/dibi/libs/DibiException.php similarity index 100% rename from core/src/core/classes/dibi/libs/DibiException.php rename to core/src/core/src/lib/dibi/libs/DibiException.php diff --git a/core/src/core/classes/dibi/libs/DibiFileLogger.php b/core/src/core/src/lib/dibi/libs/DibiFileLogger.php similarity index 100% rename from core/src/core/classes/dibi/libs/DibiFileLogger.php rename to core/src/core/src/lib/dibi/libs/DibiFileLogger.php diff --git a/core/src/core/classes/dibi/libs/DibiFirePhpLogger.php b/core/src/core/src/lib/dibi/libs/DibiFirePhpLogger.php similarity index 100% rename from core/src/core/classes/dibi/libs/DibiFirePhpLogger.php rename to core/src/core/src/lib/dibi/libs/DibiFirePhpLogger.php diff --git a/core/src/core/classes/dibi/libs/DibiFluent.php b/core/src/core/src/lib/dibi/libs/DibiFluent.php similarity index 100% rename from core/src/core/classes/dibi/libs/DibiFluent.php rename to core/src/core/src/lib/dibi/libs/DibiFluent.php diff --git a/core/src/core/classes/dibi/libs/DibiHashMap.php b/core/src/core/src/lib/dibi/libs/DibiHashMap.php similarity index 100% rename from core/src/core/classes/dibi/libs/DibiHashMap.php rename to core/src/core/src/lib/dibi/libs/DibiHashMap.php diff --git a/core/src/core/classes/dibi/libs/DibiLiteral.php b/core/src/core/src/lib/dibi/libs/DibiLiteral.php similarity index 100% rename from core/src/core/classes/dibi/libs/DibiLiteral.php rename to core/src/core/src/lib/dibi/libs/DibiLiteral.php diff --git a/core/src/core/classes/dibi/libs/DibiObject.php b/core/src/core/src/lib/dibi/libs/DibiObject.php similarity index 100% rename from core/src/core/classes/dibi/libs/DibiObject.php rename to core/src/core/src/lib/dibi/libs/DibiObject.php diff --git a/core/src/core/classes/dibi/libs/DibiResult.php b/core/src/core/src/lib/dibi/libs/DibiResult.php similarity index 100% rename from core/src/core/classes/dibi/libs/DibiResult.php rename to core/src/core/src/lib/dibi/libs/DibiResult.php diff --git a/core/src/core/classes/dibi/libs/DibiResultIterator.php b/core/src/core/src/lib/dibi/libs/DibiResultIterator.php similarity index 100% rename from core/src/core/classes/dibi/libs/DibiResultIterator.php rename to core/src/core/src/lib/dibi/libs/DibiResultIterator.php diff --git a/core/src/core/classes/dibi/libs/DibiRow.php b/core/src/core/src/lib/dibi/libs/DibiRow.php similarity index 100% rename from core/src/core/classes/dibi/libs/DibiRow.php rename to core/src/core/src/lib/dibi/libs/DibiRow.php diff --git a/core/src/core/classes/dibi/libs/DibiTranslator.php b/core/src/core/src/lib/dibi/libs/DibiTranslator.php similarity index 100% rename from core/src/core/classes/dibi/libs/DibiTranslator.php rename to core/src/core/src/lib/dibi/libs/DibiTranslator.php diff --git a/core/src/core/classes/dibi/libs/interfaces.php b/core/src/core/src/lib/dibi/libs/interfaces.php similarity index 100% rename from core/src/core/classes/dibi/libs/interfaces.php rename to core/src/core/src/lib/dibi/libs/interfaces.php diff --git a/core/src/core/classes/pclzip.lib.php b/core/src/core/src/lib/pclzip.lib.php similarity index 95% rename from core/src/core/classes/pclzip.lib.php rename to core/src/core/src/lib/pclzip.lib.php index 83e25e9cdc..035bc4ff99 100644 --- a/core/src/core/classes/pclzip.lib.php +++ b/core/src/core/src/lib/pclzip.lib.php @@ -1,5798 +1,5716 @@ -zipname = $p_zipname; - $this->zip_fd = 0; - $this->zip_ftell = 0; - $this->magic_quotes_status = -1; - - // ----- Return - return; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : - // create($p_filelist, $p_add_dir="", $p_remove_dir="") - // create($p_filelist, $p_option, $p_option_value, ...) - // Description : - // This method supports two different synopsis. The first one is historical. - // This method creates a Zip Archive. The Zip file is created in the - // filesystem. The files and directories indicated in $p_filelist - // are added in the archive. See the parameters description for the - // supported format of $p_filelist. - // When a directory is in the list, the directory and its content is added - // in the archive. - // In this synopsis, the function takes an optional variable list of - // options. See bellow the supported options. - // Parameters : - // $p_filelist : An array containing file or directory names, or - // a string containing one filename or one directory name, or - // a string containing a list of filenames and/or directory - // names separated by spaces. - // $p_add_dir : A path to add before the real path of the archived file, - // in order to have it memorized in the archive. - // $p_remove_dir : A path to remove from the real path of the file to archive, - // in order to have a shorter path memorized in the archive. - // When $p_add_dir and $p_remove_dir are set, $p_remove_dir - // is removed first, before $p_add_dir is added. - // Options : - // PCLZIP_OPT_ADD_PATH : - // PCLZIP_OPT_REMOVE_PATH : - // PCLZIP_OPT_REMOVE_ALL_PATH : - // PCLZIP_OPT_COMMENT : - // PCLZIP_CB_PRE_ADD : - // PCLZIP_CB_POST_ADD : - // Return Values : - // 0 on failure, - // The list of the added files, with a status of the add action. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- - function create($p_filelist) - { - $v_result=1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Set default values - $v_options = array(); - $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE; - - // ----- Look for variable options arguments - $v_size = func_num_args(); - - // ----- Look for arguments - if ($v_size > 1) { - // ----- Get the arguments - $v_arg_list = func_get_args(); - - // ----- Remove from the options list the first argument - array_shift($v_arg_list); - $v_size--; - - // ----- Look for first arg - if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { - - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, - array (PCLZIP_OPT_REMOVE_PATH => 'optional', - PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', - PCLZIP_OPT_ADD_PATH => 'optional', - PCLZIP_CB_PRE_ADD => 'optional', - PCLZIP_CB_POST_ADD => 'optional', - PCLZIP_OPT_NO_COMPRESSION => 'optional', - PCLZIP_OPT_COMMENT => 'optional', - PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', - PCLZIP_OPT_TEMP_FILE_ON => 'optional', - PCLZIP_OPT_TEMP_FILE_OFF => 'optional' - //, PCLZIP_OPT_CRYPT => 'optional' - )); - if ($v_result != 1) { - return 0; - } - } - - // ----- Look for 2 args - // Here we need to support the first historic synopsis of the - // method. - else { - - // ----- Get the first argument - $v_options[PCLZIP_OPT_ADD_PATH] = $v_arg_list[0]; - - // ----- Look for the optional second argument - if ($v_size == 2) { - $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1]; - } - else if ($v_size > 2) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, - "Invalid number / type of arguments"); - return 0; - } - } - } - - // ----- Look for default option values - $this->privOptionDefaultThreshold($v_options); - - // ----- Init - $v_string_list = array(); - $v_att_list = array(); - $v_filedescr_list = array(); - $p_result_list = array(); - - // ----- Look if the $p_filelist is really an array - if (is_array($p_filelist)) { - - // ----- Look if the first element is also an array - // This will mean that this is a file description entry - if (isset($p_filelist[0]) && is_array($p_filelist[0])) { - $v_att_list = $p_filelist; - } - - // ----- The list is a list of string names - else { - $v_string_list = $p_filelist; - } - } - - // ----- Look if the $p_filelist is a string - else if (is_string($p_filelist)) { - // ----- Create a list from the string - $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist); - } - - // ----- Invalid variable type for $p_filelist - else { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_filelist"); - return 0; - } - - // ----- Reformat the string list - if (sizeof($v_string_list) != 0) { - foreach ($v_string_list as $v_string) { - if ($v_string != '') { - $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string; - } - else { - } - } - } - - // ----- For each file in the list check the attributes - $v_supported_attributes - = array ( PCLZIP_ATT_FILE_NAME => 'mandatory' - ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional' - ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional' - ,PCLZIP_ATT_FILE_MTIME => 'optional' - ,PCLZIP_ATT_FILE_CONTENT => 'optional' - ,PCLZIP_ATT_FILE_COMMENT => 'optional' - ); - foreach ($v_att_list as $v_entry) { - $v_result = $this->privFileDescrParseAtt($v_entry, - $v_filedescr_list[], - $v_options, - $v_supported_attributes); - if ($v_result != 1) { - return 0; - } - } - - // ----- Expand the filelist (expand directories) - $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options); - if ($v_result != 1) { - return 0; - } - - // ----- Call the create fct - $v_result = $this->privCreate($v_filedescr_list, $p_result_list, $v_options); - if ($v_result != 1) { - return 0; - } - - // ----- Return - return $p_result_list; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : - // add($p_filelist, $p_add_dir="", $p_remove_dir="") - // add($p_filelist, $p_option, $p_option_value, ...) - // Description : - // This method supports two synopsis. The first one is historical. - // This methods add the list of files in an existing archive. - // If a file with the same name already exists, it is added at the end of the - // archive, the first one is still present. - // If the archive does not exist, it is created. - // Parameters : - // $p_filelist : An array containing file or directory names, or - // a string containing one filename or one directory name, or - // a string containing a list of filenames and/or directory - // names separated by spaces. - // $p_add_dir : A path to add before the real path of the archived file, - // in order to have it memorized in the archive. - // $p_remove_dir : A path to remove from the real path of the file to archive, - // in order to have a shorter path memorized in the archive. - // When $p_add_dir and $p_remove_dir are set, $p_remove_dir - // is removed first, before $p_add_dir is added. - // Options : - // PCLZIP_OPT_ADD_PATH : - // PCLZIP_OPT_REMOVE_PATH : - // PCLZIP_OPT_REMOVE_ALL_PATH : - // PCLZIP_OPT_COMMENT : - // PCLZIP_OPT_ADD_COMMENT : - // PCLZIP_OPT_PREPEND_COMMENT : - // PCLZIP_CB_PRE_ADD : - // PCLZIP_CB_POST_ADD : - // Return Values : - // 0 on failure, - // The list of the added files, with a status of the add action. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- - function add($p_filelist) - { - $v_result=1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Set default values - $v_options = array(); - $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE; - - // ----- Look for variable options arguments - $v_size = func_num_args(); - - // ----- Look for arguments - if ($v_size > 1) { - // ----- Get the arguments - $v_arg_list = func_get_args(); - - // ----- Remove form the options list the first argument - array_shift($v_arg_list); - $v_size--; - - // ----- Look for first arg - if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { - - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, - array (PCLZIP_OPT_REMOVE_PATH => 'optional', - PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', - PCLZIP_OPT_ADD_PATH => 'optional', - PCLZIP_CB_PRE_ADD => 'optional', - PCLZIP_CB_POST_ADD => 'optional', - PCLZIP_OPT_NO_COMPRESSION => 'optional', - PCLZIP_OPT_COMMENT => 'optional', - PCLZIP_OPT_ADD_COMMENT => 'optional', - PCLZIP_OPT_PREPEND_COMMENT => 'optional', - PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', - PCLZIP_OPT_TEMP_FILE_ON => 'optional', - PCLZIP_OPT_TEMP_FILE_OFF => 'optional' - //, PCLZIP_OPT_CRYPT => 'optional' - )); - if ($v_result != 1) { - return 0; - } - } - - // ----- Look for 2 args - // Here we need to support the first historic synopsis of the - // method. - else { - - // ----- Get the first argument - $v_options[PCLZIP_OPT_ADD_PATH] = $v_add_path = $v_arg_list[0]; - - // ----- Look for the optional second argument - if ($v_size == 2) { - $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1]; - } - else if ($v_size > 2) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); - - // ----- Return - return 0; - } - } - } - - // ----- Look for default option values - $this->privOptionDefaultThreshold($v_options); - - // ----- Init - $v_string_list = array(); - $v_att_list = array(); - $v_filedescr_list = array(); - $p_result_list = array(); - - // ----- Look if the $p_filelist is really an array - if (is_array($p_filelist)) { - - // ----- Look if the first element is also an array - // This will mean that this is a file description entry - if (isset($p_filelist[0]) && is_array($p_filelist[0])) { - $v_att_list = $p_filelist; - } - - // ----- The list is a list of string names - else { - $v_string_list = $p_filelist; - } - } - - // ----- Look if the $p_filelist is a string - else if (is_string($p_filelist)) { - // ----- Create a list from the string - $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist); - } - - // ----- Invalid variable type for $p_filelist - else { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type '".gettype($p_filelist)."' for p_filelist"); - return 0; - } - - // ----- Reformat the string list - if (sizeof($v_string_list) != 0) { - foreach ($v_string_list as $v_string) { - $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string; - } - } - - // ----- For each file in the list check the attributes - $v_supported_attributes - = array ( PCLZIP_ATT_FILE_NAME => 'mandatory' - ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional' - ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional' - ,PCLZIP_ATT_FILE_MTIME => 'optional' - ,PCLZIP_ATT_FILE_CONTENT => 'optional' - ,PCLZIP_ATT_FILE_COMMENT => 'optional' - ); - foreach ($v_att_list as $v_entry) { - $v_result = $this->privFileDescrParseAtt($v_entry, - $v_filedescr_list[], - $v_options, - $v_supported_attributes); - if ($v_result != 1) { - return 0; - } - } - - // ----- Expand the filelist (expand directories) - $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options); - if ($v_result != 1) { - return 0; - } - - // ----- Call the create fct - $v_result = $this->privAdd($v_filedescr_list, $p_result_list, $v_options); - if ($v_result != 1) { - return 0; - } - - // ----- Return - return $p_result_list; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : listContent() - // Description : - // This public method, gives the list of the files and directories, with their - // properties. - // The properties of each entries in the list are (used also in other functions) : - // filename : Name of the file. For a create or add action it is the filename - // given by the user. For an extract function it is the filename - // of the extracted file. - // stored_filename : Name of the file / directory stored in the archive. - // size : Size of the stored file. - // compressed_size : Size of the file's data compressed in the archive - // (without the headers overhead) - // mtime : Last known modification date of the file (UNIX timestamp) - // comment : Comment associated with the file - // folder : true | false - // index : index of the file in the archive - // status : status of the action (depending of the action) : - // Values are : - // ok : OK ! - // filtered : the file / dir is not extracted (filtered by user) - // already_a_directory : the file can not be extracted because a - // directory with the same name already exists - // write_protected : the file can not be extracted because a file - // with the same name already exists and is - // write protected - // newer_exist : the file was not extracted because a newer file exists - // path_creation_fail : the file is not extracted because the folder - // does not exist and can not be created - // write_error : the file was not extracted because there was a - // error while writing the file - // read_error : the file was not extracted because there was a error - // while reading the file - // invalid_header : the file was not extracted because of an archive - // format error (bad file header) - // Note that each time a method can continue operating when there - // is an action error on a file, the error is only logged in the file status. - // Return Values : - // 0 on an unrecoverable failure, - // The list of the files in the archive. - // -------------------------------------------------------------------------------- - function listContent() - { - $v_result=1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Check archive - if (!$this->privCheckFormat()) { - return(0); - } - - // ----- Call the extracting fct - $p_list = array(); - if (($v_result = $this->privList($p_list)) != 1) - { - unset($p_list); - return(0); - } - - // ----- Return - return $p_list; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : - // extract($p_path="./", $p_remove_path="") - // extract([$p_option, $p_option_value, ...]) - // Description : - // This method supports two synopsis. The first one is historical. - // This method extract all the files / directories from the archive to the - // folder indicated in $p_path. - // If you want to ignore the 'root' part of path of the memorized files - // you can indicate this in the optional $p_remove_path parameter. - // By default, if a newer file with the same name already exists, the - // file is not extracted. - // - // If both PCLZIP_OPT_PATH and PCLZIP_OPT_ADD_PATH aoptions - // are used, the path indicated in PCLZIP_OPT_ADD_PATH is append - // at the end of the path value of PCLZIP_OPT_PATH. - // Parameters : - // $p_path : Path where the files and directories are to be extracted - // $p_remove_path : First part ('root' part) of the memorized path - // (if any similar) to remove while extracting. - // Options : - // PCLZIP_OPT_PATH : - // PCLZIP_OPT_ADD_PATH : - // PCLZIP_OPT_REMOVE_PATH : - // PCLZIP_OPT_REMOVE_ALL_PATH : - // PCLZIP_CB_PRE_EXTRACT : - // PCLZIP_CB_POST_EXTRACT : - // Return Values : - // 0 or a negative value on failure, - // The list of the extracted files, with a status of the action. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- - function extract() - { - $v_result=1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Check archive - if (!$this->privCheckFormat()) { - return(0); - } - - // ----- Set default values - $v_options = array(); -// $v_path = "./"; - $v_path = ''; - $v_remove_path = ""; - $v_remove_all_path = false; - - // ----- Look for variable options arguments - $v_size = func_num_args(); - - // ----- Default values for option - $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; - - // ----- Look for arguments - if ($v_size > 0) { - // ----- Get the arguments - $v_arg_list = func_get_args(); - - // ----- Look for first arg - if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { - - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, - array (PCLZIP_OPT_PATH => 'optional', - PCLZIP_OPT_REMOVE_PATH => 'optional', - PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', - PCLZIP_OPT_ADD_PATH => 'optional', - PCLZIP_CB_PRE_EXTRACT => 'optional', - PCLZIP_CB_POST_EXTRACT => 'optional', - PCLZIP_OPT_SET_CHMOD => 'optional', - PCLZIP_OPT_BY_NAME => 'optional', - PCLZIP_OPT_BY_EREG => 'optional', - PCLZIP_OPT_BY_PREG => 'optional', - PCLZIP_OPT_BY_INDEX => 'optional', - PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', - PCLZIP_OPT_EXTRACT_IN_OUTPUT => 'optional', - PCLZIP_OPT_REPLACE_NEWER => 'optional' - ,PCLZIP_OPT_STOP_ON_ERROR => 'optional' - ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional', - PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', - PCLZIP_OPT_TEMP_FILE_ON => 'optional', - PCLZIP_OPT_TEMP_FILE_OFF => 'optional' - )); - if ($v_result != 1) { - return 0; - } - - // ----- Set the arguments - if (isset($v_options[PCLZIP_OPT_PATH])) { - $v_path = $v_options[PCLZIP_OPT_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { - $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { - $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; - } - if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { - // ----- Check for '/' in last path char - if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { - $v_path .= '/'; - } - $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; - } - } - - // ----- Look for 2 args - // Here we need to support the first historic synopsis of the - // method. - else { - - // ----- Get the first argument - $v_path = $v_arg_list[0]; - - // ----- Look for the optional second argument - if ($v_size == 2) { - $v_remove_path = $v_arg_list[1]; - } - else if ($v_size > 2) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); - - // ----- Return - return 0; - } - } - } - - // ----- Look for default option values - $this->privOptionDefaultThreshold($v_options); - - // ----- Trace - - // ----- Call the extracting fct - $p_list = array(); - $v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, - $v_remove_all_path, $v_options); - if ($v_result < 1) { - unset($p_list); - return(0); - } - - // ----- Return - return $p_list; - } - // -------------------------------------------------------------------------------- - - - // -------------------------------------------------------------------------------- - // Function : - // extractByIndex($p_index, $p_path="./", $p_remove_path="") - // extractByIndex($p_index, [$p_option, $p_option_value, ...]) - // Description : - // This method supports two synopsis. The first one is historical. - // This method is doing a partial extract of the archive. - // The extracted files or folders are identified by their index in the - // archive (from 0 to n). - // Note that if the index identify a folder, only the folder entry is - // extracted, not all the files included in the archive. - // Parameters : - // $p_index : A single index (integer) or a string of indexes of files to - // extract. The form of the string is "0,4-6,8-12" with only numbers - // and '-' for range or ',' to separate ranges. No spaces or ';' - // are allowed. - // $p_path : Path where the files and directories are to be extracted - // $p_remove_path : First part ('root' part) of the memorized path - // (if any similar) to remove while extracting. - // Options : - // PCLZIP_OPT_PATH : - // PCLZIP_OPT_ADD_PATH : - // PCLZIP_OPT_REMOVE_PATH : - // PCLZIP_OPT_REMOVE_ALL_PATH : - // PCLZIP_OPT_EXTRACT_AS_STRING : The files are extracted as strings and - // not as files. - // The resulting content is in a new field 'content' in the file - // structure. - // This option must be used alone (any other options are ignored). - // PCLZIP_CB_PRE_EXTRACT : - // PCLZIP_CB_POST_EXTRACT : - // Return Values : - // 0 on failure, - // The list of the extracted files, with a status of the action. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- - //function extractByIndex($p_index, options...) - function extractByIndex($p_index) - { - $v_result=1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Check archive - if (!$this->privCheckFormat()) { - return(0); - } - - // ----- Set default values - $v_options = array(); -// $v_path = "./"; - $v_path = ''; - $v_remove_path = ""; - $v_remove_all_path = false; - - // ----- Look for variable options arguments - $v_size = func_num_args(); - - // ----- Default values for option - $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; - - // ----- Look for arguments - if ($v_size > 1) { - // ----- Get the arguments - $v_arg_list = func_get_args(); - - // ----- Remove form the options list the first argument - array_shift($v_arg_list); - $v_size--; - - // ----- Look for first arg - if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { - - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, - array (PCLZIP_OPT_PATH => 'optional', - PCLZIP_OPT_REMOVE_PATH => 'optional', - PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', - PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', - PCLZIP_OPT_ADD_PATH => 'optional', - PCLZIP_CB_PRE_EXTRACT => 'optional', - PCLZIP_CB_POST_EXTRACT => 'optional', - PCLZIP_OPT_SET_CHMOD => 'optional', - PCLZIP_OPT_REPLACE_NEWER => 'optional' - ,PCLZIP_OPT_STOP_ON_ERROR => 'optional' - ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional', - PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', - PCLZIP_OPT_TEMP_FILE_ON => 'optional', - PCLZIP_OPT_TEMP_FILE_OFF => 'optional' - )); - if ($v_result != 1) { - return 0; - } - - // ----- Set the arguments - if (isset($v_options[PCLZIP_OPT_PATH])) { - $v_path = $v_options[PCLZIP_OPT_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { - $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { - $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; - } - if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { - // ----- Check for '/' in last path char - if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { - $v_path .= '/'; - } - $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; - } - if (!isset($v_options[PCLZIP_OPT_EXTRACT_AS_STRING])) { - $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; - } - else { - } - } - - // ----- Look for 2 args - // Here we need to support the first historic synopsis of the - // method. - else { - - // ----- Get the first argument - $v_path = $v_arg_list[0]; - - // ----- Look for the optional second argument - if ($v_size == 2) { - $v_remove_path = $v_arg_list[1]; - } - else if ($v_size > 2) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); - - // ----- Return - return 0; - } - } - } - - // ----- Trace - - // ----- Trick - // Here I want to reuse extractByRule(), so I need to parse the $p_index - // with privParseOptions() - $v_arg_trick = array (PCLZIP_OPT_BY_INDEX, $p_index); - $v_options_trick = array(); - $v_result = $this->privParseOptions($v_arg_trick, sizeof($v_arg_trick), $v_options_trick, - array (PCLZIP_OPT_BY_INDEX => 'optional' )); - if ($v_result != 1) { - return 0; - } - $v_options[PCLZIP_OPT_BY_INDEX] = $v_options_trick[PCLZIP_OPT_BY_INDEX]; - - // ----- Look for default option values - $this->privOptionDefaultThreshold($v_options); - - // ----- Call the extracting fct - if (($v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options)) < 1) { - return(0); - } - - // ----- Return - return $p_list; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : - // delete([$p_option, $p_option_value, ...]) - // Description : - // This method removes files from the archive. - // If no parameters are given, then all the archive is emptied. - // Parameters : - // None or optional arguments. - // Options : - // PCLZIP_OPT_BY_INDEX : - // PCLZIP_OPT_BY_NAME : - // PCLZIP_OPT_BY_EREG : - // PCLZIP_OPT_BY_PREG : - // Return Values : - // 0 on failure, - // The list of the files which are still present in the archive. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- - function delete() - { - $v_result=1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Check archive - if (!$this->privCheckFormat()) { - return(0); - } - - // ----- Set default values - $v_options = array(); - - // ----- Look for variable options arguments - $v_size = func_num_args(); - - // ----- Look for arguments - if ($v_size > 0) { - // ----- Get the arguments - $v_arg_list = func_get_args(); - - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, - array (PCLZIP_OPT_BY_NAME => 'optional', - PCLZIP_OPT_BY_EREG => 'optional', - PCLZIP_OPT_BY_PREG => 'optional', - PCLZIP_OPT_BY_INDEX => 'optional' )); - if ($v_result != 1) { - return 0; - } - } - - // ----- Magic quotes trick - $this->privDisableMagicQuotes(); - - // ----- Call the delete fct - $v_list = array(); - if (($v_result = $this->privDeleteByRule($v_list, $v_options)) != 1) { - $this->privSwapBackMagicQuotes(); - unset($v_list); - return(0); - } - - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_list; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : deleteByIndex() - // Description : - // ***** Deprecated ***** - // delete(PCLZIP_OPT_BY_INDEX, $p_index) should be prefered. - // -------------------------------------------------------------------------------- - function deleteByIndex($p_index) - { - - $p_list = $this->delete(PCLZIP_OPT_BY_INDEX, $p_index); - - // ----- Return - return $p_list; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : properties() - // Description : - // This method gives the properties of the archive. - // The properties are : - // nb : Number of files in the archive - // comment : Comment associated with the archive file - // status : not_exist, ok - // Parameters : - // None - // Return Values : - // 0 on failure, - // An array with the archive properties. - // -------------------------------------------------------------------------------- - function properties() - { - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Magic quotes trick - $this->privDisableMagicQuotes(); - - // ----- Check archive - if (!$this->privCheckFormat()) { - $this->privSwapBackMagicQuotes(); - return(0); - } - - // ----- Default properties - $v_prop = array(); - $v_prop['comment'] = ''; - $v_prop['nb'] = 0; - $v_prop['status'] = 'not_exist'; - - // ----- Look if file exists - if (@is_file($this->zipname)) - { - // ----- Open the zip file - $this->zip_ftell = 0; - if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) - { - $this->privSwapBackMagicQuotes(); - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); - - // ----- Return - return 0; - } - - // ----- Read the central directory informations - $v_central_dir = array(); - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) - { - $this->privSwapBackMagicQuotes(); - return 0; - } - - // ----- Close the zip file - $this->privCloseFd(); - - // ----- Set the user attributes - $v_prop['comment'] = $v_central_dir['comment']; - $v_prop['nb'] = $v_central_dir['entries']; - $v_prop['status'] = 'ok'; - } - - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_prop; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : duplicate() - // Description : - // This method creates an archive by copying the content of an other one. If - // the archive already exist, it is replaced by the new one without any warning. - // Parameters : - // $p_archive : The filename of a valid archive, or - // a valid PclZip object. - // Return Values : - // 1 on success. - // 0 or a negative value on error (error code). - // -------------------------------------------------------------------------------- - function duplicate($p_archive) - { - $v_result = 1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Look if the $p_archive is a PclZip object - if ((is_object($p_archive)) && (get_class($p_archive) == 'pclzip')) - { - - // ----- Duplicate the archive - $v_result = $this->privDuplicate($p_archive->zipname); - } - - // ----- Look if the $p_archive is a string (so a filename) - else if (is_string($p_archive)) - { - - // ----- Check that $p_archive is a valid zip file - // TBC : Should also check the archive format - if (!is_file($p_archive)) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "No file with filename '".$p_archive."'"); - $v_result = PCLZIP_ERR_MISSING_FILE; - } - else { - // ----- Duplicate the archive - $v_result = $this->privDuplicate($p_archive); - } - } - - // ----- Invalid variable - else - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); - $v_result = PCLZIP_ERR_INVALID_PARAMETER; - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : merge() - // Description : - // This method merge the $p_archive_to_add archive at the end of the current - // one ($this). - // If the archive ($this) does not exist, the merge becomes a duplicate. - // If the $p_archive_to_add archive does not exist, the merge is a success. - // Parameters : - // $p_archive_to_add : It can be directly the filename of a valid zip archive, - // or a PclZip object archive. - // Return Values : - // 1 on success, - // 0 or negative values on error (see below). - // -------------------------------------------------------------------------------- - function merge($p_archive_to_add) - { - $v_result = 1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Check archive - if (!$this->privCheckFormat()) { - return(0); - } - - // ----- Look if the $p_archive_to_add is a PclZip object - if ((is_object($p_archive_to_add)) && (get_class($p_archive_to_add) == 'pclzip')) - { - - // ----- Merge the archive - $v_result = $this->privMerge($p_archive_to_add); - } - - // ----- Look if the $p_archive_to_add is a string (so a filename) - else if (is_string($p_archive_to_add)) - { - - // ----- Create a temporary archive - $v_object_archive = new PclZip($p_archive_to_add); - - // ----- Merge the archive - $v_result = $this->privMerge($v_object_archive); - } - - // ----- Invalid variable - else - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); - $v_result = PCLZIP_ERR_INVALID_PARAMETER; - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - - - // -------------------------------------------------------------------------------- - // Function : errorCode() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function errorCode() - { - if (PCLZIP_ERROR_EXTERNAL == 1) { - return(PclErrorCode()); - } - else { - return($this->error_code); - } - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : errorName() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function errorName($p_with_code=false) - { - $v_name = array ( PCLZIP_ERR_NO_ERROR => 'PCLZIP_ERR_NO_ERROR', - PCLZIP_ERR_WRITE_OPEN_FAIL => 'PCLZIP_ERR_WRITE_OPEN_FAIL', - PCLZIP_ERR_READ_OPEN_FAIL => 'PCLZIP_ERR_READ_OPEN_FAIL', - PCLZIP_ERR_INVALID_PARAMETER => 'PCLZIP_ERR_INVALID_PARAMETER', - PCLZIP_ERR_MISSING_FILE => 'PCLZIP_ERR_MISSING_FILE', - PCLZIP_ERR_FILENAME_TOO_LONG => 'PCLZIP_ERR_FILENAME_TOO_LONG', - PCLZIP_ERR_INVALID_ZIP => 'PCLZIP_ERR_INVALID_ZIP', - PCLZIP_ERR_BAD_EXTRACTED_FILE => 'PCLZIP_ERR_BAD_EXTRACTED_FILE', - PCLZIP_ERR_DIR_CREATE_FAIL => 'PCLZIP_ERR_DIR_CREATE_FAIL', - PCLZIP_ERR_BAD_EXTENSION => 'PCLZIP_ERR_BAD_EXTENSION', - PCLZIP_ERR_BAD_FORMAT => 'PCLZIP_ERR_BAD_FORMAT', - PCLZIP_ERR_DELETE_FILE_FAIL => 'PCLZIP_ERR_DELETE_FILE_FAIL', - PCLZIP_ERR_RENAME_FILE_FAIL => 'PCLZIP_ERR_RENAME_FILE_FAIL', - PCLZIP_ERR_BAD_CHECKSUM => 'PCLZIP_ERR_BAD_CHECKSUM', - PCLZIP_ERR_INVALID_ARCHIVE_ZIP => 'PCLZIP_ERR_INVALID_ARCHIVE_ZIP', - PCLZIP_ERR_MISSING_OPTION_VALUE => 'PCLZIP_ERR_MISSING_OPTION_VALUE', - PCLZIP_ERR_INVALID_OPTION_VALUE => 'PCLZIP_ERR_INVALID_OPTION_VALUE', - PCLZIP_ERR_UNSUPPORTED_COMPRESSION => 'PCLZIP_ERR_UNSUPPORTED_COMPRESSION', - PCLZIP_ERR_UNSUPPORTED_ENCRYPTION => 'PCLZIP_ERR_UNSUPPORTED_ENCRYPTION' - ,PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE => 'PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE' - ,PCLZIP_ERR_DIRECTORY_RESTRICTION => 'PCLZIP_ERR_DIRECTORY_RESTRICTION' - ); - - if (isset($v_name[$this->error_code])) { - $v_value = $v_name[$this->error_code]; - } - else { - $v_value = 'NoName'; - } - - if ($p_with_code) { - return($v_value.' ('.$this->error_code.')'); - } - else { - return($v_value); - } - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : errorInfo() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function errorInfo($p_full=false) - { - if (PCLZIP_ERROR_EXTERNAL == 1) { - return(PclErrorString()); - } - else { - if ($p_full) { - return($this->errorName(true)." : ".$this->error_string); - } - else { - return($this->error_string." [code ".$this->error_code."]"); - } - } - } - // -------------------------------------------------------------------------------- - - -// -------------------------------------------------------------------------------- -// ***** UNDER THIS LINE ARE DEFINED PRIVATE INTERNAL FUNCTIONS ***** -// ***** ***** -// ***** THESES FUNCTIONS MUST NOT BE USED DIRECTLY ***** -// -------------------------------------------------------------------------------- - - - - // -------------------------------------------------------------------------------- - // Function : privCheckFormat() - // Description : - // This method check that the archive exists and is a valid zip archive. - // Several level of check exists. (futur) - // Parameters : - // $p_level : Level of check. Default 0. - // 0 : Check the first bytes (magic codes) (default value)) - // 1 : 0 + Check the central directory (futur) - // 2 : 1 + Check each file header (futur) - // Return Values : - // true on success, - // false on error, the error code is set. - // -------------------------------------------------------------------------------- - function privCheckFormat($p_level=0) - { - $v_result = true; - - // ----- Reset the file system cache - clearstatcache(); - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Look if the file exits - if (!is_file($this->zipname)) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "Missing archive file '".$this->zipname."'"); - return(false); - } - - // ----- Check that the file is readeable - if (!is_readable($this->zipname)) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to read archive '".$this->zipname."'"); - return(false); - } - - // ----- Check the magic code - // TBC - - // ----- Check the central header - // TBC - - // ----- Check each file header - // TBC - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privParseOptions() - // Description : - // This internal methods reads the variable list of arguments ($p_options_list, - // $p_size) and generate an array with the options and values ($v_result_list). - // $v_requested_options contains the options that can be present and those that - // must be present. - // $v_requested_options is an array, with the option value as key, and 'optional', - // or 'mandatory' as value. - // Parameters : - // See above. - // Return Values : - // 1 on success. - // 0 on failure. - // -------------------------------------------------------------------------------- - function privParseOptions(&$p_options_list, $p_size, &$v_result_list, $v_requested_options=false) - { - $v_result=1; - - // ----- Read the options - $i=0; - while ($i<$p_size) { - - // ----- Check if the option is supported - if (!isset($v_requested_options[$p_options_list[$i]])) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid optional parameter '".$p_options_list[$i]."' for this method"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Look for next option - switch ($p_options_list[$i]) { - // ----- Look for options that request a path value - case PCLZIP_OPT_PATH : - case PCLZIP_OPT_REMOVE_PATH : - case PCLZIP_OPT_ADD_PATH : - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Get the value - $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE); - $i++; - break; - - case PCLZIP_OPT_TEMP_FILE_THRESHOLD : - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - return PclZip::errorCode(); - } - - // ----- Check for incompatible options - if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); - return PclZip::errorCode(); - } - - // ----- Check the value - $v_value = $p_options_list[$i+1]; - if ((!is_integer($v_value)) || ($v_value<0)) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Integer expected for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - return PclZip::errorCode(); - } - - // ----- Get the value (and convert it in bytes) - $v_result_list[$p_options_list[$i]] = $v_value*1048576; - $i++; - break; - - case PCLZIP_OPT_TEMP_FILE_ON : - // ----- Check for incompatible options - if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); - return PclZip::errorCode(); - } - - $v_result_list[$p_options_list[$i]] = true; - break; - - case PCLZIP_OPT_TEMP_FILE_OFF : - // ----- Check for incompatible options - if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_ON])) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_ON'"); - return PclZip::errorCode(); - } - // ----- Check for incompatible options - if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_THRESHOLD'"); - return PclZip::errorCode(); - } - - $v_result_list[$p_options_list[$i]] = true; - break; - - case PCLZIP_OPT_EXTRACT_DIR_RESTRICTION : - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Get the value - if ( is_string($p_options_list[$i+1]) - && ($p_options_list[$i+1] != '')) { - $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE); - $i++; - } - else { - } - break; - - // ----- Look for options that request an array of string for value - case PCLZIP_OPT_BY_NAME : - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Get the value - if (is_string($p_options_list[$i+1])) { - $v_result_list[$p_options_list[$i]][0] = $p_options_list[$i+1]; - } - else if (is_array($p_options_list[$i+1])) { - $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; - } - else { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - $i++; - break; - - // ----- Look for options that request an EREG or PREG expression - case PCLZIP_OPT_BY_EREG : - // ereg() is deprecated starting with PHP 5.3. Move PCLZIP_OPT_BY_EREG - // to PCLZIP_OPT_BY_PREG - $p_options_list[$i] = PCLZIP_OPT_BY_PREG; - case PCLZIP_OPT_BY_PREG : - //case PCLZIP_OPT_CRYPT : - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Get the value - if (is_string($p_options_list[$i+1])) { - $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; - } - else { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - $i++; - break; - - // ----- Look for options that takes a string - case PCLZIP_OPT_COMMENT : - case PCLZIP_OPT_ADD_COMMENT : - case PCLZIP_OPT_PREPEND_COMMENT : - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, - "Missing parameter value for option '" - .PclZipUtilOptionText($p_options_list[$i]) - ."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Get the value - if (is_string($p_options_list[$i+1])) { - $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; - } - else { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, - "Wrong parameter value for option '" - .PclZipUtilOptionText($p_options_list[$i]) - ."'"); - - // ----- Return - return PclZip::errorCode(); - } - $i++; - break; - - // ----- Look for options that request an array of index - case PCLZIP_OPT_BY_INDEX : - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Get the value - $v_work_list = array(); - if (is_string($p_options_list[$i+1])) { - - // ----- Remove spaces - $p_options_list[$i+1] = strtr($p_options_list[$i+1], ' ', ''); - - // ----- Parse items - $v_work_list = explode(",", $p_options_list[$i+1]); - } - else if (is_integer($p_options_list[$i+1])) { - $v_work_list[0] = $p_options_list[$i+1].'-'.$p_options_list[$i+1]; - } - else if (is_array($p_options_list[$i+1])) { - $v_work_list = $p_options_list[$i+1]; - } - else { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Value must be integer, string or array for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Reduce the index list - // each index item in the list must be a couple with a start and - // an end value : [0,3], [5-5], [8-10], ... - // ----- Check the format of each item - $v_sort_flag=false; - $v_sort_value=0; - for ($j=0; $j= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Get the value - $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; - $i++; - break; - - // ----- Look for options that request a call-back - case PCLZIP_CB_PRE_EXTRACT : - case PCLZIP_CB_POST_EXTRACT : - case PCLZIP_CB_PRE_ADD : - case PCLZIP_CB_POST_ADD : - /* for futur use - case PCLZIP_CB_PRE_DELETE : - case PCLZIP_CB_POST_DELETE : - case PCLZIP_CB_PRE_LIST : - case PCLZIP_CB_POST_LIST : - */ - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Get the value - $v_function_name = $p_options_list[$i+1]; - - // ----- Check that the value is a valid existing function - if (!function_exists($v_function_name)) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Function '".$v_function_name."()' is not an existing function for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Set the attribute - $v_result_list[$p_options_list[$i]] = $v_function_name; - $i++; - break; - - default : - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, - "Unknown parameter '" - .$p_options_list[$i]."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Next options - $i++; - } - - // ----- Look for mandatory options - if ($v_requested_options !== false) { - for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) { - // ----- Look for mandatory option - if ($v_requested_options[$key] == 'mandatory') { - // ----- Look if present - if (!isset($v_result_list[$key])) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")"); - - // ----- Return - return PclZip::errorCode(); - } - } - } - } - - // ----- Look for default values - if (!isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) { - - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privOptionDefaultThreshold() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privOptionDefaultThreshold(&$p_options) - { - $v_result=1; - - if (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) - || isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) { - return $v_result; - } - - // ----- Get 'memory_limit' configuration value - $v_memory_limit = ini_get('memory_limit'); - $v_memory_limit = trim($v_memory_limit); - $last = strtolower(substr($v_memory_limit, -1)); - - if($last == 'g') - //$v_memory_limit = $v_memory_limit*1024*1024*1024; - $v_memory_limit = $v_memory_limit*1073741824; - if($last == 'm') - //$v_memory_limit = $v_memory_limit*1024*1024; - $v_memory_limit = $v_memory_limit*1048576; - if($last == 'k') - $v_memory_limit = $v_memory_limit*1024; - - $p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] = floor($v_memory_limit*PCLZIP_TEMPORARY_FILE_RATIO); - - - // ----- Sanity check : No threshold if value lower than 1M - if ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] < 1048576) { - unset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]); - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privFileDescrParseAtt() - // Description : - // Parameters : - // Return Values : - // 1 on success. - // 0 on failure. - // -------------------------------------------------------------------------------- - function privFileDescrParseAtt(&$p_file_list, &$p_filedescr, $v_options, $v_requested_options=false) - { - $v_result=1; - - // ----- For each file in the list check the attributes - foreach ($p_file_list as $v_key => $v_value) { - - // ----- Check if the option is supported - if (!isset($v_requested_options[$v_key])) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file attribute '".$v_key."' for this file"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Look for attribute - switch ($v_key) { - case PCLZIP_ATT_FILE_NAME : - if (!is_string($v_value)) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); - return PclZip::errorCode(); - } - - $p_filedescr['filename'] = PclZipUtilPathReduction($v_value); - - if ($p_filedescr['filename'] == '') { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty filename for attribute '".PclZipUtilOptionText($v_key)."'"); - return PclZip::errorCode(); - } - - break; - - case PCLZIP_ATT_FILE_NEW_SHORT_NAME : - if (!is_string($v_value)) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); - return PclZip::errorCode(); - } - - $p_filedescr['new_short_name'] = PclZipUtilPathReduction($v_value); - - if ($p_filedescr['new_short_name'] == '') { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty short filename for attribute '".PclZipUtilOptionText($v_key)."'"); - return PclZip::errorCode(); - } - break; - - case PCLZIP_ATT_FILE_NEW_FULL_NAME : - if (!is_string($v_value)) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); - return PclZip::errorCode(); - } - - $p_filedescr['new_full_name'] = PclZipUtilPathReduction($v_value); - - if ($p_filedescr['new_full_name'] == '') { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty full filename for attribute '".PclZipUtilOptionText($v_key)."'"); - return PclZip::errorCode(); - } - break; - - // ----- Look for options that takes a string - case PCLZIP_ATT_FILE_COMMENT : - if (!is_string($v_value)) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); - return PclZip::errorCode(); - } - - $p_filedescr['comment'] = $v_value; - break; - - case PCLZIP_ATT_FILE_MTIME : - if (!is_integer($v_value)) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". Integer expected for attribute '".PclZipUtilOptionText($v_key)."'"); - return PclZip::errorCode(); - } - - $p_filedescr['mtime'] = $v_value; - break; - - case PCLZIP_ATT_FILE_CONTENT : - $p_filedescr['content'] = $v_value; - break; - - default : - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, - "Unknown parameter '".$v_key."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Look for mandatory options - if ($v_requested_options !== false) { - for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) { - // ----- Look for mandatory option - if ($v_requested_options[$key] == 'mandatory') { - // ----- Look if present - if (!isset($p_file_list[$key])) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")"); - return PclZip::errorCode(); - } - } - } - } - - // end foreach - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privFileDescrExpand() - // Description : - // This method look for each item of the list to see if its a file, a folder - // or a string to be added as file. For any other type of files (link, other) - // just ignore the item. - // Then prepare the information that will be stored for that file. - // When its a folder, expand the folder with all the files that are in that - // folder (recursively). - // Parameters : - // Return Values : - // 1 on success. - // 0 on failure. - // -------------------------------------------------------------------------------- - function privFileDescrExpand(&$p_filedescr_list, &$p_options) - { - $v_result=1; - - // ----- Create a result list - $v_result_list = array(); - - // ----- Look each entry - for ($i=0; $iprivCalculateStoredFilename($v_descr, $p_options); - - // ----- Add the descriptor in result list - $v_result_list[sizeof($v_result_list)] = $v_descr; - - // ----- Look for folder - if ($v_descr['type'] == 'folder') { - // ----- List of items in folder - $v_dirlist_descr = array(); - $v_dirlist_nb = 0; - if ($v_folder_handler = @opendir($v_descr['filename'])) { - while (($v_item_handler = @readdir($v_folder_handler)) !== false) { - - // ----- Skip '.' and '..' - if (($v_item_handler == '.') || ($v_item_handler == '..')) { - continue; - } - - // ----- Compose the full filename - $v_dirlist_descr[$v_dirlist_nb]['filename'] = $v_descr['filename'].'/'.$v_item_handler; - - // ----- Look for different stored filename - // Because the name of the folder was changed, the name of the - // files/sub-folders also change - if (($v_descr['stored_filename'] != $v_descr['filename']) - && (!isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH]))) { - if ($v_descr['stored_filename'] != '') { - $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_descr['stored_filename'].'/'.$v_item_handler; - } - else { - $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_item_handler; - } - } - - $v_dirlist_nb++; - } - - @closedir($v_folder_handler); - } - else { - // TBC : unable to open folder in read mode - } - - // ----- Expand each element of the list - if ($v_dirlist_nb != 0) { - // ----- Expand - if (($v_result = $this->privFileDescrExpand($v_dirlist_descr, $p_options)) != 1) { - return $v_result; - } - - // ----- Concat the resulting list - $v_result_list = array_merge($v_result_list, $v_dirlist_descr); - } - else { - } - - // ----- Free local array - unset($v_dirlist_descr); - } - } - - // ----- Get the result list - $p_filedescr_list = $v_result_list; - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privCreate() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privCreate($p_filedescr_list, &$p_result_list, &$p_options) - { - $v_result=1; - $v_list_detail = array(); - - // ----- Magic quotes trick - $this->privDisableMagicQuotes(); - - // ----- Open the file in write mode - if (($v_result = $this->privOpenFd('wb')) != 1) - { - // ----- Return - return $v_result; - } - - // ----- Add the list of files - $v_result = $this->privAddList($p_filedescr_list, $p_result_list, $p_options); - - // ----- Close - $this->privCloseFd(); - - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privAdd() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privAdd($p_filedescr_list, &$p_result_list, &$p_options) - { - $v_result=1; - $v_list_detail = array(); - - // ----- Look if the archive exists or is empty - if ((!is_file($this->zipname)) || (filesize($this->zipname) == 0)) - { - - // ----- Do a create - $v_result = $this->privCreate($p_filedescr_list, $p_result_list, $p_options); - - // ----- Return - return $v_result; - } - // ----- Magic quotes trick - $this->privDisableMagicQuotes(); - - // ----- Open the zip file - if (($v_result=$this->privOpenFd('rb')) != 1) - { - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - - // ----- Read the central directory informations - $v_central_dir = array(); - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) - { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - return $v_result; - } - - // ----- Go to beginning of File - @rewind($this->zip_fd); - $this->zip_ftell = 0; - - // ----- Creates a temporay file - $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; - - // ----- Open the temporary file in write mode - $v_zip_temp_ftell = 0; - if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) - { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode'); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Copy the files from the archive to the temporary file - // TBC : Here I should better append the file and go back to erase the central dir - $v_size = $v_central_dir['offset']; - $this->zip_ftell += $v_size; - $v_zip_temp_ftell += $v_size; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = fread($this->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Swap the file descriptor - // Here is a trick : I swap the temporary fd with the zip fd, in order to use - // the following methods on the temporary fil and not the real archive - $v_swap = $this->zip_fd; - $this->zip_fd = $v_zip_temp_fd; - $v_zip_temp_fd = $v_swap; - $v_swap = $this->zip_ftell; - $this->zip_ftell = $v_zip_temp_ftell; - $v_zip_temp_ftell = $v_swap; - - // ----- Add the files - $v_header_list = array(); - if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1) - { - fclose($v_zip_temp_fd); - $this->privCloseFd(); - @unlink($v_zip_temp_name); - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - - // ----- Store the offset of the central dir - $v_offset = $this->zip_ftell; - - // ----- Copy the block of file headers from the old archive - $v_size = $v_central_dir['size']; - $this->zip_ftell += $v_size; - $v_zip_temp_ftell += $v_size; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($v_zip_temp_fd, $v_read_size); - @fwrite($this->zip_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Create the Central Dir files header - for ($i=0, $v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { - fclose($v_zip_temp_fd); - $this->privCloseFd(); - @unlink($v_zip_temp_name); - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - $v_count++; - } - - // ----- Transform the header to a 'usable' info - $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); - } - - // ----- Zip file comment - $v_comment = $v_central_dir['comment']; - if (isset($p_options[PCLZIP_OPT_COMMENT])) { - $v_comment = $p_options[PCLZIP_OPT_COMMENT]; - } - if (isset($p_options[PCLZIP_OPT_ADD_COMMENT])) { - $v_comment = $v_comment.$p_options[PCLZIP_OPT_ADD_COMMENT]; - } - if (isset($p_options[PCLZIP_OPT_PREPEND_COMMENT])) { - $v_comment = $p_options[PCLZIP_OPT_PREPEND_COMMENT].$v_comment; - } - - // ----- Calculate the size of the central header - $v_size = $this->zip_ftell-$v_offset; - - // ----- Create the central dir footer - if (($v_result = $this->privWriteCentralHeader($v_count+$v_central_dir['entries'], $v_size, $v_offset, $v_comment)) != 1) - { - // ----- Reset the file list - unset($v_header_list); - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - - // ----- Swap back the file descriptor - $v_swap = $this->zip_fd; - $this->zip_fd = $v_zip_temp_fd; - $v_zip_temp_fd = $v_swap; - $v_swap = $this->zip_ftell; - $this->zip_ftell = $v_zip_temp_ftell; - $v_zip_temp_ftell = $v_swap; - - // ----- Close - $this->privCloseFd(); - - // ----- Close the temporary file - @fclose($v_zip_temp_fd); - - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Delete the zip file - // TBC : I should test the result ... - @unlink($this->zipname); - - // ----- Rename the temporary file - // TBC : I should test the result ... - //@rename($v_zip_temp_name, $this->zipname); - PclZipUtilRename($v_zip_temp_name, $this->zipname); - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privOpenFd() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function privOpenFd($p_mode) - { - $v_result=1; - - // ----- Look if already open - if ($this->zip_fd != 0) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Zip file \''.$this->zipname.'\' already open'); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Open the zip file - $this->zip_ftell = 0; - if (($this->zip_fd = @fopen($this->zipname, $p_mode)) == 0) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in '.$p_mode.' mode'); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privCloseFd() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function privCloseFd() - { - $v_result=1; - - if ($this->zip_fd != 0) - @fclose($this->zip_fd); - $this->zip_fd = 0; - $this->zip_ftell = 0; - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privAddList() - // Description : - // $p_add_dir and $p_remove_dir will give the ability to memorize a path which is - // different from the real path of the file. This is usefull if you want to have PclTar - // running in any directory, and memorize relative path from an other directory. - // Parameters : - // $p_list : An array containing the file or directory names to add in the tar - // $p_result_list : list of added files with their properties (specially the status field) - // $p_add_dir : Path to add in the filename path archived - // $p_remove_dir : Path to remove in the filename path archived - // Return Values : - // -------------------------------------------------------------------------------- -// function privAddList($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options) - function privAddList($p_filedescr_list, &$p_result_list, &$p_options) - { - $v_result=1; - - // ----- Add the files - $v_header_list = array(); - if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1) - { - // ----- Return - return $v_result; - } - - // ----- Store the offset of the central dir - $v_offset = $this->zip_ftell; - - // ----- Create the Central Dir files header - for ($i=0,$v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { - // ----- Return - return $v_result; - } - $v_count++; - } - - // ----- Transform the header to a 'usable' info - $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); - } - - // ----- Zip file comment - $v_comment = ''; - if (isset($p_options[PCLZIP_OPT_COMMENT])) { - $v_comment = $p_options[PCLZIP_OPT_COMMENT]; - } - - // ----- Calculate the size of the central header - $v_size = $this->zip_ftell-$v_offset; - - // ----- Create the central dir footer - if (($v_result = $this->privWriteCentralHeader($v_count, $v_size, $v_offset, $v_comment)) != 1) - { - // ----- Reset the file list - unset($v_header_list); - - // ----- Return - return $v_result; - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privAddFileList() - // Description : - // Parameters : - // $p_filedescr_list : An array containing the file description - // or directory names to add in the zip - // $p_result_list : list of added files with their properties (specially the status field) - // Return Values : - // -------------------------------------------------------------------------------- - function privAddFileList($p_filedescr_list, &$p_result_list, &$p_options) - { - $v_result=1; - $v_header = array(); - - // ----- Recuperate the current number of elt in list - $v_nb = sizeof($p_result_list); - - // ----- Loop on the files - for ($j=0; ($jprivAddFile($p_filedescr_list[$j], $v_header, - $p_options); - if ($v_result != 1) { - return $v_result; - } - - // ----- Store the file infos - $p_result_list[$v_nb++] = $v_header; - } - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privAddFile() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privAddFile($p_filedescr, &$p_header, &$p_options) - { - $v_result=1; - - // ----- Working variable - $p_filename = $p_filedescr['filename']; - - // TBC : Already done in the fileAtt check ... ? - if ($p_filename == "") { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file list parameter (invalid or empty list)"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Look for a stored different filename - /* TBC : Removed - if (isset($p_filedescr['stored_filename'])) { - $v_stored_filename = $p_filedescr['stored_filename']; - } - else { - $v_stored_filename = $p_filedescr['stored_filename']; - } - */ - - // ----- Set the file properties - clearstatcache(); - $p_header['version'] = 20; - $p_header['version_extracted'] = 10; - $p_header['flag'] = 0; - $p_header['compression'] = 0; - $p_header['crc'] = 0; - $p_header['compressed_size'] = 0; - $p_header['filename_len'] = strlen($p_filename); - $p_header['extra_len'] = 0; - $p_header['disk'] = 0; - $p_header['internal'] = 0; - $p_header['offset'] = 0; - $p_header['filename'] = $p_filename; -// TBC : Removed $p_header['stored_filename'] = $v_stored_filename; - $p_header['stored_filename'] = $p_filedescr['stored_filename']; - $p_header['extra'] = ''; - $p_header['status'] = 'ok'; - $p_header['index'] = -1; - - // ----- Look for regular file - if ($p_filedescr['type']=='file') { - $p_header['external'] = 0x00000000; - $p_header['size'] = filesize($p_filename); - } - - // ----- Look for regular folder - else if ($p_filedescr['type']=='folder') { - $p_header['external'] = 0x00000010; - $p_header['mtime'] = filemtime($p_filename); - $p_header['size'] = filesize($p_filename); - } - - // ----- Look for virtual file - else if ($p_filedescr['type'] == 'virtual_file') { - $p_header['external'] = 0x00000000; - $p_header['size'] = strlen($p_filedescr['content']); - } - - - // ----- Look for filetime - if (isset($p_filedescr['mtime'])) { - $p_header['mtime'] = $p_filedescr['mtime']; - } - else if ($p_filedescr['type'] == 'virtual_file') { - $p_header['mtime'] = time(); - } - else { - $p_header['mtime'] = filemtime($p_filename); - } - - // ------ Look for file comment - if (isset($p_filedescr['comment'])) { - $p_header['comment_len'] = strlen($p_filedescr['comment']); - $p_header['comment'] = $p_filedescr['comment']; - } - else { - $p_header['comment_len'] = 0; - $p_header['comment'] = ''; - } - - // ----- Look for pre-add callback - if (isset($p_options[PCLZIP_CB_PRE_ADD])) { - - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_header, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. -// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_ADD].'(PCLZIP_CB_PRE_ADD, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_PRE_ADD](PCLZIP_CB_PRE_ADD, $v_local_header); - if ($v_result == 0) { - // ----- Change the file status - $p_header['status'] = "skipped"; - $v_result = 1; - } - - // ----- Update the informations - // Only some fields can be modified - if ($p_header['stored_filename'] != $v_local_header['stored_filename']) { - $p_header['stored_filename'] = PclZipUtilPathReduction($v_local_header['stored_filename']); - } - } - - // ----- Look for empty stored filename - if ($p_header['stored_filename'] == "") { - $p_header['status'] = "filtered"; - } - - // ----- Check the path length - if (strlen($p_header['stored_filename']) > 0xFF) { - $p_header['status'] = 'filename_too_long'; - } - - // ----- Look if no error, or file not skipped - if ($p_header['status'] == 'ok') { - - // ----- Look for a file - if ($p_filedescr['type'] == 'file') { - // ----- Look for using temporary file to zip - if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) - && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) - || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) - && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_header['size'])) ) ) { - $v_result = $this->privAddFileUsingTempFile($p_filedescr, $p_header, $p_options); - if ($v_result < PCLZIP_ERR_NO_ERROR) { - return $v_result; - } - } - - // ----- Use "in memory" zip algo - else { - - // ----- Open the source file - if (($v_file = @fopen($p_filename, "rb")) == 0) { - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); - return PclZip::errorCode(); - } - - // ----- Read the file content - $v_content = @fread($v_file, $p_header['size']); - - // ----- Close the file - @fclose($v_file); - - // ----- Calculate the CRC - $p_header['crc'] = @crc32($v_content); - - // ----- Look for no compression - if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { - // ----- Set header parameters - $p_header['compressed_size'] = $p_header['size']; - $p_header['compression'] = 0; - } - - // ----- Look for normal compression - else { - // ----- Compress the content - $v_content = @gzdeflate($v_content); - - // ----- Set header parameters - $p_header['compressed_size'] = strlen($v_content); - $p_header['compression'] = 8; - } - - // ----- Call the header generation - if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { - @fclose($v_file); - return $v_result; - } - - // ----- Write the compressed (or not) content - @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']); - $this->zip_ftell += $p_header['compressed_size']; - - } - - } - - // ----- Look for a virtual file (a file from string) - else if ($p_filedescr['type'] == 'virtual_file') { - - $v_content = $p_filedescr['content']; - - // ----- Calculate the CRC - $p_header['crc'] = @crc32($v_content); - - // ----- Look for no compression - if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { - // ----- Set header parameters - $p_header['compressed_size'] = $p_header['size']; - $p_header['compression'] = 0; - } - - // ----- Look for normal compression - else { - // ----- Compress the content - $v_content = @gzdeflate($v_content); - - // ----- Set header parameters - $p_header['compressed_size'] = strlen($v_content); - $p_header['compression'] = 8; - } - - // ----- Call the header generation - if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { - @fclose($v_file); - return $v_result; - } - - // ----- Write the compressed (or not) content - @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']); - $this->zip_ftell += $p_header['compressed_size']; - } - - // ----- Look for a directory - else if ($p_filedescr['type'] == 'folder') { - // ----- Look for directory last '/' - if (@substr($p_header['stored_filename'], -1) != '/') { - $p_header['stored_filename'] .= '/'; - } - - // ----- Set the file properties - $p_header['size'] = 0; - //$p_header['external'] = 0x41FF0010; // Value for a folder : to be checked - $p_header['external'] = 0x00000010; // Value for a folder : to be checked - - // ----- Call the header generation - if (($v_result = $this->privWriteFileHeader($p_header)) != 1) - { - return $v_result; - } - } - } - - // ----- Look for post-add callback - if (isset($p_options[PCLZIP_CB_POST_ADD])) { - - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_header, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. -// eval('$v_result = '.$p_options[PCLZIP_CB_POST_ADD].'(PCLZIP_CB_POST_ADD, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_POST_ADD](PCLZIP_CB_POST_ADD, $v_local_header); - if ($v_result == 0) { - // ----- Ignored - $v_result = 1; - } - - // ----- Update the informations - // Nothing can be modified - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privAddFileUsingTempFile() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privAddFileUsingTempFile($p_filedescr, &$p_header, &$p_options) - { - $v_result=PCLZIP_ERR_NO_ERROR; - - // ----- Working variable - $p_filename = $p_filedescr['filename']; - - - // ----- Open the source file - if (($v_file = @fopen($p_filename, "rb")) == 0) { - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); - return PclZip::errorCode(); - } - - // ----- Creates a compressed temporary file - $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz'; - if(function_exists('gzopen64')){ - $v_file_compressed = @gzopen64($v_gzip_temp_name, "wb"); - }else{ - $v_file_compressed = @gzopen($v_gzip_temp_name, "wb"); - } - if ($v_file_compressed == 0) { - fclose($v_file); - PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode'); - return PclZip::errorCode(); - } - - // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks - $v_size = filesize($p_filename); - while ($v_size != 0) { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($v_file, $v_read_size); - //$v_binary_data = pack('a'.$v_read_size, $v_buffer); - @gzputs($v_file_compressed, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Close the file - @fclose($v_file); - @gzclose($v_file_compressed); - - // ----- Check the minimum file size - if (filesize($v_gzip_temp_name) < 18) { - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'gzip temporary file \''.$v_gzip_temp_name.'\' has invalid filesize - should be minimum 18 bytes'); - return PclZip::errorCode(); - } - - // ----- Extract the compressed attributes - if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) { - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); - return PclZip::errorCode(); - } - - // ----- Read the gzip file header - $v_binary_data = @fread($v_file_compressed, 10); - $v_data_header = unpack('a1id1/a1id2/a1cm/a1flag/Vmtime/a1xfl/a1os', $v_binary_data); - - // ----- Check some parameters - $v_data_header['os'] = bin2hex($v_data_header['os']); - - // ----- Read the gzip file footer - @fseek($v_file_compressed, filesize($v_gzip_temp_name)-8); - $v_binary_data = @fread($v_file_compressed, 8); - $v_data_footer = unpack('Vcrc/Vcompressed_size', $v_binary_data); - - // ----- Set the attributes - $p_header['compression'] = ord($v_data_header['cm']); - //$p_header['mtime'] = $v_data_header['mtime']; - $p_header['crc'] = $v_data_footer['crc']; - $p_header['compressed_size'] = filesize($v_gzip_temp_name)-18; - - // ----- Close the file - @fclose($v_file_compressed); - - // ----- Call the header generation - if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { - return $v_result; - } - - // ----- Add the compressed data - if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) - { - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); - return PclZip::errorCode(); - } - - // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks - fseek($v_file_compressed, 10); - $v_size = $p_header['compressed_size']; - $this->zip_ftell += $v_size; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($v_file_compressed, $v_read_size); - //$v_binary_data = pack('a'.$v_read_size, $v_buffer); - @fwrite($this->zip_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Close the file - @fclose($v_file_compressed); - - // ----- Unlink the temporary file - @unlink($v_gzip_temp_name); - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privCalculateStoredFilename() - // Description : - // Based on file descriptor properties and global options, this method - // calculate the filename that will be stored in the archive. - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privCalculateStoredFilename(&$p_filedescr, &$p_options) - { - $v_result=1; - - // ----- Working variables - $p_filename = $p_filedescr['filename']; - if (isset($p_options[PCLZIP_OPT_ADD_PATH])) { - $p_add_dir = $p_options[PCLZIP_OPT_ADD_PATH]; - } - else { - $p_add_dir = ''; - } - if (isset($p_options[PCLZIP_OPT_REMOVE_PATH])) { - $p_remove_dir = $p_options[PCLZIP_OPT_REMOVE_PATH]; - } - else { - $p_remove_dir = ''; - } - if (isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { - $p_remove_all_dir = $p_options[PCLZIP_OPT_REMOVE_ALL_PATH]; - } - else { - $p_remove_all_dir = 0; - } - - - // ----- Look for full name change - if (isset($p_filedescr['new_full_name'])) { - // ----- Remove drive letter if any - $v_stored_filename = PclZipUtilTranslateWinPath($p_filedescr['new_full_name']); - } - - // ----- Look for path and/or short name change - else { - - // ----- Look for short name change - // Its when we cahnge just the filename but not the path - if (isset($p_filedescr['new_short_name'])) { - $v_path_info = pathinfo($p_filename); - $v_dir = ''; - if ($v_path_info['dirname'] != '') { - $v_dir = $v_path_info['dirname'].'/'; - } - $v_stored_filename = $v_dir.$p_filedescr['new_short_name']; - } - else { - // ----- Calculate the stored filename - $v_stored_filename = $p_filename; - } - - // ----- Look for all path to remove - if ($p_remove_all_dir) { - $v_stored_filename = basename($p_filename); - } - // ----- Look for partial path remove - else if ($p_remove_dir != "") { - if (substr($p_remove_dir, -1) != '/') - $p_remove_dir .= "/"; - - if ( (substr($p_filename, 0, 2) == "./") - || (substr($p_remove_dir, 0, 2) == "./")) { - - if ( (substr($p_filename, 0, 2) == "./") - && (substr($p_remove_dir, 0, 2) != "./")) { - $p_remove_dir = "./".$p_remove_dir; - } - if ( (substr($p_filename, 0, 2) != "./") - && (substr($p_remove_dir, 0, 2) == "./")) { - $p_remove_dir = substr($p_remove_dir, 2); - } - } - - $v_compare = PclZipUtilPathInclusion($p_remove_dir, - $v_stored_filename); - if ($v_compare > 0) { - if ($v_compare == 2) { - $v_stored_filename = ""; - } - else { - $v_stored_filename = substr($v_stored_filename, - strlen($p_remove_dir)); - } - } - } - - // ----- Remove drive letter if any - $v_stored_filename = PclZipUtilTranslateWinPath($v_stored_filename); - - // ----- Look for path to add - if ($p_add_dir != "") { - if (substr($p_add_dir, -1) == "/") - $v_stored_filename = $p_add_dir.$v_stored_filename; - else - $v_stored_filename = $p_add_dir."/".$v_stored_filename; - } - } - - // ----- Filename (reduce the path of stored name) - $v_stored_filename = PclZipUtilPathReduction($v_stored_filename); - $p_filedescr['stored_filename'] = $v_stored_filename; - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privWriteFileHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privWriteFileHeader(&$p_header) - { - $v_result=1; - - // ----- Store the offset position of the file - $p_header['offset'] = $this->zip_ftell; - - // ----- Transform UNIX mtime to DOS format mdate/mtime - $v_date = getdate($p_header['mtime']); - $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; - $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; - - // ----- Packed data - $v_binary_data = pack("VvvvvvVVVvv", 0x04034b50, - $p_header['version_extracted'], $p_header['flag'], - $p_header['compression'], $v_mtime, $v_mdate, - $p_header['crc'], $p_header['compressed_size'], - $p_header['size'], - strlen($p_header['stored_filename']), - $p_header['extra_len']); - - // ----- Write the first 148 bytes of the header in the archive - fputs($this->zip_fd, $v_binary_data, 30); - $this->zip_ftell += 30; - - // ----- Write the variable fields - if (strlen($p_header['stored_filename']) != 0) - { - fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); - $this->zip_ftell += strlen($p_header['stored_filename']); - } - if ($p_header['extra_len'] != 0) - { - fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); - $this->zip_ftell += $p_header['extra_len']; - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privWriteCentralFileHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privWriteCentralFileHeader(&$p_header) - { - $v_result=1; - - // TBC - //for(reset($p_header); $key = key($p_header); next($p_header)) { - //} - - // ----- Transform UNIX mtime to DOS format mdate/mtime - $v_date = getdate($p_header['mtime']); - $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; - $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; - - - // ----- Packed data - $v_binary_data = pack("VvvvvvvVVVvvvvvVV", 0x02014b50, - $p_header['version'], $p_header['version_extracted'], - $p_header['flag'], $p_header['compression'], - $v_mtime, $v_mdate, $p_header['crc'], - $p_header['compressed_size'], $p_header['size'], - strlen($p_header['stored_filename']), - $p_header['extra_len'], $p_header['comment_len'], - $p_header['disk'], $p_header['internal'], - $p_header['external'], $p_header['offset']); - - // ----- Write the 42 bytes of the header in the zip file - fputs($this->zip_fd, $v_binary_data, 46); - $this->zip_ftell += 46; - - // ----- Write the variable fields - if (strlen($p_header['stored_filename']) != 0) - { - fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); - $this->zip_ftell += strlen($p_header['stored_filename']); - } - if ($p_header['extra_len'] != 0) - { - fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); - $this->zip_ftell += $p_header['extra_len']; - } - if ($p_header['comment_len'] != 0) - { - fputs($this->zip_fd, $p_header['comment'], $p_header['comment_len']); - $this->zip_ftell += $p_header['comment_len']; - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privWriteCentralHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privWriteCentralHeader($p_nb_entries, $p_size, $p_offset, $p_comment) - { - $v_result=1; - - // ----- Packed data - $v_binary_data = pack("VvvvvVVv", 0x06054b50, 0, 0, $p_nb_entries, - $p_nb_entries, $p_size, - $p_offset, strlen($p_comment)); - - // ----- Write the 22 bytes of the header in the zip file - fputs($this->zip_fd, $v_binary_data, 22); - $this->zip_ftell += 22; - - // ----- Write the variable fields - if (strlen($p_comment) != 0) - { - fputs($this->zip_fd, $p_comment, strlen($p_comment)); - $this->zip_ftell += strlen($p_comment); - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privList() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privList(&$p_list) - { - $v_result=1; - - // ----- Magic quotes trick - $this->privDisableMagicQuotes(); - - // ----- Open the zip file - $this->zip_ftell = 0; - if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) - { - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Read the central directory informations - $v_central_dir = array(); - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) - { - $this->privSwapBackMagicQuotes(); - return $v_result; - } - - // ----- Go to beginning of Central Dir - @rewind($this->zip_fd); - $this->zip_ftell = 0; - if (@fseek($this->zip_fd, $v_central_dir['offset'])) - { - $this->privSwapBackMagicQuotes(); - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); - - // ----- Return - return PclZip::errorCode(); - } - $this->zip_ftell = $v_central_dir['offset']; - - // ----- Read each entry - for ($i=0; $i<$v_central_dir['entries']; $i++) - { - // ----- Read the file header - if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) - { - $this->privSwapBackMagicQuotes(); - return $v_result; - } - $v_header['index'] = $i; - - // ----- Get the only interesting attributes - $this->privConvertHeader2FileInfo($v_header, $p_list[$i]); - unset($v_header); - } - - // ----- Close the zip file - $this->privCloseFd(); - - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privConvertHeader2FileInfo() - // Description : - // This function takes the file informations from the central directory - // entries and extract the interesting parameters that will be given back. - // The resulting file infos are set in the array $p_info - // $p_info['filename'] : Filename with full path. Given by user (add), - // extracted in the filesystem (extract). - // $p_info['stored_filename'] : Stored filename in the archive. - // $p_info['size'] = Size of the file. - // $p_info['compressed_size'] = Compressed size of the file. - // $p_info['mtime'] = Last modification date of the file. - // $p_info['comment'] = Comment associated with the file. - // $p_info['folder'] = true/false : indicates if the entry is a folder or not. - // $p_info['status'] = status of the action on the file. - // $p_info['crc'] = CRC of the file content. - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privConvertHeader2FileInfo($p_header, &$p_info) - { - $v_result=1; - - // ----- Get the interesting attributes - $v_temp_path = PclZipUtilPathReduction($p_header['filename']); - $p_info['filename'] = $v_temp_path; - $v_temp_path = PclZipUtilPathReduction($p_header['stored_filename']); - $p_info['stored_filename'] = $v_temp_path; - $p_info['size'] = $p_header['size']; - $p_info['compressed_size'] = $p_header['compressed_size']; - $p_info['mtime'] = $p_header['mtime']; - $p_info['comment'] = $p_header['comment']; - $p_info['folder'] = (($p_header['external']&0x00000010)==0x00000010); - $p_info['index'] = $p_header['index']; - $p_info['status'] = $p_header['status']; - $p_info['crc'] = $p_header['crc']; - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privExtractByRule() - // Description : - // Extract a file or directory depending of rules (by index, by name, ...) - // Parameters : - // $p_file_list : An array where will be placed the properties of each - // extracted file - // $p_path : Path to add while writing the extracted files - // $p_remove_path : Path to remove (from the file memorized path) while writing the - // extracted files. If the path does not match the file path, - // the file is extracted with its memorized path. - // $p_remove_path does not apply to 'list' mode. - // $p_path and $p_remove_path are commulative. - // Return Values : - // 1 on success,0 or less on error (see error code list) - // -------------------------------------------------------------------------------- - function privExtractByRule(&$p_file_list, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) - { - $v_result=1; - - // ----- Magic quotes trick - $this->privDisableMagicQuotes(); - - // ----- Check the path - if ( ($p_path == "") - || ( (substr($p_path, 0, 1) != "/") - && (substr($p_path, 0, 3) != "../") - && (substr($p_path,1,2)!=":/"))) - $p_path = "./".$p_path; - - // ----- Reduce the path last (and duplicated) '/' - if (($p_path != "./") && ($p_path != "/")) - { - // ----- Look for the path end '/' - while (substr($p_path, -1) == "/") - { - $p_path = substr($p_path, 0, strlen($p_path)-1); - } - } - - // ----- Look for path to remove format (should end by /) - if (($p_remove_path != "") && (substr($p_remove_path, -1) != '/')) - { - $p_remove_path .= '/'; - } - $p_remove_path_size = strlen($p_remove_path); - - // ----- Open the zip file - if (($v_result = $this->privOpenFd('rb')) != 1) - { - $this->privSwapBackMagicQuotes(); - return $v_result; - } - - // ----- Read the central directory informations - $v_central_dir = array(); - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) - { - // ----- Close the zip file - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - return $v_result; - } - - // ----- Start at beginning of Central Dir - $v_pos_entry = $v_central_dir['offset']; - - // ----- Read each entry - $j_start = 0; - for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) - { - - // ----- Read next Central dir entry - @rewind($this->zip_fd); - $this->zip_ftell = 0; - if (@fseek($this->zip_fd, $v_pos_entry)) - { - // ----- Close the zip file - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); - - // ----- Return - return PclZip::errorCode(); - } - $this->zip_ftell = $v_pos_entry; - - // ----- Read the file header - $v_header = array(); - if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) - { - // ----- Close the zip file - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - return $v_result; - } - - // ----- Store the index - $v_header['index'] = $i; - - // ----- Store the file position - $v_pos_entry = $this->zip_ftell; - - // ----- Look for the specific extract rules - $v_extract = false; - - // ----- Look for extract by name rule - if ( (isset($p_options[PCLZIP_OPT_BY_NAME])) - && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { - - // ----- Look if the filename is in the list - for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) - && (substr($v_header['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { - $v_extract = true; - } - } - // ----- Look for a filename - elseif ($v_header['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { - $v_extract = true; - } - } - } - - // ----- Look for extract by ereg rule - // ereg() is deprecated with PHP 5.3 - /* - else if ( (isset($p_options[PCLZIP_OPT_BY_EREG])) - && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { - - if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header['stored_filename'])) { - $v_extract = true; - } - } - */ - - // ----- Look for extract by preg rule - else if ( (isset($p_options[PCLZIP_OPT_BY_PREG])) - && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { - - if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header['stored_filename'])) { - $v_extract = true; - } - } - - // ----- Look for extract by index rule - else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX])) - && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { - - // ----- Look if the index is in the list - for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { - $v_extract = true; - } - if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { - $j_start = $j+1; - } - - if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) { - break; - } - } - } - - // ----- Look for no rule, which means extract all the archive - else { - $v_extract = true; - } - - // ----- Check compression method - if ( ($v_extract) - && ( ($v_header['compression'] != 8) - && ($v_header['compression'] != 0))) { - $v_header['status'] = 'unsupported_compression'; - - // ----- Look for PCLZIP_OPT_STOP_ON_ERROR - if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) - && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { - - $this->privSwapBackMagicQuotes(); - - PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_COMPRESSION, - "Filename '".$v_header['stored_filename']."' is " - ."compressed by an unsupported compression " - ."method (".$v_header['compression'].") "); - - return PclZip::errorCode(); - } - } - - // ----- Check encrypted files - if (($v_extract) && (($v_header['flag'] & 1) == 1)) { - $v_header['status'] = 'unsupported_encryption'; - - // ----- Look for PCLZIP_OPT_STOP_ON_ERROR - if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) - && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { - - $this->privSwapBackMagicQuotes(); - - PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, - "Unsupported encryption for " - ." filename '".$v_header['stored_filename'] - ."'"); - - return PclZip::errorCode(); - } - } - - // ----- Look for real extraction - if (($v_extract) && ($v_header['status'] != 'ok')) { - $v_result = $this->privConvertHeader2FileInfo($v_header, - $p_file_list[$v_nb_extracted++]); - if ($v_result != 1) { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - return $v_result; - } - - $v_extract = false; - } - - // ----- Look for real extraction - if ($v_extract) - { - - // ----- Go to the file position - @rewind($this->zip_fd); - $this->zip_ftell = 0; - if (@fseek($this->zip_fd, $v_header['offset'])) - { - // ----- Close the zip file - $this->privCloseFd(); - - $this->privSwapBackMagicQuotes(); - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); - - // ----- Return - return PclZip::errorCode(); - } - $this->zip_ftell = $v_header['offset']; - - // ----- Look for extraction as string - if ($p_options[PCLZIP_OPT_EXTRACT_AS_STRING]) { - - $v_string = ''; - - // ----- Extracting the file - $v_result1 = $this->privExtractFileAsString($v_header, $v_string, $p_options); - if ($v_result1 < 1) { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - return $v_result1; - } - - // ----- Get the only interesting attributes - if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted])) != 1) - { - // ----- Close the zip file - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - return $v_result; - } - - // ----- Set the file content - $p_file_list[$v_nb_extracted]['content'] = $v_string; - - // ----- Next extracted file - $v_nb_extracted++; - - // ----- Look for user callback abort - if ($v_result1 == 2) { - break; - } - } - // ----- Look for extraction in standard output - elseif ( (isset($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) - && ($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) { - // ----- Extracting the file in standard output - $v_result1 = $this->privExtractFileInOutput($v_header, $p_options); - if ($v_result1 < 1) { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - return $v_result1; - } - - // ----- Get the only interesting attributes - if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - return $v_result; - } - - // ----- Look for user callback abort - if ($v_result1 == 2) { - break; - } - } - // ----- Look for normal extraction - else { - // ----- Extracting the file - $v_result1 = $this->privExtractFile($v_header, - $p_path, $p_remove_path, - $p_remove_all_path, - $p_options); - if ($v_result1 < 1) { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - return $v_result1; - } - - // ----- Get the only interesting attributes - if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) - { - // ----- Close the zip file - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - return $v_result; - } - - // ----- Look for user callback abort - if ($v_result1 == 2) { - break; - } - } - } - } - - // ----- Close the zip file - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privExtractFile() - // Description : - // Parameters : - // Return Values : - // - // 1 : ... ? - // PCLZIP_ERR_USER_ABORTED(2) : User ask for extraction stop in callback - // -------------------------------------------------------------------------------- - function privExtractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) - { - $v_result=1; - - // ----- Read the file header - if (($v_result = $this->privReadFileHeader($v_header)) != 1) - { - // ----- Return - return $v_result; - } - - - // ----- Check that the file header is coherent with $p_entry info - if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { - // TBC - } - - // ----- Look for all path to remove - if ($p_remove_all_path == true) { - // ----- Look for folder entry that not need to be extracted - if (($p_entry['external']&0x00000010)==0x00000010) { - - $p_entry['status'] = "filtered"; - - return $v_result; - } - - // ----- Get the basename of the path - $p_entry['filename'] = basename($p_entry['filename']); - } - - // ----- Look for path to remove - else if ($p_remove_path != "") - { - if (PclZipUtilPathInclusion($p_remove_path, $p_entry['filename']) == 2) - { - - // ----- Change the file status - $p_entry['status'] = "filtered"; - - // ----- Return - return $v_result; - } - - $p_remove_path_size = strlen($p_remove_path); - if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path) - { - - // ----- Remove the path - $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size); - - } - } - - // ----- Add the path - if ($p_path != '') { - $p_entry['filename'] = $p_path."/".$p_entry['filename']; - } - - // ----- Check a base_dir_restriction - if (isset($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION])) { - $v_inclusion - = PclZipUtilPathInclusion($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION], - $p_entry['filename']); - if ($v_inclusion == 0) { - - PclZip::privErrorLog(PCLZIP_ERR_DIRECTORY_RESTRICTION, - "Filename '".$p_entry['filename']."' is " - ."outside PCLZIP_OPT_EXTRACT_DIR_RESTRICTION"); - - return PclZip::errorCode(); - } - } - - // ----- Look for pre-extract callback - if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { - - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. -// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); - if ($v_result == 0) { - // ----- Change the file status - $p_entry['status'] = "skipped"; - $v_result = 1; - } - - // ----- Look for abort result - if ($v_result == 2) { - // ----- This status is internal and will be changed in 'skipped' - $p_entry['status'] = "aborted"; - $v_result = PCLZIP_ERR_USER_ABORTED; - } - - // ----- Update the informations - // Only some fields can be modified - $p_entry['filename'] = $v_local_header['filename']; - } - - - // ----- Look if extraction should be done - if ($p_entry['status'] == 'ok') { - - // ----- Look for specific actions while the file exist - if (file_exists($p_entry['filename'])) - { - - // ----- Look if file is a directory - if (is_dir($p_entry['filename'])) - { - - // ----- Change the file status - $p_entry['status'] = "already_a_directory"; - - // ----- Look for PCLZIP_OPT_STOP_ON_ERROR - // For historical reason first PclZip implementation does not stop - // when this kind of error occurs. - if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) - && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { - - PclZip::privErrorLog(PCLZIP_ERR_ALREADY_A_DIRECTORY, - "Filename '".$p_entry['filename']."' is " - ."already used by an existing directory"); - - return PclZip::errorCode(); - } - } - // ----- Look if file is write protected - else if (!is_writeable($p_entry['filename'])) - { - - // ----- Change the file status - $p_entry['status'] = "write_protected"; - - // ----- Look for PCLZIP_OPT_STOP_ON_ERROR - // For historical reason first PclZip implementation does not stop - // when this kind of error occurs. - if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) - && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { - - PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, - "Filename '".$p_entry['filename']."' exists " - ."and is write protected"); - - return PclZip::errorCode(); - } - } - - // ----- Look if the extracted file is older - else if (filemtime($p_entry['filename']) > $p_entry['mtime']) - { - // ----- Change the file status - if ( (isset($p_options[PCLZIP_OPT_REPLACE_NEWER])) - && ($p_options[PCLZIP_OPT_REPLACE_NEWER]===true)) { - } - else { - $p_entry['status'] = "newer_exist"; - - // ----- Look for PCLZIP_OPT_STOP_ON_ERROR - // For historical reason first PclZip implementation does not stop - // when this kind of error occurs. - if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) - && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { - - PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, - "Newer version of '".$p_entry['filename']."' exists " - ."and option PCLZIP_OPT_REPLACE_NEWER is not selected"); - - return PclZip::errorCode(); - } - } - } - else { - } - } - - // ----- Check the directory availability and create it if necessary - else { - if ((($p_entry['external']&0x00000010)==0x00000010) || (substr($p_entry['filename'], -1) == '/')) - $v_dir_to_check = $p_entry['filename']; - else if (!strstr($p_entry['filename'], "/")) - $v_dir_to_check = ""; - else - $v_dir_to_check = dirname($p_entry['filename']); - - if (($v_result = $this->privDirCheck($v_dir_to_check, (($p_entry['external']&0x00000010)==0x00000010))) != 1) { - - // ----- Change the file status - $p_entry['status'] = "path_creation_fail"; - - // ----- Return - //return $v_result; - $v_result = 1; - } - } - } - - // ----- Look if extraction should be done - if ($p_entry['status'] == 'ok') { - - // ----- Do the extraction (if not a folder) - if (!(($p_entry['external']&0x00000010)==0x00000010)) - { - // ----- Look for not compressed file - if ($p_entry['compression'] == 0) { - - // ----- Opening destination file - if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) - { - - // ----- Change the file status - $p_entry['status'] = "write_error"; - - // ----- Return - return $v_result; - } - - - // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks - $v_size = $p_entry['compressed_size']; - $this->zip_ftell += $v_size; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($this->zip_fd, $v_read_size); - /* Try to speed up the code - $v_binary_data = pack('a'.$v_read_size, $v_buffer); - @fwrite($v_dest_file, $v_binary_data, $v_read_size); - */ - @fwrite($v_dest_file, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Closing the destination file - fclose($v_dest_file); - - // ----- Change the file mtime - touch($p_entry['filename'], $p_entry['mtime']); - - - } - else { - // ----- TBC - // Need to be finished - if (($p_entry['flag'] & 1) == 1) { - PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, 'File \''.$p_entry['filename'].'\' is encrypted. Encrypted files are not supported.'); - return PclZip::errorCode(); - } - - - // ----- Look for using temporary file to unzip - if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) - && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) - || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) - && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_entry['size'])) ) ) { - $v_result = $this->privExtractFileUsingTempFile($p_entry, $p_options); - if ($v_result < PCLZIP_ERR_NO_ERROR) { - return $v_result; - } - } - - // ----- Look for extract in memory - else { - - - // ----- Read the compressed file in a buffer (one shot) - $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); - $this->zip_ftell += $p_entry['compressed_size']; - - // ----- Decompress the file - $v_file_content = @gzinflate($v_buffer); - unset($v_buffer); - if ($v_file_content === FALSE) { - - // ----- Change the file status - // TBC - $p_entry['status'] = "error"; - - return $v_result; - } - - // ----- Opening destination file - if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { - - // ----- Change the file status - $p_entry['status'] = "write_error"; - - return $v_result; - } - - // ----- Write the uncompressed data - @fwrite($v_dest_file, $v_file_content, $p_entry['size']); - unset($v_file_content); - - // ----- Closing the destination file - @fclose($v_dest_file); - - } - - // ----- Change the file mtime - @touch($p_entry['filename'], $p_entry['mtime']); - } - - // ----- Look for chmod option - if (isset($p_options[PCLZIP_OPT_SET_CHMOD])) { - - // ----- Change the mode of the file - @chmod($p_entry['filename'], $p_options[PCLZIP_OPT_SET_CHMOD]); - } - - } - } - - // ----- Change abort status - if ($p_entry['status'] == "aborted") { - $p_entry['status'] = "skipped"; - } - - // ----- Look for post-extract callback - elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { - - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. -// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); - - // ----- Look for abort result - if ($v_result == 2) { - $v_result = PCLZIP_ERR_USER_ABORTED; - } - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privExtractFileUsingTempFile() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privExtractFileUsingTempFile(&$p_entry, &$p_options) - { - $v_result=1; - - // ----- Creates a temporary file - $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz'; - if (($v_dest_file = @fopen($v_gzip_temp_name, "wb")) == 0) { - fclose($v_file); - PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode'); - return PclZip::errorCode(); - } - - - // ----- Write gz file format header - $v_binary_data = pack('va1a1Va1a1', 0x8b1f, Chr($p_entry['compression']), Chr(0x00), time(), Chr(0x00), Chr(3)); - @fwrite($v_dest_file, $v_binary_data, 10); - - // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks - $v_size = $p_entry['compressed_size']; - $this->zip_ftell += $v_size; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($this->zip_fd, $v_read_size); - //$v_binary_data = pack('a'.$v_read_size, $v_buffer); - @fwrite($v_dest_file, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Write gz file format footer - $v_binary_data = pack('VV', $p_entry['crc'], $p_entry['size']); - @fwrite($v_dest_file, $v_binary_data, 8); - - // ----- Close the temporary file - @fclose($v_dest_file); - - // ----- Opening destination file - if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { - $p_entry['status'] = "write_error"; - return $v_result; - } - - // ----- Open the temporary gz file - if(function_exists('gzopen64')){ - $v_src_file = @gzopen64($v_gzip_temp_name, 'rb'); - }else{ - $v_src_file = @gzopen($v_gzip_temp_name, 'rb'); - } - if ($v_src_file == 0) { - @fclose($v_dest_file); - $p_entry['status'] = "read_error"; - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); - return PclZip::errorCode(); - } - - - // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks - $v_size = $p_entry['size']; - while ($v_size != 0) { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @gzread($v_src_file, $v_read_size); - //$v_binary_data = pack('a'.$v_read_size, $v_buffer); - @fwrite($v_dest_file, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - @fclose($v_dest_file); - @gzclose($v_src_file); - - // ----- Delete the temporary file - @unlink($v_gzip_temp_name); - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privExtractFileInOutput() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privExtractFileInOutput(&$p_entry, &$p_options) - { - $v_result=1; - - // ----- Read the file header - if (($v_result = $this->privReadFileHeader($v_header)) != 1) { - return $v_result; - } - - - // ----- Check that the file header is coherent with $p_entry info - if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { - // TBC - } - - // ----- Look for pre-extract callback - if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { - - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. -// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); - if ($v_result == 0) { - // ----- Change the file status - $p_entry['status'] = "skipped"; - $v_result = 1; - } - - // ----- Look for abort result - if ($v_result == 2) { - // ----- This status is internal and will be changed in 'skipped' - $p_entry['status'] = "aborted"; - $v_result = PCLZIP_ERR_USER_ABORTED; - } - - // ----- Update the informations - // Only some fields can be modified - $p_entry['filename'] = $v_local_header['filename']; - } - - // ----- Trace - - // ----- Look if extraction should be done - if ($p_entry['status'] == 'ok') { - - // ----- Do the extraction (if not a folder) - if (!(($p_entry['external']&0x00000010)==0x00000010)) { - // ----- Look for not compressed file - if ($p_entry['compressed_size'] == $p_entry['size']) { - - // ----- Read the file in a buffer (one shot) - $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); - $this->zip_ftell += $p_entry['compressed_size']; - - // ----- Send the file to the output - echo $v_buffer; - unset($v_buffer); - } - else { - - // ----- Read the compressed file in a buffer (one shot) - $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); - $this->zip_ftell += $p_entry['compressed_size']; - - // ----- Decompress the file - $v_file_content = gzinflate($v_buffer); - unset($v_buffer); - - // ----- Send the file to the output - echo $v_file_content; - unset($v_file_content); - } - } - } - - // ----- Change abort status - if ($p_entry['status'] == "aborted") { - $p_entry['status'] = "skipped"; - } - - // ----- Look for post-extract callback - elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { - - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. -// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); - - // ----- Look for abort result - if ($v_result == 2) { - $v_result = PCLZIP_ERR_USER_ABORTED; - } - } - - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privExtractFileAsString() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privExtractFileAsString(&$p_entry, &$p_string, &$p_options) - { - $v_result=1; - - // ----- Read the file header - $v_header = array(); - if (($v_result = $this->privReadFileHeader($v_header)) != 1) - { - // ----- Return - return $v_result; - } - - - // ----- Check that the file header is coherent with $p_entry info - if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { - // TBC - } - - // ----- Look for pre-extract callback - if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { - - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. -// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); - if ($v_result == 0) { - // ----- Change the file status - $p_entry['status'] = "skipped"; - $v_result = 1; - } - - // ----- Look for abort result - if ($v_result == 2) { - // ----- This status is internal and will be changed in 'skipped' - $p_entry['status'] = "aborted"; - $v_result = PCLZIP_ERR_USER_ABORTED; - } - - // ----- Update the informations - // Only some fields can be modified - $p_entry['filename'] = $v_local_header['filename']; - } - - - // ----- Look if extraction should be done - if ($p_entry['status'] == 'ok') { - - // ----- Do the extraction (if not a folder) - if (!(($p_entry['external']&0x00000010)==0x00000010)) { - // ----- Look for not compressed file - // if ($p_entry['compressed_size'] == $p_entry['size']) - if ($p_entry['compression'] == 0) { - - // ----- Reading the file - $p_string = @fread($this->zip_fd, $p_entry['compressed_size']); - $this->zip_ftell += $p_entry['compressed_size']; - } - else { - - // ----- Reading the file - $v_data = @fread($this->zip_fd, $p_entry['compressed_size']); - $this->zip_ftell += $p_entry['compressed_size']; - - // ----- Decompress the file - if (($p_string = @gzinflate($v_data)) === FALSE) { - // TBC - } - } - - // ----- Trace - } - else { - // TBC : error : can not extract a folder in a string - } - - } - - // ----- Change abort status - if ($p_entry['status'] == "aborted") { - $p_entry['status'] = "skipped"; - } - - // ----- Look for post-extract callback - elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { - - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - - // ----- Swap the content to header - $v_local_header['content'] = $p_string; - $p_string = ''; - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. -// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); - - // ----- Swap back the content to header - $p_string = $v_local_header['content']; - unset($v_local_header['content']); - - // ----- Look for abort result - if ($v_result == 2) { - $v_result = PCLZIP_ERR_USER_ABORTED; - } - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privReadFileHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privReadFileHeader(&$p_header) - { - $v_result=1; - - // ----- Read the 4 bytes signature - $v_binary_data = @fread($this->zip_fd, 4); - $this->zip_ftell += 4; - $v_data = unpack('Vid', $v_binary_data); - - // ----- Check signature - if ($v_data['id'] != 0x04034b50) - { - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Read the first 42 bytes of the header - $v_binary_data = fread($this->zip_fd, 26); - $this->zip_ftell += 26; - - // ----- Look for invalid block size - if (strlen($v_binary_data) != 26) - { - $p_header['filename'] = ""; - $p_header['status'] = "invalid_header"; - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Extract the values - $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data); - - // ----- Get filename - $p_header['filename'] = fread($this->zip_fd, $v_data['filename_len']); - $this->zip_ftell += $v_data['filename_len']; - - // ----- Get extra_fields - if ($v_data['extra_len'] != 0) { - $p_header['extra'] = fread($this->zip_fd, $v_data['extra_len']); - $this->zip_ftell += $v_data['extra_len']; - } - else { - $p_header['extra'] = ''; - } - - // ----- Extract properties - $p_header['version_extracted'] = $v_data['version']; - $p_header['compression'] = $v_data['compression']; - $p_header['size'] = $v_data['size']; - $p_header['compressed_size'] = $v_data['compressed_size']; - $p_header['crc'] = $v_data['crc']; - $p_header['flag'] = $v_data['flag']; - $p_header['filename_len'] = $v_data['filename_len']; - - // ----- Recuperate date in UNIX format - $p_header['mdate'] = $v_data['mdate']; - $p_header['mtime'] = $v_data['mtime']; - if ($p_header['mdate'] && $p_header['mtime']) - { - // ----- Extract time - $v_hour = ($p_header['mtime'] & 0xF800) >> 11; - $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; - $v_seconde = ($p_header['mtime'] & 0x001F)*2; - - // ----- Extract date - $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; - $v_month = ($p_header['mdate'] & 0x01E0) >> 5; - $v_day = $p_header['mdate'] & 0x001F; - - // ----- Get UNIX date format - $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); - - } - else - { - $p_header['mtime'] = time(); - } - - // TBC - //for(reset($v_data); $key = key($v_data); next($v_data)) { - //} - - // ----- Set the stored filename - $p_header['stored_filename'] = $p_header['filename']; - - // ----- Set the status field - $p_header['status'] = "ok"; - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privReadCentralFileHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privReadCentralFileHeader(&$p_header) - { - $v_result=1; - - // ----- Read the 4 bytes signature - $v_binary_data = @fread($this->zip_fd, 4); - $this->zip_ftell += 4; - $v_data = unpack('Vid', $v_binary_data); - - // ----- Check signature - if ($v_data['id'] != 0x02014b50) - { - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Read the first 42 bytes of the header - $v_binary_data = fread($this->zip_fd, 42); - $this->zip_ftell += 42; - - // ----- Look for invalid block size - if (strlen($v_binary_data) != 42) - { - $p_header['filename'] = ""; - $p_header['status'] = "invalid_header"; - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Extract the values - $p_header = unpack('vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $v_binary_data); - - // ----- Get filename - if ($p_header['filename_len'] != 0) { - $p_header['filename'] = fread($this->zip_fd, $p_header['filename_len']); - $this->zip_ftell += $p_header['filename_len']; - } else { - $p_header['filename'] = ''; - } - - // ----- Get extra - if ($p_header['extra_len'] != 0) { - $p_header['extra'] = fread($this->zip_fd, $p_header['extra_len']); - $this->zip_ftell += $p_header['extra_len']; - } else { - $p_header['extra'] = ''; - } - - // ----- Get comment - if ($p_header['comment_len'] != 0) { - $p_header['comment'] = fread($this->zip_fd, $p_header['comment_len']); - $this->zip_ftell += $p_header['comment_len']; - } else { - $p_header['comment'] = ''; - } - - // ----- Extract properties - - // ----- Recuperate date in UNIX format - //if ($p_header['mdate'] && $p_header['mtime']) - // TBC : bug : this was ignoring time with 0/0/0 - if (1) - { - // ----- Extract time - $v_hour = ($p_header['mtime'] & 0xF800) >> 11; - $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; - $v_seconde = ($p_header['mtime'] & 0x001F)*2; - - // ----- Extract date - $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; - $v_month = ($p_header['mdate'] & 0x01E0) >> 5; - $v_day = $p_header['mdate'] & 0x001F; - - // ----- Get UNIX date format - $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); - - } - else - { - $p_header['mtime'] = time(); - } - - // ----- Set the stored filename - $p_header['stored_filename'] = $p_header['filename']; - - // ----- Set default status to ok - $p_header['status'] = 'ok'; - - // ----- Look if it is a directory - if (substr($p_header['filename'], -1) == '/') { - //$p_header['external'] = 0x41FF0010; - $p_header['external'] = 0x00000010; - } - - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privCheckFileHeaders() - // Description : - // Parameters : - // Return Values : - // 1 on success, - // 0 on error; - // -------------------------------------------------------------------------------- - function privCheckFileHeaders(&$p_local_header, &$p_central_header) - { - $v_result=1; - - // ----- Check the static values - // TBC - if ($p_local_header['filename'] != $p_central_header['filename']) { - } - if ($p_local_header['version_extracted'] != $p_central_header['version_extracted']) { - } - if ($p_local_header['flag'] != $p_central_header['flag']) { - } - if ($p_local_header['compression'] != $p_central_header['compression']) { - } - if ($p_local_header['mtime'] != $p_central_header['mtime']) { - } - if ($p_local_header['filename_len'] != $p_central_header['filename_len']) { - } - - // ----- Look for flag bit 3 - if (($p_local_header['flag'] & 8) == 8) { - $p_local_header['size'] = $p_central_header['size']; - $p_local_header['compressed_size'] = $p_central_header['compressed_size']; - $p_local_header['crc'] = $p_central_header['crc']; - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privReadEndCentralDir() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privReadEndCentralDir(&$p_central_dir) - { - $v_result=1; - - // ----- Go to the end of the zip file - $v_size = filesize($this->zipname); - if (@fseek($this->zip_fd, $v_size)) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to go to the end of the archive \''.$this->zipname.'\''); - - // ----- Return - return PclZip::errorCode(); - } - $this->zip_ftell = $v_size; - - // ----- First try : look if this is an archive with no commentaries (most of the time) - // in this case the end of central dir is at 22 bytes of the file end - $v_found = 0; - if ($v_size > 26) { - if (@fseek($this->zip_fd, $v_size-22)) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\''); - - // ----- Return - return PclZip::errorCode(); - } - $this->zip_ftell = $v_size-22; - $v_pos = $this->zip_ftell; - - // ----- Read for bytes - $v_binary_data = @fread($this->zip_fd, 4); - $this->zip_ftell += 4; - $v_data = @unpack('Vid', $v_binary_data); - - // ----- Check signature - if ($v_data['id'] == 0x06054b50) { - $v_found = 1; - } - - $v_pos = $this->zip_ftell; - } - - // ----- Go back to the maximum possible size of the Central Dir End Record - if (!$v_found) { - $v_maximum_size = 65557; // 0xFFFF + 22; - if ($v_maximum_size > $v_size) - $v_maximum_size = $v_size; - if (@fseek($this->zip_fd, $v_size-$v_maximum_size)) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\''); - - // ----- Return - return PclZip::errorCode(); - } - $this->zip_ftell = $v_size-$v_maximum_size; - - // ----- Read byte per byte in order to find the signature - $v_pos = $this->zip_ftell; - $v_bytes = 0x00000000; - while ($v_pos < $v_size) - { - // ----- Read a byte - $v_byte = @fread($this->zip_fd, 1); - $this->zip_ftell += 1; - - // ----- Add the byte - //$v_bytes = ($v_bytes << 8) | Ord($v_byte); - // Note we mask the old value down such that once shifted we can never end up with more than a 32bit number - // Otherwise on systems where we have 64bit integers the check below for the magic number will fail. - $v_bytes = ( ($v_bytes & 0xFFFFFF) << 8) | Ord($v_byte); - - // ----- Compare the bytes - if ($v_bytes == 0x504b0506) - { - $v_pos++; - break; - } - - $v_pos++; - } - - // ----- Look if not found end of central dir - if ($v_pos == $v_size) - { - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Unable to find End of Central Dir Record signature"); - - // ----- Return - return PclZip::errorCode(); - } - } - - // ----- Read the first 18 bytes of the header - $v_binary_data = fread($this->zip_fd, 18); - $this->zip_ftell += 18; - - // ----- Look for invalid block size - if (strlen($v_binary_data) != 18) - { - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid End of Central Dir Record size : ".strlen($v_binary_data)); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Extract the values - $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data); - - // ----- Check the global size - if (($v_pos + $v_data['comment_size'] + 18) != $v_size) { - - // ----- Removed in release 2.2 see readme file - // The check of the file size is a little too strict. - // Some bugs where found when a zip is encrypted/decrypted with 'crypt'. - // While decrypted, zip has training 0 bytes - if (0) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, - 'The central dir is not at the end of the archive.' - .' Some trailing bytes exists after the archive.'); - - // ----- Return - return PclZip::errorCode(); - } - } - - // ----- Get comment - if ($v_data['comment_size'] != 0) { - $p_central_dir['comment'] = fread($this->zip_fd, $v_data['comment_size']); - $this->zip_ftell += $v_data['comment_size']; - } - else - $p_central_dir['comment'] = ''; - - $p_central_dir['entries'] = $v_data['entries']; - $p_central_dir['disk_entries'] = $v_data['disk_entries']; - $p_central_dir['offset'] = $v_data['offset']; - $p_central_dir['size'] = $v_data['size']; - $p_central_dir['disk'] = $v_data['disk']; - $p_central_dir['disk_start'] = $v_data['disk_start']; - - // TBC - //for(reset($p_central_dir); $key = key($p_central_dir); next($p_central_dir)) { - //} - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privDeleteByRule() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privDeleteByRule(&$p_result_list, &$p_options) - { - $v_result=1; - $v_list_detail = array(); - - // ----- Open the zip file - if (($v_result=$this->privOpenFd('rb')) != 1) - { - // ----- Return - return $v_result; - } - - // ----- Read the central directory informations - $v_central_dir = array(); - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) - { - $this->privCloseFd(); - return $v_result; - } - - // ----- Go to beginning of File - // ----- Scan all the files - // ----- Start at beginning of Central Dir - $v_pos_entry = $v_central_dir['offset']; - @rewind($this->zip_fd); - $this->zip_ftell = 0; - if (@fseek($this->zip_fd, $v_pos_entry)) - { - // ----- Close the zip file - $this->privCloseFd(); - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); - - // ----- Return - return PclZip::errorCode(); - } - $this->zip_ftell = $v_pos_entry; - - // ----- Read each entry - $v_header_list = array(); - $j_start = 0; - for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) - { - - // ----- Read the file header - $v_header_list[$v_nb_extracted] = array(); - if (($v_result = $this->privReadCentralFileHeader($v_header_list[$v_nb_extracted])) != 1) - { - // ----- Close the zip file - $this->privCloseFd(); - - return $v_result; - } - - - // ----- Store the index - $v_header_list[$v_nb_extracted]['index'] = $i; - - // ----- Look for the specific extract rules - $v_found = false; - - // ----- Look for extract by name rule - if ( (isset($p_options[PCLZIP_OPT_BY_NAME])) - && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { - - // ----- Look if the filename is in the list - for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) - && (substr($v_header_list[$v_nb_extracted]['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { - $v_found = true; - } - elseif ( (($v_header_list[$v_nb_extracted]['external']&0x00000010)==0x00000010) /* Indicates a folder */ - && ($v_header_list[$v_nb_extracted]['stored_filename'].'/' == $p_options[PCLZIP_OPT_BY_NAME][$j])) { - $v_found = true; - } - } - // ----- Look for a filename - elseif ($v_header_list[$v_nb_extracted]['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { - $v_found = true; - } - } - } - - // ----- Look for extract by ereg rule - // ereg() is deprecated with PHP 5.3 - /* - else if ( (isset($p_options[PCLZIP_OPT_BY_EREG])) - && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { - - if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { - $v_found = true; - } - } - */ - - // ----- Look for extract by preg rule - else if ( (isset($p_options[PCLZIP_OPT_BY_PREG])) - && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { - - if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { - $v_found = true; - } - } - - // ----- Look for extract by index rule - else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX])) - && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { - - // ----- Look if the index is in the list - for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { - $v_found = true; - } - if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { - $j_start = $j+1; - } - - if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) { - break; - } - } - } - else { - $v_found = true; - } - - // ----- Look for deletion - if ($v_found) - { - unset($v_header_list[$v_nb_extracted]); - } - else - { - $v_nb_extracted++; - } - } - - // ----- Look if something need to be deleted - if ($v_nb_extracted > 0) { - - // ----- Creates a temporay file - $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; - - // ----- Creates a temporary zip archive - $v_temp_zip = new PclZip($v_zip_temp_name); - - // ----- Open the temporary zip file in write mode - if (($v_result = $v_temp_zip->privOpenFd('wb')) != 1) { - $this->privCloseFd(); - - // ----- Return - return $v_result; - } - - // ----- Look which file need to be kept - for ($i=0; $izip_fd); - $this->zip_ftell = 0; - if (@fseek($this->zip_fd, $v_header_list[$i]['offset'])) { - // ----- Close the zip file - $this->privCloseFd(); - $v_temp_zip->privCloseFd(); - @unlink($v_zip_temp_name); - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); - - // ----- Return - return PclZip::errorCode(); - } - $this->zip_ftell = $v_header_list[$i]['offset']; - - // ----- Read the file header - $v_local_header = array(); - if (($v_result = $this->privReadFileHeader($v_local_header)) != 1) { - // ----- Close the zip file - $this->privCloseFd(); - $v_temp_zip->privCloseFd(); - @unlink($v_zip_temp_name); - - // ----- Return - return $v_result; - } - - // ----- Check that local file header is same as central file header - if ($this->privCheckFileHeaders($v_local_header, - $v_header_list[$i]) != 1) { - // TBC - } - unset($v_local_header); - - // ----- Write the file header - if (($v_result = $v_temp_zip->privWriteFileHeader($v_header_list[$i])) != 1) { - // ----- Close the zip file - $this->privCloseFd(); - $v_temp_zip->privCloseFd(); - @unlink($v_zip_temp_name); - - // ----- Return - return $v_result; - } - - // ----- Read/write the data block - $this->zip_ftell += $v_header_list[$i]['compressed_size']; - $v_temp_zip->zip_fd += $v_header_list[$i]['compressed_size']; - if (($v_result = PclZipUtilCopyBlock($this->zip_fd, $v_temp_zip->zip_fd, $v_header_list[$i]['compressed_size'])) != 1) { - // ----- Close the zip file - $this->privCloseFd(); - $v_temp_zip->privCloseFd(); - @unlink($v_zip_temp_name); - - // ----- Return - return $v_result; - } - } - - // ----- Store the offset of the central dir - $v_offset = $v_temp_zip->zip_ftell; - - // ----- Re-Create the Central Dir files header - for ($i=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { - $v_temp_zip->privCloseFd(); - $this->privCloseFd(); - @unlink($v_zip_temp_name); - - // ----- Return - return $v_result; - } - - // ----- Transform the header to a 'usable' info - $v_temp_zip->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); - } - - - // ----- Zip file comment - $v_comment = ''; - if (isset($p_options[PCLZIP_OPT_COMMENT])) { - $v_comment = $p_options[PCLZIP_OPT_COMMENT]; - } - - // ----- Calculate the size of the central header - $v_size = $v_temp_zip->zip_ftell-$v_offset; - - // ----- Create the central dir footer - if (($v_result = $v_temp_zip->privWriteCentralHeader(sizeof($v_header_list), $v_size, $v_offset, $v_comment)) != 1) { - // ----- Reset the file list - unset($v_header_list); - $v_temp_zip->privCloseFd(); - $this->privCloseFd(); - @unlink($v_zip_temp_name); - - // ----- Return - return $v_result; - } - - // ----- Close - $v_temp_zip->privCloseFd(); - $this->privCloseFd(); - - // ----- Delete the zip file - // TBC : I should test the result ... - @unlink($this->zipname); - - // ----- Rename the temporary file - // TBC : I should test the result ... - //@rename($v_zip_temp_name, $this->zipname); - PclZipUtilRename($v_zip_temp_name, $this->zipname); - - // ----- Destroy the temporary archive - unset($v_temp_zip); - } - - // ----- Remove every files : reset the file - else if ($v_central_dir['entries'] != 0) { - $this->privCloseFd(); - - if (($v_result = $this->privOpenFd('wb')) != 1) { - return $v_result; - } - - if (($v_result = $this->privWriteCentralHeader(0, 0, 0, '')) != 1) { - return $v_result; - } - - $this->privCloseFd(); - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privDirCheck() - // Description : - // Check if a directory exists, if not it creates it and all the parents directory - // which may be useful. - // Parameters : - // $p_dir : Directory path to check. - // Return Values : - // 1 : OK - // -1 : Unable to create directory - // -------------------------------------------------------------------------------- - function privDirCheck($p_dir, $p_is_dir=false) - { - $v_result = 1; - - - // ----- Remove the final '/' - if (($p_is_dir) && (substr($p_dir, -1)=='/')) - { - $p_dir = substr($p_dir, 0, strlen($p_dir)-1); - } - - // ----- Check the directory availability - if ((is_dir($p_dir)) || ($p_dir == "")) - { - return 1; - } - - // ----- Extract parent directory - $p_parent_dir = dirname($p_dir); - - // ----- Just a check - if ($p_parent_dir != $p_dir) - { - // ----- Look for parent directory - if ($p_parent_dir != "") - { - if (($v_result = $this->privDirCheck($p_parent_dir)) != 1) - { - return $v_result; - } - } - } - - // ----- Create the directory - if (!@mkdir($p_dir, 0777)) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_DIR_CREATE_FAIL, "Unable to create directory '$p_dir'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privMerge() - // Description : - // If $p_archive_to_add does not exist, the function exit with a success result. - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privMerge(&$p_archive_to_add) - { - $v_result=1; - - // ----- Look if the archive_to_add exists - if (!is_file($p_archive_to_add->zipname)) - { - - // ----- Nothing to merge, so merge is a success - $v_result = 1; - - // ----- Return - return $v_result; - } - - // ----- Look if the archive exists - if (!is_file($this->zipname)) - { - - // ----- Do a duplicate - $v_result = $this->privDuplicate($p_archive_to_add->zipname); - - // ----- Return - return $v_result; - } - - // ----- Open the zip file - if (($v_result=$this->privOpenFd('rb')) != 1) - { - // ----- Return - return $v_result; - } - - // ----- Read the central directory informations - $v_central_dir = array(); - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) - { - $this->privCloseFd(); - return $v_result; - } - - // ----- Go to beginning of File - @rewind($this->zip_fd); - $this->zip_ftell = 0; - - // ----- Open the archive_to_add file - if (($v_result=$p_archive_to_add->privOpenFd('rb')) != 1) - { - $this->privCloseFd(); - - // ----- Return - return $v_result; - } - - // ----- Read the central directory informations - $v_central_dir_to_add = array(); - if (($v_result = $p_archive_to_add->privReadEndCentralDir($v_central_dir_to_add)) != 1) - { - $this->privCloseFd(); - $p_archive_to_add->privCloseFd(); - - return $v_result; - } - - // ----- Go to beginning of File - @rewind($p_archive_to_add->zip_fd); - $p_archive_to_add->zip_ftell = 0; - - // ----- Creates a temporay file - $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; - - // ----- Open the temporary file in write mode - $v_zip_temp_ftell = 0; - if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) - { - $this->privCloseFd(); - $p_archive_to_add->privCloseFd(); - - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode'); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Copy the files from the archive to the temporary file - // TBC : Here I should better append the file and go back to erase the central dir - $v_size = $v_central_dir['offset']; - $this->zip_ftell += $v_size; - $v_zip_temp_ftell += $v_size; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = fread($this->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Copy the files from the archive_to_add into the temporary file - $v_size = $v_central_dir_to_add['offset']; - $p_archive_to_add->zip_ftell += $v_size; - $v_zip_temp_ftell += $v_size; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = fread($p_archive_to_add->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Store the offset of the central dir - $v_offset = $v_zip_temp_ftell; - - // ----- Copy the block of file headers from the old archive - $v_size = $v_central_dir['size']; - $this->zip_ftell += $v_size; - $v_zip_temp_ftell += $v_size; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($this->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Copy the block of file headers from the archive_to_add - $v_size = $v_central_dir_to_add['size']; - $p_archive_to_add->zip_ftell += $v_size; - $v_zip_temp_ftell += $v_size; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($p_archive_to_add->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Merge the file comments - $v_comment = $v_central_dir['comment'].' '.$v_central_dir_to_add['comment']; - - // ----- Calculate the size of the (new) central header - $v_size = $v_zip_temp_ftell-$v_offset; - - // ----- Swap the file descriptor - // Here is a trick : I swap the temporary fd with the zip fd, in order to use - // the following methods on the temporary fil and not the real archive fd - $v_swap = $this->zip_fd; - $this->zip_fd = $v_zip_temp_fd; - $v_zip_temp_fd = $v_swap; - $v_swap = $this->zip_ftell; - $this->zip_ftell = $v_zip_temp_ftell; - $v_zip_temp_ftell = $v_swap; - - // ----- Create the central dir footer - if (($v_result = $this->privWriteCentralHeader($v_central_dir['entries']+$v_central_dir_to_add['entries'], $v_size, $v_offset, $v_comment)) != 1) - { - $this->privCloseFd(); - $p_archive_to_add->privCloseFd(); - @fclose($v_zip_temp_fd); - $this->zip_fd = null; - $this->zip_ftell = 0; - - // ----- Reset the file list - unset($v_header_list); - - // ----- Return - return $v_result; - } - - // ----- Swap back the file descriptor - $v_swap = $this->zip_fd; - $this->zip_fd = $v_zip_temp_fd; - $v_zip_temp_fd = $v_swap; - $v_swap = $this->zip_ftell; - $this->zip_ftell = $v_zip_temp_ftell; - $v_zip_temp_ftell = $v_swap; - - // ----- Close - $this->privCloseFd(); - $p_archive_to_add->privCloseFd(); - - // ----- Close the temporary file - @fclose($v_zip_temp_fd); - - // ----- Delete the zip file - // TBC : I should test the result ... - @unlink($this->zipname); - - // ----- Rename the temporary file - // TBC : I should test the result ... - //@rename($v_zip_temp_name, $this->zipname); - PclZipUtilRename($v_zip_temp_name, $this->zipname); - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privDuplicate() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privDuplicate($p_archive_filename) - { - $v_result=1; - - // ----- Look if the $p_archive_filename exists - if (!is_file($p_archive_filename)) - { - - // ----- Nothing to duplicate, so duplicate is a success. - $v_result = 1; - - // ----- Return - return $v_result; - } - - // ----- Open the zip file - if (($v_result=$this->privOpenFd('wb')) != 1) - { - // ----- Return - return $v_result; - } - - // ----- Open the temporary file in write mode - if (($v_zip_temp_fd = @fopen($p_archive_filename, 'rb')) == 0) - { - $this->privCloseFd(); - - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive file \''.$p_archive_filename.'\' in binary write mode'); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Copy the files from the archive to the temporary file - // TBC : Here I should better append the file and go back to erase the central dir - $v_size = filesize($p_archive_filename); - $this->zip_ftell += $v_size; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = fread($v_zip_temp_fd, $v_read_size); - @fwrite($this->zip_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Close - $this->privCloseFd(); - - // ----- Close the temporary file - @fclose($v_zip_temp_fd); - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privErrorLog() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function privErrorLog($p_error_code=0, $p_error_string='') - { - if (PCLZIP_ERROR_EXTERNAL == 1) { - PclError($p_error_code, $p_error_string); - } - else { - $this->error_code = $p_error_code; - $this->error_string = $p_error_string; - } - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privErrorReset() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function privErrorReset() - { - if (PCLZIP_ERROR_EXTERNAL == 1) { - PclErrorReset(); - } - else { - $this->error_code = 0; - $this->error_string = ''; - } - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privDisableMagicQuotes() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privDisableMagicQuotes() - { - $v_result=1; - - // ----- Look if function exists - if ( (!function_exists("get_magic_quotes_runtime")) - || (!function_exists("set_magic_quotes_runtime"))) { - return $v_result; - } - - // ----- Look if already done - if ($this->magic_quotes_status != -1) { - return $v_result; - } - - // ----- Get and memorize the magic_quote value - $this->magic_quotes_status = @get_magic_quotes_runtime(); - - // ----- Disable magic_quotes - if ($this->magic_quotes_status == 1) { - @set_magic_quotes_runtime(0); - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privSwapBackMagicQuotes() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privSwapBackMagicQuotes() - { - $v_result=1; - - // ----- Look if function exists - if ( (!function_exists("get_magic_quotes_runtime")) - || (!function_exists("set_magic_quotes_runtime"))) { - return $v_result; - } - - // ----- Look if something to do - if ($this->magic_quotes_status != -1) { - return $v_result; - } - - // ----- Swap back magic_quotes - if ($this->magic_quotes_status == 1) { - @set_magic_quotes_runtime($this->magic_quotes_status); - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - } - // End of class - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : PclZipUtilPathReduction() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function PclZipUtilPathReduction($p_dir) - { - $v_result = ""; - $scheme = parse_url($p_dir, PHP_URL_SCHEME) != null; - - // ----- Look for not empty path - if ($p_dir != "") { - // ----- Explode path by directory names - $v_list = explode("/", $p_dir); - - // ----- Study directories from last to first - $v_skip = 0; - for ($i=sizeof($v_list)-1; $i>=0; $i--) { - // ----- Look for current path - if ($v_list[$i] == ".") { - // ----- Ignore this directory - // Should be the first $i=0, but no check is done - } - else if ($v_list[$i] == "..") { - $v_skip++; - } - else if ($v_list[$i] == "") { - // ----- First '/' i.e. root slash - if ($i == 0) { - $v_result = "/".$v_result; - if ($v_skip > 0) { - // ----- It is an invalid path, so the path is not modified - // TBC - $v_result = $p_dir; - $v_skip = 0; - } - } - // ----- Last '/' i.e. indicates a directory - else if ($i == (sizeof($v_list)-1)) { - $v_result = $v_list[$i]; - } - // ----- Double '/' inside the path - else { - // ----- Ignore only the double '//' in path, - // but not the first and last '/' - } - } - else { - // ----- Look for item to skip - if ($v_skip > 0) { - $v_skip--; - } - else { - if(stripos(PHP_OS, "win") === 0){ - if (substr($p_dir, 0, 3) == "smb") { - $v_result = (($i==0 && $scheme)? $v_list[$i]."/" : $v_list[$i]).($i!=(sizeof($v_list)-1)? "/".$v_result : ""); - } else { - $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)? "/".$v_result : ""); - } - }else{ - $v_result = $v_list[$i].(( $i == 0 && $scheme)? "/" : "").($i!=(sizeof($v_list)-1)? "/".$v_result : ""); - } - } - } - } - - // ----- Look for skip - if ($v_skip > 0) { - while ($v_skip > 0) { - $v_result = '../'.$v_result; - $v_skip--; - } - } - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : PclZipUtilPathInclusion() - // Description : - // This function indicates if the path $p_path is under the $p_dir tree. Or, - // said in an other way, if the file or sub-dir $p_path is inside the dir - // $p_dir. - // The function indicates also if the path is exactly the same as the dir. - // This function supports path with duplicated '/' like '//', but does not - // support '.' or '..' statements. - // Parameters : - // Return Values : - // 0 if $p_path is not inside directory $p_dir - // 1 if $p_path is inside directory $p_dir - // 2 if $p_path is exactly the same as $p_dir - // -------------------------------------------------------------------------------- - function PclZipUtilPathInclusion($p_dir, $p_path) - { - $v_result = 1; - - // ----- Look for path beginning by ./ - if ( ($p_dir == '.') - || ((strlen($p_dir) >=2) && (substr($p_dir, 0, 2) == './'))) { - $p_dir = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_dir, 1); - } - if ( ($p_path == '.') - || ((strlen($p_path) >=2) && (substr($p_path, 0, 2) == './'))) { - $p_path = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_path, 1); - } - - // ----- Explode dir and path by directory separator - $v_list_dir = explode("/", $p_dir); - $v_list_dir_size = sizeof($v_list_dir); - $v_list_path = explode("/", $p_path); - $v_list_path_size = sizeof($v_list_path); - - // ----- Study directories paths - $i = 0; - $j = 0; - while (($i < $v_list_dir_size) && ($j < $v_list_path_size) && ($v_result)) { - - // ----- Look for empty dir (path reduction) - if ($v_list_dir[$i] == '') { - $i++; - continue; - } - if ($v_list_path[$j] == '') { - $j++; - continue; - } - - // ----- Compare the items - if (($v_list_dir[$i] != $v_list_path[$j]) && ($v_list_dir[$i] != '') && ( $v_list_path[$j] != '')) { - $v_result = 0; - } - - // ----- Next items - $i++; - $j++; - } - - // ----- Look if everything seems to be the same - if ($v_result) { - // ----- Skip all the empty items - while (($j < $v_list_path_size) && ($v_list_path[$j] == '')) $j++; - while (($i < $v_list_dir_size) && ($v_list_dir[$i] == '')) $i++; - - if (($i >= $v_list_dir_size) && ($j >= $v_list_path_size)) { - // ----- There are exactly the same - $v_result = 2; - } - else if ($i < $v_list_dir_size) { - // ----- The path is shorter than the dir - $v_result = 0; - } - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : PclZipUtilCopyBlock() - // Description : - // Parameters : - // $p_mode : read/write compression mode - // 0 : src & dest normal - // 1 : src gzip, dest normal - // 2 : src normal, dest gzip - // 3 : src & dest gzip - // Return Values : - // -------------------------------------------------------------------------------- - function PclZipUtilCopyBlock($p_src, $p_dest, $p_size, $p_mode=0) - { - $v_result = 1; - - if ($p_mode==0) - { - while ($p_size != 0) - { - $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($p_src, $v_read_size); - @fwrite($p_dest, $v_buffer, $v_read_size); - $p_size -= $v_read_size; - } - } - else if ($p_mode==1) - { - while ($p_size != 0) - { - $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @gzread($p_src, $v_read_size); - @fwrite($p_dest, $v_buffer, $v_read_size); - $p_size -= $v_read_size; - } - } - else if ($p_mode==2) - { - while ($p_size != 0) - { - $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($p_src, $v_read_size); - @gzwrite($p_dest, $v_buffer, $v_read_size); - $p_size -= $v_read_size; - } - } - else if ($p_mode==3) - { - while ($p_size != 0) - { - $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @gzread($p_src, $v_read_size); - @gzwrite($p_dest, $v_buffer, $v_read_size); - $p_size -= $v_read_size; - } - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : PclZipUtilRename() - // Description : - // This function tries to do a simple rename() function. If it fails, it - // tries to copy the $p_src file in a new $p_dest file and then unlink the - // first one. - // Parameters : - // $p_src : Old filename - // $p_dest : New filename - // Return Values : - // 1 on success, 0 on failure. - // -------------------------------------------------------------------------------- - function PclZipUtilRename($p_src, $p_dest) - { - $v_result = 1; - - // ----- Try to rename the files - if (!@rename($p_src, $p_dest)) { - - // ----- Try to copy & unlink the src - if (!@copy($p_src, $p_dest)) { - $v_result = 0; - } - else if (!@unlink($p_src)) { - $v_result = 0; - } - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : PclZipUtilOptionText() - // Description : - // Translate option value in text. Mainly for debug purpose. - // Parameters : - // $p_option : the option value. - // Return Values : - // The option text value. - // -------------------------------------------------------------------------------- - function PclZipUtilOptionText($p_option) - { - - $v_list = get_defined_constants(); - for (reset($v_list); $v_key = key($v_list); next($v_list)) { - $v_prefix = substr($v_key, 0, 10); - if (( ($v_prefix == 'PCLZIP_OPT') - || ($v_prefix == 'PCLZIP_CB_') - || ($v_prefix == 'PCLZIP_ATT')) - && ($v_list[$v_key] == $p_option)) { - return $v_key; - } - } - - $v_result = 'Unknown'; - - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : PclZipUtilTranslateWinPath() - // Description : - // Translate windows path by replacing '\' by '/' and optionally removing - // drive letter. - // Parameters : - // $p_path : path to translate. - // $p_remove_disk_letter : true | false - // Return Values : - // The path translated. - // -------------------------------------------------------------------------------- - function PclZipUtilTranslateWinPath($p_path, $p_remove_disk_letter=true) - { - if (stristr(php_uname(), 'windows')) { - // ----- Look for potential disk letter - if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) { - $p_path = substr($p_path, $v_position+1); - } - // ----- Change potential windows directory separator - if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) { - $p_path = strtr($p_path, '\\', '/'); - } - } - return $p_path; - } - // -------------------------------------------------------------------------------- - - -?> +zipname = $p_zipname; + $this->zip_fd = 0; + $this->magic_quotes_status = -1; + + // ----- Return + return; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : + // create($p_filelist, $p_add_dir="", $p_remove_dir="") + // create($p_filelist, $p_option, $p_option_value, ...) + // Description : + // This method supports two different synopsis. The first one is historical. + // This method creates a Zip Archive. The Zip file is created in the + // filesystem. The files and directories indicated in $p_filelist + // are added in the archive. See the parameters description for the + // supported format of $p_filelist. + // When a directory is in the list, the directory and its content is added + // in the archive. + // In this synopsis, the function takes an optional variable list of + // options. See bellow the supported options. + // Parameters : + // $p_filelist : An array containing file or directory names, or + // a string containing one filename or one directory name, or + // a string containing a list of filenames and/or directory + // names separated by spaces. + // $p_add_dir : A path to add before the real path of the archived file, + // in order to have it memorized in the archive. + // $p_remove_dir : A path to remove from the real path of the file to archive, + // in order to have a shorter path memorized in the archive. + // When $p_add_dir and $p_remove_dir are set, $p_remove_dir + // is removed first, before $p_add_dir is added. + // Options : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_OPT_COMMENT : + // PCLZIP_CB_PRE_ADD : + // PCLZIP_CB_POST_ADD : + // Return Values : + // 0 on failure, + // The list of the added files, with a status of the add action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + function create($p_filelist) + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Set default values + $v_options = array(); + $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE; + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Look for arguments + if ($v_size > 1) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Remove from the options list the first argument + array_shift($v_arg_list); + $v_size--; + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_ADD => 'optional', + PCLZIP_CB_POST_ADD => 'optional', + PCLZIP_OPT_NO_COMPRESSION => 'optional', + PCLZIP_OPT_COMMENT => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + //, PCLZIP_OPT_CRYPT => 'optional' + )); + if ($v_result != 1) { + return 0; + } + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + + // ----- Get the first argument + $v_options[PCLZIP_OPT_ADD_PATH] = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1]; + } + else if ($v_size > 2) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, + "Invalid number / type of arguments"); + return 0; + } + } + } + + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); + + // ----- Init + $v_string_list = array(); + $v_att_list = array(); + $v_filedescr_list = array(); + $p_result_list = array(); + + // ----- Look if the $p_filelist is really an array + if (is_array($p_filelist)) { + + // ----- Look if the first element is also an array + // This will mean that this is a file description entry + if (isset($p_filelist[0]) && is_array($p_filelist[0])) { + $v_att_list = $p_filelist; + } + + // ----- The list is a list of string names + else { + $v_string_list = $p_filelist; + } + } + + // ----- Look if the $p_filelist is a string + else if (is_string($p_filelist)) { + // ----- Create a list from the string + $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist); + } + + // ----- Invalid variable type for $p_filelist + else { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_filelist"); + return 0; + } + + // ----- Reformat the string list + if (sizeof($v_string_list) != 0) { + foreach ($v_string_list as $v_string) { + if ($v_string != '') { + $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string; + } + else { + } + } + } + + // ----- For each file in the list check the attributes + $v_supported_attributes + = array ( PCLZIP_ATT_FILE_NAME => 'mandatory' + ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional' + ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional' + ,PCLZIP_ATT_FILE_MTIME => 'optional' + ,PCLZIP_ATT_FILE_CONTENT => 'optional' + ,PCLZIP_ATT_FILE_COMMENT => 'optional' + ); + foreach ($v_att_list as $v_entry) { + $v_result = $this->privFileDescrParseAtt($v_entry, + $v_filedescr_list[], + $v_options, + $v_supported_attributes); + if ($v_result != 1) { + return 0; + } + } + + // ----- Expand the filelist (expand directories) + $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options); + if ($v_result != 1) { + return 0; + } + + // ----- Call the create fct + $v_result = $this->privCreate($v_filedescr_list, $p_result_list, $v_options); + if ($v_result != 1) { + return 0; + } + + // ----- Return + return $p_result_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : + // add($p_filelist, $p_add_dir="", $p_remove_dir="") + // add($p_filelist, $p_option, $p_option_value, ...) + // Description : + // This method supports two synopsis. The first one is historical. + // This methods add the list of files in an existing archive. + // If a file with the same name already exists, it is added at the end of the + // archive, the first one is still present. + // If the archive does not exist, it is created. + // Parameters : + // $p_filelist : An array containing file or directory names, or + // a string containing one filename or one directory name, or + // a string containing a list of filenames and/or directory + // names separated by spaces. + // $p_add_dir : A path to add before the real path of the archived file, + // in order to have it memorized in the archive. + // $p_remove_dir : A path to remove from the real path of the file to archive, + // in order to have a shorter path memorized in the archive. + // When $p_add_dir and $p_remove_dir are set, $p_remove_dir + // is removed first, before $p_add_dir is added. + // Options : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_OPT_COMMENT : + // PCLZIP_OPT_ADD_COMMENT : + // PCLZIP_OPT_PREPEND_COMMENT : + // PCLZIP_CB_PRE_ADD : + // PCLZIP_CB_POST_ADD : + // Return Values : + // 0 on failure, + // The list of the added files, with a status of the add action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + function add($p_filelist) + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Set default values + $v_options = array(); + $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE; + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Look for arguments + if ($v_size > 1) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Remove form the options list the first argument + array_shift($v_arg_list); + $v_size--; + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_ADD => 'optional', + PCLZIP_CB_POST_ADD => 'optional', + PCLZIP_OPT_NO_COMPRESSION => 'optional', + PCLZIP_OPT_COMMENT => 'optional', + PCLZIP_OPT_ADD_COMMENT => 'optional', + PCLZIP_OPT_PREPEND_COMMENT => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + //, PCLZIP_OPT_CRYPT => 'optional' + )); + if ($v_result != 1) { + return 0; + } + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + + // ----- Get the first argument + $v_options[PCLZIP_OPT_ADD_PATH] = $v_add_path = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1]; + } + else if ($v_size > 2) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + + // ----- Return + return 0; + } + } + } + + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); + + // ----- Init + $v_string_list = array(); + $v_att_list = array(); + $v_filedescr_list = array(); + $p_result_list = array(); + + // ----- Look if the $p_filelist is really an array + if (is_array($p_filelist)) { + + // ----- Look if the first element is also an array + // This will mean that this is a file description entry + if (isset($p_filelist[0]) && is_array($p_filelist[0])) { + $v_att_list = $p_filelist; + } + + // ----- The list is a list of string names + else { + $v_string_list = $p_filelist; + } + } + + // ----- Look if the $p_filelist is a string + else if (is_string($p_filelist)) { + // ----- Create a list from the string + $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist); + } + + // ----- Invalid variable type for $p_filelist + else { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type '".gettype($p_filelist)."' for p_filelist"); + return 0; + } + + // ----- Reformat the string list + if (sizeof($v_string_list) != 0) { + foreach ($v_string_list as $v_string) { + $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string; + } + } + + // ----- For each file in the list check the attributes + $v_supported_attributes + = array ( PCLZIP_ATT_FILE_NAME => 'mandatory' + ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional' + ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional' + ,PCLZIP_ATT_FILE_MTIME => 'optional' + ,PCLZIP_ATT_FILE_CONTENT => 'optional' + ,PCLZIP_ATT_FILE_COMMENT => 'optional' + ); + foreach ($v_att_list as $v_entry) { + $v_result = $this->privFileDescrParseAtt($v_entry, + $v_filedescr_list[], + $v_options, + $v_supported_attributes); + if ($v_result != 1) { + return 0; + } + } + + // ----- Expand the filelist (expand directories) + $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options); + if ($v_result != 1) { + return 0; + } + + // ----- Call the create fct + $v_result = $this->privAdd($v_filedescr_list, $p_result_list, $v_options); + if ($v_result != 1) { + return 0; + } + + // ----- Return + return $p_result_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : listContent() + // Description : + // This public method, gives the list of the files and directories, with their + // properties. + // The properties of each entries in the list are (used also in other functions) : + // filename : Name of the file. For a create or add action it is the filename + // given by the user. For an extract function it is the filename + // of the extracted file. + // stored_filename : Name of the file / directory stored in the archive. + // size : Size of the stored file. + // compressed_size : Size of the file's data compressed in the archive + // (without the headers overhead) + // mtime : Last known modification date of the file (UNIX timestamp) + // comment : Comment associated with the file + // folder : true | false + // index : index of the file in the archive + // status : status of the action (depending of the action) : + // Values are : + // ok : OK ! + // filtered : the file / dir is not extracted (filtered by user) + // already_a_directory : the file can not be extracted because a + // directory with the same name already exists + // write_protected : the file can not be extracted because a file + // with the same name already exists and is + // write protected + // newer_exist : the file was not extracted because a newer file exists + // path_creation_fail : the file is not extracted because the folder + // does not exist and can not be created + // write_error : the file was not extracted because there was a + // error while writing the file + // read_error : the file was not extracted because there was a error + // while reading the file + // invalid_header : the file was not extracted because of an archive + // format error (bad file header) + // Note that each time a method can continue operating when there + // is an action error on a file, the error is only logged in the file status. + // Return Values : + // 0 on an unrecoverable failure, + // The list of the files in the archive. + // -------------------------------------------------------------------------------- + function listContent() + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Call the extracting fct + $p_list = array(); + if (($v_result = $this->privList($p_list)) != 1) + { + unset($p_list); + return(0); + } + + // ----- Return + return $p_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : + // extract($p_path="./", $p_remove_path="") + // extract([$p_option, $p_option_value, ...]) + // Description : + // This method supports two synopsis. The first one is historical. + // This method extract all the files / directories from the archive to the + // folder indicated in $p_path. + // If you want to ignore the 'root' part of path of the memorized files + // you can indicate this in the optional $p_remove_path parameter. + // By default, if a newer file with the same name already exists, the + // file is not extracted. + // + // If both PCLZIP_OPT_PATH and PCLZIP_OPT_ADD_PATH aoptions + // are used, the path indicated in PCLZIP_OPT_ADD_PATH is append + // at the end of the path value of PCLZIP_OPT_PATH. + // Parameters : + // $p_path : Path where the files and directories are to be extracted + // $p_remove_path : First part ('root' part) of the memorized path + // (if any similar) to remove while extracting. + // Options : + // PCLZIP_OPT_PATH : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_CB_PRE_EXTRACT : + // PCLZIP_CB_POST_EXTRACT : + // Return Values : + // 0 or a negative value on failure, + // The list of the extracted files, with a status of the action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + function extract() + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Set default values + $v_options = array(); +// $v_path = "./"; + $v_path = ''; + $v_remove_path = ""; + $v_remove_all_path = false; + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Default values for option + $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; + + // ----- Look for arguments + if ($v_size > 0) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_PATH => 'optional', + PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_EXTRACT => 'optional', + PCLZIP_CB_POST_EXTRACT => 'optional', + PCLZIP_OPT_SET_CHMOD => 'optional', + PCLZIP_OPT_BY_NAME => 'optional', + PCLZIP_OPT_BY_EREG => 'optional', + PCLZIP_OPT_BY_PREG => 'optional', + PCLZIP_OPT_BY_INDEX => 'optional', + PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', + PCLZIP_OPT_EXTRACT_IN_OUTPUT => 'optional', + PCLZIP_OPT_REPLACE_NEWER => 'optional' + ,PCLZIP_OPT_STOP_ON_ERROR => 'optional' + ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + )); + if ($v_result != 1) { + return 0; + } + + // ----- Set the arguments + if (isset($v_options[PCLZIP_OPT_PATH])) { + $v_path = $v_options[PCLZIP_OPT_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { + $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { + $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; + } + if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { + // ----- Check for '/' in last path char + if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { + $v_path .= '/'; + } + $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; + } + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + + // ----- Get the first argument + $v_path = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_remove_path = $v_arg_list[1]; + } + else if ($v_size > 2) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + + // ----- Return + return 0; + } + } + } + + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); + + // ----- Trace + + // ----- Call the extracting fct + $p_list = array(); + $v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, + $v_remove_all_path, $v_options); + if ($v_result < 1) { + unset($p_list); + return(0); + } + + // ----- Return + return $p_list; + } + // -------------------------------------------------------------------------------- + + + // -------------------------------------------------------------------------------- + // Function : + // extractByIndex($p_index, $p_path="./", $p_remove_path="") + // extractByIndex($p_index, [$p_option, $p_option_value, ...]) + // Description : + // This method supports two synopsis. The first one is historical. + // This method is doing a partial extract of the archive. + // The extracted files or folders are identified by their index in the + // archive (from 0 to n). + // Note that if the index identify a folder, only the folder entry is + // extracted, not all the files included in the archive. + // Parameters : + // $p_index : A single index (integer) or a string of indexes of files to + // extract. The form of the string is "0,4-6,8-12" with only numbers + // and '-' for range or ',' to separate ranges. No spaces or ';' + // are allowed. + // $p_path : Path where the files and directories are to be extracted + // $p_remove_path : First part ('root' part) of the memorized path + // (if any similar) to remove while extracting. + // Options : + // PCLZIP_OPT_PATH : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_OPT_EXTRACT_AS_STRING : The files are extracted as strings and + // not as files. + // The resulting content is in a new field 'content' in the file + // structure. + // This option must be used alone (any other options are ignored). + // PCLZIP_CB_PRE_EXTRACT : + // PCLZIP_CB_POST_EXTRACT : + // Return Values : + // 0 on failure, + // The list of the extracted files, with a status of the action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + //function extractByIndex($p_index, options...) + function extractByIndex($p_index) + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Set default values + $v_options = array(); +// $v_path = "./"; + $v_path = ''; + $v_remove_path = ""; + $v_remove_all_path = false; + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Default values for option + $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; + + // ----- Look for arguments + if ($v_size > 1) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Remove form the options list the first argument + array_shift($v_arg_list); + $v_size--; + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_PATH => 'optional', + PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_EXTRACT => 'optional', + PCLZIP_CB_POST_EXTRACT => 'optional', + PCLZIP_OPT_SET_CHMOD => 'optional', + PCLZIP_OPT_REPLACE_NEWER => 'optional' + ,PCLZIP_OPT_STOP_ON_ERROR => 'optional' + ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + )); + if ($v_result != 1) { + return 0; + } + + // ----- Set the arguments + if (isset($v_options[PCLZIP_OPT_PATH])) { + $v_path = $v_options[PCLZIP_OPT_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { + $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { + $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; + } + if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { + // ----- Check for '/' in last path char + if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { + $v_path .= '/'; + } + $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; + } + if (!isset($v_options[PCLZIP_OPT_EXTRACT_AS_STRING])) { + $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; + } + else { + } + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + + // ----- Get the first argument + $v_path = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_remove_path = $v_arg_list[1]; + } + else if ($v_size > 2) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + + // ----- Return + return 0; + } + } + } + + // ----- Trace + + // ----- Trick + // Here I want to reuse extractByRule(), so I need to parse the $p_index + // with privParseOptions() + $v_arg_trick = array (PCLZIP_OPT_BY_INDEX, $p_index); + $v_options_trick = array(); + $v_result = $this->privParseOptions($v_arg_trick, sizeof($v_arg_trick), $v_options_trick, + array (PCLZIP_OPT_BY_INDEX => 'optional' )); + if ($v_result != 1) { + return 0; + } + $v_options[PCLZIP_OPT_BY_INDEX] = $v_options_trick[PCLZIP_OPT_BY_INDEX]; + + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); + + // ----- Call the extracting fct + if (($v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options)) < 1) { + return(0); + } + + // ----- Return + return $p_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : + // delete([$p_option, $p_option_value, ...]) + // Description : + // This method removes files from the archive. + // If no parameters are given, then all the archive is emptied. + // Parameters : + // None or optional arguments. + // Options : + // PCLZIP_OPT_BY_INDEX : + // PCLZIP_OPT_BY_NAME : + // PCLZIP_OPT_BY_EREG : + // PCLZIP_OPT_BY_PREG : + // Return Values : + // 0 on failure, + // The list of the files which are still present in the archive. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + function delete() + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Set default values + $v_options = array(); + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Look for arguments + if ($v_size > 0) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_BY_NAME => 'optional', + PCLZIP_OPT_BY_EREG => 'optional', + PCLZIP_OPT_BY_PREG => 'optional', + PCLZIP_OPT_BY_INDEX => 'optional' )); + if ($v_result != 1) { + return 0; + } + } + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Call the delete fct + $v_list = array(); + if (($v_result = $this->privDeleteByRule($v_list, $v_options)) != 1) { + $this->privSwapBackMagicQuotes(); + unset($v_list); + return(0); + } + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : deleteByIndex() + // Description : + // ***** Deprecated ***** + // delete(PCLZIP_OPT_BY_INDEX, $p_index) should be prefered. + // -------------------------------------------------------------------------------- + function deleteByIndex($p_index) + { + + $p_list = $this->delete(PCLZIP_OPT_BY_INDEX, $p_index); + + // ----- Return + return $p_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : properties() + // Description : + // This method gives the properties of the archive. + // The properties are : + // nb : Number of files in the archive + // comment : Comment associated with the archive file + // status : not_exist, ok + // Parameters : + // None + // Return Values : + // 0 on failure, + // An array with the archive properties. + // -------------------------------------------------------------------------------- + function properties() + { + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + $this->privSwapBackMagicQuotes(); + return(0); + } + + // ----- Default properties + $v_prop = array(); + $v_prop['comment'] = ''; + $v_prop['nb'] = 0; + $v_prop['status'] = 'not_exist'; + + // ----- Look if file exists + if (@is_file($this->zipname)) + { + // ----- Open the zip file + if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) + { + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); + + // ----- Return + return 0; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privSwapBackMagicQuotes(); + return 0; + } + + // ----- Close the zip file + $this->privCloseFd(); + + // ----- Set the user attributes + $v_prop['comment'] = $v_central_dir['comment']; + $v_prop['nb'] = $v_central_dir['entries']; + $v_prop['status'] = 'ok'; + } + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_prop; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : duplicate() + // Description : + // This method creates an archive by copying the content of an other one. If + // the archive already exist, it is replaced by the new one without any warning. + // Parameters : + // $p_archive : The filename of a valid archive, or + // a valid PclZip object. + // Return Values : + // 1 on success. + // 0 or a negative value on error (error code). + // -------------------------------------------------------------------------------- + function duplicate($p_archive) + { + $v_result = 1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Look if the $p_archive is a PclZip object + if ((is_object($p_archive)) && (get_class($p_archive) == 'pclzip')) + { + + // ----- Duplicate the archive + $v_result = $this->privDuplicate($p_archive->zipname); + } + + // ----- Look if the $p_archive is a string (so a filename) + else if (is_string($p_archive)) + { + + // ----- Check that $p_archive is a valid zip file + // TBC : Should also check the archive format + if (!is_file($p_archive)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "No file with filename '".$p_archive."'"); + $v_result = PCLZIP_ERR_MISSING_FILE; + } + else { + // ----- Duplicate the archive + $v_result = $this->privDuplicate($p_archive); + } + } + + // ----- Invalid variable + else + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); + $v_result = PCLZIP_ERR_INVALID_PARAMETER; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : merge() + // Description : + // This method merge the $p_archive_to_add archive at the end of the current + // one ($this). + // If the archive ($this) does not exist, the merge becomes a duplicate. + // If the $p_archive_to_add archive does not exist, the merge is a success. + // Parameters : + // $p_archive_to_add : It can be directly the filename of a valid zip archive, + // or a PclZip object archive. + // Return Values : + // 1 on success, + // 0 or negative values on error (see below). + // -------------------------------------------------------------------------------- + function merge($p_archive_to_add) + { + $v_result = 1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Look if the $p_archive_to_add is a PclZip object + if ((is_object($p_archive_to_add)) && (get_class($p_archive_to_add) == 'pclzip')) + { + + // ----- Merge the archive + $v_result = $this->privMerge($p_archive_to_add); + } + + // ----- Look if the $p_archive_to_add is a string (so a filename) + else if (is_string($p_archive_to_add)) + { + + // ----- Create a temporary archive + $v_object_archive = new PclZip($p_archive_to_add); + + // ----- Merge the archive + $v_result = $this->privMerge($v_object_archive); + } + + // ----- Invalid variable + else + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); + $v_result = PCLZIP_ERR_INVALID_PARAMETER; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + + + // -------------------------------------------------------------------------------- + // Function : errorCode() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function errorCode() + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + return(PclErrorCode()); + } + else { + return($this->error_code); + } + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : errorName() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function errorName($p_with_code=false) + { + $v_name = array ( PCLZIP_ERR_NO_ERROR => 'PCLZIP_ERR_NO_ERROR', + PCLZIP_ERR_WRITE_OPEN_FAIL => 'PCLZIP_ERR_WRITE_OPEN_FAIL', + PCLZIP_ERR_READ_OPEN_FAIL => 'PCLZIP_ERR_READ_OPEN_FAIL', + PCLZIP_ERR_INVALID_PARAMETER => 'PCLZIP_ERR_INVALID_PARAMETER', + PCLZIP_ERR_MISSING_FILE => 'PCLZIP_ERR_MISSING_FILE', + PCLZIP_ERR_FILENAME_TOO_LONG => 'PCLZIP_ERR_FILENAME_TOO_LONG', + PCLZIP_ERR_INVALID_ZIP => 'PCLZIP_ERR_INVALID_ZIP', + PCLZIP_ERR_BAD_EXTRACTED_FILE => 'PCLZIP_ERR_BAD_EXTRACTED_FILE', + PCLZIP_ERR_DIR_CREATE_FAIL => 'PCLZIP_ERR_DIR_CREATE_FAIL', + PCLZIP_ERR_BAD_EXTENSION => 'PCLZIP_ERR_BAD_EXTENSION', + PCLZIP_ERR_BAD_FORMAT => 'PCLZIP_ERR_BAD_FORMAT', + PCLZIP_ERR_DELETE_FILE_FAIL => 'PCLZIP_ERR_DELETE_FILE_FAIL', + PCLZIP_ERR_RENAME_FILE_FAIL => 'PCLZIP_ERR_RENAME_FILE_FAIL', + PCLZIP_ERR_BAD_CHECKSUM => 'PCLZIP_ERR_BAD_CHECKSUM', + PCLZIP_ERR_INVALID_ARCHIVE_ZIP => 'PCLZIP_ERR_INVALID_ARCHIVE_ZIP', + PCLZIP_ERR_MISSING_OPTION_VALUE => 'PCLZIP_ERR_MISSING_OPTION_VALUE', + PCLZIP_ERR_INVALID_OPTION_VALUE => 'PCLZIP_ERR_INVALID_OPTION_VALUE', + PCLZIP_ERR_UNSUPPORTED_COMPRESSION => 'PCLZIP_ERR_UNSUPPORTED_COMPRESSION', + PCLZIP_ERR_UNSUPPORTED_ENCRYPTION => 'PCLZIP_ERR_UNSUPPORTED_ENCRYPTION' + ,PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE => 'PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE' + ,PCLZIP_ERR_DIRECTORY_RESTRICTION => 'PCLZIP_ERR_DIRECTORY_RESTRICTION' + ); + + if (isset($v_name[$this->error_code])) { + $v_value = $v_name[$this->error_code]; + } + else { + $v_value = 'NoName'; + } + + if ($p_with_code) { + return($v_value.' ('.$this->error_code.')'); + } + else { + return($v_value); + } + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : errorInfo() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function errorInfo($p_full=false) + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + return(PclErrorString()); + } + else { + if ($p_full) { + return($this->errorName(true)." : ".$this->error_string); + } + else { + return($this->error_string." [code ".$this->error_code."]"); + } + } + } + // -------------------------------------------------------------------------------- + + +// -------------------------------------------------------------------------------- +// ***** UNDER THIS LINE ARE DEFINED PRIVATE INTERNAL FUNCTIONS ***** +// ***** ***** +// ***** THESES FUNCTIONS MUST NOT BE USED DIRECTLY ***** +// -------------------------------------------------------------------------------- + + + + // -------------------------------------------------------------------------------- + // Function : privCheckFormat() + // Description : + // This method check that the archive exists and is a valid zip archive. + // Several level of check exists. (futur) + // Parameters : + // $p_level : Level of check. Default 0. + // 0 : Check the first bytes (magic codes) (default value)) + // 1 : 0 + Check the central directory (futur) + // 2 : 1 + Check each file header (futur) + // Return Values : + // true on success, + // false on error, the error code is set. + // -------------------------------------------------------------------------------- + function privCheckFormat($p_level=0) + { + $v_result = true; + + // ----- Reset the file system cache + clearstatcache(); + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Look if the file exits + if (!is_file($this->zipname)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "Missing archive file '".$this->zipname."'"); + return(false); + } + + // ----- Check that the file is readeable + if (!is_readable($this->zipname)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to read archive '".$this->zipname."'"); + return(false); + } + + // ----- Check the magic code + // TBC + + // ----- Check the central header + // TBC + + // ----- Check each file header + // TBC + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privParseOptions() + // Description : + // This internal methods reads the variable list of arguments ($p_options_list, + // $p_size) and generate an array with the options and values ($v_result_list). + // $v_requested_options contains the options that can be present and those that + // must be present. + // $v_requested_options is an array, with the option value as key, and 'optional', + // or 'mandatory' as value. + // Parameters : + // See above. + // Return Values : + // 1 on success. + // 0 on failure. + // -------------------------------------------------------------------------------- + function privParseOptions(&$p_options_list, $p_size, &$v_result_list, $v_requested_options=false) + { + $v_result=1; + + // ----- Read the options + $i=0; + while ($i<$p_size) { + + // ----- Check if the option is supported + if (!isset($v_requested_options[$p_options_list[$i]])) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid optional parameter '".$p_options_list[$i]."' for this method"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for next option + switch ($p_options_list[$i]) { + // ----- Look for options that request a path value + case PCLZIP_OPT_PATH : + case PCLZIP_OPT_REMOVE_PATH : + case PCLZIP_OPT_ADD_PATH : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE); + $i++; + break; + + case PCLZIP_OPT_TEMP_FILE_THRESHOLD : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + return PclZip::errorCode(); + } + + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); + return PclZip::errorCode(); + } + + // ----- Check the value + $v_value = $p_options_list[$i+1]; + if ((!is_integer($v_value)) || ($v_value<0)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Integer expected for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + return PclZip::errorCode(); + } + + // ----- Get the value (and convert it in bytes) + $v_result_list[$p_options_list[$i]] = $v_value*1048576; + $i++; + break; + + case PCLZIP_OPT_TEMP_FILE_ON : + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); + return PclZip::errorCode(); + } + + $v_result_list[$p_options_list[$i]] = true; + break; + + case PCLZIP_OPT_TEMP_FILE_OFF : + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_ON])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_ON'"); + return PclZip::errorCode(); + } + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_THRESHOLD'"); + return PclZip::errorCode(); + } + + $v_result_list[$p_options_list[$i]] = true; + break; + + case PCLZIP_OPT_EXTRACT_DIR_RESTRICTION : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if ( is_string($p_options_list[$i+1]) + && ($p_options_list[$i+1] != '')) { + $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE); + $i++; + } + else { + } + break; + + // ----- Look for options that request an array of string for value + case PCLZIP_OPT_BY_NAME : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if (is_string($p_options_list[$i+1])) { + $v_result_list[$p_options_list[$i]][0] = $p_options_list[$i+1]; + } + else if (is_array($p_options_list[$i+1])) { + $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; + } + else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + $i++; + break; + + // ----- Look for options that request an EREG or PREG expression + case PCLZIP_OPT_BY_EREG : + // ereg() is deprecated starting with PHP 5.3. Move PCLZIP_OPT_BY_EREG + // to PCLZIP_OPT_BY_PREG + $p_options_list[$i] = PCLZIP_OPT_BY_PREG; + case PCLZIP_OPT_BY_PREG : + //case PCLZIP_OPT_CRYPT : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if (is_string($p_options_list[$i+1])) { + $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; + } + else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + $i++; + break; + + // ----- Look for options that takes a string + case PCLZIP_OPT_COMMENT : + case PCLZIP_OPT_ADD_COMMENT : + case PCLZIP_OPT_PREPEND_COMMENT : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, + "Missing parameter value for option '" + .PclZipUtilOptionText($p_options_list[$i]) + ."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if (is_string($p_options_list[$i+1])) { + $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; + } + else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, + "Wrong parameter value for option '" + .PclZipUtilOptionText($p_options_list[$i]) + ."'"); + + // ----- Return + return PclZip::errorCode(); + } + $i++; + break; + + // ----- Look for options that request an array of index + case PCLZIP_OPT_BY_INDEX : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_work_list = array(); + if (is_string($p_options_list[$i+1])) { + + // ----- Remove spaces + $p_options_list[$i+1] = strtr($p_options_list[$i+1], ' ', ''); + + // ----- Parse items + $v_work_list = explode(",", $p_options_list[$i+1]); + } + else if (is_integer($p_options_list[$i+1])) { + $v_work_list[0] = $p_options_list[$i+1].'-'.$p_options_list[$i+1]; + } + else if (is_array($p_options_list[$i+1])) { + $v_work_list = $p_options_list[$i+1]; + } + else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Value must be integer, string or array for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Reduce the index list + // each index item in the list must be a couple with a start and + // an end value : [0,3], [5-5], [8-10], ... + // ----- Check the format of each item + $v_sort_flag=false; + $v_sort_value=0; + for ($j=0; $j= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; + $i++; + break; + + // ----- Look for options that request a call-back + case PCLZIP_CB_PRE_EXTRACT : + case PCLZIP_CB_POST_EXTRACT : + case PCLZIP_CB_PRE_ADD : + case PCLZIP_CB_POST_ADD : + /* for futur use + case PCLZIP_CB_PRE_DELETE : + case PCLZIP_CB_POST_DELETE : + case PCLZIP_CB_PRE_LIST : + case PCLZIP_CB_POST_LIST : + */ + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_function_name = $p_options_list[$i+1]; + + // ----- Check that the value is a valid existing function + if (!is_callable($v_function_name)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Function '".$v_function_name."()' is not an existing function for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Set the attribute + $v_result_list[$p_options_list[$i]] = $v_function_name; + $i++; + break; + + default : + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, + "Unknown parameter '" + .$p_options_list[$i]."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Next options + $i++; + } + + // ----- Look for mandatory options + if ($v_requested_options !== false) { + for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) { + // ----- Look for mandatory option + if ($v_requested_options[$key] == 'mandatory') { + // ----- Look if present + if (!isset($v_result_list[$key])) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")"); + + // ----- Return + return PclZip::errorCode(); + } + } + } + } + + // ----- Look for default values + if (!isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) { + + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privOptionDefaultThreshold() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privOptionDefaultThreshold(&$p_options) + { + $v_result=1; + + if (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) + || isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) { + return $v_result; + } + + // ----- Get 'memory_limit' configuration value + $v_memory_limit = ini_get('memory_limit'); + $v_memory_limit = trim($v_memory_limit); + $last = strtolower(substr($v_memory_limit, -1)); + + if($last == 'g') + //$v_memory_limit = $v_memory_limit*1024*1024*1024; + $v_memory_limit = $v_memory_limit*1073741824; + if($last == 'm') + //$v_memory_limit = $v_memory_limit*1024*1024; + $v_memory_limit = $v_memory_limit*1048576; + if($last == 'k') + $v_memory_limit = $v_memory_limit*1024; + + $p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] = floor($v_memory_limit*PCLZIP_TEMPORARY_FILE_RATIO); + + + // ----- Sanity check : No threshold if value lower than 1M + if ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] < 1048576) { + unset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privFileDescrParseAtt() + // Description : + // Parameters : + // Return Values : + // 1 on success. + // 0 on failure. + // -------------------------------------------------------------------------------- + function privFileDescrParseAtt(&$p_file_list, &$p_filedescr, $v_options, $v_requested_options=false) + { + $v_result=1; + + // ----- For each file in the list check the attributes + foreach ($p_file_list as $v_key => $v_value) { + + // ----- Check if the option is supported + if (!isset($v_requested_options[$v_key])) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file attribute '".$v_key."' for this file"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for attribute + switch ($v_key) { + case PCLZIP_ATT_FILE_NAME : + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['filename'] = PclZipUtilPathReduction($v_value); + + if ($p_filedescr['filename'] == '') { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty filename for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + break; + + case PCLZIP_ATT_FILE_NEW_SHORT_NAME : + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['new_short_name'] = PclZipUtilPathReduction($v_value); + + if ($p_filedescr['new_short_name'] == '') { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty short filename for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + break; + + case PCLZIP_ATT_FILE_NEW_FULL_NAME : + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['new_full_name'] = PclZipUtilPathReduction($v_value); + + if ($p_filedescr['new_full_name'] == '') { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty full filename for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + break; + + // ----- Look for options that takes a string + case PCLZIP_ATT_FILE_COMMENT : + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['comment'] = $v_value; + break; + + case PCLZIP_ATT_FILE_MTIME : + if (!is_integer($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". Integer expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['mtime'] = $v_value; + break; + + case PCLZIP_ATT_FILE_CONTENT : + $p_filedescr['content'] = $v_value; + break; + + default : + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, + "Unknown parameter '".$v_key."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for mandatory options + if ($v_requested_options !== false) { + for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) { + // ----- Look for mandatory option + if ($v_requested_options[$key] == 'mandatory') { + // ----- Look if present + if (!isset($p_file_list[$key])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")"); + return PclZip::errorCode(); + } + } + } + } + + // end foreach + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privFileDescrExpand() + // Description : + // This method look for each item of the list to see if its a file, a folder + // or a string to be added as file. For any other type of files (link, other) + // just ignore the item. + // Then prepare the information that will be stored for that file. + // When its a folder, expand the folder with all the files that are in that + // folder (recursively). + // Parameters : + // Return Values : + // 1 on success. + // 0 on failure. + // -------------------------------------------------------------------------------- + function privFileDescrExpand(&$p_filedescr_list, &$p_options) + { + $v_result=1; + + // ----- Create a result list + $v_result_list = array(); + + // ----- Look each entry + for ($i=0; $iprivCalculateStoredFilename($v_descr, $p_options); + + // ----- Add the descriptor in result list + $v_result_list[sizeof($v_result_list)] = $v_descr; + + // ----- Look for folder + if ($v_descr['type'] == 'folder') { + // ----- List of items in folder + $v_dirlist_descr = array(); + $v_dirlist_nb = 0; + if ($v_folder_handler = @opendir($v_descr['filename'])) { + while (($v_item_handler = @readdir($v_folder_handler)) !== false) { + + // ----- Skip '.' and '..' + if (($v_item_handler == '.') || ($v_item_handler == '..')) { + continue; + } + + // ----- Compose the full filename + $v_dirlist_descr[$v_dirlist_nb]['filename'] = $v_descr['filename'].'/'.$v_item_handler; + + // ----- Look for different stored filename + // Because the name of the folder was changed, the name of the + // files/sub-folders also change + if (($v_descr['stored_filename'] != $v_descr['filename']) + && (!isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH]))) { + if ($v_descr['stored_filename'] != '') { + $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_descr['stored_filename'].'/'.$v_item_handler; + } + else { + $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_item_handler; + } + } + + $v_dirlist_nb++; + } + + @closedir($v_folder_handler); + } + else { + // TBC : unable to open folder in read mode + } + + // ----- Expand each element of the list + if ($v_dirlist_nb != 0) { + // ----- Expand + if (($v_result = $this->privFileDescrExpand($v_dirlist_descr, $p_options)) != 1) { + return $v_result; + } + + // ----- Concat the resulting list + $v_result_list = array_merge($v_result_list, $v_dirlist_descr); + } + else { + } + + // ----- Free local array + unset($v_dirlist_descr); + } + } + + // ----- Get the result list + $p_filedescr_list = $v_result_list; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCreate() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privCreate($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result=1; + $v_list_detail = array(); + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Open the file in write mode + if (($v_result = $this->privOpenFd('wb')) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Add the list of files + $v_result = $this->privAddList($p_filedescr_list, $p_result_list, $p_options); + + // ----- Close + $this->privCloseFd(); + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAdd() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privAdd($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result=1; + $v_list_detail = array(); + + // ----- Look if the archive exists or is empty + if ((!is_file($this->zipname)) || (filesize($this->zipname) == 0)) + { + + // ----- Do a create + $v_result = $this->privCreate($p_filedescr_list, $p_result_list, $p_options); + + // ----- Return + return $v_result; + } + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Open the zip file + if (($v_result=$this->privOpenFd('rb')) != 1) + { + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + // ----- Go to beginning of File + @rewind($this->zip_fd); + + // ----- Creates a temporay file + $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; + + // ----- Open the temporary file in write mode + if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) + { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Copy the files from the archive to the temporary file + // TBC : Here I should better append the file and go back to erase the central dir + $v_size = $v_central_dir['offset']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($this->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Swap the file descriptor + // Here is a trick : I swap the temporary fd with the zip fd, in order to use + // the following methods on the temporary fil and not the real archive + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + + // ----- Add the files + $v_header_list = array(); + if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1) + { + fclose($v_zip_temp_fd); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($this->zip_fd); + + // ----- Copy the block of file headers from the old archive + $v_size = $v_central_dir['size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($v_zip_temp_fd, $v_read_size); + @fwrite($this->zip_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Create the Central Dir files header + for ($i=0, $v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { + fclose($v_zip_temp_fd); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + $v_count++; + } + + // ----- Transform the header to a 'usable' info + $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); + } + + // ----- Zip file comment + $v_comment = $v_central_dir['comment']; + if (isset($p_options[PCLZIP_OPT_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_COMMENT]; + } + if (isset($p_options[PCLZIP_OPT_ADD_COMMENT])) { + $v_comment = $v_comment.$p_options[PCLZIP_OPT_ADD_COMMENT]; + } + if (isset($p_options[PCLZIP_OPT_PREPEND_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_PREPEND_COMMENT].$v_comment; + } + + // ----- Calculate the size of the central header + $v_size = @ftell($this->zip_fd)-$v_offset; + + // ----- Create the central dir footer + if (($v_result = $this->privWriteCentralHeader($v_count+$v_central_dir['entries'], $v_size, $v_offset, $v_comment)) != 1) + { + // ----- Reset the file list + unset($v_header_list); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + + // ----- Swap back the file descriptor + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + + // ----- Close + $this->privCloseFd(); + + // ----- Close the temporary file + @fclose($v_zip_temp_fd); + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Delete the zip file + // TBC : I should test the result ... + @unlink($this->zipname); + + // ----- Rename the temporary file + // TBC : I should test the result ... + //@rename($v_zip_temp_name, $this->zipname); + PclZipUtilRename($v_zip_temp_name, $this->zipname); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privOpenFd() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privOpenFd($p_mode) + { + $v_result=1; + + // ----- Look if already open + if ($this->zip_fd != 0) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Zip file \''.$this->zipname.'\' already open'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Open the zip file + if (($this->zip_fd = @fopen($this->zipname, $p_mode)) == 0) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in '.$p_mode.' mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCloseFd() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privCloseFd() + { + $v_result=1; + + if ($this->zip_fd != 0) + @fclose($this->zip_fd); + $this->zip_fd = 0; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddList() + // Description : + // $p_add_dir and $p_remove_dir will give the ability to memorize a path which is + // different from the real path of the file. This is usefull if you want to have PclTar + // running in any directory, and memorize relative path from an other directory. + // Parameters : + // $p_list : An array containing the file or directory names to add in the tar + // $p_result_list : list of added files with their properties (specially the status field) + // $p_add_dir : Path to add in the filename path archived + // $p_remove_dir : Path to remove in the filename path archived + // Return Values : + // -------------------------------------------------------------------------------- +// function privAddList($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options) + function privAddList($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result=1; + + // ----- Add the files + $v_header_list = array(); + if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($this->zip_fd); + + // ----- Create the Central Dir files header + for ($i=0,$v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { + // ----- Return + return $v_result; + } + $v_count++; + } + + // ----- Transform the header to a 'usable' info + $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); + } + + // ----- Zip file comment + $v_comment = ''; + if (isset($p_options[PCLZIP_OPT_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_COMMENT]; + } + + // ----- Calculate the size of the central header + $v_size = @ftell($this->zip_fd)-$v_offset; + + // ----- Create the central dir footer + if (($v_result = $this->privWriteCentralHeader($v_count, $v_size, $v_offset, $v_comment)) != 1) + { + // ----- Reset the file list + unset($v_header_list); + + // ----- Return + return $v_result; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddFileList() + // Description : + // Parameters : + // $p_filedescr_list : An array containing the file description + // or directory names to add in the zip + // $p_result_list : list of added files with their properties (specially the status field) + // Return Values : + // -------------------------------------------------------------------------------- + function privAddFileList($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result=1; + $v_header = array(); + + // ----- Recuperate the current number of elt in list + $v_nb = sizeof($p_result_list); + + // ----- Loop on the files + for ($j=0; ($jprivAddFile($p_filedescr_list[$j], $v_header, + $p_options); + if ($v_result != 1) { + return $v_result; + } + + // ----- Store the file infos + $p_result_list[$v_nb++] = $v_header; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddFile() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privAddFile($p_filedescr, &$p_header, &$p_options) + { + $v_result=1; + + // ----- Working variable + $p_filename = $p_filedescr['filename']; + + // TBC : Already done in the fileAtt check ... ? + if ($p_filename == "") { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file list parameter (invalid or empty list)"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for a stored different filename + /* TBC : Removed + if (isset($p_filedescr['stored_filename'])) { + $v_stored_filename = $p_filedescr['stored_filename']; + } + else { + $v_stored_filename = $p_filedescr['stored_filename']; + } + */ + + // ----- Set the file properties + clearstatcache(); + $p_header['version'] = 20; + $p_header['version_extracted'] = 10; + $p_header['flag'] = 0; + $p_header['compression'] = 0; + $p_header['crc'] = 0; + $p_header['compressed_size'] = 0; + $p_header['filename_len'] = strlen($p_filename); + $p_header['extra_len'] = 0; + $p_header['disk'] = 0; + $p_header['internal'] = 0; + $p_header['offset'] = 0; + $p_header['filename'] = $p_filename; +// TBC : Removed $p_header['stored_filename'] = $v_stored_filename; + $p_header['stored_filename'] = $p_filedescr['stored_filename']; + $p_header['extra'] = ''; + $p_header['status'] = 'ok'; + $p_header['index'] = -1; + + // ----- Look for regular file + if ($p_filedescr['type']=='file') { + $p_header['external'] = 0x00000000; + $p_header['size'] = filesize($p_filename); + } + + // ----- Look for regular folder + else if ($p_filedescr['type']=='folder') { + $p_header['external'] = 0x00000010; + $p_header['mtime'] = filemtime($p_filename); + $p_header['size'] = filesize($p_filename); + } + + // ----- Look for virtual file + else if ($p_filedescr['type'] == 'virtual_file') { + $p_header['external'] = 0x00000000; + $p_header['size'] = strlen($p_filedescr['content']); + } + + + // ----- Look for filetime + if (isset($p_filedescr['mtime'])) { + $p_header['mtime'] = $p_filedescr['mtime']; + } + else if ($p_filedescr['type'] == 'virtual_file') { + $p_header['mtime'] = time(); + } + else { + $p_header['mtime'] = filemtime($p_filename); + } + + // ------ Look for file comment + if (isset($p_filedescr['comment'])) { + $p_header['comment_len'] = strlen($p_filedescr['comment']); + $p_header['comment'] = $p_filedescr['comment']; + } + else { + $p_header['comment_len'] = 0; + $p_header['comment'] = ''; + } + + // ----- Look for pre-add callback + if (isset($p_options[PCLZIP_CB_PRE_ADD])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_header, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_ADD].'(PCLZIP_CB_PRE_ADD, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_PRE_ADD](PCLZIP_CB_PRE_ADD, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_header['status'] = "skipped"; + $v_result = 1; + } + + // ----- Update the informations + // Only some fields can be modified + if ($p_header['stored_filename'] != $v_local_header['stored_filename']) { + $p_header['stored_filename'] = PclZipUtilPathReduction($v_local_header['stored_filename']); + } + } + + // ----- Look for empty stored filename + if ($p_header['stored_filename'] == "") { + $p_header['status'] = "filtered"; + } + + // ----- Check the path length + if (strlen($p_header['stored_filename']) > 0xFF) { + $p_header['status'] = 'filename_too_long'; + } + + // ----- Look if no error, or file not skipped + if ($p_header['status'] == 'ok') { + + // ----- Look for a file + if ($p_filedescr['type'] == 'file') { + // ----- Look for using temporary file to zip + if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) + && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) + || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) + && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_header['size'])) ) ) { + $v_result = $this->privAddFileUsingTempFile($p_filedescr, $p_header, $p_options); + if ($v_result < PCLZIP_ERR_NO_ERROR) { + return $v_result; + } + } + + // ----- Use "in memory" zip algo + else { + + // ----- Open the source file + if (($v_file = @fopen($p_filename, "rb")) == 0) { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); + return PclZip::errorCode(); + } + + // ----- Read the file content + $v_content = @fread($v_file, $p_header['size']); + + // ----- Close the file + @fclose($v_file); + + // ----- Calculate the CRC + $p_header['crc'] = @crc32($v_content); + + // ----- Look for no compression + if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { + // ----- Set header parameters + $p_header['compressed_size'] = $p_header['size']; + $p_header['compression'] = 0; + } + + // ----- Look for normal compression + else { + // ----- Compress the content + $v_content = @gzdeflate($v_content); + + // ----- Set header parameters + $p_header['compressed_size'] = strlen($v_content); + $p_header['compression'] = 8; + } + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { + @fclose($v_file); + return $v_result; + } + + // ----- Write the compressed (or not) content + @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']); + + } + + } + + // ----- Look for a virtual file (a file from string) + else if ($p_filedescr['type'] == 'virtual_file') { + + $v_content = $p_filedescr['content']; + + // ----- Calculate the CRC + $p_header['crc'] = @crc32($v_content); + + // ----- Look for no compression + if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { + // ----- Set header parameters + $p_header['compressed_size'] = $p_header['size']; + $p_header['compression'] = 0; + } + + // ----- Look for normal compression + else { + // ----- Compress the content + $v_content = @gzdeflate($v_content); + + // ----- Set header parameters + $p_header['compressed_size'] = strlen($v_content); + $p_header['compression'] = 8; + } + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { + @fclose($v_file); + return $v_result; + } + + // ----- Write the compressed (or not) content + @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']); + } + + // ----- Look for a directory + else if ($p_filedescr['type'] == 'folder') { + // ----- Look for directory last '/' + if (@substr($p_header['stored_filename'], -1) != '/') { + $p_header['stored_filename'] .= '/'; + } + + // ----- Set the file properties + $p_header['size'] = 0; + //$p_header['external'] = 0x41FF0010; // Value for a folder : to be checked + $p_header['external'] = 0x00000010; // Value for a folder : to be checked + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) + { + return $v_result; + } + } + } + + // ----- Look for post-add callback + if (isset($p_options[PCLZIP_CB_POST_ADD])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_header, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_POST_ADD].'(PCLZIP_CB_POST_ADD, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_POST_ADD](PCLZIP_CB_POST_ADD, $v_local_header); + if ($v_result == 0) { + // ----- Ignored + $v_result = 1; + } + + // ----- Update the informations + // Nothing can be modified + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddFileUsingTempFile() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privAddFileUsingTempFile($p_filedescr, &$p_header, &$p_options) + { + $v_result=PCLZIP_ERR_NO_ERROR; + + // ----- Working variable + $p_filename = $p_filedescr['filename']; + + + // ----- Open the source file + if (($v_file = @fopen($p_filename, "rb")) == 0) { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); + return PclZip::errorCode(); + } + + // ----- Creates a compressed temporary file + $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz'; + if(function_exists('gzopen64')){ + $v_file_compressed = @gzopen64($v_gzip_temp_name, "wb"); + }else{ + $v_file_compressed = @gzopen($v_gzip_temp_name, "wb"); + } + if ($v_file_compressed == 0) { + fclose($v_file); + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode'); + return PclZip::errorCode(); + } + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = filesize($p_filename); + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($v_file, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @gzputs($v_file_compressed, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Close the file + @fclose($v_file); + @gzclose($v_file_compressed); + + // ----- Check the minimum file size + if (filesize($v_gzip_temp_name) < 18) { + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'gzip temporary file \''.$v_gzip_temp_name.'\' has invalid filesize - should be minimum 18 bytes'); + return PclZip::errorCode(); + } + + // ----- Extract the compressed attributes + if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); + return PclZip::errorCode(); + } + + // ----- Read the gzip file header + $v_binary_data = @fread($v_file_compressed, 10); + $v_data_header = unpack('a1id1/a1id2/a1cm/a1flag/Vmtime/a1xfl/a1os', $v_binary_data); + + // ----- Check some parameters + $v_data_header['os'] = bin2hex($v_data_header['os']); + + // ----- Read the gzip file footer + @fseek($v_file_compressed, filesize($v_gzip_temp_name)-8); + $v_binary_data = @fread($v_file_compressed, 8); + $v_data_footer = unpack('Vcrc/Vcompressed_size', $v_binary_data); + + // ----- Set the attributes + $p_header['compression'] = ord($v_data_header['cm']); + //$p_header['mtime'] = $v_data_header['mtime']; + $p_header['crc'] = $v_data_footer['crc']; + $p_header['compressed_size'] = filesize($v_gzip_temp_name)-18; + + // ----- Close the file + @fclose($v_file_compressed); + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { + return $v_result; + } + + // ----- Add the compressed data + if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) + { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); + return PclZip::errorCode(); + } + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + fseek($v_file_compressed, 10); + $v_size = $p_header['compressed_size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($v_file_compressed, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($this->zip_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Close the file + @fclose($v_file_compressed); + + // ----- Unlink the temporary file + @unlink($v_gzip_temp_name); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCalculateStoredFilename() + // Description : + // Based on file descriptor properties and global options, this method + // calculate the filename that will be stored in the archive. + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privCalculateStoredFilename(&$p_filedescr, &$p_options) + { + $v_result=1; + + // ----- Working variables + $p_filename = $p_filedescr['filename']; + if (isset($p_options[PCLZIP_OPT_ADD_PATH])) { + $p_add_dir = $p_options[PCLZIP_OPT_ADD_PATH]; + } + else { + $p_add_dir = ''; + } + if (isset($p_options[PCLZIP_OPT_REMOVE_PATH])) { + $p_remove_dir = $p_options[PCLZIP_OPT_REMOVE_PATH]; + } + else { + $p_remove_dir = ''; + } + if (isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { + $p_remove_all_dir = $p_options[PCLZIP_OPT_REMOVE_ALL_PATH]; + } + else { + $p_remove_all_dir = 0; + } + + + // ----- Look for full name change + if (isset($p_filedescr['new_full_name'])) { + // ----- Remove drive letter if any + $v_stored_filename = PclZipUtilTranslateWinPath($p_filedescr['new_full_name']); + } + + // ----- Look for path and/or short name change + else { + + // ----- Look for short name change + // Its when we cahnge just the filename but not the path + if (isset($p_filedescr['new_short_name'])) { + $v_path_info = pathinfo($p_filename); + $v_dir = ''; + if ($v_path_info['dirname'] != '') { + $v_dir = $v_path_info['dirname'].'/'; + } + $v_stored_filename = $v_dir.$p_filedescr['new_short_name']; + } + else { + // ----- Calculate the stored filename + $v_stored_filename = $p_filename; + } + + // ----- Look for all path to remove + if ($p_remove_all_dir) { + $v_stored_filename = basename($p_filename); + } + // ----- Look for partial path remove + else if ($p_remove_dir != "") { + if (substr($p_remove_dir, -1) != '/') + $p_remove_dir .= "/"; + + if ( (substr($p_filename, 0, 2) == "./") + || (substr($p_remove_dir, 0, 2) == "./")) { + + if ( (substr($p_filename, 0, 2) == "./") + && (substr($p_remove_dir, 0, 2) != "./")) { + $p_remove_dir = "./".$p_remove_dir; + } + if ( (substr($p_filename, 0, 2) != "./") + && (substr($p_remove_dir, 0, 2) == "./")) { + $p_remove_dir = substr($p_remove_dir, 2); + } + } + + $v_compare = PclZipUtilPathInclusion($p_remove_dir, + $v_stored_filename); + if ($v_compare > 0) { + if ($v_compare == 2) { + $v_stored_filename = ""; + } + else { + $v_stored_filename = substr($v_stored_filename, + strlen($p_remove_dir)); + } + } + } + + // ----- Remove drive letter if any + $v_stored_filename = PclZipUtilTranslateWinPath($v_stored_filename); + + // ----- Look for path to add + if ($p_add_dir != "") { + if (substr($p_add_dir, -1) == "/") + $v_stored_filename = $p_add_dir.$v_stored_filename; + else + $v_stored_filename = $p_add_dir."/".$v_stored_filename; + } + } + + // ----- Filename (reduce the path of stored name) + $v_stored_filename = PclZipUtilPathReduction($v_stored_filename); + $p_filedescr['stored_filename'] = $v_stored_filename; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privWriteFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privWriteFileHeader(&$p_header) + { + $v_result=1; + + // ----- Store the offset position of the file + $p_header['offset'] = ftell($this->zip_fd); + + // ----- Transform UNIX mtime to DOS format mdate/mtime + $v_date = getdate($p_header['mtime']); + $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; + $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; + + // ----- Packed data + $v_binary_data = pack("VvvvvvVVVvv", 0x04034b50, + $p_header['version_extracted'], $p_header['flag'], + $p_header['compression'], $v_mtime, $v_mdate, + $p_header['crc'], $p_header['compressed_size'], + $p_header['size'], + strlen($p_header['stored_filename']), + $p_header['extra_len']); + + // ----- Write the first 148 bytes of the header in the archive + fputs($this->zip_fd, $v_binary_data, 30); + + // ----- Write the variable fields + if (strlen($p_header['stored_filename']) != 0) + { + fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); + } + if ($p_header['extra_len'] != 0) + { + fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privWriteCentralFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privWriteCentralFileHeader(&$p_header) + { + $v_result=1; + + // TBC + //for(reset($p_header); $key = key($p_header); next($p_header)) { + //} + + // ----- Transform UNIX mtime to DOS format mdate/mtime + $v_date = getdate($p_header['mtime']); + $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; + $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; + + + // ----- Packed data + $v_binary_data = pack("VvvvvvvVVVvvvvvVV", 0x02014b50, + $p_header['version'], $p_header['version_extracted'], + $p_header['flag'], $p_header['compression'], + $v_mtime, $v_mdate, $p_header['crc'], + $p_header['compressed_size'], $p_header['size'], + strlen($p_header['stored_filename']), + $p_header['extra_len'], $p_header['comment_len'], + $p_header['disk'], $p_header['internal'], + $p_header['external'], $p_header['offset']); + + // ----- Write the 42 bytes of the header in the zip file + fputs($this->zip_fd, $v_binary_data, 46); + + // ----- Write the variable fields + if (strlen($p_header['stored_filename']) != 0) + { + fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); + } + if ($p_header['extra_len'] != 0) + { + fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); + } + if ($p_header['comment_len'] != 0) + { + fputs($this->zip_fd, $p_header['comment'], $p_header['comment_len']); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privWriteCentralHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privWriteCentralHeader($p_nb_entries, $p_size, $p_offset, $p_comment) + { + $v_result=1; + + // ----- Packed data + $v_binary_data = pack("VvvvvVVv", 0x06054b50, 0, 0, $p_nb_entries, + $p_nb_entries, $p_size, + $p_offset, strlen($p_comment)); + + // ----- Write the 22 bytes of the header in the zip file + fputs($this->zip_fd, $v_binary_data, 22); + + // ----- Write the variable fields + if (strlen($p_comment) != 0) + { + fputs($this->zip_fd, $p_comment, strlen($p_comment)); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privList() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privList(&$p_list) + { + $v_result=1; + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Open the zip file + if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) + { + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + // ----- Go to beginning of Central Dir + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_central_dir['offset'])) + { + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read each entry + for ($i=0; $i<$v_central_dir['entries']; $i++) + { + // ----- Read the file header + if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) + { + $this->privSwapBackMagicQuotes(); + return $v_result; + } + $v_header['index'] = $i; + + // ----- Get the only interesting attributes + $this->privConvertHeader2FileInfo($v_header, $p_list[$i]); + unset($v_header); + } + + // ----- Close the zip file + $this->privCloseFd(); + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privConvertHeader2FileInfo() + // Description : + // This function takes the file informations from the central directory + // entries and extract the interesting parameters that will be given back. + // The resulting file infos are set in the array $p_info + // $p_info['filename'] : Filename with full path. Given by user (add), + // extracted in the filesystem (extract). + // $p_info['stored_filename'] : Stored filename in the archive. + // $p_info['size'] = Size of the file. + // $p_info['compressed_size'] = Compressed size of the file. + // $p_info['mtime'] = Last modification date of the file. + // $p_info['comment'] = Comment associated with the file. + // $p_info['folder'] = true/false : indicates if the entry is a folder or not. + // $p_info['status'] = status of the action on the file. + // $p_info['crc'] = CRC of the file content. + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privConvertHeader2FileInfo($p_header, &$p_info) + { + $v_result=1; + + // ----- Get the interesting attributes + $v_temp_path = PclZipUtilPathReduction($p_header['filename']); + $p_info['filename'] = $v_temp_path; + $v_temp_path = PclZipUtilPathReduction($p_header['stored_filename']); + $p_info['stored_filename'] = $v_temp_path; + $p_info['size'] = $p_header['size']; + $p_info['compressed_size'] = $p_header['compressed_size']; + $p_info['mtime'] = $p_header['mtime']; + $p_info['comment'] = $p_header['comment']; + $p_info['folder'] = (($p_header['external']&0x00000010)==0x00000010); + $p_info['index'] = $p_header['index']; + $p_info['status'] = $p_header['status']; + $p_info['crc'] = $p_header['crc']; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractByRule() + // Description : + // Extract a file or directory depending of rules (by index, by name, ...) + // Parameters : + // $p_file_list : An array where will be placed the properties of each + // extracted file + // $p_path : Path to add while writing the extracted files + // $p_remove_path : Path to remove (from the file memorized path) while writing the + // extracted files. If the path does not match the file path, + // the file is extracted with its memorized path. + // $p_remove_path does not apply to 'list' mode. + // $p_path and $p_remove_path are commulative. + // Return Values : + // 1 on success,0 or less on error (see error code list) + // -------------------------------------------------------------------------------- + function privExtractByRule(&$p_file_list, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) + { + $v_result=1; + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Check the path + if ( ($p_path == "") + || ( (substr($p_path, 0, 1) != "/") + && (substr($p_path, 0, 3) != "../") + && (substr($p_path,1,2)!=":/"))) + $p_path = "./".$p_path; + + // ----- Reduce the path last (and duplicated) '/' + if (($p_path != "./") && ($p_path != "/")) + { + // ----- Look for the path end '/' + while (substr($p_path, -1) == "/") + { + $p_path = substr($p_path, 0, strlen($p_path)-1); + } + } + + // ----- Look for path to remove format (should end by /) + if (($p_remove_path != "") && (substr($p_remove_path, -1) != '/')) + { + $p_remove_path .= '/'; + } + $p_remove_path_size = strlen($p_remove_path); + + // ----- Open the zip file + if (($v_result = $this->privOpenFd('rb')) != 1) + { + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Start at beginning of Central Dir + $v_pos_entry = $v_central_dir['offset']; + + // ----- Read each entry + $j_start = 0; + for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) + { + + // ----- Read next Central dir entry + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_pos_entry)) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the file header + $v_header = array(); + if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Store the index + $v_header['index'] = $i; + + // ----- Store the file position + $v_pos_entry = ftell($this->zip_fd); + + // ----- Look for the specific extract rules + $v_extract = false; + + // ----- Look for extract by name rule + if ( (isset($p_options[PCLZIP_OPT_BY_NAME])) + && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { + + // ----- Look if the filename is in the list + for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) + && (substr($v_header['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { + $v_extract = true; + } + } + // ----- Look for a filename + elseif ($v_header['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { + $v_extract = true; + } + } + } + + // ----- Look for extract by ereg rule + // ereg() is deprecated with PHP 5.3 + /* + else if ( (isset($p_options[PCLZIP_OPT_BY_EREG])) + && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { + + if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header['stored_filename'])) { + $v_extract = true; + } + } + */ + + // ----- Look for extract by preg rule + else if ( (isset($p_options[PCLZIP_OPT_BY_PREG])) + && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { + + if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header['stored_filename'])) { + $v_extract = true; + } + } + + // ----- Look for extract by index rule + else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX])) + && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { + + // ----- Look if the index is in the list + for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { + $v_extract = true; + } + if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { + $j_start = $j+1; + } + + if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) { + break; + } + } + } + + // ----- Look for no rule, which means extract all the archive + else { + $v_extract = true; + } + + // ----- Check compression method + if ( ($v_extract) + && ( ($v_header['compression'] != 8) + && ($v_header['compression'] != 0))) { + $v_header['status'] = 'unsupported_compression'; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + $this->privSwapBackMagicQuotes(); + + PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_COMPRESSION, + "Filename '".$v_header['stored_filename']."' is " + ."compressed by an unsupported compression " + ."method (".$v_header['compression'].") "); + + return PclZip::errorCode(); + } + } + + // ----- Check encrypted files + if (($v_extract) && (($v_header['flag'] & 1) == 1)) { + $v_header['status'] = 'unsupported_encryption'; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + $this->privSwapBackMagicQuotes(); + + PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, + "Unsupported encryption for " + ." filename '".$v_header['stored_filename'] + ."'"); + + return PclZip::errorCode(); + } + } + + // ----- Look for real extraction + if (($v_extract) && ($v_header['status'] != 'ok')) { + $v_result = $this->privConvertHeader2FileInfo($v_header, + $p_file_list[$v_nb_extracted++]); + if ($v_result != 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + $v_extract = false; + } + + // ----- Look for real extraction + if ($v_extract) + { + + // ----- Go to the file position + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_header['offset'])) + { + // ----- Close the zip file + $this->privCloseFd(); + + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for extraction as string + if ($p_options[PCLZIP_OPT_EXTRACT_AS_STRING]) { + + $v_string = ''; + + // ----- Extracting the file + $v_result1 = $this->privExtractFileAsString($v_header, $v_string, $p_options); + if ($v_result1 < 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result1; + } + + // ----- Get the only interesting attributes + if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted])) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Set the file content + $p_file_list[$v_nb_extracted]['content'] = $v_string; + + // ----- Next extracted file + $v_nb_extracted++; + + // ----- Look for user callback abort + if ($v_result1 == 2) { + break; + } + } + // ----- Look for extraction in standard output + elseif ( (isset($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) + && ($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) { + // ----- Extracting the file in standard output + $v_result1 = $this->privExtractFileInOutput($v_header, $p_options); + if ($v_result1 < 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result1; + } + + // ----- Get the only interesting attributes + if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + // ----- Look for user callback abort + if ($v_result1 == 2) { + break; + } + } + // ----- Look for normal extraction + else { + // ----- Extracting the file + $v_result1 = $this->privExtractFile($v_header, + $p_path, $p_remove_path, + $p_remove_all_path, + $p_options); + if ($v_result1 < 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result1; + } + + // ----- Get the only interesting attributes + if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Look for user callback abort + if ($v_result1 == 2) { + break; + } + } + } + } + + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFile() + // Description : + // Parameters : + // Return Values : + // + // 1 : ... ? + // PCLZIP_ERR_USER_ABORTED(2) : User ask for extraction stop in callback + // -------------------------------------------------------------------------------- + function privExtractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) + { + $v_result=1; + + // ----- Read the file header + if (($v_result = $this->privReadFileHeader($v_header)) != 1) + { + // ----- Return + return $v_result; + } + + + // ----- Check that the file header is coherent with $p_entry info + if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { + // TBC + } + + // ----- Look for all path to remove + if ($p_remove_all_path == true) { + // ----- Look for folder entry that not need to be extracted + if (($p_entry['external']&0x00000010)==0x00000010) { + + $p_entry['status'] = "filtered"; + + return $v_result; + } + + // ----- Get the basename of the path + $p_entry['filename'] = basename($p_entry['filename']); + } + + // ----- Look for path to remove + else if ($p_remove_path != "") + { + if (PclZipUtilPathInclusion($p_remove_path, $p_entry['filename']) == 2) + { + + // ----- Change the file status + $p_entry['status'] = "filtered"; + + // ----- Return + return $v_result; + } + + $p_remove_path_size = strlen($p_remove_path); + if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path) + { + + // ----- Remove the path + $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size); + + } + } + + // ----- Add the path + if ($p_path != '') { + $p_entry['filename'] = $p_path."/".$p_entry['filename']; + } + + // ----- Check a base_dir_restriction + if (isset($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION])) { + $v_inclusion + = PclZipUtilPathInclusion($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION], + $p_entry['filename']); + if ($v_inclusion == 0) { + + PclZip::privErrorLog(PCLZIP_ERR_DIRECTORY_RESTRICTION, + "Filename '".$p_entry['filename']."' is " + ."outside PCLZIP_OPT_EXTRACT_DIR_RESTRICTION"); + + return PclZip::errorCode(); + } + } + + // ----- Look for pre-extract callback + if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_entry['status'] = "skipped"; + $v_result = 1; + } + + // ----- Look for abort result + if ($v_result == 2) { + // ----- This status is internal and will be changed in 'skipped' + $p_entry['status'] = "aborted"; + $v_result = PCLZIP_ERR_USER_ABORTED; + } + + // ----- Update the informations + // Only some fields can be modified + $p_entry['filename'] = $v_local_header['filename']; + } + + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Look for specific actions while the file exist + if (file_exists($p_entry['filename'])) + { + + // ----- Look if file is a directory + if (is_dir($p_entry['filename'])) + { + + // ----- Change the file status + $p_entry['status'] = "already_a_directory"; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + // For historical reason first PclZip implementation does not stop + // when this kind of error occurs. + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + PclZip::privErrorLog(PCLZIP_ERR_ALREADY_A_DIRECTORY, + "Filename '".$p_entry['filename']."' is " + ."already used by an existing directory"); + + return PclZip::errorCode(); + } + } + // ----- Look if file is write protected + else if (!is_writeable($p_entry['filename'])) + { + + // ----- Change the file status + $p_entry['status'] = "write_protected"; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + // For historical reason first PclZip implementation does not stop + // when this kind of error occurs. + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, + "Filename '".$p_entry['filename']."' exists " + ."and is write protected"); + + return PclZip::errorCode(); + } + } + + // ----- Look if the extracted file is older + else if (filemtime($p_entry['filename']) > $p_entry['mtime']) + { + // ----- Change the file status + if ( (isset($p_options[PCLZIP_OPT_REPLACE_NEWER])) + && ($p_options[PCLZIP_OPT_REPLACE_NEWER]===true)) { + } + else { + $p_entry['status'] = "newer_exist"; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + // For historical reason first PclZip implementation does not stop + // when this kind of error occurs. + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, + "Newer version of '".$p_entry['filename']."' exists " + ."and option PCLZIP_OPT_REPLACE_NEWER is not selected"); + + return PclZip::errorCode(); + } + } + } + else { + } + } + + // ----- Check the directory availability and create it if necessary + else { + if ((($p_entry['external']&0x00000010)==0x00000010) || (substr($p_entry['filename'], -1) == '/')) + $v_dir_to_check = $p_entry['filename']; + else if (!strstr($p_entry['filename'], "/")) + $v_dir_to_check = ""; + else + $v_dir_to_check = dirname($p_entry['filename']); + + if (($v_result = $this->privDirCheck($v_dir_to_check, (($p_entry['external']&0x00000010)==0x00000010))) != 1) { + + // ----- Change the file status + $p_entry['status'] = "path_creation_fail"; + + // ----- Return + //return $v_result; + $v_result = 1; + } + } + } + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Do the extraction (if not a folder) + if (!(($p_entry['external']&0x00000010)==0x00000010)) + { + // ----- Look for not compressed file + if ($p_entry['compression'] == 0) { + + // ----- Opening destination file + if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) + { + + // ----- Change the file status + $p_entry['status'] = "write_error"; + + // ----- Return + return $v_result; + } + + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = $p_entry['compressed_size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($this->zip_fd, $v_read_size); + /* Try to speed up the code + $v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($v_dest_file, $v_binary_data, $v_read_size); + */ + @fwrite($v_dest_file, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Closing the destination file + fclose($v_dest_file); + + // ----- Change the file mtime + touch($p_entry['filename'], $p_entry['mtime']); + + + } + else { + // ----- TBC + // Need to be finished + if (($p_entry['flag'] & 1) == 1) { + PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, 'File \''.$p_entry['filename'].'\' is encrypted. Encrypted files are not supported.'); + return PclZip::errorCode(); + } + + + // ----- Look for using temporary file to unzip + if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) + && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) + || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) + && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_entry['size'])) ) ) { + $v_result = $this->privExtractFileUsingTempFile($p_entry, $p_options); + if ($v_result < PCLZIP_ERR_NO_ERROR) { + return $v_result; + } + } + + // ----- Look for extract in memory + else { + + + // ----- Read the compressed file in a buffer (one shot) + $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Decompress the file + $v_file_content = @gzinflate($v_buffer); + unset($v_buffer); + if ($v_file_content === FALSE) { + + // ----- Change the file status + // TBC + $p_entry['status'] = "error"; + + return $v_result; + } + + // ----- Opening destination file + if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { + + // ----- Change the file status + $p_entry['status'] = "write_error"; + + return $v_result; + } + + // ----- Write the uncompressed data + @fwrite($v_dest_file, $v_file_content, $p_entry['size']); + unset($v_file_content); + + // ----- Closing the destination file + @fclose($v_dest_file); + + } + + // ----- Change the file mtime + @touch($p_entry['filename'], $p_entry['mtime']); + } + + // ----- Look for chmod option + if (isset($p_options[PCLZIP_OPT_SET_CHMOD])) { + + // ----- Change the mode of the file + @chmod($p_entry['filename'], $p_options[PCLZIP_OPT_SET_CHMOD]); + } + + } + } + + // ----- Change abort status + if ($p_entry['status'] == "aborted") { + $p_entry['status'] = "skipped"; + } + + // ----- Look for post-extract callback + elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); + + // ----- Look for abort result + if ($v_result == 2) { + $v_result = PCLZIP_ERR_USER_ABORTED; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFileUsingTempFile() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privExtractFileUsingTempFile(&$p_entry, &$p_options) + { + $v_result=1; + + // ----- Creates a temporary file + $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz'; + if (($v_dest_file = @fopen($v_gzip_temp_name, "wb")) == 0) { + fclose($v_file); + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode'); + return PclZip::errorCode(); + } + + + // ----- Write gz file format header + $v_binary_data = pack('va1a1Va1a1', 0x8b1f, Chr($p_entry['compression']), Chr(0x00), time(), Chr(0x00), Chr(3)); + @fwrite($v_dest_file, $v_binary_data, 10); + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = $p_entry['compressed_size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($this->zip_fd, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($v_dest_file, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Write gz file format footer + $v_binary_data = pack('VV', $p_entry['crc'], $p_entry['size']); + @fwrite($v_dest_file, $v_binary_data, 8); + + // ----- Close the temporary file + @fclose($v_dest_file); + + // ----- Opening destination file + if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { + $p_entry['status'] = "write_error"; + return $v_result; + } + + // ----- Open the temporary gz file + if(function_exists('gzopen64')){ + $v_src_file = @gzopen64($v_gzip_temp_name, 'rb'); + }else{ + $v_src_file = @gzopen($v_gzip_temp_name, 'rb'); + } + if ($v_src_file == 0) { + @fclose($v_dest_file); + $p_entry['status'] = "read_error"; + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); + return PclZip::errorCode(); + } + + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = $p_entry['size']; + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @gzread($v_src_file, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($v_dest_file, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + @fclose($v_dest_file); + @gzclose($v_src_file); + + // ----- Delete the temporary file + @unlink($v_gzip_temp_name); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFileInOutput() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privExtractFileInOutput(&$p_entry, &$p_options) + { + $v_result=1; + + // ----- Read the file header + if (($v_result = $this->privReadFileHeader($v_header)) != 1) { + return $v_result; + } + + + // ----- Check that the file header is coherent with $p_entry info + if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { + // TBC + } + + // ----- Look for pre-extract callback + if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_entry['status'] = "skipped"; + $v_result = 1; + } + + // ----- Look for abort result + if ($v_result == 2) { + // ----- This status is internal and will be changed in 'skipped' + $p_entry['status'] = "aborted"; + $v_result = PCLZIP_ERR_USER_ABORTED; + } + + // ----- Update the informations + // Only some fields can be modified + $p_entry['filename'] = $v_local_header['filename']; + } + + // ----- Trace + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Do the extraction (if not a folder) + if (!(($p_entry['external']&0x00000010)==0x00000010)) { + // ----- Look for not compressed file + if ($p_entry['compressed_size'] == $p_entry['size']) { + + // ----- Read the file in a buffer (one shot) + $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Send the file to the output + echo $v_buffer; + unset($v_buffer); + } + else { + + // ----- Read the compressed file in a buffer (one shot) + $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Decompress the file + $v_file_content = gzinflate($v_buffer); + unset($v_buffer); + + // ----- Send the file to the output + echo $v_file_content; + unset($v_file_content); + } + } + } + + // ----- Change abort status + if ($p_entry['status'] == "aborted") { + $p_entry['status'] = "skipped"; + } + + // ----- Look for post-extract callback + elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); + + // ----- Look for abort result + if ($v_result == 2) { + $v_result = PCLZIP_ERR_USER_ABORTED; + } + } + + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFileAsString() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privExtractFileAsString(&$p_entry, &$p_string, &$p_options) + { + $v_result=1; + + // ----- Read the file header + $v_header = array(); + if (($v_result = $this->privReadFileHeader($v_header)) != 1) + { + // ----- Return + return $v_result; + } + + + // ----- Check that the file header is coherent with $p_entry info + if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { + // TBC + } + + // ----- Look for pre-extract callback + if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_entry['status'] = "skipped"; + $v_result = 1; + } + + // ----- Look for abort result + if ($v_result == 2) { + // ----- This status is internal and will be changed in 'skipped' + $p_entry['status'] = "aborted"; + $v_result = PCLZIP_ERR_USER_ABORTED; + } + + // ----- Update the informations + // Only some fields can be modified + $p_entry['filename'] = $v_local_header['filename']; + } + + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Do the extraction (if not a folder) + if (!(($p_entry['external']&0x00000010)==0x00000010)) { + // ----- Look for not compressed file + // if ($p_entry['compressed_size'] == $p_entry['size']) + if ($p_entry['compression'] == 0) { + + // ----- Reading the file + $p_string = @fread($this->zip_fd, $p_entry['compressed_size']); + } + else { + + // ----- Reading the file + $v_data = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Decompress the file + if (($p_string = @gzinflate($v_data)) === FALSE) { + // TBC + } + } + + // ----- Trace + } + else { + // TBC : error : can not extract a folder in a string + } + + } + + // ----- Change abort status + if ($p_entry['status'] == "aborted") { + $p_entry['status'] = "skipped"; + } + + // ----- Look for post-extract callback + elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Swap the content to header + $v_local_header['content'] = $p_string; + $p_string = ''; + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); + + // ----- Swap back the content to header + $p_string = $v_local_header['content']; + unset($v_local_header['content']); + + // ----- Look for abort result + if ($v_result == 2) { + $v_result = PCLZIP_ERR_USER_ABORTED; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privReadFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privReadFileHeader(&$p_header) + { + $v_result=1; + + // ----- Read the 4 bytes signature + $v_binary_data = @fread($this->zip_fd, 4); + $v_data = unpack('Vid', $v_binary_data); + + // ----- Check signature + if ($v_data['id'] != 0x04034b50) + { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the first 42 bytes of the header + $v_binary_data = fread($this->zip_fd, 26); + + // ----- Look for invalid block size + if (strlen($v_binary_data) != 26) + { + $p_header['filename'] = ""; + $p_header['status'] = "invalid_header"; + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Extract the values + $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data); + + // ----- Get filename + $p_header['filename'] = fread($this->zip_fd, $v_data['filename_len']); + + // ----- Get extra_fields + if ($v_data['extra_len'] != 0) { + $p_header['extra'] = fread($this->zip_fd, $v_data['extra_len']); + } + else { + $p_header['extra'] = ''; + } + + // ----- Extract properties + $p_header['version_extracted'] = $v_data['version']; + $p_header['compression'] = $v_data['compression']; + $p_header['size'] = $v_data['size']; + $p_header['compressed_size'] = $v_data['compressed_size']; + $p_header['crc'] = $v_data['crc']; + $p_header['flag'] = $v_data['flag']; + $p_header['filename_len'] = $v_data['filename_len']; + + // ----- Recuperate date in UNIX format + $p_header['mdate'] = $v_data['mdate']; + $p_header['mtime'] = $v_data['mtime']; + if ($p_header['mdate'] && $p_header['mtime']) + { + // ----- Extract time + $v_hour = ($p_header['mtime'] & 0xF800) >> 11; + $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; + $v_seconde = ($p_header['mtime'] & 0x001F)*2; + + // ----- Extract date + $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; + $v_month = ($p_header['mdate'] & 0x01E0) >> 5; + $v_day = $p_header['mdate'] & 0x001F; + + // ----- Get UNIX date format + $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); + + } + else + { + $p_header['mtime'] = time(); + } + + // TBC + //for(reset($v_data); $key = key($v_data); next($v_data)) { + //} + + // ----- Set the stored filename + $p_header['stored_filename'] = $p_header['filename']; + + // ----- Set the status field + $p_header['status'] = "ok"; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privReadCentralFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privReadCentralFileHeader(&$p_header) + { + $v_result=1; + + // ----- Read the 4 bytes signature + $v_binary_data = @fread($this->zip_fd, 4); + $v_data = unpack('Vid', $v_binary_data); + + // ----- Check signature + if ($v_data['id'] != 0x02014b50) + { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the first 42 bytes of the header + $v_binary_data = fread($this->zip_fd, 42); + + // ----- Look for invalid block size + if (strlen($v_binary_data) != 42) + { + $p_header['filename'] = ""; + $p_header['status'] = "invalid_header"; + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Extract the values + $p_header = unpack('vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $v_binary_data); + + // ----- Get filename + if ($p_header['filename_len'] != 0) + $p_header['filename'] = fread($this->zip_fd, $p_header['filename_len']); + else + $p_header['filename'] = ''; + + // ----- Get extra + if ($p_header['extra_len'] != 0) + $p_header['extra'] = fread($this->zip_fd, $p_header['extra_len']); + else + $p_header['extra'] = ''; + + // ----- Get comment + if ($p_header['comment_len'] != 0) + $p_header['comment'] = fread($this->zip_fd, $p_header['comment_len']); + else + $p_header['comment'] = ''; + + // ----- Extract properties + + // ----- Recuperate date in UNIX format + //if ($p_header['mdate'] && $p_header['mtime']) + // TBC : bug : this was ignoring time with 0/0/0 + if (1) + { + // ----- Extract time + $v_hour = ($p_header['mtime'] & 0xF800) >> 11; + $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; + $v_seconde = ($p_header['mtime'] & 0x001F)*2; + + // ----- Extract date + $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; + $v_month = ($p_header['mdate'] & 0x01E0) >> 5; + $v_day = $p_header['mdate'] & 0x001F; + + // ----- Get UNIX date format + $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); + + } + else + { + $p_header['mtime'] = time(); + } + + // ----- Set the stored filename + $p_header['stored_filename'] = $p_header['filename']; + + // ----- Set default status to ok + $p_header['status'] = 'ok'; + + // ----- Look if it is a directory + if (substr($p_header['filename'], -1) == '/') { + //$p_header['external'] = 0x41FF0010; + $p_header['external'] = 0x00000010; + } + + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCheckFileHeaders() + // Description : + // Parameters : + // Return Values : + // 1 on success, + // 0 on error; + // -------------------------------------------------------------------------------- + function privCheckFileHeaders(&$p_local_header, &$p_central_header) + { + $v_result=1; + + // ----- Check the static values + // TBC + if ($p_local_header['filename'] != $p_central_header['filename']) { + } + if ($p_local_header['version_extracted'] != $p_central_header['version_extracted']) { + } + if ($p_local_header['flag'] != $p_central_header['flag']) { + } + if ($p_local_header['compression'] != $p_central_header['compression']) { + } + if ($p_local_header['mtime'] != $p_central_header['mtime']) { + } + if ($p_local_header['filename_len'] != $p_central_header['filename_len']) { + } + + // ----- Look for flag bit 3 + if (($p_local_header['flag'] & 8) == 8) { + $p_local_header['size'] = $p_central_header['size']; + $p_local_header['compressed_size'] = $p_central_header['compressed_size']; + $p_local_header['crc'] = $p_central_header['crc']; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privReadEndCentralDir() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privReadEndCentralDir(&$p_central_dir) + { + $v_result=1; + + // ----- Go to the end of the zip file + $v_size = filesize($this->zipname); + @fseek($this->zip_fd, $v_size); + if (@ftell($this->zip_fd) != $v_size) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to go to the end of the archive \''.$this->zipname.'\''); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- First try : look if this is an archive with no commentaries (most of the time) + // in this case the end of central dir is at 22 bytes of the file end + $v_found = 0; + if ($v_size > 26) { + @fseek($this->zip_fd, $v_size-22); + if (($v_pos = @ftell($this->zip_fd)) != ($v_size-22)) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\''); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read for bytes + $v_binary_data = @fread($this->zip_fd, 4); + $v_data = @unpack('Vid', $v_binary_data); + + // ----- Check signature + if ($v_data['id'] == 0x06054b50) { + $v_found = 1; + } + + $v_pos = ftell($this->zip_fd); + } + + // ----- Go back to the maximum possible size of the Central Dir End Record + if (!$v_found) { + $v_maximum_size = 65557; // 0xFFFF + 22; + if ($v_maximum_size > $v_size) + $v_maximum_size = $v_size; + @fseek($this->zip_fd, $v_size-$v_maximum_size); + if (@ftell($this->zip_fd) != ($v_size-$v_maximum_size)) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\''); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read byte per byte in order to find the signature + $v_pos = ftell($this->zip_fd); + $v_bytes = 0x00000000; + while ($v_pos < $v_size) + { + // ----- Read a byte + $v_byte = @fread($this->zip_fd, 1); + + // ----- Add the byte + //$v_bytes = ($v_bytes << 8) | Ord($v_byte); + // Note we mask the old value down such that once shifted we can never end up with more than a 32bit number + // Otherwise on systems where we have 64bit integers the check below for the magic number will fail. + $v_bytes = ( ($v_bytes & 0xFFFFFF) << 8) | Ord($v_byte); + + // ----- Compare the bytes + if ($v_bytes == 0x504b0506) + { + $v_pos++; + break; + } + + $v_pos++; + } + + // ----- Look if not found end of central dir + if ($v_pos == $v_size) + { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Unable to find End of Central Dir Record signature"); + + // ----- Return + return PclZip::errorCode(); + } + } + + // ----- Read the first 18 bytes of the header + $v_binary_data = fread($this->zip_fd, 18); + + // ----- Look for invalid block size + if (strlen($v_binary_data) != 18) + { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid End of Central Dir Record size : ".strlen($v_binary_data)); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Extract the values + $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data); + + // ----- Check the global size + if (($v_pos + $v_data['comment_size'] + 18) != $v_size) { + + // ----- Removed in release 2.2 see readme file + // The check of the file size is a little too strict. + // Some bugs where found when a zip is encrypted/decrypted with 'crypt'. + // While decrypted, zip has training 0 bytes + if (0) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, + 'The central dir is not at the end of the archive.' + .' Some trailing bytes exists after the archive.'); + + // ----- Return + return PclZip::errorCode(); + } + } + + // ----- Get comment + if ($v_data['comment_size'] != 0) { + $p_central_dir['comment'] = fread($this->zip_fd, $v_data['comment_size']); + } + else + $p_central_dir['comment'] = ''; + + $p_central_dir['entries'] = $v_data['entries']; + $p_central_dir['disk_entries'] = $v_data['disk_entries']; + $p_central_dir['offset'] = $v_data['offset']; + $p_central_dir['size'] = $v_data['size']; + $p_central_dir['disk'] = $v_data['disk']; + $p_central_dir['disk_start'] = $v_data['disk_start']; + + // TBC + //for(reset($p_central_dir); $key = key($p_central_dir); next($p_central_dir)) { + //} + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privDeleteByRule() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privDeleteByRule(&$p_result_list, &$p_options) + { + $v_result=1; + $v_list_detail = array(); + + // ----- Open the zip file + if (($v_result=$this->privOpenFd('rb')) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privCloseFd(); + return $v_result; + } + + // ----- Go to beginning of File + @rewind($this->zip_fd); + + // ----- Scan all the files + // ----- Start at beginning of Central Dir + $v_pos_entry = $v_central_dir['offset']; + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_pos_entry)) + { + // ----- Close the zip file + $this->privCloseFd(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read each entry + $v_header_list = array(); + $j_start = 0; + for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) + { + + // ----- Read the file header + $v_header_list[$v_nb_extracted] = array(); + if (($v_result = $this->privReadCentralFileHeader($v_header_list[$v_nb_extracted])) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + + return $v_result; + } + + + // ----- Store the index + $v_header_list[$v_nb_extracted]['index'] = $i; + + // ----- Look for the specific extract rules + $v_found = false; + + // ----- Look for extract by name rule + if ( (isset($p_options[PCLZIP_OPT_BY_NAME])) + && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { + + // ----- Look if the filename is in the list + for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) + && (substr($v_header_list[$v_nb_extracted]['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { + $v_found = true; + } + elseif ( (($v_header_list[$v_nb_extracted]['external']&0x00000010)==0x00000010) /* Indicates a folder */ + && ($v_header_list[$v_nb_extracted]['stored_filename'].'/' == $p_options[PCLZIP_OPT_BY_NAME][$j])) { + $v_found = true; + } + } + // ----- Look for a filename + elseif ($v_header_list[$v_nb_extracted]['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { + $v_found = true; + } + } + } + + // ----- Look for extract by ereg rule + // ereg() is deprecated with PHP 5.3 + /* + else if ( (isset($p_options[PCLZIP_OPT_BY_EREG])) + && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { + + if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { + $v_found = true; + } + } + */ + + // ----- Look for extract by preg rule + else if ( (isset($p_options[PCLZIP_OPT_BY_PREG])) + && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { + + if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { + $v_found = true; + } + } + + // ----- Look for extract by index rule + else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX])) + && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { + + // ----- Look if the index is in the list + for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { + $v_found = true; + } + if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { + $j_start = $j+1; + } + + if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) { + break; + } + } + } + else { + $v_found = true; + } + + // ----- Look for deletion + if ($v_found) + { + unset($v_header_list[$v_nb_extracted]); + } + else + { + $v_nb_extracted++; + } + } + + // ----- Look if something need to be deleted + if ($v_nb_extracted > 0) { + + // ----- Creates a temporay file + $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; + + // ----- Creates a temporary zip archive + $v_temp_zip = new PclZip($v_zip_temp_name); + + // ----- Open the temporary zip file in write mode + if (($v_result = $v_temp_zip->privOpenFd('wb')) != 1) { + $this->privCloseFd(); + + // ----- Return + return $v_result; + } + + // ----- Look which file need to be kept + for ($i=0; $izip_fd); + if (@fseek($this->zip_fd, $v_header_list[$i]['offset'])) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the file header + $v_local_header = array(); + if (($v_result = $this->privReadFileHeader($v_local_header)) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Check that local file header is same as central file header + if ($this->privCheckFileHeaders($v_local_header, + $v_header_list[$i]) != 1) { + // TBC + } + unset($v_local_header); + + // ----- Write the file header + if (($v_result = $v_temp_zip->privWriteFileHeader($v_header_list[$i])) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Read/write the data block + if (($v_result = PclZipUtilCopyBlock($this->zip_fd, $v_temp_zip->zip_fd, $v_header_list[$i]['compressed_size'])) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($v_temp_zip->zip_fd); + + // ----- Re-Create the Central Dir files header + for ($i=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { + $v_temp_zip->privCloseFd(); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Transform the header to a 'usable' info + $v_temp_zip->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); + } + + + // ----- Zip file comment + $v_comment = ''; + if (isset($p_options[PCLZIP_OPT_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_COMMENT]; + } + + // ----- Calculate the size of the central header + $v_size = @ftell($v_temp_zip->zip_fd)-$v_offset; + + // ----- Create the central dir footer + if (($v_result = $v_temp_zip->privWriteCentralHeader(sizeof($v_header_list), $v_size, $v_offset, $v_comment)) != 1) { + // ----- Reset the file list + unset($v_header_list); + $v_temp_zip->privCloseFd(); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Close + $v_temp_zip->privCloseFd(); + $this->privCloseFd(); + + // ----- Delete the zip file + // TBC : I should test the result ... + @unlink($this->zipname); + + // ----- Rename the temporary file + // TBC : I should test the result ... + //@rename($v_zip_temp_name, $this->zipname); + PclZipUtilRename($v_zip_temp_name, $this->zipname); + + // ----- Destroy the temporary archive + unset($v_temp_zip); + } + + // ----- Remove every files : reset the file + else if ($v_central_dir['entries'] != 0) { + $this->privCloseFd(); + + if (($v_result = $this->privOpenFd('wb')) != 1) { + return $v_result; + } + + if (($v_result = $this->privWriteCentralHeader(0, 0, 0, '')) != 1) { + return $v_result; + } + + $this->privCloseFd(); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privDirCheck() + // Description : + // Check if a directory exists, if not it creates it and all the parents directory + // which may be useful. + // Parameters : + // $p_dir : Directory path to check. + // Return Values : + // 1 : OK + // -1 : Unable to create directory + // -------------------------------------------------------------------------------- + function privDirCheck($p_dir, $p_is_dir=false) + { + $v_result = 1; + + + // ----- Remove the final '/' + if (($p_is_dir) && (substr($p_dir, -1)=='/')) + { + $p_dir = substr($p_dir, 0, strlen($p_dir)-1); + } + + // ----- Check the directory availability + if ((is_dir($p_dir)) || ($p_dir == "")) + { + return 1; + } + + // ----- Extract parent directory + $p_parent_dir = dirname($p_dir); + + // ----- Just a check + if ($p_parent_dir != $p_dir) + { + // ----- Look for parent directory + if ($p_parent_dir != "") + { + if (($v_result = $this->privDirCheck($p_parent_dir)) != 1) + { + return $v_result; + } + } + } + + // ----- Create the directory + if (!@mkdir($p_dir, 0777)) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_DIR_CREATE_FAIL, "Unable to create directory '$p_dir'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privMerge() + // Description : + // If $p_archive_to_add does not exist, the function exit with a success result. + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privMerge(&$p_archive_to_add) + { + $v_result=1; + + // ----- Look if the archive_to_add exists + if (!is_file($p_archive_to_add->zipname)) + { + + // ----- Nothing to merge, so merge is a success + $v_result = 1; + + // ----- Return + return $v_result; + } + + // ----- Look if the archive exists + if (!is_file($this->zipname)) + { + + // ----- Do a duplicate + $v_result = $this->privDuplicate($p_archive_to_add->zipname); + + // ----- Return + return $v_result; + } + + // ----- Open the zip file + if (($v_result=$this->privOpenFd('rb')) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privCloseFd(); + return $v_result; + } + + // ----- Go to beginning of File + @rewind($this->zip_fd); + + // ----- Open the archive_to_add file + if (($v_result=$p_archive_to_add->privOpenFd('rb')) != 1) + { + $this->privCloseFd(); + + // ----- Return + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir_to_add = array(); + if (($v_result = $p_archive_to_add->privReadEndCentralDir($v_central_dir_to_add)) != 1) + { + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + + return $v_result; + } + + // ----- Go to beginning of File + @rewind($p_archive_to_add->zip_fd); + + // ----- Creates a temporay file + $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; + + // ----- Open the temporary file in write mode + if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) + { + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Copy the files from the archive to the temporary file + // TBC : Here I should better append the file and go back to erase the central dir + $v_size = $v_central_dir['offset']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($this->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Copy the files from the archive_to_add into the temporary file + $v_size = $v_central_dir_to_add['offset']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($p_archive_to_add->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($v_zip_temp_fd); + + // ----- Copy the block of file headers from the old archive + $v_size = $v_central_dir['size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($this->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Copy the block of file headers from the archive_to_add + $v_size = $v_central_dir_to_add['size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($p_archive_to_add->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Merge the file comments + $v_comment = $v_central_dir['comment'].' '.$v_central_dir_to_add['comment']; + + // ----- Calculate the size of the (new) central header + $v_size = @ftell($v_zip_temp_fd)-$v_offset; + + // ----- Swap the file descriptor + // Here is a trick : I swap the temporary fd with the zip fd, in order to use + // the following methods on the temporary fil and not the real archive fd + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + + // ----- Create the central dir footer + if (($v_result = $this->privWriteCentralHeader($v_central_dir['entries']+$v_central_dir_to_add['entries'], $v_size, $v_offset, $v_comment)) != 1) + { + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + @fclose($v_zip_temp_fd); + $this->zip_fd = null; + + // ----- Reset the file list + unset($v_header_list); + + // ----- Return + return $v_result; + } + + // ----- Swap back the file descriptor + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + + // ----- Close + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + + // ----- Close the temporary file + @fclose($v_zip_temp_fd); + + // ----- Delete the zip file + // TBC : I should test the result ... + @unlink($this->zipname); + + // ----- Rename the temporary file + // TBC : I should test the result ... + //@rename($v_zip_temp_name, $this->zipname); + PclZipUtilRename($v_zip_temp_name, $this->zipname); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privDuplicate() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privDuplicate($p_archive_filename) + { + $v_result=1; + + // ----- Look if the $p_archive_filename exists + if (!is_file($p_archive_filename)) + { + + // ----- Nothing to duplicate, so duplicate is a success. + $v_result = 1; + + // ----- Return + return $v_result; + } + + // ----- Open the zip file + if (($v_result=$this->privOpenFd('wb')) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Open the temporary file in write mode + if (($v_zip_temp_fd = @fopen($p_archive_filename, 'rb')) == 0) + { + $this->privCloseFd(); + + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive file \''.$p_archive_filename.'\' in binary write mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Copy the files from the archive to the temporary file + // TBC : Here I should better append the file and go back to erase the central dir + $v_size = filesize($p_archive_filename); + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($v_zip_temp_fd, $v_read_size); + @fwrite($this->zip_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Close + $this->privCloseFd(); + + // ----- Close the temporary file + @fclose($v_zip_temp_fd); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privErrorLog() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privErrorLog($p_error_code=0, $p_error_string='') + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + PclError($p_error_code, $p_error_string); + } + else { + $this->error_code = $p_error_code; + $this->error_string = $p_error_string; + } + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privErrorReset() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privErrorReset() + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + PclErrorReset(); + } + else { + $this->error_code = 0; + $this->error_string = ''; + } + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privDisableMagicQuotes() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privDisableMagicQuotes() + { + $v_result=1; + + // ----- Look if function exists + if ( (!function_exists("get_magic_quotes_runtime")) + || (!function_exists("set_magic_quotes_runtime"))) { + return $v_result; + } + + // ----- Look if already done + if ($this->magic_quotes_status != -1) { + return $v_result; + } + + // ----- Get and memorize the magic_quote value + $this->magic_quotes_status = @get_magic_quotes_runtime(); + + // ----- Disable magic_quotes + if ($this->magic_quotes_status == 1) { + @set_magic_quotes_runtime(0); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privSwapBackMagicQuotes() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privSwapBackMagicQuotes() + { + $v_result=1; + + // ----- Look if function exists + if ( (!function_exists("get_magic_quotes_runtime")) + || (!function_exists("set_magic_quotes_runtime"))) { + return $v_result; + } + + // ----- Look if something to do + if ($this->magic_quotes_status != -1) { + return $v_result; + } + + // ----- Swap back magic_quotes + if ($this->magic_quotes_status == 1) { + @set_magic_quotes_runtime($this->magic_quotes_status); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + } + // End of class + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilPathReduction() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function PclZipUtilPathReduction($p_dir) + { + $v_result = ""; + $scheme = parse_url($p_dir, PHP_URL_SCHEME) != null; + + // ----- Look for not empty path + if ($p_dir != "") { + // ----- Explode path by directory names + $v_list = explode("/", $p_dir); + + // ----- Study directories from last to first + $v_skip = 0; + for ($i=sizeof($v_list)-1; $i>=0; $i--) { + // ----- Look for current path + if ($v_list[$i] == ".") { + // ----- Ignore this directory + // Should be the first $i=0, but no check is done + } + else if ($v_list[$i] == "..") { + $v_skip++; + } + else if ($v_list[$i] == "") { + // ----- First '/' i.e. root slash + if ($i == 0) { + $v_result = "/".$v_result; + if ($v_skip > 0) { + // ----- It is an invalid path, so the path is not modified + // TBC + $v_result = $p_dir; + $v_skip = 0; + } + } + // ----- Last '/' i.e. indicates a directory + else if ($i == (sizeof($v_list)-1)) { + $v_result = $v_list[$i]; + } + // ----- Double '/' inside the path + else { + // ----- Ignore only the double '//' in path, + // but not the first and last '/' + } + } + else { + // ----- Look for item to skip + if ($v_skip > 0) { + $v_skip--; + } + else { + if(stripos(PHP_OS, "win") === 0){ + if (substr($p_dir, 0, 3) == "smb") { + $v_result = (($i==0 && $scheme)? $v_list[$i]."/" : $v_list[$i]).($i!=(sizeof($v_list)-1)? "/".$v_result : ""); + } else { + $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)? "/".$v_result : ""); + } + }else{ + $v_result = $v_list[$i].(( $i == 0 && $scheme)? "/" : "").($i!=(sizeof($v_list)-1)? "/".$v_result : ""); + } + } + } + } + + // ----- Look for skip + if ($v_skip > 0) { + while ($v_skip > 0) { + $v_result = '../'.$v_result; + $v_skip--; + } + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilPathInclusion() + // Description : + // This function indicates if the path $p_path is under the $p_dir tree. Or, + // said in an other way, if the file or sub-dir $p_path is inside the dir + // $p_dir. + // The function indicates also if the path is exactly the same as the dir. + // This function supports path with duplicated '/' like '//', but does not + // support '.' or '..' statements. + // Parameters : + // Return Values : + // 0 if $p_path is not inside directory $p_dir + // 1 if $p_path is inside directory $p_dir + // 2 if $p_path is exactly the same as $p_dir + // -------------------------------------------------------------------------------- + function PclZipUtilPathInclusion($p_dir, $p_path) + { + $v_result = 1; + + // ----- Look for path beginning by ./ + if ( ($p_dir == '.') + || ((strlen($p_dir) >=2) && (substr($p_dir, 0, 2) == './'))) { + $p_dir = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_dir, 1); + } + if ( ($p_path == '.') + || ((strlen($p_path) >=2) && (substr($p_path, 0, 2) == './'))) { + $p_path = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_path, 1); + } + + // ----- Explode dir and path by directory separator + $v_list_dir = explode("/", $p_dir); + $v_list_dir_size = sizeof($v_list_dir); + $v_list_path = explode("/", $p_path); + $v_list_path_size = sizeof($v_list_path); + + // ----- Study directories paths + $i = 0; + $j = 0; + while (($i < $v_list_dir_size) && ($j < $v_list_path_size) && ($v_result)) { + + // ----- Look for empty dir (path reduction) + if ($v_list_dir[$i] == '') { + $i++; + continue; + } + if ($v_list_path[$j] == '') { + $j++; + continue; + } + + // ----- Compare the items + if (($v_list_dir[$i] != $v_list_path[$j]) && ($v_list_dir[$i] != '') && ( $v_list_path[$j] != '')) { + $v_result = 0; + } + + // ----- Next items + $i++; + $j++; + } + + // ----- Look if everything seems to be the same + if ($v_result) { + // ----- Skip all the empty items + while (($j < $v_list_path_size) && ($v_list_path[$j] == '')) $j++; + while (($i < $v_list_dir_size) && ($v_list_dir[$i] == '')) $i++; + + if (($i >= $v_list_dir_size) && ($j >= $v_list_path_size)) { + // ----- There are exactly the same + $v_result = 2; + } + else if ($i < $v_list_dir_size) { + // ----- The path is shorter than the dir + $v_result = 0; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilCopyBlock() + // Description : + // Parameters : + // $p_mode : read/write compression mode + // 0 : src & dest normal + // 1 : src gzip, dest normal + // 2 : src normal, dest gzip + // 3 : src & dest gzip + // Return Values : + // -------------------------------------------------------------------------------- + function PclZipUtilCopyBlock($p_src, $p_dest, $p_size, $p_mode=0) + { + $v_result = 1; + + if ($p_mode==0) + { + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($p_src, $v_read_size); + @fwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } + else if ($p_mode==1) + { + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @gzread($p_src, $v_read_size); + @fwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } + else if ($p_mode==2) + { + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($p_src, $v_read_size); + @gzwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } + else if ($p_mode==3) + { + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @gzread($p_src, $v_read_size); + @gzwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilRename() + // Description : + // This function tries to do a simple rename() function. If it fails, it + // tries to copy the $p_src file in a new $p_dest file and then unlink the + // first one. + // Parameters : + // $p_src : Old filename + // $p_dest : New filename + // Return Values : + // 1 on success, 0 on failure. + // -------------------------------------------------------------------------------- + function PclZipUtilRename($p_src, $p_dest) + { + $v_result = 1; + + // ----- Try to rename the files + if (!@rename($p_src, $p_dest)) { + + // ----- Try to copy & unlink the src + if (!@copy($p_src, $p_dest)) { + $v_result = 0; + } + else if (!@unlink($p_src)) { + $v_result = 0; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilOptionText() + // Description : + // Translate option value in text. Mainly for debug purpose. + // Parameters : + // $p_option : the option value. + // Return Values : + // The option text value. + // -------------------------------------------------------------------------------- + function PclZipUtilOptionText($p_option) + { + + $v_list = get_defined_constants(); + for (reset($v_list); $v_key = key($v_list); next($v_list)) { + $v_prefix = substr($v_key, 0, 10); + if (( ($v_prefix == 'PCLZIP_OPT') + || ($v_prefix == 'PCLZIP_CB_') + || ($v_prefix == 'PCLZIP_ATT')) + && ($v_list[$v_key] == $p_option)) { + return $v_key; + } + } + + $v_result = 'Unknown'; + + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilTranslateWinPath() + // Description : + // Translate windows path by replacing '\' by '/' and optionally removing + // drive letter. + // Parameters : + // $p_path : path to translate. + // $p_remove_disk_letter : true | false + // Return Values : + // The path translated. + // -------------------------------------------------------------------------------- + function PclZipUtilTranslateWinPath($p_path, $p_remove_disk_letter=true) + { + if (stristr(php_uname(), 'windows')) { + // ----- Look for potential disk letter + if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) { + $p_path = substr($p_path, $v_position+1); + } + // ----- Change potential windows directory separator + if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) { + $p_path = strtr($p_path, '\\', '/'); + } + } + return $p_path; + } + // -------------------------------------------------------------------------------- + + +?> diff --git a/core/src/core/src/phpunit/HOWTO b/core/src/core/src/phpunit/HOWTO new file mode 100644 index 0000000000..4a6b807a0b --- /dev/null +++ b/core/src/core/src/phpunit/HOWTO @@ -0,0 +1,4 @@ +We pass the base.conf.php file as the PHPUnit "bootstrap" file. For example, running a given +test suite, knowing Pydio is installed at /var/www/pydio : + +> phpunit --bootstrap /var/www/pydio/base.conf.php Pydio_Tests_Suite_Atomics diff --git a/core/src/core/src/phpunit/Pydio/Tests/Atomics/CryptoTests.php b/core/src/core/src/phpunit/Pydio/Tests/Atomics/CryptoTests.php new file mode 100644 index 0000000000..73f0f3d1cf --- /dev/null +++ b/core/src/core/src/phpunit/Pydio/Tests/Atomics/CryptoTests.php @@ -0,0 +1,40 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests\Atomics; + +use Pydio\Core\Utils\Crypto; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class Crypto + * @package Pydio\Tests\Atomics + */ +class CryptoTests extends \PHPUnit_Framework_TestCase +{ + public function testEncryptDecrypt(){ + $key = "test"; + $data = "toto"; + $encoded = Crypto::encrypt($data, Crypto::buildKey($key, Crypto::getApplicationSecret())); + $decoded = Crypto::decrypt($encoded, Crypto::buildKey($key, Crypto::getApplicationSecret())); + $this->assertTrue($data === $decoded); + } +} \ No newline at end of file diff --git a/core/src/phpunit/AJXP/Tests/Atomics/FiltersTest.php b/core/src/core/src/phpunit/Pydio/Tests/Atomics/FiltersTest.php similarity index 88% rename from core/src/phpunit/AJXP/Tests/Atomics/FiltersTest.php rename to core/src/core/src/phpunit/Pydio/Tests/Atomics/FiltersTest.php index c6ed84f5f9..2718d4d1f4 100644 --- a/core/src/phpunit/AJXP/Tests/Atomics/FiltersTest.php +++ b/core/src/core/src/phpunit/Pydio/Tests/Atomics/FiltersTest.php @@ -1,6 +1,6 @@ + * Copyright 2007-2016 Charles du Jeu - Abstrium SAS * This file is part of Pydio. * * Pydio is free software: you can redistribute it and/or modify @@ -16,9 +16,9 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ -namespace AJXP\Tests\Atomics; +namespace Pydio\Tests\Atomics; class FiltersTest extends \PHPUnit_Framework_TestCase { diff --git a/core/src/phpunit/AJXP/Tests/Atomics/MemStoresTest.php b/core/src/core/src/phpunit/Pydio/Tests/Atomics/MemStoresTest.php similarity index 85% rename from core/src/phpunit/AJXP/Tests/Atomics/MemStoresTest.php rename to core/src/core/src/phpunit/Pydio/Tests/Atomics/MemStoresTest.php index 774d7b7307..1be3e91085 100644 --- a/core/src/phpunit/AJXP/Tests/Atomics/MemStoresTest.php +++ b/core/src/core/src/phpunit/Pydio/Tests/Atomics/MemStoresTest.php @@ -1,6 +1,6 @@ + * Copyright 2007-2016 Charles du Jeu - Abstrium SAS * This file is part of Pydio. * * Pydio is free software: you can redistribute it and/or modify @@ -16,9 +16,9 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ -namespace AJXP\Tests\Atomics; +namespace Pydio\Tests\Atomics; class MemStoresTest extends \PHPUnit_Framework_TestCase { diff --git a/core/src/core/src/phpunit/Pydio/Tests/Atomics/PermissionMaskTest.php b/core/src/core/src/phpunit/Pydio/Tests/Atomics/PermissionMaskTest.php new file mode 100644 index 0000000000..4de7d3a545 --- /dev/null +++ b/core/src/core/src/phpunit/Pydio/Tests/Atomics/PermissionMaskTest.php @@ -0,0 +1,193 @@ +assertTrue($permission->canRead()); + $this->assertTrue($permission->canWrite()); + $this->assertFalse($permission->denies()); + } + + /** + * @param AJXP_Permission $permission + */ + protected function isReadonly($permission){ + $this->assertTrue($permission->canRead()); + $this->assertFalse($permission->canWrite()); + $this->assertFalse($permission->denies()); + } + + /** + * @param AJXP_Permission $permission + */ + protected function isWriteonly($permission){ + + $this->assertFalse($permission->canRead()); + $this->assertTrue($permission->canWrite()); + $this->assertFalse($permission->denies()); + + } + + /** + * @param AJXP_Permission $permission + */ + protected function isDenied($permission){ + + $this->assertTrue($permission->denies()); + $this->assertFalse($permission->canRead()); + $this->assertFalse($permission->canWrite()); + + } + + + public function testSimplePermission(){ + + $perm = new AJXP_Permission(); + $this->isDenied($perm); + + $perm->setRead(); + $this->isReadonly($perm); + $perm->setWrite(); + $this->isRW($perm); + $perm->setDeny(); + $this->isDenied($perm); + + $perm->setDeny(false); + + // When perm is empty, => denied by default + $this->isDenied($perm); + $perm->setRead(); + $perm->setRead(false); + $this->isDenied($perm); + $perm->setWrite(); + $perm->setWrite(false); + $this->isDenied($perm); + + } + + + public function testPermissionOverride(){ + + $readonly = new AJXP_Permission("r"); + $writeonly = new AJXP_Permission("w"); + $readwrite = new AJXP_Permission("rw"); + $deny = new AJXP_Permission("d"); + $empty = new AJXP_Permission(); + + $this->isRW($writeonly->override($readonly)); + $this->isRW($readonly->override($writeonly)); + $this->isRW($readwrite->override($readonly)); + $this->isRW($readwrite->override($writeonly)); + $this->isRW($readonly->override($readwrite)); + $this->isRW($writeonly->override($readwrite)); + + $this->isDenied($deny->override($readonly)); + $this->isDenied($deny->override($writeonly)); + $this->isDenied($deny->override($readwrite)); + + $this->isDenied($empty->override($readonly)); + $this->isDenied($empty->override($writeonly)); + $this->isDenied($empty->override($readwrite)); + $this->isRW($writeonly->override($readonly)); + } + + /** + * @parra + * + */ + public function testPermissionMask(){ + $mask = new AJXP_PermissionMask(); + $mask->updateBranch("/a1/b1/c1", new AJXP_Permission("r")); + echo "\n"; + $level = 1; + $mask->toStr($mask->getTree(), $level); + $mask->updateBranch("/a1/b1/c2", new AJXP_Permission("rw")); + $mask->updateBranch("/a1/b1/c3", new AJXP_Permission()); + echo "\n"; + $mask->toStr($mask->getTree(), $level); + $mask->updateBranch("/a1/b2", new AJXP_Permission("rw")); + echo "\n"; + $mask->toStr($mask->getTree(), $level); + + $mask->updateBranch("/a1/b3", new AJXP_Permission()); + + $mask->updateBranch("a2/b1", new AJXP_Permission()); + + $this->assertFalse($mask->match("/", AJXP_Permission::WRITE)); + $this->assertTrue($mask->match("/", AJXP_Permission::READ)); + $this->assertTrue($mask->match("/a1/b1/c1", AJXP_Permission::READ)); + $this->assertTrue($mask->match("/a1/b1", AJXP_Permission::READ)); + $this->assertFalse($mask->match("/a1/b1", AJXP_Permission::WRITE)); + + $this->assertFalse($mask->match("/a1/b1/c3", AJXP_Permission::READ)); + $this->assertTrue($mask->match("/a1/b1/c1/d1/e1/f1", AJXP_Permission::READ)); + $this->assertFalse($mask->match("/a1/b1/c1/d1/e1/f1", AJXP_Permission::WRITE)); + + // This overrides the whole /a1/b1 branch >> do we want that? + $mask2 = new AJXP_PermissionMask(); + $mask2->updateBranch("/a1/b1", new AJXP_Permission("d")); + //$mask->override($mask2); + + echo "mask 2\n"; + $level = 0; + $mask2->toStr($mask2->getTree(), $level); + echo "mask 1\n"; + $mask->toStr($mask->getTree(), $level); + echo "Mask 2 override mask 1\n"; + $mask->override($mask2); + $mask->toStr($mask->getTree(), $level); + + //$this->assertTrue($mask->match("/a1/b1", AJXP_Permission::WRITE)); + // Todo write more tests + + $mask->updateBranch("/a1/b1/c1", new AJXP_Permission("w")); + + //$this->assertFalse($mask->match("/a1/b1/c1", AJXP_Permission::READ)); + $this->assertTrue($mask->match("/a1/b1/c1", AJXP_Permission::WRITE)); + + // + // + // $this->assertTrue($mask->match("/a1/b1/c2", AJXP_Permission::READ)); + //$this->assertTrue($mask->match("/a1/b1/c2", AJXP_Permission::WRITE)); + + //$this->assertTrue($mask->match("/a1", AJXP_Permission::DENY)); + + // Test that a deny is cutting the sub branches + $mask1 = new AJXP_PermissionMask(); + $mask1->updateBranch("/a1/b1", new AJXP_Permission("rw")); + $mask1->updateBranch("/a1/b2", new AJXP_Permission("rw")); + $mask1->updateBranch("/a1/b3/c1", new AJXP_Permission("rw")); + $mask1->updateBranch("/a1/b3/c2", new AJXP_Permission("rw")); + + $mask2 = new AJXP_PermissionMask(); + $mask2->updateBranch("/a1", new AJXP_Permission("d")); + + $result = $mask1->override($mask2); + $this->assertFalse($result->match("/a1", AJXP_Permission::READ)); + $this->assertFalse($result->match("/a1/b2", AJXP_Permission::READ)); + $this->assertFalse($result->match("/a1/b3", AJXP_Permission::READ)); + $this->assertFalse($result->match("/a1/b3/c1", AJXP_Permission::READ)); + $this->assertFalse($result->match("/a1/any", AJXP_Permission::READ)); + + } + +} + + + diff --git a/core/src/phpunit/AJXP/Tests/Atomics/RolesTest.php b/core/src/core/src/phpunit/Pydio/Tests/Atomics/RolesTest.php similarity index 86% rename from core/src/phpunit/AJXP/Tests/Atomics/RolesTest.php rename to core/src/core/src/phpunit/Pydio/Tests/Atomics/RolesTest.php index 6bb1a266e0..ddfa0554df 100644 --- a/core/src/phpunit/AJXP/Tests/Atomics/RolesTest.php +++ b/core/src/core/src/phpunit/Pydio/Tests/Atomics/RolesTest.php @@ -1,6 +1,6 @@ + * Copyright 2007-2016 Charles du Jeu - Abstrium SAS * This file is part of Pydio. * * Pydio is free software: you can redistribute it and/or modify @@ -16,18 +16,20 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ -namespace AJXP\Tests\Atomics; +namespace Pydio\Tests\Atomics; + +use Pydio\Conf\Core\AJXP_Role; class RolesTest extends \PHPUnit_Framework_TestCase { public function testRolesNumericKeys() { - $r1 = new \AJXP_Role("role1"); - $r2 = new \AJXP_Role("role2"); + $r1 = new AJXP_Role("role1"); + $r2 = new AJXP_Role("role2"); $r1->setAcl(1, "rw"); $r3 = $r2->override($r1); @@ -37,8 +39,8 @@ public function testRolesNumericKeys() public function testRolesAclAdditivity() { - $r1 = new \AJXP_Role("role1"); - $r2 = new \AJXP_Role("role2"); + $r1 = new AJXP_Role("role1"); + $r2 = new AJXP_Role("role2"); $r1->setAcl("repository_id", ""); $r2->setAcl("repository_id", "w"); @@ -58,14 +60,14 @@ public function testRolesAclAdditivity() $r1->setAcl("repository_id", "r"); $r2->setAcl("repository_id", AJXP_VALUE_CLEAR); $r3 = $r2->override($r1); - $this->assertEquals("", $r3->getAcl("repository_id")); + $this->assertEquals(AJXP_VALUE_CLEAR, $r3->getAcl("repository_id")); } public function testRolesParametersAdditivity() { - $r1 = new \AJXP_Role("role1"); - $r2 = new \AJXP_Role("role2"); + $r1 = new AJXP_Role("role1"); + $r2 = new AJXP_Role("role2"); $r1->setParameterValue("type.id", "param_name", "param_value1", "repository_id"); $this->assertEquals("param_value1", $r1->filterParameterValue("type.id", "param_name", "repository_id", "anyvalue1")); @@ -88,8 +90,8 @@ public function testRolesParametersAdditivity() public function testRolesActionsAdditivity() { - $r1 = new \AJXP_Role("role1"); - $r2 = new \AJXP_Role("role2"); + $r1 = new AJXP_Role("role1"); + $r2 = new AJXP_Role("role2"); $r1->setActionState("type.id", "action_name", "repository_id", "disabled"); $this->assertFalse($r1->actionEnabled("type.id", "action_name", "repository_id", true)); diff --git a/core/src/phpunit/AJXP/Tests/Atomics/UtilsTest.php b/core/src/core/src/phpunit/Pydio/Tests/Atomics/UtilsTest.php similarity index 77% rename from core/src/phpunit/AJXP/Tests/Atomics/UtilsTest.php rename to core/src/core/src/phpunit/Pydio/Tests/Atomics/UtilsTest.php index 84f937e1cc..5d2ecc1f48 100644 --- a/core/src/phpunit/AJXP/Tests/Atomics/UtilsTest.php +++ b/core/src/core/src/phpunit/Pydio/Tests/Atomics/UtilsTest.php @@ -1,6 +1,6 @@ + * Copyright 2007-2016 Charles du Jeu - Abstrium SAS * This file is part of Pydio. * * Pydio is free software: you can redistribute it and/or modify @@ -16,9 +16,11 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ -namespace AJXP\Tests\Atomics; +namespace Pydio\Tests\Atomics; + +use Pydio\Core\Utils\Vars\StatHelper; class UtilsTest extends \PHPUnit_Framework_TestCase { @@ -29,7 +31,7 @@ public function testPHPUnitIsWorking() public function testFrameworkLoads() { - $this->assertEquals(\AJXP_Utils::convertBytes("2M"), 2097152); + $this->assertEquals(StatHelper::convertBytes("2M"), 2097152); } } diff --git a/core/src/phpunit/AJXP/Tests/Core/StoragesTest.php b/core/src/core/src/phpunit/Pydio/Tests/Core/StoragesTest.php similarity index 93% rename from core/src/phpunit/AJXP/Tests/Core/StoragesTest.php rename to core/src/core/src/phpunit/Pydio/Tests/Core/StoragesTest.php index 435a2ba04a..b06991fe34 100644 --- a/core/src/phpunit/AJXP/Tests/Core/StoragesTest.php +++ b/core/src/core/src/phpunit/Pydio/Tests/Core/StoragesTest.php @@ -1,6 +1,6 @@ + * Copyright 2007-2016 Charles du Jeu - Abstrium SAS * This file is part of Pydio. * * Pydio is free software: you can redistribute it and/or modify @@ -16,9 +16,9 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ -namespace AJXP\Tests\Core; +namespace Pydio\Tests\Core; class StoragesTest extends \PHPUnit_Framework_TestCase { diff --git a/core/src/phpunit/AJXP/Tests/Core/UsersTests.php b/core/src/core/src/phpunit/Pydio/Tests/Core/UsersTests.php similarity index 89% rename from core/src/phpunit/AJXP/Tests/Core/UsersTests.php rename to core/src/core/src/phpunit/Pydio/Tests/Core/UsersTests.php index 106b61ce97..8a69a3cb54 100644 --- a/core/src/phpunit/AJXP/Tests/Core/UsersTests.php +++ b/core/src/core/src/phpunit/Pydio/Tests/Core/UsersTests.php @@ -1,6 +1,6 @@ + * Copyright 2007-2016 Charles du Jeu - Abstrium SAS * This file is part of Pydio. * * Pydio is free software: you can redistribute it and/or modify @@ -16,9 +16,9 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ -namespace AJXP\Tests\Core; +namespace Pydio\Tests\Core; class UsersTests extends \PHPUnit_Framework_TestCase { diff --git a/core/src/core/src/phpunit/Pydio/Tests/Suite/Atomics.php b/core/src/core/src/phpunit/Pydio/Tests/Suite/Atomics.php new file mode 100644 index 0000000000..558de51393 --- /dev/null +++ b/core/src/core/src/phpunit/Pydio/Tests/Suite/Atomics.php @@ -0,0 +1,43 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests\Suite; + +class Atomics extends \PHPUnit_Framework_TestSuite +{ + public static function suite() + { + $s = new Atomics(); + $s->addTestFile("Pydio/Tests/Atomics/RolesTest.php"); + $s->addTestFile("Pydio/Tests/Atomics/UtilsTest.php"); + $s->addTestFile("Pydio/Tests/Atomics/PermissionMaskTest.php"); + return $s; + + } + + protected function setUp() + { + } + + protected function tearDown() + { + } + +} diff --git a/core/src/phpunit/AJXP/Tests/Suite/CoreStorages.php b/core/src/core/src/phpunit/Pydio/Tests/Suite/CoreStorages.php similarity index 83% rename from core/src/phpunit/AJXP/Tests/Suite/CoreStorages.php rename to core/src/core/src/phpunit/Pydio/Tests/Suite/CoreStorages.php index b9cddfa41a..b39d9aba90 100644 --- a/core/src/phpunit/AJXP/Tests/Suite/CoreStorages.php +++ b/core/src/core/src/phpunit/Pydio/Tests/Suite/CoreStorages.php @@ -1,6 +1,6 @@ + * Copyright 2007-2016 Charles du Jeu - Abstrium SAS * This file is part of Pydio. * * Pydio is free software: you can redistribute it and/or modify @@ -16,16 +16,16 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ -namespace AJXP\Tests\Suite; +namespace Pydio\Tests\Suite; class AJXP_Suite_CoreStorages extends PHPUnit_Framework_TestSuite { public static function suite() { $s = new AJXP_Suite_CoreStorages(); - $s->addTestFile("AJXP/Core/Conf/StoragesTest.php"); + $s->addTestFile("Pydio/Core/Conf/StoragesTest.php"); return $s; } diff --git a/core/src/core/src/pydio/Core/Controller/CliRunner.php b/core/src/core/src/pydio/Core/Controller/CliRunner.php new file mode 100644 index 0000000000..88f6719e6c --- /dev/null +++ b/core/src/core/src/pydio/Core/Controller/CliRunner.php @@ -0,0 +1,151 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Controller; + +use Pydio\Auth\Core\MemorySafe; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\UsersService; +use Pydio\Core\Utils\Crypto; +use Pydio\Log\Core\Logger; +use Pydio\Tasks\Task; +use Pydio\Tasks\TaskService; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Class CliRunner + * @package Pydio\Core\Controller + */ +class CliRunner +{ + + /** + * Apply a Task in background + * + * @param Task $task + */ + public static function applyTaskInBackground(Task $task) + { + + $parameters = $task->getParameters(); + $task->setStatus(Task::STATUS_RUNNING); + TaskService::getInstance()->updateTask($task); + self::applyActionInBackground($task->getContext(), $task->getAction(), $parameters, "", $task->getId(), $task->getImpersonateUsers()); + + } + + /** + * Launch a command-line version of the framework by passing the actionName & parameters as arguments. + * @static + * @param ContextInterface $ctx + * @param String $actionName + * @param array $parameters + * @param string $statusFile + * @param string $taskId + * @param string $impersonateUsers + * @return null|UnixProcess + */ + public static function applyActionInBackground(ContextInterface $ctx, $actionName, $parameters, $statusFile = "", $taskId = null, $impersonateUsers = null) + { + $repositoryId = $ctx->getRepositoryId(); + $user = $ctx->hasUser() ? $ctx->getUser()->getId() : "shared"; + + $token = md5(time()); + $logDir = AJXP_CACHE_DIR . "/cmd_outputs"; + if (!is_dir($logDir)) mkdir($logDir, 0755); + $logFile = $logDir . "/" . $token . ".out"; + + if (UsersService::usersEnabled()) { + $user = Crypto::encrypt($user, Crypto::buildKey($token , Crypto::getCliSecret())); + } + $robustInstallPath = str_replace("/", DIRECTORY_SEPARATOR, AJXP_INSTALL_PATH); + $cmd = ConfService::getGlobalConf("CLI_PHP") . " " . $robustInstallPath . DIRECTORY_SEPARATOR . "cmd.php -u=$user -t=$token -a=$actionName -r=$repositoryId"; + if($impersonateUsers !== null){ + $cmd .= " -i=".$impersonateUsers; + } + /* Inserted next 3 lines to quote the command if in windows - rmeske*/ + if (PHP_OS == "WIN32" || PHP_OS == "WINNT" || PHP_OS == "Windows") { + $cmd = ConfService::getGlobalConf("CLI_PHP") . " " . chr(34) . $robustInstallPath . DIRECTORY_SEPARATOR . "cmd.php" . chr(34) . " -u=$user -t=$token -a=$actionName -r=$repositoryId"; + } + if (!empty($statusFile)) { + $cmd .= " -s=" . $statusFile; + } + if (!empty($taskId)) { + $cmd .= " -k=" . $taskId; + } + foreach ($parameters as $key => $value) { + if ($key == "action" || $key == "get_action") continue; + if (is_array($value)) { + $index = 0; + foreach ($value as $v) { + $cmd .= " --file_" . $index . "=" . escapeshellarg($v); + $index++; + } + } else { + $cmd .= " --$key=" . escapeshellarg($value); + } + } + $envSet = false; + if ($ctx->getRepository()->getContextOption($ctx, "USE_SESSION_CREDENTIALS")) { + $envSet = MemorySafe::setEnv(); + } + + // NOW RUN COMMAND + $res = self::runCommandInBackground($cmd, $logFile); + + if($envSet){ + MemorySafe::clearEnv(); + } + return $res; + } + + /** + * @param $cmd + * @param $logFile + * @param $forceLog + * @return UnixProcess|null + */ + public static function runCommandInBackground($cmd, $logFile, $forceLog = false) + { + if (PHP_OS == "WIN32" || PHP_OS == "WINNT" || PHP_OS == "Windows") { + if (AJXP_SERVER_DEBUG || $forceLog) $cmd .= " > " . $logFile; + if (class_exists("COM") && ConfService::getGlobalConf("CLI_USE_COM")) { + $WshShell = new \COM("WScript.Shell"); + $WshShell->Run("cmd /C $cmd", 0, false); + } else { + $basePath = str_replace("/", DIRECTORY_SEPARATOR, AJXP_INSTALL_PATH); + $tmpBat = implode(DIRECTORY_SEPARATOR, array($basePath, "data", "tmp", md5(time()) . ".bat")); + $cmd = "@chcp 1252 > nul \r\n" . $cmd; + $cmd .= "\n DEL " . chr(34) . $tmpBat . chr(34); + Logger::debug("Writing file $cmd to $tmpBat"); + file_put_contents($tmpBat, $cmd); + pclose(popen('start /b "CLI" "' . $tmpBat . '"', 'r')); + } + return null; + } else { + $process = new UnixProcess($cmd, (AJXP_SERVER_DEBUG || $forceLog ? $logFile : null)); + Logger::debug("Starting process and sending output dev null"); + return $process; + } + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Controller/Controller.php b/core/src/core/src/pydio/Core/Controller/Controller.php new file mode 100644 index 0000000000..ff657c2f0e --- /dev/null +++ b/core/src/core/src/pydio/Core/Controller/Controller.php @@ -0,0 +1,553 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Controller; + +use Pydio\Core\Utils\XMLHelper; +use Zend\Diactoros\Response; +use Zend\Diactoros\ServerRequestFactory; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; + +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Model\ContextProviderInterface; + +use Pydio\Core\Services; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Services\UsersService; +use Pydio\Log\Core\Logger; + +use Pydio\Core\Exception\ActionNotFoundException; +use Pydio\Core\Exception\AuthRequiredException; +use Pydio\Core\Exception\PydioException; + +defined('AJXP_EXEC') or die( 'Access not allowed'); +/** + * Core controller for dispatching the actions. + * It uses the XML Registry (simple version, not extended) to search all the tags and find the action. + * @package Pydio + * @subpackage Core + */ +class Controller +{ + /** + * @var \DOMXPath[] + */ + private static $xPathes = []; + /** + * @var array + */ + private static $includeHooks = []; + /** + * @var array + */ + private static $hooksCaches = []; + + /** + * Initialize the queryable xPath object, pointing to the registry associated + * with the current context + * @static + * @param ContextInterface $ctx + * @param bool $useCache Whether to cache the registry version in a memory cache. + * @return \DOMXPath + */ + private static function initXPath($ctx, $useCache = false) + { + $ctxId = $ctx->getStringIdentifier(); + if (!isSet(self::$xPathes[$ctxId])) { + $registry = PluginsService::getInstance($ctx)->getFilteredXMLRegistry(false, false, $useCache); + self::$xPathes[$ctxId] = new \DOMXPath($registry); + } + return self::$xPathes[$ctxId]; + } + + /** + * API V1 : parse parameters based on the URL and their definitions in the manifest. + * @param ServerRequestInterface $request + * @return bool|\DOMElement + * @throws ActionNotFoundException + */ + public static function parseRestParameters(ServerRequestInterface &$request){ + + $actionName = $request->getAttribute("action"); + $path = $request->getAttribute("rest_path"); + $ctx = $request->getAttribute("ctx"); + $reqParameters = $request->getParsedBody(); + + $xPath = self::initXPath($ctx, true); + $actions = $xPath->query("actions/action[@name='$actionName']"); + if (!$actions->length) { + throw new ActionNotFoundException($actionName); + } + $action = $actions->item(0); + $restPathList = $xPath->query("processing/serverCallback/@restParams", $action); + if (!$restPathList->length) { + throw new ActionNotFoundException($actionName); + } + $restPath = $restPathList->item(0)->nodeValue; + $paramNames = explode("/", trim($restPath, "/")); + $exploded = explode("?", $path); + $path = array_shift($exploded); + $paramValues = array_map("urldecode", explode("/", trim($path, "/"), count($paramNames))); + foreach ($paramNames as $i => $pName) { + if (strpos($pName, "+") !== false) { + $paramNames[$i] = str_replace("+", "", $pName); + $paramValues[$i] = "/" . $paramValues[$i]; + } + } + if (count($paramValues) < count($paramNames)) { + $paramNames = array_slice($paramNames, 0, count($paramValues)); + } + $paramValues = array_map(array("Pydio\\Core\\Utils\\TextEncoder", "toUTF8"), $paramValues); + + $reqParameters = array_merge($reqParameters, array_combine($paramNames, $paramValues)); + $request = $request->withParsedBody($reqParameters); + return $action; + + } + + /** + * Middleware entry point + * + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @param callable|null $nextCallable + * @return ResponseInterface + * @throws AuthRequiredException + */ + public static function registryActionMiddleware(ServerRequestInterface $request, ResponseInterface $response, callable $nextCallable = null){ + $action = null; + if($request->getAttribute("api") == "v1"){ + $action = Controller::parseRestParameters($request); + } + $response = Controller::run($request, $action); + if($nextCallable != null){ + $response = call_user_func_array($nextCallable, array(&$request, &$response)); + } + return $response; + } + + /** + * Check if mandatory parameters as defined in the manifests are correct. + * + * @static + * @param array $parameters + * @param \DOMNode $callbackNode + * @param \DOMXPath $xPath + * @throws \Exception + */ + public static function checkParams(&$parameters, $callbackNode, $xPath) + { + if (!$callbackNode->attributes->getNamedItem('checkParams') || $callbackNode->attributes->getNamedItem('checkParams')->nodeValue != "true") { + return; + } + $inputParams = $xPath->query("input_param", $callbackNode); + $declaredParams = array(); + foreach ($inputParams as $param) { + $name = $param->attributes->getNamedItem("name")->nodeValue; + $type = $param->attributes->getNamedItem("type")->nodeValue; + $defaultNode = $param->attributes->getNamedItem("default"); + $mandatory = ($param->attributes->getNamedItem("mandatory")->nodeValue == "true"); + if ($mandatory && !isSet($parameters[$name])) { + throw new \Exception("Missing parameter '".$name."' of type '$type'"); + } + if ($defaultNode != null && !isSet($parameters[$name])) { + $parameters[$name] = $defaultNode->nodeValue; + } + $declaredParams[] = $name; + } + foreach ($parameters as $k => $n) { + if(!in_array($k, $declaredParams)) unset($parameters[$k]); + } + } + + /** + * Main method for querying the XML registry, find an action and all its associated processors, + * and apply all the callbacks. + * + * @static + * @param ServerRequestInterface $request + * @param \DOMNode $actionNode + * @return ResponseInterface + * @throws \Exception + */ + public static function run(ServerRequestInterface $request, &$actionNode = null) + { + $actionName = $request->getAttribute("action"); + /** @var ContextInterface $ctx */ + $ctx = $request->getAttribute("ctx"); + + $xPath = self::initXPath($ctx, true); + if ($actionNode == null) { + $actions = $xPath->query("actions/action[@name='$actionName']"); + if (!$actions->length) { + throw new ActionNotFoundException($actionName); + } + $actionNode = $actions->item(0); + } + //Check Rights + if (UsersService::usersEnabled()) { + $loggedUser = $ctx->getUser(); + if( $actionName != "logout" && Controller::actionNeedsRight($actionNode, $xPath, "userLogged", "only") && $loggedUser == null){ + throw new AuthRequiredException(); + } + if( Controller::actionNeedsRight($actionNode, $xPath, "adminOnly") && + ($loggedUser == null || !$loggedUser->isAdmin())){ + throw new AuthRequiredException("207"); + } + if( Controller::actionNeedsRight($actionNode, $xPath, "read") && + ($loggedUser == null || !$loggedUser->canRead($ctx->getRepositoryId().""))){ + if($actionName == "ls" & $loggedUser!=null + && $loggedUser->canWrite($ctx->getRepositoryId()."")){ + // Special case of "write only" right : return empty listing, no auth error. + $response = new Response(); + $response->getBody()->write(XMLHelper::wrapDocument("")); + return $response; + }else{ + throw new AuthRequiredException("208"); + } + } + if( Controller::actionNeedsRight($actionNode, $xPath, "write") && + ($loggedUser == null || !$loggedUser->canWrite($ctx->getRepositoryId().""))){ + throw new AuthRequiredException("207"); + } + } + + $queries = [ + 'pre_processing/serverCallback' => true, + 'processing/serverCallback' => false, + 'post_processing/serverCallback[not(@capture="true")]' => true, + 'post_processing/serverCallback[@capture="true"]' => true + ]; + + $response = new Response(); + + foreach ($queries as $cbQuery => $multiple){ + $calls = self::getCallbackNode($xPath, $actionNode, $cbQuery, $actionName, $request->getParsedBody(), $_FILES, $multiple); + if(!$multiple && count($calls)){ + self::checkParams($httpVars, $calls[0], $xPath); + } + foreach ($calls as $call){ + self::handleRequest($call, $request, $response); + } + } + + self::applyHook("response.send", array($ctx, &$response)); + + return $response; + } + + /** + * @param ContextInterface $context + * @param string $action + * @param array $parameters + * @return ServerRequestInterface + */ + public static function executableRequest(ContextInterface $context, $action, $parameters = []){ + $request = ServerRequestFactory::fromGlobals(); + $request = $request + ->withAttribute("ctx", $context) + ->withAttribute("action", $action) + ->withParsedBody($parameters); + return $request; + } + + + /** + * Find a callback node by its xpath query, filtering with the applyCondition if the xml attribute exists. + * @static + * @param \DOMXPath $xPath + * @param \DOMNode $actionNode + * @param string $query + * @param string $actionName + * @param array $httpVars + * @param array $fileVars + * @param bool $multiple + * @return \DOMElement[] + */ + private static function getCallbackNode($xPath, $actionNode, $query ,$actionName, $httpVars, $fileVars, $multiple = true) + { + $callbacks = $xPath->query($query, $actionNode); + if(!$callbacks->length) return []; + if ($multiple) { + $cbArray = array(); + foreach ($callbacks as $callback) { + if (self::appliesCondition($callback, $actionName, $httpVars, $fileVars)) { + $cbArray[] = $callback; + } + } + if(!count($cbArray)) return []; + return $cbArray; + } else { + $callback=$callbacks->item(0); + if(!self::appliesCondition($callback, $actionName, $httpVars, $fileVars)) return []; + return [$callback]; + } + } + + /** + * Check in the callback node if an applyCondition XML attribute exists, and eval its content. + * The content must set an $apply boolean as result + * @static + * @param \DOMElement|\DOMNode $callback + * @param string $actionName + * @param array $httpVars + * @param array $fileVars + * @return bool + */ + private static function appliesCondition($callback, $actionName, $httpVars, $fileVars) + { + if ($callback->getAttribute("applyCondition")!="") { + $apply = false; + eval($callback->getAttribute("applyCondition")); + if(!$apply) return false; + } + return true; + } + + /** + * Applies a callback node + * @static + * @param ContextInterface $context + * @param \DOMElement|array $callback The DOM Node or directly an array of attributes + * @param null $variableArgs + * @param bool $defer + * @throws PydioException + * @return mixed + */ + private static function applyCallback($context, $callback, &$variableArgs, $defer = false) + { + //Processing + if(is_array($callback)){ + $plugId = $callback["pluginId"]; + $methodName = $callback["methodName"]; + }else{ + $plugId = $callback->getAttribute("pluginId"); + $methodName = $callback->getAttribute("methodName"); + } + $plugInstance = PluginsService::getInstance($context)->getPluginById($plugId); + //return call_user_func(array($plugInstance, $methodName), $actionName, $httpVars, $fileVars); + // Do not use call_user_func, it cannot pass parameters by reference. + if (method_exists($plugInstance, $methodName)) { + if ($defer == true) { + ShutdownScheduler::getInstance()->registerShutdownEventArray(array($plugInstance, $methodName), $variableArgs); + } else { + call_user_func_array(array($plugInstance, $methodName), $variableArgs); + } + } else { + throw new PydioException("Cannot find method $methodName for plugin $plugId!"); + } + return null; + } + + /** + * @param $callback + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @throws PydioException + */ + private static function handleRequest($callback, ServerRequestInterface &$request, ResponseInterface &$response){ + + list($plugInstance, $methodName) = self::parseCallback($request->getAttribute("ctx"), $callback); + + $reflectMethod = new \ReflectionMethod($plugInstance, $methodName); + $reflectParams = $reflectMethod->getParameters(); + $supportPSR = false; + foreach ($reflectParams as $reflectParam){ + if($reflectParam->getClass() !== null && $reflectParam->getClass()->getName() == "Psr\\Http\\Message\\ServerRequestInterface"){ + $supportPSR = true; + break; + } + } + if($supportPSR){ + + $plugInstance->$methodName($request, $response); + + }else{ + + $httpVars = $request->getParsedBody(); + $result = $plugInstance->$methodName($request->getAttribute("action"), $httpVars, $_FILES, $request->getAttribute("ctx")); + // May have been modified + $request = $request->withParsedBody($httpVars); + + if(!empty($result)){ + $response->getBody()->write(XMLHelper::wrapDocument($result)); + $response = $response->withHeader("Content-type", "text/xml; charset=UTF-8"); + } + } + + } + + /** + * @param \DOMElement|array $callback + * @throws PydioException + * @return array + */ + private static function parseCallback(ContextInterface $ctx, $callback){ + if(is_array($callback)){ + $plugId = $callback["pluginId"]; + $methodName = $callback["methodName"]; + }else{ + $plugId = $callback->getAttribute("pluginId"); + $methodName = $callback->getAttribute("methodName"); + } + $plugInstance = PluginsService::getInstance($ctx)->getPluginById($plugId); + if(empty($plugInstance) || !method_exists($plugInstance, $methodName)){ + throw new PydioException("Cannot find method $methodName for plugin $plugId!"); + } + return [$plugInstance, $methodName]; + } + + /** + * Find all callbacks registered for a given hook and apply them. Caches the hooks locally, on a + * per-context basis + * + * @static + * @param string $hookName + * @param array $args + * @param bool $forceNonDefer + * @throws PydioException + * @throws \Exception + */ + public static function applyHook($hookName, $args, $forceNonDefer = false) + { + $findContext = null; + foreach ($args as $arg){ + if($arg instanceof ContextInterface) { + $findContext = $arg; + break; + }else if($arg instanceof ContextProviderInterface){ + $findContext = $arg->getContext(); + break; + } + } + if($findContext == null){ + Logger::error("Controller", "applyHook", "Applying hook $hookName without context"); + throw new \Exception("No context found for hook $hookName: please make sure to pass at list one ContextInterface or Context Provider object"); + } + + $contextId = $findContext->getStringIdentifier(); + if(isSet(self::$hooksCaches[$contextId][$hookName])){ + $hooks = self::$hooksCaches[$contextId][$hookName]; + foreach($hooks as $hook){ + if (isSet($hook["applyCondition"]) && $hook["applyCondition"]!="") { + $apply = false; + eval($hook["applyCondition"]); + if(!$apply) continue; + } + $defer = $hook["defer"]; + if($defer && $forceNonDefer) $defer = false; + self::applyCallback($findContext, $hook, $args, $defer); + } + return; + } + $xPath = self::initXPath($findContext, true); + $callbacks = $xPath->query("hooks/serverCallback[@hookName='$hookName']"); + if(!$callbacks->length) return ; + if(!isSet(self::$hooksCaches[$contextId])){ + self::$hooksCaches[$contextId] = []; + } + self::$hooksCaches[$contextId][$hookName] = []; + /** + * @var $callback \DOMElement + */ + foreach ($callbacks as $callback) { + $defer = ($callback->getAttribute("defer") === "true"); + $applyCondition = $callback->getAttribute("applyCondition"); + $plugId = $callback->getAttribute("pluginId"); + $methodName = $callback->getAttribute("methodName"); + $dontBreakOnExceptionAtt = $callback->getAttribute("dontBreakOnException"); + $dontBreakOnException = !empty($dontBreakOnExceptionAtt) && $dontBreakOnExceptionAtt == "true"; + $hookCallback = array( + "defer" => $defer, + "applyCondition" => $applyCondition, + "pluginId" => $plugId, + "methodName" => $methodName + ); + self::$hooksCaches[$contextId][$hookName][] = $hookCallback; + if (!empty($applyCondition)) { + $apply = false; + eval($applyCondition); + if(!$apply) continue; + } + if($defer && $forceNonDefer) $defer = false; + if($dontBreakOnException){ + try{ + self::applyCallback($findContext, $hookCallback, $args, $defer); + }catch(\Exception $e){ + Logger::error("[Hook $hookName]", "[Callback ".$plugId.".".$methodName."]", $e->getMessage()); + } + }else{ + self::applyCallback($findContext, $hookCallback, $args, $defer); + } + } + } + + /** + * Find the statically defined callbacks for a given hook and apply them + * @static + * @param $hookName + * @param $args + * @return void + */ + public static function applyIncludeHook($hookName, &$args) + { + if(!isSet(self::$includeHooks[$hookName])) return; + foreach (self::$includeHooks[$hookName] as $callback) { + call_user_func_array($callback, $args); + } + } + + /** + * Register a hook statically when it must be defined before the XML registry construction. + * @static + * @param $hookName + * @param $callback + * @return void + */ + public static function registerIncludeHook($hookName, $callback) + { + if (!isSet(self::$includeHooks[$hookName])) { + self::$includeHooks[$hookName] = array(); + } + self::$includeHooks[$hookName][] = $callback; + } + + /** + * Check the rightsContext node of an action. + * @static + * @param \DOMNode $actionNode + * @param \DOMXPath $xPath + * @param string $right + * @return bool + */ + public static function actionNeedsRight($actionNode, $xPath, $right, $expectedValue="true") + { + $rights = $xPath->query("rightsContext", $actionNode); + if(!$rights->length) return false; + $rightNode = $rights->item(0); + $rightAttr = $xPath->query("@".$right, $rightNode); + if ($rightAttr->length && $rightAttr->item(0)->value == $expectedValue) { + return true; + } + return false; + } + +} diff --git a/core/src/core/src/pydio/Core/Controller/HTMLWriter.php b/core/src/core/src/pydio/Core/Controller/HTMLWriter.php new file mode 100644 index 0000000000..43b6eb82ca --- /dev/null +++ b/core/src/core/src/pydio/Core/Controller/HTMLWriter.php @@ -0,0 +1,268 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Controller; + +use Psr\Http\Message\ResponseInterface; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Utils\TextEncoder; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Static functions for generating HTML. + * @package Pydio + * @subpackage Core + */ +class HTMLWriter +{ + + /** + * Replace the doc files keywords + * @static + * @param string $docFileName + * @return string + */ + public static function getDocFile($docFileName) + { + $realName = AJXP_DOCS_FOLDER."/".$docFileName.".txt"; + if (is_file($realName)) { + $content = implode("
    ", file($realName)); + $content = preg_replace("(http:\/\/[a-z|.|\/|\-|0-9]*)", "$0", $content); + $content = preg_replace("(\[(.*)\])", "
    $1
    ", $content); + $content = preg_replace("(\+\+ (.*) \+\+)", "
    $1
    ", $content); + $content = str_replace("__AJXP_VERSION__", AJXP_VERSION, $content); + $content = str_replace("__AJXP_VERSION_DATE__", AJXP_VERSION_DATE, $content); + return $content; + } + return "File not found : ".$docFileName; + } + + /** + * Send a simple Content-type header + * @static + */ + public static function internetExplorerMainDocumentHeader(ResponseInterface &$response = null) + { + if (strstr($_SERVER["HTTP_USER_AGENT"], "MSIE 9.")) { + if(isSet($response)){ + $response = $response->withHeader("X-UA-Compatible", "IE=9"); + }else{ + header("X-UA-Compatible: IE=9"); + } + } else if (strstr($_SERVER["HTTP_USER_AGENT"], "MSIE 10.")) { + if(isSet($response)){ + $response = $response->withHeader("X-UA-Compatible", "IE=Edge, chrome=1"); + }else{ + header("X-UA-Compatible: IE=Edge,chrome=1"); + } + } + if(isSet($response)){ + $response = $response->withHeader("Content-type", "text/html; charset=utf-8"); + } + } + + /** + * Send a simple Content-type header + * @static + * @param string $type + * @param string $charset + * @return void + */ + public static function charsetHeader($type = 'text/html', $charset='UTF-8') + { + header("Content-type:$type; charset=$charset"); + } + + /** + * Write directly an error as a javascript instruction + * @static + * @param $errorType + * @param $errorMessage + */ + public static function javascriptErrorHandler($errorType, $errorMessage) + { + // Handle "@" case! + if(error_reporting() == 0) return ; + restore_error_handler(); + die(""); + } + + + /** + * @param ResponseInterface $response + * @param $attachName + * @param $fileSize + * @param $mimeType + * @return ResponseInterface + */ + public static function responseWithInlineHeaders(ResponseInterface $response, $attachName, $fileSize, $mimeType){ + return self::generateInlineHeaders($attachName, $fileSize, $mimeType, $response); + } + + /** + * @param ResponseInterface $response + * @param $attachmentName + * @param $dataSize + * @param bool $isFile + * @param bool $gzip + * @return null|ResponseInterface + */ + public static function responseWithAttachmentsHeaders(ResponseInterface $response, &$attachmentName, $dataSize, $isFile=true, $gzip=false){ + return self::generateAttachmentsHeader($attachmentName, $dataSize, $isFile, $gzip, $response); + } + + + /** + * @param $attachName + * @param $fileSize + * @param $mimeType + */ + public static function emitInlineHeaders($attachName, $fileSize, $mimeType){ + self::generateInlineHeaders($attachName, $fileSize, $mimeType); + } + + /** + * @param $attachmentName + * @param $dataSize + * @param bool $isFile + * @param bool $gzip + */ + public static function emitAttachmentsHeaders(&$attachmentName, $dataSize, $isFile=true, $gzip=false){ + self::generateAttachmentsHeader($attachmentName, $dataSize, $isFile, $gzip); + } + + + + /** + * Correctly encode name for attachment header + * @param string $name + * @return string + */ + private static function encodeAttachmentName($name){ + if (preg_match('/ MSIE /',$_SERVER['HTTP_USER_AGENT']) + || preg_match('/ WebKit /',$_SERVER['HTTP_USER_AGENT']) + || preg_match('/ Trident/',$_SERVER['HTTP_USER_AGENT'])) { + $name = str_replace("+", " ", urlencode(TextEncoder::toUTF8($name))); + } + return $name; + } + + /** + * @static + * @param string $attachmentName + * @param int $dataSize + * @param bool $isFile + * @param bool $gzip If true, make sure the $dataSize is the size of the ENCODED data. + * @param ResponseInterface $response Response to update instead of generating headers + * @return ResponseInterface|null Update response interface if passed by argument + */ + private static function generateAttachmentsHeader(&$attachmentName, $dataSize, $isFile=true, $gzip=false, ResponseInterface $response = null) + { + $attachmentName = self::encodeAttachmentName($attachmentName); + + $headers = []; + $headers["Content-Type"] = "application/force-download; name=\"".$attachmentName."\""; + $headers["Content-Transfer-Encoding"] = "binary"; + if ($gzip) { + $headers["Content-Encoding"] = "gzip"; + } + $headers["Content-Length"] = $dataSize; + if ($isFile && ($dataSize != 0)) { + $headers["Content-Range"] = "bytes 0-" . ($dataSize- 1) . "/" . $dataSize . ";"; + } + $headers["Content-Disposition"] = "attachment; filename=\"".$attachmentName."\""; + $headers["Expires"] = "0"; + $headers["Cache-Control"] = "no-cache, must-revalidate"; + $headers["Pragma"] = "no-cache"; + + if (preg_match('/ MSIE /',$_SERVER['HTTP_USER_AGENT'])) { + $headers["Pragma"] = "public"; + $headers["Cache-Control"] = "max_age=0"; + } + + // IE8 is dumb + if (preg_match('/ MSIE /',$_SERVER['HTTP_USER_AGENT'])) { + $headers["Pragma"] = "public"; + $headers["Cache-Control"] = "private, must-revalidate, post-check=0, pre-check=0"; + } + + // For SSL websites there is a bug with IE see article KB 323308 + // therefore we must reset the Cache-Control and Pragma Header + if (ConfService::getConf("USE_HTTPS")==1 && preg_match('/ MSIE /',$_SERVER['HTTP_USER_AGENT'])) { + $headers["Pragma"] = ""; + $headers["Cache-Control"] = ""; + } + + foreach($headers as $headerName => $headerValue){ + if($response !== null){ + $response = $response->withHeader($headerName, (string) $headerValue); + }else{ + header($headerName.": ".$headerValue); + } + } + + return $response; + } + + /** + * Generate correct headers + * @param string $attachName + * @param int $fileSize + * @param string $mimeType + * @param ResponseInterface $response + * @return ResponseInterface + */ + private static function generateInlineHeaders($attachName, $fileSize, $mimeType, ResponseInterface $response = null) + { + $attachName = self::encodeAttachmentName($attachName); + + $headers = []; + $headers["Content-Type"] = $mimeType . "; name=\"" . $attachName . "\""; + $headers["Content-Disposition"] = "inline; filename=\"" . $attachName . "\""; + // changed header for IE 7 & 8 + if (preg_match('/ MSIE /',$_SERVER['HTTP_USER_AGENT'])) { + $headers["Expires"] = "0"; + $headers["Pragma"] = "public"; + $headers["Cache-Control"] = "private, must-revalidate, post-check=0, pre-check=0"; + } else { + $headers["Cache-Control"] = "public"; + } + $headers["Content-Length"] = $fileSize; + + // Neccessary for IE 8 and xx + if (ConfService::getConf("USE_HTTPS")==1 && preg_match('/ MSIE /',$_SERVER['HTTP_USER_AGENT'])) { + $headers["Pragma"] = ""; + $headers["Cache-Control"] = ""; + } + + foreach($headers as $headerName => $headerValue){ + if($response !== null){ + $response = $response->withHeader($headerName, (string) $headerValue); + }else{ + header($headerName.": ".$headerValue); + } + } + + return $response; + + } + +} diff --git a/core/src/core/src/pydio/Core/Controller/ProgressBarCLI.php b/core/src/core/src/pydio/Core/Controller/ProgressBarCLI.php new file mode 100644 index 0000000000..e2fbcbd23b --- /dev/null +++ b/core/src/core/src/pydio/Core/Controller/ProgressBarCLI.php @@ -0,0 +1,175 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Controller; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Class AJXP_ProgressBarCLI + * Simple object to print a progress bar on the command-line. + * + * @package Pydio + * @subpackage Core + */ +class ProgressBarCLI { + private $strProgress = ""; + private $strLength = 25; // 50 '=' + private $strName = ""; + private $strNameLengthMax = 20; + private $total = 0; + private $isFinish = false; + private $lastPercent = -1; + private $barSign = "="; + private $startPoint ; + private $lastsecond = 0; + + public function init($currentValue = 0, $total, $name){ + if(!(php_sapi_name() == "cli")) return; + $this->total = $total; + $this->strName = $name; + $this->startPoint = time(); + $this->barSign = "="; + //echo "\nobjects: ". $this->total."\n"; + $str = ''; + $str .= $this->convertNumToStrBlank($this->strNameLengthMax)."[=="; + $str .= "Total: ".$total." objects"; + $str .= $this->convertNumToStr($this->strLength - strlen($str) + 21); + $str .= "]"; + echo "\n"; + echo $str; + echo "\n"; + } + + public function update($currentValue = 0){ + if(!(php_sapi_name() == "cli")) return; + if($this->isFinish) return; + $percentage = $this->convertToPercent($currentValue, $this->total); + if (($percentage != $this->lastPercent) || ((time() - $this->lastsecond) > 0.9)){ + $this->lastPercent = $percentage; + $this->lastsecond = time(); + } + else{ + return; + } + $strDone = $this->convertNumToStr((int) $percentage/4); + $this->strProgress .= $strDone; + $this->strProgress .= $this->convertNumToStrBlank($this->strLength - strlen($strDone)); + $this->addStartString(); + $this->strProgress = $this->formatName($this->strName).$this->strProgress; + $this->addEndString(); + $this->strProgress .= " "; + + if($percentage < 10){ + $this->strProgress .= " "; + }elseif($percentage > 10 && $percentage < 100){ + $this->strProgress .= " "; + } + $this->strProgress .= "".$percentage." % " . $this->ago($this->startPoint); + $this->clearLine(strlen($this->strProgress) + 2); + echo $this->strProgress; + $this->strProgress = ""; + if($percentage == 100){ + echo "\n"; + $this->isFinish = true; + return ; + } + } + + private function addStartString(){ + $this->strProgress = "[".$this->strProgress; + } + + private function addEndString(){ + $this->strProgress = $this->strProgress."]"; + } + + private function convertToPercent($currentValue = 0, $maxValue){ + if($maxValue > 0 && $currentValue > 0){ + $percent = ( int ) ((100*$currentValue) / $maxValue); + return $percent; + } + elseif($maxValue === $currentValue){ + return 100; + } + return 0; + } + + private function convertNumToStr($num){ + if ($num >= 0 && $num <= $this->strLength){ + $str = ""; + for($i = 0; $i < $num; $i++){ + $str .= $this->barSign; + } + return $str; + }else{ + return ""; + } + } + + private function formatName($name){ + if(strlen($name) > $this->strNameLengthMax){ + return substr($name, 0, $this->strNameLengthMax); + }else{ + $str = $name . $this->convertNumToStrBlank($this->strNameLengthMax - strlen($name)); + return $str; + } + } + + public function convertNumToStrBlank($num){ + if($num > 0){ + $str = ""; + for($i = 0; $i < $num; $i++){ + $str .= " "; + } + return $str; + } + return ""; + } + + public function clearLine($lineLength = 80){ + for($i = 0; $i < $lineLength ;$i ++){ + echo "\010"; + } + } + + public function setName($name){ + $this->strName = $name; + } + + public function ago($time) { + $timediff=time()-$time; + + $days=intval($timediff/86400); + $remain=$timediff%86400; + $hours=intval($remain/3600); + $remain=$remain%3600; + $mins=intval($remain/60); + $secs=$remain%60; + $timestring = "-"; + + if ($secs>=0) $timestring = "0m".$secs."s"; + if ($mins>0) $timestring = $mins."m".$secs."s"; + if ($hours>0) $timestring = $hours."u".$mins."m"; + if ($days>0) $timestring = $days."d".$hours."u"; + + return $timestring; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Controller/ShutdownScheduler.php b/core/src/core/src/pydio/Core/Controller/ShutdownScheduler.php new file mode 100644 index 0000000000..b1798f914c --- /dev/null +++ b/core/src/core/src/pydio/Core/Controller/ShutdownScheduler.php @@ -0,0 +1,131 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Controller; + +use Pydio\Log\Core\Logger; + +defined('AJXP_EXEC') or die('Access not allowed'); +/** + * + * Registry for callbacks that must be triggered after the script is finished. + * + * @package Pydio + * @subpackage Core + * + */ +class ShutdownScheduler +{ + private static $instance; + + private $callbacks; // array to store user callbacks + + /** + * @static + * @return ShutdownScheduler + */ + public static function getInstance() + { + if(self::$instance == null) self::$instance = new ShutdownScheduler(); + return self::$instance; + } + + /** + * ShutdownScheduler constructor. + */ + public function __construct() + { + $this->callbacks = array(); + register_shutdown_function(array($this, 'callRegisteredShutdown')); + ob_start(); + } + + /** + * @return bool + * @throws \Exception + */ + public function registerShutdownEventArray() + { + $callback = func_get_args(); + + if (empty($callback)) { + throw new \Exception('No callback passed to '.__FUNCTION__.' method'); + } + if (!is_callable($callback[0])) { + throw new \Exception('Invalid callback ('.$callback[0].') passed to the '.__FUNCTION__.' method'); + } + $flattenArray = array(); + $flattenArray[0] = $callback[0]; + if (is_array($callback[1])) { + foreach($callback[1] as $argument) $flattenArray[] = $argument; + } + $this->callbacks[] = $flattenArray; + return true; + } + + /** + * @return bool + * @throws \Exception + */ + public function registerShutdownEvent() + { + $callback = func_get_args(); + + if (empty($callback)) { + throw new \Exception('No callback passed to '.__FUNCTION__.' method'); + } + if (!is_callable($callback[0])) { + throw new \Exception('Invalid callback ('.$callback[0].') passed to the '.__FUNCTION__.' method'); + } + $this->callbacks[] = $callback; + return true; + } + + /** + * Trigger the schedulers + */ + public function callRegisteredShutdown() + { + session_write_close(); + if (!headers_sent()) { + $size = ob_get_length(); + header("Connection: close\r\n"); + //header("Content-Encoding: none\r\n"); + header("Content-Length: $size"); + } + ob_end_flush(); + flush(); + $index = 0; + while (count($this->callbacks)) { + $arguments = array_shift($this->callbacks); + $callback = array_shift($arguments); + try { + call_user_func_array($callback, $arguments); + } catch (\Exception $e) { + Logger::error(__CLASS__, __FUNCTION__, array("context" => "Applying hook " . get_class($callback[0]) . "::" . $callback[1], "message" => $e->getMessage())); + } + $index++; + if($index > 100000) { + Logger::error(__CLASS__, __FUNCTION__, "Breaking ShutdownScheduler loop, seems too big (100000)"); + break; + } + } + } +} diff --git a/core/src/core/src/pydio/Core/Controller/UnixProcess.php b/core/src/core/src/pydio/Core/Controller/UnixProcess.php new file mode 100644 index 0000000000..6a4cb98cbb --- /dev/null +++ b/core/src/core/src/pydio/Core/Controller/UnixProcess.php @@ -0,0 +1,121 @@ +, Cyril Russo + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Controller; + +defined('AJXP_EXEC') or die( 'Access not allowed'); +/** + * Utilitary to launch a process and keep track of it + * @package Pydio + * @subpackage Core + */ +class UnixProcess +{ + /** + * @var string + */ + private $pid; + /** + * @var string + */ + private $command; + /** + * @var string + */ + private $output; + + /** + * @param bool|string $cl Command to execute + * @param bool|string $output A file in which to redirect the output. Send to /dev/null if false. + */ + public function __construct($cl=false, $output=false) + { + if ($output != false) { + $this->output = $output; + } else { + $this->output = "/dev/null"; + } + if ($cl != false) { + $this->command = $cl; + $this->runCom(); + } + } + /** + * Run the command + * @return void + */ + private function runCom() + { + $command = $this->command.' > '.$this->output.' 2>&1 & echo $!'; + exec($command ,$op); + $this->pid = (int) $op[0]; + $this->command = $command; + } + /** + * Processid setter + * @param $pid + * @return void + */ + public function setPid($pid) + { + $this->pid = $pid; + } + + /** + * Processid getter + * @return string + */ + public function getPid() + { + return $this->pid; + } + + /** + * Try to get status from command line by running "ps -p PID" + * @return bool + */ + public function status() + { + $command = 'ps -p '.$this->pid; + exec($command,$op); + if (!isset($op[1]))return false; + else return true; + } + /** + * Start the command + * @return bool + */ + public function start() + { + if ($this->command != '') $this->runCom(); + return true; + } + /** + * Try to kill the process via command line. + * @return bool + */ + public function stop() + { + $command = 'kill '.$this->pid; + exec($command); + if ($this->status() == false)return true; + else return false; + } +} diff --git a/core/src/core/src/pydio/Core/Exception/ActionNotFoundException.php b/core/src/core/src/pydio/Core/Exception/ActionNotFoundException.php new file mode 100644 index 0000000000..caa9cdd708 --- /dev/null +++ b/core/src/core/src/pydio/Core/Exception/ActionNotFoundException.php @@ -0,0 +1,40 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Exception; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class ActionNotFoundException + * Exception thrown when a requests is calling an unknown (or disabled) action. + * @package Pydio\Core\Exception + */ +class ActionNotFoundException extends PydioException +{ + /** + * ActionNotFoundException constructor. + * @param string $action + */ + public function __construct($action) + { + parent::__construct("Could not find action ".$action, null); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Exception/AuthRequiredException.php b/core/src/core/src/pydio/Core/Exception/AuthRequiredException.php new file mode 100644 index 0000000000..208345d267 --- /dev/null +++ b/core/src/core/src/pydio/Core/Exception/AuthRequiredException.php @@ -0,0 +1,91 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Exception; + +use Pydio\Core\Http\Message\LoggingResult; +use Pydio\Core\Http\Message\UserMessage; +use Pydio\Core\Http\Response\JSONSerializableResponseChunk; +use Pydio\Core\Http\Response\XMLSerializableResponseChunk; + +use Pydio\Core\Services\LocaleService; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class AuthRequiredException + * @package Pydio\Core\Exception + */ +class AuthRequiredException extends PydioException implements XMLSerializableResponseChunk, JSONSerializableResponseChunk +{ + private $loginResult; + + /** + * AuthRequiredException constructor. + * @param string $messageId + * @param string $messageString + * @param null $loginResult + */ + public function __construct($messageId = "", $messageString = "", $loginResult = null) + { + if(!empty($messageId)){ + $mess = LocaleService::getMessages(); + if(isSet($mess[$messageId])) $messageString = $mess[$messageId]; + } + if(!empty($loginResult)){ + $this->loginResult = $loginResult; + } + parent::__construct($messageString, $messageId); + } + + /** + * @return mixed + */ + public function jsonSerializableData() + { + return ["message" => $this->getMessage()]; + } + + /** + * @return string + */ + public function jsonSerializableKey() + { + return "authRequired"; + } + + /** + * @return string + */ + public function toXML() + { + if(!empty($this->loginResult)){ + $res = new LoggingResult($this->loginResult, "", "", ""); + $xml = $res->toXML(); + }else{ + $xml = ""; + if($this->getMessage()){ + $error = new UserMessage($this->getMessage(), LOG_LEVEL_ERROR); + $xml.= $error->toXML(); + } + } + return $xml; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Exception/DBConnectionException.php b/core/src/core/src/pydio/Core/Exception/DBConnectionException.php new file mode 100644 index 0000000000..4fe5832308 --- /dev/null +++ b/core/src/core/src/pydio/Core/Exception/DBConnectionException.php @@ -0,0 +1,39 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Exception; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class DBConnectionException + * @package Pydio\Core\Exception + */ +class DBConnectionException extends PydioException +{ + /** + * DBConnectionException constructor. + */ + public function __construct() + { + $messageString = "There was an error trying to connect to your database! Did you change any configuration for the core connection? Or maybe your database is down?"; + parent::__construct($messageString); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Exception/ForbiddenCharacterException.php b/core/src/core/src/pydio/Core/Exception/ForbiddenCharacterException.php new file mode 100644 index 0000000000..0f54b0c19e --- /dev/null +++ b/core/src/core/src/pydio/Core/Exception/ForbiddenCharacterException.php @@ -0,0 +1,39 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Exception; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class ForbiddenCharacterException + * @package Pydio\Core\Exception + */ +class ForbiddenCharacterException extends PydioException +{ + /** + * ForbiddenCharacterException constructor. + * @param string $originalString + */ + public function __construct($originalString) + { + parent::__construct("$originalString contains forbbiden characters", null, 5012); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Exception/LoginException.php b/core/src/core/src/pydio/Core/Exception/LoginException.php new file mode 100644 index 0000000000..178fcf862f --- /dev/null +++ b/core/src/core/src/pydio/Core/Exception/LoginException.php @@ -0,0 +1,50 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Exception; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Class LoginException + * @package Pydio\Core\Exception + */ +class LoginException extends PydioException +{ + private $loginError; + + /** + * LoginException constructor. + * @param integer $errorCode + */ + public function __construct($errorCode) + { + $this->loginError = $errorCode; + parent::__construct("Error while trying to log user", null, $errorCode); + } + + /** + * @return int + */ + public function getLoginError(){ + return $this->loginError; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Exception/NoActiveWorkspaceException.php b/core/src/core/src/pydio/Core/Exception/NoActiveWorkspaceException.php new file mode 100644 index 0000000000..1a2632b8c4 --- /dev/null +++ b/core/src/core/src/pydio/Core/Exception/NoActiveWorkspaceException.php @@ -0,0 +1,39 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Exception; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Class NoActiveWorkspaceException + * @package Pydio\Core\Exception + */ +class NoActiveWorkspaceException extends PydioException +{ + /** + * NoActiveWorkspaceException constructor. + */ + public function __construct() + { + parent::__construct("No active workspace found for user!", false, 423); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Exception/PydioException.php b/core/src/core/src/pydio/Core/Exception/PydioException.php new file mode 100644 index 0000000000..f7570f1979 --- /dev/null +++ b/core/src/core/src/pydio/Core/Exception/PydioException.php @@ -0,0 +1,119 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Exception; + +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\LocaleService; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Class PydioException + * @package Pydio\Core\Exception + */ +class PydioException extends \Exception +{ + private $errorCode; + + /** + * PydioException constructor. + * @param string $messageString + * @param bool $messageId + * @param null $errorCode + */ + public function __construct($messageString, $messageId = false, $errorCode = null) + { + if ($messageId !== false && class_exists("ConfService")) { + $messages = LocaleService::getMessages(); + if (array_key_exists($messageId, $messages)) { + $messageString = $messages[$messageId]; + } else { + $messageString = $messageId; + } + } + if(isSet($errorCode)){ + $this->errorCode = $errorCode; + } + parent::__construct($messageString); + } + + /** + * @param $mixed + * @throws PydioException + */ + public function errorToXml($mixed) + { + if ($mixed instanceof \Exception) { + throw $this; + } else { + throw new PydioException($mixed); + } + } + + /** + * @return bool + */ + public function hasErrorCode(){ + return isSet($this->errorCode); + } + + /** + * @return null + */ + public function getErrorCode(){ + return $this->errorCode; + } + + /** + * @return string + */ + public static function buildDebugBackTrace(){ + + $message = ""; + + if (ConfService::getConf("SERVER_DEBUG")) { + $stack = debug_backtrace(); + $stackLen = count($stack); + for ($i = 1; $i < $stackLen; $i++) { + $entry = $stack[$i]; + + $func = $entry['function'] . '('; + $argsLen = count($entry['args']); + for ($j = 0; $j < $argsLen; $j++) { + $s = $entry['args'][$j]; + if(is_string($s)){ + $func .= $s; + }else if (is_object($s)){ + $func .= get_class($s); + } + if ($j < $argsLen - 1) $func .= ', '; + } + $func .= ')'; + + $message .= "\n". str_replace(dirname(__FILE__), '', $entry['file']) . ':' . $entry['line'] . ' - ' . $func . PHP_EOL; + } + } + + return $message; + + } + +} diff --git a/core/src/core/src/pydio/Core/Exception/PydioPromptException.php b/core/src/core/src/pydio/Core/Exception/PydioPromptException.php new file mode 100644 index 0000000000..3659ea520d --- /dev/null +++ b/core/src/core/src/pydio/Core/Exception/PydioPromptException.php @@ -0,0 +1,139 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Exception; + +use Pydio\Core\Http\Response\JSONSerializableResponseChunk; +use Pydio\Core\Http\Response\XMLSerializableResponseChunk; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +define('AJXP_PROMPT_EXCEPTION_PROMPT', 'AJXP_PROMPT_EXCEPTION_PROMPT'); +define('AJXP_PROMPT_EXCEPTION_CONFIRM', 'AJXP_PROMPT_EXCEPTION_CONFIRM'); +define('AJXP_PROMPT_EXCEPTION_ALERT', 'AJXP_PROMPT_EXCEPTION_ALERT'); + +/** + * Class AJXP_PromptException + * Specific exception that triggers a prompt in the UI instead of displaying an error message. + * + * @package Pydio + * @subpackage Core + */ +class PydioPromptException extends PydioException implements XMLSerializableResponseChunk, JSONSerializableResponseChunk +{ + + private $promptType = "prompt"; + /** + * @var array + */ + private $promptData = array(); + + /** + * @return array + */ + public function getPromptData() + { + return $this->promptData; + } + + /** + * @return string + */ + public function getPromptType() + { + return $this->promptType; + } + + /** + * @param $promptType + * @param array $data + * @param String $messageString + * @param string|bool $messageId + */ + public function __construct($promptType, $data, $messageString, $messageId = false) + { + $this->promptType = $promptType; + $this->promptData = $data; + parent::__construct($messageString, $messageId); + } + + /** + * Prompt user for credentials + * @param $sessionVariable + * @param $switchToRepositoryId + * @throws PydioPromptException + */ + public static function testOrPromptForCredentials($sessionVariable, $switchToRepositoryId){ + if(isSet($_GET["prompt_passed_data"]) && isSet($_GET["variable_name"]) && $_GET["variable_name"] == $sessionVariable){ + $_SESSION[$sessionVariable] = true; + } + if(!isSet($_SESSION[$sessionVariable])){ + throw new PydioPromptException( + "confirm", + array( + "DIALOG" => "Please enter your credentials for this workspace + + + + + ", + "OK" => array( + "GET_FIELDS" => array("get_action", "repository_id", "prompt_passed_data", "variable_name"), + "EVAL" => "ajaxplorer.loadXmlRegistry();" + ), + "CANCEL" => array( + "EVAL" => "ajaxplorer.loadXmlRegistry();" + ) + ), + "Credentials Needed"); + } + + } + + /** + * @return mixed + */ + public function jsonSerializableData() + { + return [ + "promptType" => $this->promptType, + "promptMessage" => $this->getMessage(), + "promptData" => $this->promptData + ]; + } + + /** + * @return string + */ + public function jsonSerializableKey() + { + return "userPrompt"; + } + + /** + * @return string + */ + public function toXML() + { + return "promptType."\">".$this->getMessage()."promptData)."]]>"; + } + + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Exception/PydioUserAlertException.php b/core/src/core/src/pydio/Core/Exception/PydioUserAlertException.php new file mode 100644 index 0000000000..b5d60894a1 --- /dev/null +++ b/core/src/core/src/pydio/Core/Exception/PydioUserAlertException.php @@ -0,0 +1,31 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Exception; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class PydioUserAlertException + * @package Pydio\Core\Exception + */ +class PydioUserAlertException extends PydioException{ + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Exception/RepositoryLoadException.php b/core/src/core/src/pydio/Core/Exception/RepositoryLoadException.php new file mode 100644 index 0000000000..40add21d70 --- /dev/null +++ b/core/src/core/src/pydio/Core/Exception/RepositoryLoadException.php @@ -0,0 +1,59 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Exception; + +use Pydio\Core\Model\RepositoryInterface; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class RepositoryLoadException + * @package Pydio\Core\Exception + */ +class RepositoryLoadException extends PydioException +{ + /** + * @var RepositoryInterface + */ + private $repository; + /** + * RepositoryLoadException constructor. + * @param RepositoryInterface|String $repository + * @param array $errors + */ + public function __construct($repository, $errors) + { + if($repository instanceof RepositoryInterface){ + $message = "Error while loading workspace ".$repository->getDisplay()." : ".implode("\n-", $errors); + $this->repository = $repository; + }else{ + $message = "Error while loading workspace ".$repository." : ".implode("\n-", $errors); + } + parent::__construct($message, false, 5000); + } + + /** + * @return RepositoryInterface|String + */ + public function getRepository(){ + return $this->repository; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Exception/RouteNotFoundException.php b/core/src/core/src/pydio/Core/Exception/RouteNotFoundException.php new file mode 100644 index 0000000000..fe4e9b9caf --- /dev/null +++ b/core/src/core/src/pydio/Core/Exception/RouteNotFoundException.php @@ -0,0 +1,20 @@ +code = 404; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Exception/UserNotFoundException.php b/core/src/core/src/pydio/Core/Exception/UserNotFoundException.php new file mode 100644 index 0000000000..4ae5971a96 --- /dev/null +++ b/core/src/core/src/pydio/Core/Exception/UserNotFoundException.php @@ -0,0 +1,39 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Exception; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class UserNotFoundException + * @package Pydio\Core\Exception + */ +class UserNotFoundException extends PydioException +{ + /** + * UserNotFoundException constructor. + * @param string $userId + */ + public function __construct($userId) + { + parent::__construct("Cannot find user ".$userId, false, 50014); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Exception/WorkspaceForbiddenException.php b/core/src/core/src/pydio/Core/Exception/WorkspaceForbiddenException.php new file mode 100644 index 0000000000..f756c98467 --- /dev/null +++ b/core/src/core/src/pydio/Core/Exception/WorkspaceForbiddenException.php @@ -0,0 +1,39 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Exception; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class WorkspaceForbiddenException + * @package Pydio\Core\Exception + */ +class WorkspaceForbiddenException extends PydioException +{ + /** + * WorkspaceForbiddenException constructor. + * @param string $wsId + */ + public function __construct($wsId) + { + parent::__construct("Cannot access to workspace with id/alias ".$wsId, null); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Exception/WorkspaceNotFoundException.php b/core/src/core/src/pydio/Core/Exception/WorkspaceNotFoundException.php new file mode 100644 index 0000000000..bf60e07360 --- /dev/null +++ b/core/src/core/src/pydio/Core/Exception/WorkspaceNotFoundException.php @@ -0,0 +1,39 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Exception; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class WorkspaceNotFoundException + * @package Pydio\Core\Exception + */ +class WorkspaceNotFoundException extends PydioException +{ + /** + * WorkspaceNotFoundException constructor. + * @param string $wsId + */ + public function __construct($wsId) + { + parent::__construct("Could not find workspace with id/alias ".$wsId, null); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Base.php b/core/src/core/src/pydio/Core/Http/Base.php new file mode 100644 index 0000000000..381f21c80c --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Base.php @@ -0,0 +1,64 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http; + +use Pydio\Core\Services\ConfService; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Very Top Level Routing + * @package Pydio\Core\Http + */ +class Base +{ + + /** + * @param string $base + * @param string $route + * @param array $additionalAttributes + */ + public static function handleRoute($base, $route, $additionalAttributes = []){ + + if ($route === "/api") { + $server = new Rest\RestApiServer($base.$route, $additionalAttributes); + } else if ($route == "/wopi") { + $server = new Wopi\Server($base.$route, $additionalAttributes); + } else if ($route === "/user") { + $_GET["get_action"] = "user_access_point"; + $_GET["key"] = $additionalAttributes["key"]; + $server = new Server($base, $additionalAttributes); + } else if ($route == "/favicon"){ + $_GET["get_action"] = "serve_favicon"; + $server = new Server($base, $additionalAttributes); + } else { + $server = new Server($base, $additionalAttributes); + } + + $server->registerCatchAll(); + + ConfService::init(); + ConfService::start(); + + $server->listen(); + } + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Cli/AuthCliMiddleware.php b/core/src/core/src/pydio/Core/Http/Cli/AuthCliMiddleware.php new file mode 100644 index 0000000000..0cee2ddc80 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Cli/AuthCliMiddleware.php @@ -0,0 +1,245 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Cli; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Auth\Core\MemorySafe; + +use Pydio\Core\Exception\AuthRequiredException; +use Pydio\Core\Exception\LoginException; +use Pydio\Core\Http\Response\SerializableResponseStream; +use Pydio\Core\Http\Server; +use Pydio\Core\Model\Context; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Services\AuthService; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\RolesService; +use Pydio\Core\Services\UsersService; +use Pydio\Core\Services\ApplicationState; +use Pydio\Core\Utils\Crypto; +use Pydio\Core\Utils\TextEncoder; +use Pydio\Log\Core\Logger; +use Pydio\Tasks\Task; +use Pydio\Tasks\TaskService; +use Symfony\Component\Console\Output\OutputInterface; +use Zend\Diactoros\Response; +use Zend\Diactoros\Stream; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Authenticatino middleware used in Command Line context + * @package Pydio\Core\Http\Cli + */ +class AuthCliMiddleware +{ + + /** + * @param $options + * @return \Pydio\Core\Model\UserInterface + * @throws AuthRequiredException + */ + protected static function authenticateFromCliParameters($options){ + + $optUser = $options["u"]; + $optPass = $options["p"]; + + if (!empty($options["p"])) { + + $optPass = $options["p"]; + + } else { + // Consider "u" is a crypted version of u:p + $optToken = $options["t"]; + $key = Crypto::buildKey($optToken, Crypto::getCliSecret()); + $optUser = Crypto::decrypt($optUser, $key); + $envPass = MemorySafe::loadPasswordStringFromEnvironment($optUser); + if($envPass !== false){ + unset($optToken); + $optPass = $envPass; + } + } + + + if (UsersService::usersEnabled() && !empty($optUser)) { + try{ + $loggedUser = AuthService::logUser($optUser, $optPass, isSet($optToken), false); + }catch (LoginException $l){ + throw new AuthRequiredException(); + } + + } else { + throw new AuthRequiredException(); + } + + return $loggedUser; + } + + /** + * @param ServerRequestInterface $requestInterface + * @param \Psr\Http\Message\ResponseInterface $responseInterface + * @param callable|null $next + * @return ResponseInterface + * @throws AuthRequiredException + */ + public static function handleRequest(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface, callable $next = null){ + + $driverImpl = ConfService::getAuthDriverImpl(); + PluginsService::getInstance(Context::emptyContext())->setPluginUniqueActiveForType("auth", $driverImpl->getName(), $driverImpl); + + /** @var OutputInterface $output */ + $output = $requestInterface->getAttribute("cli-output"); + $options = $requestInterface->getAttribute("cli-options"); + $optRepoId = $options["r"]; + + $impersonateUsers = false; + $loggedUser = self::authenticateFromCliParameters($options); + + $requestInterface = $requestInterface->withAttribute("action", $options["a"]); + + if(UsersService::usersEnabled() && ApplicationState::detectApplicationFirstRun()){ + + RolesService::bootSequence(); + + } + + // Output directly to STDOUT + $responseInterface = $responseInterface->withBody(new Stream(STDOUT, "w")); + + $applyCallback = function ($userId, $baseGroup, $index, $total) use ($optRepoId, $requestInterface, $output, $next){ + + $actionName = $requestInterface->getAttribute("action"); + $taskId = $requestInterface->getAttribute("pydio-task-id"); + $output->writeln("*****************************"); + $output->writeln("Current User is '".$userId."'"); + $output->writeln("*****************************"); + try{ + $user = UsersService::getUserById($userId); + + if(is_array($optRepoId)){ + $repos = []; + foreach($optRepoId as $repoId){ + $repoObj = UsersService::getRepositoryWithPermission($user, $repoId); + $repos[$repoObj->getId()] = $repoObj; + } + }else if($optRepoId === "*"){ + + $repos = UsersService::getRepositoriesForUser($user, false); // Do not include shared + // Exclude "ajxp_*" repositories and "inbox" + foreach($repos as $id=>$object){ + if($id === "inbox" || strpos($id, "ajxp_") === 0) unset($repos[$id]); + } + + }else{ + $repoObject = UsersService::getRepositoryWithPermission($user, $optRepoId); + $repos = [$repoObject->getId() => $repoObject]; + } + + foreach( $repos as $repoId => $repositoryInterface ){ + + try{ + $output->writeln("Applying action '$actionName' on workspace ".$repositoryInterface->getDisplay()." (".$repoId.")"); + $subResponse = new Response(); + $ctx = Context::contextWithObjects($user, $repositoryInterface); + $requestInterface = $requestInterface->withAttribute("ctx", $ctx); + Logger::updateContext($ctx); + TextEncoder::updateContext($ctx); + + $subResponse = Server::callNextMiddleWareAndRewind(function($middleware){ + return (is_array($middleware) && $middleware["0"] == "Pydio\\Core\\Http\\Cli\\AuthCliMiddleware" && $middleware[1] == "handleRequest"); + }, + $requestInterface, + $subResponse, + $next + ); + + $body = $subResponse->getBody(); + if($body instanceof SerializableResponseStream){ + if($requestInterface->getParsedBody()["format"] == "json"){ + $body->setSerializer(SerializableResponseStream::SERIALIZER_TYPE_JSON, ["pretty" => true]); + }else if($requestInterface->getParsedBody()["format"] == "xml"){ + $body->setSerializer(SerializableResponseStream::SERIALIZER_TYPE_XML, ["pretty" => true]); + }else if($body->supportsSerializer(SerializableResponseStream::SERIALIZER_TYPE_CLI)){ + $body->setSerializer(SerializableResponseStream::SERIALIZER_TYPE_CLI, ["output" => $output]); + } + } + $output->writeln( $body->getContents() ); + + }catch (\Exception $repoEx){ + + $output->writeln("$taskId: ".$repoEx->getMessage().""); + if(!empty($taskId)){ + TaskService::getInstance()->updateTaskStatus($taskId, Task::STATUS_FAILED, $repoEx->getMessage()); + } + + } + + } + + }catch (\Exception $userEx){ + + $output->writeln("USER: ".$userEx->getMessage().""); + if(!empty($taskId)){ + TaskService::getInstance()->updateTaskStatus($taskId, Task::STATUS_FAILED, $userEx->getMessage()); + } + + } + + }; + + + if(!empty($options["i"])){ + if(!is_array($options["i"]) && strpos($options["i"], "*") === false){ + $options["i"] = [$options["i"]]; + } + $impersonateUsers = $options["i"]; + } + + if ($impersonateUsers !== false && $loggedUser->isAdmin()) { + + if(is_array($impersonateUsers)){ + // Apply to a predefined list of users + foreach ($impersonateUsers as $index => $impersonateUser) { + $applyCallback($impersonateUser, "/", $index, count($impersonateUsers)); + } + + }else if($impersonateUsers === "*"){ + // Apply to all users of current group + UsersService::browseUsersGroupsWithCallback($loggedUser->getGroupPath(), $applyCallback, false); + + }else if($impersonateUsers === "**/*"){ + // Apply to all users recursively + UsersService::browseUsersGroupsWithCallback($loggedUser->getGroupPath(), $applyCallback, true); + + } + + }else{ + + $applyCallback($loggedUser->getId(), "/", 1, 1); + + } + + return $responseInterface; + + } + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Cli/CliMiddleware.php b/core/src/core/src/pydio/Core/Http/Cli/CliMiddleware.php new file mode 100644 index 0000000000..f1709d2126 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Cli/CliMiddleware.php @@ -0,0 +1,123 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Cli; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Core\Exception\AuthRequiredException; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Exception\WorkspaceNotFoundException; +use Pydio\Core\Http\Middleware\ITopLevelMiddleware; +use Pydio\Core\Http\Response\SerializableResponseStream; +use Pydio\Core\Http\Server; +use Pydio\Tasks\Task; +use Pydio\Tasks\TaskService; +use Symfony\Component\Console\Output\OutputInterface; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class CliMiddleware + * Dedicated Middleware for interacting with Symfony Command + * @package Pydio\Core\Http\Cli + */ +class CliMiddleware implements ITopLevelMiddleware +{ + /** + * @param ServerRequestInterface $requestInterface + * @param \Psr\Http\Message\ResponseInterface $responseInterface + * @return \Psr\Http\Message\ResponseInterface + * @param callable|null $next + * @throws WorkspaceNotFoundException + */ + public function handleRequest(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface, callable $next = null){ + + /** + * @var OutputInterface + */ + $output = $requestInterface->getAttribute("cli-output"); + $options = $requestInterface->getAttribute("cli-options"); + $statusFile = (!empty($options["s"]) ? $options["s"] : false); + $taskId = $requestInterface->getAttribute("pydio-task-id"); + + try { + + $responseInterface = Server::callNextMiddleWare($requestInterface, $responseInterface, $next); + + $this->emitResponse($requestInterface, $responseInterface); + + } catch (AuthRequiredException $e){ + + $output->writeln("Authentication Failed"); + if($statusFile !== false){ + file_put_contents($statusFile, "ERROR:Authentication Failed."); + } + if(!empty($taskId)){ + TaskService::getInstance()->updateTaskStatus($taskId, Task::STATUS_FAILED, "Authentication Failed"); + } + + } catch (PydioException $e){ + + $output->writeln("".$e->getMessage().""); + if($statusFile !== false){ + file_put_contents($statusFile, "ERROR:".$e->getMessage()); + } + if(!empty($taskId)){ + TaskService::getInstance()->updateTaskStatus($taskId, Task::STATUS_FAILED, $e->getMessage()); + } + + } catch (\Exception $e){ + + $output->writeln("".$e->getMessage().""); + if($statusFile !== false){ + file_put_contents($statusFile, "ERROR:".$e->getMessage()); + } + if(!empty($taskId)){ + TaskService::getInstance()->updateTaskStatus($taskId, Task::STATUS_FAILED, $e->getMessage()); + } + + } + + } + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @return void + */ + public function emitResponse(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface){ + + if($responseInterface !== false && $responseInterface->getBody() && $responseInterface->getBody() instanceof SerializableResponseStream){ + // For the moment, use XML by default + // Todo: Create A CLI Serializer for pretty printing? + if($requestInterface->getParsedBody()["format"] == "json"){ + $responseInterface->getBody()->setSerializer(SerializableResponseStream::SERIALIZER_TYPE_JSON); + } + } + $output = $requestInterface->getAttribute("cli-output"); + if(!empty($output)){ + $output->writeln("".$responseInterface->getBody()); + }else{ + echo "".$responseInterface->getBody(); + } + + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Cli/CliServer.php b/core/src/core/src/pydio/Core/Http/Cli/CliServer.php new file mode 100644 index 0000000000..b5101df55f --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Cli/CliServer.php @@ -0,0 +1,39 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ + +namespace Pydio\Core\Http\Cli; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class CliServer + * @package Pydio\Core\Http\Cli + */ +class CliServer extends \Pydio\Core\Http\Server +{ + protected function stackMiddleWares() + { + $this->middleWares->push(array("Pydio\\Core\\Controller\\Controller", "registryActionMiddleware")); + $this->middleWares->push(array("Pydio\\Core\\Http\\Cli\\AuthCliMiddleware", "handleRequest")); + $this->topMiddleware = new CliMiddleware(); + $this->middleWares->push(array($this->topMiddleware, "handleRequest")); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Cli/Command.php b/core/src/core/src/pydio/Core/Http/Cli/Command.php new file mode 100644 index 0000000000..2f4b2481b2 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Cli/Command.php @@ -0,0 +1,155 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Cli; + +defined('AJXP_EXEC') or die('Access not allowed'); +use Pydio\Core\PluginFramework\PluginsService; +use Symfony; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\Question; + +/** + * Class Command + * Pydio implementation of Symfony command + * @package Pydio\Core\Http\Cli + */ +class Command extends Symfony\Component\Console\Command\Command +{ + protected function configure() + { + $this->setDefinition(new FreeDefOptions()); + $this + ->setName('pydio') + ->setDescription('Pydio Command Line Tool. This tool can load any action defined in the framework. Should be used to run time-consuming task in background without hogging the webserver.') + ->addOption( + 'cli_username', + 'u', + InputOption::VALUE_REQUIRED, + '[Mandatory] User id or user token' + )->addOption( + 'cli_repository_id', + 'r', + InputOption::VALUE_REQUIRED, + '[Mandatory] Repository id or alias, can be a comma-separated list of identifier, or "*" for all repositories of the current user.' + )->addOption( + 'cli_action_name', + 'a', + InputOption::VALUE_REQUIRED, + '[Mandatory] Action name to apply. If not passed, command will interactively ask for the action, with autocompletion feature.' + )->addOption( + 'cli_password', + 'p', + InputOption::VALUE_OPTIONAL, + 'User Password. You can pass either a passowrd (p) or a token (t). If none of them passed, command will ask for password interactively.' + )->addOption( + 'cli_token', + 't', + InputOption::VALUE_OPTIONAL, + 'Encrypted Token used to replace password' + )->addOption( + 'cli_impersonate', + 'i', + InputOption::VALUE_OPTIONAL, + 'If authenticated user has administrative role, apply action under a different user name. Possible values are: a comma-separated list of users login, "*" means all users in the current group, "**/*" means all users from all groups recursively.' + )->addOption( + 'cli_status_file', + 's', + InputOption::VALUE_OPTIONAL, + 'Path to a file to write status information about the running task.' + )->addOption( + 'cli_task_uuid', + 'k', + InputOption::VALUE_OPTIONAL, + 'Task uuid: command will store status information in the corresponding task.' + ) + ; + } + + /** + * Execture the command + * @param InputInterface $input + * @param OutputInterface $output + * @return int|null|void + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $server = new CliServer("/"); + $server->registerCatchAll(); + + $definitionsKeys = array_keys($this->getDefinition()->getOptions()); + $actionParameters = []; + $pydioCliOptions = []; + foreach ($input->getOptions() as $key => $option){ + if(in_array($key, $definitionsKeys)){ + if(strpos($key, "cli_") === 0) { + $shortcut = $this->getDefinition()->getOption($key)->getShortcut(); + $pydioCliOptions[$shortcut] = FreeArgvOptions::removeEqualsSign($option); + } + }else{ + $actionParameters[$key] = $option; + } + } + + $helper = $this->getHelper("question"); + if(empty($pydioCliOptions["p"]) && empty($pydioCliOptions["t"])){ + // Ask password interactively + $question = new Question('Please enter the password: '); + $question->setHidden(true); + $question->setHiddenFallback(false); + $password = $helper->ask($input, $output, $question); + $pydioCliOptions["p"] = $password; + } + if(empty($pydioCliOptions["a"])){ + $actions = PluginsService::searchManifestsWithCache("//actions/action", function($resultNodes){ + $output = []; + /** @var \DOMElement[] $resultNodes */ + foreach($resultNodes as $node){ + $output[] = $node->getAttribute("name"); + } + return $output; + }); + $question = new Question('Please type in an action to apply: ', ''); + $question->setAutocompleterValues($actions); + $pydioCliOptions["a"] = $helper->ask($input, $output, $question); + } + + $taskUid = $pydioCliOptions["k"]; + $request = $server->getRequest(); + + $request = $request + ->withParsedBody($actionParameters) + ->withAttribute("api", "cli") + ->withAttribute("cli-options", $pydioCliOptions) + ->withAttribute("cli-output", $output) + ->withAttribute("cli-input", $input) + ->withAttribute("cli-command", $this); + + if(!empty($taskUid)){ + $request = $request->withAttribute("pydio-task-id", $taskUid); + } + + $server->updateRequest($request); + $server->listen(); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Cli/FreeArgvOptions.php b/core/src/core/src/pydio/Core/Http/Cli/FreeArgvOptions.php new file mode 100644 index 0000000000..2f731e592f --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Cli/FreeArgvOptions.php @@ -0,0 +1,129 @@ +freeParsed)){ + $options = parent::getOptions(); + foreach ($options as $key => $value) { + $options[$key] = self::removeEqualsSign($value); + $options[$key] = $this->splitByComma($value); + } + $this->freeParsed = $options; + } + return $this->freeParsed; + } + /** + * @param string $name + * @return mixed + */ + public function getOption($name) + { + $this->options = $this->getOptions(); + return parent::getOption($name); + } + /** + * @param array|string $value + * @return array|string + */ + public static function removeEqualsSign($value) + { + if (is_array($value)) { + array_walk($value, function (&$singleValue) { + $singleValue = ltrim($singleValue, '='); + }); + } else { + $value = ltrim($value, '='); + } + return $value; + } + /** + * @param mixed $value + * @return mixed + */ + private function splitByComma($value) + { + if (is_array($value) && count($value) === 1) { + array_walk($value, function (&$singleValue) { + if ($this->containsComma($singleValue)) { + $singleValue = explode(',', $singleValue); + } + }); + if (is_array($value[0])) { + return $value[0]; + } + } + if ($this->containsComma($value)) { + $value = explode(',', $value); + } + return $value; + } + /** + * @param string $value + * @return bool + */ + private function containsComma($value) + { + return strpos($value, ',') !== FALSE; + } +} diff --git a/core/src/core/src/pydio/Core/Http/Cli/FreeDefOptions.php b/core/src/core/src/pydio/Core/Http/Cli/FreeDefOptions.php new file mode 100644 index 0000000000..afb34b40c0 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Cli/FreeDefOptions.php @@ -0,0 +1,53 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Cli; + +defined('AJXP_EXEC') or die('Access not allowed'); + +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * Class FreeDefOptions + * @package Pydio\Core\Http\Cli + */ +class FreeDefOptions extends InputDefinition +{ + /** + * @param string $optionName + * @return bool + */ + public function hasOption($optionName){ + return true; + } + + /** + * @param string $optionName + * @return InputOption + */ + public function getOption($optionName){ + if(!parent::hasOption($optionName)){ + return new InputOption($optionName, null, InputOption::VALUE_OPTIONAL); + }else{ + return parent::getOption($optionName); + } + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Dav/AuthBackendBasic.php b/core/src/core/src/pydio/Core/Http/Dav/AuthBackendBasic.php new file mode 100644 index 0000000000..2bf765f7b2 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Dav/AuthBackendBasic.php @@ -0,0 +1,204 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ + +namespace Pydio\Core\Http\Dav; + +use Pydio\Auth\Core\MemorySafe; +use Pydio\Core\Exception\LoginException; +use Pydio\Core\Exception\RepositoryLoadException; +use Pydio\Core\Exception\UserNotFoundException; +use Pydio\Core\Exception\WorkspaceForbiddenException; +use Pydio\Core\Exception\WorkspaceNotFoundException; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Model\UserInterface; + +use Pydio\Core\Services\AuthService; +use Pydio\Core\Services\ConfService; + +use Pydio\Core\Services\UsersService; +use Pydio\Core\Utils\TextEncoder; +use Pydio\Log\Core\Logger; +use \Sabre; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + + +/** + * Class AuthBackendBasic + * @package Pydio\Core\Http\Dav + */ +class AuthBackendBasic extends Sabre\DAV\Auth\Backend\AbstractBasic +{ + /** + * @var ContextInterface + */ + private $context; + + /** + * Utilitary method to detect basic header. + * @return bool + */ + public static function detectBasicHeader() + { + if(isSet($_SERVER["PHP_AUTH_USER"])) return true; + if(isSet($_SERVER["HTTP_AUTHORIZATION"])) $value = $_SERVER["HTTP_AUTHORIZATION"]; + if(!isSet($value) && isSet($_SERVER["REDIRECT_HTTP_AUTHORIZATION"])) $value = $_SERVER["REDIRECT_HTTP_AUTHORIZATION"]; + if(!isSet($value)) return false; + return (strpos(strtolower($value),'basic') ===0) ; + } + + /** + * AuthBackendBasic constructor. + * @param ContextInterface $ctx + */ + public function __construct(ContextInterface $ctx) + { + $this->context = $ctx; + } + + + /** + * @param string $username + * @param string $password + * @return bool|void + */ + protected function validateUserPass($username, $password) + { + return UsersService::checkPassword($username, $password, false); + } + + /** + * @param Sabre\DAV\Server $server + * @param string $realm + * @return bool + * @throws Sabre\DAV\Exception\NotAuthenticated + */ + public function authenticate(Sabre\DAV\Server $server, $realm) + { + $auth = new Sabre\HTTP\BasicAuth(); + $auth->setHTTPRequest($server->httpRequest); + $auth->setHTTPResponse($server->httpResponse); + $auth->setRealm($realm); + $userpass = $auth->getUserPass(); + if (!$userpass) { + $auth->requireLogin(); + throw new Sabre\DAV\Exception\NotAuthenticated('No basic authentication headers were found'); + } + + // Authenticates the user + //AJXP_Logger::info(__CLASS__,"authenticate",$userpass[0]); + + try{ + $userObject = UsersService::getUserById($userpass[0]); + }catch (UserNotFoundException $e){ + throw new Sabre\DAV\Exception\NotAuthenticated(); + } + + $webdavData = $userObject->getPref("AJXP_WEBDAV_DATA"); + $active = ConfService::getGlobalConf("WEBDAV_ACTIVE_ALL"); + if(!empty($webdavData) && isSet($webdavData["ACTIVE"]) && $webdavData["ACTIVE"] === false){ + $active = false; + } + if (!$active) { + Logger::warning(__CLASS__, "Login failed", array("user" => $userpass[0], "error" => "WebDAV user not found or disabled")); + throw new Sabre\DAV\Exception\NotAuthenticated(); + } + // check if there are cached credentials. prevents excessive authentication calls to external + // auth mechanism. + $cachedPasswordValid = 0; + $secret = (defined("AJXP_SECRET_KEY")? AJXP_SECRET_KEY:"\1CDAFx¨op#"); + $encryptedPass = md5($userpass[1].$secret.date('YmdHi')); + if (isSet($webdavData["TMP_PASS"]) && ($encryptedPass == $webdavData["TMP_PASS"])) { + $cachedPasswordValid = true; + //AJXP_Logger::debug("Using Cached Password"); + } + + if (!$cachedPasswordValid && (!$this->validateUserPass($userpass[0],$userpass[1]))) { + Logger::warning(__CLASS__, "Login failed", array("user" => $userpass[0], "error" => "Invalid WebDAV user or password")); + $auth->requireLogin(); + throw new Sabre\DAV\Exception\NotAuthenticated('Username or password does not match'); + } + $this->currentUser = $userpass[0]; + + try{ + $loggedUser = AuthService::logUser($this->currentUser, $userpass[1], true); + }catch (LoginException $l){ + throw new Sabre\DAV\Exception\NotAuthenticated(); + } + $this->updateCurrentUserRights($loggedUser); + if($this->context->hasRepository()){ + $repoId = $this->context->getRepositoryId(); + try{ + $repoObject = UsersService::getRepositoryWithPermission($loggedUser, $repoId); + }catch (WorkspaceForbiddenException $e){ + throw new Sabre\DAV\Exception\NotAuthenticated('You are not allowed to access this workspace'); + }catch (WorkspaceNotFoundException $e){ + throw new Sabre\DAV\Exception\NotAuthenticated('Could not find workspace!'); + }catch (RepositoryLoadException $e){ + throw new Sabre\DAV\Exception\NotAuthenticated('Error while loading workspace'); + }catch (\Exception $e){ + throw new Sabre\DAV\Exception\NotAuthenticated('Error while loading workspace'); + } + if($repoObject->getContextOption($this->context, "AJXP_WEBDAV_DISABLED", false)){ + throw new Sabre\DAV\Exception\NotAuthenticated('WebDAV access is disabled for this workspace'); + } + $this->context->setRepositoryObject($repoObject); + } + + // NOW UPDATE CONTEXT + $this->context->setUserId($this->currentUser); + if (ConfService::getContextConf($this->context, "SESSION_SET_CREDENTIALS", "auth")) { + MemorySafe::storeCredentials($this->currentUser, $userpass[1]); + } + //PluginsService::getInstance($this->context); + Logger::updateContext($this->context); + TextEncoder::updateContext($this->context); + + // the method used here will invalidate the cached password every minute on the minute + if (!$cachedPasswordValid) { + $webdavData["TMP_PASS"] = $encryptedPass; + $userObject->setPref("AJXP_WEBDAV_DATA", $webdavData); + $userObject->save("user"); + AuthService::updateSessionUser($userObject); + } + + return true; + } + + + /** + * @param UserInterface $user + * @return bool + * @throws Sabre\DAV\Exception\NotAuthenticated + */ + protected function updateCurrentUserRights($user) + { + if (!$this->context->hasRepository()) { + return true; + } + if (!$user->canSwitchTo($this->context->getRepositoryId())) { + throw new Sabre\DAV\Exception\NotAuthenticated(); + } + return true; + } + + +} diff --git a/core/src/core/src/pydio/Core/Http/Dav/AuthBackendDigest.php b/core/src/core/src/pydio/Core/Http/Dav/AuthBackendDigest.php new file mode 100644 index 0000000000..182e6504bc --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Dav/AuthBackendDigest.php @@ -0,0 +1,191 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Dav; + +use Pydio\Core\Exception\LoginException; +use Pydio\Core\Exception\RepositoryLoadException; +use Pydio\Core\Exception\UserNotFoundException; +use Pydio\Core\Exception\WorkspaceForbiddenException; +use Pydio\Core\Exception\WorkspaceNotFoundException; + + +use Pydio\Core\Services\UsersService; +use Pydio\Core\Utils\Crypto; +use Pydio\Core\Utils\TextEncoder; +use \Sabre; +use Pydio\Auth\Core\MemorySafe; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\AuthService; +use Pydio\Core\Services\ConfService; +use Pydio\Log\Core\Logger; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * @package Pydio + * @subpackage SabreDav + */ +class AuthBackendDigest extends Sabre\DAV\Auth\Backend\AbstractDigest +{ + private $secretKey; + private $repositoryId; + /** + * @var ContextInterface + */ + private $context; + + /** + * AuthBackendDigest constructor. + * @param $context + */ + public function __construct($context) + { + $this->context = $context; + $this->secretKey = Crypto::getApplicationSecret(); + } + + /** + * @param string $realm + * @param string $username + * @return bool|string + * @throws Sabre\DAV\Exception\NotAuthenticated + */ + public function getDigestHash($realm, $username) + { + try{ + $user = UsersService::getUserById($username); + }catch (UserNotFoundException $e){ + throw new Sabre\DAV\Exception\NotAuthenticated(); + } + $webdavData = $user->getPref("AJXP_WEBDAV_DATA"); + $active = ConfService::getGlobalConf("WEBDAV_ACTIVE_ALL"); + if(!empty($webdavData) && isSet($webdavData["ACTIVE"]) && $webdavData["ACTIVE"] === false){ + $active = false; + } + if (!$active || (!isSet($webdavData["PASS"]) && !isset($webdavData["HA1"]) ) ) { + return false; + } + if (isSet($webdavData["HA1"])) { + return $webdavData["HA1"]; + } else { + $pass = $this->_decodePassword($webdavData["PASS"], $username); + return md5("{$username}:{$realm}:{$pass}"); + } + + } + + /** + * @param Sabre\DAV\Server $server + * @param string $realm + * @return bool + * @throws Sabre\DAV\Exception\NotAuthenticated + */ + public function authenticate(Sabre\DAV\Server $server, $realm) + { + //AJXP_Logger::debug("Try authentication on $realm", $server); + $errmsg = ""; + try { + + $success = parent::authenticate($server, $realm); + + } catch(\Exception $e) { + $success = 0; + $errmsg = $e->getMessage(); + if ($errmsg != "No digest authentication headers were found") + $success = false; + } + + if ($success) { + + try{ + $loggedUser = AuthService::logUser($this->currentUser, null, true); + }catch (LoginException $l){ + throw new Sabre\DAV\Exception\NotAuthenticated(); + } + $this->updateCurrentUserRights($loggedUser); + } else { + if ($success === false) { + Logger::warning(__CLASS__, "Login failed", array("user" => $this->currentUser, "error" => "Invalid WebDAV user or password")); + } + throw new Sabre\DAV\Exception\NotAuthenticated($errmsg); + } + + if($this->context->hasRepository()){ + $repoId = $this->context->getRepositoryId(); + try{ + $repoObject = UsersService::getRepositoryWithPermission($loggedUser, $repoId); + }catch (WorkspaceForbiddenException $e){ + throw new Sabre\DAV\Exception\NotAuthenticated('You are not allowed to access this workspace'); + }catch (WorkspaceNotFoundException $e){ + throw new Sabre\DAV\Exception\NotAuthenticated('Could not find workspace!'); + }catch (RepositoryLoadException $e){ + throw new Sabre\DAV\Exception\NotAuthenticated('Error while loading workspace'); + }catch (\Exception $e){ + throw new Sabre\DAV\Exception\NotAuthenticated('Error while loading workspace'); + } + if($repoObject->getContextOption($this->context, "AJXP_WEBDAV_DISABLED", false)){ + throw new Sabre\DAV\Exception\NotAuthenticated('WebDAV access is disabled for this workspace'); + } + $this->context->setRepositoryObject($repoObject); + } + if (ConfService::getContextConf($this->context, "SESSION_SET_CREDENTIALS", "auth")) { + $webdavData = $loggedUser->getPref("AJXP_WEBDAV_DATA"); + MemorySafe::storeCredentials($this->currentUser, $this->_decodePassword($webdavData["PASS"], $this->currentUser)); + } + + // NOW UPDATE CONTEXT + $this->context->setUserObject($loggedUser); + Logger::updateContext($this->context); + TextEncoder::updateContext($this->context); + + return true; + } + + /** + * @param \Pydio\Core\Model\UserInterface $user + * @return bool + * @throws \Sabre\DAV\Exception\NotAuthenticated + */ + protected function updateCurrentUserRights($user) + { + if ($this->repositoryId == null) { + return true; + } + if (!$user->canSwitchTo($this->repositoryId)) { + throw new Sabre\DAV\Exception\NotAuthenticated(); + } + return true; + } + + /** + * @param $encoded + * @param $user + * @return string + */ + private function _decodePassword($encoded, $user) + { + $key = Crypto::buildKey($user, Crypto::getApplicationSecret(), $encoded); + return Crypto::decrypt($encoded, $key); + } + + + +} diff --git a/core/src/core/src/pydio/Core/Http/Dav/BrowserPlugin.php b/core/src/core/src/pydio/Core/Http/Dav/BrowserPlugin.php new file mode 100644 index 0000000000..7b006c4eee --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Dav/BrowserPlugin.php @@ -0,0 +1,84 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Dav; + +use \Sabre; +use Pydio\Core\Services\ConfService; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * @package Pydio + * @subpackage SabreDav + */ +class BrowserPlugin extends Sabre\DAV\Browser\Plugin +{ + + protected $repositoryLabel; + + /** + * BrowserPlugin constructor. + * @param null $currentRepositoryLabel + */ + public function __construct($currentRepositoryLabel = null) + { + parent::__construct(false, true); + $this->repositoryLabel = $currentRepositoryLabel; + } + + /** + * @param string $path + * @return mixed|string + */ + public function generateDirectoryIndex($path) + { + $html = parent::generateDirectoryIndex($path); + $html = str_replace("image/vnd.microsoft.icon", "image/png", $html); + + $title = ConfService::getGlobalConf("APPLICATION_TITLE"); + $html = preg_replace("/(.*)<\/title>/i", '<title>'.$title.'', $html); + + $repoString = "

    "; + if (!empty($this->repositoryLabel)) { + $repoString = " - ".$this->repositoryLabel."

    Index of ".$this->escapeHTML($path)."/

    "; + } + $html = preg_replace("/

    (.*)<\/h1>/i", "

    ".$title.$repoString, $html); + + $html = str_replace("h1 { font-size: 150% }", "h1 { font-size: 150% } \n h2 { font-size: 115% }", $html); + + return $html; + + } + + /** + * @param string $name + * @return string + * @throws Sabre\DAV\Exception\Forbidden + */ + public function getLocalAssetPath($name) + { + if ($name != "favicon.ico") { + return parent::getLocalAssetPath($name); + } + return AJXP_INSTALL_PATH."/plugins/gui.ajax/res/themes/orbit/images/html-folder.png"; + } + +} diff --git a/core/src/core/src/pydio/Core/Http/Dav/Collection.php b/core/src/core/src/pydio/Core/Http/Dav/Collection.php new file mode 100644 index 0000000000..6a742b430d --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Dav/Collection.php @@ -0,0 +1,214 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Dav; + +use \Sabre; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Core\Controller\Controller; +use Pydio\Log\Core\Logger; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * @package Pydio + * @subpackage SabreDav + */ +class Collection extends Node implements Sabre\DAV\ICollection +{ + + protected $children; + + /** + * Creates a new file in the directory + * + * Data will either be supplied as a stream resource, or in certain cases + * as a string. Keep in mind that you may have to support either. + * + * After succesful creation of the file, you may choose to return the ETag + * of the new file here. + * + * The returned ETag must be surrounded by double-quotes (The quotes should + * be part of the actual string). + * + * If you cannot accurately determine the ETag, you should not return it. + * If you don't store the file exactly as-is (you're transforming it + * somehow) you should also not return an ETag. + * + * This means that if a subsequent GET to this new file does not exactly + * return the same contents of what was submitted here, you are strongly + * recommended to omit the ETag. + * + * @param string $name Name of the file + * @param resource|string $data Initial payload + * @return null|string + */ + public function createFile($name, $data = null) + { + try { + $name = ltrim($name, "/"); + Logger::debug("CREATE FILE $name"); + + $request = new \Zend\Diactoros\ServerRequest(); + $request = $request + ->withParsedBody([ + "dir" => $this->path, + "filename" => $name + ]) + ->withAttribute("action", "mkfile") + ->withAttribute("api", "session") + ->withAttribute("ctx", $this->context) + ; + Controller::run($request); + + if ( $data != null && is_file($this->getUrl()."/".$name)) { + + $p = $this->path."/".$name; + $newNode = new AJXP_Node($this->getUrl().$p); + Controller::applyHook("node.before_change", array($newNode, $_SERVER["CONTENT_LENGTH"])); + //AJXP_Logger::debug("Should now copy stream or string in ".$this->getUrl()."/".$name); + if (is_resource($data)) { + $stream = fopen($this->getUrl()."/".$name, "w"); + stream_copy_to_stream($data, $stream); + fclose($stream); + } else if (is_string($data)) { + file_put_contents($data, $this->getUrl()."/".$name); + } + + $toto = null; + Controller::applyHook("node.change", array(null, $newNode, false)); + + } + $node = new NodeLeaf($this->path."/".$name, $this->context); + if (isSet($this->children)) { + $this->children = null; + } + return $node->getETag(); + + } catch (\Exception $e) { + Logger::debug("Error ".$e->getMessage(), $e->getTraceAsString()); + return null; + } + + + } + + /** + * Creates a new subdirectory + * + * @param string $name + * @return void + */ + public function createDirectory($name) + { + if (isSet($this->children)) { + $this->children = null; + } + $request = new \Zend\Diactoros\ServerRequest(); + $request = $request + ->withParsedBody([ + "dir" => $this->path, + "dirname" => $name + ]) + ->withAttribute("action", "mkdir") + ->withAttribute("api", "session") + ->withAttribute("ctx", $this->context) + ; + Controller::run($request); + + } + + /** + * Returns a specific child node, referenced by its name + * + * @param string $name + * @throws Sabre\DAV\Exception\NotFound + * @return Sabre\DAV\INode + */ + public function getChild($name) + { + foreach ($this->getChildren() as $child) { + + if ($child->getName()==$name) return $child; + + } + throw new Sabre\DAV\Exception\NotFound('File not found: ' . $name); + + } + + /** + * Returns an array with all the child nodes + * + * @return Sabre\DAV\INode[] + */ + public function getChildren() + { + if (isSet($this->children)) { + return $this->children; + } + + + $contents = array(); + + $nodes = scandir($this->getUrl()); + + foreach ($nodes as $file) { + if ($file == "." || $file == "..") { + continue; + } + // This function will perform the is_dir() call and update $isLeaf variable. + $isLeaf = ""; + if (!$this->getAccessDriver()->filterNodeName($this->context, $this->getUrl(), $file, $isLeaf, array("d"=>true, "f"=>true, "z"=>true))){ + continue; + } + if ( !$isLeaf ) { + // Add collection without any children + $contents[] = new Collection($this->path."/".$file, $this->context); + } else { + // Add files without content + $contents[] = new NodeLeaf($this->path."/".$file, $this->context); + } + } + $this->children = $contents; + + $ajxpNode = new AJXP_Node($this->getUrl()); + Controller::applyHook("node.read", array(&$ajxpNode)); + + return $contents; + + } + + /** + * Checks if a child-node with the specified name exists + * + * @param string $name + * @return bool + */ + public function childExists($name) + { + foreach ($this->getChildren() as $child) { + + if ($child->getName()==$name) return true; + + } + return false; + + } +} diff --git a/core/src/core/src/pydio/Core/Http/Dav/DAVServer.php b/core/src/core/src/pydio/Core/Http/Dav/DAVServer.php new file mode 100644 index 0000000000..e586447273 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Dav/DAVServer.php @@ -0,0 +1,134 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Dav; + +use Pydio\Core\Model\Context; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\RepositoryService; +use Pydio\Log\Core\Logger; +use Sabre\DAV as DAV; +use Sabre\DAV\Exception\Forbidden; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class DAVServer + * @package Pydio\Core\Http\Dav + */ +class DAVServer +{ + /** + * @var ContextInterface + */ + private static $context; + + + /** + * @param $baseURI + * @param $davRoute + * @throws Forbidden + */ + public static function handleRoute($baseURI, $davRoute){ + + ConfService::init(); + ConfService::start(); + self::$context = Context::emptyContext(); + + if (!ConfService::getGlobalConf("WEBDAV_ENABLE")) { + throw new Forbidden('You are not allowed to access this service'); + } + + PluginsService::getInstance(self::$context)->initActivePlugins(); + + $baseURI = $baseURI . $davRoute; + $requestUri = $_SERVER["REQUEST_URI"]; + if (substr($requestUri, 0, strlen($baseURI)) != $baseURI) + { + $baseURI = substr($requestUri, 0, stripos($requestUri, $baseURI)) . $baseURI; + } + $end = trim(substr($requestUri, strlen($baseURI."/"))); + $rId = null; + if ((!empty($end) || $end ==="0") && $end[0] != "?") { + + $parts = explode("/", $end); + $pathBase = $parts[0]; + $repositoryId = $pathBase; + + $repository = RepositoryService::getRepositoryById($repositoryId); + if ($repository == null) { + $repository = RepositoryService::getRepositoryByAlias($repositoryId); + if ($repository != null) { + $repositoryId = $repository->getId(); + } + } + if ($repository == null) { + throw new Forbidden('You are not allowed to access this service'); + } + + self::$context->setRepositoryId($repositoryId); + $rootDir = new Collection("/", self::$context); + $server = new DAV\Server($rootDir); + $server->setBaseUri($baseURI."/".$pathBase); + + + } else { + + $rootDir = new RootCollection("root"); + $rootDir->setContext(self::$context); + $server = new DAV\Server($rootDir); + $server->setBaseUri($baseURI); + + } + + if((AuthBackendBasic::detectBasicHeader() || ConfService::getGlobalConf("WEBDAV_FORCE_BASIC"))){ + $authBackend = new AuthBackendBasic(self::$context); + } else { + $authBackend = new AuthBackendDigest(self::$context); + } + $authPlugin = new DAV\Auth\Plugin($authBackend, ConfService::getGlobalConf("WEBDAV_DIGESTREALM")); + $server->addPlugin($authPlugin); + + if (!is_dir(AJXP_DATA_PATH."/plugins/server.sabredav")) { + mkdir(AJXP_DATA_PATH."/plugins/server.sabredav", 0755); + $fp = fopen(AJXP_DATA_PATH."/plugins/server.sabredav/locks", "w"); + fwrite($fp, ""); + fclose($fp); + } + + $lockBackend = new DAV\Locks\Backend\File(AJXP_DATA_PATH."/plugins/server.sabredav/locks"); + $lockPlugin = new DAV\Locks\Plugin($lockBackend); + $server->addPlugin($lockPlugin); + + if (ConfService::getGlobalConf("WEBDAV_BROWSER_LISTING")) { + $browerPlugin = new BrowserPlugin((isSet($repository)?$repository->getDisplay():null)); + $extPlugin = new DAV\Browser\GuessContentType(); + $server->addPlugin($browerPlugin); + $server->addPlugin($extPlugin); + } + try { + $server->exec(); + } catch ( \Exception $e ) { + Logger::error(__CLASS__,"Exception",$e->getMessage()); + } + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Dav/Node.php b/core/src/core/src/pydio/Core/Http/Dav/Node.php new file mode 100644 index 0000000000..65ce3b4a53 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Dav/Node.php @@ -0,0 +1,281 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Dav; + +use \Sabre; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\IAjxpWrapperProvider; +use Pydio\Access\Core\Model\Repository; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Controller\Controller; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Access\Metastore\Core\IMetaStoreProvider; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * @package Pydio + * @subpackage SabreDav + */ +class Node implements Sabre\DAV\INode, Sabre\DAV\IProperties +{ + + /** @var ContextInterface */ + protected $context; + + + /** + * @var IAjxpWrapperProvider + */ + protected $accessDriver; + + /** + * @var String + */ + protected $url; + protected $path; + + /** + * Node constructor. + * @param $path + * @param ContextInterface $context + */ + public function __construct($path, $context) + { + $this->context = $context; + $this->path = $path; + } + + /** + * @return ContextInterface + */ + public function getContext(){ + return $this->context; + } + + /** + * @param Repository $repository + */ + public function updateRepository($repository){ + $this->repository = $repository; + } + + /** + * @return IAjxpWrapperProvider + * @throws \Sabre\DAV\Exception\NotFound + */ + public function getAccessDriver() + { + $driver = $this->context->getRepository()->getDriverInstance($this->context); + if(empty($driver)){ + $n = new AJXP_Node($this->getUrl()); + return $n->getDriver(); + }else{ + return $driver; + } + } + + /** + * @return String + */ + public function getUrl() + { + if (!isSet($this->url)) { + $this->url = $this->context->getUrlBase().$this->path; + } + return $this->url; + } + + /** + * Deleted the current node + * + * @return void + */ + public function delete() + { + ob_start(); + try { + $request = new \Zend\Diactoros\ServerRequest(); + $request = $request + ->withParsedBody([ + "dir" => dirname($this->path), + "file_0" => $this->path + ]) + ->withAttribute("action", "delete") + ->withAttribute("api", "session") + ->withAttribute("ctx", $this->context); + Controller::run($request); + } catch (\Exception $e) { + + } + ob_get_flush(); + $this->putResourceData(array()); + + } + + /** + * Returns the name of the node. + * + * This is used to generate the url. + * + * @return string + */ + public function getName() + { + return basename($this->getUrl()); + } + + /** + * Renames the node + * + * @param string $name The new name + * @return void + */ + public function setName($name) + { + $data = $this->getResourceData(); + ob_start(); + $request = new \Zend\Diactoros\ServerRequest(); + $request = $request + ->withParsedBody([ + "filename_new" => $name, + "dir" => dirname($this->path), + "file" => $this->path + ]) + ->withAttribute("action", "rename") + ->withAttribute("api", "session") + ->withAttribute("ctx", $this->context) + ; + Controller::run($request); + ob_get_flush(); + $this->putResourceData(array()); + $this->putResourceData($data, dirname($this->getUrl())."/".$name); + } + + /** + * Returns the last modification time, as a unix timestamp + * + * @return int + */ + public function getLastModified() + { + return filemtime($this->getUrl()); + } + + + /** + * Updates properties on this node, + * + * @param array $properties + * @see Sabre\DAV\IProperties::updateProperties + * @return bool|array + */ + public function updateProperties($properties) + { + $resourceData = $this->getResourceData(); + + foreach ($properties as $propertyName=>$propertyValue) { + + // If it was null, we need to delete the property + if (is_null($propertyValue)) { + if (isset($resourceData['properties'][$propertyName])) { + unset($resourceData['properties'][$propertyName]); + } + } else { + $resourceData['properties'][$propertyName] = $propertyValue; + } + + } + + //AJXP_Logger::debug("Saving Data", $resourceData); + $this->putResourceData($resourceData); + return true; + } + + /** + * Returns a list of properties for this nodes.; + * + * The properties list is a list of propertynames the client requested, encoded as xmlnamespace#tagName, for example: http://www.example.org/namespace#author + * If the array is empty, all properties should be returned + * + * @param array $properties + * @return array + */ + public function getProperties($properties) + { + $resourceData = $this->getResourceData(); + + // if the array was empty, we need to return everything + if (!$properties) return $resourceData['properties']; + + $props = array(); + foreach ($properties as $property) { + if (isset($resourceData['properties'][$property])) $props[$property] = $resourceData['properties'][$property]; + } + + return $props; + + } + + /** + * Metadata manager + * @param $array + * @param null $newURL + */ + protected function putResourceData($array, $newURL = null) + { + $metaStore = $this->getMetastore(); + if ($metaStore != false) { + $metaStore->setMetadata(new AJXP_Node(($newURL!=null?$newURL:$this->getUrl())), "SABRE_DAV", $array, false, AJXP_METADATA_SCOPE_GLOBAL); + } + + } + + /** + * Metadata manager + * @return array + */ + protected function getResourceData() + { + $metaStore = $this->getMetastore(); + $data = array(); + if ($metaStore != false) { + $data = $metaStore->retrieveMetadata(new AJXP_Node($this->getUrl()), "SABRE_DAV", false, AJXP_METADATA_SCOPE_GLOBAL); + } + if (!isset($data['properties'])) $data['properties'] = array(); + return $data; + } + + + /** + * @return IMetaStoreProvider|bool + */ + protected function getMetastore() + { + /** @var IMetaStoreProvider $metaStore */ + $metaStore = PluginsService::getInstance($this->context)->getUniqueActivePluginForType("metastore"); + if($metaStore === false) return false; + $metaStore->initMeta($this->context, $this->getAccessDriver()); + return $metaStore; + } + + +} diff --git a/core/src/core/src/pydio/Core/Http/Dav/NodeLeaf.php b/core/src/core/src/pydio/Core/Http/Dav/NodeLeaf.php new file mode 100644 index 0000000000..8cc4fab084 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Dav/NodeLeaf.php @@ -0,0 +1,182 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Dav; + +use \Sabre; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Core\Controller\Controller; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * @package AjaXplorer + * @subpackage SabreDav + */ +class NodeLeaf extends Node implements Sabre\DAV\IFile +{ + + /** + * Updates the data + * + * The data argument is a readable stream resource. + * + * After a succesful put operation, you may choose to return an ETag. The + * etag must always be surrounded by double-quotes. These quotes must + * appear in the actual string you're returning. + * + * Clients may use the ETag from a PUT request to later on make sure that + * when they update the file, the contents haven't changed in the mean + * time. + * + * If you don't plan to store the file byte-by-byte, and you return a + * different object on a subsequent GET you are strongly recommended to not + * return an ETag, and just return null. + * + * @param resource $data + * @return null|string + * @throws \Sabre\DAV\Exception\Forbidden + */ + public function put($data) + { + // Warning, passed by ref + $p = $this->path; + + if (!$this->context->getUser()->canWrite($this->context->getRepositoryId())) { + throw new Sabre\DAV\Exception\Forbidden() ; + } + $newNode = new AJXP_Node($this->getUrl().$p); + Controller::applyHook("node.before_change", array($newNode, $_SERVER["CONTENT_LENGTH"])); + + $stream = fopen($this->getUrl(), "w"); + stream_copy_to_stream($data, $stream); + fclose($stream); + + $toto = null; + Controller::applyHook("node.change", array(null, $newNode, false)); + + return $this->getETag(); + } + + /** + * Returns the data + * + * This method may either return a string or a readable stream resource + * + * @return mixed + */ + public function get() + { + $ajxpNode = new AJXP_Node($this->getUrl()); + Controller::applyHook("node.read", array(&$ajxpNode)); + return fopen($this->getUrl(), "r"); + } + + /** + * Returns the mime-type for a file + * + * If null is returned, we'll assume application/octet-stream + * + * @return void|string + */ + public function getContentType() + { + //Get mimetype with fileinfo PECL extension + $fp = fopen($this->getUrl(), "r"); + $fileMime = null; + if (class_exists("finfo")) { + $finfo = new \finfo(FILEINFO_MIME); + $fileMime = $finfo->buffer(fread($fp, 100)); + } elseif (function_exists("mime_content_type")) { + $fileMime = @mime_content_type($fp); + } else { + $fileExt = substr(strrchr(basename($this->getUrl()), '.'), 1); + if(empty($fileExt)) + $fileMime = "application/octet-stream"; + else { + $regex = "/^([\w\+\-\.\/]+)\s+(\w+\s)*($fileExt\s)/i"; + $lines = file( AJXP_CONF_PATH ."/mime.types"); + foreach ($lines as $line) { + if(substr($line, 0, 1) == '#') + continue; // skip comments + $line = rtrim($line) . " "; + if(!preg_match($regex, $line, $matches)) + continue; // no match to the extension + $fileMime = $matches[1]; + } + } + } + fclose($fp); + return $fileMime; + /* + if ( $this->options->useMimeExts && ezcBaseFeatures::hasExtensionSupport( 'fileinfo' ) ) { + $fInfo = new fInfo( FILEINFO_MIME ); + $mimeType = $fInfo->file( $this->getUrl() ); + + // The documentation tells to do this, but it does not work with a + // current version of pecl/fileinfo + // $fInfo->close(); + + return $mimeType; + } + + // Check if extension ext/mime-magic is usable. + if ( $this->options->useMimeExts && + ezcBaseFeatures::hasExtensionSupport( 'mime_magic' ) && + ( $mimeType = mime_content_type( $this->getUrl() ) ) !== false ) + { + return $mimeType; + } + */ + } + + /** + * Returns the ETag for a file + * + * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. + * + * Return null if the ETag can not effectively be determined + * + * @return String|null + */ + public function getETag() + { + clearstatcache(); + return '"'.md5( + $this->path + . $this->getSize() + . date( 'c', $this->getLastModified() ) + ).'"'; + } + + /** + * Returns the size of the node, in bytes + * + * @return int + */ + public function getSize() + { + return filesize($this->getUrl()); + } + + + + +} diff --git a/core/src/core/src/pydio/Core/Http/Dav/RootCollection.php b/core/src/core/src/pydio/Core/Http/Dav/RootCollection.php new file mode 100644 index 0000000000..62fe411007 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Dav/RootCollection.php @@ -0,0 +1,96 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Dav; + +use Pydio\Core\Model\FilteredRepositoriesList; +use \Sabre; +use Pydio\Core\Model\ContextInterface; + +use Pydio\Core\PluginFramework\PluginsService; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Class RootCollection + * @package Pydio\Core\Http\Dav + */ +class RootCollection extends Sabre\DAV\SimpleCollection +{ + /** @var ContextInterface */ + protected $context; + + /** + * @return ContextInterface + */ + public function getContext(){ + return $this->context; + } + + /** + * @return ContextInterface + */ + public function setContext($context){ + return $this->context = $context; + } + + /** + * @return Node[] + */ + public function getChildren() + { + $this->children = array(); + if($this->context == null || !$this->context->hasUser()){ + return $this->children; + } + $filteredList = new FilteredRepositoriesList($this->context->getUser()); + $repos = $filteredList->load(); + // Refilter to make sure the driver is an AjxpWebdavProvider + foreach ($repos as $repository) { + $accessType = $repository->getAccessType(); + $driver = PluginsService::getInstance($this->context)->getPluginByTypeName("access", $accessType); + if ($driver instanceof \Pydio\Access\Core\IAjxpWrapperProvider && $repository->getContextOption($this->context, "AJXP_WEBDAV_DISABLED") !== true) { + $this->children[$repository->getSlug()] = new Sabre\DAV\SimpleCollection($repository->getSlug()); + } + } + return $this->children; + } + + /** + * @param string $name + * @return bool + */ + public function childExists($name) + { + $c = $this->getChildren(); + return array_key_exists($name, $c); + } + + /** + * @param string $name + * @return Node + */ + public function getChild($name) + { + $c = $this->getChildren(); + return $c[$name]; + } + +} diff --git a/core/src/core/src/pydio/Core/Http/Message/ExternalUploadedFile.php b/core/src/core/src/pydio/Core/Http/Message/ExternalUploadedFile.php new file mode 100644 index 0000000000..1d80f667a3 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Message/ExternalUploadedFile.php @@ -0,0 +1,73 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Message; + +use Zend\Diactoros\UploadedFile; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class ExternalUploadedFile + * @package Pydio\Core\Http\Message + */ +class ExternalUploadedFile extends UploadedFile +{ + const STATUS_REQUEST_OPTIONS = "request-options"; + const STATUS_UPLOAD_FINISHED = "upload-finished"; + const STATUS_UPLOAD_ERROR = "upload-error"; + + private $status; + + /** + * ExternalUploadedFile constructor. + * @param string $status self::STATUS_REQUEST_OPTIONS, UPLOAD_FINISHED, UPLOAD_ERROR + * @param int $size + * @param int $clientFilename + */ + public function __construct($status, $size, $clientFilename) + { + parent::__construct( + "fake-tmp-file", + $size, + $status === self::STATUS_UPLOAD_ERROR ? UPLOAD_ERR_NO_FILE : UPLOAD_ERR_OK, + $clientFilename, + null + ); + $this->status = $status; + } + + /** + * @return string One of the ExternalUploadedFile::STATUS_XXX constant value + */ + public function getStatus(){ + return $this->status; + } + + /** + * Check if status has a valid value + * @param $status + * @return bool + */ + public static function isValidStatus($status){ + return in_array($status, [self::STATUS_REQUEST_OPTIONS, self::STATUS_UPLOAD_ERROR, self::STATUS_UPLOAD_FINISHED]); + } + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Message/JsActionTrigger.php b/core/src/core/src/pydio/Core/Http/Message/JsActionTrigger.php new file mode 100644 index 0000000000..bca1370cdf --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Message/JsActionTrigger.php @@ -0,0 +1,57 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Message; + +use Pydio\Core\Http\Response\XMLSerializableResponseChunk; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class BgActionTrigger + * An XML response triggering a background action in Pydio UI. + * @package Pydio\Core\Http\Message + */ +class JsActionTrigger implements XMLSerializableResponseChunk +{ + private $delay; + private $javascriptCode; + + /** + * @param string $jsCode + * @param int $delay + */ + public function __construct($jsCode, $delay = 0) + { + $this->javascriptCode = $jsCode; + $this->delay = $delay; + } + + /** + * @return string + */ + public function toXML() + { + $data = "delay."\">"; + $data .= "javascriptCode."]]>"; + $data .= ""; + return $data; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Message/LoggingResult.php b/core/src/core/src/pydio/Core/Http/Message/LoggingResult.php new file mode 100644 index 0000000000..1278b44134 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Message/LoggingResult.php @@ -0,0 +1,88 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Message; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +use Pydio\Core\Http\Response\XMLSerializableResponseChunk; + +/** + * XML result sent after successful or failed login. + * @package Pydio\Core\Http\Message + */ +class LoggingResult implements XMLSerializableResponseChunk +{ + /** + * @var int + */ + private $result; + /** + * @var string + */ + private $rememberLogin; + /** + * @var string + */ + private $rememberPass; + /** + * @var string + */ + private $secureToken; + + /** + * LoggingResult constructor. + * @param $result + * @param string $rememberLogin + * @param string $rememberPass + * @param string $secureToken + */ + public function __construct($result, $rememberLogin="", $rememberPass = "", $secureToken="") + { + $this->result = $result; + $this->rememberLogin = $rememberLogin; + $this->rememberPass = $rememberPass; + $this->secureToken = $secureToken; + } + + /** + * @return int + */ + public function getResult(){ + return $this->result; + } + + /** + * @return string + */ + public function toXML() + { + $remString = ""; + if ($this->rememberPass != "" && $this->rememberLogin!= "") { + $remString = " remember_login=\"$this->rememberLogin\" remember_pass=\"$this->rememberPass\""; + } + if ($this->secureToken != "") { + $remString .= " secure_token=\"$this->secureToken\""; + } + return "result\"$remString/>"; + + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Message/Message.php b/core/src/core/src/pydio/Core/Http/Message/Message.php new file mode 100644 index 0000000000..68e03bc578 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Message/Message.php @@ -0,0 +1,78 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Message; + +use Pydio\Core\Http\Response\JSONSerializableResponseChunk; +use Pydio\Core\Http\Response\XMLSerializableResponseChunk; +use Pydio\Core\Utils\Vars\StringHelper; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class Message + * Generic user message serialized either as XML or JSON. + * @package Pydio\Core\Http\Message + */ +class Message implements XMLSerializableResponseChunk, JSONSerializableResponseChunk +{ + /** + * @var string + */ + private $message; + + /** + * Message constructor. + * @param string $message + */ + public function __construct($message) + { + $this->message = $message; + } + + /** + * @return string + */ + public function toXML() + { + if(is_string($this->message)){ + $m = $this->message; + }else{ + $m = json_encode($this->message); + } + return "".StringHelper::xmlContentEntities($m).""; + } + + /** + * @return string + */ + public function jsonSerializableData() + { + return $this->message; + } + + /** + * @return string + */ + public function jsonSerializableKey() + { + return 'message'; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Message/RegistryMessage.php b/core/src/core/src/pydio/Core/Http/Message/RegistryMessage.php new file mode 100644 index 0000000000..d535329dd3 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Message/RegistryMessage.php @@ -0,0 +1,139 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Message; + +use Pydio\Core\Utils\Vars\XMLFilter; +use Pydio\Core\Http\Response\JSONSerializableResponseChunk; +use Pydio\Core\Http\Response\XMLDocSerializableResponseChunk; +use Pydio\Core\Services\ApplicationState; +use Pydio\Core\Utils\XMLHelper; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class RegistryMessage + * Send a piece or a full registry as XML or JSON + * @package Pydio\Core\Http\Message + */ +class RegistryMessage implements XMLDocSerializableResponseChunk, JSONSerializableResponseChunk +{ + /** + * @var \DOMDocument + */ + protected $registry; + + /** + * @var string|null + */ + protected $xPath; + + /** + * @var \DOMXPath|null + */ + protected $xPathObject; + + /** + * @var String + */ + protected $renderedXML; + + /** + * RegistryMessage constructor. + * @param $registry + * @param null $xPath + * @param null $xPathObject + */ + public function __construct($registry, $xPath = null, $xPathObject = null) + { + $this->registry = $registry; + $this->xPath = $xPath; + $this->xPathObject = $xPathObject; + } + + + /** + * @return string + */ + public function getCharset() + { + return "UTF-8"; + } + + /** + * @return string + */ + public function toXML() + { + if(!empty($this->renderedXML)){ + return $this->renderedXML; + } + if (!empty($this->xPath)) { + + // Warning dirty hack for legacy iOS application : leave the space + // after the xPath value (before closing >). + $xml = "xPath."\" >"; + if(empty($this->xPathObject)){ + $this->xPathObject = new \DOMXPath($this->registry); + } + $nodes = $this->xPathObject->query($this->xPath); + if ($nodes->length) { + $xml .= XMLFilter::resolveKeywords($this->registry->saveXML($nodes->item(0))); + } + $xml .= ""; + + } else { + + ApplicationState::safeIniSet("zlib.output_compression", "4096"); + $xml = XMLFilter::resolveKeywords($this->registry->saveXML()); + + } + $this->renderedXML = $xml; + return $xml; + } + + /** + * @return mixed + */ + public function jsonSerializableData() + { + if(!empty($this->xPath)){ + if(empty($this->xPathObject)){ + $this->xPathObject = new \DOMXPath($this->registry); + } + $nodes = $this->xPathObject->query($this->xPath); + $data = []; + if($nodes->length){ + $data = XMLHelper::xmlToArray($nodes->item(0)); + } + return $data; + }else{ + return XMLHelper::xmlToArray($this->registry); + } + } + + /** + * @return string + */ + public function jsonSerializableKey() + { + return null; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Message/ReloadMessage.php b/core/src/core/src/pydio/Core/Http/Message/ReloadMessage.php new file mode 100644 index 0000000000..d726c36c3e --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Message/ReloadMessage.php @@ -0,0 +1,75 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Message; + +use Pydio\Core\Http\Response\JSONSerializableResponseChunk; +use Pydio\Core\Http\Response\XMLSerializableResponseChunk; +use Pydio\Core\Utils\Vars\StringHelper; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class ReloadMessage + * Sends a Reload instruction to the UI + * @package Pydio\Core\Http\Message + */ +class ReloadMessage implements XMLSerializableResponseChunk, JSONSerializableResponseChunk +{ + private $dataNode = ''; + private $pendingSelection = ''; + + /** + * ReloadMessage constructor. + * @param string $dataNode + * @param string $pendingSelection + */ + public function __construct($dataNode = "", $pendingSelection = ""){ + $this->dataNode = $dataNode; + $this->pendingSelection = $pendingSelection; + } + + + /** + * @return array + */ + public function jsonSerializableData() + { + return ['node'=> $this->dataNode, 'pendingSelection' => $this->pendingSelection]; + } + + /** + * @return string + */ + public function jsonSerializableKey() + { + return 'reload'; + } + + /** + * @return string + */ + public function toXML() + { + $nodePath = StringHelper::xmlEntities($this->dataNode, true); + $pendingSelection = StringHelper::xmlEntities($this->pendingSelection, true); + return ""; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Message/ReloadRepoListMessage.php b/core/src/core/src/pydio/Core/Http/Message/ReloadRepoListMessage.php new file mode 100644 index 0000000000..8248a91381 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Message/ReloadRepoListMessage.php @@ -0,0 +1,55 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Message; + +use Pydio\Core\Http\Response\XMLSerializableResponseChunk; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class ReloadRepoListMessage Encapsulate XML message for reloading repositories list + * @package Pydio\Core\Http\Message + */ +class ReloadRepoListMessage extends Message implements XMLSerializableResponseChunk +{ + /** + * ReloadRepoListMessage constructor. + */ + public function __construct() + { + parent::__construct("reload_repositories"); + } + + /** + * Serialize as XML + * @return string + */ + public function toXML() + { + return ""; + } + + public static function XML(){ + $m = new ReloadRepoListMessage(); + return $m->toXML(); + } + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Message/UserMessage.php b/core/src/core/src/pydio/Core/Http/Message/UserMessage.php new file mode 100644 index 0000000000..464f0a3703 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Message/UserMessage.php @@ -0,0 +1,80 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Message; + +use Pydio\Core\Http\Response\JSONSerializableResponseChunk; +use Pydio\Core\Http\Response\XMLSerializableResponseChunk; +use Pydio\Core\Utils\Vars\StringHelper; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class UserMessage + * User message displayed on the UI + * @package Pydio\Core\Http\Message + */ +class UserMessage implements XMLSerializableResponseChunk, JSONSerializableResponseChunk +{ + + private $level; + private $message; + + /** + * UserMessage constructor. + * @param $message + * @param string $level + */ + public function __construct($message, $level=LOG_LEVEL_INFO) + { + $this->message = $message; + $this->level = $level; + } + + /** + * @return string + */ + public function toXML() + { + if ($this->level === LOG_LEVEL_INFO) { + $messageType = "SUCCESS"; + } else { + $messageType = strtoupper($this->level); + } + $message = StringHelper::xmlContentEntities($this->message); + return "".$message.""; + } + + /** + * @return array + */ + public function jsonSerializableData() + { + return ['level'=>$this->level, 'message' => $this->message]; + } + + /** + * @return string + */ + public function jsonSerializableKey() + { + return 'message'; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Message/XMLDocMessage.php b/core/src/core/src/pydio/Core/Http/Message/XMLDocMessage.php new file mode 100644 index 0000000000..dc96fb40ae --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Message/XMLDocMessage.php @@ -0,0 +1,61 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Message; + +defined('AJXP_EXEC') or die('Access not allowed'); + +use Pydio\Core\Http\Response\XMLDocSerializableResponseChunk; + +/** + * Class XMLDocMessage + * XML Message, represented as a whole XML Document + * @package Pydio\Core\Http\Message + */ +class XMLDocMessage extends \DOMDocument implements XMLDocSerializableResponseChunk +{ + /** + * XMLDocMessage constructor. + * @param string $xmlString + */ + public function __construct($xmlString = null) + { + parent::__construct("1.0", "UTF-8"); + if(!empty($xmlString)){ + $this->loadXML($xmlString); + } + } + + /** + * @return string + */ + public function getCharset() + { + return 'UTF-8'; + } + + /** + * @return string + */ + public function toXML() + { + return $this->saveXML(); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Message/XMLMessage.php b/core/src/core/src/pydio/Core/Http/Message/XMLMessage.php new file mode 100644 index 0000000000..d6655685b7 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Message/XMLMessage.php @@ -0,0 +1,54 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Message; + +use Pydio\Core\Http\Response\XMLSerializableResponseChunk; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class XMLMessage serialized as a piece of XML + * @package Pydio\Core\Http\Message + */ +class XMLMessage implements XMLSerializableResponseChunk +{ + /** + * @var string + */ + private $inner; + + /** + * XMLMessage constructor. + * @param string $xmlString + */ + public function __construct($xmlString) + { + $this->inner = $xmlString; + } + + /** + * @return string + */ + public function toXML() + { + return $this->inner; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Middleware/AuthMiddleware.php b/core/src/core/src/pydio/Core/Http/Middleware/AuthMiddleware.php new file mode 100644 index 0000000000..aef50ca133 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Middleware/AuthMiddleware.php @@ -0,0 +1,86 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Middleware; + +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Auth\Frontend\Core\FrontendsLoader; +use Pydio\Core\Exception\ActionNotFoundException; +use Pydio\Core\Exception\AuthRequiredException; +use Pydio\Core\Exception\NoActiveWorkspaceException; +use Pydio\Core\Exception\PydioException; + +use Pydio\Core\Http\Server; +use Pydio\Core\Model\Context; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\PluginFramework\PluginsService; + +use Pydio\Core\Services\ConfService; +use Zend\Diactoros\Response\EmptyResponse; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class AuthMiddleware + * PSR7 Middleware that encapsulates the call to pydio authfront plugins + * @package Pydio\Core\Http\Middleware + */ +class AuthMiddleware +{ + + /** + * @param ServerRequestInterface $requestInterface + * @param \Psr\Http\Message\ResponseInterface $responseInterface + * @return \Psr\Http\Message\ResponseInterface + * @param callable|null $next + * @throws PydioException + */ + public static function handleRequest(\Psr\Http\Message\ServerRequestInterface $requestInterface, \Psr\Http\Message\ResponseInterface $responseInterface, callable $next = null){ + + $driverImpl = ConfService::getAuthDriverImpl(); + PluginsService::getInstance(Context::emptyContext())->setPluginUniqueActiveForType("auth", $driverImpl->getName(), $driverImpl); + + $response = FrontendsLoader::frontendsAsAuthMiddlewares($requestInterface, $responseInterface); + if($response != null){ + return $response; + } + + try{ + + return Server::callNextMiddleWare($requestInterface, $responseInterface, $next); + + } catch (NoActiveWorkspaceException $ex){ + + throw new AuthRequiredException(); + + } catch(ActionNotFoundException $a){ + + /** @var ContextInterface $ctx */ + $ctx = $requestInterface->getAttribute("ctx"); + if(!$ctx->hasUser()){ + throw new AuthRequiredException(); + }else{ + return new EmptyResponse(); + } + } + + } + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Middleware/ITopLevelMiddleware.php b/core/src/core/src/pydio/Core/Http/Middleware/ITopLevelMiddleware.php new file mode 100644 index 0000000000..77fccbd8dd --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Middleware/ITopLevelMiddleware.php @@ -0,0 +1,51 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Middleware; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Core\Exception\WorkspaceNotFoundException; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Interface ITopLevelMiddleware + * @package Pydio\Core\Http\Middleware + */ +interface ITopLevelMiddleware +{ + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @return mixed + */ + public function emitResponse(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface); + + /** + * @param ServerRequestInterface $requestInterface + * @param \Psr\Http\Message\ResponseInterface $responseInterface + * @return \Psr\Http\Message\ResponseInterface + * @param callable|null $next + * @throws WorkspaceNotFoundException + */ + public function handleRequest(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface, callable $next = null); + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Middleware/SapiMiddleware.php b/core/src/core/src/pydio/Core/Http/Middleware/SapiMiddleware.php new file mode 100644 index 0000000000..5a88501cf5 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Middleware/SapiMiddleware.php @@ -0,0 +1,132 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Middleware; + +use \Psr\Http\Message\ServerRequestInterface; +use \Psr\Http\Message\ResponseInterface; +use Pydio\Core\Exception\PydioException; + +use Pydio\Core\Http\Response\SerializableResponseStream; +use Pydio\Core\Http\Server; +use Pydio\Core\Utils\Vars\InputFilter; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class SapiMiddleware + * Main Middleware for Http requests + * @package Pydio\Core\Http\Middleware + */ +class SapiMiddleware implements ITopLevelMiddleware +{ + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @param callable|null $next + * @return ResponseInterface + * @throws PydioException + */ + public function handleRequest(ServerRequestInterface $request, ResponseInterface $response, callable $next = null){ + + $params = $request->getQueryParams(); + $postParams = $request->getParsedBody(); + if(is_array($postParams)){ + $params = array_merge($params, $postParams); + } + $request = $request->withParsedBody($params); + + if(in_array("application/json", $request->getHeader("Content-Type"))){ + $body = "".$request->getBody(); + $body = json_decode($body, true); + if(is_array($body)){ + $request = $request->withParsedBody(array_merge($request->getParsedBody(), ["request_body" => $body])); + } + } + + $this->parseRequestRouteAndParams($request, $response); + + $response = Server::callNextMiddleWare($request, $response, $next); + + if(headers_sent()){ + return; + } + + $this->emitResponse($request, $response); + } + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $responseInterface + */ + protected function parseRequestRouteAndParams(ServerRequestInterface &$request, ResponseInterface &$responseInterface){ + + $serverData = $request->getServerParams(); + $params = $request->getParsedBody(); + if(isSet($params["get_action"])){ + $action = $params["get_action"]; + }else if(isSet($params["action"])){ + $action = $params["action"]; + }else if (preg_match('/MSIE 7/',$serverData['HTTP_USER_AGENT']) || preg_match('/MSIE 8/',$serverData['HTTP_USER_AGENT'])) { + $action = "get_boot_gui"; + } else { + $action = (strpos($serverData["HTTP_ACCEPT"], "text/html") !== false ? "get_boot_gui" : "ping"); + } + $request = $request + ->withAttribute("action", InputFilter::sanitize($action, InputFilter::SANITIZE_EMAILCHARS)) + ->withAttribute("api", "session") + ; + + } + + /** + * Output the response to the browser, if no headers were already sent. + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @return void + */ + public function emitResponse(ServerRequestInterface $request, ResponseInterface $response){ + if($response !== false && $response->getBody() && $response->getBody() instanceof SerializableResponseStream){ + /** + * @var SerializableResponseStream $body; + */ + $body = &$response->getBody(); + $params = $request->getParsedBody(); + $forceJson = false; + if(isSet($params["format"]) && $params["format"] == "json"){ + $forceJson = true; + } + if(($request->hasHeader("Accept") && $request->getHeader("Accept")[0] == "application/json") || $forceJson){ + $body->setSerializer(SerializableResponseStream::SERIALIZER_TYPE_JSON); + $response = $response->withHeader("Content-type", "application/json; charset=UTF-8"); + }else{ + $body->setSerializer(SerializableResponseStream::SERIALIZER_TYPE_XML); + $response = $response->withHeader("Content-type", "text/xml; charset=UTF-8"); + } + } + + if($response !== false && ($response->getBody()->getSize() || $response instanceof \Zend\Diactoros\Response\EmptyResponse) || $response->getStatusCode() != 200) { + $emitter = new \Zend\Diactoros\Response\SapiEmitter(); + $emitter->emit($response); + } + } + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Middleware/SecureTokenMiddleware.php b/core/src/core/src/pydio/Core/Http/Middleware/SecureTokenMiddleware.php new file mode 100644 index 0000000000..1a9be956a5 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Middleware/SecureTokenMiddleware.php @@ -0,0 +1,112 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Middleware; + + +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Http\Server; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Services\SessionService; +use Pydio\Core\Utils\Vars\StringHelper; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Class SecureTokenMiddleware + * CSRF Prevention + * @package Pydio\Core\Http\Middleware + */ +class SecureTokenMiddleware +{ + + const SECURE_TOKENS_KEY = "PYDIO_SECURE_TOKENS"; + + /** + * + * @param ServerRequestInterface $requestInterface + * @param \Psr\Http\Message\ResponseInterface $responseInterface + * @return \Psr\Http\Message\ResponseInterface + * @param callable|null $next + * @throws PydioException + */ + public static function handleRequest(\Psr\Http\Message\ServerRequestInterface $requestInterface, \Psr\Http\Message\ResponseInterface $responseInterface, callable $next = null){ + + $pluginsUnSecureActions = PluginsService::searchManifestsWithCache("//action[@skipSecureToken]", function($nodes){ + $res = array(); + /** @var \DOMElement $node */ + foreach ($nodes as $node) { + $res[] = $node->getAttribute("name"); + } + return $res; + }); + + $pluginsUnSecureActions[] = "get_secure_token"; + if (!in_array($requestInterface->getAttribute("action"), $pluginsUnSecureActions) && self::hasSecureToken()) { + $params = $requestInterface->getParsedBody(); + if(array_key_exists("secure_token", $params)){ + $token = $params["secure_token"]; + } + if ( !isSet($token) || !self::checkSecureToken($token)) { + throw new PydioException("You are not allowed to access this resource."); + } + } + return Server::callNextMiddleWare($requestInterface, $responseInterface, $next); + + } + + /** + * Put a secure token in the session + * @static + * @return string + */ + public static function generateSecureToken() + { + $arr = SessionService::fetch(self::SECURE_TOKENS_KEY) OR []; + $newToken = StringHelper::generateRandomString(32); + $arr[] = $newToken; + SessionService::save(self::SECURE_TOKENS_KEY, $arr); + return $newToken; + } + /** + * Get the secure token from the session + * @static + * @return string|bool + */ + protected static function hasSecureToken() + { + $arr = SessionService::fetch(self::SECURE_TOKENS_KEY); + return ($arr !== null && is_array($arr) && count($arr)); + } + /** + * Verify a secure token value from the session + * @static + * @param string $token + * @return bool + */ + public static function checkSecureToken($token) + { + $arr = SessionService::fetch(self::SECURE_TOKENS_KEY); + return ($arr !== null && is_array($arr) && in_array($token, $arr)); + } + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Middleware/SessionMiddleware.php b/core/src/core/src/pydio/Core/Http/Middleware/SessionMiddleware.php new file mode 100644 index 0000000000..dfd47e6afa --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Middleware/SessionMiddleware.php @@ -0,0 +1,95 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Middleware; + +use Pydio\Core\Http\Server; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\SessionService; +use Pydio\Core\Services\ApplicationState; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * SessionMiddleware launches a working session + * @package Pydio\Core\Http\Middleware + */ +class SessionMiddleware +{ + /** + * @var \Pydio\Enterprise\Session\PydioSessionHandler $sessionHandler + */ + private static $sessionHandler; + + /** + * @param \Psr\Http\Message\ServerRequestInterface $requestInterface + * @param \Psr\Http\Message\ResponseInterface $responseInterface + * @param callable|null $next + * @return \Psr\Http\Message\ResponseInterface + */ + public static function handleRequest(\Psr\Http\Message\ServerRequestInterface $requestInterface, \Psr\Http\Message\ResponseInterface $responseInterface, callable $next = null){ + + $getParams = $requestInterface->getQueryParams(); + $sessionName = SessionService::getSessionName(); + + if (isSet($getParams[PYDIO_SESSION_QUERY_PARAM])) { + $cookies = $requestInterface->getCookieParams(); + if (!isSet($cookies[$sessionName])) { + $cookies[$sessionName] = $getParams[PYDIO_SESSION_QUERY_PARAM]; + $_COOKIE[$sessionName] = $getParams[PYDIO_SESSION_QUERY_PARAM]; + $requestInterface = $requestInterface->withCookieParams($cookies); + } + } + + if(defined("AJXP_SESSION_HANDLER_PATH") && defined("AJXP_SESSION_HANDLER_CLASSNAME") && file_exists(AJXP_SESSION_HANDLER_PATH)){ + require_once(AJXP_SESSION_HANDLER_PATH); + if(class_exists(AJXP_SESSION_HANDLER_CLASSNAME, false)){ + $sessionHandlerClass = AJXP_SESSION_HANDLER_CLASSNAME; + /** @var \Pydio\Enterprise\Session\PydioSessionHandler $sessionHandler */ + $sessionHandler = new $sessionHandlerClass(); + self::$sessionHandler = $sessionHandler; + $sessionHandler->updateContext($requestInterface->getAttribute("ctx")); + session_set_save_handler($sessionHandler, false); + } + } + session_name($sessionName); + session_start(); + + register_shutdown_function(function(){ + SessionService::close(); + }); + + if(SessionService::has(SessionService::CTX_MINISITE_HASH)){ + ApplicationState::setStateMinisite(SessionService::fetch(SessionService::CTX_MINISITE_HASH)); + } + + return Server::callNextMiddleWare($requestInterface, $responseInterface, $next); + + } + + /** + * @param ContextInterface $ctx + */ + public static function updateContext($ctx){ + if(self::$sessionHandler){ + self::$sessionHandler->updateContext($ctx); + } + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Middleware/SessionRepositoryMiddleware.php b/core/src/core/src/pydio/Core/Http/Middleware/SessionRepositoryMiddleware.php new file mode 100644 index 0000000000..445cead22e --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Middleware/SessionRepositoryMiddleware.php @@ -0,0 +1,174 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Middleware; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Core\Exception\NoActiveWorkspaceException; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Exception\RepositoryLoadException; +use Pydio\Core\Http\Server; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Model\RepositoryInterface; +use Pydio\Core\Model\UserInterface; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Services\RolesService; +use Pydio\Core\Services\SessionService; +use Pydio\Core\Services\UsersService; +use Pydio\Log\Core\Logger; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Class SessionRepositoryMiddleware + * @package Pydio\Core\Http\Middleware + */ +class SessionRepositoryMiddleware +{ + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @return ResponseInterface + * @param callable|null $next + * @throws PydioException + */ + public static function handleRequest(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface, callable $next = null) { + + /** @var ContextInterface $ctx */ + $ctx = $requestInterface->getAttribute("ctx"); + $repository = null; + if($ctx->hasUser()){ + $loggedUser = $ctx->getUser(); + try{ + + $repository = self::switchUserToRepository($loggedUser, $requestInterface); + + } catch (RepositoryLoadException $r){ + + $previous = SessionService::getPreviousRepositoryId(); + if($previous !== null){ + SessionService::saveRepositoryId($previous); + } + throw $r; + + } catch (NoActiveWorkspaceException $nA) { + + $lock = $loggedUser->getLock(); + if(!empty($lock)){ + return Server::callNextMiddleWare($requestInterface, $responseInterface, $next); + }else{ + throw $nA; + } + + } + } + + if($repository !== null){ + $ctx->setRepositoryObject($repository); + $requestInterface = $requestInterface->withAttribute("ctx", $ctx); + } + + SessionMiddleware::updateContext($ctx); + Logger::updateContext($ctx); + + //Set language + if($ctx->hasUser() && $ctx->getUser()->getPref("lang") != "") { + LocaleService::setLanguage($ctx->getUser()->getPref("lang")); + } else if(isSet($requestInterface->getCookieParams()["AJXP_lang"])) { + LocaleService::setLanguage($requestInterface->getCookieParams()["AJXP_lang"]); + } else if(SessionService::getLanguage() !== null){ + LocaleService::setLanguage(SessionService::getLanguage()); + } + + if(UsersService::usersEnabled()){ + try{ + RolesService::bootSequence(); + }catch (PydioException $e){ + if($requestInterface->getAttribute("action") == "get_boot_gui"){ + $requestInterface = $requestInterface->withAttribute("flash", $e->getMessage()); + }else{ + throw $e; + } + } + } + + return Server::callNextMiddleWare($requestInterface, $responseInterface, $next); + + } + + + /** + * @param UserInterface $user + * @param ServerRequestInterface $requestInterface + * @return RepositoryInterface + * @throws NoActiveWorkspaceException + * @throws PydioException + * @throws \Pydio\Core\Exception\WorkspaceNotFoundException + */ + public static function switchUserToRepository(UserInterface $user, ServerRequestInterface $requestInterface) { + + $parameters = $requestInterface->getParsedBody(); + $restRepositoryId = isSet($parameters["tmp_repository_id"]) ? $parameters["tmp_repository_id"] : null; + $repoObject = null; + + if($restRepositoryId !== null){ + + $repoObject = UsersService::getRepositoryWithPermission($user, $restRepositoryId); + + }else{ + + $repoId = SessionService::getSessionRepositoryId(); + if($repoId !== null){ + try{ + $repoObject = UsersService::getRepositoryWithPermission($user, $repoId); + }catch (\Exception $e){ + $previous = SessionService::getPreviousRepositoryId(); + if($previous !== null){ + $repoObject = UsersService::getRepositoryWithPermission($user, $previous); + } + } + }else{ + $userRepositories = UsersService::getRepositoriesForUser($user); + if(empty($userRepositories)){ + throw new NoActiveWorkspaceException(); + } + $default = $user->getMergedRole()->filterParameterValue("core.conf", "DEFAULT_START_REPOSITORY", AJXP_REPO_SCOPE_ALL, -1); + $lastVisited = $user->getArrayPref("history", "last_repository"); + if($default !== -1 && array_key_exists($default, $userRepositories)){ + $repoObject = $userRepositories[$default]; + }else if($lastVisited !== "" && array_key_exists($lastVisited, $userRepositories)){ + $repoObject = $userRepositories[$lastVisited]; + }else{ + $repoObject = array_shift($userRepositories); + } + } + + if($repoObject !== null){ + SessionService::saveRepositoryId($repoObject->getId()); + $user->setArrayPref("history", "last_repository", $repoObject->getId()); + } + } + + return $repoObject; + + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Response/AsyncResponseStream.php b/core/src/core/src/pydio/Core/Http/Response/AsyncResponseStream.php new file mode 100644 index 0000000000..cc32340e59 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Response/AsyncResponseStream.php @@ -0,0 +1,246 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Response; + +use Psr\Http\Message\StreamInterface; +use Zend\Diactoros\Response; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class AsyncResponseStream + * Response Stream built with a callable that will be called when the stream is read. + * @package Pydio\Core\Http\Response + */ +class AsyncResponseStream implements StreamInterface +{ + /** + * @var callable + */ + private $callback; + + /** + * @var bool + */ + private $callbackExecuted = false; + + /** + * AsyncResponseStream constructor. + * @param callable $callable + */ + public function __construct($callable){ + $this->callback = $callable; + } + + /** + * Reads all data from the stream into a string, from the beginning to end. + * + * This method MUST attempt to seek to the beginning of the stream before + * reading data and read the stream until the end is reached. + * + * Warning: This could attempt to load a large amount of data into memory. + * + * This method MUST NOT raise an exception in order to conform with PHP's + * string casting operations. + * + * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring + * @return string + */ + public function __toString() + { + return $this->getContents(); + } + + /** + * Closes the stream and any underlying resources. + * + * @return void + */ + public function close() + { + $this->callback = null; + $this->callbackExecuted = false; + } + + /** + * Separates any underlying resources from the stream. + * + * After the stream has been detached, the stream is in an unusable state. + * + * @return resource|null Underlying PHP stream, if any + */ + public function detach() + { + return null; + } + + /** + * Get the size of the stream if known. + * + * @return int|null Returns the size in bytes if known, or null if unknown. + */ + public function getSize() + { + // Return a non-null size if there's a pending callback. + return (isSet($this->callback) && !$this->callbackExecuted ? 1 : null); + } + + /** + * Returns the current position of the file read/write pointer + * + * @return int Position of the file pointer + * @throws \RuntimeException on error. + */ + public function tell() + { + throw new \RuntimeException(); + } + + /** + * Returns true if the stream is at the end of the stream. + * + * @return bool + */ + public function eof() + { + return $this->callbackExecuted; + } + + /** + * Returns whether or not the stream is seekable. + * + * @return bool + */ + public function isSeekable() + { + return false; + } + + /** + * Seek to a position in the stream. + * + * @link http://www.php.net/manual/en/function.fseek.php + * @param int $offset Stream offset + * @param int $whence Specifies how the cursor position will be calculated + * based on the seek offset. Valid values are identical to the built-in + * PHP $whence values for `fseek()`. SEEK_SET: Set position equal to + * offset bytes SEEK_CUR: Set position to current location plus offset + * SEEK_END: Set position to end-of-stream plus offset. + * @throws \RuntimeException on failure. + */ + public function seek($offset, $whence = SEEK_SET) + { + throw new \RuntimeException(); + } + + /** + * Seek to the beginning of the stream. + * + * If the stream is not seekable, this method will raise an exception; + * otherwise, it will perform a seek(0). + * + * @see seek() + * @link http://www.php.net/manual/en/function.fseek.php + * @throws \RuntimeException on failure. + */ + public function rewind() + { + throw new \RuntimeException(); + } + + /** + * Returns whether or not the stream is writable. + * + * @return bool + */ + public function isWritable() + { + return false; + } + + /** + * Write data to the stream. + * + * @param string $string The string that is to be written. + * @return int Returns the number of bytes written to the stream. + * @throws \RuntimeException on failure. + */ + public function write($string) + { + throw new \RuntimeException(); + } + + /** + * Returns whether or not the stream is readable. + * + * @return bool + */ + public function isReadable() + { + return true; + } + + /** + * Read data from the stream. + * + * @param int $length Read up to $length bytes from the object and return + * them. Fewer than $length bytes may be returned if underlying stream + * call returns fewer bytes. + * @return string Returns the data read from the stream, or an empty string + * if no bytes are available. + * @throws \RuntimeException if an error occurs. + */ + public function read($length) + { + call_user_func($this->callback); + $this->callbackExecuted = true; + return ""; + } + + /** + * Returns the remaining contents in a string + * + * @return string + * @throws \RuntimeException if unable to read or an error occurs while + * reading. + */ + public function getContents() + { + return $this->read(0); + } + + /** + * Get stream metadata as an associative array or retrieve a specific key. + * + * The keys returned are identical to the keys returned from PHP's + * stream_get_meta_data() function. + * + * @link http://php.net/manual/en/function.stream-get-meta-data.php + * @param string $key Specific metadata to retrieve. + * @return array|mixed|null Returns an associative array if no key is + * provided. Returns a specific key value if a key is provided and the + * value is found, or null if the key is not found. + */ + public function getMetadata($key = null) + { + return []; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Response/CLISerializableResponseChunk.php b/core/src/core/src/pydio/Core/Http/Response/CLISerializableResponseChunk.php new file mode 100644 index 0000000000..a23d0e2a66 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Response/CLISerializableResponseChunk.php @@ -0,0 +1,39 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Response; + +use Symfony\Component\Console\Output\OutputInterface; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Interface CLISerializableResponseChunk + * @package Pydio\Core\Http\Response + */ +interface CLISerializableResponseChunk extends SerializableResponseChunk +{ + /** + * @param OutputInterface $output + * @return mixed + */ + public function render($output); + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Response/FileReaderResponse.php b/core/src/core/src/pydio/Core/Http/Response/FileReaderResponse.php new file mode 100644 index 0000000000..c5ee2839e5 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Response/FileReaderResponse.php @@ -0,0 +1,474 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Response; + +use Pydio\Access\Core\MetaStreamWrapper; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Core\Controller\HTMLWriter; +use Pydio\Core\Services\ApiKeysService; +use Pydio\Core\Services\AuthService; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\ApplicationState; +use Pydio\Core\Utils\FileHelper; +use Pydio\Core\Utils\Vars\PathUtils; +use Pydio\Core\Utils\Vars\StatHelper; +use Pydio\Core\Utils\TextEncoder; +use Pydio\Log\Core\Logger; +use Zend\Diactoros\ServerRequestFactory; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class FileReaderResponse + * Async class that can be used as a Body for a ResponseInterface. Will read the file or data + * when the reponse is emitted. + * + * @package Pydio\Core\Http\Response + */ +class FileReaderResponse extends AsyncResponseStream +{ + /** @var AJXP_Node */ + private $node; + + /** @var string */ + private $file; + + /** @var string */ + private $data; + + /** @var string */ + private $localName = ""; + + /** @var string */ + private $headerType = "force-download"; + + /** @var integer */ + private $offset = -1; + + /** @var integer */ + private $length = -1; + + /** @var callable */ + private $preRead; + + /** @var callable */ + private $postRead; + + /** @var boolean */ + private $unlinkAfterRead; + + /** + * FileReaderResponse constructor. + * @param AJXP_Node|string $nodeOrFile + * @param string $data + */ + public function __construct($nodeOrFile = null, $data = null) + { + $callable = array($this, "readData"); + if($nodeOrFile instanceof AJXP_Node){ + $this->node = $nodeOrFile; + }else{ + $this->file = $nodeOrFile; + } + if($data !== null){ + $this->data = $data; + } + parent::__construct($callable); + } + + /** + * Simple debug instruction + * @param $message + * @param array $params + */ + protected function logDebug($message, $params = []){ + Logger::getInstance()->logDebug("FileReader", $message, $params); + } + + /** + * Set a possible header type "plain", "image" if it's force attachement. + * @param $headerType + */ + public function setHeaderType($headerType){ + $this->headerType = $headerType; + } + + /** + * Set the name as it should appear in the download window, + * if it's not the file basename + * @param $localName + */ + public function setLocalName($localName){ + $this->localName = $localName; + } + + /** + * @param integer $offset + * @param integer $length + */ + public function setPartial($offset, $length){ + $this->offset = $offset; + $this->length = $length; + } + + /** + * Callback to be triggered just before reading the file + * @param callable $pre + */ + public function setPreReadCallback(callable $pre){ + $this->preRead = $pre; + } + + /** + * Callback to be triggered at the end of the file read operation + * @param callable $post + */ + public function setPostReadCallback(callable $post){ + $this->postRead = $post; + } + + /** + * Set a flag to trigger an unlink after reading the file. + */ + public function setUnlinkAfterRead(){ + $this->unlinkAfterRead = true; + } + + /** + * Actually read the data to the output + * @throws \Exception + */ + protected function readData(){ + if($this->preRead){ + call_user_func($this->preRead); + } + + $this->readFile($this->node, $this->file, $this->data, $this->headerType, $this->localName, $this->offset, $this->length); + + if($this->postRead){ + call_user_func($this->postRead); + } + } + + /** + * @param AJXP_Node|null $node + * @param string $filePath + * @param string|bool $data + * @param string $headerType + * @param string $localName + * @param int $byteOffset + * @param int $byteLength + * @throws \Exception + */ + public function readFile($node = null, $filePath = null, $data = null, $headerType="plain", $localName="", $byteOffset=-1, $byteLength=-1) + { + if($node !== null){ + $filePathOrData = $node->getUrl(); + }else{ + $filePathOrData = $filePath; + } + + if(!$data === null && !file_exists($filePathOrData)){ + throw new \Exception("File $filePathOrData not found!"); + } + $confGzip = ConfService::getGlobalConf("GZIP_COMPRESSION"); + $confGzipLimit = ConfService::getGlobalConf("GZIP_LIMIT"); + $confUseAccelerator = ConfService::getGlobalConf("USE_DOWNLOAD_ACCELERATOR"); + if($this->unlinkAfterRead && $filePathOrData !== null && empty($confUseAccelerator)){ + register_shutdown_function(function () use ($filePathOrData){ + FileHelper::silentUnlink($filePathOrData); + }); + } + + $fakeReq = ServerRequestFactory::fromGlobals(); + $serverParams = $fakeReq->getServerParams(); + + if ($node !== null && !$node->wrapperIsRemote()) { + $originalFilePath = $filePathOrData; + $filePathOrData = PathUtils::patchPathForBaseDir($filePathOrData); + } + session_write_close(); + + restore_error_handler(); + restore_exception_handler(); + + set_exception_handler('Pydio\Access\Driver\StreamProvider\FS\download_exception_handler'); + set_error_handler('Pydio\Access\Driver\StreamProvider\FS\download_exception_handler'); + // required for IE, otherwise Content-disposition is ignored + if (ini_get('zlib.output_compression')) { + ApplicationState::safeIniSet('zlib.output_compression', 'Off'); + } + + $isFile = ($data !== null) && !$confGzip; + if ($byteLength == -1) { + if ($data !== null) { + $size = strlen($data); + } else if ($node === null) { + $size = sprintf("%u", filesize($filePathOrData)); + } else { + $size = filesize($filePathOrData); + } + } else { + $size = $byteLength; + } + if ($confGzip && ($size > $confGzipLimit || !function_exists("gzencode") || (isSet($serverParams['HTTP_ACCEPT_ENCODING']) && strpos($serverParams['HTTP_ACCEPT_ENCODING'], 'gzip') === FALSE))) { + $confGzip = false; + } + + $localName = ($localName =="" ? basename((isSet($originalFilePath)?$originalFilePath:$filePathOrData)) : $localName); + + if ($headerType == "plain") { + + header("Content-type:text/plain"); + header("Content-Length: ".$size); + + } else if ($headerType == "image") { + + header("Content-Type: ". StatHelper::getImageMimeType(basename($filePathOrData)) ."; name=\"".$localName."\""); + header("Content-Length: ".$size); + header('Cache-Control: public'); + + } else { + + if ($isFile) { + header("Accept-Ranges: 0-$size"); + $this->logDebug("Sending accept range 0-$size"); + } + + // Check if we have a range header (we are resuming a transfer) + if ( isset($serverParams['HTTP_RANGE']) && $isFile && $size != 0 ) { + + if ($headerType == "stream_content") { + + if (extension_loaded('fileinfo') && (( $node !== null && !$node->wrapperIsRemote()) || $filePath !== null)) { + $fInfo = new \fInfo( FILEINFO_MIME ); + if($node !== null){ + $realfile = $node->getRealFile(); + }else{ + $realfile = $filePathOrData; + } + $mimeType = $fInfo->file($realfile); + $splitChar = explode(";", $mimeType); + $mimeType = trim($splitChar[0]); + $this->logDebug("Detected mime $mimeType for $realfile"); + } else { + $mimeType = StatHelper::getStreamingMimeType(basename($filePathOrData)); + } + header('Content-type: '.$mimeType); + } + // multiple ranges, which can become pretty complex, so ignore it for now + $ranges = explode('=', $_SERVER['HTTP_RANGE']); + $offsets = explode('-', $ranges[1]); + $offset = floatval($offsets[0]); + + $length = floatval($offsets[1]) - $offset; + if (!$length) $length = $size - $offset; + if ($length + $offset > $size || $length < 0) $length = $size - $offset; + $this->logDebug('Content-Range: bytes ' . $offset . '-' . $length . '/' . $size); + header('HTTP/1.1 206 Partial Content'); + header('Content-Range: bytes ' . $offset . '-' . ($offset + $length) . '/' . $size); + + header("Content-Length: ". $length); + $file = fopen($filePathOrData, 'rb'); + if(!is_resource($file)){ + throw new \Exception("Failed opening file ".$filePathOrData); + } + fseek($file, 0); + $relOffset = $offset; + while ($relOffset > 2.0E9) { + // seek to the requested offset, this is 0 if it's not a partial content request + fseek($file, 2000000000, SEEK_CUR); + $relOffset -= 2000000000; + // This works because we never overcome the PHP 32 bit limit + } + fseek($file, $relOffset, SEEK_CUR); + + while(ob_get_level()) ob_end_flush(); + $readSize = 0.0; + $bufferSize = 1024 * 8; + while (!feof($file) && $readSize < $length && connection_status() == 0) { + $this->logDebug("dl reading $readSize to $length", ["httpRange" => $serverParams["HTTP_RANGE"]]); + echo fread($file, $bufferSize); + $readSize += $bufferSize; + flush(); + } + + fclose($file); + return; + + } else { + + if ($confGzip) { + + $gzippedData = ($data?gzencode($filePathOrData,9):gzencode(file_get_contents($filePathOrData), 9)); + $size = strlen($gzippedData); + + } + + HTMLWriter::emitAttachmentsHeaders($localName, $size, $isFile, $confGzip); + + if ($confGzip && isSet($gzippedData)) { + print $gzippedData; + return; + } + } + } + + if ($data !== null) { + + print($data); + + } else { + + if ( !empty($confUseAccelerator)){ + $requestSent = $this->sendToAccelerator($confUseAccelerator, ($node !== null ? $node : $filePath), $serverParams); + if($requestSent){ + return; + } + } + + $stream = fopen("php://output", "a"); + + if ($node == null) { + + $this->logDebug("realFS!", array("file"=>$filePathOrData)); + $fp = fopen($filePathOrData, "rb"); + if(!is_resource($fp)){ + throw new \Exception("Failed opening file ".$filePathOrData); + } + if ($byteOffset != -1) { + fseek($fp, $byteOffset); + } + $sentSize = 0; + $readChunk = 4096; + while (!feof($fp)) { + if ( $byteLength != -1 && ($sentSize + $readChunk) >= $byteLength) { + // compute last chunk and break after + $readChunk = $byteLength - $sentSize; + $break = true; + } + $data = fread($fp, $readChunk); + $dataSize = strlen($data); + fwrite($stream, $data, $dataSize); + $sentSize += $dataSize; + if (isSet($break)) { + break; + } + } + fclose($fp); + } else { + + MetaStreamWrapper::copyFileInStream($filePathOrData, $stream); + + } + fflush($stream); + fclose($stream); + + } + } + + /** + * @param string $accelConfiguration + * @param string|AJXP_Node $localPathOrNode + * @param array $serverParams + * @return bool Wether headers were sent and we should interrupt DL now or not. + */ + protected function sendToAccelerator($accelConfiguration, $localPathOrNode, $serverParams){ + + $remoteNode = false; + if($localPathOrNode instanceof AJXP_Node) { + $filePathOrData = $localPathOrNode->getRealFile(); + $remoteNode = $localPathOrNode->wrapperIsRemote(); + }else{ + $filePathOrData = $localPathOrNode; + } + + // TRY XSendFile for local FS nodes or local file + if (!$remoteNode && $accelConfiguration === "xsendfile") { + + $filePathOrData = str_replace("\\", "/", $filePathOrData); + $server_name = $serverParams["SERVER_SOFTWARE"]; + $regex = '/^(lighttpd\/1.4).([0-9]{2}$|[0-9]{3}$|[0-9]{4}$)+/'; + if(preg_match($regex, $server_name)) + $header_sendfile = "X-LIGHTTPD-send-file"; + else + $header_sendfile = "X-Sendfile"; + + + header($header_sendfile.": ".TextEncoder::toUTF8($filePathOrData)); + header("Content-type: application/octet-stream"); + header('Content-Disposition: attachment; filename="' . basename($filePathOrData) . '"'); + return true; + + } + + // TRY XAccelRedirect for local FS nodes or local file + if (!$remoteNode && $accelConfiguration === "xaccelredirect" && array_key_exists("HTTP_X_ACCEL_MAPPING", $serverParams)) { + + $filePathOrData = str_replace("\\", "/", $filePathOrData); + $filePathOrData = TextEncoder::toUTF8($filePathOrData); + $mapping = explode('=',$serverParams['X-Accel-Mapping']); + $replacecount = 0; + $accelfile = str_replace($mapping[0],$mapping[1],$filePathOrData,$replacecount); + if ($replacecount == 1) { + header("X-Accel-Redirect: $accelfile"); + header("Content-type: application/octet-stream"); + header('Content-Disposition: attachment; filename="' . basename($accelfile) . '"'); + return true; + } else { + $this->logDebug("X-Accel-Redirect: Problem with X-Accel-Mapping for file $filePathOrData"); + return false; + } + + } + + // Pydio Agent acceleration - We make sure that request was really proxied by Agent, by checking a specific header. + if($accelConfiguration === "pydio" && array_key_exists("HTTP_X_PYDIO_DOWNLOAD_SUPPORTED", $serverParams) + && ApiKeysService::requestHasValidHeadersForAdminTask($serverParams, PYDIO_BOOSTER_TASK_IDENTIFIER)) { + + if ($localPathOrNode instanceof AJXP_Node) { + $options = MetaStreamWrapper::getResolvedOptionsForNode($localPathOrNode); + if($options["TYPE"] === "php"){ + // Not implemented + return false; + } + $path = $localPathOrNode->getPath(); + }else{ + $options = ["TYPE" => "local"]; + $path = $localPathOrNode; + } + $data = [ + "OPTIONS" => $options, + "PATH" => $path + ]; + if($this->unlinkAfterRead){ + $data["UNLINK_AFTER_READ"] = true; + } + header("X-Pydio-Download-Redirect: ".json_encode($data)); + header("Content-type: application/octet-stream"); + header('Content-Disposition: attachment; filename="' . basename($path) . '"'); + return true; + } + + return false; + } + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Response/JSONSerializableResponseChunk.php b/core/src/core/src/pydio/Core/Http/Response/JSONSerializableResponseChunk.php new file mode 100644 index 0000000000..b82c007415 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Response/JSONSerializableResponseChunk.php @@ -0,0 +1,43 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Response; + + + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Interface JSONSerializableResponseChunk + * Piece of response that can be serialized into JSON. Must return a "key" and additional data to be serialized. + * @package Pydio\Core\Http\Response + */ +interface JSONSerializableResponseChunk extends SerializableResponseChunk +{ + /** + * @return mixed + */ + public function jsonSerializableData(); + + /** + * @return string + */ + public function jsonSerializableKey(); +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Response/SerializableResponseChunk.php b/core/src/core/src/pydio/Core/Http/Response/SerializableResponseChunk.php new file mode 100644 index 0000000000..88b40b519c --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Response/SerializableResponseChunk.php @@ -0,0 +1,33 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Response; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Interface SerializableResponseChunk + * Generic piece of response that can be serialized. + * @package Pydio\Core\Http\Response + */ +interface SerializableResponseChunk +{ + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Response/SerializableResponseStream.php b/core/src/core/src/pydio/Core/Http/Response/SerializableResponseStream.php new file mode 100644 index 0000000000..70c0ac468d --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Response/SerializableResponseStream.php @@ -0,0 +1,398 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Response; + + +use Psr\Http\Message\StreamInterface; +use Pydio\Core\Utils\Vars\XMLFilter; +use Pydio\Core\Http\Message\UserMessage; +use Pydio\Core\Utils\XMLHelper; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class SerializableResponseStream + * Transport stream for various data types that can be serialized to various formats + * @package Pydio\Core\Http\Response + */ +class SerializableResponseStream implements StreamInterface +{ + const SERIALIZER_TYPE_XML = 'xml'; + const SERIALIZER_TYPE_JSON = 'json'; + const SERIALIZER_TYPE_CLI = 'cli'; + + /** + * @var string + */ + protected $serializer = self::SERIALIZER_TYPE_XML; + + /** + * @var array Additional context variable depending on serializer + */ + protected $serializerContext; + /** + * @var SerializableResponseChunk[] + */ + protected $data = []; + /** + * @var string SerializedContent + */ + protected $serializedContent; + + private $streamStatus = 'open'; + + /** + * SerializableResponseStream constructor. + * @param SerializableResponseChunk[]|SerializableResponseChunk $chunks + */ + public function __construct($chunks = []) + { + if(is_object($chunks) && $chunks instanceof SerializableResponseChunk){ + $chunks = [$chunks]; + } + if(count($chunks)){ + $this->data = $chunks; + } + } + + /** + * @param string $serializer SERIALIZER_TYPE_XML|SERIALIZER_TYPE_JSON|SERIALIZER_TYPE_CLI + * @param array $context Additional data for serializer + */ + public function setSerializer($serializer, $context = null){ + $this->serializer = $serializer; + if($context !== null){ + $this->serializerContext = $context; + } + } + + /** + * Test if at least one chunk of data can be serialized using this format + * @param $serializer + * @return bool + */ + public function supportsSerializer($serializer){ + // We can always JSON serialize output + if($serializer === self::SERIALIZER_TYPE_JSON){ + return true; + } + foreach($this->data as $chunk){ + if($serializer === self::SERIALIZER_TYPE_XML && $chunk instanceof XMLSerializableResponseChunk){ + return true; + }else if($serializer === self::SERIALIZER_TYPE_CLI && $chunk instanceof CLISerializableResponseChunk){ + return true; + } + } + return false; + } + + /** + * @param SerializableResponseChunk $chunk + */ + public function addChunk($chunk){ + array_push($this->data, $chunk); + } + + /** + * @return SerializableResponseChunk[] + */ + public function getChunks(){ + return $this->data; + } + + /** + * @return string + */ + public function getContents() + { + if(isSet($this->serializedContent)){ + return $this->serializedContent; + } + return $this->serializeData($this->data, $this->serializer); + } + + + /** + * @param SerializableResponseChunk[] $data + * @param string $serializer + * @return string + * @throws \RuntimeException + */ + protected function serializeData($data, $serializer){ + + if($serializer === self::SERIALIZER_TYPE_JSON){ + $buffer = []; + foreach ($data as $serializableItem){ + if($serializableItem instanceof JSONSerializableResponseChunk){ + $key = $serializableItem->jsonSerializableKey(); + if($key != null){ + $buffer[$key] = $serializableItem->jsonSerializableData(); + }else{ + $buffer[] = $serializableItem->jsonSerializableData(); + } + }else{ + $buffer[] = $serializableItem; + } + } + $pretty = 0; + if(isSet($this->serializerContext) && isSet($this->serializerContext["pretty"]) && $this->serializerContext["pretty"] === true){ + $pretty = JSON_PRETTY_PRINT; + } + if(count($buffer) == 1) { + $json = json_encode(array_shift($buffer), $pretty); + }else { + $json = json_encode($buffer, $pretty); + } + if($json === null){ + $msg = json_last_error_msg(); + $error = json_last_error(); + $message = new UserMessage($msg. " ($error)", LOG_LEVEL_ERROR); + return json_encode($message->jsonSerializableData(), $pretty); + }else{ + return $json; + } + }else if($serializer === self::SERIALIZER_TYPE_XML){ + $wrap = true; + $buffer = ""; + $charset = null; + /** @var XMLDocSerializableResponseChunk[] $xmlDocs */ + $xmlDocs = array_filter($data, function($serial){ + return $serial instanceof XMLDocSerializableResponseChunk; + }); + if(count($xmlDocs)){ + $buffer = $xmlDocs[0]->toXML(); + $charset = $xmlDocs[0]->getCharset(); + $wrap = false; + }else{ + foreach ($data as $serializableItem){ + if(!$serializableItem instanceof XMLSerializableResponseChunk){ + continue; + } + $buffer .= $serializableItem->toXML(); + } + } + + if($wrap){ + $output = XMLHelper::wrapDocument($buffer); + }else{ + if(substr($buffer, 0, 5) !== "".$buffer; + } + $output = $buffer; + } + if(isSet($this->serializerContext) && isSet($this->serializerContext["pretty"]) && $this->serializerContext["pretty"] === true){ + // Rewrite Doc with pretty printing + $doc = new \DOMDocument("1.0", $charset); + $doc->loadXML($output); + $doc->preserveWhiteSpace = false; + $doc->formatOutput = true; + $output = $doc->saveXML(); + } + return $output; + + }else if($serializer === self::SERIALIZER_TYPE_CLI){ + $buffer = ""; + $output = $this->serializerContext["output"]; + foreach($data as $serializableItem){ + if($serializableItem instanceof CLISerializableResponseChunk){ + $buffer .= $serializableItem->render($output); + }else{ + // Default to JSON + $buffer .= json_encode($serializableItem); + } + } + + } + return ""; + } + + /** + * Reads all data from the stream into a string, from the beginning to end. + * + * This method MUST attempt to seek to the beginning of the stream before + * reading data and read the stream until the end is reached. + * + * Warning: This could attempt to load a large amount of data into memory. + * + * This method MUST NOT raise an exception in order to conform with PHP's + * string casting operations. + * + * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring + * @return string + */ + public function __toString() + { + return $this->getContents(); + } + + /** + * Closes the stream and any underlying resources. + * + * @return void + */ + public function close() + { + $this->streamStatus = 'closed'; + } + + /** + * Separates any underlying resources from the stream. + * + * After the stream has been detached, the stream is in an unusable state. + * + * @return resource|null Underlying PHP stream, if any + */ + public function detach() + { + return null; + } + + /** + * Get the size of the stream if known. + * + * @return int|null Returns the size in bytes if known, or null if unknown. + */ + public function getSize() + { + if(!empty($this->data)){ + $this->serializedContent = $this->getContents(); + return strlen($this->serializedContent); + }else{ + return 0; + } + } + + /** + * Returns the current position of the file read/write pointer + * + * @return int Position of the file pointer + * @throws \RuntimeException on error. + */ + public function tell() + { + return -1; + } + + /** + * Returns true if the stream is at the end of the stream. + * + * @return bool + */ + public function eof() + { + return false; + } + + /** + * Returns whether or not the stream is seekable. + * + * @return bool + */ + public function isSeekable() + { + return false; + } + + /** + * Seek to a position in the stream. + * + * @link http://www.php.net/manual/en/function.fseek.php + * @param int $offset Stream offset + * @param int $whence Specifies how the cursor position will be calculated + * based on the seek offset. Valid values are identical to the built-in + * PHP $whence values for `fseek()`. SEEK_SET: Set position equal to + * offset bytes SEEK_CUR: Set position to current location plus offset + * SEEK_END: Set position to end-of-stream plus offset. + * @throws \RuntimeException on failure. + */ + public function seek($offset, $whence = SEEK_SET){} + + /** + * Seek to the beginning of the stream. + * + * If the stream is not seekable, this method will raise an exception; + * otherwise, it will perform a seek(0). + * + * @see seek() + * @link http://www.php.net/manual/en/function.fseek.php + * @throws \RuntimeException on failure. + */ + public function rewind(){} + + /** + * Returns whether or not the stream is writable. + * + * @return bool + */ + public function isWritable(){ + return true; + } + + /** + * Write data to the stream. + * + * @param string $string The string that is to be written. + * @return int Returns the number of bytes written to the stream. + * @throws \RuntimeException on failure. + */ + public function write($string){ + return strlen($string); + } + + /** + * Returns whether or not the stream is readable. + * + * @return bool + */ + public function isReadable(){ + return true; + } + + /** + * Read data from the stream. + * + * @param int $length Read up to $length bytes from the object and return + * them. Fewer than $length bytes may be returned if underlying stream + * call returns fewer bytes. + * @return string Returns the data read from the stream, or an empty string + * if no bytes are available. + * @throws \RuntimeException if an error occurs. + */ + public function read($length){ + return ''; + } + + /** + * Get stream metadata as an associative array or retrieve a specific key. + * + * The keys returned are identical to the keys returned from PHP's + * stream_get_meta_data() function. + * + * @link http://php.net/manual/en/function.stream-get-meta-data.php + * @param string $key Specific metadata to retrieve. + * @return array|mixed|null Returns an associative array if no key is + * provided. Returns a specific key value if a key is provided and the + * value is found, or null if the key is not found. + */ + public function getMetadata($key = null){ + return null; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Response/XMLDocSerializableResponseChunk.php b/core/src/core/src/pydio/Core/Http/Response/XMLDocSerializableResponseChunk.php new file mode 100644 index 0000000000..2c1f652d8d --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Response/XMLDocSerializableResponseChunk.php @@ -0,0 +1,37 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Response; + + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Interface XMLDocSerializableResponseChunk + * Piece of response that will be serialized as a whole XML document + * @package Pydio\Core\Http\Response + */ +interface XMLDocSerializableResponseChunk extends XMLSerializableResponseChunk +{ + /** + * @return string + */ + public function getCharset(); +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Response/XMLSerializableResponseChunk.php b/core/src/core/src/pydio/Core/Http/Response/XMLSerializableResponseChunk.php new file mode 100644 index 0000000000..1911182ee2 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Response/XMLSerializableResponseChunk.php @@ -0,0 +1,36 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Response; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Interface XMLSerializableResponseChunk + * Piece of response serialized as XML. + * @package Pydio\Core\Http\Response + */ +interface XMLSerializableResponseChunk extends SerializableResponseChunk +{ + /** + * @return string + */ + public function toXML(); +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Rest/ApiRouter.php b/core/src/core/src/pydio/Core/Http/Rest/ApiRouter.php new file mode 100644 index 0000000000..41bbf255b4 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Rest/ApiRouter.php @@ -0,0 +1,183 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Rest; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class ApiRouter + * Router used for REST API v1 and REST API v2. + * Based on fast-route, creates route for api/v1 using XML declaration, and routes for api/v2 using + * swagger file. + * @package Pydio\Core\Http\Rest + */ +class ApiRouter +{ + /** + * @var array + */ + private $base; + /** + * @var array + * "cacheOptions" => ["cacheFile" => "path", "cacheDisabled" => true], + */ + private $cacheOptions; + + private $v2Base = "/v2"; + private $v1Base = ""; + + /** + * ApiRouter constructor. + * @param string $base + * @param array $cacheOptions + */ + public function __construct($base, $cacheOptions = []){ + $this->base = $base; + $this->cacheOptions = array_merge([ + "cacheDisabled" => AJXP_SKIP_CACHE, + "cacheFile" => AJXP_DATA_PATH."/cache/plugins_api2routes.php" + ], $cacheOptions); + } + + + /** + * @param \FastRoute\RouteCollector $r + */ + public function configureRoutes(\FastRoute\RouteCollector &$r){ + + $configObject = json_decode(file_get_contents(AJXP_INSTALL_PATH . "/" . AJXP_DOCS_FOLDER . "/api2.json"), true); + foreach ($configObject["paths"] as $path => $methods){ + foreach($methods as $method => $apiData){ + if(preg_match('/\{path\}/', $path)){ + if(isset($apiData["parameters"]["0"]['$ref']) && preg_match('/Optional$/', $apiData["parameters"]["0"]['$ref'])){ + $path = str_replace("{path}", "{path:.*}", $path); + }else{ + $path = str_replace("{path}", "{path:.+}", $path); + } + } + $path = str_replace("{roleId}", "{roleId:.+}", $path); + $r->addRoute(strtoupper($method), $this->base . $this->v2Base . $path , $apiData); + } + } + // Legacy V1 API + $r->addRoute("GET", $this->base . $this->v1Base."/{repository_id}/{action}[{optional:.+}]", ["api-v1" => true]); + $r->addRoute("POST", $this->base . $this->v1Base."/{repository_id}/{action}[{optional:.+}]", ["api-v1" => true]); + + } + + /** + * Get Path component of the URI, without query parameters + * @param ServerRequestInterface $request + * @return string + */ + public function getURIForRequest(ServerRequestInterface $request){ + + $uri = $request->getServerParams()['REQUEST_URI']; + // Strip query string (?foo=bar) and decode URI + if (false !== $pos = strpos($uri, '?')) { + $uri = substr($uri, 0, $pos); + } + return rawurldecode($uri); + } + + /** + * Find a route in api definitions + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + public function route(ServerRequestInterface &$request, ResponseInterface &$response){ + + $dispatcher = \FastRoute\cachedDispatcher(function(\FastRoute\RouteCollector $r) { + + $this->configureRoutes($r); + + }, $this->cacheOptions); + + $httpMethod = $request->getServerParams()['REQUEST_METHOD']; + $uri = $this->getURIForRequest($request); + $routeInfo = $dispatcher->dispatch($httpMethod, $uri); + + switch ($routeInfo[0]) { + case \FastRoute\Dispatcher::NOT_FOUND: + //$response = $response->withStatus(404); + break; + case \FastRoute\Dispatcher::METHOD_NOT_ALLOWED: + //$allowedMethods = $routeInfo[1]; + //$response = $response->withStatus(405); + break; + case \FastRoute\Dispatcher::FOUND: + $apiData = $routeInfo[1]; + $vars = $routeInfo[2]; + if(isSet($apiData["api-v1"])){ + $apiUri = preg_replace('/^'.preg_quote($this->base.$this->v1Base, '/').'/', '', $uri); + $request = $request + ->withAttribute("action", $vars["action"]) + ->withAttribute("repository_id", $vars["repository_id"]) + ->withAttribute("rest_base", $this->base.$this->v1Base) + ->withAttribute("rest_path", $vars["optional"]) + ->withAttribute("api", "v1") + ->withAttribute("api_uri", $apiUri) + ; + }else{ + $apiUri = preg_replace('/^'.preg_quote($this->base.$this->v2Base, '/').'/', '', $uri); + $request = $request->withAttribute("api_uri", $apiUri); + $repoId = $this->findRepositoryInParameters($request, $vars); + $request = $request + ->withAttribute("action", $apiData["x-pydio-action"]) + ->withAttribute("repository_id", $repoId) + ->withAttribute("rest_base", $this->base.$this->v2Base) + ->withAttribute("api", "v2") + ->withParsedBody(array_merge($request->getParsedBody(), $vars)); + } + return true; + default: + break; + } + + return false; + } + + /** + * Analyze URI and parameters to guess the current workspace + * + * @param ServerRequestInterface $request + * @param array $pathVars + * @return mixed|string + */ + protected function findRepositoryInParameters(ServerRequestInterface $request, array $pathVars){ + $params = array_merge($request->getParsedBody(), $pathVars); + if (preg_match('/^\/admin\//', $request->getAttribute("api_uri"))) { + return "ajxp_conf"; + }else if(isSet($params["workspaceId"])){ + return $params["workspaceId"]; + }else if(isSet($params["path"]) && strpos($params["path"], "/") !== false){ + return array_shift(explode("/", ltrim($params["path"], "/"))); + } + // If no repo ID was found, return default repo id "pydio". + return "pydio"; + } + + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Rest/RestApiMiddleware.php b/core/src/core/src/pydio/Core/Http/Rest/RestApiMiddleware.php new file mode 100644 index 0000000000..dca05374d6 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Rest/RestApiMiddleware.php @@ -0,0 +1,62 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Rest; + +use \Psr\Http\Message\ServerRequestInterface; +use \Psr\Http\Message\ResponseInterface; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Http\Middleware\SapiMiddleware; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class RestApiMiddleware + * Main middleware for routing REST API. + * @package Pydio\Core\Http\Rest + */ +class RestApiMiddleware extends SapiMiddleware +{ + protected $base; + + /** + * RestApiMiddleware constructor. + * @param string $base + */ + public function __construct($base) + { + $this->base = $base; + } + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @throws PydioException + */ + protected function parseRequestRouteAndParams(ServerRequestInterface &$request, ResponseInterface &$response){ + + $router = new ApiRouter($this->base); + if(!$router->route($request, $response)){ + throw new PydioException("Could not find any endpoint for this URI"); + } + + } + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Rest/RestApiServer.php b/core/src/core/src/pydio/Core/Http/Rest/RestApiServer.php new file mode 100644 index 0000000000..e82b91da09 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Rest/RestApiServer.php @@ -0,0 +1,54 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ + +namespace Pydio\Core\Http\Rest; + +use Pydio\Core\Http\Server; +use Pydio\Core\Services\ApplicationState; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class RestApiServer + * REST API listener + * @package Pydio\Core\Http\Rest + */ +class RestApiServer extends Server +{ + /** + * RestApiServer constructor. + * @param $base + * @param array $requestAttributes + */ + public function __construct($base, $requestAttributes = []) + { + parent::__construct($base, $requestAttributes); + ApplicationState::setSapiRestBase($base); + } + + protected function stackMiddleWares() + { + $this->middleWares->push(array("Pydio\\Core\\Controller\\Controller", "registryActionMiddleware")); + $this->middleWares->push(array("Pydio\\Core\\Http\\Rest\\RestAuthMiddleware", "handleRequest")); + $this->topMiddleware = new RestApiMiddleware($this->base); + $this->middleWares->push(array($this->topMiddleware, "handleRequest")); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Rest/RestAuthMiddleware.php b/core/src/core/src/pydio/Core/Http/Rest/RestAuthMiddleware.php new file mode 100644 index 0000000000..5bea7b877a --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Rest/RestAuthMiddleware.php @@ -0,0 +1,107 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Rest; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Auth\Frontend\Core\FrontendsLoader; +use Pydio\Core\Exception\NoActiveWorkspaceException; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Exception\WorkspaceForbiddenException; + +use Pydio\Core\Http\Server; +use Pydio\Core\Model\Context; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Services\ConfService; + +use Pydio\Core\Services\RolesService; +use Pydio\Core\Services\UsersService; +use Pydio\Core\Services\ApplicationState; +use Pydio\Log\Core\Logger; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Authentication middleware used in Rest context + * @package Pydio\Core\Http\Rest + */ +class RestAuthMiddleware +{ + + /** + * @param ServerRequestInterface $requestInterface + * @param \Psr\Http\Message\ResponseInterface $responseInterface + * @return \Psr\Http\Message\ResponseInterface + * @param callable|null $next + * @throws PydioException + */ + public static function handleRequest(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface, callable $next = null){ + + $driverImpl = ConfService::getAuthDriverImpl(); + PluginsService::getInstance(Context::emptyContext())->setPluginUniqueActiveForType("auth", $driverImpl->getName(), $driverImpl); + + $response = FrontendsLoader::frontendsAsAuthMiddlewares($requestInterface, $responseInterface); + if($response != null){ + return $response; + } + /** @var ContextInterface $ctx */ + $ctx = $requestInterface->getAttribute("ctx"); + + if(!$ctx->hasUser()){ + $responseInterface = $responseInterface->withStatus(401); + $responseInterface->getBody()->write('You are not authorized to access this API.'); + return $responseInterface; + } + + $repoID = $requestInterface->getAttribute("repository_id"); + if($repoID == 'pydio'){ + $userRepositories = UsersService::getRepositoriesForUser($ctx->getUser()); + if(empty($userRepositories)){ + throw new NoActiveWorkspaceException(); + } + $repo = array_shift($userRepositories); + }else{ + try{ + $repo = UsersService::getRepositoryWithPermission($ctx->getUser(), $repoID); + }catch (WorkspaceForbiddenException $w){ + $responseInterface = $responseInterface->withStatus(401); + $responseInterface->getBody()->write('You are not authorized to access this API.'); + return $responseInterface; + } + } + + $ctx->setRepositoryObject($repo); + $requestInterface = $requestInterface->withAttribute("ctx", $ctx); + + Logger::updateContext($ctx); + + if(UsersService::usersEnabled() && ApplicationState::detectApplicationFirstRun()){ + RolesService::bootSequence(); + } + + return Server::callNextMiddleWare($requestInterface, $responseInterface, $next); + + } + + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Server.php b/core/src/core/src/pydio/Core/Http/Server.php new file mode 100644 index 0000000000..1ea80a894e --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Server.php @@ -0,0 +1,279 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Core\Exception\PydioException; + +use Pydio\Core\Http\Message\UserMessage; +use Pydio\Core\Http\Middleware\ITopLevelMiddleware; +use Pydio\Core\Http\Middleware\SapiMiddleware; +use Pydio\Core\Http\Response\SerializableResponseChunk; +use Pydio\Core\Http\Response\SerializableResponseStream; +use Pydio\Core\Model\Context; +use Pydio\Log\Core\Logger; +use Zend\Diactoros\Response; +use Zend\Diactoros\ServerRequestFactory; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Pydio HTTP Server + * @package Pydio\Core\Http + */ +class Server +{ + /** + * @var ServerRequestInterface + */ + protected $request; + + /** + * @var \SplStack + */ + protected $middleWares; + + /** + * @var ITopLevelMiddleware + */ + protected $topMiddleware; + + /** + * @var string + */ + protected $base; + + /** + * @var \SplStack + */ + protected static $middleWareInstance; + + /** + * @var array Additional attributes will be added to the initial Request object + */ + private $requestAttributes; + + /** + * Server constructor. + * @param $base + * @param $requestAttributes + */ + public function __construct($base, $requestAttributes = []){ + + $this->middleWares = new \SplStack(); + $this->middleWares->setIteratorMode(\SplDoublyLinkedList::IT_MODE_LIFO | \SplDoublyLinkedList::IT_MODE_KEEP); + + $this->base = $base; + + $this->stackMiddleWares(); + + self::$middleWareInstance = &$this->middleWares; + + $this->requestAttributes = $requestAttributes; + } + + protected function stackMiddleWares(){ + + $this->middleWares->push(array("Pydio\\Core\\Controller\\Controller", "registryActionMiddleware")); + $this->middleWares->push(array("Pydio\\Core\\Http\\Middleware\\SessionRepositoryMiddleware", "handleRequest")); + $this->middleWares->push(array("Pydio\\Core\\Http\\Middleware\\AuthMiddleware", "handleRequest")); + $this->middleWares->push(array("Pydio\\Core\\Http\\Middleware\\SecureTokenMiddleware", "handleRequest")); + $this->middleWares->push(array("Pydio\\Core\\Http\\Middleware\\SessionMiddleware", "handleRequest")); + + $topMiddleware = new SapiMiddleware(); + $this->topMiddleware = $topMiddleware; + $this->middleWares->push(array($topMiddleware, "handleRequest")); + + } + + public function registerCatchAll(){ + if (is_file(TESTS_RESULT_FILE) || is_file(TESTS_RESULT_FILE_LEGACY)) { + set_error_handler(array($this, "catchError"), E_ALL & ~E_NOTICE & ~E_STRICT ); + set_exception_handler(array($this, "catchException")); + } + } + + /** + * @return ServerRequestInterface + */ + public function getRequest(){ + if(!isSet($this->request)){ + $this->request = $this->initServerRequest(); + } + return $this->request; + } + + /** + * @param ServerRequestInterface $request + */ + public function updateRequest(ServerRequestInterface $request){ + $this->request = $request; + } + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @return ResponseInterface + */ + protected function nextCallable(&$request, &$response){ + if($this->middleWares->valid()){ + $callable = $this->middleWares->current(); + $this->middleWares->next(); + $response = call_user_func_array($callable, array($request, $response, function($req, $res){ + return $this->nextCallable($req, $res); + })); + } + return $response; + } + + + + /** + * To be used by middlewares + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @param callable|null $next + * @return ResponseInterface + */ + public static function callNextMiddleWare(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface, callable $next = null){ + if($next !== null){ + $responseInterface = call_user_func_array($next, array($requestInterface, $responseInterface)); + } + return $responseInterface; + } + + /** + * @param callable $comparisonFunction + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @param callable|null $next + * @return ResponseInterface + */ + public static function callNextMiddleWareAndRewind(callable $comparisonFunction, ServerRequestInterface $requestInterface, ResponseInterface $responseInterface, callable $next = null){ + if($next !== null){ + $responseInterface = call_user_func_array($next, array($requestInterface, $responseInterface)); + } + self::$middleWareInstance->rewind(); + while(!$comparisonFunction(self::$middleWareInstance->current())){ + self::$middleWareInstance->next(); + } + self::$middleWareInstance->next(); + return $responseInterface; + } + + /** + * @param callable $middleWareCallable + */ + public function addMiddleware(callable $middleWareCallable){ + $this->middleWares->push($middleWareCallable); + self::$middleWareInstance = $this->middleWares; + } + + public function listen(){ + + $response = new Response(); + $this->middleWares->rewind(); + $this->nextCallable($this->getRequest(), $response); + + } + + /** + * @param bool $rest + * @return ServerRequestInterface + */ + protected function initServerRequest($rest = false){ + + $request = ServerRequestFactory::fromGlobals(); + $request = $request->withAttribute("ctx", Context::emptyContext()); + if(!empty($this->requestAttributes) && count($this->requestAttributes)){ + foreach($this->requestAttributes as $attName => $attValue){ + $request = $request->withAttribute($attName, $attValue); + } + } + return $request; + + } + + /** + * Error Catcher for PHP errors. Depending on the SERVER_DEBUG config + * shows the file/line info or not. + * @static + * @param $code + * @param $message + * @param $fichier + * @param $ligne + * @param $context + */ + public function catchError($code, $message, $fichier, $ligne, $context) + { + if(error_reporting() == 0) { + return ; + } + Logger::error(basename($fichier), "error l.$ligne", array("message" => $message)); + if(AJXP_SERVER_DEBUG){ + if($context instanceof \Exception){ + $message .= $context->getTraceAsString(); + }else{ + $message .= PydioException::buildDebugBackTrace(); + } + } + $req = $this->getRequest(); + $resp = new Response(); + $x = new SerializableResponseStream(); + if($code > 100 && $code < 599){ + $resp = $resp->withStatus($code); + } + $resp = $resp->withBody($x); + $x->addChunk(new UserMessage($message, LOG_LEVEL_ERROR)); + $this->topMiddleware->emitResponse($req, $resp); + + } + + /** + * Catch exceptions, @see catchError + * @param \Exception $exception + */ + public function catchException($exception) + { + if($exception instanceof SerializableResponseChunk){ + + $req = $this->getRequest(); + $resp = new Response(); + $x = new SerializableResponseStream(); + $resp = $resp->withBody($x); + $x->addChunk($exception); + $this->topMiddleware->emitResponse($req, $resp); + return; + } + + try { + $this->catchError($exception->getCode(), $exception->getMessage(), $exception->getFile(), $exception->getLine(), $exception); + } catch (\Exception $innerEx) { + error_log(get_class($innerEx)." thrown within the exception handler!"); + error_log("Original exception was: ".$innerEx->getMessage()." in ".$innerEx->getFile()." on line ".$innerEx->getLine()); + error_log("New exception is: ".$innerEx->getMessage()." in ".$innerEx->getFile()." on line ".$innerEx->getLine()." ".$innerEx->getTraceAsString()); + print("Error"); + } + } + + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/SimpleRestResourceRouter.php b/core/src/core/src/pydio/Core/Http/SimpleRestResourceRouter.php new file mode 100644 index 0000000000..1975160ed4 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/SimpleRestResourceRouter.php @@ -0,0 +1,254 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class SimpleRestResourceRouter + * FastRoute encapsuler that can be used to make a simple CRUD Rest API on an existing + * route of the application. + * @package Pydio\Core\Http + */ +class SimpleRestResourceRouter +{ + /** + * @var array + * ["resourceName" => "name", + * "parameterName" => "object_id" + * "crudCallbacks" => ["CREATE" => methodName, + * "RETRIEVE_MANY"=> methodName, + * "RETRIEVE_ONE"=> methodName, + * "UPDATE" => methodName, + * "DELETE" => methodName], + * "linkedResources" => [NESTED CONFIG ARRAY] + * "additionalRoutes" => [[method=>"GET", uri=>"/path/to/route", callable]] + * ] + */ + private $config; + /** + * @var array + * "cacheOptions" => ["cacheFile" => "path", "cacheDisabled" => true], + */ + private $cacheOptions; + /** + * @var object Will be used as '$this' for triggering the callbacks + */ + private $callbacksContext; + + private $base = "/api/{repository_id}"; + + /** + * SimpleRestResourceRouter constructor. + * @param object $callbacksContext + * @param array $config + * @param array $cacheOptions + */ + public function __construct($callbacksContext, $config, $cacheOptions){ + $this->config = $config; + $this->cacheOptions = $cacheOptions; + $this->callbacksContext = $callbacksContext; + } + + /** + * @param $method + * @param $uri + * @param $callable + */ + public function addRoute($method, $uri, $callable){ + $this->config["additional_routes"][] = ["method" => $method, "uri" => $uri, "callable" => $callable]; + } + + /** + * @param array $configObject + * @param \FastRoute\RouteCollector $r + */ + public function configureRoutes($base, $configObject, \FastRoute\RouteCollector &$r){ + + $parameterName = $configObject["parameterName"]; + $resName = $configObject["resourceName"]; + $callbacks = $configObject["crudCallbacks"]; + if(isSet($callbacks["RETRIEVE_MANY"])){ + $r->addRoute('GET', $base."/".$resName, ['simpleHandler', $callbacks["RETRIEVE_MANY"]]); + } + if(isset($callbacks["RETRIEVE_ONE"])){ + $r->addRoute('GET', $base."/".$resName.'/{'.$parameterName.'}', ['simpleHandler', $callbacks["RETRIEVE_ONE"]]); + } + if(isSet($callbacks["CREATE"])){ + $r->addRoute('POST', $base."/".$resName, ['bodyHandler', $callbacks["CREATE"]]); + } + if(isSet($callbacks["UPDATE"])){ + $r->addRoute('PUT', $base."/".$resName.'/{'.$parameterName.'}', ['bodyHandler', $callbacks["UPDATE"]]); + $r->addRoute('PATCH', $base."/".$resName.'/{'.$parameterName.'}', ['bodyHandler', $callbacks["UPDATE"]]); + } + if(isSet($callbacks["DELETE"])){ + $r->addRoute('DELETE', $base."/".$resName.'/{'.$parameterName.'}', ['simpleHandler', $callbacks["DELETE"]]); + } + + if(is_array($configObject["additionalRoutes"])){ + foreach ($configObject["additionalRoutes"] as $additional_route){ + if(in_array($additional_route["method"], ["GET", "DELETE", "HEAD"])){ + $r->addRoute($additional_route["method"], $base.$additional_route["route"], ['simpleHandler', $additional_route["callback"]]); + }else if(in_array($additional_route["method"], ["POST", "PUT", "PATCH"])){ + $r->addRoute($additional_route["method"], $base.$additional_route["route"], ['bodyHandler', $additional_route["callback"]]); + } + } + } + + if(is_array($configObject["linkedResources"])){ + foreach ($configObject["linkedResources"] as $linkedResource){ + $this->configureRoutes($base."/".$resName."/{".$parameterName."}", $linkedResource, $r); + } + } + + } + + /** + * @param ServerRequestInterface $request + * @return string + */ + public function getURIForRequest(ServerRequestInterface $request){ + + $uri = $request->getServerParams()['REQUEST_URI']; + // Strip query string (?foo=bar) and decode URI + if (false !== $pos = strpos($uri, '?')) { + $uri = substr($uri, 0, $pos); + } + return rawurldecode($uri); + } + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + public function route(ServerRequestInterface &$request, ResponseInterface &$response){ + + $dispatcher = \FastRoute\cachedDispatcher(function(\FastRoute\RouteCollector $r) { + + $this->configureRoutes($this->base, $this->config, $r); + + }, $this->cacheOptions); + + $httpMethod = $request->getServerParams()['REQUEST_METHOD']; + $uri = $this->getURIForRequest($request); + $routeInfo = $dispatcher->dispatch($httpMethod, $uri); + + switch ($routeInfo[0]) { + case \FastRoute\Dispatcher::NOT_FOUND: + //$response = $response->withStatus(404); + break; + case \FastRoute\Dispatcher::METHOD_NOT_ALLOWED: + //$allowedMethods = $routeInfo[1]; + //$response = $response->withStatus(405); + break; + case \FastRoute\Dispatcher::FOUND: + $handler = $routeInfo[1][0]; + $callback = $routeInfo[1][1]; + $vars = $routeInfo[2]; + + $request = $request->withParsedBody(array_merge($request->getParsedBody(), $vars)); + $this->$handler($callback, $request, $response); + return true; + default: + break; + } + + return false; + } + + /** + * @param callable $callback + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + */ + protected function simpleHandler($callback, ServerRequestInterface &$requestInterface, ResponseInterface &$responseInterface){ + + $data = $this->callbacksContext->$callback($requestInterface, $responseInterface); + + $responseInterface = $responseInterface->withHeader("Content-type", "application/json"); + $responseInterface->getBody()->write(json_encode($data)); + } + + /** + * @param callable $callback + * @param ServerRequestInterface $request + * @param ResponseInterface $response + */ + protected function bodyHandler($callback, ServerRequestInterface &$request, ResponseInterface &$response){ + + $postedObject = json_decode($request->getBody()->getContents()); + $request = $request->withParsedBody(array_merge($request->getParsedBody(), ["postedObject" => $postedObject])); + $data = $this->callbacksContext->$callback($request, $response); + + $response = $response->withHeader("Content-Type", "application/json"); + $response->getBody()->write(json_encode($data)); + + } + + + /** + * Class casting + * + * @param string|object $destination + * @param object $sourceObject + * @return mixed + */ + public static function cast($destination, $sourceObject) + { + if (is_string($destination)) { + $destination = new $destination(); + } + $destinationReflection = new \ReflectionObject($destination); + if(is_object($sourceObject)){ + $sourceReflection = new \ReflectionObject($sourceObject); + $sourceProperties = $sourceReflection->getProperties(); + foreach ($sourceProperties as $sourceProperty) { + $sourceProperty->setAccessible(true); + $name = $sourceProperty->getName(); + $value = $sourceProperty->getValue($sourceObject); + if ($destinationReflection->hasProperty($name)) { + $propDest = $destinationReflection->getProperty($name); + $propDest->setAccessible(true); + $propDest->setValue($destination,$value); + } else { + $destination->$name = $value; + } + } + }else{ + foreach($sourceObject as $name => $value){ + if ($destinationReflection->hasProperty($name)) { + $propDest = $destinationReflection->getProperty($name); + $propDest->setAccessible(true); + $propDest->setValue($destination,$value); + } else { + $destination->$name = $value; + } + } + } + return $destination; + } + + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/TopLevelRouter.php b/core/src/core/src/pydio/Core/Http/TopLevelRouter.php new file mode 100644 index 0000000000..880843f490 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/TopLevelRouter.php @@ -0,0 +1,136 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http; + +use FastRoute\Dispatcher; +use FastRoute\RouteCollector; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Core\Exception\PydioException; + +use Pydio\Core\Services\ConfService; +use Pydio\Core\Utils\Vars\PathUtils; +use Zend\Diactoros\ServerRequestFactory; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Class TopLevelRouter + * Creates a simple router for top level segments. Will replace the RewriteRules and others. + * + * @package Pydio\Core\Http + */ +class TopLevelRouter +{ + /** + * @var array + * "cacheOptions" => ["cacheFile" => "path", "cacheDisabled" => true], + */ + private $cacheOptions; + + private $base = ""; + + const ROUTE_CACHE_FILENAME = "plugins_toplevel_routes.php"; + + /** + * TopLevelRouter constructor. + * @param array $cacheOptions + */ + public function __construct($cacheOptions = []){ + $this->cacheOptions = array_merge([ + "cacheDisabled" => AJXP_SKIP_CACHE, + "cacheFile" => AJXP_CACHE_DIR."/".TopLevelRouter::ROUTE_CACHE_FILENAME + ], $cacheOptions); + } + + + /** + * @param string $base Base URI (empty string if "/"). + * @param \FastRoute\RouteCollector $r + */ + public function configureRoutes($base, RouteCollector &$r){ + + $allMethods = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH', 'PROPFIND', 'PROPPATCH', 'MKCOL', 'COPY', 'MOVE', 'LOCK', 'UNLOCK']; + $file = AJXP_INSTALL_PATH."/".AJXP_PLUGINS_FOLDER."/core.ajaxplorer/routes.json"; + $textContent = file_get_contents($file); + $textContent = str_replace("%PUBLIC_BASEURI%", ConfService::getGlobalConf("PUBLIC_BASEURI"), $textContent); + $textContent = str_replace("%WEBDAV_BASEURI%", ConfService::getGlobalConf("WEBDAV_BASEURI"), $textContent); + $routes = json_decode($textContent, true); + foreach ($routes as $short => $data){ + $methods = $data["methods"] == "*" ? $allMethods : $data["methods"]; + foreach($data["routes"] as $route){ + $data["short"] = $short; + $r->addRoute($methods, $this->base.$route, $data); + } + } + + } + + /** + * Simple parser to get URI + * @param ServerRequestInterface $request + * @return string + */ + public function getURIForRequest(ServerRequestInterface $request){ + + $uri = $request->getServerParams()['REQUEST_URI']; + // Strip query string (?foo=bar) and decode URI + if (false !== $pos = strpos($uri, '?')) { + $uri = substr($uri, 0, $pos); + } + return rawurldecode($uri); + } + + /** + * @throws PydioException + */ + public function route(){ + + $request = ServerRequestFactory::fromGlobals(); + $this->base = rtrim(PathUtils::forwardSlashDirname($request->getServerParams()["SCRIPT_NAME"]), "/"); + + $dispatcher = \FastRoute\cachedDispatcher(function(RouteCollector $r) { + $this->configureRoutes($this->base, $r); + }, $this->cacheOptions); + + $httpMethod = $request->getServerParams()['REQUEST_METHOD']; + $uri = $this->getURIForRequest($request); + $routeInfo = $dispatcher->dispatch($httpMethod, $uri); + + switch ($routeInfo[0]) { + case Dispatcher::FOUND: + $data = $routeInfo[1]; + if(isSet($data["path"])){ + require_once (AJXP_INSTALL_PATH."/".$data["path"]); + } + call_user_func(array($data["class"], $data["method"]), $this->base, $data["short"], $routeInfo[2]); + break; + case Dispatcher::NOT_FOUND: + default: + //throw new PydioException("Oups, could not find any valid route for ".$uri.", method was was ".$httpMethod); + header("HTTP/1.0 404 Not Found"); + echo file_get_contents(AJXP_INSTALL_PATH . "/plugins/gui.ajax/res/html/404.html"); + die(); + } + + } + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Wopi/AuthFrontend.php b/core/src/core/src/pydio/Core/Http/Wopi/AuthFrontend.php new file mode 100644 index 0000000000..401ea95d23 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Wopi/AuthFrontend.php @@ -0,0 +1,162 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Wopi; + +use JWT; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\ApiKeysService; +use Pydio\Auth\Frontend\Core\AbstractAuthFrontend; +use Pydio\Conf\Sql\SqlConfDriver; +use Pydio\Log\Core\Logger; +use Zend\Diactoros\UploadedFile; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Class Pydio\Core\Http\Wopi\WopiJWTAuthFrontend; + */ +class AuthFrontend extends AbstractAuthFrontend +{ + const NOT_FOUND = ""; + + /** + * @var SqlConfDriver $storage + */ + var $storage; + + /** + * @param $httpVars + * @param $varName + * @return string + */ + function detectVar(&$httpVars, $varName) + { + if (isSet($httpVars[$varName])) return $httpVars[$varName]; + if (isSet($_SERVER["HTTP_PYDIO_" . strtoupper($varName)])) return $_SERVER["HTTP_" . strtoupper($varName)]; + return ""; + } + + function retrieveParams(ServerRequestInterface &$request, ResponseInterface &$response) { + + /** @var ContextInterface $context */ + $action = $request->getAttribute("action"); + + $httpVars = $request->getParsedBody(); + + $jwt = $this->detectVar($httpVars, "access_token"); + if (empty($jwt)) { + return false; + } + + // We have an access token - decode + $payload = JWT::decode($jwt); + + $httpVars["auth_token"] = $payload->token; + $httpVars["auth_hash"] = $payload->hash; + + // NOT GREAT - WE REMOVE /contents from the uri to ensure that the auth_hash works fine + $uri = $request->getUri(); + $path = str_replace("/contents", "", $uri->getPath()); + $uri = $uri->withPath($path); + + $_SERVER["REQUEST_URI"] = $uri->getPath() . '?' . $uri->getQuery(); + + // Handle upload case + if ($action == "upload") { + $stream = $request->getBody(); + + $size = (int)$request->getHeader("Content-Length")[0]; + + $uploadedFile = new UploadedFile( + $stream, + $size, + 0, + basename($path) + ); + + $request = $request->withUploadedFiles(["userfile_0" => $uploadedFile]); + } + + $request = $request + ->withUri($uri) + ->withParsedBody($httpVars); + } + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @param bool $isLast + * @return bool + */ + function tryToLogUser(ServerRequestInterface &$request, ResponseInterface &$response, $isLast = false) { + + // This plugin is depending on other authfront having found the current user + /** @var ContextInterface $context */ + $context = $request->getAttribute("ctx"); + if (!$context->hasUser()) { + return false; + } + + $currentUser = $context->getUser(); + + $httpVars = $request->getParsedBody(); + $jwt = $this->detectVar($httpVars, "access_token"); + if (empty($jwt)) { + return false; + } + + // We have an access token - decode + $payload = JWT::decode($jwt); + + if (!isset($payload->token) || !isset($payload->task)) { + return false; + } + + // We have a token - retrieve private signature + $token = $payload->token; + $task = $payload->task; + + $key = ApiKeysService::findPairForAdminTask($task, $currentUser->getId()); + + if ($key["t"] !== $token) { + return false; + } + + $signature = $key["p"]; + + if ($signature == self::NOT_FOUND) { + return false; + } + + // We have a signature - verify the payload + try { + JWT::decode($jwt, $signature, ['HS256']); + } catch (\Exception $e) { + return false; + } + + // We're through + return true; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Wopi/AuthMiddleware.php b/core/src/core/src/pydio/Core/Http/Wopi/AuthMiddleware.php new file mode 100644 index 0000000000..d3ed121eed --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Wopi/AuthMiddleware.php @@ -0,0 +1,116 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Wopi; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Auth\Frontend\Core\FrontendsLoader; +use Pydio\Core\Exception\NoActiveWorkspaceException; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Exception\WorkspaceForbiddenException; + +use Pydio\Core\Model\Context; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Services\ConfService; + +use Pydio\Core\Services\RolesService; +use Pydio\Core\Services\UsersService; +use Pydio\Core\Services\ApplicationState; +use Pydio\Log\Core\Logger; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Authentication middleware used in Rest context + * @package Pydio\Core\Http\Rest + */ +class AuthMiddleware +{ + + /** + * @param ServerRequestInterface $requestInterface + * @param \Psr\Http\Message\ResponseInterface $responseInterface + * @return \Psr\Http\Message\ResponseInterface + * @param callable|null $next + * @throws PydioException + */ + public static function handleRequest(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface, callable $next = null){ + + $driverImpl = ConfService::getAuthDriverImpl(); + PluginsService::getInstance(Context::emptyContext())->setPluginUniqueActiveForType("auth", $driverImpl->getName(), $driverImpl); + + $jwtFrontend = new AuthFrontend("jwt-auth-frontend", ""); + $jwtFrontend->retrieveParams($requestInterface, $responseInterface); + + $response = FrontendsLoader::frontendsAsAuthMiddlewares($requestInterface, $responseInterface); + if($response != null) { + return $response; + } + + $response = $jwtFrontend->tryToLogUser($requestInterface, $responseInterface); + if(!$response != null) { + $responseInterface = $responseInterface->withStatus(401); + $responseInterface->getBody()->write('You are not authorized to access this API.'); + return $responseInterface; + } + + /** @var ContextInterface $ctx */ + $ctx = $requestInterface->getAttribute("ctx"); + + if(!$ctx->hasUser()){ + $responseInterface = $responseInterface->withStatus(401); + $responseInterface->getBody()->write('You are not authorized to access this API.'); + return $responseInterface; + } + + $repoID = $requestInterface->getAttribute("repository_id"); + if($repoID == 'pydio'){ + $userRepositories = UsersService::getRepositoriesForUser($ctx->getUser()); + if(empty($userRepositories)){ + throw new NoActiveWorkspaceException(); + } + $repo = array_shift($userRepositories); + }else{ + try{ + $repo = UsersService::getRepositoryWithPermission($ctx->getUser(), $repoID); + }catch (WorkspaceForbiddenException $w){ + $responseInterface = $responseInterface->withStatus(401); + $responseInterface->getBody()->write('You are not authorized to access this API.'); + return $responseInterface; + } + } + + $ctx->setRepositoryObject($repo); + $requestInterface = $requestInterface->withAttribute("ctx", $ctx); + + Logger::updateContext($ctx); + + if(UsersService::usersEnabled() && ApplicationState::detectApplicationFirstRun()){ + RolesService::bootSequence(); + } + + return Server::callNextMiddleWare($requestInterface, $responseInterface, $next); + } + + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Wopi/Middleware.php b/core/src/core/src/pydio/Core/Http/Wopi/Middleware.php new file mode 100644 index 0000000000..2944244bc8 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Wopi/Middleware.php @@ -0,0 +1,133 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Wopi; + +use \Psr\Http\Message\ServerRequestInterface; +use \Psr\Http\Message\ResponseInterface; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Model\NodesList; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Exception\RouteNotFoundException; +use Pydio\Core\Http\Message\Message; +use Pydio\Core\Http\Middleware\SapiMiddleware; +use Pydio\Core\Http\Response\SerializableResponseStream; +use Zend\Diactoros\Response\SapiEmitter; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class RestWopiMiddleware + * Specific middleware to handle Wopi actions + * @package Pydio\Core\Http\Wopi + */ +class Middleware extends SapiMiddleware +{ + protected $base; + + /** + * RestWopiMiddleware constructor. + * @param $base + */ + public function __construct($base) + { + $this->base = $base; + } + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @throws PydioException + */ + protected function parseRequestRouteAndParams(ServerRequestInterface &$request, ResponseInterface &$response){ + + $router = new Router($this->base); + if(!$router->route($request, $response)) { + throw new RouteNotFoundException(); + } + } + + /** + * Output the response to the browser, if no headers were already sent. + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @return void + */ + public function emitResponse(ServerRequestInterface $request, ResponseInterface $response) { + + if($response !== false && $response->getBody() && $response->getBody() instanceof SerializableResponseStream){ + + /** + * @var SerializableResponseStream $body; + */ + $status = &$response->getStatusCode(); + + // Modifying the results + if ($status == 200) { + $body = &$response->getBody(); + + /** @var NodesList $originalData */ + $originalData = $body->getChunks()[0]; + + /** @var AJXP_Node $node */ + $node = $originalData->getChildren()[0]; + + // We modify the result to have the correct format required by the api + $x = new SerializableResponseStream(); + $meta = $node->getNodeInfoMeta(); + $userId = $node->getUser()->getId(); + $data = [ + "BaseFileName" => $node->getLabel(), + "OwnerId" => $userId, + "Size" => $meta["bytesize"], + "UserId" => $userId, + "Version" => "" . $meta["ajxp_modiftime"] + ]; + + $x->addChunk(new Message($data)); + $response = $response->withBody($x); + + } + + $body = &$response->getBody(); + + $params = $request->getParsedBody(); + $forceXML = false; + + if(isSet($params["format"]) && $params["format"] == "xml"){ + $forceXML = true; + } + + if(($request->hasHeader("Accept") && $request->getHeader("Accept")[0] == "text/xml" ) || $forceXML){ + $body->setSerializer(SerializableResponseStream::SERIALIZER_TYPE_XML); + $response = $response->withHeader("Content-type", "text/xml; charset=UTF-8"); + } else { + $body->setSerializer(SerializableResponseStream::SERIALIZER_TYPE_JSON); + $response = $response->withHeader("Content-type", "application/json; charset=UTF-8"); + } + } + + if($response !== false && ($response->getBody()->getSize() || $response instanceof EmptyResponse) || $response->getStatusCode() != 200) { + $emitter = new SapiEmitter(); + $emitter->emit($response); + } + } + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Wopi/Router.php b/core/src/core/src/pydio/Core/Http/Wopi/Router.php new file mode 100644 index 0000000000..c343ed8394 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Wopi/Router.php @@ -0,0 +1,170 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Http\Wopi; + +use FastRoute\Dispatcher; +use FastRoute\RouteCollector; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class WopiRouter + * Simple router for /wopi/ endpoints + * @package Pydio\Core\Http\Wopi + */ +class Router +{ + /** + * @var array + */ + private $base; + + /** + * @var array + * "cacheOptions" => ["cacheFile" => "path", "cacheDisabled" => true], + */ + private $cacheOptions; + + /** + * ApiRouter constructor. + * @param string $base + * @param array $cacheOptions + */ + public function __construct($base, $cacheOptions = []){ + $this->base = $base; + $this->cacheOptions = array_merge([ + "cacheDisabled" => AJXP_SKIP_CACHE, + "cacheFile" => AJXP_DATA_PATH."/cache/plugins_wopiroutes.php" + ], $cacheOptions); + } + + + /** + * @param RouteCollector $r + */ + public function configureRoutes(RouteCollector &$r){ + + $configObject = json_decode(file_get_contents(AJXP_INSTALL_PATH . "/" . AJXP_DOCS_FOLDER . "/wopi.json"), true); + + foreach ($configObject["paths"] as $path => $methods){ + foreach($methods as $method => $apiData){ + if(preg_match('/\{path\}/', $path)){ + if(isset($apiData["parameters"]["0"]['$ref']) && preg_match('/Optional$/', $apiData["parameters"]["0"]['$ref'])){ + $path = str_replace("{path}", "{path:.*}", $path); + }else{ + $path = str_replace("{path}", "{path:.+}", $path); + } + } + $path = str_replace("{roleId}", "{roleId:.+}", $path); + $r->addRoute(strtoupper($method), $this->base . $path , $apiData); + } + } + } + + /** + * Get Path component of the URI, without query parameters + * @param ServerRequestInterface $request + * @return string + */ + public function getURIForRequest(ServerRequestInterface $request){ + + $uri = $request->getServerParams()['REQUEST_URI']; + // Strip query string (?foo=bar) and decode URI + if (false !== $pos = strpos($uri, '?')) { + $uri = substr($uri, 0, $pos); + } + return rawurldecode($uri); + } + + /** + * Find a route in api definitions + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + public function route(ServerRequestInterface &$request, ResponseInterface &$response){ + + $dispatcher = \FastRoute\cachedDispatcher(function(RouteCollector $r) { + + $this->configureRoutes($r); + + }, $this->cacheOptions); + + $httpMethod = $request->getServerParams()['REQUEST_METHOD']; + $uri = $this->getURIForRequest($request); + $routeInfo = $dispatcher->dispatch($httpMethod, $uri); + + switch ($routeInfo[0]) { + case Dispatcher::NOT_FOUND: + $response = $response->withStatus(404); + break; + case Dispatcher::METHOD_NOT_ALLOWED: + //$allowedMethods = $routeInfo[1]; + //$response = $response->withStatus(405); + break; + case Dispatcher::FOUND: + $apiData = $routeInfo[1]; + $vars = $routeInfo[2]; + + $apiUri = preg_replace('/^'.preg_quote($this->base, '/').'/', '', $uri); + + $request = $request->withAttribute("api_uri", $apiUri); + $repoId = $this->findRepositoryInParameters($request, $vars); + + $request = $request + ->withAttribute("action", $apiData["x-pydio-action"]) + ->withAttribute("repository_id", $repoId) + ->withAttribute("rest_base", $this->base) + ->withAttribute("api", "v2") // We want the same behaviour as for the v2 api + ->withParsedBody(array_merge($request->getParsedBody(), $vars)); + + return true; + default: + break; + } + + return false; + } + + /** + * Analyze URI and parameters to guess the current workspace + * + * @param ServerRequestInterface $request + * @param array $pathVars + * @return mixed|string + */ + protected function findRepositoryInParameters(ServerRequestInterface $request, array $pathVars){ + $params = array_merge($request->getParsedBody(), $pathVars); + if (preg_match('/^\/admin\//', $request->getAttribute("api_uri"))) { + return "ajxp_conf"; + }else if(isSet($params["workspaceId"])){ + return $params["workspaceId"]; + }else if(isSet($params["path"]) && strpos($params["path"], "/") !== false){ + return array_shift(explode("/", ltrim($params["path"], "/"))); + } + // If no repo ID was found, return default repo id "pydio". + return "pydio"; + } + + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Http/Wopi/Server.php b/core/src/core/src/pydio/Core/Http/Wopi/Server.php new file mode 100644 index 0000000000..98dbb05824 --- /dev/null +++ b/core/src/core/src/pydio/Core/Http/Wopi/Server.php @@ -0,0 +1,54 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ + +namespace Pydio\Core\Http\Wopi; + +use Pydio\Core\Http\Server as HttpServer; +use Pydio\Core\Services\ApplicationState; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class RestWopiServer + * Dedicated server for Wopi implementation (must start with /wopi). + * @package Pydio\Core\Http\Wopi + */ +class Server extends HttpServer +{ + /** + * RestWopiServer constructor. + * @param $base + * @param array $additionalAttributes + */ + public function __construct($base, $additionalAttributes = []) + { + parent::__construct($base, $additionalAttributes); + ApplicationState::setSapiRestBase($base); + } + + protected function stackMiddleWares() + { + $this->middleWares->push(array("Pydio\\Core\\Controller\\Controller", "registryActionMiddleware")); + $this->middleWares->push(array("Pydio\\Core\\Http\\Wopi\\AuthMiddleware", "handleRequest")); + $this->topMiddleware = new Middleware($this->base); + $this->middleWares->push(array($this->topMiddleware, "handleRequest")); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Model/Context.php b/core/src/core/src/pydio/Core/Model/Context.php new file mode 100644 index 0000000000..ff5f28a575 --- /dev/null +++ b/core/src/core/src/pydio/Core/Model/Context.php @@ -0,0 +1,247 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Model; + + +use Pydio\Core\Services\RepositoryService; +use Pydio\Core\Services\UsersService; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class Context + * Main implementation of ContextInterface, propagating a user and a repository all along the application + * @package Pydio\Core\Model + */ +class Context implements ContextInterface +{ + + /** + * @var string + */ + private $userId; + + /** + * @var + */ + private $userObject; + + /** + * @var + */ + private $repositoryId; + + /** + * @var + */ + private $repositoryObject; + + /** + * Context constructor. + * @param string $userId + * @param string $repositoryId + */ + public function __construct($userId = null, $repositoryId = null) + { + if($userId !== null) { + $this->userId = $userId; + } + if($repositoryId !== null){ + $this->repositoryId = $repositoryId; + } + } + + /** + * @param $userObject + * @param $repositoryObject + * @return Context + */ + public static function contextWithObjects($userObject, $repositoryObject){ + $ctx = new Context(); + $ctx->setUserObject($userObject); + $ctx->setRepositoryObject($repositoryObject); + return $ctx; + } + + /** + * @return Context + */ + public static function emptyContext(){ + return new Context(); + } + + /** + * @param $userId + * @return ContextInterface + */ + public function withUserId($userId){ + return new Context($userId, $this->repositoryId); + } + + /** + * @param $repositoryId + * @return ContextInterface + */ + public function withRepositoryId($repositoryId){ + return new Context($this->userId, $repositoryId); + } + + + /** + * @return boolean + */ + public function hasUser() + { + return !empty($this->userId); + } + + /** + * @return UserInterface|null + */ + public function getUser() + { + if(isSet($this->userObject)){ + return $this->userObject; + } + if(isSet($this->userId)){ + $this->userObject = UsersService::getUserById($this->userId, false); + return $this->userObject; + } + return null; + } + + /** + * @param string $userId + */ + public function setUserId($userId) + { + $this->userId = $userId; + } + + /** + * @param UserInterface $user + */ + public function setUserObject($user) + { + $this->userObject = $user; + if($user !== null){ + $this->userId = $user->getId(); + } + } + + /** + * Set userId and userObject to null + */ + public function resetUser(){ + $this->userId = null; + $this->userObject = null; + } + + /** + * Builds pydio://user@repository url + * @return string + */ + public function getUrlBase() + { + $uId = $this->hasUser() ? $this->getUser()->getId() : null; + return "pydio://".$uId."@".$this->getRepositoryId(); + } + + /** + * @return boolean + */ + public function hasRepository() + { + return ($this->repositoryId !== null); + } + + /** + * @return RepositoryInterface|null + */ + public function getRepository() + { + if(isSet($this->repositoryId)){ + if(!isSet($this->repositoryObject)){ + $this->repositoryObject = RepositoryService::getRepositoryById($this->repositoryId); + } + return $this->repositoryObject; + } + return null; + } + + /** + * @param string $repositoryId + */ + public function setRepositoryId($repositoryId) + { + $this->repositoryId = $repositoryId; + } + + /** + * @return string|null + */ + public function getRepositoryId() + { + return $this->repositoryId; + } + + /** + * @param RepositoryInterface $repository + */ + public function setRepositoryObject($repository) + { + $this->repositoryObject = $repository; + if($repository !== null){ + $this->repositoryId = $repository->getId(); + } + } + + /** + * Set repositoryId and repositoryObject to null + */ + public function resetRepository() + { + $this->repositoryId = $this->repositoryObject = null; + } + + /** + * Build a unique string identifier for this context + * @return string + */ + public function getStringIdentifier() + { + $u = $this->userId == null ? "shared" : $this->userId; + $a = "norepository"; + $r = $this->getRepository(); + if($r !== null){ + $a = $r->getSlug(); + } + return $u.":".$a; + } + + /** + * @return bool + */ + public function isEmpty() + { + return !$this->hasRepository() && !$this->hasUser(); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Model/ContextInterface.php b/core/src/core/src/pydio/Core/Model/ContextInterface.php new file mode 100644 index 0000000000..50186d32f9 --- /dev/null +++ b/core/src/core/src/pydio/Core/Model/ContextInterface.php @@ -0,0 +1,110 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Model; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Interface ContextInterface + * @package Pydio\Core\Model + */ +interface ContextInterface +{ + /** + * @return bool + */ + public function isEmpty(); + + /** + * @return boolean + */ + public function hasUser(); + + /** + * @return UserInterface|null + */ + public function getUser(); + + /** + * @param string $userId + */ + public function setUserId($userId); + + /** + * @param UserInterface $user + */ + public function setUserObject($user); + + public function resetUser(); + + /** + * Build Url base pydio:://user'@'repoId + * @return string + */ + public function getUrlBase(); + + /** + * @return boolean + */ + public function hasRepository(); + + /** + * @return RepositoryInterface|null + */ + public function getRepository(); + + /** + * @param string $repositoryId + */ + public function setRepositoryId($repositoryId); + + /** + * @return string|null + */ + public function getRepositoryId(); + + /** + * @param RepositoryInterface $repository + */ + public function setRepositoryObject($repository); + + /** + * @return mixed + */ + public function resetRepository(); + + /** + * @return string + */ + public function getStringIdentifier(); + + /** + * @param $userId + * @return ContextInterface + */ + public function withUserId($userId); + + /** + * @param $repositoryId + * @return ContextInterface + */ + public function withRepositoryId($repositoryId); +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Model/ContextProviderInterface.php b/core/src/core/src/pydio/Core/Model/ContextProviderInterface.php new file mode 100644 index 0000000000..ec0bdf45fa --- /dev/null +++ b/core/src/core/src/pydio/Core/Model/ContextProviderInterface.php @@ -0,0 +1,37 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Model; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Interface ContextProviderInterface + * Objects that implement this interface can provide a valid context + * @package Pydio\Core\Model + */ +interface ContextProviderInterface +{ + /** + * Returns a valid context + * @return ContextInterface + */ + public function getContext(); +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Model/FilteredRepositoriesList.php b/core/src/core/src/pydio/Core/Model/FilteredRepositoriesList.php new file mode 100644 index 0000000000..f85d1f1b74 --- /dev/null +++ b/core/src/core/src/pydio/Core/Model/FilteredRepositoriesList.php @@ -0,0 +1,159 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Model; + +use Pydio\Core\Controller\Controller; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\RepositoryService; +use Pydio\Log\Core\Logger; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class FilteredRepositoriesList + * Set of repositories filtered on a specific scope + * @package Pydio\Core\Model + */ +class FilteredRepositoriesList +{ + + /** @var string */ + private $scope; + /** @var bool */ + private $includeShared = true; + /** @var UserInterface */ + private $user; + /** @var bool */ + private $details = false; + + /** + * FilteredRepositoriesList constructor. + * @param UserInterface $userInterface + */ + public function __construct(UserInterface $userInterface = null) { + if(!empty($userInterface)){ + $this->user = $userInterface; + $this->scope = "user"; + }else{ + $this->scope = "all"; + } + } + + /** + * @param $bool + */ + public function setDetails($bool){ + $this->details = $bool; + } + + /** + * @param $bool + */ + public function setIncludeShared($bool){ + $this->includeShared = $bool; + } + + /** + * @return RepositoryInterface[] + */ + public function load() + { + // APPEND CONF FILE REPOSITORIES + $objList = array(); + if($this->user != null){ + $l = $this->user->getLock(); + if( !empty($l)) return $objList; + } + $statics = RepositoryService::getStaticRepositories(); + foreach ($statics as $index=>$repository) { + if(!empty($this->user) + && !RepositoryService::repositoryIsAccessible($repository, $this->user, $this->details, $this->includeShared)){ + continue; + } + $objList["".$repository->getId()] = $repository; + } + // LOAD FROM DRIVER + $confDriver = ConfService::getConfStorageImpl(); + if($this->scope == "user"){ + $acls = array(); + if($this->user != null){ + $acls = $this->user->getMergedRole()->listAcls(true); + } + if(!count($acls)) { + $drvList = array(); + }else{ + $criteria = array( + "uuid" => array_keys($acls) + ); + $drvList = RepositoryService::listRepositoriesWithCriteria($criteria, $count); + } + }else{ + if($this->includeShared){ + $drvList = $confDriver->listRepositories(); + }else{ + $drvList = RepositoryService::listRepositoriesWithCriteria(array( + "owner_user_id" => AJXP_FILTER_EMPTY + ), $count); + } + } + if (is_array($drvList)) { + /** + * @var $drvList \Pydio\Access\Core\Model\Repository[] + */ + foreach ($drvList as $repoId=>$repoObject) { + $driver = PluginsService::getInstance(Context::emptyContext())->getPluginByTypeName("access", $repoObject->getAccessType()); + if (!is_object($driver) || !$driver->isEnabled()) { + unset($drvList[$repoId]); + } else { + $repoObject->setId($repoId); + $drvList[$repoId] = $repoObject; + } + if($repoObject->hasParent() && !RepositoryService::findRepositoryByIdOrAlias($repoObject->getParentId())){ + Logger::error(__CLASS__, __FUNCTION__, "Disabling repository ".$repoObject->getSlug()." as parent cannot be correctly loaded."); + unset($drvList[$repoId]); + } + } + foreach($drvList as $key => $value){ + + // Refilter with internal options + if(isSet($this->user) && !RepositoryService::repositoryIsAccessible($value, $this->user, $this->details, $this->includeShared)){ + continue; + } + + $objList[$key] = $value; + } + } + $args = array(&$objList, $this->scope, $this->user, $this->includeShared); + Controller::applyIncludeHook("repository.list", $args); + return $objList; + } + + /** @return array */ + public function loadLabels(){ + $repos = $this->load(); + $result = []; + foreach($repos as $repo){ + $result[$repo->getId()] = $repo->getDisplay(); + } + return $result; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Model/RepositoryInterface.php b/core/src/core/src/pydio/Core/Model/RepositoryInterface.php new file mode 100644 index 0000000000..f1369cbed7 --- /dev/null +++ b/core/src/core/src/pydio/Core/Model/RepositoryInterface.php @@ -0,0 +1,297 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Model; + +defined('AJXP_EXEC') or die('Access not allowed'); + +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Access\Core\Filter\ContentFilter; +use Pydio\Conf\Core\IGroupPathProvider; +use Pydio\Core\Exception\RepositoryLoadException; + + +/** + * The basic abstraction of a data store. Can map a FileSystem, but can also map data from a totally + * different source, like the application configurations, a mailbox, etc. + * @package Pydio + * @subpackage Core + */ +interface RepositoryInterface extends IGroupPathProvider +{ + + /** + * @param ContextInterface $ctx + * @return AbstractAccessDriver + * @throws RepositoryLoadException + */ + public function getDriverInstance(ContextInterface $ctx); + + /** + * @param AbstractAccessDriver $driverInstance + */ + public function setDriverInstance($driverInstance); + + /** + * @param ContentFilter $contentFilter + */ + public function setContentFilter($contentFilter); + + /** + * Check if a ContentFilter is set or not + * @return bool + */ + public function hasContentFilter(); + + /** + * @return ContentFilter + */ + public function getContentFilter(); + + /** + * Create a shared version of this repository + * @param string $newLabel + * @param array $newOptions + * @param string $parentId + * @param string $owner + * @param string $uniqueUser + * @return RepositoryInterface + */ + public function createSharedChild($newLabel, $newOptions, $parentId, $owner, $uniqueUser = null); + + /** + * Create a child from this repository if it's a template + * @param string $newLabel + * @param array $newOptions + * @param string $creator + * @param string $uniqueUser + * @return RepositoryInterface + */ + public function createTemplateChild($newLabel, $newOptions, $creator = null, $uniqueUser = null); + + /** + * Recompute uuid + * @return bool + */ + public function upgradeId(); + + /** + * Get a uuid + * @param bool $serial + * @return string + */ + public function getUniqueId($serial = false); + + /** + * Alias for this repository + * @return string + */ + public function getSlug(); + + /** + * Use the slugify function to generate an alias from the label + * @param string $slug + * @return void + */ + public function setSlug($slug = null); + + /** + * Add options + * @param $oName + * @param $oValue + * @return void + */ + public function addOption($oName, $oValue); + + /** + * @param ContextInterface $ctx + * @param string $oName + * @param null $default + * @return mixed + */ + public function getContextOption(ContextInterface $ctx, $oName, $default=null); + + /** + * @param string $oName + * @return mixed|string + * @throws \Exception + */ + public function getSafeOption($oName); + + /** + * Get the options that already have a value + * @return array + */ + public function getOptionsDefined(); + + /** + * Get the DEFAULT_RIGHTS option + * @return string + */ + public function getDefaultRight(); + + /** + * The the access driver type + * @return String + */ + public function getAccessType(); + + /** + * The label of this repository + * @return String + */ + public function getDisplay(); + + /** + * @return string + */ + public function getId(); + + /** + * @return boolean + */ + public function getCreate(); + + /** + * @param boolean $create + */ + public function setCreate($create); + + /** + * @param String $accessType + */ + public function setAccessType($accessType); + + /** + * @param String $display + */ + public function setDisplay($display); + + /** + * @param int $id + */ + public function setId($id); + + /** + * @return bool + */ + public function isWriteable(); + + /** + * @param bool $w + */ + public function setWriteable($w); + + /** + * @param string $id + */ + public function setDisplayStringId($id); + + /** + * @param string $repoParentId + * @param string|null $ownerUserId + * @param string|null $childUserId + */ + public function setOwnerData($repoParentId, $ownerUserId = null, $childUserId = null); + + /** + * @return string|null + */ + public function getOwner(); + + /** + * @return string|null + */ + public function getParentId(); + + /** + * @return null|RepositoryInterface + */ + public function getParentRepository(); + + /** + * @return string|null + */ + public function getUniqueUser(); + + /** + * @return bool + */ + public function hasOwner(); + + /** + * @return bool + */ + public function hasParent(); + + /** + * @return bool + */ + public function isTemplate(); + + /** + * @param $bool + */ + public function setInferOptionsFromParent($bool); + + /** + * @return bool + */ + public function getInferOptionsFromParent(); + + /** + * @param String $descriptionText + */ + public function setDescription($descriptionText); + + /** + * @return string + */ + public function getAccessStatus(); + + /** + * @param string $accessStatus + */ + public function setAccessStatus($accessStatus); + + /** + * @return string + */ + public function getRepositoryType(); + + /** + * @param string $repositoryType + */ + public function setRepositoryType($repositoryType); + + /** + * @param bool $public + * @param null $ownerLabel + * @return String + */ + public function getDescription($public = false, $ownerLabel = null); + + /** + * Infer a security scope for this repository. Will determine to whome the messages + * will be broadcasted. + * @return bool|string + */ + public function securityScope(); +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Model/UserInterface.php b/core/src/core/src/pydio/Core/Model/UserInterface.php new file mode 100644 index 0000000000..62f72ef317 --- /dev/null +++ b/core/src/core/src/pydio/Core/Model/UserInterface.php @@ -0,0 +1,290 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Model; + +defined('AJXP_EXEC') or die('Access not allowed'); + +use Pydio\Conf\Core\AJXP_Role; +use Pydio\Conf\Core\IGroupPathProvider; + +/** + * User abstraction, the "conf" driver must provides its own implementation + */ +interface UserInterface extends IGroupPathProvider +{ + /** + * @param bool $hidden + */ + public function setHidden($hidden); + + /** + * @return bool + */ + public function isHidden(); + + /** + * @return string + */ + public function getId(); + + /** + * @param string $id + */ + public function setId($id); + + /** + * @return bool + */ + public function storageExists(); + + /** + * @param AJXP_Role $roleObject + */ + public function addRole($roleObject); + + /** + * @param string $roleId + * @throws \Exception + */ + public function removeRole($roleId); + + /** + * @param $orderedRolesIds array of roles ids + */ + public function updateRolesOrder($orderedRolesIds); + + /** + * @return AJXP_Role[] + */ + public function getRoles(); + + /** + * @return string + */ + public function getProfile(); + + /** + * @param string $profile + */ + public function setProfile($profile); + + /** + * @param string $lockAction + * @throws \Exception + */ + public function setLock($lockAction); + + /** + * @throws \Exception + */ + public function removeLock($lockAction); + + /** + * @param $lockAction + * @return string|false + */ + public function hasLockByName($lockAction); + + /** + * @return string|false + */ + public function getLock(); + + /** + * @return bool + */ + public function isAdmin(); + + /** + * @param bool $boolean + */ + public function setAdmin($boolean); + + /** + * @return bool Whether the user has a parent or not + */ + public function hasParent(); + + /** + * @param string $user A user ID + */ + public function setParent($user); + + /** + * @return string Returns the ID of the parent user + */ + public function getParent(); + + /** + * @param string $repositoryId + * @return bool + */ + public function canRead($repositoryId); + + /** + * @param string $repositoryId + * @return bool + */ + public function canWrite($repositoryId); + + /** + * @param RepositoryInterface|string $idOrObject + * @return boolean + */ + public function canAccessRepository($idOrObject); + + /** + * Test if user can switch to this repository + * + * @param integer $repositoryId + * @return boolean + */ + public function canSwitchTo($repositoryId); + + /** + * @param $prefName + * @return mixed|string + */ + public function getPref($prefName); + + /** + * @param $prefName + * @param $prefValue + */ + public function setPref($prefName, $prefValue); + + /** + * @param string $prefName + * @param string $prefPath + * @param mixed $prefValue + */ + public function setArrayPref($prefName, $prefPath, $prefValue); + + /** + * @param $prefName + * @param $prefPath + * @return mixed|string + */ + public function getArrayPref($prefName, $prefPath); + + /** + * @param $repositoryId + * @param string $path + * @param string $title + * @return + */ + public function addBookmark($repositoryId, $path, $title); + + /** + * @param string $repositoryId + * @param string $path + */ + public function removeBookmark($repositoryId, $path); + + /** + * @param string $repositoryId + * @param string $path + * @param string $title + */ + public function renameBookmark($repositoryId, $path, $title); + + /** + * @return array + */ + public function getBookmarks($repositoryId); + + /** + * Check if the current user can administrate the GroupPathProvider object + * @param IGroupPathProvider $provider + * @return bool + */ + public function canAdministrate(IGroupPathProvider $provider); + + /** + * Check if the current user can assign administration for the GroupPathProvider object + * @param IGroupPathProvider $provider + * @return bool + */ + public function canSee(IGroupPathProvider $provider); + + /** + * Automatically set the group to the current user base + * @param $baseGroup + * @return string + */ + public function getRealGroupPath($baseGroup); + + /** + * @return mixed + */ + public function load(); + + /** + * @param string $context + */ + public function save($context = "superuser"); + + /** + * @param string $key + * @return mixed + */ + public function getTemporaryData($key); + + /** + * @param string $key + * @param mixed $value + * @return mixed + */ + public function saveTemporaryData($key, $value); + + /** + * Rebuild the current merged role + * @throws \Exception + */ + public function recomputeMergedRole(); + + /** + * @return AJXP_Role + */ + public function getMergedRole(); + + /** + * @return AJXP_Role + */ + public function getPersonalRole(); + + /** + * @param AJXP_Role $role + */ + public function updatePersonalRole(AJXP_Role $role); + + /** + * @return bool + * @throws \Exception + */ + public function reloadRolesIfRequired(); + + /** + * @return array + */ + public function getRolesKeys(); + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/PluginFramework/CoreInstanceProvider.php b/core/src/core/src/pydio/Core/PluginFramework/CoreInstanceProvider.php new file mode 100644 index 0000000000..f49bd2a044 --- /dev/null +++ b/core/src/core/src/pydio/Core/PluginFramework/CoreInstanceProvider.php @@ -0,0 +1,36 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\PluginFramework; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Interface CoreInstanceProvider + * @package Pydio\Core\PluginFramework + */ +interface CoreInstanceProvider +{ + /** + * @param PluginsService|null $pluginServiceInstance + * @return Plugin + */ + public function getImplementation($pluginServiceInstance = null); +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/PluginFramework/Plugin.php b/core/src/core/src/pydio/Core/PluginFramework/Plugin.php new file mode 100644 index 0000000000..df3f5f3fe0 --- /dev/null +++ b/core/src/core/src/pydio/Core/PluginFramework/Plugin.php @@ -0,0 +1,1114 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\PluginFramework; + +use Pydio\Access\Core\MetaStreamWrapper; +use Pydio\Core\Model\Context; +use Pydio\Core\Model\ContextInterface; + + +use Pydio\Core\Services\ConfService; +use Pydio\Core\Utils\Vars\XMLFilter; +use Pydio\Log\Core\Logger; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +if (!defined('LOG_LEVEL_DEBUG')) { + define("LOG_LEVEL_DEBUG", "Debug"); + define("LOG_LEVEL_INFO", "Info"); + define("LOG_LEVEL_NOTICE", "Notice"); + define("LOG_LEVEL_WARNING", "Warning"); + define("LOG_LEVEL_ERROR", "Error"); +} + +/** + * The basic concept of plugin. Only needs a manifest.xml file. + * @package Pydio + * @subpackage Core + */ +class Plugin implements \Serializable +{ + protected $baseDir; + protected $id; + protected $name; + protected $type; + /** + * XPath query + * + * @var \DOMXPath + */ + private $xPath; + protected $manifestLoaded = false; + protected $externalFilesAppended = false; + protected $enabled; + protected $registryContributions = []; + protected $contributionsLoaded = false; + /** + * @var array + */ + protected $options; // can be passed at init time + protected $pluginConf; // can be passed at load time + protected $pluginConfDefinition; + protected $dependencies; + protected $extensionsDependencies; + protected $streamData; + protected $mixins = []; + protected $cachedXPathResults = []; + public $loadingState = ""; + /** + * The manifest.xml loaded + * + * @var \DOMDocument + */ + protected $manifestDoc; + + /** + * Internally store XML during serialization state. + * + * @var string + */ + private $manifestXML; + + private $serializableAttributes = [ + "baseDir", + "id", + "name", + "type", + "manifestLoaded", + "externalFilesAppended", + "enabled", + "registryContributions", + "contributionsLoaded", + "mixins", + "streamData", + "options", "pluginConf", "pluginConfDefinition", "dependencies", "extensionsDependencies", "loadingState", "manifestXML", "cachedXPathResults"]; + + /** + * Construction method + * + * @param string $id + * @param string $baseDir + */ + public function __construct($id, $baseDir) + { + $this->baseDir = $baseDir; + $this->id = $id; + $split = explode(".", $id); + $this->type = $split[0]; + $this->name = $split[1]; + $this->dependencies = []; + $this->extensionsDependencies = []; + } + + /** + * Make sure to clone the XML manifest document, otherwise if the plugin + * changes it dynamically, it can mess up things. + * @inheritdoc + */ + public function __clone() + { + if($this->manifestDoc !== null){ + $this->manifestDoc = clone $this->manifestDoc; + $this->reloadXPath(); + } + } + + /** + * @param $pluginId + * @return string + */ + public static function getWorkDirForPluginId($pluginId) { + return AJXP_DATA_PATH.DIRECTORY_SEPARATOR."plugins".DIRECTORY_SEPARATOR.$pluginId; + } + + /** + * @param bool $check + * @return string + * @throws \Exception + */ + protected function getPluginWorkDir($check = false) + { + $d = self::getWorkDirForPluginId($this->getId()); + if(!$check) return $d; + if (!is_dir($d)) { + $res = @mkdir($d, 0755, true); + if(!$res) throw new \Exception("Error while creating plugin directory for ".$this->getId()); + } + return $d; + } + + /** + * @param bool $shared + * @param bool $check + * @return string + * @throws \Exception + */ + protected function getPluginCacheDir($shared = false, $check = false) + { + $d = ($shared ? AJXP_SHARED_CACHE_DIR : AJXP_CACHE_DIR).DIRECTORY_SEPARATOR."plugins".DIRECTORY_SEPARATOR.$this->getId(); + if(!$check) return $d; + if (!is_dir($d)) { + $res = @mkdir($d, 0755, true); + if(!$res) throw new \Exception("Error while creating plugin cache directory for ".$this->getId()); + } + return $d; + } + + /** + * @return \DOMXPath + */ + protected function getXPath(){ + $this->unserializeManifest(); + return $this->xPath; + } + + /** + * @param ContextInterface $ctx + * @param array $options + * @return void + */ + public function init(ContextInterface $ctx, $options = []) + { + $this->options = array_merge($this->loadOptionsDefaults(), $options); + } + + /** + * @param ContextInterface $ctx + * @param $optionName + * @return mixed|null + */ + protected function getContextualOption(ContextInterface $ctx, $optionName){ + + if(!is_array($this->options)) $this->options = []; + $merged = $this->options; + if(is_array($this->pluginConf)) $merged = array_merge($merged, $this->pluginConf); + + if ($ctx->hasUser()) { + + $loggedUser = $ctx->getUser(); + if($ctx->hasRepository()){ + $repository = $ctx->getRepository(); + $repositoryScope = $ctx->getRepositoryId(); + }else{ + $repositoryScope = AJXP_REPO_SCOPE_ALL; + } + + $test = $loggedUser->getMergedRole()->filterParameterValue( + $this->getId(), + $optionName, + $repositoryScope, + isSet($merged[$optionName]) ? $merged[$optionName] : null + ); + + if (isSet($repository) && $repository->hasParent()) { + $retest = $loggedUser->getMergedRole()->filterParameterValue( + $this->getId(), + $optionName, + $repository->getParentId(), + null + ); + if($retest !== null) { + $test = $retest; + + }else if($repository->hasOwner()) { + // Test shared scope + $retest = $loggedUser->getMergedRole()->filterParameterValue( + $this->getId(), + $optionName, + AJXP_REPO_SCOPE_SHARED, + null + ); + if($retest !== null) { + $test = $retest; + } + } + } + return $test; + } else { + return isSet($merged[$optionName]) ? $merged[$optionName] : null; + } + } + /** + * Perform initialization checks, and throw exception if problems found. + * @throws \Exception + */ + public function performChecks() + { + } + /** + * @return bool + */ + public function isEnabled() + { + if(isSet($this->enabled)) return $this->enabled; + $this->enabled = true; + if(isSet($this->cachedXPathResults["@enabled"])){ + $value = $this->cachedXPathResults["@enabled"]; + if($value === "false"){ + $this->enabled = false; + } + return $this->enabled; + } + if ($this->manifestLoaded) { + if($this->manifestXML != null) $this->unserializeManifest(); + $l = $this->xPath->query("@enabled", $this->manifestDoc->documentElement); + if ($l->length && $l->item(0)->nodeValue === "false") { + $this->enabled = false; + } + } + return $this->enabled; + } + + /** + * Main function for loading all the nodes under registry_contributions. + * @param ContextInterface $ctx + * @param bool $dry + */ + protected function loadRegistryContributions(ContextInterface $ctx, $dry = false) + { + if($this->manifestXML != null) $this->unserializeManifest(); + $regNodes = $this->xPath->query("registry_contributions/*"); + for ($i=0;$i<$regNodes->length;$i++) { + $regNode = $regNodes->item($i); + if($regNode->nodeType != XML_ELEMENT_NODE) continue; + if ($regNode->nodeName == "external_file" && !$this->externalFilesAppended) { + $data = $this->nodeAttrToHash($regNode); + $filename = $data["filename"] OR ""; + $include = $data["include"] OR "*"; + $exclude = $data["exclude"] OR ""; + if(!is_file(AJXP_INSTALL_PATH."/".$filename)) continue; + if ($include != "*") { + $include = explode(",", $include); + } else { + $include = ["*"]; + } + if ($exclude != "") { + $exclude = explode(",", $exclude); + } else { + $exclude = []; + } + $this->initXmlContributionFile($ctx, $filename, $include, $exclude, $dry); + } else { + if (!$dry) { + $this->registryContributions[]=$regNode; + $this->parseSpecificContributions($ctx, $regNode); + } + } + } + // add manifest as a "plugins" (remove parsed contrib) + $pluginContrib = new \DOMDocument(); + $pluginContrib->loadXML(""); + $manifestNode = $pluginContrib->importNode($this->manifestDoc->documentElement, true); + $pluginContrib->documentElement->appendChild($manifestNode); + $xP=new \DOMXPath($pluginContrib); + $regNodeParent = $xP->query("registry_contributions", $manifestNode); + if ($regNodeParent->length) { + $manifestNode->removeChild($regNodeParent->item(0)); + } + if (!$dry) { + $this->registryContributions[]=$pluginContrib->documentElement; + $this->parseSpecificContributions($ctx, $pluginContrib->documentElement); + $this->contributionsLoaded = true; + } + } + + /** + * Load an external XML file and include/exclude its nodes as contributions. + * @param ContextInterface $ctx + * @param string $xmlFile Path to the file from the base install path + * @param array $include XPath query for XML Nodes to include + * @param array $exclude XPath query for XML Nodes to exclude from the included ones. + * @param bool $dry Dry-run of the inclusion + */ + protected function initXmlContributionFile(ContextInterface $ctx, $xmlFile, $include= ["*"], $exclude= [], $dry = false) + { + $contribDoc = new \DOMDocument(); + $contribDoc->load(AJXP_INSTALL_PATH."/".$xmlFile); + if (!is_array($include) && !is_array($exclude)) { + if (!$dry) { + $this->registryContributions[] = $contribDoc->documentElement; + $this->parseSpecificContributions($ctx, $contribDoc->documentElement); + } + return; + } + $xPath = new \DOMXPath($contribDoc); + $excluded = []; + foreach ($exclude as $excludePath) { + $children = $xPath->query($excludePath); + foreach ($children as $child) { + $excluded[] = $child; + } + } + $selected = []; + foreach ($include as $includePath) { + $incChildren = $xPath->query($includePath); + if(!$incChildren->length) continue; + $parentNode = $incChildren->item(0)->parentNode; + if (!isSet($selected[$parentNode->nodeName])) { + $selected[$parentNode->nodeName]= ["parent"=>$parentNode, "nodes"=> []]; + } + foreach ($incChildren as $incChild) { + $foundEx = false; + foreach ($excluded as $exChild) { + if ($this->nodesEqual($exChild, $incChild)) { + $foundEx = true;break; + } + } + if($foundEx) continue; + $selected[$parentNode->nodeName]["nodes"][] = $incChild; + } + if (!count($selected[$parentNode->nodeName]["nodes"])) { + unset($selected[$parentNode->nodeName]); + } + } + if(!count($selected)) return; + $originalRegContrib = $this->xPath->query("registry_contributions")->item(0); + $localRegParent = null; + foreach ($selected as $parentNodeName => $data) { + $node = $data["parent"]->cloneNode(false); + //$newNode = $originalRegContrib->ownerDocument->importNode($node, false); + if ($dry) { + $localRegParent = $this->xPath->query("registry_contributions/".$parentNodeName); + if ($localRegParent->length) { + $localRegParent = $localRegParent->item(0); + } else { + $localRegParent = $originalRegContrib->ownerDocument->createElement($parentNodeName); + $originalRegContrib->appendChild($localRegParent); + } + } + foreach ($data["nodes"] as $childNode) { + $node->appendChild($childNode); + if($dry) $localRegParent->appendChild($localRegParent->ownerDocument->importNode($childNode, true)); + } + if (!$dry) { + $this->registryContributions[] = $node; + $this->parseSpecificContributions($ctx, $node); + } else { + $this->reloadXPath(); + } + } + } + /** + * Dynamically modify some registry contributions nodes. Can be easily derivated to enable/disable + * some features dynamically during plugin initialization. + * @param ContextInterface $ctx + * @param \DOMNode $contribNode + * @return void + */ + protected function parseSpecificContributions(ContextInterface $ctx, \DOMNode &$contribNode) + { + //Append plugin id to callback tags + $callbacks = $contribNode->getElementsByTagName("serverCallback"); + foreach ($callbacks as $callback) { + $attr = $callback->ownerDocument->createAttribute("pluginId"); + $attr->value = $this->id; + $callback->appendChild($attr); + } + } + + /** + * Load the main manifest.xml file of the plugni + * @throws \Exception + * @return void + */ + public function loadManifest() + { + $file = $this->baseDir."/manifest.xml"; + if (!is_file($file)) { + return; + } + $this->manifestDoc = new \DOMDocument(); + try { + $this->manifestDoc->load($file); + } catch (\Exception $e) { + throw $e; + } + $id = $this->manifestDoc->documentElement->getAttribute("id"); + if (empty($id) || $id != $this->getId()) { + $this->manifestDoc->documentElement->setAttribute("id", $this->getId()); + } + $this->xPath = new \DOMXPath($this->manifestDoc); + $this->loadMixins(); + $this->detectStreamWrapper(); + $this->manifestLoaded = true; + $this->loadDependencies(); + } + + /** + * Get the plugin label as defined in the manifest file (label attribute) + * @return mixed|string + */ + public function getManifestLabel() + { + if($this->manifestXML != null) $this->unserializeManifest(); + $l = $this->xPath->query("@label", $this->manifestDoc->documentElement); + if($l->length) return XMLFilter::resolveKeywords($l->item(0)->nodeValue); + else return $this->id; + } + /** + * Get the plugin description as defined in the manifest file (description attribute) + * @return mixed|string + */ + public function getManifestDescription() + { + if($this->manifestXML != null) $this->unserializeManifest(); + $l = $this->xPath->query("@description", $this->manifestDoc->documentElement); + if($l->length) return XMLFilter::resolveKeywords($l->item(0)->nodeValue); + else return ""; + } + + /** + * Serialized all declared attributes and return a serialized representation of this plugin. + * The XML Manifest is base64 encoded before serialization. + * @return string + */ + public function serialize() + { + if ($this->manifestDoc != null) { + $this->manifestXML = serialize(base64_encode($this->manifestDoc->saveXML())); + } + $serialArray = []; + foreach ($this->serializableAttributes as $attr) { + $serialArray[$attr] = $this->$attr; + } + return serialize($serialArray); + } + + /** + * Load this plugin from its serialized reprensation. The manifest XML is base64 decoded. + * @param $string + * @return void + */ + public function unserialize($string) + { + $serialArray = unserialize($string); + foreach ($serialArray as $key => $value) { + $this->$key = $value; + } + /* + if ($this->manifestXML != NULL) { + $this->manifestDoc = new DOMDocument(1.0, "UTF-8"); + $this->manifestDoc->loadXML(base64_decode(unserialize($this->manifestXML))); + $this->reloadXPath(); + unset($this->manifestXML); + } + */ + } + + /** + * Load DOMDocument from serialized value. Must be called after checking + * that property $this->manifestXML is not null. + */ + protected function unserializeManifest(){ + if($this->manifestXML !== null){ + $this->manifestDoc = new \DOMDocument(1.0, "UTF-8"); + $this->manifestDoc->loadXML(base64_decode(unserialize($this->manifestXML))); + $this->reloadXPath(); + unset($this->manifestXML); + } + } + + /** + * Legacy function, should better be used to return XML Nodes than string. Perform a query + * on the manifest. + * @param string $xmlNodeName + * @param string $format + * @param bool $externalFiles + * @return \DOMElement|\DOMNodeList|string + */ + public function getManifestRawContent($xmlNodeName = "", $format = "string", $externalFiles = false) + { + if ($externalFiles && !$this->externalFilesAppended) { + $this->loadRegistryContributions(Context::emptyContext(), true); + $this->externalFilesAppended = true; + } + if($this->manifestXML != null) $this->unserializeManifest(); + if ($xmlNodeName == "") { + if ($format == "string") { + return $this->manifestDoc->saveXML($this->manifestDoc->documentElement); + } else { + return $this->manifestDoc->documentElement; + } + } else { + $nodes = $this->xPath->query($xmlNodeName); + if ($format == "string") { + $buffer = ""; + foreach ($nodes as $node) { + $buffer .= $this->manifestDoc->saveXML($node); + } + return $buffer; + } else { + return $nodes; + } + } + } + /** + * Return the registry contributions. The parameter can be used by subclasses to optimize the size of the XML returned : + * the extended version is called when sending to the client, whereas the "small" version is loaded to find and apply actions. + * @param ContextInterface $ctx + * @param bool $extendedVersion Can be used by subclasses to optimize the size of the XML returned. + * @return \DOMElement[] + */ + public function getRegistryContributions(ContextInterface $ctx, $extendedVersion = true) + { + if (!$this->contributionsLoaded) { + $this->loadRegistryContributions($ctx); + } + return $this->registryContributions; + } + /** + * Load the declared dependant plugins + * @return void + */ + protected function loadDependencies() + { + $depPaths = "dependencies/*/@pluginName"; + $nodes = $this->xPath->query($depPaths); + foreach ($nodes as $attr) { + $value = $attr->value; + $this->dependencies = array_merge($this->dependencies, explode("|", $value)); + } + $extPaths = "dependencies/phpExtension/@name"; + $nodes = $this->xPath->query($extPaths); + foreach ($nodes as $attr) { + $this->extensionsDependencies[] = $attr->value; + } + $this->cachedNodesFromManifest("dependencies/activePlugin"); + $this->cachedNodesFromManifest("//server_settings/param[@default]"); + $this->cachedNodesFromManifest("//server_settings/global_param|//server_settings/param"); + $l = $this->xPath->query("@enabled", $this->manifestDoc->documentElement); + if ($l->length) { + $this->cachedXPathResults["@enabled"] = $l->item(0)->nodeValue; + }else{ + $this->cachedXPathResults["@enabled"] = "not_set"; + } + + } + /** + * Update dependencies dynamically + * @param PluginsService $pluginService + * @return void + */ + public function updateDependencies($pluginService) + { + $append = false; + foreach ($this->dependencies as $index => $dependency) { + if ($dependency == "access.AJXP_STREAM_PROVIDER") { + unset($this->dependencies[$index]); + $append = true; + } + } + if ($append) { + $this->dependencies = array_merge($this->dependencies, $pluginService->getStreamWrapperPlugins()); + } + } + /** + * Check if this plugin depends on another one. + * @param string $pluginName + * @return bool + */ + public function dependsOn($pluginName) + { + return (in_array($pluginName, $this->dependencies) + || in_array(substr($pluginName, 0, strpos($pluginName, "."))."+", $this->dependencies)); + } + + /** + * @param $query + * @return array|mixed + */ + protected function cachedNodesFromManifest($query){ + if(isSet($this->cachedXPathResults[$query])){ + return $this->cachedXPathResults[$query]; + } + if(!$this->manifestLoaded) return []; + if($this->manifestXML != null) $this->unserializeManifest(); + $nodes = $this->xPath->query($query); + $res = []; + foreach($nodes as $xmlNode){ + $arrayNode = $this->nodeAttrToHash($xmlNode); + $arrayNode["__NODE_NAME__"] = $xmlNode->nodeName; + $res[] = $arrayNode; + } + $this->cachedXPathResults[$query] = $res; + return $res; + } + + /** + * Get dependencies + * + * @param PluginsService $pluginService + * @return array + */ + public function getActiveDependencies($pluginService) + { + $deps = []; + $nodes = $this->cachedNodesFromManifest("dependencies/activePlugin"); + foreach ($nodes as $arrayNode) { + $value = $arrayNode["pluginName"]; + $parts = explode("|", $value); + foreach ($parts as $depName) { + if ($depName == "access.AJXP_STREAM_PROVIDER") { + $deps = array_merge($deps, $pluginService->getStreamWrapperPlugins()); + } else if (strpos($depName, "+") !== false) { + $typed = $pluginService->getPluginsByType(substr($depName, 0, strlen($depName)-1)); + foreach($typed as $typPlug) $deps[] = $typPlug->getId(); + } else { + $deps[] = $depName;// array_merge($deps, explode("|", $value)); + } + } + } + return $deps; + } + /** + * Load the global parameters for this plugin + * @return void + */ + protected function loadConfigsDefinitions() + { + $params = $this->cachedNodesFromManifest("//server_settings/global_param|//server_settings/param"); + $this->pluginConf = []; + foreach ($params as $paramNode) { + $global = ($paramNode['__NODE_NAME__'] == "global_param"); + $this->pluginConfDefinition[$paramNode["name"]] = $paramNode; + if ($global && isset($paramNode["default"])) { + if ($paramNode["type"] == "boolean") { + $paramNode["default"] = ($paramNode["default"] === "true" ? true: false); + } else if ($paramNode["type"] == "integer") { + $paramNode["default"] = intval($paramNode["default"]); + } + $this->pluginConf[$paramNode["name"]] = $paramNode["default"]; + } + } + } + /** + * Load the default values for this plugin options + * @return array + */ + protected function loadOptionsDefaults() + { + $optionsDefaults = []; + $params = $this->cachedNodesFromManifest("//server_settings/param[@default]"); + foreach ($params as $node) { + $default = $node["default"]; + if ($node["type"] == "boolean") { + $default = ($default === "true" ? true: false); + } else if ($node["type"] == "integer") { + $default = intval($default); + } + $optionsDefaults[$node["name"]] = $default; + } + return $optionsDefaults; + } + /** + * @return array + */ + public function getConfigsDefinitions() + { + return $this->pluginConfDefinition; + } + /** + * Load the configs passed as parameter. This method will + * + Parse the config definitions and load the default values + * + Merge these values with the $configData parameter + * + Publish their value in the manifest if the global_param is "exposed" to the client. + * @param array $configData + * @return void + */ + public function loadConfigs($configData) + { + // PARSE DEFINITIONS AND LOAD DEFAULT VALUES + if (!isSet($this->pluginConf)) { + $this->loadConfigsDefinitions(); + } + // MERGE WITH PASSED CONFIGS + $this->pluginConf = array_merge($this->pluginConf, $configData); + + // PUBLISH IF NECESSARY + foreach ($this->pluginConf as $key => $value) { + if (isSet($this->pluginConfDefinition[$key]) && isSet($this->pluginConfDefinition[$key]["expose"]) && $this->pluginConfDefinition[$key]["expose"] == "true") { + $this->exposeConfigInManifest($key, $value); + } + } + + // ASSIGN SPECIFIC OPTIONS TO PLUGIN KEY + if (isSet($this->pluginConf["AJXP_PLUGIN_ENABLED"])) { + $this->enabled = $this->pluginConf["AJXP_PLUGIN_ENABLED"]; + } + } + /** + * Return this plugin configs, merged with its associated "core" configs. + * @return array + */ + public function getConfigs() + { + $core = PluginsService::getInstance()->getPluginByTypeName("core", $this->type); + if (!empty($core)) { + $coreConfs = $core->getConfigs(); + return array_merge($coreConfs, $this->pluginConf); + } else { + return $this->pluginConf; + } + } + /** + * Return the file path of the specific class to load + * @return array|bool + */ + public function getClassFile() + { + if($this->manifestXML != null) $this->unserializeManifest(); + $files = $this->xPath->query("class_definition"); + if(!$files->length) return false; + return $this->nodeAttrToHash($files->item(0)); + } + + /** + * @return array + */ + public function missingExtensions(){ + $missing = []; + if(count($this->extensionsDependencies)){ + foreach($this->extensionsDependencies as $ext){ + if (!extension_loaded($ext)) { + $missing[] = $ext; + } + } + } + return $missing; + } + + /** + * @return bool + */ + public function hasMissingExtensions(){ + return count($this->missingExtensions()) > 0; + } + + /** + * @return bool + */ + public function manifestLoaded() + { + return $this->manifestLoaded; + } + /** + * @return string + */ + public function getId() + { + return $this->id; + } + /** + * @return string + */ + public function getName() + { + return $this->name; + } + /** + * @return string + */ + public function getType() + { + return $this->type; + } + /** + * @return string + */ + public function getBaseDir() + { + return $this->baseDir; + } + /** + * @return array + */ + public function getDependencies() + { + return $this->dependencies; + } + /** + * Write a debug log with the plugin id as source + * @param string $prefix A quick description or action + * @param array|string $message Variable number of message args (string or array) + * @return void + */ + public function logDebug($prefix, $message = "") + { + if(!class_exists("ConfService")) return ; + if(!ConfService::getConf("SERVER_DEBUG")) return ; + $args = func_get_args(); + array_shift($args); + Logger::log2(LOG_LEVEL_DEBUG, $this->getId(), $prefix, $args); + } + + /** + * Write an info log with the plugin id as source + * @param string $prefix A quick description or action + * @param array|string $message Variable number of message args (string or array) + * @return void + */ + public function logInfo($prefix, $message) + { + $args = func_get_args(); + array_shift($args); + Logger::log2(LOG_LEVEL_INFO, $this->getId(), $prefix, $args); + } + + /** + * Write a notice log with the plugin id as source + * @param string $prefix A quick description or action + * @param array|string $message Variable number of message args (string or array) + * @return void + */ + public function logNotice($prefix, $message) + { + $args = func_get_args(); + array_shift($args); + Logger::log2(LOG_LEVEL_NOTICE, $this->getId(), $prefix, $args); + } + + /** + * Write a warning log with the plugin id as source + * @param string $prefix A quick description or action + * @param array|string $message Variable number of message args (string or array) + * @return void + */ + public function logWarning($prefix, $message) + { + $args = func_get_args(); + array_shift($args); + Logger::log2(LOG_LEVEL_WARNING, $this->getId(), $prefix, $args); + } + + /** + * Write an error log with the plugin id as source + * @param string $prefix A quick description or action + * @param array|string $message Variable number of message args (string or array) + * @return void + */ + public function logError($prefix, $message) + { + $args = func_get_args(); + array_shift($args); + Logger::log2(LOG_LEVEL_ERROR, $this->getId(), $prefix, $args); + } + + /** + * Detect if this plugin declares a StreamWrapper, and if yes loads it and register the stream. + * @param bool $register + * @return array|bool + */ + public function detectStreamWrapper($register = false, ContextInterface $ctx = null) + { + if (isSet($this->streamData)) { + if($this->streamData === false) return false; + $streamData = $this->streamData; + // include wrapper, no other checks needed. + include_once(AJXP_INSTALL_PATH."/".$streamData["filename"]); + } else { + if($this->manifestXML != null) $this->unserializeManifest(); + $files = $this->xPath->query("class_stream_wrapper"); + if (!$files->length) { + $this->streamData = false; + return false; + } + $streamData = $this->nodeAttrToHash($files->item(0)); + if (!is_file(AJXP_INSTALL_PATH."/".$streamData["filename"])) { + $this->streamData = false; + return false; + } + include_once(AJXP_INSTALL_PATH."/".$streamData["filename"]); + if (!class_exists($streamData["classname"])) { + $this->streamData = false; + return false; + } + $this->streamData = $streamData; + } + if ($register) { + $pServ = PluginsService::getInstance($ctx); + $wrappers = stream_get_wrappers(); + if (!in_array($streamData["protocol"], $wrappers)) { + stream_wrapper_register($streamData["protocol"], $streamData["classname"]); + $pServ->registerWrapperClass($streamData["protocol"], $streamData["classname"]); + } + MetaStreamWrapper::register($wrappers); + } + return $streamData; + } + /** + * Add a name/value pair in the manifest to be published to the world. + * @param $configName + * @param $configValue + * @return void + */ + protected function exposeConfigInManifest($configName, $configValue) + { + if($this->manifestXML != null) $this->unserializeManifest(); + $confBranch = $this->xPath->query("plugin_configs"); + if (!$confBranch->length) { + $configNode = $this->manifestDoc->importNode(new \DOMElement("plugin_configs", "")); + $this->manifestDoc->documentElement->appendChild($configNode); + } else { + $configNode = $confBranch->item(0); + } + $prop = $this->manifestDoc->createElement("property"); + $propValue = $this->manifestDoc->createCDATASection(json_encode($configValue)); + $prop->appendChild($propValue); + $attName = $this->manifestDoc->createAttribute("name"); + $attValue = $this->manifestDoc->createTextNode($configName); + $attName->appendChild($attValue); + $prop->appendChild($attName); + $configNode->appendChild($prop); + $this->reloadXPath(); + } + + /** + * @return array + */ + public function getPluginInformation() + { + $info = [ + "plugin_author" => "", + "plugin_uri" => "", + "core_packaged" => true, + "plugin_version"=> "follow", + "core_version" => AJXP_VERSION, + ]; + if($this->manifestXML != null) $this->unserializeManifest(); + $infoBranch = $this->xPath->query("plugin_info"); + if ($infoBranch->length) { + /** @var \DOMElement $child */ + foreach ($infoBranch->item(0)->childNodes as $child) { + if($child->nodeType != 1) continue; + if ($child->nodeName != "core_relation") { + $info[$child->nodeName] = $child->nodeValue; + } else { + if($child->getAttribute("packaged") == "false") $info["core_packaged"] = false; + $coreV = $child->getAttribute("tested_version"); + if($coreV == "follow") $coreV = AJXP_VERSION; + $info["core_version"] = $coreV; + } + } + } + return $info; + } + + /** + * @param $defaultAuthor + * @param $defaultUriBase + * @return string + */ + public function getPluginInformationHTML($defaultAuthor, $defaultUriBase) + { + $pInfo = $this->getPluginInformation(); + $pInfoString = ""; + if (empty($pInfo["plugin_uri"])) { + if($pInfo["core_packaged"]) $pInfo["plugin_uri"] = $defaultUriBase.$this->getType()."/".$this->getName()."/"; + else $pInfo["plugin_uri"] = "N/A"; + } + if ($pInfo["plugin_uri"] != "N/A") { + $pInfo["plugin_uri"] = "".$pInfo["plugin_uri"].""; + } + if ($pInfo["core_packaged"]) { + unset($pInfo["plugin_version"]); + unset($pInfo["core_version"]); + } + $pInfo["core_packaged"] = ($pInfo["core_packaged"] === true ? "Yes": "No"); + $humanKeys = [ + "plugin_author" => "Author", + "plugin_version" => "Version", + "plugin_uri" => "Url", + "core_packaged" => "Core distrib.", + "core_version" => "Core version" + ]; + if(empty($pInfo["plugin_author"])) $pInfo["plugin_author"] = $defaultAuthor; + foreach ($pInfo as $k => $v) { + $pInfoString .= "
  • ".$humanKeys[$k]."$v
  • "; + } + return "
      $pInfoString
    "; + } + + /** + * @return void + */ + public function reloadXPath() + { + // Relaunch xpath + $this->xPath = new \DOMXPath($this->manifestDoc); + } + /** + * @param $mixinName + * @return bool + */ + public function hasMixin($mixinName) + { + return (in_array($mixinName, $this->mixins)); + } + /** + * Check if the plugin declares mixins, and load them using PluginsService::patchPluginWithMixin method + * @return void + */ + protected function loadMixins() + { + $attr = $this->manifestDoc->documentElement->getAttribute("mixins"); + if ($attr != "") { + $this->mixins = explode(",", $attr); + foreach ($this->mixins as $mixin) { + PluginsService::getInstance()->patchPluginWithMixin($this, $this->manifestDoc, $mixin); + } + } + } + + /** + * Transform a simple node and its attributes to a HashTable + * + * @param \DOMNode $node + * @return array + */ + protected function nodeAttrToHash($node) + { + $hash = []; + $attributes = $node->attributes; + if ($attributes!=null) { + foreach ($attributes as $domAttr) { + $hash[$domAttr->name] = $domAttr->value; + } + } + return $hash; + } + /** + * Compare two nodes at first level (nodename and attributes) + * + * @param \DOMNode $node1 + * @param \DOMNode $node2 + * @return bool + */ + protected function nodesEqual($node1, $node2) + { + if($node1->nodeName != $node2->nodeName) return false; + $hash1 = $this->nodeAttrToHash($node1); + $hash2 = $this->nodeAttrToHash($node2); + foreach ($hash1 as $name=>$value) { + if(!isSet($hash2[$name]) || $hash2[$name] != $value) return false; + } + return true; + } +} diff --git a/core/src/core/src/pydio/Core/PluginFramework/PluginsService.php b/core/src/core/src/pydio/Core/PluginFramework/PluginsService.php new file mode 100644 index 0000000000..3a6b658c0c --- /dev/null +++ b/core/src/core/src/pydio/Core/PluginFramework/PluginsService.php @@ -0,0 +1,1379 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\PluginFramework; + +use DOMXPath; +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Access\Core\Model\Repository; +use Pydio\Cache\Core\AbstractCacheDriver; +use Pydio\Conf\Core\AbstractConfDriver; +use Pydio\Conf\Core\CoreConfLoader; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Exception\PydioUserAlertException; +use Pydio\Core\Exception\RepositoryLoadException; +use Pydio\Core\Http\TopLevelRouter; +use Pydio\Core\Model\Context; +use Pydio\Core\Model\ContextInterface; + + +use Pydio\Core\Model\RepositoryInterface; +use Pydio\Core\Services\CacheService; + +use Pydio\Core\Services\UsersService; +use Pydio\Core\Services\ApplicationState; +use Pydio\Core\Utils\FileHelper; +use Pydio\Log\Core\Logger; +use Pydio\Access\Meta\Core\AbstractMetaSource; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Core parser for loading / serving plugins + * @package Pydio + * @subpackage Core + */ +class PluginsService +{ + /** + * @var PluginsService[] + */ + private static $instances = []; + + /** + * All detected plugins + * @var array + */ + private $detectedPlugins = []; + + /** + * @var ContextInterface + */ + private $context; + + /** + * @var string + */ + private $pluginsDir; + + private $required_files = []; + private $activePlugins = []; + private $streamWrapperPlugins = []; + private $registeredWrappers = []; + private $xmlRegistry; + private $registryVersion; + private $tmpDeferRegistryBuild = false; + + private $mixinsDoc; + private $mixinsXPath; + + /*********************************/ + /* STATIC FUNCTIONS */ + /*********************************/ + + /** + * Load registry either from cache or from plugins folder. + * @throws PydioException + */ + public static function initCoreRegistry(){ + + $coreInstance = self::getInstance(Context::emptyContext()); + $coreInstance->getDetectedPlugins(); + + } + + /** + * Clear the cached files with the plugins + */ + public static function clearPluginsCache(){ + @unlink(AJXP_PLUGINS_CACHE_FILE); + @unlink(AJXP_PLUGINS_REQUIRES_FILE); + @unlink(AJXP_PLUGINS_QUERIES_CACHE); + @unlink(AJXP_PLUGINS_BOOTSTRAP_CACHE); + @unlink(AJXP_CACHE_DIR."/".TopLevelRouter::ROUTE_CACHE_FILENAME); + if(@unlink(AJXP_PLUGINS_REPOSITORIES_CACHE)){ + $content = "getRegistryCacheKey(true)); + CacheService::delete(AJXP_CACHE_SERVICE_NS_SHARED, $instance->getRegistryCacheKey(false)); + } + } + + /** + * @return AbstractConfDriver + */ + public static function confPluginSoftLoad() + { + $ctx = Context::emptyContext(); + /** @var AbstractConfDriver $booter */ + $booter = PluginsService::getInstance($ctx)->softLoad("boot.conf", []); + $coreConfigs = $booter->loadPluginConfig("core", "conf"); + $corePlug = PluginsService::getInstance($ctx)->softLoad("core.conf", []); + $corePlug->loadConfigs($coreConfigs); + return $corePlug->getImplementation(); + + } + + /** + * @return AbstractCacheDriver + */ + public static function cachePluginSoftLoad() + { + $ctx = Context::emptyContext(); + $coreConfigs = []; + $corePlug = PluginsService::getInstance($ctx)->softLoad("core.cache", []); + /** @var CoreConfLoader $coreConf */ + $coreConf = PluginsService::getInstance($ctx)->softLoad("core.conf", []); + $coreConf->loadBootstrapConfForPlugin("core.cache", $coreConfigs); + if (!empty($coreConfigs)) $corePlug->loadConfigs($coreConfigs); + return $corePlug->getImplementation(); + } + + /** + * Find a plugin by its type/name + * @param string $type + * @param string $name + * @return Plugin + */ + public static function findPluginWithoutCtxt($type, $name) + { + return self::getInstance()->getPluginByTypeName($type, $name); + } + + /** + * Singleton method + * @param ContextInterface|null $ctx + * @return PluginsService the service instance + */ + public static function getInstance($ctx = null) + { + if(empty($ctx)){ + $ctx = Context::emptyContext(); + } + $identifier = $ctx->getStringIdentifier(); + if (!isSet(self::$instances[$identifier])) { + $c = __CLASS__; + /** + * @var PluginsService $pServ + */ + self::$instances[$identifier] = $pServ = new $c($ctx); + if(!$ctx->isEmpty()) { + $emptyInstance = self::getInstance(Context::contextWithObjects(null, null)); + //$pServ->getDetectedPlugins(); + $pServ->cloneDetectedPluginsFromCoreInstance($emptyInstance); + $pServ->copyCorePluginFromService($emptyInstance, "conf"); + $pServ->copyCorePluginFromService($emptyInstance, "cache"); + $pServ->copyCorePluginFromService($emptyInstance, "auth"); + if($ctx->hasRepository()){ + $pServ->initRepositoryPlugins($ctx); + } + $pServ->initActivePlugins(); + }else{ + $pServ->getDetectedPlugins(); + } + } + return self::$instances[$identifier]; + } + + /** + * @param ContextInterface $ctx + */ + public static function clearInstance($ctx){ + $identifier = $ctx->getStringIdentifier(); + if(isSet(self::$instances[$identifier])){ + unset(self::$instances[$identifier]); + gc_collect_cycles(); + } + } + + /** + * @param ContextInterface $ctx + * @throws RepositoryLoadException + * @throws PydioUserAlertException + */ + private function initRepositoryPlugins($ctx){ + + $errors = []; + $repository = $ctx->getRepository(); + if(empty($repository)){ + throw new RepositoryLoadException($repository, []); + } + $accessType = $repository->getAccessType(); + /** @var AbstractAccessDriver $plugInstance */ + $plugInstance = $this->getPluginByTypeName("access", $accessType); + if(!$plugInstance instanceof AbstractAccessDriver){ + throw new RepositoryLoadException($repository, ["Could not load plugin as an accessDriver"]); + } + + // TRIGGER BEFORE INIT META + $metaSources = $repository->getContextOption($ctx, "META_SOURCES"); + if (isSet($metaSources) && is_array($metaSources) && count($metaSources)) { + $keys = array_keys($metaSources); + foreach ($keys as $plugId) { + if($plugId == "") continue; + /** @var AbstractMetaSource $instance */ + $instance = $this->getPluginById($plugId); + if (!is_object($instance)) { + continue; + } + if (!method_exists($instance, "beforeInitMeta")) { + continue; + } + try { + $options = $metaSources[$plugId]; + if($ctx->hasUser()) { + $options = $ctx->getUser()->getMergedRole()->filterPluginConfigs($plugId, $options, $repository->getId()); + } + $instance->init($ctx, $options); + $instance->beforeInitMeta($ctx, $plugInstance); + } catch (\Exception $e) { + Logger::error(__CLASS__, 'Meta plugin', 'Cannot instanciate Meta plugin, reason : '.$e->getMessage()); + $errors[] = $e->getMessage(); + } + } + } + + // INIT MAIN DRIVER + try { + $plugInstance->init($ctx); + }catch (PydioUserAlertException $ua){ + throw $ua; + }catch (\Exception $e){ + new RepositoryLoadException($repository, [$e->getMessage()]); + } + $repository->setDriverInstance($plugInstance); + + $this->deferBuildingRegistry(); + $this->setPluginUniqueActiveForType("access", $accessType); + + // TRIGGER INIT META + $metaSources = $repository->getContextOption($ctx, "META_SOURCES"); + if (isSet($metaSources) && is_array($metaSources) && count($metaSources)) { + $keys = array_keys($metaSources); + foreach ($keys as $plugId) { + if($plugId == "") continue; + $split = explode(".", $plugId); + /** @var AbstractMetaSource $instance */ + $instance = $this->getPluginById($plugId); + if (!is_object($instance)) { + continue; + } + try { + $options = $metaSources[$plugId]; + if($ctx->hasUser()) { + $options = $ctx->getUser()->getMergedRole()->filterPluginConfigs($plugId, $options, $repository->getId()); + } + $instance->init($ctx, $options); + if(!method_exists($instance, "initMeta")) { + throw new \Exception("Meta Source $plugId does not implement the initMeta method."); + } + $instance->initMeta($ctx, $plugInstance); + } catch (\Exception $e) { + Logger::error(__CLASS__, 'Meta plugin', 'Cannot instanciate Meta plugin, reason : '.$e->getMessage()); + $errors[] = $e->getMessage(); + } + $this->setPluginActive($split[0], $split[1]); + } + } + $this->flushDeferredRegistryBuilding(); + if(count($errors)){ + throw new RepositoryLoadException($repository, $errors); + } + + } + + /** + * @param PluginsService $source + * @param string $type + */ + private function copyCorePluginFromService($source, $type){ + + /** + * @var CoreInstanceProvider $corePlugin + */ + $corePlugin = $source->getPluginByTypeName("core", $type); + $this->setPluginActive("core", $corePlugin->getName(), true, $corePlugin); + $implementation = $corePlugin->getImplementation(); + if(!empty($implementation)){ + $this->setPluginUniqueActiveForType($type, $implementation->getName(), $implementation); + } + + } + + /** + * Search in all plugins (enabled / active or not) and store result in cache + * @param string $query + * @param callable $typeChecker + * @param callable $callback + * @return mixed + */ + public static function searchManifestsWithCache($query, $callback, $typeChecker = null){ + $coreInstance = self::getInstance(Context::emptyContext()); + $result = $coreInstance->loadFromPluginQueriesCache($query); + if(empty($typeChecker)){ + $typeChecker = function($test){ + return ($test !== null && is_array($test)); + }; + } + if($typeChecker($result)){ + return $result; + } + $nodes = $coreInstance->searchAllManifests($query, "node", false, false, true); + $result = $callback($nodes); + $coreInstance->storeToPluginQueriesCache($query, $result); + return $result; + } + + /** + * Search all plugins manifest with an XPath query, and return either the Nodes, or directly an XML string. + * @param string $query + * @param string $stringOrNodeFormat + * @param boolean $limitToActivePlugins Whether to search only in active plugins or in all plugins + * @param bool $limitToEnabledPlugins + * @param bool $loadExternalFiles + * @return \DOMNode[] + */ + public function searchAllManifests($query, $stringOrNodeFormat = "string", $limitToActivePlugins = false, $limitToEnabledPlugins = false, $loadExternalFiles = false) + { + $buffer = ""; + $nodes = []; + $detectedPlugins = $this->getDetectedPlugins(); + foreach ($detectedPlugins as $plugType) { + /** @var Plugin $plugObject */ + foreach ($plugType as $plugName => $plugObject) { + if ($limitToActivePlugins) { + $plugId = $plugObject->getId(); + if ($limitToActivePlugins && (!isSet($this->activePlugins[$plugId]) || $this->activePlugins[$plugId] === false)) { + continue; + } + } + if ($limitToEnabledPlugins) { + if(!$plugObject->isEnabled()) continue; + } + $res = $plugObject->getManifestRawContent($query, $stringOrNodeFormat, $loadExternalFiles); + if ($stringOrNodeFormat == "string") { + $buffer .= $res; + } else { + foreach ($res as $node) { + $nodes[] = $node; + } + } + } + } + if($stringOrNodeFormat == "string") return $buffer; + else return $nodes; + } + + + /** + * Gather stream data from repositories driver, without loading the whole context. + * + * @param RepositoryInterface[] $repositories + * @return array + */ + public static function detectRepositoriesStreams($repositories){ + $streams = []; + foreach ($repositories as $repository) { + $accessType = $repository->getAccessType(); + // Find access driver from base plugins + $plugin = self::findPluginWithoutCtxt("access", $accessType); + if($plugin instanceof AbstractAccessDriver){ + $streamData = $plugin->detectStreamWrapper(false); + if($streamData !== false) $streams[$streamData["protocol"]] = $accessType; + } + } + return $streams; + } + + /*********************************/ + /* PUBLIC FUNCTIONS */ + /*********************************/ + + /** + * Get publicable XML Registry, filtered by roles + * @param bool $extendedVersion + * @param bool $clone + * @param bool $useCache + * @return \DOMDocument|\DOMNode + */ + public function getFilteredXMLRegistry($extendedVersion = true, $clone = false, $useCache = false) + { + if ($useCache) { + $cacheKey = $this->getRegistryCacheKey($extendedVersion); + $tStamps = []; + if($this->context->hasUser()) $tStamps[] = "pydio:user:".$this->context->getUser()->getId(); + if($this->context->hasRepository()) $tStamps[] = "pydio:repository:".$this->context->getRepositoryId(); + $cachedXml = CacheService::fetchWithTimestamps(AJXP_CACHE_SERVICE_NS_SHARED, $cacheKey, $tStamps); + if ($cachedXml !== false) { + $registry = new \DOMDocument("1.0", "utf-8"); + $registry->loadXML($cachedXml); + $this->updateXmlRegistry($registry, $extendedVersion); + if ($clone) { + return $registry->cloneNode(true); + } else { + return $registry; + } + } + } + + $registry = $this->getXmlRegistry($extendedVersion); + if(UsersService::usersEnabled()){ + $changes = $this->filterRegistryFromRole($registry, $this->context); + if ($changes) { + $this->updateXmlRegistry($registry, $extendedVersion); + } + } + + if ($useCache && isSet($cacheKey)) { + CacheService::saveWithTimestamp(AJXP_CACHE_SERVICE_NS_SHARED, $cacheKey, $registry->saveXML()); + } + + if ($clone) { + $cloneDoc = $registry->cloneNode(true); + $registry = $cloneDoc; + } + return $registry; + + } + + /** + * @param string $plugType + * @param string $plugName + * @return Plugin + */ + public function getPluginByTypeName($plugType, $plugName) + { + if (isSet($this->detectedPlugins[$plugType]) && isSet($this->detectedPlugins[$plugType][$plugName])) { + return $this->detectedPlugins[$plugType][$plugName]; + } else { + return false; + } + } + + /** + * Loads the full registry, from the cache only + * @param AbstractCacheDriver + * @return bool + */ + private function loadPluginsFromCache($cacheStorage) { + + if(!empty($this->detectedPlugins)){ + return true; + } + if(!empty($cacheStorage) && $this->_loadDetectedPluginsFromCache($cacheStorage)){ + return true; + } + + return false; + } + + /** + * Loads the full registry, from the cache or not + * @param String $pluginFolder + * @param AbstractConfDriver $confStorage + * @param AbstractCacheDriver|null $cacheStorage + */ + private function loadPlugins($pluginFolder, $confStorage, $cacheStorage) + { + if (!empty($cacheStorage) && $this->loadPluginsFromCache($cacheStorage)) { + return; + } + + if (is_string($pluginFolder)) { + $pluginFolder = [$pluginFolder]; + } + + $pluginsPool = []; + foreach ($pluginFolder as $sourceFolder) { + $handler = @opendir($sourceFolder); + if ($handler) { + while ( ($item = readdir($handler)) !==false) { + if($item == "." || $item == ".." || !@is_dir($sourceFolder."/".$item) || strstr($item,".")===false) continue ; + $plugin = new Plugin($item, $sourceFolder."/".$item); + $plugin->loadManifest(); + if ($plugin->manifestLoaded()) { + $pluginsPool[$plugin->getId()] = $plugin; + if (method_exists($plugin, "detectStreamWrapper") && $plugin->detectStreamWrapper(false) !== false) { + $this->streamWrapperPlugins[] = $plugin->getId(); + } + } + } + closedir($handler); + } + } + if (count($pluginsPool)) { + $this->checkDependencies($pluginsPool); + foreach ($pluginsPool as $plugin) { + $this->recursiveLoadPlugin($confStorage, $plugin, $pluginsPool); + } + } + + if (!defined("AJXP_SKIP_CACHE") || AJXP_SKIP_CACHE === false) { + FileHelper::saveSerialFile(AJXP_PLUGINS_REQUIRES_FILE, $this->required_files, false, false); + FileHelper::saveSerialFile(AJXP_PLUGINS_CACHE_FILE, $this->detectedPlugins, false, false); + if (is_file(AJXP_PLUGINS_QUERIES_CACHE)) { + @unlink(AJXP_PLUGINS_QUERIES_CACHE); + } + + $this->savePluginsRegistryToCache($cacheStorage); + } + } + + /** + * Simply load a plugin class, without the whole dependencies et.all + * @param string $pluginId + * @param array $pluginOptions + * @return Plugin|CoreInstanceProvider + */ + public function softLoad($pluginId, $pluginOptions) + { + // Try to get from cache + list($type, $name) = explode(".", $pluginId); + if(!empty($this->detectedPlugins) && isSet($this->detectedPlugins[$type][$name])) { + /** + * @var Plugin $plugin + */ + $plugin = $this->detectedPlugins[$type][$name]; + $plugin->init($this->context, $pluginOptions); + return clone $plugin; + } + + + $plugin = new Plugin($pluginId, AJXP_INSTALL_PATH."/".AJXP_PLUGINS_FOLDER."/".$pluginId); + $plugin->loadManifest(); + $plugin = $this->instanciatePluginClass($plugin); + $plugin->loadConfigs([]); // Load default + $plugin->init($this->context, $pluginOptions); + return $plugin; + } + + /** + * All the plugins of a given type + * @param string $type + * @return Plugin[] + */ + public function getPluginsByType($type) + { + if(isSet($this->detectedPlugins[$type])) return $this->detectedPlugins[$type]; + else return []; + } + + /** + * Get a plugin instance + * + * @param string $pluginId + * @return Plugin + */ + public function getPluginById($pluginId) + { + $split = explode(".", $pluginId); + return $this->getPluginByTypeName($split[0], $split[1]); + } + + /** + * Load data from cache + * @param $key + * @return mixed|null + */ + public function loadFromPluginQueriesCache($key) + { + if(AJXP_SKIP_CACHE) return null; + $test = FileHelper::loadSerialFile(AJXP_PLUGINS_QUERIES_CACHE); + if (!empty($test) && is_array($test) && isset($test[$key])) { + return $test[$key]; + } + return null; + } + + /** + * Copy data to cache + * @param $key + * @param $value + * @throws \Exception + */ + public function storeToPluginQueriesCache($key, $value) + { + if(AJXP_SKIP_CACHE) return; + $test = FileHelper::loadSerialFile(AJXP_PLUGINS_QUERIES_CACHE); + if(!is_array($test)) $test = []; + $test[$key] = $value; + FileHelper::saveSerialFile(AJXP_PLUGINS_QUERIES_CACHE, $test); + } + + /*********************************/ + /* PUBLIC: ACTIVE PLUGINS */ + /*********************************/ + + /** + * Set the service in defer mode : do not rebuild + * registry on each plugin activation + */ + public function deferBuildingRegistry(){ + $this->tmpDeferRegistryBuild = true; + } + + /** + * If service was in defer mode, now build the registry + */ + public function flushDeferredRegistryBuilding(){ + $this->tmpDeferRegistryBuild = false; + if (isSet($this->xmlRegistry)) { + $this->buildXmlRegistry(($this->registryVersion == "extended")); + } + } + + /** + * Load the plugins list, and set active the plugins automatically, + * except for the specific types that declare a "core.*" plugin. In that case, + * either the core class has an AUTO_LOAD_TYPE property and all plugins are activated, + * or it's the task of the core class to load the necessary plugin(s) of this type. + * @return void + */ + public function initActivePlugins() + { + $detected = $this->getDetectedPlugins(); + $this->deferBuildingRegistry(); + /** + * @var Plugin $pObject + */ + $toActivate = []; + foreach ($detected as $pType => $pObjects) { + $coreP = $this->getPluginByTypeName("core", $pType); + if($coreP !== false && !isSet($coreP->AUTO_LOAD_TYPE)) continue; + foreach ($pObjects as $pName => $pObject) { + $toActivate[$pObject->getId()] = $pObject ; + } + } + $o = $this->getOrderByDependency($toActivate, false); + foreach ($o as $id) { + $pObject = $toActivate[$id]; + $pObject->init($this->context, []); + try { + $pObject->performChecks(); + if(!$pObject->isEnabled() || $pObject->hasMissingExtensions()) continue; + $this->setPluginActive($pObject->getType(), $pObject->getName(), true); + } catch (\Exception $e) { + //$this->errors[$pName] = "[$pName] ".$e->getMessage(); + } + + } + $this->flushDeferredRegistryBuilding(); + } + + /** + * Add a plugin to the list of active plugins + * @param $type + * @param $name + * @param bool $active + * @param Plugin $updateInstance + * @return void + */ + public function setPluginActive($type, $name, $active=true, $updateInstance = null) + { + if ($active) { + // Check active plugin dependencies + $plug = $this->getPluginById($type.".".$name); + if(!$plug || !$plug->isEnabled()) return; + $deps = $plug->getActiveDependencies($this); + if (count($deps)) { + $found = false; + foreach ($deps as $dep) { + if (isSet($this->activePlugins[$dep]) && $this->activePlugins[$dep] !== false) { + $found = true; break; + } + } + if (!$found) { + $this->activePlugins[$type.".".$name] = false; + return ; + } + } + } + if(isSet($this->activePlugins[$type.".".$name])){ + unset($this->activePlugins[$type.".".$name]); + } + $this->activePlugins[$type.".".$name] = $active; + if (isSet($updateInstance) && isSet($this->detectedPlugins[$type][$name])) { + $this->detectedPlugins[$type][$name] = $updateInstance; + } + if (isSet($this->xmlRegistry) && !$this->tmpDeferRegistryBuild) { + $this->buildXmlRegistry(($this->registryVersion == "extended")); + } + } + + /** + * Some type require only one active plugin at a time + * @param $type + * @param $name + * @param Plugin $updateInstance + * @return void + */ + public function setPluginUniqueActiveForType($type, $name, $updateInstance = null) + { + $typePlugs = $this->getPluginsByType($type); + $originalValue = $this->tmpDeferRegistryBuild; + $this->tmpDeferRegistryBuild = true; + foreach ($typePlugs as $plugName => $plugObject) { + $this->setPluginActive($type, $plugName, false); + } + $this->tmpDeferRegistryBuild = $originalValue; + $this->setPluginActive($type, $name, true, $updateInstance); + } + + /** + * Retrieve the whole active plugins list + * @return array + */ + public function getActivePlugins() + { + return $this->activePlugins; + } + + /** + * Retrieve an array of active plugins for type + * @param string $type + * @param bool $unique + * @return Plugin[] + */ + public function getActivePluginsForType($type, $unique = false) + { + $detectedPlugins = $this->getDetectedPlugins(); + $acts = []; + foreach ($this->activePlugins as $plugId => $active) { + if(!$active) continue; + list($pT,$pN) = explode(".", $plugId); + if ($pT == $type && isset($detectedPlugins[$pT][$pN])) { + if ($unique) { + return $detectedPlugins[$pT][$pN]; + break; + } + $acts[$pN] = $detectedPlugins[$pT][$pN]; + } + } + if($unique && !count($acts)) return false; + return $acts; + } + + /** + * Return only one of getActivePluginsForType + * @param $type + * @return Plugin + */ + public function getUniqueActivePluginForType($type) + { + return $this->getActivePluginsForType($type, true); + } + + /** + * All the plugins registry, active or not + * @param AbstractConfDriver $confPlugin + * @param AbstractCacheDriver $cachePlugin + * @return array + * @throws PydioException + */ + public function getDetectedPlugins($confPlugin = null, $cachePlugin = null) + { + if(empty($this->detectedPlugins)){ + + if($cachePlugin === null){ + $cachePlugin = self::cachePluginSoftLoad(); + } + if (!$this->loadPluginsFromCache($cachePlugin)) { + // Load from conf + try { + if($confPlugin === null){ + $confPlugin = self::confPluginSoftLoad(); + } + $this->loadPlugins($this->pluginsDir, $confPlugin, $cachePlugin); + } catch (\Exception $e) { + throw new PydioException("Severe error while loading plugins registry : ".$e->getMessage()); + } + } + } + return $this->detectedPlugins; + } + + /** + * @param $emptyInstance PluginsService + */ + public function cloneDetectedPluginsFromCoreInstance($emptyInstance){ + $detected = $emptyInstance->getDetectedPlugins(); + $this->detectedPlugins = []; + $this->streamWrapperPlugins = $emptyInstance->streamWrapperPlugins; + foreach($detected as $type => $plugins){ + $this->detectedPlugins[$type] = []; + /** + * @var string $name + * @var Plugin $plugin + */ + foreach($plugins as $name => $plugin){ + $cloned = clone $plugin; + $this->detectedPlugins[$type][$name] = $cloned; + } + } + } + + /*********************************/ + /* PUBLIC: WRAPPERS METHODS */ + /*********************************/ + /** + * All the plugins that declare a stream wrapper + * @return array + */ + public function getStreamWrapperPlugins() + { + return $this->streamWrapperPlugins; + } + + /** + * Add the $protocol/$wrapper to an internal cache + * @param string $protocol + * @param string $wrapperClassName + * @return void + */ + public function registerWrapperClass($protocol, $wrapperClassName) + { + $this->registeredWrappers[$protocol] = $wrapperClassName; + } + + /** + * Find a classname for a given protocol + * @param $protocol + * @return string + */ + public function getWrapperClassName($protocol, $register = false) + { + if(isSet($this->registeredWrappers[$protocol])){ + return $this->registeredWrappers[$protocol]; + } + /** @var AbstractAccessDriver $access */ + $access = $this->getActivePluginsForType("access", true); + $data = $access->detectStreamWrapper($register); + if($data !== null && $data["protocol"] == $protocol){ + return $data["classname"]; + } + return null; + } + + /** + * The protocol/classnames table + * @return array + */ + public function getRegisteredWrappers() + { + return $this->registeredWrappers; + } + + /** + * Append some predefined XML to a plugin instance + * @param Plugin $plugin + * @param \DOMDocument $manifestDoc + * @param String $mixinName + */ + public function patchPluginWithMixin(&$plugin, &$manifestDoc, $mixinName) + { + // Load behaviours if not already + if (!isSet($this->mixinsDoc)) { + $this->mixinsDoc = new \DOMDocument(); + $this->mixinsDoc->load(AJXP_INSTALL_PATH."/".AJXP_PLUGINS_FOLDER."/core.ajaxplorer/ajxp_mixins.xml"); + $this->mixinsXPath = new \DOMXPath($this->mixinsDoc); + } + // Merge into manifestDoc + $nodeList = $this->mixinsXPath->query($mixinName); + if(!$nodeList->length) return; + $mixinNode = $nodeList->item(0); + /** @var \DOMElement $child */ + foreach ($mixinNode->childNodes as $child) { + if($child->nodeType != XML_ELEMENT_NODE) continue; + $uuidAttr = $child->getAttribute("uuidAttr") OR "name"; + $this->mergeNodes($manifestDoc, $child->nodeName, $uuidAttr, $child->childNodes, true); + } + + // Reload plugin XPath + $plugin->reloadXPath(); + } + + + /*********************************/ + /* PRIVATE FUNCTIONS */ + /*********************************/ + /** + * Save plugin registry to cache + * @param AbstractCacheDriver|null $cacheStorage + */ + private function savePluginsRegistryToCache($cacheStorage) { + if (!empty ($cacheStorage)) { + $cacheStorage->save(AJXP_CACHE_SERVICE_NS_SHARED, "plugins_registry", $this->detectedPlugins); + } + } + + /** + * Go through all plugins and call their getRegistryContributions() method. + * Add all these contributions to the main XML ajxp_registry document. + * @param bool $extendedVersion Will be passed to the plugin, for optimization purpose. + * @return void + */ + private function buildXmlRegistry($extendedVersion = true) + { + $actives = $this->getActivePlugins(); + $reg = new \DOMDocument(); + $reg->loadXML(""); + foreach ($actives as $activeName=>$status) { + if($status === false) continue; + $plug = $this->getPluginById($activeName); + $contribs = $plug->getRegistryContributions($this->context, $extendedVersion); + foreach ($contribs as $contrib) { + $parent = $contrib->nodeName; + $nodes = $contrib->childNodes; + if(!$nodes->length) continue; + $uuidAttr = $contrib->getAttribute("uuidAttr"); + if($uuidAttr == "") $uuidAttr = "name"; + $this->mergeNodes($reg, $parent, $uuidAttr, $nodes); + } + } + $this->xmlRegistry = $reg; + } + + /** + * Load plugin class with dependencies first + * + * @param AbstractConfDriver $confStorage + * @param Plugin $plugin + * @param array $pluginsPool + */ + private function recursiveLoadPlugin(AbstractConfDriver $confStorage, $plugin, $pluginsPool) + { + if ($plugin->loadingState!="") { + return ; + } + $dependencies = $plugin->getDependencies(); + $plugin->loadingState = "lock"; + foreach ($dependencies as $dependencyId) { + if (isSet($pluginsPool[$dependencyId])) { + $this->recursiveLoadPlugin($confStorage, $pluginsPool[$dependencyId], $pluginsPool); + } else if (strpos($dependencyId, "+") !== false) { + foreach (array_keys($pluginsPool) as $pId) { + if (strpos($pId, str_replace("+", "", $dependencyId)) === 0) { + $this->recursiveLoadPlugin($confStorage, $pluginsPool[$pId], $pluginsPool); + } + } + } + } + $plugType = $plugin->getType(); + if (!isSet($this->detectedPlugins[$plugType])) { + $this->detectedPlugins[$plugType] = []; + } + $options = $confStorage->loadPluginConfig($plugType, $plugin->getName()); + if($plugin->isEnabled() || (isSet($options["AJXP_PLUGIN_ENABLED"]) && $options["AJXP_PLUGIN_ENABLED"] === true)){ + $plugin = $this->instanciatePluginClass($plugin); + } + $plugin->loadConfigs($options); + $this->detectedPlugins[$plugType][$plugin->getName()] = $plugin; + $plugin->loadingState = "loaded"; + } + + /** + * @param AbstractCacheDriver $cacheStorage + * @return bool + */ + private function _loadDetectedPluginsFromCache($cacheStorage){ + + if((!defined("AJXP_SKIP_CACHE") || AJXP_SKIP_CACHE === false)){ + $reqs = FileHelper::loadSerialFile(AJXP_PLUGINS_REQUIRES_FILE); + if (count($reqs)) { + foreach ($reqs as $fileName) { + if (!is_file($fileName)) { + // Cache is out of sync + return false; + } + require_once($fileName); + } + + $res = null; + + // Retrieving Registry from Server Cache + if (!empty($cacheStorage)) { + $res = $cacheStorage->fetch(AJXP_CACHE_SERVICE_NS_SHARED, 'plugins_registry'); + if(is_array($res)){ + $this->detectedPlugins=$res; + } + } + + // Retrieving Registry from files cache + if (empty($res)) { + $res = FileHelper::loadSerialFile(AJXP_PLUGINS_CACHE_FILE); + $this->detectedPlugins=$res; + $this->savePluginsRegistryToCache($cacheStorage); + } + + // Refresh streamWrapperPlugins + foreach ($this->detectedPlugins as $plugs) { + /** @var Plugin $plugin */ + foreach ($plugs as $plugin) { + if (method_exists($plugin, "detectStreamWrapper") && $plugin->detectStreamWrapper(false) !== false) { + $this->streamWrapperPlugins[] = $plugin->getId(); + } + } + } + + return true; + }else{ + return false; + } + }else{ + return false; + } + + } + + /** + * Find a PHP class and instanciate it to replace the empty Plugin + * + * @param Plugin $plugin + * @return Plugin + */ + private function instanciatePluginClass($plugin) + { + $definition = $plugin->getClassFile(); + if(!$definition) return $plugin; + $filename = AJXP_INSTALL_PATH."/".$definition["filename"]; + $className = $definition["classname"]; + if (is_file($filename)) { + /** + * @var Plugin $newPlugin + */ + require_once($filename); + $newPlugin = new $className($plugin->getId(), $plugin->getBaseDir()); + $newPlugin->loadManifest(); + $this->required_files[$filename] = $filename; + return $newPlugin; + } else { + return $plugin; + } + } + + /** + * Check that a plugin dependencies are loaded, disable it otherwise. + * @param Plugin[] $arrayToSort + */ + private function checkDependencies(&$arrayToSort) + { + // First make sure that the given dependencies are present + foreach ($arrayToSort as $plugId => $plugObject) { + $plugObject->updateDependencies($this); + if($plugObject->hasMissingExtensions()){ + unset($arrayToSort[$plugId]); + continue; + } + $dependencies = $plugObject->getDependencies(); + if(!count($dependencies)) continue;// return ; + $found = false; + foreach ($dependencies as $requiredPlugId) { + if ( strpos($requiredPlugId, "+") !== false || isSet($arrayToSort[$requiredPlugId])) { + $found = true; break; + } + } + if (!$found) { + unset($arrayToSort[$plugId]); + } + } + } + + /** + * @param $plugins + * @param bool $withStatus + * @return array + */ + private function getOrderByDependency($plugins, $withStatus = true) + { + $keys = []; + $unkowns = []; + if ($withStatus) { + foreach ($plugins as $pid => $status) { + if($status) $keys[] = $pid; + } + } else { + $keys = array_keys($plugins); + } + $result = []; + while (count($keys) > 0) { + $test = array_shift($keys); + $testObject = $this->getPluginById($test); + $deps = $testObject->getActiveDependencies($this); + if (!count($deps)) { + $result[] = $test; + continue; + } + $found = false; + $inOriginalPlugins = false; + foreach ($deps as $depId) { + if (in_array($depId, $result)) { + $found = true; + break; + } + if (!$inOriginalPlugins && array_key_exists($depId, $plugins) && (!$withStatus || $plugins[$depId] == true)) { + $inOriginalPlugins = true; + } + } + if ($found) { + $result[] = $test; + } else { + if($inOriginalPlugins) $keys[] = $test; + else { + unset($plugins[$test]); + $unkowns[] = $test; + } + } + } + return array_merge($result, $unkowns); + } + + /** + * Check the current user "specificActionsRights" and filter the full registry actions with these. + * @static + * @param \DOMDocument $registry + * @param ContextInterface $ctx + * @return bool + */ + private function filterRegistryFromRole(&$registry, ContextInterface $ctx) + { + $loggedUser = $ctx->getUser(); + if ($loggedUser == null) return false; + $crtRepo = $ctx->getRepository(); + $crtRepoId = AJXP_REPO_SCOPE_ALL; // "ajxp.all"; + if ($crtRepo != null && $crtRepo instanceof Repository) { + $crtRepoId = $crtRepo->getId(); + } + $actionRights = $loggedUser->getMergedRole()->listActionsStatesFor($crtRepo); + $changes = false; + $xPath = new DOMXPath($registry); + foreach ($actionRights as $pluginName => $actions) { + foreach ($actions as $actionName => $enabled) { + if ($enabled !== false) continue; + $actions = $xPath->query("actions/action[@name='$actionName']"); + if (!$actions->length) { + continue; + } + $action = $actions->item(0); + $action->parentNode->removeChild($action); + $changes = true; + } + } + $parameters = $loggedUser->getMergedRole()->listParameters(); + foreach ($parameters as $scope => $paramsPlugs) { + if ($scope === AJXP_REPO_SCOPE_ALL || $scope === $crtRepoId || ($crtRepo != null && $crtRepo->hasParent() && $scope === AJXP_REPO_SCOPE_SHARED)) { + foreach ($paramsPlugs as $plugId => $params) { + foreach ($params as $name => $value) { + // Search exposed plugin_configs, replace if necessary. + $searchparams = $xPath->query("plugins/*[@id='$plugId']/plugin_configs/property[@name='$name']"); + if (!$searchparams->length) continue; + $param = $searchparams->item(0); + $newCdata = $registry->createCDATASection(json_encode($value)); + $param->removeChild($param->firstChild); + $param->appendChild($newCdata); + } + } + } + } + return $changes; + } + + /** + * Build the XML Registry if not already built, and return it. + * @static + * @param bool $extendedVersion + * @return \DOMDocument The registry + */ + private function getXmlRegistry($extendedVersion = true) + { + if (!isSet($this->xmlRegistry) || ($this->registryVersion == "light" && $extendedVersion)) { + $this->buildXmlRegistry( $extendedVersion ); + $this->registryVersion = ($extendedVersion ? "extended":"light"); + } + return $this->xmlRegistry; + } + + /** + * Replace the current xml registry + * @static + * @param $registry + * @param bool $extendedVersion + */ + private function updateXmlRegistry($registry, $extendedVersion = true) + { + $this->xmlRegistry = $registry; + $this->registryVersion = ($extendedVersion? "extended" : "light"); + } + + /** + * Get a unique string identifier for caching purpose + * @param bool $extendedVersion + * @return string + */ + private function getRegistryCacheKey($extendedVersion = true) + { + $phpContext = 'session'; + if(ApplicationState::getSapiRestBase() !== null){ + $phpContext = 'rest'; + }else if(ApplicationState::sapiIsCli()){ + $phpContext = 'cli'; + } + $v = $extendedVersion ? "extended" : "light"; + return "xml_registry:". $phpContext . ":" . $v . ":" . $this->context->getStringIdentifier(); + } + + /** + * Central function of the registry construction, merges some nodes into the existing registry. + * @param \DOMDocument $original + * @param $parentName + * @param $uuidAttr + * @param $childrenNodes + * @param bool $doNotOverrideChildren + * @return void + */ + private function mergeNodes(&$original, $parentName, $uuidAttr, $childrenNodes, $doNotOverrideChildren = false) + { + // find or create parent + $parentSelection = $original->getElementsByTagName($parentName); + if ($parentSelection->length) { + $parentNode = $parentSelection->item(0); + $xPath = new \DOMXPath($original); + /** @var \DOMElement $child */ + foreach ($childrenNodes as $child) { + if($child->nodeType != XML_ELEMENT_NODE) continue; + if ($child->getAttribute($uuidAttr) == "*") { + $query = $parentName.'/'.$child->nodeName; + } else { + $query = $parentName.'/'.$child->nodeName.'[@'.$uuidAttr.' = "'.$child->getAttribute($uuidAttr).'"]'; + } + $childrenSel = $xPath->query($query); + if ($childrenSel->length) { + if($doNotOverrideChildren) continue; + /** @var \DOMElement $existingNode */ + foreach ($childrenSel as $existingNode) { + if($existingNode->getAttribute("forbidOverride") == "true"){ + continue; + } + // Clone as many as needed + $clone = $original->importNode($child, true); + $this->mergeChildByTagName($clone, $existingNode); + } + } else { + $clone = $original->importNode($child, true); + $parentNode->appendChild($clone); + } + } + } else { + //create parentNode and append children + if ($childrenNodes->length) { + $parentNode = $original->importNode($childrenNodes->item(0)->parentNode, true); + $original->documentElement->appendChild($parentNode); + } else { + $parentNode = $original->createElement($parentName); + $original->documentElement->appendChild($parentNode); + } + } + } + + /** + * Utilitary function + * @param \DOMNode $new + * @param \DOMNode $old + */ + private function mergeChildByTagName($new, &$old) + { + if (!$this->hasElementChild($new) || !$this->hasElementChild($old)) { + $old->parentNode->replaceChild($new, $old); + return; + } + /** @var \DOMElement $newChild */ + foreach ($new->childNodes as $newChild) { + if($newChild->nodeType != XML_ELEMENT_NODE) continue; + + $found = null; + /** @var \DOMElement $oldChild */ + foreach ($old->childNodes as $oldChild) { + if($oldChild->nodeType != XML_ELEMENT_NODE) continue; + if ($oldChild->nodeName == $newChild->nodeName) { + $found = $oldChild; + } + } + if ($found != null) { + if ($newChild->nodeName == "post_processing" || $newChild->nodeName == "pre_processing") { + $old->appendChild($newChild->cloneNode(true)); + } else { + if($found->getAttribute("forbidOverride") == "true") { + continue; + } + $this->mergeChildByTagName($newChild->cloneNode(true), $found); + } + } else { + // CloneNode or it's messing with the current foreach loop. + $old->appendChild($newChild->cloneNode(true)); + } + } + } + + /** + * Utilitary + * @param \DOMNode $node + * @return bool + */ + private function hasElementChild($node) + { + if(!$node->hasChildNodes()) return false; + foreach ($node->childNodes as $child) { + if($child->nodeType == XML_ELEMENT_NODE) return true; + } + return false; + } + + /** + * PluginsService constructor. + * @param ContextInterface|null $ctx + * @param string|null $pluginsDir + */ + private function __construct($ctx = null, $pluginsDir = null) + { + $this->context = $ctx; + if(empty($pluginsDir)){ + $pluginsDir = AJXP_INSTALL_PATH.DIRECTORY_SEPARATOR.AJXP_PLUGINS_FOLDER; + } + $this->pluginsDir = $pluginsDir; + } + + public function __clone() + { + trigger_error("Cannot clone me, i'm a singleton!", E_USER_ERROR); + } +} diff --git a/core/src/core/src/pydio/Core/PluginFramework/SqlTableProvider.php b/core/src/core/src/pydio/Core/PluginFramework/SqlTableProvider.php new file mode 100644 index 0000000000..f32bba6938 --- /dev/null +++ b/core/src/core/src/pydio/Core/PluginFramework/SqlTableProvider.php @@ -0,0 +1,42 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\PluginFramework; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Interface SqlTableProvider + * Plugins that need to have their SQL tables installed at startup + * must implement this interface. + * @package Pydio + * @subpackage Core + * @interface SqlTableProvider + */ +interface SqlTableProvider{ + + /** + * Install SQL table using a dibi driver data + * @param $param array("SQL_DRIVER" => $dibiDriverData) + * @return mixed + */ + public function installSQLTables($param); + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Serializer/NodeXML.php b/core/src/core/src/pydio/Core/Serializer/NodeXML.php new file mode 100644 index 0000000000..93443d10ca --- /dev/null +++ b/core/src/core/src/pydio/Core/Serializer/NodeXML.php @@ -0,0 +1,82 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Serializer; + +use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Core\Utils\Vars\StringHelper; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * XML Serializer for AJXP_Node objects + * @package Pydio\Core\Serializer + */ +class NodeXML +{ + + /** + * The basic node + * @static + * @param string $nodeName + * @param string $nodeLabel + * @param bool $isLeaf + * @param array $metaData + * @param bool $close + * @return string|void + * @internal param bool $print + */ + public static function toNode($nodeName, $nodeLabel, $isLeaf, $metaData = array(), $close = true) + { + $string = " $value) { + if (InputFilter::detectXSS($value)) $value = "XSS Detected!"; + $value = StringHelper::xmlEntities($value, true); + $string .= " $key=\"$value\""; + } + if ($close) { + $string .= "/>"; + } else { + $string .= ">"; + } + return $string; + } + + /** + * @static + * @param \Pydio\Access\Core\Model\AJXP_Node $node + * @param bool $close + * @return string|void + */ + public static function toXML($node, $close = true) + { + return NodeXML::toNode($node->getPath(), $node->getLabel(), $node->isLeaf(), $node->getNodeInfoMeta(), $close); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Serializer/RepositoryXML.php b/core/src/core/src/pydio/Core/Serializer/RepositoryXML.php new file mode 100644 index 0000000000..e5a04a396e --- /dev/null +++ b/core/src/core/src/pydio/Core/Serializer/RepositoryXML.php @@ -0,0 +1,203 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Serializer; + +use Pydio\Core\Model\Context; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Model\RepositoryInterface; +use Pydio\Core\Model\UserInterface; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Services\UsersService; +use Pydio\Core\Services\ApplicationState; +use Pydio\Core\Utils\Vars\StringHelper; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Class RepositoryXML + * @package Pydio\Core\Serializer + */ +class RepositoryXML +{ + /** + * @var array + */ + protected $exposed; + + /** + * @var array + */ + protected $streams; + + /** + * @var UserInterface + */ + protected $loggedUser; + + /** + * RepositoryXML constructor. + * @param array $exposed + * @param array $streams + * @param UserInterface $loggedUser + */ + public function __construct($exposed, $streams, $loggedUser = null) + { + $this->exposed = $exposed; + $this->streams = $streams; + $this->loggedUser = $loggedUser; + } + + /** + * @param RepositoryInterface $repository + * @param $isActive + * @param $accessStatus + * @return string + */ + public function serialize(RepositoryInterface $repository, $isActive, $accessStatus){ + return $this->repositoryToXML($repository, $isActive, $this->exposed, $this->streams, $this->loggedUser, $accessStatus); + } + + + /** + * @param RepositoryInterface $repoObject + * @param bool $isActive + * @param array $exposed + * @param array $streams + * @param UserInterface $loggedUser + * @param string $accessStatus + * @return string + * @throws \Exception + */ + protected function repositoryToXML($repoObject, $isActive, $exposed, $streams, $loggedUser, $accessStatus = ""){ + + $repoId = $repoObject->getId(); + $statusString = " repository_type=\"".$repoObject->getRepositoryType()."\""; + if(empty($accessStatus)){ + $accessStatus = $repoObject->getAccessStatus(); + } + if(!empty($accessStatus)){ + $statusString .= " access_status=\"$accessStatus\" "; + }else if($loggedUser != null){ + $lastConnected = $loggedUser->getArrayPref("repository_last_connected", $repoId); + if(!empty($lastConnected)) $statusString .= " last_connection=\"$lastConnected\" "; + } + + $streamString = ""; + if (in_array($repoObject->getAccessType(), $streams)) { + $streamString = "allowCrossRepositoryCopy=\"true\""; + } + if ($repoObject->getUniqueUser()) { + $streamString .= " user_editable_repository=\"true\" "; + } + if ($repoObject->hasContentFilter()){ + $streamString .= " hasContentFilter=\"true\""; + } + $slugString = ""; + $slug = $repoObject->getSlug(); + if (!empty($slug)) { + $slugString = "repositorySlug=\"$slug\""; + } + $isSharedString = ""; + $currentUserIsOwner = false; + $ownerLabel = null; + if ($repoObject->hasOwner()) { + $uId = $repoObject->getOwner(); + if($loggedUser != null && $loggedUser->getId() == $uId){ + $currentUserIsOwner = true; + } + $label = UsersService::getUserPersonalParameter("USER_DISPLAY_NAME", $uId, "core.conf", $uId); + $ownerLabel = $label; + $isSharedString = 'owner="'. StringHelper::xmlEntities($label) .'"'; + } + if ($repoObject->securityScope() == "USER" || $currentUserIsOwner){ + $streamString .= " userScope=\"true\""; + } + + $descTag = ""; + $description = $repoObject->getDescription(ApplicationState::hasMinisiteHash(), $ownerLabel); + if (!empty($description)) { + $descTag = ''. StringHelper::xmlEntities($description, true) .''; + } + $ctx = Context::contextWithObjects($loggedUser, $repoObject); + $roleString=""; + if($loggedUser != null){ + $merged = $loggedUser->getMergedRole(); + $params = array(); + foreach($exposed as $exposed_prop){ + $metaOptions = $repoObject->getContextOption($ctx, "META_SOURCES"); + if(!isSet($metaOptions[$exposed_prop["PLUGIN_ID"]])){ + continue; + } + $value = $exposed_prop["DEFAULT"]; + if(isSet($metaOptions[$exposed_prop["PLUGIN_ID"]][$exposed_prop["NAME"]])){ + $value = $metaOptions[$exposed_prop["PLUGIN_ID"]][$exposed_prop["NAME"]]; + } + $value = $merged->filterParameterValue($exposed_prop["PLUGIN_ID"], $exposed_prop["NAME"], $repoId, $value); + if($value !== null){ + if($value === true || $value === false) $value = ($value === true ?"true":"false"); + $params[] = ''; + $roleString .= str_replace(".", "_",$exposed_prop["PLUGIN_ID"])."_".$exposed_prop["NAME"].'="'. StringHelper::xmlEntities($value) .'" '; + } + } + $roleString.='acl="'.$merged->getAcl($repoId).'"'; + if($merged->hasMask($repoId)){ + $roleString.= ' hasMask="true" '; + } + } + $clientSettings = (!$isActive ? "" : $this->repositoryClientSettings($repoObject, $ctx)); + return "getAccessType()."\" id=\"".$repoId."\"$statusString $streamString $slugString $isSharedString $roleString>".$descTag.$clientSettings.""; + + } + + /** + * @param RepositoryInterface $repoObject + * @param ContextInterface $ctx + * @return string + */ + protected function repositoryClientSettings($repoObject, $ctx){ + + $plugin = $repoObject->getDriverInstance($ctx); + if(empty($plugin)){ + $plugin = PluginsService::getInstance($ctx)->getPluginByTypeName("access", $repoObject->getAccessType()); + } + if(empty($plugin)){ + return ""; + } + if ($repoObject->hasParent()) { + $parentObject = $repoObject->getParentRepository(); + if ($parentObject != null && $parentObject->isTemplate()) { + $ic = $parentObject->getContextOption($ctx, "TPL_ICON_SMALL"); + $settings = $plugin->getManifestRawContent("//client_settings", "node"); + if (!empty($ic) && $settings->length) { + $newAttr = $settings->item(0)->ownerDocument->createAttribute("icon_tpl_id"); + $newAttr->nodeValue = $repoObject->getParentId(); + $settings->item(0)->appendChild($newAttr); + return $settings->item(0)->ownerDocument->saveXML($settings->item(0)); + } + } + } + return $plugin->getManifestRawContent("//client_settings", "string"); + + } + + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Serializer/UserXML.php b/core/src/core/src/pydio/Core/Serializer/UserXML.php new file mode 100644 index 0000000000..5f83320280 --- /dev/null +++ b/core/src/core/src/pydio/Core/Serializer/UserXML.php @@ -0,0 +1,198 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Serializer; + +use Pydio\Access\Core\IAjxpWrapperProvider; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Core\Utils\Vars\XMLFilter; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\UsersService; +use Pydio\Core\Services\ApplicationState; +use Pydio\Core\Utils\Vars\StringHelper; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class UserXML + * @package Pydio\Core\Serializer + */ +class UserXML +{ + /** + * List all bookmmarks as XML + * @static + * @param $allBookmarks + * @param ContextInterface $context + * @param bool $print + * @param string $format legacy|node_list + * @return string + */ + public static function writeBookmarks($allBookmarks, $context, $print = true, $format = "legacy") + { + $driver = false; + $repository = $context->getRepository(); + if ($format == "node_list") { + $driver = $repository->getDriverInstance($context); + if (!($driver instanceof IAjxpWrapperProvider)) { + $driver = false; + } + } + $buffer = ""; + foreach ($allBookmarks as $bookmark) { + $path = ""; + $title = ""; + if (is_array($bookmark)) { + $path = $bookmark["PATH"]; + $title = $bookmark["TITLE"]; + } else if (is_string($bookmark)) { + $path = $bookmark; + $title = basename($bookmark); + } + if ($format == "node_list") { + if ($driver) { + $node = new AJXP_Node($context->getUrlBase() . $path); + $buffer .= NodeXML::toXML($node, true); + } else { + $buffer .= NodeXML::toNode($path, $title, false, array('icon' => "mime_empty.png"), true); + } + } else { + $buffer .= ""; + } + } + if ($print) { + print $buffer; + return null; + } else { + return $buffer; + } + } + + /** + * Extract all the user data and put it in XML + * @param ContextInterface $ctx + * @return string + */ + public function serialize(ContextInterface $ctx) + { + $buffer = ""; + $loggedUser = $ctx->getUser(); + $currentRepoId = $ctx->getRepositoryId(); + $confDriver = ConfService::getConfStorageImpl(); + + if (!UsersService::usersEnabled()) { + $buffer.=""; + $buffer.=""; + $buffer.= $this->writeRepositoriesData($ctx); + $buffer.=""; + } else if ($loggedUser !== null) { + $lock = $loggedUser->getLock(); + $buffer.="getId()."\">"; + $buffer.="canWrite($currentRepoId)?"1":"0")."\" read=\"".($loggedUser->canRead($currentRepoId)?"1":"0")."\"/>"; + $buffer.= $this->writeRepositoriesData($ctx); + $buffer.=""; + $preferences = $confDriver->getExposedPreferences($loggedUser); + foreach ($preferences as $prefName => $prefData) { + $atts = ""; + if (isSet($prefData["exposed"]) && $prefData["exposed"] == true) { + foreach ($prefData as $k => $v) { + if($k=="name") continue; + if($k == "value") $k = "default"; + $atts .= "$k='$v' "; + } + } + if (isset($prefData["pluginId"])) { + $atts .= "pluginId='".$prefData["pluginId"]."' "; + } + if ($prefData["type"] == "string") { + $buffer.=""; + } else if ($prefData["type"] == "json") { + $buffer.=""; + } + } + $buffer.=""; + $buffer.="isAdmin()?"1":"0")."\" ".($lock!==false?"lock=\"$lock\"":"")."/>"; + $buffer.=""; + } + return $buffer; + } + + /** + * Write the repositories access rights in XML format + * @static + * @param ContextInterface $ctx + * @return string + */ + protected function writeRepositoriesData(ContextInterface $ctx) + { + $loggedUser = $ctx->getUser(); + $accessible = UsersService::getRepositoriesForUser($loggedUser); + $streams = PluginsService::detectRepositoriesStreams($accessible); + $exposed = PluginsService::searchManifestsWithCache("//server_settings/param[contains(@scope,'repository') and @expose='true']", function($nodes){ + $exposedNodes = []; + /** @var \DOMElement $exposed_prop */ + foreach($nodes as $exposed_prop){ + $pluginId = $exposed_prop->parentNode->parentNode->getAttribute("id"); + $paramName = $exposed_prop->getAttribute("name"); + $paramDefault = $exposed_prop->getAttribute("default"); + $exposedNodes[] = array("PLUGIN_ID" => $pluginId, "NAME" => $paramName, "DEFAULT" => $paramDefault); + } + return $exposedNodes; + }); + $repoSerializer = new RepositoryXML($exposed, $streams, $loggedUser); + + + $st = ""; + $inboxStatus = 0; + foreach($accessible as $repoId => $repoObject){ + if(!$repoObject->hasContentFilter()) { + continue; + } + $accessStatus = $repoObject->getAccessStatus(); + if(empty($accessStatus) && $loggedUser != null){ + $lastConnected = $loggedUser->getArrayPref("repository_last_connected", $repoId); + if(empty($lastConnected)){ + $accessStatus = 1; + } + } + if(!empty($accessStatus)){ + $inboxStatus ++; + } + } + + foreach ($accessible as $repoId => $repoObject) { + if(!ApplicationState::hasMinisiteHash() && $repoObject->hasContentFilter()){ + continue; + } + $accessStatus = ''; + if($repoObject->getAccessType() == "inbox"){ + $accessStatus = $inboxStatus; + } + $xmlString = $repoSerializer->serialize($repoObject, $repoId === $ctx->getRepositoryId(), $accessStatus); + $st .= $xmlString; + } + + $st .= ""; + return $st; + } + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Services/ApiKeysService.php b/core/src/core/src/pydio/Core/Services/ApiKeysService.php new file mode 100644 index 0000000000..bfc915de4d --- /dev/null +++ b/core/src/core/src/pydio/Core/Services/ApiKeysService.php @@ -0,0 +1,235 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Services; + +use Pydio\Conf\Sql\SqlConfDriver; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Utils\Http\UserAgent; +use Pydio\Core\Utils\Vars\StringHelper; +use Pydio\Log\Core\Logger; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class ApiKeysService + * @package Pydio\Core\Services + */ +class ApiKeysService +{ + /** + * @return SqlConfDriver + * @throws PydioException + */ + private static function getStore(){ + $store = ConfService::getConfStorageImpl(); + if (!($store instanceof SqlConfDriver)){ + throw new PydioException("Wrong configuration driver, please use Sql"); + } + return $store; + } + + /** + * @param $userId + * @param string $deviceId + * @param string $deviceUA + * @param string $deviceIP + * @return array + * @throws PydioException + * @throws \Exception + */ + public static function generatePairForAuthfront($userId, $deviceId = "", $deviceUA = "", $deviceIP = ""){ + + $store = self::getStore(); + $token = StringHelper::generateRandomString(); + $private = StringHelper::generateRandomString(); + $data = array("USER_ID" => $userId, "PRIVATE" => $private); + if (!empty($deviceId)) { + // Revoke previous tokens for this device + $cursor = null; + $keys = $store->simpleStoreList("keystore", $cursor, "", "serial", '%"DEVICE_ID";s:' . strlen($deviceId) . ':"' . $deviceId . '"%'); + foreach ($keys as $keyId => $keyData) { + if ($keyData["USER_ID"] != $userId) continue; + $store->simpleStoreClear("keystore", $keyId); + } + $data["DEVICE_ID"] = $deviceId; + } + $data["DEVICE_UA"] = $deviceUA; + $data["DEVICE_IP"] = $deviceIP; + $store->simpleStoreSet("keystore", $token, $data, "serial"); + return ["t" => $token, "p" => $private]; + + } + + /** + * @param $userId + * @param $adminTaskId + * @param string $restrictToIP + * @return array + * @throws PydioException + * @throws \Exception + */ + public static function generatePairForAdminTask($adminTaskId, $userId = "", $restrictToIP = ""){ + + $store = self::getStore(); + $token = StringHelper::generateRandomString(); + $private = StringHelper::generateRandomString(); + $data = [ + "PRIVATE" => $private, + "ADMIN_TASK_ID" => $adminTaskId + ]; + if(!empty($userId)){ + $data["USER_ID"] = $userId; + } + if(!empty($restrictToIP)){ + $data["RESTRICT_TO_IP"] = $restrictToIP; + } + $store->simpleStoreSet("keystore", $token, $data, "serial"); + return ["t" => $token, "p" => $private]; + + } + + /** + * @param $adminTaskId + * @param $userId + * @return array|null + * @throws PydioException + */ + public static function findPairForAdminTask($adminTaskId, $userId = ""){ + + $keys = self::getStore()->simpleStoreList("keystore", $cursor, "", "serial", '%"ADMIN_TASK_ID";s:' . strlen($adminTaskId) . ':"' . $adminTaskId . '"%'); + foreach($keys as $kId => $kData){ + if(empty($userId) || $kData["USER_ID"] === $userId){ + return ["t" => $kId, "p" => $kData["PRIVATE"]]; + } + } + return null; + + } + + /** + * @param string $adminTaskId + * @param string $userId + * @return integer number of deleted keys + * @throws PydioException + */ + public static function revokePairForAdminTask($adminTaskId, $userId = ""){ + + $keys = self::getStore()->simpleStoreList("keystore", $cursor, "", "serial", '%"ADMIN_TASK_ID";s:' . strlen($adminTaskId) . ':"' . $adminTaskId . '"%'); + $c = 0; + foreach($keys as $kId => $kData){ + if(empty($userId) || $kData["USER_ID"] === $userId){ + self::getStore()->simpleStoreClear("keystore", $kId); + $c++; + } + } + return $c; + + } + + /** + * @param $serverData + * @param $adminTaskId + * @param $userId + * @return bool + */ + public static function requestHasValidHeadersForAdminTask($serverData, $adminTaskId, $userId = ""){ + if(!isSet($serverData['HTTP_X_PYDIO_ADMIN_AUTH'])){ + Logger::error(__CLASS__, __FUNCTION__,"Invalid tokens for admin task $adminTaskId"); + return false; + } + list($t, $p) = explode(":", trim($serverData['HTTP_X_PYDIO_ADMIN_AUTH'])); + $existingKey = self::findPairForAdminTask(PYDIO_BOOSTER_TASK_IDENTIFIER); + if($existingKey === null || $existingKey['p'] !== $p || $existingKey['t'] !== $t){ + Logger::error(__CLASS__, __FUNCTION__, "Invalid tokens for admin task $adminTaskId"); + return false; + } + Logger::debug(__CLASS__, "Valid tokens for admin task $adminTaskId"); + return true; + } + + /** + * @param $token + * @param string $checkPrivate + * @return bool + * @throws PydioException + */ + public static function loadDataForPair($token, $checkPrivate = ""){ + $data = null; + self::getStore()->simpleStoreGet("keystore", $token, "serial", $data); + if (empty($data)) { + return false; + } + if(!empty($checkPrivate)){ + $p = $data["PRIVATE"]; + unset($data["PRIVATE"]); + return $checkPrivate === $p ? $data : false; + } + return $data; + } + + /** + * @param $userId + * @param $token + * @return int + * @throws PydioException + */ + public static function revokeTokens($userId, $token = ""){ + $keys = self::getStore()->simpleStoreList("keystore", $cursor, $token, "serial", '%"USER_ID";s:' . strlen($userId) . ':"' . $userId . '"%'); + $c = 0; + foreach ($keys as $keyId => $keyData) { + $c ++; + self::getStore()->simpleStoreClear("keystore", $keyId); + } + return $c; + } + + /** + * @param $userId + * @return array + * @throws PydioException + */ + public static function listPairsForUser($userId){ + $cursor = null; + $keys = self::getStore()->simpleStoreList("keystore", $cursor, "", "serial", '%"USER_ID";s:' . strlen($userId) . ':"' . $userId . '"%'); + foreach ($keys as $keyId => &$keyData) { + unset($keyData["PRIVATE"]); + unset($keyData["USER_ID"]); + $deviceDesc = "Web Browser"; + $deviceOS = "Unkown"; + if (isSet($keyData["DEVICE_UA"])) { + $agent = $keyData["DEVICE_UA"]; + if (strpos($agent, "python-requests") !== false) { + $deviceDesc = "PydioSync"; + if (strpos($agent, "Darwin") !== false) $deviceOS = "Mac OS X"; + else if (strpos($agent, "Windows/7") !== false) $deviceOS = "Windows 7"; + else if (strpos($agent, "Windows/8") !== false) $deviceOS = "Windows 8"; + else if (strpos($agent, "Linux") !== false) $deviceOS = "Linux"; + } else { + $deviceOS = UserAgent::osFromUserAgent($agent); + } + } + $keyData["DEVICE_DESC"] = $deviceDesc; + $keyData["DEVICE_OS"] = $deviceOS; + } + return $keys; + } + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Services/ApplicationState.php b/core/src/core/src/pydio/Core/Services/ApplicationState.php new file mode 100644 index 0000000000..a881179d99 --- /dev/null +++ b/core/src/core/src/pydio/Core/Services/ApplicationState.php @@ -0,0 +1,258 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Services; + +use Pydio\Core\Model\RepositoryInterface; +use Pydio\Core\Utils\Vars\PathUtils; +use Pydio\Log\Core\Logger; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Various helpers to get application state, global folders, etc. + * @package Pydio\Core\Utils + */ +class ApplicationState +{ + /** + * @var null|string + */ + private static $restBase = null; + + /** + * @var string + */ + private static $sapiType = "session"; + + /** + * @var string + */ + private static $minisiteHash = null; + + /** + * @param string $restBase + */ + public static function setSapiRestBase($restBase){ + self::$sapiType = "rest"; + self::$restBase = $restBase; + } + + public static function setSapiTypeCLI(){ + self::$sapiType = "cli"; + } + + /** + * @return bool + */ + public static function sapiIsCli(){ + return php_sapi_name() === "cli" || self::$sapiType === "cli"; + } + + /** + * @return null|string + */ + public static function getSapiRestBase(){ + return self::$restBase; + } + + /** + * @return bool + */ + public static function sapiUsesSession(){ + return !self::sapiIsCli() && self::$restBase === null; + } + + /** + * @param $hash + */ + public static function setStateMinisite($hash){ + self::$minisiteHash = $hash; + } + + /** + * @return bool + */ + public static function hasMinisiteHash(){ + return self::$minisiteHash !== null; + } + + /** + * @return string + */ + public static function getMinisiteHash(){ + return self::$minisiteHash; + } + + /** + * Check if data/cache/first_run_passed file exists or not + * @return bool + */ + public static function detectApplicationFirstRun() + { + return !file_exists(AJXP_CACHE_DIR . "/first_run_passed") && !file_exists(AJXP_DATA_PATH . "/plugins/boot.conf/first_run_passed"); + } + + /** + * Touch data/cache/first_run_passed file + */ + public static function setApplicationFirstRunPassed() + { + if(!is_file(AJXP_DATA_PATH . "/plugins/boot.conf")){ + @mkdir(AJXP_DATA_PATH . "/plugins/boot.conf", 0666, true); + } + @file_put_contents(AJXP_DATA_PATH . "/plugins/boot.conf/first_run_passed", "true"); + } + + /** + * Search include path for a given file + * @static + * @param string $file + * @return bool + */ + public static function searchIncludePath($file) + { + $ps = explode(PATH_SEPARATOR, ini_get('include_path')); + foreach ($ps as $path) { + if (@file_exists($path . DIRECTORY_SEPARATOR . $file)) return true; + } + if (@file_exists($file)) return true; + return false; + } + + /** + * @static + * @param $from + * @param $to + * @return string + */ + public static function getTravelPath($from, $to) + { + $from = explode('/', $from); + $to = explode('/', $to); + $relPath = $to; + + foreach ($from as $depth => $dir) { + // find first non-matching dir + if ($dir === $to[$depth]) { + // ignore this directory + array_shift($relPath); + } else { + // get number of remaining dirs to $from + $remaining = count($from) - $depth; + if ($remaining > 1) { + // add traversals up to first matching dir + $padLength = (count($relPath) + $remaining - 1) * -1; + $relPath = array_pad($relPath, $padLength, '..'); + break; + } else { + $relPath[0] = './' . $relPath[0]; + } + } + } + return implode('/', $relPath); + } + + /** + * Build the current server URL + * @param bool $withURI Wether to return the "path" part in scheme://host[/path] + * @param bool $forceInternal Wether to force server local IP detection, or to use external URL if it is set in the configuration + * @static + * @return string + */ + public static function detectServerURL($withURI = false, $forceInternal = false) + { + $setUrl = ConfService::getGlobalConf("SERVER_URL"); + if (!empty($setUrl) && !$forceInternal) { + return (string)$setUrl; + } + if (self::sapiIsCli()) { + Logger::debug("WARNING, THE SERVER_URL IS NOT SET, WE CANNOT BUILD IT WHEN WORKING IN CLI"); + return ""; + } + $protocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http'); + $port = (($protocol === 'http' && $_SERVER['SERVER_PORT'] == 80 || $protocol === 'https' && $_SERVER['SERVER_PORT'] == 443) + ? "" : ":" . $_SERVER['SERVER_PORT']); + $name = $_SERVER["SERVER_NAME"]; + if (!$withURI) { + return "$protocol://$name$port"; + } else { + $uri = dirname($_SERVER["REQUEST_URI"]); + $apiBase = self::getSapiRestBase(); + if (!empty($apiBase)) { + // apiBase must be /path/to/pydio/endpoint => remove endpoint + $uri = PathUtils::forwardSlashDirname(rtrim($apiBase)); + if(empty($uri)) $uri = "/"; + } + return "$protocol://$name$port" . $uri; + } + } + + /** + * @param RepositoryInterface $repository + * @return string + */ + public static function getWorkspaceShortcutURL($repository) + { + if (empty($repository)) { + return ""; + } + $repoSlug = $repository->getSlug(); + $skipHistory = ConfService::getGlobalConf("SKIP_USER_HISTORY", "conf"); + if ($skipHistory) { + $prefix = "/ws-"; + } else { + $prefix = "?goto="; + } + return trim(self::detectServerURL(true), "/") . $prefix . $repoSlug; + } + + /** + * Try to load the tmp dir from the CoreConf AJXP_TMP_DIR, or the constant AJXP_TMP_DIR, + * or the sys_get_temp_dir + * @static + * @return mixed|null|string + */ + public static function getAjxpTmpDir() + { + $conf = ConfService::getGlobalConf("AJXP_TMP_DIR"); + if (!empty($conf)) { + return $conf; + } + if (defined("AJXP_TMP_DIR") && AJXP_TMP_DIR != "") { + return AJXP_TMP_DIR; + } + return realpath(sys_get_temp_dir()); + } + + /** + * Try to set an ini config, without errors + * @static + * @param string $paramName + * @param string $paramValue + * @return void + */ + public static function safeIniSet($paramName, $paramValue) + { + $current = ini_get($paramName); + if ($current == $paramValue) return; + @ini_set($paramName, $paramValue); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Services/AuthService.php b/core/src/core/src/pydio/Core/Services/AuthService.php new file mode 100644 index 0000000000..a62a02efd9 --- /dev/null +++ b/core/src/core/src/pydio/Core/Services/AuthService.php @@ -0,0 +1,173 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Services; +use Pydio\Auth\Core\MemorySafe; +use Pydio\Core\Controller\Controller; +use Pydio\Core\Exception\LoginException; +use Pydio\Core\Model\Context; +use Pydio\Core\Model\UserInterface; +use Pydio\Core\Utils\Http\BruteForceHelper; +use Pydio\Core\Utils\Http\CookiesHelper; +use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Log\Core\Logger; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Static access to the authentication mechanism. Encapsulates the authDriver implementation + * @package Pydio + * @subpackage Core + */ +class AuthService +{ + + public static $bufferedMessage = null; + + /** + * Log the user from its credentials + * @static + * @param string $user_id The user id + * @param string $pwd The password + * @param bool $bypass_pwd Ignore password or not + * @param bool $cookieLogin Is it a logging from the remember me cookie? + * @return UserInterface + * @throws LoginException + * @throws \Exception + * @throws \Pydio\Core\Exception\UserNotFoundException + */ + public static function logUser($user_id, $pwd, $bypass_pwd = false, $cookieLogin = false) + { + $user_id = UsersService::filterUserSensitivity($user_id); + $authDriver = ConfService::getAuthDriverImpl(); + // CHECK USER PASSWORD HERE! + $loginAttempt = BruteForceHelper::getBruteForceLoginArray(); + $bruteForceLogin = BruteForceHelper::checkBruteForceLogin($loginAttempt); + BruteForceHelper::setBruteForceLoginArray($loginAttempt); + + if (!$authDriver->userExists($user_id)) { + Logger::warning(__CLASS__, "Login failed", array("user" => InputFilter::sanitize($user_id, InputFilter::SANITIZE_EMAILCHARS), "error" => "Invalid user")); + if ($bruteForceLogin === FALSE) { + throw new LoginException(-4); + } else { + throw new LoginException(-1); + } + } + if (!$bypass_pwd) { + if (!UsersService::checkPassword($user_id, $pwd, $cookieLogin)) { + Logger::warning(__CLASS__, "Login failed", array("user" => InputFilter::sanitize($user_id, InputFilter::SANITIZE_EMAILCHARS), "error" => "Invalid password")); + if ($bruteForceLogin === FALSE) { + throw new LoginException(-4); + } else { + if($cookieLogin) throw new LoginException(-5); + throw new LoginException(-1); + } + } + } + // Successful login attempt + BruteForceHelper::setBruteForceLoginArray($loginAttempt, true); + + $user = UsersService::getUserById($user_id, false); + + $tempContext = Context::contextWithObjects($user, null); + Controller::applyHook("user.before_login", [$tempContext, &$user]); + + // Setting session credentials if asked in config + if (ConfService::getContextConf($tempContext, "SESSION_SET_CREDENTIALS", "auth")) { + list($authId, $authPwd) = $authDriver->filterCredentials($user_id, $pwd); + MemorySafe::storeCredentials($authId, $authPwd); + } + + + if ($user->hasLockByName("logout")) { + Logger::warning(__CLASS__, "Login failed", array("user" => InputFilter::sanitize($user_id, InputFilter::SANITIZE_EMAILCHARS), "error" => "Locked user")); + throw new LoginException(-1); + } + + if ($authDriver->isAjxpAdmin($user_id)) { + $user->setAdmin(true); + } + + if ($user->isAdmin()) { + $user = RolesService::updateAdminRights($user); + } + + if ($authDriver->autoCreateUser() && !$user->storageExists()) { + $user->save("superuser"); // make sure update rights now + } + + //self::updateSessionUser($user); + Controller::applyHook("user.after_login", [$tempContext, $user]); + + Logger::info(__CLASS__, "Log In", array("context"=> ApplicationState::sapiUsesSession() ?"WebUI":"API")); + return $user; + } + + /** + * Store the object in the session + * @static + * @param $userObject + * @return void + */ + public static function updateSessionUser($userObject) { + SessionService::save(SessionService::USER_KEY, $userObject); + } + + /** + * Force an acl change for the current session user + * @param $repositoryId + * @param $acl + */ + public static function updateSessionUserAcl($repositoryId, $acl){ + /** + * @var $u UserInterface + */ + $u = SessionService::fetch(SessionService::USER_KEY); + if($u instanceof UserInterface){ + $u->getPersonalRole()->setAcl($repositoryId, $acl); + $u->recomputeMergedRole(); + self::updateSessionUser($u); + } + } + + + /** + * Clear the session + * @static + * @return void + */ + public static function disconnect() + { + $user = SessionService::fetch(SessionService::USER_KEY); + if(empty($user) || !$user instanceof UserInterface){ + return; + } + $userId = $user->getId(); + Controller::applyHook("user.before_disconnect", array(Context::emptyContext(), $user)); + CookiesHelper::clearRememberCookie($user); + Logger::info(__CLASS__, "Log Out", ""); + SessionService::delete(SessionService::USER_KEY); + if (ConfService::getContextConf(Context::contextWithObjects($user, null), "SESSION_SET_CREDENTIALS", "auth")) { + MemorySafe::clearCredentials(); + } + Controller::applyHook("user.after_disconnect", array(Context::emptyContext(), $userId)); + } + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Services/CacheService.php b/core/src/core/src/pydio/Core/Services/CacheService.php new file mode 100644 index 0000000000..62d40af5e2 --- /dev/null +++ b/core/src/core/src/pydio/Core/Services/CacheService.php @@ -0,0 +1,168 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Services; + + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Static access to the caching mechanism. Encapsulates the cacheDriver implementation + * @package Pydio + * @subpackage Core + */ +class CacheService +{ + /** + * @param $namespace + * @param $id + * @return bool + */ + public static function contains($namespace, $id) { + $cacheDriver = ConfService::getCacheDriverImpl(); + + if ($cacheDriver) { + return $cacheDriver->contains($namespace, $id); + } + + return false; + } + + /** + * @param $namespace + * @param $id + * @param $object + * @param int $timelimit + * @return bool + */ + public static function save($namespace, $id, $object, $timelimit = 0) { + $cacheDriver = ConfService::getCacheDriverImpl(); + + if ($cacheDriver) { + //if($namespace === AJXP_CACHE_SERVICE_NS_SHARED) error_log("Saving data for $id"); + return $cacheDriver->save($namespace, $id, $object, $timelimit); + } + + return false; + } + + /** + * @param $namespace + * @param $id + * @param $object + * @param int $timelimit + * @return bool + */ + public static function saveWithTimestamp($namespace, $id, $object, $timelimit = 0){ + $cacheDriver = ConfService::getCacheDriverImpl(); + + if ($cacheDriver) { + //if($namespace === AJXP_CACHE_SERVICE_NS_SHARED) error_log("Saving data for $id with timestamp"); + return $cacheDriver->saveWithTimestamp($namespace, $id, $object, $timelimit); + } + + return false; + + } + + /** + * @param $namespace + * @param $id + * @return bool|mixed + */ + public static function fetch($namespace, $id) { + $cacheDriver = ConfService::getCacheDriverImpl(); + + if ($cacheDriver) { + $data = $cacheDriver->fetch($namespace, $id); + if($data !== false && $namespace === AJXP_CACHE_SERVICE_NS_SHARED){ + //error_log("Found data for $id"); + } + return $data; + } + + return false; + } + + /** + * @param $namespace + * @param array $ids + * @return bool|mixed + */ + public static function fetchMultiple($namespace, $ids) { + $cacheDriver = ConfService::getCacheDriverImpl(); + + if ($cacheDriver) { + return $cacheDriver->fetchMultiple($namespace, $ids); + } + + return false; + } + + /** + * @param $namespace + * @param $id + * @param array $idsToCheck + * @return bool|false|mixed + */ + public static function fetchWithTimestamps($namespace, $id, $idsToCheck){ + $cacheDriver = ConfService::getCacheDriverImpl(); + + if ($cacheDriver) { + $data = $cacheDriver->fetchWithTimestamps($namespace, $id, $idsToCheck); + if($data !== false && $namespace === AJXP_CACHE_SERVICE_NS_SHARED){ + //error_log("Found data for $id after checking timestamps for ".implode(",", $idsToCheck)); + } + return $data; + } + + return false; + + } + + /** + * @param $namespace + * @param $id + * @return bool + */ + public static function delete($namespace, $id) { + $cacheDriver = ConfService::getCacheDriverImpl(); + + if ($cacheDriver) { + return $cacheDriver->delete($namespace, $id); + } + + return false; + } + + /** + * @param $namespace + * @return bool + */ + public static function deleteAll($namespace) { + $cacheDriver = ConfService::getCacheDriverImpl(); + + if ($cacheDriver) { + return $cacheDriver->deleteAll($namespace); + } + + return false; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Services/ConfService.php b/core/src/core/src/pydio/Core/Services/ConfService.php new file mode 100644 index 0000000000..6245eaa7d2 --- /dev/null +++ b/core/src/core/src/pydio/Core/Services/ConfService.php @@ -0,0 +1,408 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Services; + + +use Pydio\Auth\Core\AbstractAuthDriver; +use Pydio\Cache\Core\AbstractCacheDriver; +use Pydio\Conf\Core\AbstractConfDriver; + +use Pydio\Core\Model\Context; + +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\PluginFramework\CoreInstanceProvider; +use Pydio\Core\Utils\Vars\VarsFilter; +use Pydio\Core\PluginFramework\Plugin; +use Pydio\Core\PluginFramework\PluginsService; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Configuration holder. Singleton class accessed statically, encapsulates the confDriver implementation. + * @package Pydio + * @subpackage Core + */ +class ConfService +{ + private static $instance; + + private $errors = array(); + private $configs = array(); + + + /** + * @return AbstractConfDriver + */ + public static function getBootConfStorageImpl() + { + $inst = PluginsService::getInstance(Context::emptyContext())->getPluginById("boot.conf"); + if (empty($inst)) { + $inst = PluginsService::getInstance(Context::emptyContext())->softLoad("boot.conf", array()); + } + return $inst; + } + + /** + * Initialize singleton + * @static + * @param string $installPath + * @param string $pluginDir + */ + public static function init($installPath=AJXP_INSTALL_PATH, $pluginDir="plugins") + { + $inst = self::getInstance(); + $inst->initInst(); + } + + /** + * Load the boostrap_* files and their configs + * @return void + */ + private function initInst() + { + // INIT AS GLOBAL + if (isSet($_SERVER["HTTPS"]) && strtolower($_SERVER["HTTPS"]) == "on") { + $this->configs["USE_HTTPS"] = true; + } + if (isSet($this->configs["USE_HTTPS"])) { + ApplicationState::safeIniSet("session.cookie_secure", true); + } + $this->configs["JS_DEBUG"] = AJXP_CLIENT_DEBUG; + $this->configs["SERVER_DEBUG"] = AJXP_SERVER_DEBUG; + + } + + /** + * Start the singleton + * @static + * @return void + */ + public static function start() + { + $inst = self::getInstance(); + $inst->startInst(); + $confStorageDriver = self::getConfStorageImpl(); + $userFile = $confStorageDriver->getUserClassFileName(); + if(!empty($userFile)){ + require_once($userFile); + } + } + /** + * Init CONF, AUTH drivers + * Init Repositories + * @return void + */ + public function startInst() + { + PluginsService::getInstance(Context::emptyContext())->setPluginUniqueActiveForType("conf", self::getConfStorageImpl()->getName()); + } + /** + * Get errors generated by the boot sequence (init/start) + * @static + * @return array + */ + public static function getErrors() + { + return self::getInstance()->errors; + } + + public static function clearAllCaches(){ + PluginsService::clearPluginsCache(); + LocaleService::clearMessagesCache(); + CacheService::deleteAll(AJXP_CACHE_SERVICE_NS_SHARED); + if(function_exists('opcache_reset')){ + opcache_reset(); + } + } + + /** + * @static + * @param $globalsArray + * @param string $interfaceCheck + * @param PluginsService|null $pService + * @return Plugin|null + */ + public static function instanciatePluginFromGlobalParams($globalsArray, $interfaceCheck = "", $pService = null) + { + $plugin = false; + if($pService === null){ + $pService = PluginsService::getInstance(Context::emptyContext()); + } + + if (is_string($globalsArray)) { + $globalsArray = array("instance_name" => $globalsArray); + } + + if (is_array($globalsArray) && !isSet($globalsArray["instance_name"]) && isSet($globalsArray["group_switch_value"])){ + $globalsArray["instance_name"] = $globalsArray["group_switch_value"]; + } + + if (isSet($globalsArray["instance_name"])) { + $pName = $globalsArray["instance_name"]; + unset($globalsArray["instance_name"]); + + $plugin = $pService->softLoad($pName, $globalsArray); + $plugin->performChecks(); + } + + if ($plugin != false && !empty($interfaceCheck)) { + if (!is_a($plugin, $interfaceCheck)) { + $plugin = false; + } + } + return $plugin; + + } + + /** + * Check the presence of mcrypt and option CMDLINE_ACTIVE + * @static + * @return bool + */ + public static function backgroundActionsSupported() + { + return ConfService::getGlobalConf("CMDLINE_ACTIVE"); + } + + /** + * @var AbstractConfDriver + */ + private static $tmpConfStorageImpl; + /** + * @var AbstractAuthDriver + */ + private static $tmpAuthStorageImpl; + /** + * @var AbstractCacheDriver + */ + private static $tmpCacheStorageImpl; + + /** + * @param $confStorage AbstractConfDriver + * @param $authStorage AbstractAuthDriver + * @param $cacheStorage AbstractCacheDriver + */ + public static function setTmpStorageImplementations($confStorage, $authStorage, $cacheStorage) + { + self::$tmpConfStorageImpl = $confStorage; + self::$tmpAuthStorageImpl = $authStorage; + self::$tmpCacheStorageImpl = $cacheStorage; + } + + /** + * Get conf driver implementation + * + * @return AbstractConfDriver + */ + public static function getConfStorageImpl() + { + if(isSet(self::$tmpConfStorageImpl)) return self::$tmpConfStorageImpl; + /** @var CoreInstanceProvider $p */ + $p = PluginsService::getInstance(Context::emptyContext())->getPluginById("core.conf"); + return $p->getImplementation(); + } + + /** + * Get auth driver implementation + * + * @return AbstractAuthDriver + */ + public static function getAuthDriverImpl() + { + if(isSet(self::$tmpAuthStorageImpl)) return self::$tmpAuthStorageImpl; + /** @var CoreInstanceProvider $p */ + $p = PluginsService::getInstance(Context::emptyContext())->getPluginById("core.auth"); + return $p->getImplementation(); + } + + /** + * Return info about auth plugins + * @return string + */ + public static function getInfo(){ + return "&a=".self::getAuthDriverImpl()->getStats(); + } + + /** + * Get auth driver implementation + * + * @return AbstractCacheDriver + */ + public static function getCacheDriverImpl() + { + if(isSet(self::$tmpCacheStorageImpl)) return self::$tmpCacheStorageImpl; + /** + * Get CacheService implementation, directly from the "empty" plugin registry + * @var CoreInstanceProvider $p + */ + $p = PluginsService::getInstance(Context::emptyContext())->getPluginById("core.cache"); + return $p->getImplementation(); + } + + + public function invalidateLoadedRepositories() + { + UsersService::invalidateCache(); + PluginsService::clearRegistryCaches(); + } + + + /** + * ZIP FEATURES + */ + + /** + * Check if the gzopen function exists + * @static + * @return bool + */ + public static function zipEnabled() + { + return (function_exists("gzopen") || function_exists("gzopen64")); + } + + /** + * Check if users are allowed to browse ZIP content + * @static + * @return bool + */ + public static function zipBrowsingEnabled() + { + if(!self::zipEnabled()) return false; + return !ConfService::getGlobalConf("DISABLE_ZIP_BROWSING"); + } + + /** + * Check if users are allowed to create ZIP archive + * @static + * @return bool + */ + public static function zipCreationEnabled() + { + if(!self::zipEnabled()) return false; + return ConfService::getGlobalConf("ZIP_CREATION"); + } + + /** + * Get a config by its name + * @static + * @param string $varName + * @return mixed + */ + public static function getConf($varName) + { + return self::getInstance()->getConfInst($varName); + } + /** + * Set a config by its name + * @static + * @param string $varName + * @param mixed $varValue + * @return void + */ + public static function setConf($varName, $varValue) + { + self::getInstance()->setConfInst($varName, $varValue); + } + /** + * See static method + * @param $varName + * @return mixed + */ + protected function getConfInst($varName) + { + if (isSet($this->configs[$varName])) { + return $this->configs[$varName]; + } + if (defined("AJXP_".$varName)) { + return constant("AJXP_".$varName); + } + return null; + } + /** + * See static method + * @param $varName + * @param $varValue + * @return void + */ + protected function setConfInst($varName, $varValue) + { + $this->configs[$varName] = $varValue; + } + + /** + * Get config from the core.$coreType plugin without context + * @static + * @param string $varName + * @param string $coreType + * @return mixed|null|string + */ + public static function getGlobalConf($varName, $coreType = "ajaxplorer") + { + $ctx = Context::emptyContext(); + $coreP = PluginsService::getInstance($ctx)->getPluginByTypeName("core", $coreType); + if($coreP === false) return null; + $confs = $coreP->getConfigs(); + return (isSet($confs[$varName]) ? VarsFilter::filter($confs[$varName], $ctx) : null); + } + + /** + * @param ContextInterface $ctx + * @param $varName + * @param string $coreType + * @return mixed + */ + public static function getContextConf(ContextInterface $ctx, $varName, $coreType = "ajaxplorer"){ + + $coreP = PluginsService::getInstance($ctx)->getPluginByTypeName("core", $coreType); + if($coreP === false) return null; + $confs = $coreP->getConfigs(); + if($ctx->hasUser()){ + $confs = $ctx->getUser()->getMergedRole()->filterPluginConfigs("core".$coreType, $confs, $ctx->getRepositoryId()); + } + return (isSet($confs[$varName]) ? VarsFilter::filter($confs[$varName], $ctx) : null); + + } + + /** + * Singleton method + * + * @return ConfService the service instance + */ + public static function getInstance() + { + if (!isSet(self::$instance)) { + $c = __CLASS__; + self::$instance = new $c; + } + return self::$instance; + } + + /** + * ConfService constructor. + */ + private function __construct(){} + public function __clone() + { + trigger_error("Cannot clone me, i'm a singleton!", E_USER_ERROR); + } + +} diff --git a/core/src/core/src/pydio/Core/Services/LocalCache.php b/core/src/core/src/pydio/Core/Services/LocalCache.php new file mode 100644 index 0000000000..0680acef5e --- /dev/null +++ b/core/src/core/src/pydio/Core/Services/LocalCache.php @@ -0,0 +1,177 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Services; + +defined('AJXP_EXEC') or die( 'Access not allowed'); +/** + * Generic caching system that can be used by the plugins. Use the static factory getItem() to generate + * a actual cached instance. + * @package Pydio + * @subpackage Core + */ +class LocalCache +{ + protected $cacheDir; + protected $cacheId; + protected $masterFile; + protected $dataCallback; + protected $idComputerCallback; + + /** + * Create an LocalCache instance + * @param string $pluginId + * @param string $filepath + * @param callable $dataCallback A function to generate the data cache. If no callback provided, will simply use the content of the master item as the cache data + * @param string $idComputerCallback A function to generate the ID of the cache. If not provided, will generate a random hash + * @return LocalCache + */ + public static function getItem($pluginId, $filepath, $dataCallback=null, $idComputerCallback = null) + { + if ($dataCallback == null) { + $dataCallback = array("Pydio\\Core\\Services\\LocalCache", "simpleCopy"); + } + return new LocalCache($pluginId,$filepath, $dataCallback, $idComputerCallback); + } + + /** + * The default dataCallback + * @static + * @param string $master + * @param string $target + * @return void + */ + public static function simpleCopy($master, $target) + { + file_put_contents($target, file_get_contents($master)); + } + + /** + * Clear a cache item associated with the master filepath + * @static + * @param String $pluginId + * @param String $filepath + * @return void + */ + public static function clearItem($pluginId, $filepath) + { + $inst = new LocalCache($pluginId,$filepath, false); + if (file_exists($inst->getId())) { + @unlink($inst->getId()); + } + } + + /** + * Actual Cache object. Should not be used directly, but via the factory static method getItem() + * @param $pluginId + * @param $filepath + * @param $dataCallback + * @param null $idComputerCallback + */ + function __construct($pluginId, $filepath, $dataCallback, $idComputerCallback = NULL) { + $this->cacheDir = (defined('AJXP_SHARED_CACHE_DIR')?AJXP_SHARED_CACHE_DIR:AJXP_CACHE_DIR); + $this->masterFile = $filepath; + $this->dataCallback = $dataCallback; + if ($idComputerCallback != null) { + $this->idComputerCallback = $idComputerCallback; + } + $this->cacheId = $this->buildCacheId($pluginId, $filepath); + } + + /** + * Load the actual data, either from the cache or from the master, and save it in the cache if necessary. + * @return string + */ + public function getData() + { + if (!$this->hasCachedVersion()) { + $result = call_user_func($this->dataCallback, $this->masterFile, $this->cacheId); + if ($result !== false) { + $this->touch(); + } + } else { + + } + return file_get_contents($this->cacheId); + } + + /** + * Check if the cache dir is writeable + * @return bool + */ + public function writeable() + { + return is_dir($this->cacheDir) && is_writeable($this->cacheDir); + } + + /** + * The unique ID of the item + * @return string + */ + public function getId() + { + return $this->cacheId; + } + + /** + * Check whether a cached version of the master file exists or not + * @return bool + */ + public function hasCachedVersion() + { + $modifTime = filemtime($this->masterFile); + if (file_exists($this->cacheId) && filemtime($this->cacheId) >= $modifTime) { + return true; + } + return false; + } + + /** + * Refresh the cached version modif date to the master modif date + * @return void + */ + public function touch() + { + @touch($this->cacheId, filemtime($this->masterFile)); + } + + /** + * Generate an ID for the cached file, either using the idComputerCallback, or a simple hash function. + * @param $pluginId + * @param $filePath + * @return string + */ + protected function buildCacheId($pluginId, $filePath) + { + if (!is_dir($this->cacheDir."/".$pluginId)) { + mkdir($this->cacheDir."/".$pluginId, 0755); + } + $root = $this->cacheDir ."/".$pluginId."/"; + if (isSet($this->idComputerCallback)) { + $hash = call_user_func($this->idComputerCallback, $filePath); + } else { + $info = pathinfo($filePath); + $hash = md5($filePath).(!empty($info["extension"])?".".$info["extension"]:""); + } + return $root.$hash; + } + + +} diff --git a/core/src/core/src/pydio/Core/Services/LocaleService.php b/core/src/core/src/pydio/Core/Services/LocaleService.php new file mode 100644 index 0000000000..fab8ff03ae --- /dev/null +++ b/core/src/core/src/pydio/Core/Services/LocaleService.php @@ -0,0 +1,246 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Services; + +use Pydio\Core\Model\Context; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Utils\Vars\VarsFilter; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Class LocaleService + * @package Pydio\Core\Services + */ +class LocaleService +{ + /** + * @var LocaleService + */ + private static $instance; + + private $cache = []; + + private $currentLanguage; + + /** + * Singleton method + * + * @return LocaleService the service instance + */ + public static function getInstance() + { + if (!isSet(self::$instance)) { + $c = __CLASS__; + self::$instance = new $c; + } + return self::$instance; + } + + /** + * LocaleService constructor. + */ + private function __construct(){ + $this->cache["AVAILABLE_LANG"] = LocaleService::listAvailableLanguages(); + } + + /** + * PUBLIC STATIC METHODS + */ + /** + * Set the language in the session + * @static + * @param string $lang + * @return void + */ + public static function setLanguage($lang) + { + $inst = self::getInstance(); + if (array_key_exists($lang, $inst->cache["AVAILABLE_LANG"])) { + if($lang !== $inst->currentLanguage && isSet($inst->cache["MESSAGES"])){ + $inst->cache["MESSAGES"] = null; + } + $inst->currentLanguage = $lang; + SessionService::setLanguage($lang); + } + } + + /** + * Get the language from the session + * @static + * @return string + */ + public static function getLanguage() + { + $lang = self::getInstance()->currentLanguage; + if ($lang == null) { + $lang = ConfService::getInstance()->getGlobalConf("DEFAULT_LANGUAGE"); + } + if (empty($lang)) return "en"; + return $lang; + } + + /** + * Get the list of all "conf" messages + * @static + * @param bool $forceRefresh Refresh the list + * @return array + */ + public static function getConfigMessages($forceRefresh = false) + { + return self::getInstance()->getMessagesInstConf($forceRefresh); + } + + /** + * Get all i18n message + * @static + * @param bool $forceRefresh + * @return array + */ + public static function getMessages($forceRefresh = false) + { + return self::getInstance()->getMessagesInst($forceRefresh); + } + + /** + * Detect available languages from the core i18n library + * @static + * @return array + */ + public static function listAvailableLanguages() + { + // Cache in session! + if (SessionService::has(SessionService::LANGUAGES_KEY) && !isSet($_GET["refresh_langs"])) { + return SessionService::fetch(SessionService::LANGUAGES_KEY); + } + $langDir = AJXP_COREI18N_FOLDER; + $languages = array(); + if (($dh = opendir($langDir)) !== FALSE) { + while (($file = readdir($dh)) !== false) { + $matches = array(); + if (preg_match("/(.*)\.php/", $file, $matches) == 1) { + $fRadical = $matches[1]; + include($langDir . "/" . $fRadical . ".php"); + $langName = isSet($mess["languageLabel"]) ? $mess["languageLabel"] : "Not Found"; + $languages[$fRadical] = $langName; + } + } + closedir($dh); + } + if (count($languages)) { + SessionService::save(SessionService::LANGUAGES_KEY, $languages); + } + return $languages; + } + + /** + * Clear the messages cache + */ + public static function clearMessagesCache() + { + $i18nFiles = glob(dirname(AJXP_PLUGINS_MESSAGES_FILE) . "/i18n/*.ser"); + if (is_array($i18nFiles)) { + foreach ($i18nFiles as $file) { + @unlink($file); + } + } + } + + + + /** + * PRIVATE INSTANCE METHODS + */ + /** + * See static method + * @param bool $forceRefresh + * @return array + */ + private function getMessagesInstConf($forceRefresh = false) + { + // make sure they are loaded + $this->getMessagesInst($forceRefresh); + return $this->cache["CONF_MESSAGES"]; + } + + /** + * Get i18n messages + * @param bool $forceRefresh + * @return + */ + private function getMessagesInst($forceRefresh = false) + { + $crtLang = self::getLanguage(); + $messageCacheDir = dirname(AJXP_PLUGINS_MESSAGES_FILE)."/i18n"; + $messageFile = $messageCacheDir."/".$crtLang."_".basename(AJXP_PLUGINS_MESSAGES_FILE); + if (isSet($this->cache["MESSAGES"]) && !$forceRefresh) { + return $this->cache["MESSAGES"]; + } + if (!isset($this->cache["MESSAGES"]) && is_file($messageFile)) { + include($messageFile); + if (isSet($MESSAGES)) { + $this->cache["MESSAGES"] = $MESSAGES; + } + if (isSet($CONF_MESSAGES)) { + $this->cache["CONF_MESSAGES"] = $CONF_MESSAGES; + } + } else { + $this->cache["MESSAGES"] = array(); + $this->cache["CONF_MESSAGES"] = array(); + $nodes = PluginsService::getInstance(Context::emptyContext())->searchAllManifests("//i18n", "nodes"); + /** @var \DOMElement $node */ + foreach ($nodes as $node) { + $nameSpace = $node->getAttribute("namespace"); + $path = AJXP_INSTALL_PATH."/".$node->getAttribute("path"); + $lang = $crtLang; + if (!is_file($path."/".$crtLang.".php")) { + $lang = "en"; // Default language, minimum required. + } + if (is_file($path."/".$lang.".php")) { + require($path."/".$lang.".php"); + if (isSet($mess)) { + foreach ($mess as $key => $message) { + $this->cache["MESSAGES"][(empty($nameSpace)?"":$nameSpace.".").$key] = $message; + } + } + } + $lang = $crtLang; + if (!is_file($path."/conf/".$crtLang.".php")) { + $lang = "en"; + } + if (is_file($path."/conf/".$lang.".php")) { + $mess = array(); + require($path."/conf/".$lang.".php"); + $this->cache["CONF_MESSAGES"] = array_merge($this->cache["CONF_MESSAGES"], $mess); + } + } + if(!is_dir($messageCacheDir)) mkdir($messageCacheDir); + VarsFilter::filterI18nStrings($this->cache["MESSAGES"]); + VarsFilter::filterI18nStrings($this->cache["CONF_MESSAGES"]); + @file_put_contents($messageFile, "cache["MESSAGES"], true) ." ; \$CONF_MESSAGES = ".var_export($this->cache["CONF_MESSAGES"], true) ." ; "); + } + + return $this->cache["MESSAGES"]; + } + + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Services/RepositoriesCache.php b/core/src/core/src/pydio/Core/Services/RepositoriesCache.php new file mode 100644 index 0000000000..3da67b8de8 --- /dev/null +++ b/core/src/core/src/pydio/Core/Services/RepositoriesCache.php @@ -0,0 +1,49 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Services; + +use Pydio\Core\Model\RepositoryInterface; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Interface RepositoriesCache + * @package Pydio\Core\Services + */ +interface RepositoriesCache +{ + /** + * @return RepositoryInterface[]|null + */ + public static function getLoadedRepositories(); + + /** + * @param RepositoryInterface[] $repositories + * @return mixed + */ + public static function updateLoadedRepositories($repositories); + + /** + * @return boolean + */ + public static function invalidateLoadedRepositories(); + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Services/RepositoryService.php b/core/src/core/src/pydio/Core/Services/RepositoryService.php new file mode 100644 index 0000000000..825bd1b44a --- /dev/null +++ b/core/src/core/src/pydio/Core/Services/RepositoryService.php @@ -0,0 +1,513 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Services; + +use Pydio\Access\Core\Model\Repository; +use Pydio\Core\Controller\Controller; +use Pydio\Core\Model\Context; +use Pydio\Core\Model\FilteredRepositoriesList; +use Pydio\Core\Model\RepositoryInterface; +use Pydio\Core\Model\UserInterface; +use Pydio\Log\Core\Logger; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Class RepositoryService + * @package Pydio\Core\Services + */ +class RepositoryService +{ + private $cache = []; + /** + * @var RepositoryService + */ + private static $instance; + + /** + * Singleton method + * + * @return RepositoryService The service instance + */ + public static function getInstance() + { + if (!isSet(self::$instance)) { + $c = __CLASS__; + self::$instance = new $c; + } + return self::$instance; + } + + /** + * RepositoryService constructor. + */ + private function __construct(){ + + if (is_file(AJXP_CONF_PATH."/bootstrap_repositories.php")) { + $REPOSITORIES = array(); + include(AJXP_CONF_PATH."/bootstrap_repositories.php"); + $this->cache["DEFAULT_REPOSITORIES"] = $REPOSITORIES; + } else { + $this->cache["DEFAULT_REPOSITORIES"] = array(); + } + + } + + /** + * @param RepositoryInterface $repositoryObject + * @param UserInterface $userObject + * @param bool $details + * @param bool $includeShared + * @return bool + */ + public static function repositoryIsAccessible($repositoryObject, $userObject, $details=false, $includeShared=true) + { + if ($userObject === null && UsersService::usersEnabled()) { + return false; + } + if (!$userObject->canSee($repositoryObject)) { + return false; + } + if ($repositoryObject->isTemplate()) { + return false; + } + if (($repositoryObject->getAccessType()=="ajxp_conf" || $repositoryObject->getAccessType()=="ajxp_admin") && $userObject != null) { + if (UsersService::usersEnabled() && !$userObject->isAdmin()) { + return false; + } + } + $repositoryId = $repositoryObject->getId(); + if ($repositoryObject->getAccessType()=="ajxp_user" && $userObject != null) { + return ($userObject->canRead($repositoryId) || $userObject->canWrite($repositoryId)) ; + } + if ($repositoryObject->getAccessType() == "ajxp_shared" && !UsersService::usersEnabled()) { + return false; + } + if ($repositoryObject->getUniqueUser() && (!UsersService::usersEnabled() || $userObject == null || $userObject->getId() == "shared" || $userObject->getId() != $repositoryObject->getUniqueUser() )) { + return false; + } + if ( $userObject != null && !($userObject->canRead($repositoryId) || $userObject->canWrite($repositoryId)) && !$details) { + return false; + } + if ($userObject == null || $userObject->canRead($repositoryId) || $userObject->canWrite($repositoryId) || $details) { + // Do not display standard repositories even in details mode for "sub"users + if ($userObject != null && $userObject->hasParent() && !($userObject->canRead($repositoryId) || $userObject->canWrite($repositoryId) )) { + return false; + } + // Do not display shared repositories otherwise. + if ($repositoryObject->hasOwner() && !$includeShared && ($userObject == null || $userObject->getParent() != $repositoryObject->getOwner())) { + return false; + } + if ($userObject != null && $repositoryObject->hasOwner() && !$userObject->hasParent()) { + // Display the repositories if allow_crossusers is ok + $fakeCtx = Context::contextWithObjects($userObject, $repositoryObject); + if(ConfService::getContextConf($fakeCtx, "ALLOW_CROSSUSERS_SHARING", "conf") === false + || ConfService::getContextConf($fakeCtx, "ALLOW_CROSSUSERS_SHARING", "conf") === 0) { + return false; + } + // But still do not display its own shared repositories! + if ($repositoryObject->getOwner() == $userObject->getId()) { + return false; + } + } + if ($repositoryObject->hasOwner() && $userObject != null && $details && !($userObject->canRead($repositoryId) || $userObject->canWrite($repositoryId) ) ) { + return false; + } + } + $res = null; + $args = array($repositoryId, $repositoryObject, $userObject, &$res); + Controller::applyIncludeHook("repository.test_access", $args); + if($res === false){ + return false; + } + return true; + } + + + /** + * PUBLIC STATIC METHODS + */ + + /** + * @return RepositoryInterface[] + */ + public static function getStaticRepositories(){ + $self = self::getInstance(); + if(!isSet($self->cache["STATIC"])){ + $self->cache["STATIC"] = []; + foreach ($self->cache["DEFAULT_REPOSITORIES"] as $index=>$repository) { + $repoObject = self::createRepositoryFromArray($index, $repository); + $repoObject->setWriteable(false); + $self->cache["STATIC"][$repoObject->getId()] = $repoObject; + } + } + return $self->cache["STATIC"]; + } + + /** + * @param bool $includeShared + * @return \Pydio\Core\Model\RepositoryInterface[] + */ + public static function listAllRepositories($includeShared = false){ + $filtered = new FilteredRepositoriesList(); + $filtered->setIncludeShared($includeShared); + return $filtered->load(); + } + + /** + * @param RepositoryInterface[] $repoList + * @param array $criteria + * @return RepositoryInterface[] array + */ + public static function filterRepositoryListWithCriteria($repoList, $criteria) + { + $repositories = array(); + $searchableKeys = array("uuid", "parent_uuid", "owner_user_id", "display", "accessType", "isTemplate", "slug", "groupPath"); + foreach ($repoList as $repoId => $repoObject) { + $failOneCriteria = false; + foreach ($criteria as $key => $value) { + if (!in_array($key, $searchableKeys)) continue; + $criteriumOk = false; + $comp = null; + if ($key == "uuid") $comp = $repoObject->getId(); + else if ($key == "parent_uuid") $comp = $repoObject->getParentId(); + else if ($key == "owner_user_id") $comp = $repoObject->getUniqueUser(); + else if ($key == "display") $comp = $repoObject->getDisplay(); + else if ($key == "accessType") $comp = $repoObject->getAccessType(); + else if ($key == "isTemplate") $comp = $repoObject->isTemplate(); + else if ($key == "slug") $comp = $repoObject->getSlug(); + else if ($key == "groupPath") $comp = $repoObject->getGroupPath(); + if (is_array($value) && in_array($comp, $value)) { + //$repositories[$repoId] = $repoObject; + $criteriumOk = true; + } else if ($value == AJXP_FILTER_EMPTY && empty($comp)) { + //$repositories[$repoId] = $repoObject; + $criteriumOk = true; + } else if ($value == AJXP_FILTER_NOT_EMPTY && !empty($comp)) { + //$repositories[$repoId] = $repoObject; + $criteriumOk = true; + } else if (is_string($value) && strpos($value, "regexp:") === 0 && preg_match(str_replace("regexp:", "", $value), $comp)) { + //$repositories[$repoId] = $repoObject; + $criteriumOk = true; + } else if ($value == $comp) { + //$repositories[$repoId] = $repoObject; + $criteriumOk = true; + } + if (!$criteriumOk) { + $failOneCriteria = true; + break; + } + } + if (!$failOneCriteria) { + $repositories[$repoId] = $repoObject; + } + } + return $repositories; + } + + /** + * @param array $criteria + * @param $count + * @return \Pydio\Access\Core\Model\Repository[] + */ + public static function listRepositoriesWithCriteria($criteria, &$count) + { + + $statics = self::getStaticRepositories(); + $statics = self::filterRepositoryListWithCriteria($statics, $criteria); + $dyna = ConfService::getConfStorageImpl()->listRepositoriesWithCriteria($criteria, $count); + $count += count($statics); + return $statics + $dyna; + + } + + /** + * Create a repository object from a config options array + * + * @param integer $index + * @param array $repository + * @return Repository + */ + public static function createRepositoryFromArray($index, $repository) + { + return self::getInstance()->createRepositoryFromArrayInst($index, $repository); + } + + /** + * Add dynamically created repository + * + * @param \Pydio\Core\Model\RepositoryInterface $oRepository + * @return -1|null if error + */ + public static function addRepository($oRepository) + { + return self::getInstance()->addRepositoryInst($oRepository); + } + + /** + * @param $idOrAlias + * @return null|\Pydio\Access\Core\Model\Repository + */ + public static function findRepositoryByIdOrAlias($idOrAlias) + { + $repository = RepositoryService::getRepositoryById($idOrAlias); + if ($repository != null) return $repository; + $repository = RepositoryService::getRepositoryByAlias($idOrAlias); + if ($repository != null) return $repository; + return null; + } + + /** + * Get the reserved slugs used for config defined repositories + * @return array + */ + public static function reservedSlugsFromConfig() + { + $slugs = array(); + $statics = self::getStaticRepositories(); + foreach ($statics as $repo) { + $slugs[] = $repo->getSlug(); + } + return $slugs; + } + + /** + * Retrieve a repository object + * + * @param String $repoId + * @return RepositoryInterface + */ + public static function getRepositoryById($repoId) + { + return self::getInstance()->getRepositoryByIdInst($repoId); + } + + /** + * Retrieve a repository object by its slug + * + * @param String $repoAlias + * @return RepositoryInterface + */ + public static function getRepositoryByAlias($repoAlias) + { + $repo = ConfService::getConfStorageImpl()->getRepositoryByAlias($repoAlias); + if ($repo !== null) return $repo; + // check default repositories + return self::getInstance()->getRepositoryByAliasInstDefaults($repoAlias); + } + + /** + * Replace a repository by an update one. + * + * @param String $oldId + * @param RepositoryInterface $oRepositoryObject + * @return mixed + */ + public static function replaceRepository($oldId, $oRepositoryObject) + { + return self::getInstance()->replaceRepositoryInst($oldId, $oRepositoryObject); + } + + /** + * Remove a repository using the conf driver implementation + * @static + * @param $repoId + * @return int + */ + public static function deleteRepository($repoId) + { + return self::getInstance()->deleteRepositoryInst($repoId); + } + + public function __clone() + { + trigger_error("Cannot clone me, i'm a singleton!", E_USER_ERROR); + } + + + + + + /** + * PRIVATE INSTANCE IMPLEMENTATIONS + */ + /** + * See static method + * @param $repoId + * @return Repository|null + */ + private function getRepositoryByIdInst($repoId) + { + if (isSet($this->cache["REPOSITORIES"]) && isSet($this->cache["REPOSITORIES"][$repoId])) { + return $this->cache["REPOSITORIES"][$repoId]; + } + $test = CacheService::fetch(AJXP_CACHE_SERVICE_NS_SHARED, "pydio:repository:" . $repoId); + if($test !== false){ + $this->cache["REPOSITORIES"][$repoId] = $test; + return $test; + } + $test = ConfService::getConfStorageImpl()->getRepositoryById($repoId); + if($test != null) { + $this->cache["REPOSITORIES"][$repoId] = $test; + CacheService::saveWithTimestamp(AJXP_CACHE_SERVICE_NS_SHARED, "pydio:repository:" . $repoId, $test); + return $test; + } + // Finally try to search in default repositories + $statics = self::getStaticRepositories(); + if (isSet($statics[$repoId])) { + $repo = $statics[$repoId]; + $this->cache["REPOSITORIES"][$repoId] = $test; + CacheService::saveWithTimestamp(AJXP_CACHE_SERVICE_NS_SHARED, "pydio:repository:" . $repoId, $repo); + return $repo; + } + $hookedRepo = null; + $args = array($repoId, &$hookedRepo); + Controller::applyIncludeHook("repository.search", $args); + if($hookedRepo !== null){ + return $hookedRepo; + } + return null; + } + + + + /** + * See static method + * @param string $index + * @param array $repository + * @return Repository + */ + private function createRepositoryFromArrayInst($index, $repository) + { + $repo = new Repository($index, $repository["DISPLAY"], $repository["DRIVER"]); + if (isSet($repository["DISPLAY_ID"])) { + $repo->setDisplayStringId($repository["DISPLAY_ID"]); + } + if (isSet($repository["DESCRIPTION_ID"])) { + $repo->setDescription($repository["DESCRIPTION_ID"]); + } + if (isSet($repository["AJXP_SLUG"])) { + $repo->setSlug($repository["AJXP_SLUG"]); + } + if (isSet($repository["IS_TEMPLATE"]) && $repository["IS_TEMPLATE"]) { + $repo->isTemplate = true; + $repo->uuid = $index; + } + if (array_key_exists("DRIVER_OPTIONS", $repository) && is_array($repository["DRIVER_OPTIONS"])) { + foreach ($repository["DRIVER_OPTIONS"] as $oName=>$oValue) { + $repo->addOption($oName, $oValue); + } + } + // BACKWARD COMPATIBILITY! + if (array_key_exists("PATH", $repository)) { + $repo->addOption("PATH", $repository["PATH"]); + $repo->addOption("CREATE", intval($repository["CREATE"])); + $repo->addOption("RECYCLE_BIN", $repository["RECYCLE_BIN"]); + } + return $repo; + } + + /** + * @param Repository|\Pydio\Core\Model\RepositoryInterface $oRepository + * @return -1|null on error + */ + private function addRepositoryInst($oRepository) + { + Controller::applyHook("workspace.before_create", array(Context::emptyContext(), $oRepository)); + $confStorage = ConfService::getConfStorageImpl(); + $res = $confStorage->saveRepository($oRepository); + if ($res == -1) { + return $res; + } + Controller::applyHook("workspace.after_create", array(Context::emptyContext(), $oRepository)); + Logger::info(__CLASS__,"Create Repository", array("repo_name"=>$oRepository->getDisplay())); + CacheService::saveWithTimestamp(AJXP_CACHE_SERVICE_NS_SHARED, "pydio:repository:".$oRepository->getId(), $oRepository); + return null; + } + + /** + * See static method + * @param $repoAlias + * @return RepositoryInterface|null + */ + private function getRepositoryByAliasInstDefaults($repoAlias) + { + $conf = self::getStaticRepositories(); + foreach ($conf as $repoId => $repo) { + if ($repo->getSlug() === $repoAlias) { + return $repo; + } + } + return null; + } + + + /** + * See static method + * @param string $oldId + * @param RepositoryInterface $oRepositoryObject + * @return int + */ + private function replaceRepositoryInst($oldId, $oRepositoryObject) + { + Controller::applyHook("workspace.before_update", array(Context::emptyContext(), $oRepositoryObject)); + $confStorage = ConfService::getConfStorageImpl(); + $res = $confStorage->saveRepository($oRepositoryObject, true); + if ($res == -1) { + return -1; + } + Controller::applyHook("workspace.after_update", array(Context::emptyContext(), $oRepositoryObject)); + Logger::info(__CLASS__,"Edit Repository", array("repo_name"=>$oRepositoryObject->getDisplay())); + CacheService::saveWithTimestamp(AJXP_CACHE_SERVICE_NS_SHARED, "pydio:repository:" . $oRepositoryObject->getId(), $oRepositoryObject); + return 0; + } + + /** + * See static method + * @param $repoId + * @return int + */ + private function deleteRepositoryInst($repoId) + { + Controller::applyHook("workspace.before_delete", array(Context::emptyContext(), $repoId)); + $confStorage = ConfService::getConfStorageImpl(); + $shares = $confStorage->listRepositoriesWithCriteria(array("parent_uuid" => $repoId)); + $toDelete = array(); + foreach($shares as $share){ + $toDelete[] = $share->getId(); + } + $res = $confStorage->deleteRepository($repoId); + if ($res == -1) { + return $res; + } + foreach($toDelete as $deleteId){ + $this->deleteRepositoryInst($deleteId); + } + Controller::applyHook("workspace.after_delete", array(Context::emptyContext(), $repoId)); + Logger::info(__CLASS__,"Delete Repository", array("repo_id"=>$repoId)); + CacheService::delete(AJXP_CACHE_SERVICE_NS_SHARED, "pydio:repository:".$repoId); + return 0; + } + + + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Services/RolesService.php b/core/src/core/src/pydio/Core/Services/RolesService.php new file mode 100644 index 0000000000..bde7b27a86 --- /dev/null +++ b/core/src/core/src/pydio/Core/Services/RolesService.php @@ -0,0 +1,393 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Services; + +use Pydio\Conf\Core\AJXP_Role; +use Pydio\Conf\Core\AjxpRole; +use Pydio\Core\Controller\Controller; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Http\Message\ReloadRepoListMessage; +use Pydio\Core\Model\Context; +use Pydio\Core\Model\UserInterface; +use Pydio\Core\PluginFramework\PluginsService; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class RolesService + * @package Pydio\Core\Services + */ +class RolesService +{ + + /** + * Update a user with admin rights and return it + * @param UserInterface $adminUser + * @return UserInterface + */ + public static function updateAdminRights($adminUser) + { + if ($adminUser->getPersonalRole()->getAcl('ajxp_conf') != "rw") { + $adminUser->getPersonalRole()->setAcl('ajxp_conf', 'rw'); + $adminUser->recomputeMergedRole(); + $adminUser->save("superuser"); + } + return $adminUser; + } + + /** + * Update a user object with the default repositories rights + * + * @param UserInterface $userObject + */ + public static function updateDefaultRights(&$userObject) + { + if (!$userObject->hasParent()) { + $rolesList = self::getRolesList(array(), true); + foreach ($rolesList as $roleId => $roleObject) { + if (!$userObject->canSee($roleObject)) continue; + if ($userObject->getProfile() == "shared" && $roleObject->autoAppliesTo("shared")) { + $userObject->addRole($roleObject); + } else if ($roleObject->autoAppliesTo("standard")) { + $userObject->addRole($roleObject); + } + } + } + } + + /** + * @static + * @param UserInterface $userObject + */ + public static function updateAutoApplyRole(&$userObject) + { + $roles = self::getRolesList(array(), true); + foreach ($roles as $roleObject) { + if (!$userObject->canSee($roleObject)) continue; + if ($roleObject->autoAppliesTo($userObject->getProfile()) || $roleObject->autoAppliesTo("all")) { + $userObject->addRole($roleObject); + } + } + } + + /** + * @param UserInterface $userObject + */ + public static function updateAuthProvidedData(&$userObject) + { + ConfService::getAuthDriverImpl()->updateUserObject($userObject); + } + + /** + * Retrieve the current users who have either read or write access to a repository + * @param $repositoryId + * @param string $rolePrefix + * @param bool $splitByType + * @return array + */ + public static function getRolesForRepository($repositoryId, $rolePrefix = '', $splitByType = false) + { + return ConfService::getConfStorageImpl()->getRolesForRepository($repositoryId, $rolePrefix, $splitByType); + } + + /** + * Get Role by Id + * + * @param string $roleId + * @return AJXP_Role + */ + public static function getRole($roleId) + { + $roles = self::getRolesList(array($roleId)); + if (isSet($roles[$roleId])) return $roles[$roleId]; + return false; + } + + /** + * @param string $roleId Id of the role + * @param string $groupPath GroupPath to be applied + * @return AJXP_Role + */ + public static function getOrCreateRole($roleId, $groupPath = "/") + { + $roles = self::getRolesList(array($roleId)); + if (isSet($roles[$roleId])) return $roles[$roleId]; + $role = new AJXP_Role($roleId); + $role->setGroupPath($groupPath); + self::updateRole($role); + return $role; + } + + /** + * Create or update role + * + * @param AJXP_Role $roleObject + * @param UserInterface|null $userObject + */ + public static function updateRole($roleObject, $userObject = null) + { + ConfService::getConfStorageImpl()->updateRole($roleObject, $userObject); + CacheService::saveWithTimestamp(AJXP_CACHE_SERVICE_NS_SHARED, "pydio:role:".$roleObject->getId(), $roleObject); + ConfService::getInstance()->invalidateLoadedRepositories(); + $roleId = $roleObject->getId(); + if(strpos($roleId, "AJXP_GRP_/") === 0){ + $groupPath = substr($roleId, strlen("AJXP_GRP_")); + Controller::applyHook("msg.instant", array(Context::contextWithObjects(null, null), ReloadRepoListMessage::XML(), null, $groupPath)); + } + } + + /** + * Delete a role by its id + * @static + * @param string $roleId + * @return void + */ + public static function deleteRole($roleId) + { + ConfService::getConfStorageImpl()->deleteRole($roleId); + CacheService::delete(AJXP_CACHE_SERVICE_NS_SHARED, "pydio:role:".$roleId); + ConfService::getInstance()->invalidateLoadedRepositories(); + } + + /** + * @param string $parentUserId + * @return AJXP_Role + */ + public static function limitedRoleFromParent($parentUserId) + { + $parentRole = self::getRole("AJXP_USR_/" . $parentUserId); + if ($parentRole === false) return null; + + $inheritActions = PluginsService::searchManifestsWithCache("//server_settings/param[@inherit='true']", function ($nodes) { + $result = []; + if (is_array($nodes) && count($nodes)) { + foreach ($nodes as $node) { + $paramName = $node->getAttribute("name"); + $pluginId = $node->parentNode->parentNode->getAttribute("id"); + if (isSet($result[$pluginId])) $result[$pluginId] = array(); + $result[$pluginId][] = $paramName; + } + } + return $result; + }); + + // Clear ACL, Keep disabled actions, keep 'inherit' parameters. + $childRole = new AJXP_Role("AJXP_PARENT_USR_/"); + $childRole->bunchUpdate(array( + "ACL" => array(), + "ACTIONS" => $parentRole->listAllActionsStates(), + "APPLIES" => array(), + "PARAMETERS" => array())); + $params = $parentRole->listParameters(); + + foreach ($params as $scope => $plugData) { + foreach ($plugData as $pId => $paramData) { + if (!isSet($inheritActions[$pId])) continue; + foreach ($paramData as $pName => $pValue) { + $childRole->setParameterValue($pId, $pName, $pValue, $scope); + } + } + } + + return $childRole; + } + + /** + * @param boolean $status + */ + public static function enableRolesCache($status){ + self::$useCache = $status; + if($status){ + self::$rolesCache = null; + } + } + + /** @var boolean */ + private static $useCache; + + /** @var array */ + private static $rolesCache; + + /** + * Get all defined roles + * @static + * @param array $roleIds + * @param boolean $excludeReserved, + * @return AJXP_Role[] + */ + public static function getRolesList($roleIds = array(), $excludeReserved = false) + { + if (self::$useCache && !count($roleIds) && $excludeReserved == true && self::$rolesCache != null) { + return self::$rolesCache; + } + $searches = array_map(function($k){return "pydio:role:".$k;}, $roleIds); + $fetches = CacheService::fetchMultiple(AJXP_CACHE_SERVICE_NS_SHARED, $searches); + $missing = []; + if(count($fetches)){ + $found = []; + foreach($searches as $cacheKey){ + $okKey = preg_replace('/^pydio:role:/', "", $cacheKey); + if(isSet($fetches[$cacheKey]) && $fetches[$cacheKey] instanceof AJXP_Role){ + $found[$okKey] = $fetches[$cacheKey]; + }else{ + $missing[] = $okKey; + } + } + if(!count($missing)){ + return $found; + }else{ + $roleIds = $missing; + } + } + + $confDriver = ConfService::getConfStorageImpl(); + $roles = $confDriver->listRoles($roleIds, $excludeReserved); + $repoList = null; + foreach ($roles as $roleId => $roleObject) { + if ($roleObject instanceof AjxpRole) { + if ($repoList == null) $repoList = RepositoryService::listAllRepositories(true); + $newRole = new AJXP_Role($roleId); + $newRole->migrateDeprecated($repoList, $roleObject); + $roles[$roleId] = $newRole; + self::updateRole($newRole); + } + if(count($roleIds)){ + CacheService::saveWithTimestamp(AJXP_CACHE_SERVICE_NS_SHARED, "pydio:role:".$roleId, $roleObject); + } + } + if(isSet($found) && count($found)){ + // Add the ones loaded from cache + $roles = array_merge($roles, $found); + } + if (self::$useCache && !count($roleIds) && $excludeReserved == true) { + self::$rolesCache = $roles; + } + return $roles; + } + + /** + * Specific operations to perform at boot time + * @static + * @throws PydioException + * @throws \Exception + */ + public static function bootSequence() + { + $bootConfDir = AJXP_DATA_PATH. "/plugins/boot.conf"; + if (file_exists(AJXP_CACHE_DIR . "/admin_counted") || file_exists($bootConfDir."/admin_counted")) return; + $rootRole = RolesService::getRole("AJXP_GRP_/"); + if ($rootRole === false) { + $rootRole = new AJXP_Role("AJXP_GRP_/"); + $rootRole->setLabel("Root Group"); + //$rootRole->setAutoApplies(array("standard", "admin")); + //$dashId = ""; + $allRepos = RepositoryService::listAllRepositories(); + foreach ($allRepos as $repositoryId => $repoObject) { + if ($repoObject->isTemplate) continue; + //if($repoObject->getAccessType() == "ajxp_user") $dashId = $repositoryId; + $gp = $repoObject->getGroupPath(); + if (empty($gp) || $gp == "/") { + if ($repoObject->getDefaultRight() != "") { + $rootRole->setAcl($repositoryId, $repoObject->getDefaultRight()); + } + } + } + //if(!empty($dashId)) $rootRole->setParameterValue("core.conf", "DEFAULT_START_REPOSITORY", $dashId); + $parameters = PluginsService::searchManifestsWithCache("//server_settings/param[@scope]", function ($paramNodes) { + $result = []; + /** @var \DOMElement $xmlNode */ + foreach ($paramNodes as $xmlNode) { + $default = $xmlNode->getAttribute("default"); + if (empty($default)) continue; + $parentNode = $xmlNode->parentNode->parentNode; + $pluginId = $parentNode->getAttribute("id"); + if (empty($pluginId)) { + $pluginId = $parentNode->nodeName . "." . $parentNode->getAttribute("name"); + } + $result[] = ["pluginId" => $pluginId, "name" => $xmlNode->getAttribute("name"), "default" => $default]; + } + return $result; + }); + foreach ($parameters as $parameter) { + $rootRole->setParameterValue($parameter["pluginId"], $parameter["name"], $parameter["default"]); + } + RolesService::updateRole($rootRole); + } + $miniRole = RolesService::getRole("MINISITE"); + if ($miniRole === false) { + $rootRole = new AJXP_Role("MINISITE"); + $rootRole->setLabel("Minisite Users"); + $actions = array( + "access.fs" => array("ajxp_link", "chmod", "purge"), + "meta.watch" => array("toggle_watch"), + "conf.serial" => array("get_bookmarks"), + "conf.sql" => array("get_bookmarks"), + "index.lucene" => array("index"), + "action.share" => array("share", "share-edit-shared", "share-folder-workspace", "share-file-minisite", "share-selection-minisite", "share-folder-minisite-public"), + "gui.ajax" => array("bookmark"), + "auth.serial" => array("pass_change"), + "auth.sql" => array("pass_change"), + ); + foreach ($actions as $pluginId => $acts) { + foreach ($acts as $act) { + $rootRole->setActionState($pluginId, $act, AJXP_REPO_SCOPE_SHARED, false); + } + } + RolesService::updateRole($rootRole); + } + $miniRole = RolesService::getRole("MINISITE_NODOWNLOAD"); + if ($miniRole === false) { + $rootRole = new AJXP_Role("MINISITE_NODOWNLOAD"); + $rootRole->setLabel("Minisite Users - No Download"); + $actions = array( + "access.fs" => array("download", "download_chunk", "prepare_chunk_dl", "download_all") + ); + foreach ($actions as $pluginId => $acts) { + foreach ($acts as $act) { + $rootRole->setActionState($pluginId, $act, AJXP_REPO_SCOPE_SHARED, false); + } + } + RolesService::updateRole($rootRole); + } + $miniRole = RolesService::getRole("GUEST"); + if ($miniRole === false) { + $rootRole = new AJXP_Role("GUEST"); + $rootRole->setLabel("Guest user role"); + $actions = array( + "access.fs" => array("purge"), + "meta.watch" => array("toggle_watch"), + "index.lucene" => array("index"), + ); + $rootRole->setAutoApplies(array("guest")); + foreach ($actions as $pluginId => $acts) { + foreach ($acts as $act) { + $rootRole->setActionState($pluginId, $act, AJXP_REPO_SCOPE_ALL); + } + } + RolesService::updateRole($rootRole); + } + if(!file_exists($bootConfDir)){ + mkdir($bootConfDir, 0666, true); + } + file_put_contents($bootConfDir . "/admin_counted", "true"); + + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Services/SessionService.php b/core/src/core/src/pydio/Core/Services/SessionService.php new file mode 100644 index 0000000000..5fbecd6164 --- /dev/null +++ b/core/src/core/src/pydio/Core/Services/SessionService.php @@ -0,0 +1,267 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Services; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Core\Model\RepositoryInterface; +use Pydio\Core\Model\UserInterface; + +defined('AJXP_EXEC') or die('Access not allowed'); + +define('PYDIO_SESSION_NAME', 'AjaXplorer'); +define('PYDIO_SESSION_QUERY_PARAM', 'ajxp_sessid'); + +/** + * Class SessionService + * @package Pydio\Core\Services + */ +class SessionService implements RepositoriesCache +{ + const USER_KEY = "PYDIO_USER"; + const LANGUAGES_KEY = "PYDIO_LANGUAGES"; + const CTX_LANGUAGE_KEY = "PYDIO_CTX_LANGUAGE"; + const CTX_CHARSET_KEY = "PYDIO_CTX_CHARSET"; + const CTX_MINISITE_HASH = "PYDIO_CTX_MINISITE"; + + const LOADED_REPOSITORIES = "PYDIO_REPOSITORIES"; + const PREVIOUS_REPOSITORY = "PYDIO_PREVIOUS_REPO_ID"; + const CTX_REPOSITORY_ID = "PYDIO_REPO_ID"; + const PENDING_REPOSITORY_ID = "PYDIO_PENDING_REPO_ID"; + const PENDING_FOLDER = "PYDIO_PENDING_FOLDER"; + + private static $sessionName = PYDIO_SESSION_NAME; + + /** + * @param $sessionName + */ + public static function setSessionName($sessionName){ + self::$sessionName = $sessionName; + } + + /** + * @return string + */ + public static function getSessionName(){ + return self::$sessionName; + } + + /** + * @param $id + * @return null + */ + public static function fetch($id){ + if(!is_array($_SESSION) || !ApplicationState::sapiUsesSession()) return null; + if(isSet($_SESSION[$id]) && !$_SESSION[$id] instanceof \__PHP_Incomplete_Class){ + return $_SESSION[$id]; + } + return null; + } + + /** + * @param $id + * @param $data + * @return bool + */ + public static function save($id, $data){ + if(!is_array($_SESSION) || !ApplicationState::sapiUsesSession()) return false; + $_SESSION[$id] = $data; + return true; + } + + /** + * @param $id string + */ + public static function delete($id){ + if(!is_array($_SESSION) || !ApplicationState::sapiUsesSession()) return; + if(array_key_exists($id, $_SESSION)){ + unset($_SESSION[$id]); + } + } + + /** + * @param $id + * @return bool + */ + public static function has($id){ + return(is_array($_SESSION) && array_key_exists($id, $_SESSION)); + } + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @param callable|null $next + * @return mixed|ResponseInterface + */ + public static function handleRequest(ServerRequestInterface $request, ResponseInterface $response, callable $next = null){ + + $getParams = $request->getQueryParams(); + if (isSet($getParams[PYDIO_SESSION_QUERY_PARAM])) { + $cookies = $request->getCookieParams(); + if (!isSet($cookies[self::$sessionName])) { + $cookies[self::$sessionName] = $getParams[PYDIO_SESSION_QUERY_PARAM]; + $request = $request->withCookieParams($cookies); + } + } + + if(defined("AJXP_SESSION_HANDLER_PATH") && defined("AJXP_SESSION_HANDLER_CLASSNAME") && file_exists(AJXP_SESSION_HANDLER_PATH)){ + require_once(AJXP_SESSION_HANDLER_PATH); + if(class_exists(AJXP_SESSION_HANDLER_CLASSNAME, false)){ + $sessionHandlerClass = AJXP_SESSION_HANDLER_CLASSNAME; + $sessionHandler = new $sessionHandlerClass(); + session_set_save_handler($sessionHandler, false); + } + } + session_name(self::$sessionName); + session_start(); + + if($next !== null){ + $response = call_user_func_array($next, array(&$request, &$response)); + } + + register_shutdown_function(function(){ + SessionService::close(); + }); + + return $response; + + } + + public static function close(){ + session_write_close(); + } + + /** + * @param UserInterface $ctxUser + */ + public static function checkPendingRepository($ctxUser){ + if (self::has(self::PENDING_REPOSITORY_ID) && self::has(self::PENDING_FOLDER)) { + $ctxUser->setArrayPref("history", "last_repository", self::fetch(self::PENDING_REPOSITORY_ID)); + $ctxUser->setPref("pending_folder", self::fetch(self::PENDING_FOLDER)); + self::delete(self::PENDING_REPOSITORY_ID); + self::delete(self::PENDING_FOLDER); + } + } + + /** + * @return null + */ + public static function getSessionRepositoryId(){ + return self::fetch(self::CTX_REPOSITORY_ID); + } + + /** + * @param $repoId + */ + public static function saveRepositoryId($repoId){ + self::save(self::CTX_REPOSITORY_ID, $repoId); + } + + /** + * @param $repoId + */ + public static function switchSessionRepositoriId($repoId){ + if(self::has(self::CTX_REPOSITORY_ID)) { + self::save(self::PREVIOUS_REPOSITORY, self::fetch(self::CTX_REPOSITORY_ID)); + } + self::save(self::CTX_REPOSITORY_ID, $repoId); + } + + /** + * @return null + */ + public static function getPreviousRepositoryId(){ + return self::fetch(self::PREVIOUS_REPOSITORY); + } + + /** + * @return RepositoryInterface[]|null + */ + public static function getLoadedRepositories() + { + $arr = self::fetch(self::LOADED_REPOSITORIES); + if (is_array($arr)) { + $sessionNotCorrupted = array_reduce($arr, function($carry, $item){ return $carry && is_object($item) && ($item instanceof RepositoryInterface); }, true); + if($sessionNotCorrupted){ + return $arr; + } else { + self::delete(self::LOADED_REPOSITORIES); + } + } + return null; + } + + /** + * @param RepositoryInterface[] $repositories + * @return mixed + */ + public static function updateLoadedRepositories($repositories) + { + self::save(self::LOADED_REPOSITORIES, $repositories); + } + + /** + * @return boolean + */ + public static function invalidateLoadedRepositories() + { + self::delete(self::LOADED_REPOSITORIES); + } + + /** + * @param $repositoryId + * @return null + */ + public static function getContextCharset($repositoryId) + { + $arr = self::fetch(self::CTX_CHARSET_KEY); + if(!empty($arr) && isSet($arr[$repositoryId])){ + return $arr[$repositoryId]; + } + return null; + } + + /** + * @param $repositoryId + * @param $value + */ + public static function setContextCharset($repositoryId, $value) + { + $arr = self::fetch(self::CTX_CHARSET_KEY) OR []; + $arr[$repositoryId] = $value; + self::save(self::CTX_CHARSET_KEY, $arr); + } + + /** + * @param string $lang + */ + public static function setLanguage($lang){ + self::save(self::CTX_LANGUAGE_KEY, $lang); + } + + /** + * @return string|null + */ + public static function getLanguage(){ + return self::fetch(self::CTX_LANGUAGE_KEY); + } + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Services/UsersService.php b/core/src/core/src/pydio/Core/Services/UsersService.php new file mode 100644 index 0000000000..06873f7697 --- /dev/null +++ b/core/src/core/src/pydio/Core/Services/UsersService.php @@ -0,0 +1,754 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Services; + +use Pydio\Conf\Core\AbstractUser; +use Pydio\Core\Controller\Controller; +use Pydio\Core\Exception\UserNotFoundException; +use Pydio\Core\Exception\WorkspaceForbiddenException; +use Pydio\Core\Exception\WorkspaceNotFoundException; +use Pydio\Core\Http\Message\ReloadRepoListMessage; +use Pydio\Core\Model\Context; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Model\FilteredRepositoriesList; +use Pydio\Core\Model\RepositoryInterface; +use Pydio\Core\Model\UserInterface; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Utils\Http\CookiesHelper; +use Pydio\Log\Core\Logger; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class UsersService + * @package Pydio\Core\Services + */ +class UsersService +{ + /** + * @var UsersService + */ + private static $_instance; + /** + * @var array + */ + private $repositoriesCache = []; + /** + * @var array + */ + private $usersCache = []; + + /** + * @var array + */ + private $userParametersCache = []; + + /** + * @return UsersService + */ + public static function instance(){ + if(empty(self::$_instance)) self::$_instance = new UsersService(); + return self::$_instance; + } + + /** + * @param string $userId + * @param bool $checkExists + * @return UserInterface + * @throws UserNotFoundException + */ + public static function getUserById($userId, $checkExists = true){ + + $self = self::instance(); + // Try to get from memory + if(isSet($self->usersCache[$userId])){ + return $self->usersCache[$userId]; + } + // Try to get from cache + $test = CacheService::fetch(AJXP_CACHE_SERVICE_NS_SHARED, "pydio:user:" . $userId); + if($test !== false && $test instanceof UserInterface){ + // Second check : if roles were updated in cache + $roleCacheIds = array_map(function($k){ return "pydio:role:".$k; }, $test->getRolesKeys()); + $test = CacheService::fetchWithTimestamps(AJXP_CACHE_SERVICE_NS_SHARED, "pydio:user:".$userId, $roleCacheIds); + if($test !== false){ + if($test->getPersonalRole() === null){ + $test->updatePersonalRole($test->getRoles()["AJXP_USR_/".$userId]); + } + $test->recomputeMergedRole(); + $self->usersCache[$userId] = $test; + return $test; + } + } + if($checkExists && !self::userExists($userId)){ + throw new UserNotFoundException($userId); + } + // Try to get from conf + $userObject = ConfService::getConfStorageImpl()->createUserObject($userId); + if($userObject instanceof UserInterface){ + // Save in memory + $self->usersCache[$userId] = $userObject; + // Save in cache + CacheService::saveWithTimestamp(AJXP_CACHE_SERVICE_NS_SHARED, "pydio:user:" . $userId, $userObject); + } + return $userObject; + + } + + /** + * @param $userObject UserInterface + * @param string $scope + */ + public static function updateUser($userObject, $scope = "user"){ + $self = self::instance(); + $userId = $userObject->getId(); + $self->usersCache[$userId] = $userObject; + if($scope === "user"){ + CacheService::save(AJXP_CACHE_SERVICE_NS_SHARED, "pydio:user:" . $userId, $userObject); + }else{ + CacheService::saveWithTimestamp(AJXP_CACHE_SERVICE_NS_SHARED, "pydio:user:" . $userId, $userObject); + Controller::applyHook("msg.instant", array(Context::contextWithObjects($userObject, null), ReloadRepoListMessage::XML(), $userObject->getId())); + } + } + + /** + * @param UserInterface $user + * @param string $repositoryId + * @return null|RepositoryInterface + * @throws WorkspaceNotFoundException + * @throws WorkspaceForbiddenException + */ + public static function getRepositoryWithPermission($user, $repositoryId){ + $repo = RepositoryService::findRepositoryByIdOrAlias($repositoryId); + if($repo == null){ + throw new WorkspaceNotFoundException($repositoryId); + } + if(!RepositoryService::repositoryIsAccessible($repo, $user)){ + throw new WorkspaceForbiddenException($repositoryId); + } + return $repo; + } + + /** + * @param UserInterface $user + * @param bool $includeShared + * @param bool $details + * @param bool $labelsOnly + * @return \Pydio\Core\Model\RepositoryInterface[] + */ + public static function getRepositoriesForUser($user, $includeShared = true, $details = false, $labelsOnly = false){ + + $self = self::instance(); + $repos = $self->getFromCaches($user->getId()); + if($repos !== null) { + $userRepos = $repos; + } else{ + $list = new FilteredRepositoriesList($user); + $repos = $list->load(); + $self->setInCache($user->getId(), $repos); + $userRepos = $repos; + } + if($includeShared && !$details && !$labelsOnly) { + return $userRepos; + } + $output = []; + foreach ($userRepos as $repoId => $repoObject){ + if(!RepositoryService::repositoryIsAccessible($repoObject, $user, $details, $includeShared)){ + continue; + } + if($labelsOnly) $output[$repoId] = $repoObject->getDisplay(); + else $output[$repoId] = $repoObject; + } + return $output; + + } + + /** + * @param $userId + * @return mixed|null|\Pydio\Core\Model\RepositoryInterface[] + */ + private function getFromCaches($userId){ + + $fromSesssion = SessionService::getLoadedRepositories(); + if($fromSesssion !== null){ + $this->repositoriesCache[$userId] = $fromSesssion; + return $fromSesssion; + } + if(isSet($this->repositoriesCache[$userId])) { + $configsNotCorrupted = array_reduce($this->repositoriesCache[$userId], function($carry, $item){ return $carry && is_object($item) && ($item instanceof RepositoryInterface); }, true); + if($configsNotCorrupted){ + return $this->repositoriesCache[$userId]; + }else{ + $this->repositoriesCache = []; + } + } + return null; + + } + + /** + * @param string $userId + * @param RepositoryInterface[] $repoList + */ + private function setInCache($userId, $repoList){ + + $this->repositoriesCache[$userId] = $repoList; + SessionService::updateLoadedRepositories($repoList); + + } + + public static function invalidateCache(){ + + self::instance()->repositoriesCache = []; + self::instance()->usersCache = []; + SessionService::invalidateLoadedRepositories(); + + } + + + + /** + * Whether the whole users management system is enabled or not. + * @static + * @return bool + */ + public static function usersEnabled() + { + return ConfService::getGlobalConf("ENABLE_USERS", "auth"); + } + + /** + * Whether the current auth driver supports password update or not + * @static + * @return bool + */ + public static function changePasswordEnabled() + { + $authDriver = ConfService::getAuthDriverImpl(); + return $authDriver->passwordsEditable(); + } + + /** + * Return user to lower case if ignoreUserCase + * @param $user + * @return string + */ + public static function filterUserSensitivity($user) + { + if (!ConfService::getGlobalConf("CASE_SENSITIVE", "auth")) { + return strtolower($user); + } else { + return $user; + } + } + + /** + * Get config to knwo whether we should ignore user case + * @return bool + */ + public static function ignoreUserCase() + { + return !ConfService::getGlobalConf("CASE_SENSITIVE", "auth"); + } + + /** + * If the auth driver implementation has a logout redirect URL. + * @static + * @return string + */ + public static function getLogoutAddress() + { + $authDriver = ConfService::getAuthDriverImpl(); + $logout = $authDriver->getLogoutRedirect(); + return $logout; + } + + /** + * Use driver implementation to check whether the user exists or not. + * @static + * @param String $userId + * @param String $mode "r" or "w" + * @return bool + */ + public static function userExists($userId, $mode = "r") + { + if ($userId == "guest" && !ConfService::getGlobalConf("ALLOW_GUEST_BROWSING", "auth")) { + return false; + } + $userId = self::filterUserSensitivity($userId); + $authDriver = ConfService::getAuthDriverImpl(); + if ($mode == "w") { + return $authDriver->userExistsWrite($userId); + } + return $authDriver->userExists($userId); + } + + /** + * Make sure a user id is not reserved for low-level tasks (currently "guest" and "shared"). + * @static + * @param String $username + * @return bool + */ + public static function isReservedUserId($username) + { + $username = self::filterUserSensitivity($username); + return in_array($username, array("guest", "shared")); + } + + /** + * Check a password + * @static + * @param $userId + * @param $userPass + * @param bool $cookieString + * @return bool|void + * @throws UserNotFoundException + */ + public static function checkPassword($userId, $userPass, $cookieString = false) + { + if (ConfService::getGlobalConf("ALLOW_GUEST_BROWSING", "auth") && $userId == "guest") return true; + $userId = self::filterUserSensitivity($userId); + $authDriver = ConfService::getAuthDriverImpl(); + if ($cookieString) { + $userObject = self::getUserById($userId); + $res = CookiesHelper::checkCookieString($userObject, $userPass); + return $res; + } + return $authDriver->checkPassword($userId, $userPass); + } + + /** + * Update the password in the auth driver implementation. + * @static + * @throws \Exception + * @param $userId + * @param $userPass + * @return bool + */ + public static function updatePassword($userId, $userPass) + { + if (strlen($userPass) < ConfService::getGlobalConf("PASSWORD_MINLENGTH", "auth")) { + $messages = LocaleService::getMessages(); + throw new \Exception($messages[378]); + } + $userId = self::filterUserSensitivity($userId); + $authDriver = ConfService::getAuthDriverImpl(); + $ctx = Context::emptyContext(); + Controller::applyHook("user.before_password_change", array($ctx, $userId)); + $authDriver->changePassword($userId, $userPass); + Controller::applyHook("user.after_password_change", array($ctx, $userId)); + + self::storeWebdavDigestForUser(self::getUserById($userId), $userPass); + + Logger::info(__CLASS__, "Update Password", array("user_id" => $userId)); + return true; + } + + /** + * Create a user + * @static + * @throws \Exception + * @param $userId + * @param $userPass + * @param bool $isAdmin + * @param bool $isHidden + * @return UserInterface + */ + public static function createUser($userId, $userPass, $isAdmin = false, $isHidden = false) + { + $userId = self::filterUserSensitivity($userId); + $localContext = new Context($userId, null); + Controller::applyHook("user.before_create", array($localContext, $userId, $userPass, $isAdmin, $isHidden)); + if (!ConfService::getGlobalConf("ALLOW_GUEST_BROWSING", "auth") && $userId == "guest") { + throw new \Exception("Reserved user id"); + } + $authDriver = ConfService::getAuthDriverImpl(); + $authDriver->createUser($userId, $userPass); + $user = self::getUserById($userId, false); + if ($isAdmin) { + $user->setAdmin(true); + $user->save("superuser"); + } + if ($isHidden) { + $user->setHidden(true); + $user->save("superuser"); + } + + self::storeWebdavDigestForUser($user, $userPass); + + Controller::applyHook("user.after_create", array($localContext, $user)); + Logger::info(__CLASS__, "Create User", array("user_id" => $userId)); + return $user; + } + + /** + * Store the HA1 digest for Digest Authentication in WebDAV + * @param UserInterface $userObject + * @param string $password + * @throws UserNotFoundException + */ + private static function storeWebdavDigestForUser($userObject, $password){ + + $realm = ConfService::getGlobalConf("WEBDAV_DIGESTREALM"); + $ha1 = md5("{$userObject->getId()}:{$realm}:{$password}"); + $wData = $userObject->getPref("AJXP_WEBDAV_DATA"); + if (!is_array($wData)) $wData = array(); + $wData["HA1"] = $ha1; + $userObject->setPref("AJXP_WEBDAV_DATA", $wData); + $userObject->save(); + + } + + /** + * Detect the number of admin users + * @static + * @return int|void + */ + public static function countAdminUsers() + { + $confDriver = ConfService::getConfStorageImpl(); + $auth = ConfService::getAuthDriverImpl(); + $count = $confDriver->countAdminUsers(); + if (!$count && $auth->userExists("admin") && $confDriver->getName() == "serial") { + return -1; + } + return $count; + } + + /** + * Delete a user in the auth/conf driver impl + * @static + * @param $userId + * @return bool + */ + public static function deleteUser($userId) + { + $ctx = Context::emptyContext(); + Controller::applyHook("user.before_delete", array($ctx, $userId)); + $userId = self::filterUserSensitivity($userId); + $authDriver = ConfService::getAuthDriverImpl(); + $authDriver->deleteUser($userId); + $subUsers = array(); + ConfService::getConfStorageImpl()->deleteUser($userId, $subUsers); + foreach ($subUsers as $deletedUser) { + $authDriver->deleteUser($deletedUser); + } + Controller::applyHook("user.after_delete", array($ctx, $userId)); + Logger::info(__CLASS__, "Delete User", array("user_id" => $userId, "sub_user" => implode(",", $subUsers))); + return true; + } + + /** + * List children groups of current base + * @param string $baseGroup + * @return string[] + */ + public static function listChildrenGroups($baseGroup = "/") + { + return ConfService::getAuthDriverImpl()->listChildrenGroups($baseGroup); + + } + + /** + * Create a new group at the given path + * + * @param $baseGroup + * @param $groupName + * @param $groupLabel + * @throws \Exception + */ + public static function createGroup($baseGroup, $groupName, $groupLabel) + { + if (empty($groupName)) throw new \Exception("Please provide a name for this new group!"); + $fullGroupPath = rtrim($baseGroup, "/") . "/" . $groupName; + $exists = ConfService::getConfStorageImpl()->groupExists($fullGroupPath); + if ($exists) { + throw new \Exception("Group with this name already exists, please pick another name!"); + } + if (empty($groupLabel)) $groupLabel = $groupName; + ConfService::getConfStorageImpl()->createGroup(rtrim($baseGroup, "/") . "/" . $groupName, $groupLabel); + } + + /** + * Delete group by name + * @param $baseGroup + * @param $groupName + */ + public static function deleteGroup($baseGroup, $groupName) + { + ConfService::getConfStorageImpl()->deleteGroup(rtrim($baseGroup, "/") . "/" . $groupName); + } + + /** + * Count the number of children a given user has already created + * @param $parentUserId + * @return AbstractUser[] + */ + public static function getChildrenUsers($parentUserId) + { + return ConfService::getConfStorageImpl()->getUserChildren($parentUserId); + } + + /** + * Count the number of users who have either read or write access to a repository + * @param ContextInterface $ctx + * @param $repositoryId + * @param bool $details + * @param bool $admin True if called in an admin context + * @return array|int + */ + public static function countUsersForRepository(ContextInterface $ctx, $repositoryId, $details = false, $admin = false) + { + return ConfService::getConfStorageImpl()->countUsersForRepository($ctx, $repositoryId, $details, $admin); + } + + /** + * List users with a specific filter + * @param string $baseGroup + * @param null $regexp + * @param $offset + * @param $limit + * @param bool $cleanLosts + * @param bool $recursive + * @param null $countCallback + * @param null $loopCallback + * @return UserInterface[] + */ + public static function listUsers($baseGroup = "/", $regexp = null, $offset = -1, $limit = -1, $cleanLosts = true, $recursive = true, $countCallback = null, $loopCallback = null) + { + $authDriver = ConfService::getAuthDriverImpl(); + $confDriver = ConfService::getConfStorageImpl(); + /** + * @var $allUsers AbstractUser[] + */ + $allUsers = array(); + $paginated = false; + if (($regexp != null || $offset != -1 || $limit != -1) && $authDriver->supportsUsersPagination()) { + $users = $authDriver->listUsersPaginated($baseGroup, $regexp, $offset, $limit, $recursive); + $paginated = ($offset != -1 || $limit != -1); + } else { + $users = $authDriver->listUsers($baseGroup); + } + $index = 0; + + // Callback func for display progression on cli mode + if ($countCallback != null) { + call_user_func($countCallback, $index, count($users), "Update users"); + } + + RolesService::enableRolesCache(true); + foreach (array_keys($users) as $userId) { + if (($userId == "guest" && !ConfService::getGlobalConf("ALLOW_GUEST_BROWSING", "auth")) || $userId == "ajxp.admin.users" || $userId == "") continue; + if ($regexp != null && !$authDriver->supportsUsersPagination() && !preg_match("/$regexp/i", $userId)) continue; + $allUsers[$userId] = self::getUserById($userId); + $index++; + + // Callback func for display progression on cli mode + if ($countCallback != null) { + call_user_func($loopCallback, $index); + } + + if (empty($regexp) && $paginated) { + // Make sure to reload all children objects + foreach ($confDriver->getUserChildren($userId) as $childObject) { + $allUsers[$childObject->getId()] = $childObject; + } + } + } + RolesService::enableRolesCache(false); + + if (empty($regexp) && $paginated && $cleanLosts) { + // Remove 'lost' items (children without parents). + foreach ($allUsers as $id => $object) { + if ($object->hasParent() && !array_key_exists($object->getParent(), $allUsers)) { + unset($allUsers[$id]); + } + } + } + return $allUsers; + } + + /** + * Scroll every registered users and apply a callback + * + * @param string $baseGroup Where to start from + * @param bool $recursive Browse all subgroups and their children + * @param callable $userCallback Apply this callback to every user found. Parameters are (userId, userGroup, index, total) where index/total are relative in the current group. + * @param callable $groupCallback Apply this callback to every group found. Parameters are (groupPath, groupLabel). Generally for logging purpose. + * @param string $baseGroupLabel Pass label of the current group, used by groupCallback. + */ + public static function browseUsersGroupsWithCallback($baseGroup, $userCallback, $recursive = false, $groupCallback = null, $baseGroupLabel = null){ + + $authDriver = ConfService::getAuthDriverImpl(); + $pairs = $authDriver->listUsers($baseGroup, false); + if($groupCallback !== null){ + $groupCallback($baseGroup, $baseGroupLabel); + } + $currentUsers = array_keys($pairs); + $total = count($currentUsers); + foreach($currentUsers as $index => $userId){ + $userCallback($userId, $baseGroup, $index, $total); + } + if($recursive){ + $childrenGroups = $authDriver->listChildrenGroups($baseGroup); + foreach($childrenGroups as $relativePath => $groupLabel){ + self::browseUsersGroupsWithCallback(rtrim($baseGroup, "/")."/".$relativePath, $userCallback, true, $groupCallback, $groupLabel); + } + } + + } + + /** + * Depending on the plugin, tried to compute the actual page where a given user can be located + * + * @param $baseGroup + * @param $userLogin + * @param $usersPerPage + * @param int $offset + * @return int + */ + public static function findUserPage($baseGroup, $userLogin, $usersPerPage, $offset = 0) + { + if (ConfService::getAuthDriverImpl()->supportsUsersPagination()) { + return ConfService::getAuthDriverImpl()->findUserPage($baseGroup, $userLogin, $usersPerPage, $offset); + } else { + return -1; + } + } + + /** + * Whether the current auth driver supports paginated listing + * + * @return bool + */ + public static function authSupportsPagination() + { + $authDriver = ConfService::getAuthDriverImpl(); + return $authDriver->supportsUsersPagination(); + } + + /** + * Count the total number of users inside a group (recursive). + * Regexp can be used to limit the users IDs with a specific expression + * Property can be used for basic filtering, either on "parent" or "admin". + * + * @param string $baseGroup + * @param string $regexp + * @param null $filterProperty Can be "parent" or "admin" + * @param null $filterValue Can be a string, or constants AJXP_FILTER_EMPTY / AJXP_FILTER_NOT_EMPTY + * @param bool $recursive + * @return int + */ + public static function authCountUsers($baseGroup = "/", $regexp = "", $filterProperty = null, $filterValue = null, $recursive = true) + { + $authDriver = ConfService::getAuthDriverImpl(); + return $authDriver->getUsersCount($baseGroup, $regexp, $filterProperty, $filterValue, $recursive); + } + + /** + * Makes a correspondance between a user and its auth scheme, for multi auth + * @param $userName + * @return String + */ + public static function getAuthScheme($userName) + { + $authDriver = ConfService::getAuthDriverImpl(); + return $authDriver->getAuthScheme($userName); + } + + /** + * Check if auth implementation supports schemes detection + * @return bool + */ + public static function driverSupportsAuthSchemes() + { + $authDriver = ConfService::getAuthDriverImpl(); + return $authDriver->supportsAuthSchemes(); + } + + /** + * Get parameters with scope='user' expose='true' attributes. + * Cached in plugin service. + * + * @return array Array of [PLUGIN_ID=>id, NAME=>name] objects. + */ + public static function getUsersExposedParameters(){ + $exposed = PluginsService::searchManifestsWithCache("//server_settings/param[contains(@scope,'user') and @expose='true']", function($nodes){ + $result = []; + /** @var \DOMElement $exposed_prop */ + foreach($nodes as $exposed_prop){ + $parentNode = $exposed_prop->parentNode->parentNode; + $pluginId = $parentNode->getAttribute("id"); + if (empty($pluginId)) { + $pluginId = $parentNode->nodeName.".".$parentNode->getAttribute("name"); + } + $paramName = $exposed_prop->getAttribute("name"); + $result[] = ["PLUGIN_ID" => $pluginId, "NAME" => $paramName]; + } + return $result; + }); + return $exposed; + } + + /** + * @param string $parameterName Plugin parameter name + * @param UserInterface|string $userIdOrObject + * @param string $pluginId Plugin name, core.conf by default + * @param null $defaultValue + * @return mixed + */ + public static function getUserPersonalParameter($parameterName, $userIdOrObject, $pluginId = "core.conf", $defaultValue = null) + { + $self = self::instance(); + + $cacheId = $pluginId . "-" . $parameterName; + if (!isSet($self->userParametersCache[$cacheId])) { + $self->userParametersCache[$cacheId] = []; + } + // Passed an already loaded object + if ($userIdOrObject instanceof UserInterface) { + $value = $userIdOrObject->getPersonalRole()->filterParameterValue($pluginId, $parameterName, AJXP_REPO_SCOPE_ALL, $defaultValue); + $self->userParametersCache[$cacheId][$userIdOrObject->getId()] = $value; + if (empty($value) && !empty($defaultValue)) $value = $defaultValue; + return $value; + } + // Already in memory cache + if (isSet($self->userParametersCache[$cacheId][$userIdOrObject])) { + return $self->userParametersCache[$cacheId][$userIdOrObject]; + } + + // Try to load personal role if it was already loaded. + $uRole = RolesService::getRole("AJXP_USR_/" . $userIdOrObject); + if ($uRole === false && UsersService::userExists($userIdOrObject)) { + $uObject = self::getUserById($userIdOrObject, false); + $uRole = $uObject->getPersonalRole(); + } + if (empty($uRole)) { + return $defaultValue; + } + $value = $uRole->filterParameterValue($pluginId, $parameterName, AJXP_REPO_SCOPE_ALL, $defaultValue); + if (empty($value) && !empty($defaultValue)) { + $value = $userIdOrObject; + } + $self->userParametersCache[$cacheId][$userIdOrObject] = $value; + return $value; + + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Utils/Crypto.php b/core/src/core/src/pydio/Core/Utils/Crypto.php new file mode 100644 index 0000000000..46e6c25dfa --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Crypto.php @@ -0,0 +1,175 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils; + +use phpseclib\Crypt\Rijndael; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Utils\Crypto\Key; +use Pydio\Core\Utils\Crypto\ZeroPaddingRijndael; +use Pydio\Core\Utils\Vars\StringHelper; + + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Simple encrypt / decrypt utils for small strings + * Legacy is using mcrypt Rijndael_256, will be replaced by openssl or libsodium with standard cypher + * @package Pydio\Core\Utils + */ +class Crypto +{ + const HEADER_CBC_128 = 'cbc-128'; + + /** + * @return string + */ + public static function getApplicationSecret(){ + if (defined('AJXP_SAFE_SECRET_KEY')) { + return AJXP_SAFE_SECRET_KEY; + } else { + return "\1CDAFx¨op#"; + } + } + + /** + * @return string + */ + public static function getCliSecret(){ + $cKey = ConfService::getGlobalConf("AJXP_CLI_SECRET_KEY", "conf"); + if (empty($cKey)) { + $cKey = "\1CDAFx¨op#"; + } + return $cKey; + } + + /** + * @param bool $base64encode + * @return string + */ + public static function getRandomSalt($base64encode = true, $size = 32){ + if(function_exists('openssl_random_pseudo_bytes')){ + $salt = openssl_random_pseudo_bytes($size); + }else if (function_exists('mcrypt_create_iv')){ + $salt = mcrypt_create_iv(PBKDF2_SALT_BYTE_SIZE, MCRYPT_DEV_URANDOM); + }else{ + $salt = StringHelper::generateRandomString($size, true); + } + return ($base64encode ? base64_encode($salt) : $salt); + } + + /** + * @return string + */ + protected static function getDataHeader(){ + return substr(md5(self::HEADER_CBC_128), 0, 16); + } + + /** + * @param $data + * @return bool + */ + public static function hasCBCEnctypeHeader($data){ + $h = self::getDataHeader(); + return (strpos($data, $h) === 0); + } + + /** + * @param $data + * @return bool + */ + private static function removeCBCEnctypeHeader(&$data){ + $h = self::getDataHeader(); + if(strpos($data, $h) === 0){ + $data = substr($data, strlen($h)); + return true; + }else{ + return false; + } + } + + /** + * Builds a key using various methods depending on legacy status or not + * @param string $userKey + * @param string $secret + * @param null $encodedData + * @return array|bool|string + */ + public static function buildKey($userKey, $secret, $encodedData = null){ + if($encodedData === null){ + // new encryption, use new method + return Key::create($userKey.$secret); + }else if(self::hasCBCEnctypeHeader($encodedData)){ + // New method detected + return Key::create($userKey . $secret, Key::STRENGTH_MEDIUM); + }else{ + // Legacy + return Key::createLegacy($userKey . $secret); + } + } + + /** + * @param mixed $data + * @param string $key + * @param bool $base64encode + * @return mixed + */ + public static function encrypt($data, $key, $base64encode = true){ + // Encrypt in new mode, prepending a fixed header to the encoded data. + $r = new ZeroPaddingRijndael(Rijndael::MODE_CBC); + $r->setKey($key); + $r->setBlockLength(128); + $iv = self::getRandomSalt(false, 16); + $r->setIV($iv); + $encoded = $iv . $r->encrypt($data); + if($base64encode) { + return self::getDataHeader().base64_encode($encoded); + } else { + return self::getDataHeader().$encoded; + } + } + + /** + * @param string $data + * @param string $key + * @param bool $base64encoded + * @return mixed + */ + public static function decrypt($data, $key, $base64encoded = true){ + $test = self::removeCBCEnctypeHeader($data); + if($base64encoded){ + $data = base64_decode($data); + } + if($test){ + $iv = substr($data, 0, 16); + $data = substr($data, 16); + $r = new ZeroPaddingRijndael(Rijndael::MODE_CBC); + $r->setIV($iv); + $r->setBlockLength(128); + }else{ + // Legacy encoding + $r = new ZeroPaddingRijndael(Rijndael::MODE_ECB); + $r->setBlockLength(256); + } + $r->setKey($key); + return $r->decrypt($data); + } + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Utils/Crypto/Key.php b/core/src/core/src/pydio/Core/Utils/Crypto/Key.php new file mode 100644 index 0000000000..cc79145a46 --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Crypto/Key.php @@ -0,0 +1,100 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils\Crypto; + +use Pydio\Core\Utils\Crypto; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class Key + * @package Pydio\Core\Utils\Crypto + */ +class Key +{ + const STRENGTH_LOW = 0; + const STRENGTH_MEDIUM = 1; + const STRENGTH_HIGH = 2; + + const SIZE_128 = 16; + const SIZE_256 = 32; + + /** + * @param $password + * @param int $strength + * @param null $options + * @return array|bool|string + */ + public static function create($password, $strength = Key::STRENGTH_MEDIUM, $options = null){ + + if(!$options){ + $options = array( + "strength" => self::STRENGTH_MEDIUM, + "size" => self::SIZE_256, + "iterations" => 20000, + "salt" => md5(Crypto::getApplicationSecret()), + "hash_function" => "SHA512" + ); + } + + if($strength == self::STRENGTH_HIGH && function_exists('openssl_random_pseudo_bytes')){ + + $aes_key = self::create($password); + $method = "aes-" . strlen($options["size"]) . "-cbc"; + + $key = openssl_random_pseudo_bytes($options["size"]); + $rsa = openssl_pkey_new(array( + "digest_algo" => "sha512", + "private_key_bits" => "4096", + "private_key_type" => OPENSSL_KEYTYPE_RSA + )); + openssl_pkey_export($rsa, $private); + + $iv = openssl_random_pseudo_bytes(16); + $private = openssl_encrypt($private, $method, $aes_key, OPENSSL_RAW_DATA, $iv); + $public = openssl_pkey_get_details($rsa)["key"]; + + $options["public"] = $public; + $options["private"] = $private; + $options["iv"] = $iv; + openssl_public_encrypt($key, $options["key"], $public); + + return array( + $key, + $options + ); + + } else if($strength == self::STRENGTH_LOW){ + return substr(hash($options["hash_function"], $password), 0, $options["size"]); + } else { + return openssl_pbkdf2($password, $options["salt"], $options["size"], $options["iterations"], $options["hash_function"]); + } + } + + /** + * @param $password + * @return string + */ + public static function createLegacy($password){ + return md5($password); + } + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Utils/Crypto/ZeroPaddingRijndael.php b/core/src/core/src/pydio/Core/Utils/Crypto/ZeroPaddingRijndael.php new file mode 100644 index 0000000000..4889e7c33e --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Crypto/ZeroPaddingRijndael.php @@ -0,0 +1,79 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ + +namespace Pydio\Core\Utils\Crypto; + +defined('AJXP_EXEC') or die('Access not allowed'); + +use \phpseclib\Crypt\Rijndael; + +/** + * Class ZeroPaddingRijndael + * @package Pydio\Core\Utils\Crypto + */ +class ZeroPaddingRijndael extends Rijndael { + /** + * Pads a string + * + * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize. + * $this->block_size - (strlen($text) % $this->block_size) bytes are added, each of which is equal to + * chr($this->block_size - (strlen($text) % $this->block_size) + * + * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless + * and padding will, hence forth, be enabled. + * + * @see self::_unpad() + * @param string $text + * @throws \LengthException if padding is disabled and the plaintext's length is not a multiple of the block size + * @access private + * @return string + */ + function _pad($text) + { + $length = strlen($text); + + if (!$this->padding) { + if ($length % $this->block_size == 0) { + return $text; + } else { + throw new \LengthException("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size}). Try enabling padding."); + } + } + + $pad = $this->block_size - ($length % $this->block_size); + return str_pad($text, $length + $pad, "\0"); + } + /** + * Unpads a string. + * + * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong + * and false will be returned. + * + * @see self::_pad() + * @param string $text + * @throws \LengthException if the ciphertext's length is not a multiple of the block size + * @access private + * @return string + */ + function _unpad($text) { + return trim($text, "\0"); + } +} diff --git a/core/src/core/src/pydio/Core/Utils/DBHelper.php b/core/src/core/src/pydio/Core/Utils/DBHelper.php new file mode 100644 index 0000000000..89c5aea815 --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/DBHelper.php @@ -0,0 +1,105 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Generic SQL utils + * @package Pydio\Core\Utils + */ +class DBHelper +{ + + /** + * @param $p + * @param $file + * @return string + */ + public static function runCreateTablesQuery($p, $file) + { + + switch ($p["driver"]) { + case "sqlite": + case "sqlite3": + if (!file_exists(dirname($p["database"]))) { + @mkdir(dirname($p["database"]), 0755, true); + } + $ext = ".sqlite"; + break; + case "mysql": + $ext = ".mysql"; + break; + case "postgre": + $ext = ".pgsql"; + break; + default: + return "ERROR!, DB driver " . $p["driver"] . " not supported yet in __FUNCTION__"; + } + + $result = array(); + $file = dirname($file) . "/" . str_replace(".sql", $ext, basename($file)); + $sql = file_get_contents($file); + $separators = explode("/** SEPARATOR **/", $sql); + + $allParts = array(); + + foreach ($separators as $sep) { + $explode = explode("\n", trim($sep)); + $firstLine = array_shift($explode); + if ($firstLine == "/** BLOCK **/") { + $allParts[] = $sep; + } else { + $parts = explode(";", $sep); + $remove = array(); + $count = count($parts); + for ($i = 0; $i < $count; $i++) { + $part = $parts[$i]; + if (strpos($part, "BEGIN") && isSet($parts[$i + 1])) { + $parts[$i] .= ';' . $parts[$i + 1]; + $remove[] = $i + 1; + } + } + foreach ($remove as $rk) unset($parts[$rk]); + $allParts = array_merge($allParts, $parts); + } + } + \dibi::connect($p); + \dibi::begin(); + foreach ($allParts as $createPart) { + $sqlPart = trim($createPart); + if (empty($sqlPart)) continue; + try { + \dibi::nativeQuery($sqlPart); + $resKey = str_replace("\n", "", substr($sqlPart, 0, 50)) . "..."; + $result[] = "OK: $resKey executed successfully"; + } catch (\DibiException $e) { + $result[] = "ERROR! $sqlPart failed"; + } + } + \dibi::commit(); + \dibi::disconnect(); + $message = implode("\n", $result); + if (strpos($message, "ERROR!")) return $message; + else return "SUCCESS:" . $message; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Utils/FileHelper.php b/core/src/core/src/pydio/Core/Utils/FileHelper.php new file mode 100644 index 0000000000..15df8fd851 --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/FileHelper.php @@ -0,0 +1,146 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils; + +use HttpClient; +use Pydio\Core\Model\Context; +use Pydio\Core\Utils\Vars\StringHelper; +use Pydio\Core\Utils\Vars\VarsFilter; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class FileHelper + * @package Pydio\Core\Utils + */ +class FileHelper +{ + + /** + * Load an array stored serialized inside a file. + * Warning : currently does not take a context, filtering will be applied only based on global configs + * (AJXP_DATA_PATH, etc...). Make sure to filter the path if required (e.g. AJXP_USER) before passing it to the function. + * + * @param String $filePath Full path to the file + * @param Boolean $skipCheck do not test for file existence before opening + * @param string $format + * @return array + */ + public static function loadSerialFile($filePath, $skipCheck = false, $format = "ser") + { + $filePath = VarsFilter::filter($filePath, Context::emptyContext()); + $result = array(); + if ($skipCheck) { + $fileLines = @file($filePath); + if ($fileLines !== false) { + if ($format == "ser") $result = unserialize(implode("", $fileLines)); + else if ($format == "json") $result = json_decode(implode("", $fileLines), true); + } + return $result; + } + if (is_file($filePath)) { + $fileLines = file($filePath); + if ($format == "ser") $result = unserialize(implode("", $fileLines)); + else if ($format == "json") $result = json_decode(implode("", $fileLines), true); + } + return $result; + } + + /** + * Stores an Array as a serialized string inside a file. + * @see loadSerialFile regarding path filtering. + * + * @param String $filePath Full path to the file + * @param array|object $value The value to store + * @param Boolean $createDir Whether to create the parent folder or not, if it does not exist. + * @param bool $silent Silently write the file, are throw an exception on problem. + * @param string $format "ser" or "json" + * @param bool $jsonPrettyPrint If json, use pretty printing + * @throws \Exception + */ + public static function saveSerialFile($filePath, $value, $createDir = true, $silent = false, $format = "ser", $jsonPrettyPrint = false) + { + if (!in_array($format, array("ser", "json"))) { + throw new \Exception("Unsupported serialization format: " . $format); + } + $filePath = VarsFilter::filter($filePath, Context::emptyContext()); + if ($createDir && !is_dir(dirname($filePath))) { + @mkdir(dirname($filePath), 0755, true); + if (!is_dir(dirname($filePath))) { + // Creation failed + if ($silent) return; + else throw new \Exception("[AJXP_Utils::saveSerialFile] Cannot write into " . dirname(dirname($filePath))); + } + } + try { + $fp = fopen($filePath, "w"); + if ($format == "ser") { + $content = serialize($value); + } else { + $content = json_encode($value); + if ($jsonPrettyPrint) $content = StringHelper::prettyPrintJSON($content); + } + fwrite($fp, $content); + fclose($fp); + } catch (\Exception $e) { + if ($silent) return; + else throw $e; + } + } + + /** + * Try to remove a file without errors + * @static + * @param $file + * @return void + */ + public static function silentUnlink($file) + { + @unlink($file); + } + + /** + * @static + * @param string $url + * @return bool|mixed|string + */ + public static function getRemoteContent($url) + { + if (ini_get("allow_url_fopen")) { + return file_get_contents($url); + } else if (function_exists("curl_init")) { + $ch = curl_init(); + $timeout = 30; // set to zero for no timeout + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); + $return = curl_exec($ch); + curl_close($ch); + return $return; + } else { + $i = parse_url($url); + require_once AJXP_BIN_FOLDER."/lib/HttpClient.php"; + $httpClient = new HttpClient($i["host"]); + $httpClient->timeout = 30; + return $httpClient->quickGet($url); + } + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Utils/Http/BruteForceHelper.php b/core/src/core/src/pydio/Core/Utils/Http/BruteForceHelper.php new file mode 100644 index 0000000000..5d2c4f27ef --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Http/BruteForceHelper.php @@ -0,0 +1,116 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils\Http; + + +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\ApplicationState; +use Pydio\Log\Core\Logger; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class BruteForceHelper + * @package Pydio\Core\Utils + */ +class BruteForceHelper +{ + + /** + * The array is located in the AjxpTmpDir/failedAJXP.log + * @static + * @return array + */ + public static function getBruteForceLoginArray() + { + $failedLog = ApplicationState::getAjxpTmpDir() . "/failedAJXP.log"; + $loginAttempt = @file_get_contents($failedLog); + $loginArray = unserialize($loginAttempt); + $ret = array(); + $curTime = time(); + if (is_array($loginArray)) { + // Filter the array (all old time are cleaned) + foreach ($loginArray as $key => $login) { + if (($curTime - $login["time"]) <= 60 * 60 * 24) $ret[$key] = $login; + } + } + return $ret; + } + + /** + * Store the array + * @static + * @param $loginArray + * @return void + */ + public static function setBruteForceLoginArray($loginArray, $validCurrent = false) + { + if($validCurrent && isSet($loginArray[$_SERVER["REMOTE_ADDR"]])){ + unset($loginArray[$_SERVER["REMOTE_ADDR"]]); + } + $failedLog = ApplicationState::getAjxpTmpDir() . "/failedAJXP.log"; + @file_put_contents($failedLog, serialize($loginArray)); + } + + /** + * Determines whether the user is try to make many attemps + * @static + * @param $loginArray + * @return bool + */ + public static function checkBruteForceLogin(&$loginArray) + { + if (isSet($_SERVER['REMOTE_ADDR'])) { + $serverAddress = $_SERVER['REMOTE_ADDR']; + } else if (isSet($_SERVER['SERVER_ADDR'])) { + $serverAddress = $_SERVER['SERVER_ADDR']; + } else { + return TRUE; + } + $login = null; + if (isSet($loginArray[$serverAddress])) { + $login = $loginArray[$serverAddress]; + } + if (is_array($login)) { + $login["count"]++; + } else $login = array("count" => 1, "time" => time()); + $loginArray[$serverAddress] = $login; + if ($login["count"] > 3) { + if (AJXP_SERVER_DEBUG || ConfService::getGlobalConf("DISABLE_BRUTE_FORCE_CHECK", "auth") === true) { + Logger::debug("Warning: failed login 3 time from address $serverAddress, but ignored because captcha is disabled."); + return true; + } + return FALSE; + } + return TRUE; + } + + /** + * Is there a brute force login attempt? + * @static + * @return bool + */ + public static function suspectBruteForceLogin() + { + $loginAttempt = self::getBruteForceLoginArray(); + return !self::checkBruteForceLogin($loginAttempt); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Utils/Http/CaptchaProvider.php b/core/src/core/src/pydio/Core/Utils/Http/CaptchaProvider.php new file mode 100644 index 0000000000..5c87dad69f --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Http/CaptchaProvider.php @@ -0,0 +1,80 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils\Http; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Encapsulation of the securimage external library, to generate a Captcha Image on brute force login attempt. + */ +class CaptchaProvider +{ + /** + * Print out a Captcha image + * @static + * @return void + */ + public static function sendCaptcha() + { + $img = new \Securimage(); + + $img->wordlist_file = dirname(__FILE__).DIRECTORY_SEPARATOR.'captcha_words.txt'; + $img->image_height = 80; + $img->image_width = 170; + $img->perturbation = 0.85; + $img->image_bg_color = new \Securimage_Color("#f6f6f6"); + $img->multi_text_color = array(new \Securimage_Color("#3399ff"), + new \Securimage_Color("#3300cc"), + new \Securimage_Color("#3333cc"), + new \Securimage_Color("#6666ff"), + new \Securimage_Color("#99cccc") + ); + $img->use_multi_text = true; + $img->text_angle_minimum = -5; + $img->text_angle_maximum = 5; + $img->use_transparent_text = true; + $img->text_transparency_percentage = 30; // 100 = completely transparent + $img->num_lines = 5; + $img->line_color = new \Securimage_Color("#eaeaea"); + $img->signature_color = new \Securimage_Color(rand(0, 64), rand(64, 128), rand(128, 255)); + $img->use_wordlist = true; + if (!function_exists('imagettftext')) { + $img->use_gd_font = true; + $img->use_transparent_text = false; + $img->use_multi_text = false; + } + $img->show(); + } + + /** + * Verify the code against the current image. + * @static + * @param $code + * @return bool + */ + public static function checkCaptchaResult($code) + { + $img = new \Securimage(); + return $img->check($code); + + } + +} diff --git a/core/src/core/src/pydio/Core/Utils/Http/CookiesHelper.php b/core/src/core/src/pydio/Core/Utils/Http/CookiesHelper.php new file mode 100644 index 0000000000..49171287d0 --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Http/CookiesHelper.php @@ -0,0 +1,124 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils\Http; + +use Pydio\Core\Model\UserInterface; +use Pydio\Core\Utils\Vars\StringHelper; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class CookiesHelper - Helper for http cookies + * @package Pydio\Core\Utils + */ +class CookiesHelper +{ + + /** + * @static + * @param UserInterface $user + */ + public static function refreshRememberCookie($user) + { + $current = $_COOKIE["AjaXplorer-remember"]; + if (!empty($current)) { + CookiesHelper::invalidateCookieString($user, substr($current, strpos($current, ":")+1)); + } + $rememberPass = CookiesHelper::getCookieString($user); + setcookie("AjaXplorer-remember", $user->getId().":".$rememberPass, time()+3600*24*10, null, null, (isSet($_SERVER["HTTPS"]) && strtolower($_SERVER["HTTPS"]) == "on"), true); + } + + /** + * @static + * @return bool + */ + public static function hasRememberCookie() + { + return (isSet($_COOKIE["AjaXplorer-remember"]) && !empty($_COOKIE["AjaXplorer-remember"])); + } + + /** + * + * @return array [fakeuser,fakepass] + */ + public static function getRememberCookieData(){ + return explode(":", $_COOKIE["AjaXplorer-remember"]); + } + + /** + * @static + * Warning, must be called before sending other headers! + * @param $user + */ + public static function clearRememberCookie($user) + { + $current = $_COOKIE["AjaXplorer-remember"]; + if (!empty($current) && $user != null) { + CookiesHelper::invalidateCookieString($user, substr($current, strpos($current, ":")+1)); + } + setcookie("AjaXplorer-remember", "", time()-3600, null, null, (isSet($_SERVER["HTTPS"]) && strtolower($_SERVER["HTTPS"]) == "on"), true); + } + + /** + * @param UserInterface $user + * @param string $cookieString + * @return bool + */ + public static function checkCookieString(UserInterface $user, $cookieString) + { + if($user->getPref("cookie_hash") == "") return false; + $hashes = explode(",", $user->getPref("cookie_hash")); + return in_array($cookieString, $hashes); + } + + /** + * @param UserInterface $user + * @param string $cookieString + */ + public static function invalidateCookieString(UserInterface $user, $cookieString = "") + { + if($user->getPref("cookie_hash") == "") return; + $hashes = explode(",", $user->getPref("cookie_hash")); + if(in_array($cookieString, $hashes)) $hashes = array_diff($hashes, array($cookieString)); + $user->setPref("cookie_hash", implode(",", $hashes)); + $user->save("user"); + } + + /** + * @param UserInterface $user + * @return string + */ + public static function getCookieString(UserInterface $user) + { + $hashes = $user->getPref("cookie_hash"); + if ($hashes == "") { + $hashes = array(); + } else { + $hashes = explode(",", $hashes); + } + $newHash = md5($user->getId().":". StringHelper::generateRandomString()); + array_push($hashes, $newHash); + $user->setPref("cookie_hash", implode(",",$hashes)); + $user->save("user"); + return $newHash; + } + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Utils/Http/UserAgent.php b/core/src/core/src/pydio/Core/Utils/Http/UserAgent.php new file mode 100644 index 0000000000..fe6d5b8e65 --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Http/UserAgent.php @@ -0,0 +1,212 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils\Http; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Helpers to detect current User-Agent values + * @package Pydio\Core\Utils + */ +class UserAgent +{ + + /** + * Detect mobile browsers + * @static + * @return bool + */ + public static function userAgentIsMobile() + { + $op = strtolower($_SERVER['HTTP_X_OPERAMINI_PHONE'] OR ""); + $ua = strtolower($_SERVER['HTTP_USER_AGENT']); + $ac = strtolower($_SERVER['HTTP_ACCEPT']); + $isMobile = strpos($ac, 'application/vnd.wap.xhtml+xml') !== false + || $op != '' + || strpos($ua, 'sony') !== false + || strpos($ua, 'symbian') !== false + || strpos($ua, 'nokia') !== false + || strpos($ua, 'samsung') !== false + || strpos($ua, 'mobile') !== false + || strpos($ua, 'android') !== false + || strpos($ua, 'windows ce') !== false + || strpos($ua, 'epoc') !== false + || strpos($ua, 'opera mini') !== false + || strpos($ua, 'nitro') !== false + || strpos($ua, 'j2me') !== false + || strpos($ua, 'midp-') !== false + || strpos($ua, 'cldc-') !== false + || strpos($ua, 'netfront') !== false + || strpos($ua, 'mot') !== false + || strpos($ua, 'up.browser') !== false + || strpos($ua, 'up.link') !== false + || strpos($ua, 'audiovox') !== false + || strpos($ua, 'blackberry') !== false + || strpos($ua, 'ericsson,') !== false + || strpos($ua, 'panasonic') !== false + || strpos($ua, 'philips') !== false + || strpos($ua, 'sanyo') !== false + || strpos($ua, 'sharp') !== false + || strpos($ua, 'sie-') !== false + || strpos($ua, 'portalmmm') !== false + || strpos($ua, 'blazer') !== false + || strpos($ua, 'avantgo') !== false + || strpos($ua, 'danger') !== false + || strpos($ua, 'palm') !== false + || strpos($ua, 'series60') !== false + || strpos($ua, 'palmsource') !== false + || strpos($ua, 'pocketpc') !== false + || strpos($ua, 'smartphone') !== false + || strpos($ua, 'rover') !== false + || strpos($ua, 'ipaq') !== false + || strpos($ua, 'au-mic,') !== false + || strpos($ua, 'alcatel') !== false + || strpos($ua, 'ericy') !== false + || strpos($ua, 'up.link') !== false + || strpos($ua, 'vodafone/') !== false + || strpos($ua, 'wap1.') !== false + || strpos($ua, 'wap2.') !== false; + return $isMobile; + } + + /** + * Detect iOS browser + * @static + * @return bool + */ + public static function userAgentIsIOS() + { + if (stripos($_SERVER["HTTP_USER_AGENT"], "iphone") !== false) return true; + if (stripos($_SERVER["HTTP_USER_AGENT"], "ipad") !== false) return true; + if (stripos($_SERVER["HTTP_USER_AGENT"], "ipod") !== false) return true; + return false; + } + + /** + * Detect Windows Phone + * @static + * @return bool + */ + public static function userAgentIsWindowsPhone() + { + if (stripos($_SERVER["HTTP_USER_AGENT"], "IEMobile") !== false) return true; + return false; + } + + /** + * Detect Android UA + * @static + * @return bool + */ + public static function userAgentIsAndroid() + { + return (stripos($_SERVER["HTTP_USER_AGENT"], "android") !== false); + } + + /** + * Detect native apps user agent values (io, android, python) + * @return bool + */ + public static function userAgentIsNativePydioApp() + { + + return ( + stripos($_SERVER["HTTP_USER_AGENT"], "ajaxplorer-ios-client") !== false + || stripos($_SERVER["HTTP_USER_AGENT"], "Apache-HttpClient") !== false + || stripos($_SERVER["HTTP_USER_AGENT"], "python-requests") !== false + || stripos($_SERVER["HTTP_USER_AGENT"], "Pydio-Native") !== false + ); + } + + /** + * + * @param null $useragent + * @return int|string + */ + public static function osFromUserAgent($useragent = null) + { + + $osList = array + ( + 'Windows 10' => 'windows nt 10.0', + 'Windows 8.1' => 'windows nt 6.3', + 'Windows 8' => 'windows nt 6.2', + 'Windows 7' => 'windows nt 6.1', + 'Windows Vista' => 'windows nt 6.0', + 'Windows Server 2003' => 'windows nt 5.2', + 'Windows XP' => 'windows nt 5.1', + 'Windows 2000 sp1' => 'windows nt 5.01', + 'Windows 2000' => 'windows nt 5.0', + 'Windows NT 4.0' => 'windows nt 4.0', + 'Windows Me' => 'win 9x 4.9', + 'Windows 98' => 'windows 98', + 'Windows 95' => 'windows 95', + 'Windows CE' => 'windows ce', + 'Windows (version unknown)' => 'windows', + 'OpenBSD' => 'openbsd', + 'SunOS' => 'sunos', + 'Ubuntu' => 'ubuntu', + 'Linux' => '(linux)|(x11)', + 'Mac OSX Beta (Kodiak)' => 'mac os x beta', + 'Mac OSX Cheetah' => 'mac os x 10.0', + 'Mac OSX Jaguar' => 'mac os x 10.2', + 'Mac OSX Panther' => 'mac os x 10.3', + 'Mac OSX Tiger' => 'mac os x 10.4', + 'Mac OSX Leopard' => 'mac os x 10.5', + 'Mac OSX Snow Leopard' => 'mac os x 10.6', + 'Mac OSX Lion' => 'mac os x 10.7', + 'Mac OSX Mountain Lion' => 'mac os x 10.8', + 'Mac OSX Mavericks' => 'mac os x 10.9', + 'Mac OSX Yosemite' => 'mac os x 10.10', + 'Mac OSX El Capitan' => 'mac os x 10.11', + 'Mac OSX Puma' => 'mac os x 10.1', + 'Mac OS (classic)' => '(mac_powerpc)|(macintosh)', + 'QNX' => 'QNX', + 'BeOS' => 'beos', + 'Apple iPad' => 'iPad', + 'Apple iPhone' => 'iPhone', + 'OS2' => 'os\/2', + 'SearchBot' => '(nuhk)|(googlebot)|(yammybot)|(openbot)|(slurp)|(msnbot)|(ask jeeves\/teoma)|(ia_archiver)', + 'Pydio iOS Native Application' => 'ajaxplorer-ios', + 'PydioPro iOS Native Application' => 'Pydio-Native-iOS', + 'Pydio Android Native Application' => 'Apache-HttpClient', + 'PydioPro Android Native Application' => 'Pydio-Native-Android', + 'Pydio Sync Client' => 'python-requests' + ); + + if ($useragent == null) { + $useragent = $_SERVER['HTTP_USER_AGENT']; + $useragent = strtolower($useragent); + } + + $found = "Not automatically detected.$useragent"; + foreach ($osList as $os => $match) { + if (preg_match('/' . $match . '/i', $useragent)) { + $found = $os; + break; + } + } + + return $found; + + + } +} \ No newline at end of file diff --git a/core/src/core/classes/securimage/words/words.txt b/core/src/core/src/pydio/Core/Utils/Http/captcha_words.txt similarity index 100% rename from core/src/core/classes/securimage/words/words.txt rename to core/src/core/src/pydio/Core/Utils/Http/captcha_words.txt diff --git a/core/src/core/src/pydio/Core/Utils/Reflection/DiagnosticRunner.php b/core/src/core/src/pydio/Core/Utils/Reflection/DiagnosticRunner.php new file mode 100644 index 0000000000..57489f75a5 --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Reflection/DiagnosticRunner.php @@ -0,0 +1,181 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils\Reflection; + +use Pydio\Core\Services\RepositoryService; +use Pydio\Tests\AbstractTest; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Class DiagnosticRunner: run startup tests and output them in different formats + * @package Pydio\Core\Utils + */ +class DiagnosticRunner +{ + + /** + * Generate an HTML table for the tests results. We should use a template somewhere... + * @static + * @param $outputArray + * @param $testedParams + * @param bool $showSkipLink + * @return string + */ + public static function testResultsToTable($outputArray, $testedParams, $showSkipLink = true) + { + $dumpRows = ""; + $passedRows = array(); + $warnRows = ""; + $errRows = ""; + $errs = $warns = 0; + $ALL_ROWS = array( + "error" => array(), + "warning" => array(), + "dump" => array(), + "passed" => array(), + ); + $TITLES = array( + "error" => "Failed Tests", + "warning" => "Warnings", + "dump" => "Server Information", + "passed" => "Other tests passed", + ); + foreach ($outputArray as $item) { + + // A test is output only if it hasn't succeeded (doText returned FALSE) + $result = $item["result"] ? "passed" : ($item["level"] == "info" ? "dump" : ($item["level"] == "warning" + ? "warning" : "error")); + $success = $result == "passed"; + if ($result == "dump") $result = "passed"; + $ALL_ROWS[$result][$item["name"]] = $item["info"]; + } + ob_start(); + include(AJXP_TESTS_FOLDER . "/startup.phtml"); + return ob_get_flush(); + } + + /** + * @static + * @param $outputArray + * @param $testedParams + * @return bool + */ + public static function runTests(&$outputArray, &$testedParams) + { + // At first, list folder in the tests subfolder + chdir(AJXP_TESTS_FOLDER); + $files = glob('*.php'); + + $outputArray = array(); + $testedParams = array(); + $passed = true; + foreach ($files as $file) { + require_once($file); + // Then create the test class + $testName = "Pydio\\Tests\\" . str_replace(".php", "", $file); + if (!class_exists($testName) || $testName == "Pydio\\Tests\\AbstractTest") continue; + $class = new $testName(); + if (!($class instanceof AbstractTest)) continue; + + $result = $class->doTest(); + if (!$result && $class->failedLevel != "info") $passed = false; + $outputArray[] = array( + "name" => $class->name, + "result" => $result, + "level" => $class->failedLevel, + "info" => $class->failedInfo); + if (count($class->testedParams)) { + $testedParams = array_merge($testedParams, $class->testedParams); + } + } + // PREPARE REPOSITORY LISTS + $repoList = array(); + $REPOSITORIES = array(); + //require_once("../classes/class.ConfService.php"); + //require_once("../classes/class.Repository.php"); + include(AJXP_CONF_PATH . "/bootstrap_repositories.php"); + foreach ($REPOSITORIES as $index => $repo) { + $repoList[] = RepositoryService::createRepositoryFromArray($index, $repo); + } + // Try with the serialized repositories + if (is_file(AJXP_DATA_PATH . "/plugins/conf.serial/repo.ser")) { + $fileLines = file(AJXP_DATA_PATH . "/plugins/conf.serial/repo.ser"); + $repos = unserialize($fileLines[0]); + $repoList = array_merge($repoList, $repos); + } + + // NOW TRY THE PLUGIN TESTS + chdir(AJXP_INSTALL_PATH . "/" . AJXP_PLUGINS_FOLDER); + $files = glob('access.*/test.*.php'); + foreach ($files as $file) { + require_once($file); + // Then create the test class + list($accessFolder, $testFileName) = explode("/", $file); + $testName = "Pydio\\Tests\\" . str_replace(".php", "", substr($testFileName, 5) . "Test"); + $class = new $testName(); + foreach ($repoList as $repository) { + if ($repository->isTemplate || $repository->getParentId() != null) continue; + if (!($class instanceof AbstractTest)) continue; + $result = $class->doRepositoryTest($repository); + if ($result === false || $result === true) { + if (!$result && $class->failedLevel != "info") { + $passed = false; + } + $outputArray[] = array( + "name" => $class->name . "\n Testing repository : " . $repository->getDisplay(), + "result" => $result, + "level" => $class->failedLevel, + "info" => $class->failedInfo); + if (count($class->testedParams)) { + $testedParams = array_merge($testedParams, $class->testedParams); + } + } + } + } + + return $passed; + } + + /** + * @static + * @param $outputArray + * @param $testedParams + * @return void + */ + public static function testResultsToFile($outputArray, $testedParams) + { + ob_start(); + echo '$diagResults = '; + var_export($testedParams); + echo ';'; + echo '$outputArray = '; + var_export($outputArray); + echo ';'; + $content = ''; + ob_end_clean(); + if(!file_exists(dirname(TESTS_RESULT_FILE))){ + mkdir(dirname(TESTS_RESULT_FILE), 0666, true); + } + file_put_contents(TESTS_RESULT_FILE, $content); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Utils/Reflection/DocsParser.php b/core/src/core/src/pydio/Core/Utils/Reflection/DocsParser.php new file mode 100644 index 0000000000..de9ec73207 --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Reflection/DocsParser.php @@ -0,0 +1,138 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils\Reflection; + +use Pydio\Core\Model\Context; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Utils\Vars\StringHelper; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Helper methods to parse Pydio methods / hooks and generate documentation + * @package Pydio\Core\Utils + */ +class DocsParser +{ + /** + * File where to store JSON + * @return string + */ + public static function getHooksFile() + { + return AJXP_INSTALL_PATH . "/" . AJXP_DOCS_FOLDER . "/hooks.json"; + } + + /** + * Browse application registered hooks and send their definition to Hooks File. + */ + public static function extractHooksToDoc() + { + $docFile = self::getHooksFile(); + if (is_file($docFile)) { + copy($docFile, $docFile . ".bak"); + $existingHooks = json_decode(file_get_contents($docFile), true); + } else { + $existingHooks = array(); + } + $allPhpFiles1 = self::glob_recursive(AJXP_BIN_FOLDER . "/*.php"); + $allPhpFiles2 = self::glob_recursive(AJXP_INSTALL_PATH . "/plugins/*.php"); + $allPhpFiles3 = self::glob_recursive(AJXP_INSTALL_PATH . "/conf/*.php"); + $allPhpFiles = array_merge(array_merge($allPhpFiles1, $allPhpFiles2), $allPhpFiles3); + $hooks = array(); + foreach ($allPhpFiles as $phpFile) { + $fileContent = file($phpFile); + foreach ($fileContent as $lineNumber => $line) { + if (preg_match_all('/Controller::applyHook\("([^"]+)", (.*)\)/', $line, $matches)) { + $names = $matches[1]; + $params = $matches[2]; + foreach ($names as $index => $hookName) { + if (!isSet($hooks[$hookName])) $hooks[$hookName] = array("TRIGGERS" => array(), "LISTENERS" => array()); + $filename = substr($phpFile, strlen(AJXP_INSTALL_PATH)); + if (strpos($filename, "/plugins") === 0) { + $source = explode("/", $filename)[2]; + } else { + $parts = explode("/", $filename); + $source = str_replace(array("class.", ".php"), "", array_pop($parts)); + } + if (!isSet($hooks[$hookName]["TRIGGERS"][$source])) { + $hooks[$hookName]["TRIGGERS"][$source] = array(); + } + $hooks[$hookName]["TRIGGERS"][$source][] = array( + "FILE" => $filename, + "LINE" => $lineNumber + ); + $hooks[$hookName]["PARAMETER_SAMPLE"] = $params[$index]; + } + } + + } + } + $registryHooks = PluginsService::getInstance(Context::emptyContext())->searchAllManifests("//hooks/serverCallback", "xml", false, false, true); + $regHooks = array(); + /** @var \DOMElement $xmlHook */ + foreach ($registryHooks as $xmlHook) { + $name = $xmlHook->getAttribute("hookName"); + $method = $xmlHook->getAttribute("methodName"); + $pluginId = $xmlHook->getAttribute("pluginId"); + $deferred = $xmlHook->getAttribute("defer") === "true"; + if ($pluginId == "") $pluginId = $xmlHook->parentNode->parentNode->parentNode->getAttribute("id"); + if (!isSet($regHooks[$name])) $regHooks[$name] = array(); + $data = array("PLUGIN_ID" => $pluginId, "METHOD" => $method); + if ($deferred) $data["DEFERRED"] = true; + $regHooks[$name][] = $data; + } + + foreach ($hooks as $h => $data) { + + if (isSet($regHooks[$h])) { + $data["LISTENERS"] = $regHooks[$h]; + } + if (isSet($existingHooks[$h])) { + $existingHooks[$h]["TRIGGERS"] = $data["TRIGGERS"]; + $existingHooks[$h]["LISTENERS"] = $data["LISTENERS"]; + $existingHooks[$h]["PARAMETER_SAMPLE"] = $data["PARAMETER_SAMPLE"]; + } else { + $existingHooks[$h] = $data; + } + } + file_put_contents($docFile, StringHelper::prettyPrintJSON(json_encode($existingHooks))); + + } + + /** + * Does not support flag GLOB_BRACE + * @param $pattern + * @param int $flags + * @return array + */ + private static function glob_recursive($pattern, $flags = 0) + { + $files = glob($pattern, $flags); + foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir) { + $files = array_merge($files, self::glob_recursive($dir.'/'.basename($pattern), $flags)); + } + return $files; + } + + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Utils/Reflection/JSPacker.php b/core/src/core/src/pydio/Core/Utils/Reflection/JSPacker.php new file mode 100644 index 0000000000..fd097fd316 --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Reflection/JSPacker.php @@ -0,0 +1,125 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils\Reflection; +use Pydio\Core\Model\Context; +use Pydio\Core\PluginFramework\PluginsService; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Encapsulation of the javascript/css packing library + * @package Pydio + * @subpackage Core + */ +class JSPacker +{ + /** + * Static function for packing all js and css into big files + * Auto detect /js/*_list.txt files and /css/*_list.txt files and pack them. + */ + public static function pack() + { + // Make sure that the gui.* plugin is loaded + PluginsService::getInstance(Context::emptyContext())->getPluginsByType("gui"); + + $sList = glob(CLIENT_RESOURCES_FOLDER."/js/*_list.txt"); + foreach ($sList as $list) { + $scriptName = str_replace("_list.txt", ".js", $list); + JSPacker::concatListAndPack($list, + $scriptName, + "Normal"); + if(isSet($_GET["separate"])){ + self::compactEach($list, "Normal"); + } + } + } + + /** + * Perform actual compression + * @param $src + * @param $out + * @param $mode + * @return bool + */ + public static function concatListAndPack($src, $out, $mode) + { + if (!is_file($src) || !is_readable($src)) { + return false; + } + + // Concat List into one big string + $jscode = '' ; + $noMiniCode = ''; + $lines = file($src); + foreach($lines as $jsline){ + if(trim($jsline) == '') continue; + $noMini = false; + if(strpos($jsline, "#NO_MINI") !== FALSE){ + $jsline = str_replace("#NO_MINI", "", $jsline); + $noMini = true; + } + $code = file_get_contents(AJXP_INSTALL_PATH."/".CLIENT_RESOURCES_FOLDER."/".rtrim($jsline,"\n\r")) ; + if ($code) { + if($noMini) $noMiniCode .= $code; + else $jscode .= $code ; + } + + } + + $packer = new \JavaScriptPacker($jscode, $mode , true, false); + $packed = $packer->pack(); + if ($mode == "None") { // css case, hack for I.E. + $packed = str_replace("solid#", "solid #", $packed); + } + if(!empty($noMiniCode)){ + $packed.="\n".$noMiniCode; + } + @file_put_contents($out, $packed); + + return true; + } + + /** + * @param $list + * @param $mode + */ + private function compactEach($list, $mode){ + $lines = file($list); + $fullcode = ''; + foreach($lines as $line){ + $in = AJXP_INSTALL_PATH."/".CLIENT_RESOURCES_FOLDER."/".rtrim($line,"\n\r"); + $out = str_replace("/js/", "/js/min/", $in); + $outfull = str_replace(".js", ".full.js", $out); + $jscode = file_get_contents($in); + $fullcode .= $jscode; + // Pack and write to file + $packer = new \JavaScriptPacker($jscode, $mode , true, false); + $packed = $packer->pack(); + file_put_contents($out, $packed); + + // Pack and write to file + $packer = new \JavaScriptPacker($fullcode, $mode , true, false); + $packed = $packer->pack(); + file_put_contents($outfull, $packed); + } + } + +} diff --git a/core/src/core/src/pydio/Core/Utils/Reflection/LocaleExtractor.php b/core/src/core/src/pydio/Core/Utils/Reflection/LocaleExtractor.php new file mode 100644 index 0000000000..29e5e1cea8 --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Reflection/LocaleExtractor.php @@ -0,0 +1,184 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils\Reflection; + +use Pydio\Core\Model\Context; +use Pydio\Core\PluginFramework\Plugin; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Services\LocaleService; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class LocaleExtractor + * @package Pydio\Core\Utils + */ +class LocaleExtractor +{ + + /** + * i18n utilitary for extracting the CONF_MESSAGE[] strings out of the XML files + * @static + * @return void + */ + public static function extractConfStringsFromManifests() + { + $plugins = PluginsService::getInstance(Context::emptyContext())->getDetectedPlugins(); + /** + * @var Plugin $plug + */ + foreach ($plugins as $pType => $plugs) { + foreach ($plugs as $plug) { + $lib = $plug->getManifestRawContent("//i18n", "nodes"); + if (!$lib->length) continue; + /** @var \DOMElement $library */ + $library = $lib->item(0); + $namespace = $library->getAttribute("namespace"); + $path = $library->getAttribute("path"); + $xml = $plug->getManifestRawContent(); + // for core, also load mixins + $refFile = AJXP_INSTALL_PATH . "/" . $path . "/conf/en.php"; + $reference = array(); + if (preg_match_all("/CONF_MESSAGE(\[.*?\])/", $xml, $matches, PREG_SET_ORDER)) { + foreach ($matches as $match) { + $match[1] = str_replace(array("[", "]"), "", $match[1]); + $reference[$match[1]] = $match[1]; + } + } + if ($namespace == "") { + $mixXml = file_get_contents(AJXP_INSTALL_PATH . "/" . AJXP_PLUGINS_FOLDER . "/core.ajaxplorer/ajxp_mixins.xml"); + if (preg_match_all("/MIXIN_MESSAGE(\[.*?\])/", $mixXml, $matches, PREG_SET_ORDER)) { + foreach ($matches as $match) { + $match[1] = str_replace(array("[", "]"), "", $match[1]); + $reference[$match[1]] = $match[1]; + } + } + } + if (count($reference)) { + self::updateI18nFromRef($refFile, $reference); + } + } + } + } + + /** + * Browse the i18n libraries and update the languages with the strings missing + * @static + * @param string $createLanguage + * @param string $pluginId + * @return void + */ + public static function updateAllI18nLibraries($createLanguage = "", $pluginId = "") + { + // UPDATE EN => OTHER LANGUAGES + $nodes = PluginsService::getInstance(Context::emptyContext())->searchAllManifests("//i18n", "nodes"); + /** @var \DOMElement $node */ + foreach ($nodes as $node) { + $nameSpace = $node->getAttribute("namespace"); + if(!empty($pluginId)){ + $plug = $node->parentNode->parentNode->parentNode->getAttribute("id"); + if($plug !== $pluginId) continue; + } + $path = AJXP_INSTALL_PATH . "/" . $node->getAttribute("path"); + if ($nameSpace == "") { + self::updateI18nFiles($path, false, $createLanguage); + self::updateI18nFiles($path . "/conf", true, $createLanguage); + } else { + self::updateI18nFiles($path, true, $createLanguage); + self::updateI18nFiles($path . "/conf", true, $createLanguage); + } + } + } + + /** + * Patch the languages files of an i18n library with the references strings from the "en" file. + * @static + * @param $baseDir + * @param bool $detectLanguages + * @param string $createLanguage + */ + public static function updateI18nFiles($baseDir, $detectLanguages = true, $createLanguage = "") + { + if (!is_dir($baseDir) || !is_file($baseDir . "/en.php")) return; + if ($createLanguage != "" && !is_file($baseDir . "/$createLanguage.php")) { + @copy(AJXP_INSTALL_PATH . "/plugins/core.ajaxplorer/i18n-template.php", $baseDir . "/$createLanguage.php"); + } + if (!$detectLanguages) { + $languages = LocaleService::listAvailableLanguages(); + $filenames = array(); + foreach ($languages as $key => $value) { + $filenames[] = $baseDir . "/" . $key . ".php"; + } + } else { + $filenames = glob($baseDir . "/*.php"); + } + + $mess = array(); + include($baseDir . "/en.php"); + $reference = $mess; + + foreach ($filenames as $filename) { + self::updateI18nFromRef($filename, $reference); + } + } + + /** + * i18n Utilitary + * @static + * @param $filename + * @param $reference + */ + public static function updateI18nFromRef($filename, $reference) + { + if (!is_file($filename)) return; + $mess = array(); + include($filename); + $missing = array(); + foreach ($reference as $messKey => $message) { + if (!array_key_exists($messKey, $mess)) { + $missing[] = "\"$messKey\" => \"$message\","; + } + } + //print_r($missing); + if (count($missing)) { + $header = array(); + $currentMessages = array(); + $footer = array(); + $fileLines = file($filename); + $insideArray = false; + foreach ($fileLines as $line) { + if (strstr($line, "\"") !== false) { + $currentMessages[] = trim($line); + $insideArray = true; + } else { + if (!$insideArray && strstr($line, ");") !== false) $insideArray = true; + if (!$insideArray) { + $header[] = trim($line); + } else { + $footer[] = trim($line); + } + } + } + $currentMessages = array_merge($header, $currentMessages, $missing, $footer); + file_put_contents($filename, join("\n", $currentMessages)); + } + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Utils/Reflection/PydioSdkGenerator.php b/core/src/core/src/pydio/Core/Utils/Reflection/PydioSdkGenerator.php new file mode 100644 index 0000000000..535ce7157c --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Reflection/PydioSdkGenerator.php @@ -0,0 +1,237 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + * + * + */ + +namespace Pydio\Core\Utils\Reflection; + +//define("JSON_DIR", AJXP_INSTALL_PATH."/core/doc/api"); +use Pydio\Core\PluginFramework\Plugin; +use Pydio\Core\PluginFramework\PluginsService; + +define("JSON_DIR", AJXP_INSTALL_PATH."/../api"); +define("JSON_URL", "https://pydio.com/static-docs/api"); +define("API_DOC_PAGE", "https://pydio.com/en/docs/references/pydio-api#!/"); + +/** + * Class PydioSdkGenerator + * Generate a Swagger file for Rest APIs v1 documentation + * @package Pydio\Core\Utils\Reflection + */ +class PydioSdkGenerator +{ + static $apiGroups = [ + "fs" => ["access.fs", "index.*", "meta.*", "editor.*", "action.share", "action.powerfs"], + "conf" => ["access.ajxp_conf", "action.scheduler", "action.updater"], + "lifecycle" => ["conf.*", "auth.*", "gui.*", "core.*", "action.avatar"], + "nonfs" => ["access.*"], + "misc" => ["*"] + ]; + + static $apiGroupsLabels = [ + "fs" => "Most current operations on files and folders, their metadata, and additional sharing features.", + "conf" => "Administration task : users/groups/workspaces provisionning, maintenance tasks, etc... Generally performed using /settings/ as workspace alias.", + "lifecycle" => "Application objects lifecycle, like current user access rights and preferences, authentication utils, etc. As they are generally not linked to a specific workspace, these actions can be performed using /pydio/ instead of a workspace alias.", + "nonfs" => "Non-standard drivers accessing to structured data like IMAP, MySQL, Apis, etc.", + "misc" => "Other plugins actions." + ]; + + /** + * @param $pluginId + * @return int|string + */ + private static function findApiGroupForPlugin($pluginId){ + list($pType, $pName) = explode(".", $pluginId); + foreach(self::$apiGroups as $groupName => $pluginPatterns){ + foreach($pluginPatterns as $pattern){ + if($pattern == "*" || $pattern == "$pType.*" || $pattern == "$pType.$pName"){ + return $groupName; + } + } + } + return "misc"; + } + + /** + * @param string $versionString + */ + public static function analyzeRegistry($versionString) + { + if(!AJXP_SERVER_DEBUG) { + echo "Please switch the server to debug mode to use this API."; + return; + } + + $pServ = PluginsService::getInstance(); + $nodes = $pServ->searchAllManifests('//actions/*/processing/serverCallback[@developerComment]', 'node', false, false, true); + $jsFile = AJXP_DATA_PATH."/public/sdkMethods.js"; + $swaggerJsonDir = JSON_DIR."/".$versionString; + $swaggerAPIs = array(); + $methods = array(); + $alreadyParsed = array(); + foreach ($nodes as $callbackNode) { + $params = array(); + $swaggerParams = array(); + $pluginName = $callbackNode->parentNode->parentNode->parentNode->parentNode->parentNode->getAttribute("id"); + $actionName = $callbackNode->parentNode->parentNode->getAttribute("name"); + $methodName = $callbackNode->getAttribute("sdkMethodName"); + if(empty($methodName)){ + $methodName = $actionName; + } + $outputType = 'xml'; + /* + if(in_array($actionName, $alreadyParsed)){ + continue; + } + $alreadyParsed[] = $actionName; + */ + if(!isset($swaggerAPIs[$pluginName])) $swaggerAPIs[$pluginName] = array(); + + foreach ($callbackNode->childNodes as $child) { + if($child->nodeType != XML_ELEMENT_NODE)continue; + if ($child->nodeName == "input_param") { + $params[$child->getAttribute("name")] = array( + "name" => $child->getAttribute("name"), + "type" => $child->getAttribute("type"), + "mandatory" => $child->getAttribute("mandatory") === "true", + "default" => $child->getAttribute("default"), + ); + $default = $child->getAttribute("default"); + $swaggerParams[] = array( + "name" => $child->getAttribute("name"), + "description" => $child->getAttribute("description"). "
    ".(!empty($default) ? "Default: $default" : ""), + "required" => ($child->getAttribute("mandatory") === "true"), + "allowMultiple" => (strpos($child->getAttribute("type"), "[]") !== false), + "dataType" => (strpos($child->getAttribute("type"), "[]") !== false) ? "array":$child->getAttribute("type"), + "paramType" => "query" + ); + + } else if ($child->nodeName=="output") { + $outputType = $child->getAttribute("type"); + } + } + $methods[$methodName] = array( + "action" => $actionName, + "params" => $params, + "output" => $outputType + ); + $comment = $callbackNode->getAttribute("developerComment"); + $http = $callbackNode->getAttribute("preferredHttp"); + $restParams = $callbackNode->getAttribute("restParams"); + $prefix = "/workspace_alias"; + $apiGroup = self::findApiGroupForPlugin($pluginName); + if($apiGroup == "conf"){ + $prefix = "/settings"; + }else if($apiGroup == "lifecycle"){ + $prefix = "/pydio"; + } + $api = array( + "path" => $prefix."/".$actionName . (empty($restParams) ? "" : $restParams), + "operations" => array( + array( + "method" => empty($http) ? "POST" : strtoupper($http), + "summary" => substr($comment, 0, 80) . (strlen($comment) > 80 ? "..." : ""), + "notes" => $comment, + "responseClass" => $outputType, + "nickname" => $methodName, + "parameters" => $swaggerParams + ) + ) + ); + $swaggerAPIs[$pluginName][] = $api; + } + + file_put_contents($jsFile, "window.sdkMethods = ".json_encode($methods, JSON_PRETTY_PRINT)); + + $apidocs = array( + "apiVersion" => $versionString, + "swaggerVersion" => "1.2", + "apis" => array() + ); + $allDocs = array(); + $markdowns = array(); + + foreach($swaggerAPIs as $pluginName => $apis){ + + echo("Writing file for $pluginName"); + + $swaggerJson = array( + "apiVersion" => $versionString, + "swaggerVersion" => 1.2, + "basePath" => JSON_URL."/$versionString", + "resourcePath" => "/api", + "produces" => array("application/xml"), + "apis" => $apis + ); + file_put_contents($swaggerJsonDir."/".$pluginName, json_encode($swaggerJson, JSON_PRETTY_PRINT)); + $p = $pServ->getPluginById($pluginName); + $apiGroup = self::findApiGroupForPlugin($pluginName); + if(!isset($allDocs[$apiGroup])) { + $allDocs[$apiGroup] = array(); + $markdowns[$apiGroup] = array(); + } + $markdowns[$apiGroup][] = self::makeMarkdown($p, $apis); + $allDocs[$apiGroup][] = array( + "path" => JSON_URL."/$versionString/".$pluginName, + "description" => $p->getManifestDescription() + ); + $apidocs["apis"][] = array( + "path" => JSON_URL."/$versionString/".$pluginName, + "description" => $p->getManifestDescription() + ); + + } + foreach($allDocs as $apiGroupName => $groupApis){ + $groupApiDocs = array( + "apiVersion" => $versionString, + "swaggerVersion" => "1.2", + "apis" => $groupApis + ); + file_put_contents($swaggerJsonDir."/api-docs-".$apiGroupName, json_encode($groupApiDocs, JSON_PRETTY_PRINT)); + file_put_contents($swaggerJsonDir."/api-md-".$apiGroupName, self::$apiGroupsLabels[$apiGroupName]."\n\n".implode("", $markdowns[$apiGroupName])); + } + // Store file with all apis. + file_put_contents($swaggerJsonDir."/api-docs", json_encode($apidocs, JSON_PRETTY_PRINT)); + } + + /** + * @param Plugin $plugin + * @param array $apis + * @return string + */ + private static function makeMarkdown($plugin, $apis){ + + $md = "\n\n"; + $md .= "## ".$plugin->getManifestLabel()." "; + $md .= "\n".$plugin->getManifestDescription()."\n\n"; + $id = $plugin->getId(); + foreach($apis as $index => $api) { + $md .= "\n"; + $md .= "- **".$api["path"]."** \n"; + $md .= " ".$api["operations"][0]["notes"]." \n"; + $md .= " [Details](".API_DOC_PAGE."".$id."/".$api["operations"][0]["nickname"]."_".strtolower($api["operations"][0]["method"])."_".$index.")"; + } + + return $md; + + } + +} diff --git a/core/src/core/src/pydio/Core/Utils/TextEncoder.php b/core/src/core/src/pydio/Core/Utils/TextEncoder.php new file mode 100644 index 0000000000..4b500b7168 --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/TextEncoder.php @@ -0,0 +1,199 @@ +, Cyril Russo + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils; + +use Pydio\Core\Model\ContextInterface; + +use Pydio\Core\Services\SessionService; + +defined('AJXP_EXEC') or die( 'Access not allowed'); +/** + * Static utilitaries to encode/decode charset to/from utf8 + * @package Pydio + * @subpackage Core + */ +class TextEncoder +{ + /** @var ContextInterface */ + private static $context; + + /** + * @param ContextInterface $ctx + */ + public static function updateContext($ctx){ + self::$context = $ctx; + } + + /** + * Change the charset of a string from input to output + * @static + * @param string $inputCharset + * @param string $outputCharset + * @param string $text + * @return string + */ + public static function changeCharset($inputCharset, $outputCharset, $text) + { + if ($inputCharset == $outputCharset) return $text; + // Due to iconv bug when dealing with text with non ASCII encoding for last char, we use this workaround http://fr.php.net/manual/fr/function.iconv.php#81494 + if (function_exists("iconv")) { + + return iconv($inputCharset, $outputCharset, $text); + } else { + $content = @htmlentities($text, ENT_QUOTES, $inputCharset); + return @html_entity_decode($content, ENT_QUOTES , $outputCharset); + } + } + + public static $currentCharsetValue; + /** + * Detect the current charset from the current locale + * @static + * @param string $locale + * @return string + */ + public static function parseCharset($locale) + { + $test = explode("@", $locale); + $locale = $test[0]; + $encoding = substr(strrchr($locale, "."), 1); + if (is_numeric($encoding)) { + if (substr($encoding, 0, 2) == "12") // CP12xx are changed to Windows-12xx to allow PHP4 conversion + $encoding = "windows-".$encoding; + else $encoding = "CP".$encoding; // In other cases, PHP4 won't work anyway, so use CPxxxx encoding (that iconv supports) + } else if ($locale == "C") { // Locale not set correctly, most probable error cause is /etc/init.d/apache having "LANG=C" defined + // In any case, "C" is ASCII-7 bit so it's safe to use the extra bit as if it was UTF-8 + $encoding = "UTF-8"; + } + if (!strlen($encoding)) $encoding = "UTF-8"; + return $encoding; + } + /** + * Try to detect the current encoding (cached in session) + * @static + * @return string + */ + public static function getEncoding() + { + if (self::$currentCharsetValue == null) { + $charset = null; + if (!empty(self::$context) && self::$context->hasRepository()) { + $charset = self::$context->getRepository()->getSafeOption("CHARSET"); + if(empty($charset) && SessionService::getContextCharset(self::$context->getRepositoryId()) !== null){ + $charset = SessionService::getContextCharset(self::$context->getRepositoryId()); + } + } + if (!empty($charset)) { + // Check if the session get an assigned charset encoding (it's the case for remote SSH for example) + self::$currentCharsetValue = $charset; + } else { + // Get the current locale (expecting the filesystem is in the same locale, as the standard says) + self::$currentCharsetValue = self::parseCharset(setlocale(LC_CTYPE, 0)); + if(!empty(self::$context) && self::$context->hasRepository()){ + self::$context->getRepository()->addOption("CHARSET", $charset); + SessionService::setContextCharset(self::$context->getRepositoryId(), $charset); + } + } + } + return self::$currentCharsetValue; + } + /** + * Decode a string from UTF8 to current Charset + * @static + * @param string $filesystemElement + * @param bool $test Try to detect if it's really utf8 or not + * @return string + */ + public static function fromUTF8($filesystemElement, $test = false) + { + if ($test && !TextEncoder::isUtf8($filesystemElement)) { + return $filesystemElement; + } + $enc = TextEncoder::getEncoding(); + return TextEncoder::changeCharset("UTF-8", $enc, $filesystemElement); + } + + + /** + * Transform a string from current charset to utf8 + * @static + * @param string $filesystemElement + * @param bool $test Test if it's already UTF8 or not, to avoid double-encoding + * @return string + */ + public static function toUTF8($filesystemElement, $test = true) + { + if ($test && TextEncoder::isUtf8($filesystemElement)) { + return $filesystemElement; + } + $enc = TextEncoder::getEncoding(); + return TextEncoder::changeCharset($enc, "UTF-8", $filesystemElement); + } + /** + * Test if a string seem to be already UTF8-encoded + * @static + * @param string $string + * @return bool + */ + public static function isUtf8($string) + { + return preg_match('%^(?: + [\x09\x0A\x0D\x20-\x7E] # ASCII + | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte + | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs + | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte + | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates + | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 + | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 + | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 + )*$%xs', $string); + } + /** + * Transform a string from current Storage charset to utf8 + * @static + * @param string $filesystemElement + * @param bool $test Test if it's already UTF8 or not, to avoid double-encoding + * @return string + */ + public static function fromStorageEncoding($filesystemElement, $test = true) + { + if ($test && TextEncoder::isUtf8($filesystemElement)) { + return $filesystemElement; + } + $enc = TextEncoder::getEncoding(); + return TextEncoder::changeCharset($enc, "UTF-8", $filesystemElement); + } + /** + * Decode a string from UTF8 to current Storage Charset + * @static + * @param string $filesystemElement + * @param bool $test Try to detect if it's really utf8 or not + * @return string + */ + public static function toStorageEncoding($filesystemElement, $test = false) + { + if ($test && !TextEncoder::isUtf8($filesystemElement)) { + return $filesystemElement; + } + $enc = TextEncoder::getEncoding(); + return TextEncoder::changeCharset("UTF-8", $enc, $filesystemElement); + } +} diff --git a/core/src/core/src/pydio/Core/Utils/Vars/InputFilter.php b/core/src/core/src/pydio/Core/Utils/Vars/InputFilter.php new file mode 100644 index 0000000000..c1b0aa62a5 --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Vars/InputFilter.php @@ -0,0 +1,336 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils\Vars; + +use Psr\Http\Message\UploadedFileInterface; +use Pydio\Core\Exception\ForbiddenCharacterException; +use Pydio\Core\Services\LocaleService; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Tools to clean inputs + * @package Pydio\Core\Utils + */ +class InputFilter +{ + + const SANITIZE_HTML = 1; + const SANITIZE_HTML_STRICT = 2; + const SANITIZE_ALPHANUM = 3; + const SANITIZE_EMAILCHARS = 4; + const SANITIZE_FILENAME = 5; + const SANITIZE_DIRNAME = 6; + + /** + * Remove all "../../" tentatives, replace double slashes + * @static + * @param string $path + * @return string + */ + public static function securePath($path) + { + if ($path == null) { + return ""; + } + // + // REMOVE ALL "../" TENTATIVES + // + $path = str_replace(chr(0), "", $path); + $dirs = self::safeExplode($path); + $count = count($dirs); + for ($i = 0; $i < $count; $i++) { + if ($dirs[$i] == '.' or $dirs[$i] == '..') { + $dirs[$i] = ''; + } + } + // rebuild safe directory string + $path = implode('/', $dirs); + + // + // REPLACE DOUBLE SLASHES + // + while (preg_match('/\/\//', $path)) { + $path = str_replace('//', '/', $path); + } + return $path; + } + + /** + * @param $path + * @return array + */ + public static function safeExplode($path) { + return (DIRECTORY_SEPARATOR === "\\" ? preg_split('/(\\\|\\/)/', $path) : explode('/', $path)); + } + + + /** + * Given a string, this function will determine if it potentially an + * XSS attack and return boolean. + * + * @param string $string + * The string to run XSS detection logic on + * @return boolean + * True if the given `$string` contains XSS, false otherwise. + */ + public static function detectXSS($string) + { + $contains_xss = FALSE; + + // Skip any null or non string values + if (is_null($string) || !is_string($string)) { + return $contains_xss; + } + + // Keep a copy of the original string before cleaning up + $orig = $string; + + // Set the patterns we'll test against + $patterns = array( + // Match any attribute starting with "on" or xmlns + '#(<[^>]+[\x00-\x20\"\'\/])(on|xmlns)[^>]*>?#iUu', + + // Match javascript:, livescript:, vbscript: and mocha: protocols + '!((java|live|vb)script|mocha|feed|data):(\w)*!iUu', + '#-moz-binding[\x00-\x20]*:#u', + + // Match style attributes + '#(<[^>]+[\x00-\x20\"\'\/])style=[^>]*>?#iUu', + + // Match unneeded tags + '#]*>?#i' + ); + + foreach ($patterns as $pattern) { + // Test both the original string and clean string + if (preg_match($pattern, $string) || preg_match($pattern, $orig)) { + $contains_xss = TRUE; + } + if ($contains_xss === TRUE) return TRUE; + } + + return FALSE; + } + + /** + * Function to clean a string from specific characters + * + * @static + * @param string $s + * @param int $level Can be InputFilter::SANITIZE_ALPHANUM, InputFilter::SANITIZE_EMAILCHARS, InputFilter::SANITIZE_HTML, InputFilter::SANITIZE_HTML_STRICT + * @param string $expand + * @param bool $throwException + * @return mixed|string + * @throws ForbiddenCharacterException + */ + public static function sanitize($s, $level = InputFilter::SANITIZE_HTML, $throwException = false, $expand = 'script|style|noframes|select|option') + { + $original = $s; + if ($level == InputFilter::SANITIZE_ALPHANUM) { + $s = preg_replace("/[^a-zA-Z0-9_\-\.]/", "", $s); + if($throwException && $original !== $s){ + throw new ForbiddenCharacterException($original); + } + return $s; + } else if ($level == InputFilter::SANITIZE_EMAILCHARS) { + $s = preg_replace("/[^a-zA-Z0-9_\-\.@!%\+=|~\?]/", "", $s); + if($throwException && $original !== $s){ + throw new ForbiddenCharacterException($original); + } + return $s; + } else if ($level == InputFilter::SANITIZE_FILENAME || $level == InputFilter::SANITIZE_DIRNAME) { + // Convert Hexadecimals + $s = preg_replace_callback('!(&#|\\\)[xX]([0-9a-fA-F]+);?!', function($array){ + return chr(hexdec($array[1])); + }, $s); + // Clean up entities + $s = preg_replace('!(�+[0-9]+)!', '$1;', $s); + // Decode entities + $s = html_entity_decode($s, ENT_NOQUOTES, 'UTF-8'); + // Strip whitespace characters + $s = ltrim($s); + $s = str_replace(chr(0), "", $s); + if ($level == InputFilter::SANITIZE_FILENAME) { + $s = preg_replace("/[\"\/\|\?\\\]/", "", $s); + } else { + $s = preg_replace("/[\"\|\?\\\]/", "", $s); + } + if (self::detectXSS($s)) { + if (strpos($s, "/") === 0) $s = "/XSS Detected - Rename Me"; + else $s = "XSS Detected - Rename Me"; + } + if($throwException && $original !== $s){ + throw new ForbiddenCharacterException($original); + } + return $s; + } + + /**/ //prep the string + $s = ' ' . $s; + + //begin removal + //remove comment blocks + $pos = []; $len = []; + while (stripos($s, '', $pos[1]); + $len[1] = $pos[2] - $pos[1] + 3; + $x = substr($s, $pos[1], $len[1]); + $s = str_replace($x, '', $s); + } + + //remove tags with content between them + if (strlen($expand) > 0) { + $e = explode('|', $expand); + $pos = []; $len = []; + $eLength = count($e); + for ($i = 0; $i < $eLength; $i++) { + while (stripos($s, '<' . $e[$i]) > 0) { + $len[1] = strlen('<' . $e[$i]); + $pos[1] = stripos($s, '<' . $e[$i]); + $pos[2] = stripos($s, $e[$i] . '>', $pos[1] + $len[1]); + $len[2] = $pos[2] - $pos[1] + $len[1]; + $x = substr($s, $pos[1], $len[2]); + $s = str_replace($x, '', $s); + } + } + } + + $s = strip_tags($s); + if ($level == InputFilter::SANITIZE_HTML_STRICT) { + $s = preg_replace("/[\",;\/`<>:\*\|\?!\^\\\]/", "", $s); + } else { + $s = str_replace(array("<", ">"), array("<", ">"), $s); + } + if($throwException && $original !== $s){ + throw new ForbiddenCharacterException($original); + } + return ltrim($s); + } + + /** + * Perform standard urldecode, sanitization and securepath + * @static + * @param $data + * @param int $sanitizeLevel + * @return string + * @throws ForbiddenCharacterException + */ + public static function decodeSecureMagic($data, $sanitizeLevel = InputFilter::SANITIZE_DIRNAME) + { + return InputFilter::sanitize(InputFilter::securePath($data), $sanitizeLevel, true); + } + + /** + * Parse the $fileVars[] PHP errors + * @static + * @param array|UploadedFileInterface $boxData + * @param bool $throwException + * @return array|null + * @throws \Exception + */ + public static function parseFileDataErrors($boxData, $throwException = false) + { + $mess = LocaleService::getMessages(); + if (is_array($boxData)) { + $userfile_error = $boxData["error"]; + $userfile_tmp_name = $boxData["tmp_name"]; + $userfile_size = $boxData["size"]; + } else { + $userfile_error = $boxData->getError(); + $userfile_size = $boxData->getSize(); + $userfile_tmp_name = ""; + } + if ($userfile_error != UPLOAD_ERR_OK) { + $errorsArray = array(); + $errorsArray[UPLOAD_ERR_FORM_SIZE] = $errorsArray[UPLOAD_ERR_INI_SIZE] = array(409, str_replace("%i", ini_get("upload_max_filesize"), $mess["537"])); + $errorsArray[UPLOAD_ERR_NO_FILE] = array(410, $mess[538]); + $errorsArray[UPLOAD_ERR_PARTIAL] = array(410, $mess[539]); + $errorsArray[UPLOAD_ERR_NO_TMP_DIR] = array(410, $mess[540]); + $errorsArray[UPLOAD_ERR_CANT_WRITE] = array(411, $mess[541]); + $errorsArray[UPLOAD_ERR_EXTENSION] = array(410, $mess[542]); + if ($userfile_error == UPLOAD_ERR_NO_FILE) { + // OPERA HACK, do not display "no file found error" + if (strpos($_SERVER['HTTP_USER_AGENT'], 'Opera') === false) { + $data = $errorsArray[$userfile_error]; + if ($throwException) throw new \Exception($data[1], $data[0]); + return $data; + } + } else { + $data = $errorsArray[$userfile_error]; + if ($throwException) throw new \Exception($data[1], $data[0]); + return $data; + } + } + if ($userfile_tmp_name == "none" || $userfile_size == 0) { + if ($throwException) throw new \Exception($mess[31], 410); + return array(410, $mess[31]); + } + return null; + } + + /** + * Parse a Comma-Separated-Line value + * @static + * @param $string + * @param bool $hash + * @return array + */ + public static function parseCSL($string, $hash = false) + { + $exp = array_map("trim", explode(",", $string)); + if (!$hash) return $exp; + $assoc = array(); + foreach ($exp as $explVal) { + $reExp = explode("|", $explVal); + if (count($reExp) == 1) $assoc[$reExp[0]] = $reExp[0]; + else $assoc[$reExp[0]] = $reExp[1]; + } + return $assoc; + } + + /** + * This function is used when the server's PHP configuration is using magic quote + * @param string $text + * @return string + */ + public static function magicDequote($text) + { + // If the PHP server enables magic quotes, remove them + if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) + return stripslashes($text); + return $text; + } + + /** + * call fromUTF8 + * @static + * @param string $filesystemElement + * @return string + */ + public static function fromPostedFileName($filesystemElement) + { + return InputFilter::magicDequote($filesystemElement); + } + + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Utils/Vars/OptionsHelper.php b/core/src/core/src/pydio/Core/Utils/Vars/OptionsHelper.php new file mode 100644 index 0000000000..9f5ddc8390 --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Vars/OptionsHelper.php @@ -0,0 +1,244 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils\Vars; + +use Pydio\Core\Model\Context; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\ApplicationState; +use Pydio\Core\Utils\Crypto; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Class FormHelper: manipulate "standard" form submission + * @package Pydio\Core\Utils + */ +class OptionsHelper +{ + + /** + * + * @param $userId + * @param $password + * @return string + */ + public static function decypherStandardFormPassword($userId, $password) + { + if(Crypto::hasCBCEnctypeHeader($password)){ + return Crypto::decrypt($password, Crypto::buildKey($userId, Crypto::getApplicationSecret())); + }else{ + // Legacy encryption + return Crypto::decrypt($password, md5($userId . "\1CDAFx¨op#")); + } + } + + /** + * @param $metadata + * @param $nestedData + * @param null $userId + * @param null $binariesContext + * @param string $cypheredPassPrefix + */ + public static function filterFormElementsFromMeta(ContextInterface $ctx, $metadata, &$nestedData, $userId = null, $binariesContext = null, $cypheredPassPrefix = "") + { + foreach ($metadata as $key => $level) { + if (!array_key_exists($key, $nestedData)) continue; + if (!is_array($level)) continue; + if (isSet($level["ajxp_form_element"])) { + // filter now + $type = $level["type"]; + if ($type == "binary" && $binariesContext != null) { + $value = $nestedData[$key]; + if ($value == "ajxp-remove-original") { + if (!empty($level["original_binary"])) { + ConfService::getConfStorageImpl()->deleteBinary($binariesContext, $level["original_binary"]); + } + $value = ""; + } else { + $file = ApplicationState::getAjxpTmpDir() . "/" . $value; + if (file_exists($file)) { + $id = !empty($level["original_binary"]) ? $level["original_binary"] : null; + $id = ConfService::getConfStorageImpl()->saveBinary($binariesContext, $file, $id); + $value = $id; + } + } + $nestedData[$key] = $value; + }else if($type === "password" && !empty($cypheredPassPrefix) && $ctx->hasUser()){ + $value = $nestedData[$key]; + if(trim($value) !== '' && $value !== '__AJXP_VALUE_SET__'){ + $nestedData[$key] = $cypheredPassPrefix . Crypto::encrypt($value, Crypto::buildKey($ctx->getUser()->getId(), Crypto::getApplicationSecret())); + } + } + } else { + self::filterFormElementsFromMeta($ctx, $level, $nestedData[$key], $userId, $binariesContext, $cypheredPassPrefix); + } + } + } + + /** + * @param ContextInterface $ctx + * @param $repDef + * @param $options + * @param string $prefix + * @param null $binariesContext + * @param string $cypheredPassPrefix + */ + public static function parseStandardFormParameters(ContextInterface $ctx, &$repDef, &$options, $prefix = "DRIVER_OPTION_", $binariesContext = null, $cypheredPassPrefix = "") + { + if ($binariesContext === null) { + $binariesContext = array("USER" => ($ctx->hasUser()) ? $ctx->getUser()->getId() : "shared"); + } + $replicationGroups = array(); + $switchesGroups = array(); + foreach ($repDef as $key => $value) { + if (((!empty($prefix) && strpos($key, $prefix) !== false && strpos($key, $prefix) == 0) || empty($prefix)) + && strpos($key, "ajxptype") === false + && strpos($key, "_original_binary") === false + && strpos($key, "_replication") === false + && strpos($key, "_checkbox") === false + ) { + if (isSet($repDef[$key . "_ajxptype"])) { + $type = $repDef[$key . "_ajxptype"]; + if ($type == "boolean") { + $value = ($value == "true" ? true : false); + } else if ($type == "integer") { + $value = intval($value); + } else if ($type == "array") { + $value = explode(",", $value); + } else if ($type == "password" && $ctx->hasUser() && !empty($cypheredPassPrefix)) { + if (trim($value) != "" && $value != "__AJXP_VALUE_SET__") { + $value = $cypheredPassPrefix . Crypto::encrypt($value, Crypto::buildKey($ctx->getUser()->getId(), Crypto::getApplicationSecret())); + } + } else if ($type == "binary" && $binariesContext !== null) { + if (!empty($value)) { + if ($value == "ajxp-remove-original") { + if (!empty($repDef[$key . "_original_binary"])) { + ConfService::getConfStorageImpl()->deleteBinary($binariesContext, $repDef[$key . "_original_binary"]); + } + $value = ""; + } else { + $file = ApplicationState::getAjxpTmpDir() . "/" . $value; + if (file_exists($file)) { + $id = !empty($repDef[$key . "_original_binary"]) ? $repDef[$key . "_original_binary"] : null; + $id = ConfService::getConfStorageImpl()->saveBinary($binariesContext, $file, $id); + $value = $id; + } + } + } else if (!empty($repDef[$key . "_original_binary"])) { + $value = $repDef[$key . "_original_binary"]; + } + } else if (strpos($type, "group_switch:") === 0) { + $tmp = explode(":", $type); + $gSwitchName = $tmp[1]; + $switchesGroups[substr($key, strlen($prefix))] = $gSwitchName; + } else if ($type == "text/json") { + $value = json_decode($value, true); + } + if (!in_array($type, array("textarea", "boolean", "text/json", "password"))) { + $value = InputFilter::sanitize($value, InputFilter::SANITIZE_HTML); + } + unset($repDef[$key . "_ajxptype"]); + } + if (isSet($repDef[$key . "_checkbox"])) { + $checked = $repDef[$key . "_checkbox"] == "checked"; + unset($repDef[$key . "_checkbox"]); + if (!$checked) continue; + } + if (isSet($repDef[$key . "_replication"])) { + $repKey = $repDef[$key . "_replication"]; + if (!is_array($replicationGroups[$repKey])) $replicationGroups[$repKey] = array(); + $replicationGroups[$repKey][] = $key; + } + $options[substr($key, strlen($prefix))] = $value; + unset($repDef[$key]); + } else { + $repDef[$key] = $value; + } + } + // DO SOMETHING WITH REPLICATED PARAMETERS? + if (count($switchesGroups)) { + $gValues = array(); + foreach ($switchesGroups as $fieldName => $groupName) { + if (isSet($options[$fieldName])) { + $gValues = array(); + $radic = $groupName . "_" . $options[$fieldName] . "_"; + foreach ($options as $optN => $optV) { + if (strpos($optN, $radic) === 0) { + $newName = substr($optN, strlen($radic)); + $gValues[$newName] = $optV; + } + } + } + $options[$fieldName . "_group_switch"] = $options[$fieldName]; + $options[$fieldName] = $gValues; + } + } + + } + + private static $_dibiParamClean = array(); + + + /** + * @param $params + * @return array|mixed + * @throws \Exception + * @throws \Pydio\Core\Exception\PydioException + */ + public static function cleanDibiDriverParameters($params) + { + if (!is_array($params)) return $params; + $value = $params["group_switch_value"]; + if (isSet($value)) { + if (isSet(self::$_dibiParamClean[$value])) { + return self::$_dibiParamClean[$value]; + } + if ($value == "core") { + $bootStorage = ConfService::getBootConfStorageImpl(); + $configs = $bootStorage->loadPluginConfig("core", "conf"); + $params = $configs["DIBI_PRECONFIGURATION"]; + if (!is_array($params)) { + throw new \Exception("Empty SQL default connexion, there is something wrong with your setup! You may have switch to an SQL-based plugin without defining a connexion."); + } + } else { + unset($params["group_switch_value"]); + } + foreach ($params as $k => $v) { + $explode = explode("_", $k, 2); + $params[array_pop($explode)] = VarsFilter::filter($v, Context::emptyContext()); + unset($params[$k]); + } + } + switch ($params["driver"]) { + case "sqlite": + case "sqlite3": + $params["formatDateTime"] = "'Y-m-d H:i:s'"; + $params["formatDate"] = "'Y-m-d'"; + break; + } + if (isSet($value)) { + self::$_dibiParamClean[$value] = $params; + } + return $params; + } +} diff --git a/core/src/core/src/pydio/Core/Utils/Vars/PasswordEncoder.php b/core/src/core/src/pydio/Core/Utils/Vars/PasswordEncoder.php new file mode 100644 index 0000000000..9180c5e4e0 --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Vars/PasswordEncoder.php @@ -0,0 +1,177 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils\Vars; + +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Utils\Crypto; + +defined('AJXP_EXEC') or die('Access not allowed'); + +// THESE ARE DEFINED IN bootstrap_context.php +// REPEAT HERE FOR BACKWARD COMPATIBILITY. +if (!defined('PBKDF2_HASH_ALGORITHM')) { + + define("PBKDF2_HASH_ALGORITHM", "sha256"); + define("PBKDF2_ITERATIONS", 1000); + define("PBKDF2_SALT_BYTE_SIZE", 24); + define("PBKDF2_HASH_BYTE_SIZE", 24); + + define("HASH_SECTIONS", 4); + define("HASH_ALGORITHM_INDEX", 0); + define("HASH_ITERATION_INDEX", 1); + define("HASH_SALT_INDEX", 2); + define("HASH_PBKDF2_INDEX", 3); + + define("USE_OPENSSL_RANDOM", false); + +} + +/** + * Class PasswordEncoder + * @package Pydio\Core\Utils + */ +class PasswordEncoder +{ + + /** + * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt + * $algorithm - The hash algorithm to use. Recommended: SHA256 + * $password - The password. + * $salt - A salt that is unique to the password. + * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000. + * $key_length - The length of the derived key in bytes. + * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise. + * Returns: A $key_length-byte key derived from the password and salt. + * + * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt + * + * This implementation of PBKDF2 was originally created by https://defuse.ca + * With improvements by http://www.variations-of-shadow.com + * @param $algorithm + * @param $password + * @param $salt + * @param $count + * @param $key_length + * @param bool $raw_output + * @return string + * @throws PydioException + */ + public static function pbkdf2_apply($algorithm, $password, $salt, $count, $key_length, $raw_output = false) + { + $algorithm = strtolower($algorithm); + + if (!in_array($algorithm, hash_algos(), true)) + throw new PydioException('PBKDF2 ERROR: Invalid hash algorithm.'); + if ($count <= 0 || $key_length <= 0) + throw new PydioException('PBKDF2 ERROR: Invalid parameters.'); + + $hash_length = strlen(hash($algorithm, "", true)); + $block_count = ceil($key_length / $hash_length); + + $output = ""; + + for ($i = 1; $i <= $block_count; $i++) { + // $i encoded as 4 bytes, big endian. + $last = $salt . pack("N", $i); + // first iteration + $last = $xorsum = hash_hmac($algorithm, $last, $password, true); + + // perform the other $count - 1 iterations + for ($j = 1; $j < $count; $j++) { + $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true)); + } + + $output .= $xorsum; + } + + if ($raw_output) + return substr($output, 0, $key_length); + else + return bin2hex(substr($output, 0, $key_length)); + } + + /** + * Compares two strings $a and $b in length-constant time. + * @param $a + * @param $b + * @return bool + */ + public static function pbkdf2_slow_equals($a, $b) + { + $diff = strlen($a) ^ strlen($b); + for ($i = 0; $i < strlen($a) && $i < strlen($b); $i++) { + $diff |= ord($a[$i]) ^ ord($b[$i]); + } + + return $diff === 0; + } + + /** + * @param $password + * @param $correct_hash + * @return bool + * @throws PydioException + */ + public static function pbkdf2_validate_password($password, $correct_hash) + { + $params = explode(":", $correct_hash); + + if (count($params) < HASH_SECTIONS) { + if (strlen($correct_hash) == 32 && count($params) == 1) { + return md5($password) == $correct_hash; + } + return false; + } + + $pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]); + return self::pbkdf2_slow_equals( + $pbkdf2, + self::pbkdf2_apply( + $params[HASH_ALGORITHM_INDEX], + $password, + $params[HASH_SALT_INDEX], + (int)$params[HASH_ITERATION_INDEX], + strlen($pbkdf2), + true + ) + ); + } + + /** + * @param $password + * @return string + * @throws PydioException + */ + public static function pbkdf2_create_hash($password) + { + // format: algorithm:iterations:salt:hash + $salt = Crypto::getRandomSalt(); + return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" . $salt . ":" . + base64_encode(self::pbkdf2_apply( + PBKDF2_HASH_ALGORITHM, + $password, + $salt, + PBKDF2_ITERATIONS, + PBKDF2_HASH_BYTE_SIZE, + true + )); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Utils/Vars/PathUtils.php b/core/src/core/src/pydio/Core/Utils/Vars/PathUtils.php new file mode 100644 index 0000000000..92acea1103 --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Vars/PathUtils.php @@ -0,0 +1,77 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils\Vars; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Helpers to manipulate file paths + * @package Pydio\Core\Utils + */ +class PathUtils +{ + + /** + * Parse file and replace \ by / + * @param $path + * @return mixed|string + */ + public static function forwardSlashDirname($path) + { + return (DIRECTORY_SEPARATOR === "\\" ? str_replace("\\", "/", dirname($path)) : dirname($path)); + } + + /** + * Parse file and replace \ by / + * @param $path + * @return mixed|string + */ + public static function forwardSlashBasename($path) + { + return (DIRECTORY_SEPARATOR === "\\" ? str_replace("\\", "/", basename($path)) : basename($path)); + } + + /** + * Fix openbasedir issue when browsing zip content as a normal folder + * @param string $dirPath + * @return string + */ + public static function patchPathForBaseDir($dirPath) + { + if (!ini_get("open_basedir") || !preg_match('/\.zip/i', $dirPath)) return $dirPath; + return str_replace(".zip", "__ZIP_EXTENSION__", $dirPath); + + } + + /** + * Fix openbasedir issue when browsing zip content as a normal folder + * @param string $dirPath + * @return string + */ + public static function unPatchPathForBaseDir($dirPath) + { + if (!ini_get("open_basedir")) return $dirPath; + return str_replace("__ZIP_EXTENSION__", ".zip", $dirPath); + } + + +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Utils/Vars/StatHelper.php b/core/src/core/src/pydio/Core/Utils/Vars/StatHelper.php new file mode 100644 index 0000000000..6e705d4b47 --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Vars/StatHelper.php @@ -0,0 +1,361 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils\Vars; + +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Core\Model\Context; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\LocaleService; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Class MimesHelper + * Various utils functions for file mimetypes + * @package Pydio\Core\Utils + */ +class StatHelper +{ + public static $sizeUnit; + + /** + * @param AJXP_Node $ajxpNode + * @param bool|null $isDir + * @return array + */ + public static function getMimeInfo($ajxpNode, $isDir = null) + { + if ($isDir === null) { + $isDir = !$ajxpNode->isLeaf(); + } + $fileName = strtolower($ajxpNode->getPath()); + $registeredExtensions = self::getRegisteredExtensions($ajxpNode->getContext()); + + if ($isDir) { + $mime = $registeredExtensions["ajxp_folder"]; + } else { + $pos = strrpos($fileName, "."); + if ($pos !== false) { + $fileExt = substr($fileName, $pos + 1); + if (!empty($fileExt) && array_key_exists($fileExt, $registeredExtensions) && $fileExt != "ajxp_folder" && $fileExt != "ajxp_empty") { + $mime = $registeredExtensions[$fileExt]; + } + } + } + if (!isSet($mime)) { + $mime = $registeredExtensions["ajxp_empty"]; + } + return array($mime[3], $mime[1], $mime[2]); + + } + + /** + * MISC CONFS + */ + private static $extensionsCache; + /** + * Get all registered extensions, from both the conf/extensions.conf.php and from the plugins + * @static + * @param ContextInterface $ctx + * @return array + */ + private static function getRegisteredExtensions(ContextInterface $ctx) + { + if (!is_array(self::$extensionsCache) || !array_key_exists($ctx->getStringIdentifier(), self::$extensionsCache)) { + $EXTENSIONS = array(); + $RESERVED_EXTENSIONS = array(); + include_once(AJXP_CONF_PATH."/extensions.conf.php"); + $EXTENSIONS = array_merge($RESERVED_EXTENSIONS, $EXTENSIONS); + foreach ($EXTENSIONS as $key => $value) { + unset($EXTENSIONS[$key]); + $EXTENSIONS[$value[0]] = $value; + } + $nodes = PluginsService::getInstance($ctx)->searchAllManifests("//extensions/extension", "nodes", true); + $res = array(); + /** @var \DOMElement $node */ + foreach ($nodes as $node) { + $res[$node->getAttribute("mime")] = array( + $node->getAttribute("mime"), + $node->getAttribute("icon"), + $node->getAttribute("font"), + $node->getAttribute("messageId") + ); + } + if (count($res)) { + $EXTENSIONS = array_merge($EXTENSIONS, $res); + } + if(!is_array(self::$extensionsCache)){ + self::$extensionsCache = []; + } + self::$extensionsCache[$ctx->getStringIdentifier()] = $EXTENSIONS; + } + return self::$extensionsCache[$ctx->getStringIdentifier()]; + } + + + /** + * Gather a list of mime that must be treated specially. Used for dynamic replacement in XML mainly. + * @static + * @param string $keyword "editable", "image", "audio", "zip" + * @return string + */ + public static function getAjxpMimes($keyword) + { + if ($keyword == "editable") { + // Gather editors! + $pServ = PluginsService::getInstance(Context::emptyContext()); + $plugs = $pServ->getPluginsByType("editor"); + //$plugin = new Plugin(); + $mimes = array(); + foreach ($plugs as $plugin) { + $node = $plugin->getManifestRawContent("/editor/@mimes", "node"); + $openable = $plugin->getManifestRawContent("/editor/@openable", "node"); + if ($openable->item(0) && $openable->item(0)->value == "true" && $node->item(0)) { + $mimestring = $node->item(0)->value; + $mimesplit = explode(",", $mimestring); + foreach ($mimesplit as $value) { + $mimes[$value] = $value; + } + } + } + return implode(",", array_values($mimes)); + } else if ($keyword == "image") { + return "png,bmp,jpg,jpeg,gif"; + } else if ($keyword == "audio") { + return "mp3"; + } else if ($keyword == "zip") { + if (ConfService::zipBrowsingEnabled()) { + return "zip,ajxp_browsable_archive"; + } else { + return "none_allowed"; + } + } + return ""; + } + + /** + * Whether a file is to be considered as an image or not + * @static + * @param $fileName + * @return bool + */ + public static function basenameIsImage($fileName) + { + return (preg_match("/\.png$|\.bmp$|\.jpg$|\.jpeg$|\.gif$/i", $fileName) ? true : false); + } + + /** + * Static image mime type headers + * @static + * @param $fileName + * @return string + */ + public static function getImageMimeType($fileName) + { + if (preg_match("/\.jpg$|\.jpeg$/i", $fileName)) { + return "image/jpeg"; + } else if (preg_match("/\.png$/i", $fileName)) { + return "image/png"; + } else if (preg_match("/\.bmp$/i", $fileName)) { + return "image/bmp"; + } else if (preg_match("/\.gif$/i", $fileName)) { + return "image/gif"; + } + return ""; + } + + /** + * Headers to send when streaming + * @static + * @param $fileName + * @return bool|string + */ + public static function getStreamingMimeType($fileName) + { + if (preg_match("/\.mp3$/i", $fileName)) { + return "audio/mp3"; + } else if (preg_match("/\.wav$/i", $fileName)) { + return "audio/wav"; + } else if (preg_match("/\.aac$/i", $fileName)) { + return "audio/aac"; + } else if (preg_match("/\.m4a$/i", $fileName)) { + return "audio/m4a"; + } else if (preg_match("/\.aiff$/i", $fileName)) { + return "audio/aiff"; + } else if (preg_match("/\.mp4$/i", $fileName)) { + return "video/mp4"; + } else if (preg_match("/\.mov$/i", $fileName)) { + return "video/quicktime"; + } else if (preg_match("/\.m4v$/i", $fileName)) { + return "video/x-m4v"; + } else if (preg_match("/\.3gp$/i", $fileName)) { + return "video/3gpp"; + } else if (preg_match("/\.3g2$/i", $fileName)) { + return "video/3gpp2"; + } else return false; + } + + /** + * Display a human readable string for a bytesize (1MB, 2,3Go, etc) + * @static + * @param $filesize + * @param bool $phpConfig + * @return string + */ + public static function roundSize($filesize, $phpConfig = false) + { + if (self::$sizeUnit == null) { + $mess = LocaleService::getMessages(); + self::$sizeUnit = $mess["byte_unit_symbol"]; + } + if ($filesize < 0) { + $filesize = sprintf("%u", $filesize); + } + if ($filesize >= 1073741824) { + $filesize = round($filesize / 1073741824 * 100) / 100 . ($phpConfig ? "G" : " G" . self::$sizeUnit); + } elseif ($filesize >= 1048576) { + $filesize = round($filesize / 1048576 * 100) / 100 . ($phpConfig ? "M" : " M" . self::$sizeUnit); + } elseif ($filesize >= 1024) { + $filesize = round($filesize / 1024 * 100) / 100 . ($phpConfig ? "K" : " K" . self::$sizeUnit); + } else { + $filesize = $filesize . " " . self::$sizeUnit; + } + if ($filesize == 0) { + $filesize = "-"; + } + return $filesize; + } + + /** + * Hidden files start with dot + * @static + * @param string $fileName + * @return bool + */ + public static function isHidden($fileName) + { + return (substr($fileName, 0, 1) == "."); + } + + /** + * Whether a file is a browsable archive + * @static + * @param string $fileName + * @return int + */ + public static function isBrowsableArchive($fileName) + { + return preg_match("/\.zip$/i", $fileName); + } + + /** + * Convert a shorthand byte value from a PHP configuration directive to an integer value + * @param string $value + * @return int + */ + public static function convertBytes($value) + { + if (is_numeric($value)) { + return intval($value); + } else { + $value_length = strlen($value); + $value = str_replace(",", ".", $value); + $qty = floatval(substr($value, 0, $value_length - 1)); + $unit = strtolower(substr($value, $value_length - 1)); + switch ($unit) { + case 'k': + $qty *= 1024; + break; + case 'm': + $qty *= 1048576; + break; + case 'g': + $qty *= 1073741824; + break; + } + return $qty; + } + } + + /** + * Build a relative date string, using pydio messages library + * @param $time + * @param $messages + * @param bool $shortestForm + * @return bool|mixed|string + */ + public static function relativeDate($time, $messages, $shortestForm = false) + { + $crtYear = date('Y'); + $today = strtotime(date('M j, Y')); + $reldays = ($time - $today) / 86400; + $relTime = date($messages['date_relative_time_format'], $time); + + if ($reldays >= 0 && $reldays < 1) { + return str_replace("TIME", $relTime, $messages['date_relative_today']); + } else if ($reldays >= 1 && $reldays < 2) { + return str_replace("TIME", $relTime, $messages['date_relative_tomorrow']); + } else if ($reldays >= -1 && $reldays < 0) { + return str_replace("TIME", $relTime, $messages['date_relative_yesterday']); + } + + if (abs($reldays) < 7) { + + if ($reldays > 0) { + $reldays = floor($reldays); + return str_replace("%s", $reldays, $messages['date_relative_days_ahead']); + //return 'In ' . $reldays . ' day' . ($reldays != 1 ? 's' : ''); + } else { + $reldays = abs(floor($reldays)); + return str_replace("%s", $reldays, $messages['date_relative_days_ago']); + //return $reldays . ' day' . ($reldays != 1 ? 's' : '') . ' ago'; + } + + } + $finalDate = date($messages["date_relative_date_format"], $time ? $time : time()); + if (strpos($messages["date_relative_date_format"], "F") !== false && isSet($messages["date_intl_locale"]) && extension_loaded("intl")) { + $intl = \IntlDateFormatter::create($messages["date_intl_locale"], \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, null, null, "MMMM"); + $localizedMonth = $intl->format($time ? $time : time()); + $dateFuncMonth = date("F", $time ? $time : time()); + $finalDate = str_replace($dateFuncMonth, $localizedMonth, $finalDate); + } + if (!$shortestForm || strpos($finalDate, $crtYear) !== false) { + $finalDate = str_replace($crtYear, '', $finalDate); + return str_replace("DATE", $finalDate, $messages["date_relative_date"]); + } else { + return $finalDate = date("M Y", $time ? $time : time()); + } + + } + + /** + * Hide file or folder for Windows OS + * @static + * @param $file + */ + public static function winSetHidden($file) + { + @shell_exec("attrib +H " . escapeshellarg($file)); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Utils/Vars/StringHelper.php b/core/src/core/src/pydio/Core/Utils/Vars/StringHelper.php new file mode 100644 index 0000000000..c58bea851a --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Vars/StringHelper.php @@ -0,0 +1,320 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils\Vars; + +use Pydio\Core\Utils\TextEncoder; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class StringHelper + * @package Pydio\Core\Utils + */ +class StringHelper +{ + + /** + * Performs a natural sort on the array keys. + * Behaves the same as ksort() with natural sorting added. + * + * @param array $array The array to sort + * @return boolean + */ + public static function natksort(&$array) + { + uksort($array, 'strnatcasecmp'); + return true; + } + + /** + * Performs a reverse natural sort on the array keys + * Behaves the same as krsort() with natural sorting added. + * + * @param array $array The array to sort + * @return boolean + */ + public static function natkrsort(&$array) + { + StringHelper::natksort($array); + $array = array_reverse($array, TRUE); + return true; + } + + /** + * Replace specific chars by their XML Entities, for use inside attributes value + * @static + * @param $string + * @param bool $toUtf8 + * @return mixed|string + */ + public static function xmlEntities($string, $toUtf8 = false) + { + $xmlSafe = str_replace(array("&", "<", ">", "\"", "\n", "\r"), array("&", "<", ">", """, " ", " "), $string); + if ($toUtf8 && TextEncoder::getEncoding() != "UTF-8") { + return TextEncoder::toUTF8($xmlSafe); + } else { + return $xmlSafe; + } + } + + /** + * Replace specific chars by their XML Entities, for use inside attributes value + * @static + * @param $string + * @param bool $toUtf8 + * @return mixed|string + */ + public static function xmlContentEntities($string, $toUtf8 = false) + { + $xmlSafe = str_replace(array("&", "<", ">", "\""), array("&", "<", ">", """), $string); + if ($toUtf8) { + return TextEncoder::toUTF8($xmlSafe); + } else { + return $xmlSafe; + } + } + + /** + * Modifies a string to remove all non ASCII characters and spaces. + * @param string $text + * @return string + */ + public static function slugify($text) + { + if (empty($text)) return ""; + // replace non letter or digits by - + $text = preg_replace('~[^\\pL\d]+~u', '-', $text); + + // trim + $text = trim($text, '-'); + + // transliterate + if (function_exists('iconv')) { + $text = iconv('utf-8', 'us-ascii//TRANSLIT', $text); + } + + // lowercase + $text = strtolower($text); + + // remove unwanted characters + $text = preg_replace('~[^-\w]+~', '', $text); + + if (empty($text)) { + return 'n-a'; + } + + return $text; + } + + /** + * Indents a flat JSON string to make it more human-readable. + * + * @param string $json The original JSON string to process. + * + * @return string Indented version of the original JSON string. + */ + public static function prettyPrintJSON($json) + { + $result = ''; + $pos = 0; + $strLen = strlen($json); + $indentStr = ' '; + $newLine = "\n"; + $prevChar = ''; + $outOfQuotes = true; + + for ($i = 0; $i <= $strLen; $i++) { + + // Grab the next character in the string. + $char = substr($json, $i, 1); + + // Are we inside a quoted string? + if ($char == '"' && $prevChar != '\\') { + $outOfQuotes = !$outOfQuotes; + + // If this character is the end of an element, + // output a new line and indent the next line. + } else if (($char == '}' || $char == ']') && $outOfQuotes) { + $result .= $newLine; + $pos--; + for ($j = 0; $j < $pos; $j++) { + $result .= $indentStr; + } + } + + // Add the character to the result string. + $result .= $char; + + // If the last character was the beginning of an element, + // output a new line and indent the next line. + if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) { + $result .= $newLine; + if ($char == '{' || $char == '[') { + $pos++; + } + + for ($j = 0; $j < $pos; $j++) { + $result .= $indentStr; + } + } + + $prevChar = $char; + } + + return $result; + } + + /** + * generates a random password, uses base64: 0-9a-zA-Z + * @param int [optional] $length length of password, default 24 (144 Bit) + * @param bool $complexChars + * @return string password + */ + public static function generateRandomString($length = 24, $complexChars = false) + { + if (function_exists('openssl_random_pseudo_bytes') && USE_OPENSSL_RANDOM && !$complexChars) { + $password = base64_encode(openssl_random_pseudo_bytes($length, $strong)); + if ($strong == TRUE) + return substr(str_replace(array("/", "+", "="), "", $password), 0, $length); //base64 is about 33% longer, so we need to truncate the result + } + + //fallback to mt_rand if php < 5.3 or no openssl available + $characters = '0123456789'; + $characters .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + if ($complexChars) { + $characters .= "!@#$%&*?"; + } + $charactersLength = strlen($characters) - 1; + $password = ''; + + //select some random characters + for ($i = 0; $i < $length; $i++) { + $password .= $characters[mt_rand(0, $charactersLength)]; + } + + return $password; + } + + /** + * @param $regexp + * @return string + */ + public static function regexpToLike($regexp) + { + $regexp = trim($regexp, '/'); + $left = "~"; + $right = "~"; + if ($regexp[0] == "^") { + $left = ""; + } + if ($regexp[strlen($regexp) - 1] == "$") { + $right = ""; + } + if ($left == "" && $right == "") { + return "= %s"; + } + return "LIKE %" . $left . "like" . $right; + } + + /** + * @param $regexp + * @return string + */ + public static function cleanRegexp($regexp) + { + $regexp = str_replace("\/", "/", trim($regexp, '/')); + return ltrim(rtrim($regexp, "$"), "^"); + } + + /** + * @param $regexp + * @return string + */ + public static function likeToLike($regexp) + { + $left = ""; + $right = ""; + if ($regexp[0] == "%") { + $left = "~"; + } + if ($regexp[strlen($regexp) - 1] == "%") { + $right = "~"; + } + if ($left == "" && $right == "") { + return "= %s"; + } + return "LIKE %" . $left . "like" . $right; + } + + /** + * @param $regexp + * @return string + */ + public static function cleanLike($regexp) + { + return ltrim(rtrim($regexp, "%"), "%"); + } + + /** + * @param $regexp + * @return null|string + */ + public static function regexpToLdap($regexp) + { + if (empty($regexp)) + return null; + + $left = "*"; + $right = "*"; + if ($regexp[0] == "^") { + $regexp = ltrim($regexp, "^"); + $left = ""; + } + if ($regexp[strlen($regexp) - 1] == "$") { + $regexp = rtrim($regexp, "$"); + $right = ""; + } + return $left . $regexp . $right; + } + + /** + * Create a unique UID + * @return string + */ + public static function createGUID() + { + if (function_exists('com_create_guid')) { + return com_create_guid(); + } else { + mt_srand((double)microtime() * 10000);//optional for php 4.2.0 and up. + $charid = strtoupper(md5(uniqid(rand(), true))); + $hyphen = chr(45);// "-" + $uuid = ""//chr(123)// "{" + . substr($charid, 0, 8) . $hyphen + . substr($charid, 8, 4) . $hyphen + . substr($charid, 12, 4) . $hyphen + . substr($charid, 16, 4) . $hyphen + . substr($charid, 20, 12); + //.chr(125);// "}" + return $uuid; + } + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Utils/Vars/UrlUtils.php b/core/src/core/src/pydio/Core/Utils/Vars/UrlUtils.php new file mode 100644 index 0000000000..6a1b8c836d --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Vars/UrlUtils.php @@ -0,0 +1,44 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils\Vars; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Various Helpers for URL and Path parsing + * @package Pydio\Core\Utils + */ +class UrlUtils +{ + + /** + * Parse URL ignoring # and ? + * @param $path + * @return array + */ + public static function safeParseUrl($path) + { + $parts = parse_url(str_replace(array("#", "?"), array("__AJXP_FRAGMENT__", "__AJXP_MARK__"), $path)); + $parts["path"] = str_replace(array("__AJXP_FRAGMENT__", "__AJXP_MARK__"), array("#", "?"), $parts["path"]); + return $parts; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Core/Utils/Vars/VarsFilter.php b/core/src/core/src/pydio/Core/Utils/Vars/VarsFilter.php new file mode 100644 index 0000000000..974e457a27 --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Vars/VarsFilter.php @@ -0,0 +1,116 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils\Vars; + +use Pydio\Core\Controller\Controller; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Model\Context; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services; + + +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\UsersService; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Standard values filtering used in the core. + * @static + * @package Pydio + * @subpackage Core + */ +class VarsFilter +{ + /** + * Filter the very basic keywords from the XML : AJXP_USER, AJXP_INSTALL_PATH, AJXP_DATA_PATH + * Calls the vars.filter hooks. + * @static + * @param mixed $value + * @param ContextInterface $ctx + * @return mixed|string + * @throws PydioException + */ + public static function filter($value, ContextInterface $ctx) + { + if (is_string($value) && strpos($value, "AJXP_USER")!==false) { + if (UsersService::usersEnabled()) { + if(!$ctx->hasUser()){ + throw new PydioException("Cannot resolve AJXP_USER without user passed in context"); + } + $value = str_replace("AJXP_USER", $ctx->getUser()->getId(), $value); + } else { + $value = str_replace("AJXP_USER", "shared", $value); + } + } + if (is_string($value) && strpos($value, "AJXP_GROUP_PATH")!==false) { + if (UsersService::usersEnabled()) { + if(!$ctx->hasUser()){ + throw new PydioException("Cannot resolve path AJXP_GROUP_PATH without user passed in context"); + } + $gPath = $ctx->getUser()->getGroupPath(); + $value = str_replace("AJXP_GROUP_PATH_FLAT", str_replace("/", "_", trim($gPath, "/")), $value); + $value = str_replace("AJXP_GROUP_PATH", $gPath, $value); + } else { + $value = str_replace(array("AJXP_GROUP_PATH", "AJXP_GROUP_PATH_FLAT"), "shared", $value); + } + } + if (is_string($value) && strpos($value, "AJXP_INSTALL_PATH") !== false) { + $value = str_replace("AJXP_INSTALL_PATH", AJXP_INSTALL_PATH, $value); + } + if (is_string($value) && strpos($value, "AJXP_DATA_PATH") !== false) { + $value = str_replace("AJXP_DATA_PATH", AJXP_DATA_PATH, $value); + } + if (is_string($value) && strstr($value, "AJXP_WORKSPACE_UUID") !== false) { + $value = rtrim(str_replace("AJXP_WORKSPACE_UUID", $ctx->getRepository()->getUniqueId(), $value), "/"); + } + if (is_string($value) && strstr($value, "AJXP_WORKSPACE_SLUG") !== false) { + $value = rtrim(str_replace("AJXP_WORKSPACE_SLUG", $ctx->getRepository()->getSlug(), $value), "/"); + } + if(is_string($value) && preg_match("/AJXP_PARENT_OPTION:([\w_-]*):/", $value, $matches)){ + $repoObject = $ctx->getRepository(); + $parentRepository = $repoObject->getParentRepository(); + if(empty($parentRepository)){ + throw new PydioException("Cannot resolve ".$matches[0]." without parent workspace"); + } + $parentOwner = $ctx->getRepository()->getOwner(); + $parentContext = Context::contextWithObjects(null, $parentRepository); + $parentContext->setUserId($parentOwner); + $parentPath = rtrim($parentRepository->getContextOption($parentContext, $matches[1]), "/"); + $value = str_replace($matches[0], $parentPath, $value); + } + + $tab = array(&$value, $ctx); + Controller::applyIncludeHook("vars.filter", $tab); + return $value; + } + + /** + * @param $array + */ + public static function filterI18nStrings(&$array){ + if(!is_array($array)) return; + $appTitle = ConfService::getGlobalConf("APPLICATION_TITLE"); + foreach($array as &$value){ + $value = str_replace("APPLICATION_TITLE", $appTitle, $value); + } + } +} diff --git a/core/src/core/src/pydio/Core/Utils/Vars/XMLFilter.php b/core/src/core/src/pydio/Core/Utils/Vars/XMLFilter.php new file mode 100644 index 0000000000..6c9ca1f13c --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/Vars/XMLFilter.php @@ -0,0 +1,111 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils\Vars; + +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\IAjxpWrapperProvider; +use Pydio\Core\Controller\Controller; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Serializer\NodeXML; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Services\ApplicationState; +use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Core\Utils\Vars\StatHelper; +use Pydio\Core\Utils\Vars\StringHelper; +use Pydio\Core\Services; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Utils\XMLHelper; + + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Filters XML data with pydio specific keywords + * @package Pydio + * @subpackage Core + */ +class XMLFilter +{ + + /** + * Dynamically replace XML keywords with their live values. + * AJXP_SERVER_ACCESS, AJXP_MIMES_*,AJXP_ALL_MESSAGES, etc. + * @static + * @param string $xml + * @param bool $stripSpaces + * @return mixed + */ + public static function resolveKeywords($xml, $stripSpaces = false) + { + $messages = LocaleService::getMessages(); + $confMessages = LocaleService::getConfigMessages(); + $matches = array(); + $xml = str_replace("AJXP_APPLICATION_TITLE", ConfService::getGlobalConf("APPLICATION_TITLE"), $xml); + $xml = str_replace("AJXP_MIMES_EDITABLE", StatHelper::getAjxpMimes("editable"), $xml); + $xml = str_replace("AJXP_MIMES_IMAGE", StatHelper::getAjxpMimes("image"), $xml); + $xml = str_replace("AJXP_MIMES_AUDIO", StatHelper::getAjxpMimes("audio"), $xml); + $xml = str_replace("AJXP_MIMES_ZIP", StatHelper::getAjxpMimes("zip"), $xml); + $authDriver = ConfService::getAuthDriverImpl(); + if ($authDriver != NULL) { + $loginRedirect = $authDriver->getLoginRedirect(); + $xml = str_replace("AJXP_LOGIN_REDIRECT", ($loginRedirect!==false?"'".$loginRedirect."'":"false"), $xml); + } + $xml = str_replace("AJXP_REMOTE_AUTH", "false", $xml); + $xml = str_replace("AJXP_NOT_REMOTE_AUTH", "true", $xml); + $xml = str_replace("AJXP_ALL_MESSAGES", "MessageHash=".json_encode(LocaleService::getMessages()).";", $xml); + + if (preg_match_all("/AJXP_MESSAGE(\[.*?\])/", $xml, $matches, PREG_SET_ORDER)) { + foreach ($matches as $match) { + $messId = str_replace("]", "", str_replace("[", "", $match[1])); + $xml = str_replace("AJXP_MESSAGE[$messId]", $messages[$messId], $xml); + } + } + if (preg_match_all("/CONF_MESSAGE(\[.*?\])/", $xml, $matches, PREG_SET_ORDER)) { + foreach ($matches as $match) { + $messId = str_replace(array("[", "]"), "", $match[1]); + $message = $messId; + if (array_key_exists($messId, $confMessages)) { + $message = $confMessages[$messId]; + } + $xml = str_replace("CONF_MESSAGE[$messId]", StringHelper::xmlEntities($message), $xml); + } + } + if (preg_match_all("/MIXIN_MESSAGE(\[.*?\])/", $xml, $matches, PREG_SET_ORDER)) { + foreach ($matches as $match) { + $messId = str_replace(array("[", "]"), "", $match[1]); + $message = $messId; + if (array_key_exists($messId, $confMessages)) { + $message = $confMessages[$messId]; + } + $xml = str_replace("MIXIN_MESSAGE[$messId]", StringHelper::xmlEntities($message), $xml); + } + } + if ($stripSpaces) { + $xml = preg_replace("/[\n\r]?/", "", $xml); + $xml = preg_replace("/\t/", " ", $xml); + } + $xml = str_replace(array('xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"','xsi:noNamespaceSchemaLocation="file:../core.ajaxplorer/ajxp_registry.xsd"'), "", $xml); + $tab = array(&$xml); + Controller::applyIncludeHook("xml.filter", $tab); + return $xml; + } + +} diff --git a/core/src/core/src/pydio/Core/Utils/XMLHelper.php b/core/src/core/src/pydio/Core/Utils/XMLHelper.php new file mode 100644 index 0000000000..c13fe327c1 --- /dev/null +++ b/core/src/core/src/pydio/Core/Utils/XMLHelper.php @@ -0,0 +1,183 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Core\Utils; + +use Pydio\Core\Utils\Vars\XMLFilter; +use Pydio\Core\Utils\Vars\StringHelper; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Static methods to help handling XML + * @package Pydio\Core\Utils + */ +class XMLHelper +{ + /** + * @var bool|string + */ + private static $headerSent = false; + + /** + * Simple XML element build from associative array. Can pass specific $children for nested elements. + * @param string $tagName + * @param array $attributes + * @param string $xmlChildren + * @return string + */ + public static function toXmlElement($tagName, $attributes, $xmlChildren = "") + { + $buffer = "<$tagName "; + foreach ($attributes as $attName => $attValue) { + $buffer .= "$attName=\"" . StringHelper::xmlEntities($attValue) . "\" "; + } + if (!strlen($xmlChildren)) { + $buffer .= "/>"; + } else { + $buffer .= ">" . $xmlChildren . ""; + } + return $buffer; + } + + /** + * Create plain PHP associative array from XML. + * + * Example usage: + * $xmlNode = simplexml_load_file('example.xml'); + * $arrayData = xmlToArray($xmlNode); + * echo json_encode($arrayData); + * + * @param \DOMNode $domXml The dom node to load + * @param array $options Associative array of options + * @return array + * @link http://outlandishideas.co.uk/blog/2012/08/xml-to-json/ More info + * @author Tamlyn Rhodes + * @license http://creativecommons.org/publicdomain/mark/1.0/ Public Domain + */ + public static function xmlToArray($domXml, $options = array()) + { + $xml = simplexml_import_dom($domXml); + $defaults = array( + 'namespaceSeparator' => ':',//you may want this to be something other than a colon + 'attributePrefix' => '@', //to distinguish between attributes and nodes with the same name + 'alwaysArray' => array(), //array of xml tag names which should always become arrays + 'autoArray' => true, //only create arrays for tags which appear more than once + 'textContent' => '$', //key used for the text content of elements + 'autoText' => true, //skip textContent key if node has no attributes or child nodes + 'keySearch' => false, //optional search and replace on tag and attribute names + 'keyReplace' => false //replace values for above search values (as passed to str_replace()) + ); + $options = array_merge($defaults, $options); + $namespaces = $xml->getDocNamespaces(); + $namespaces[''] = null; //add base (empty) namespace + + //get attributes from all namespaces + $attributesArray = array(); + foreach ($namespaces as $prefix => $namespace) { + foreach ($xml->attributes($namespace) as $attributeName => $attribute) { + //replace characters in attribute name + if ($options['keySearch']) $attributeName = + str_replace($options['keySearch'], $options['keyReplace'], $attributeName); + $attributeKey = $options['attributePrefix'] + . ($prefix ? $prefix . $options['namespaceSeparator'] : '') + . $attributeName; + $attributesArray[$attributeKey] = (string)$attribute; + } + } + + //get child nodes from all namespaces + $tagsArray = array(); + foreach ($namespaces as $prefix => $namespace) { + foreach ($xml->children($namespace) as $childXml) { + //recurse into child nodes + $childArray = self::xmlToArray($childXml, $options); + list($childTagName, $childProperties) = each($childArray); + + //replace characters in tag name + if ($options['keySearch']) $childTagName = + str_replace($options['keySearch'], $options['keyReplace'], $childTagName); + //add namespace prefix, if any + if ($prefix) $childTagName = $prefix . $options['namespaceSeparator'] . $childTagName; + + if (!isset($tagsArray[$childTagName])) { + //only entry with this key + //test if tags of this type should always be arrays, no matter the element count + $tagsArray[$childTagName] = + in_array($childTagName, $options['alwaysArray']) || !$options['autoArray'] + ? array($childProperties) : $childProperties; + } elseif ( + is_array($tagsArray[$childTagName]) && array_keys($tagsArray[$childTagName]) + === range(0, count($tagsArray[$childTagName]) - 1) + ) { + //key already exists and is integer indexed array + $tagsArray[$childTagName][] = $childProperties; + } else { + //key exists so convert to integer indexed array with previous value in position 0 + $tagsArray[$childTagName] = array($tagsArray[$childTagName], $childProperties); + } + } + } + + //get text content of node + $textContentArray = array(); + $plainText = trim((string)$xml); + if ($plainText !== '') $textContentArray[$options['textContent']] = $plainText; + + //stick it all together + $propertiesArray = !$options['autoText'] || $attributesArray || $tagsArray || ($plainText === '') + ? array_merge($attributesArray, $tagsArray, $textContentArray) : $plainText; + + //return node as array + return array( + $xml->getName() => $propertiesArray + ); + } + + /** + * Wrap xml inside a ... document, including declaration. + * @param $content + * @param string $docNode + * @param array $attributes + * @return string + */ + public static function wrapDocument($content, $docNode = "tree", $attributes = array()) + { + + if (self::$headerSent !== false && self::$headerSent == $docNode) { + return $content; + } + //header('Content-Type: text/xml; charset=UTF-8'); + //header('Cache-Control: no-cache'); + $buffer = ''; + $attString = ""; + if (count($attributes)) { + foreach ($attributes as $name => $value) { + $attString .= "$name=\"$value\" "; + } + } + self::$headerSent = $docNode; + $buffer .= "<$docNode $attString>"; + $buffer .= $content; + $buffer .= ""; + return $buffer; + + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/AbstractTest.php b/core/src/core/src/pydio/Tests/AbstractTest.php new file mode 100644 index 0000000000..52404e3965 --- /dev/null +++ b/core/src/core/src/pydio/Tests/AbstractTest.php @@ -0,0 +1,110 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +use Pydio\Access\Core\Model\Repository; +use Pydio\Core\Model\RepositoryInterface; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +global $MAIN_testsArray; +/** + * Abstract test class + * Abstract class for diagnostic tests. These tests are run at the first application start up, and their + * results are displayed in the Diagnostic page. It's possible to re-run the full diagnostic by calling + * the runTests.php script (first line needs to be commented first). + * @package Pydio + * @subpackage Core + */ +abstract class AbstractTest +{ + /** The test name */ + public $name; + /** The test information when failed */ + public $failedInfo; + /** The test results output (used for report) */ + public $resultOutput; + /** Tested params - When used as a diagnostic tool, can store variables used by the test*/ + public $testedParas; + /** The test level when failed (warning, info or error, default to error) */ + public $failedLevel; + /** The test parameters */ + public $params; + + /** + * AbstractTest constructor. + * @param $name + * @param $failedInfo + * @param null $params + */ + public function __construct($name, $failedInfo, $params = NULL) + { + $this->name = $name; + $this->failedInfo = $failedInfo; + $this->params = $params; + $this->failedLevel = "error"; + $this->testedParams = array(); + global $MAIN_testsArray; + $MAIN_testsArray[] = $this; + } + + /** + * Perform the test, should be overwritten in concrete classes + * @abstract + * @return Boolean + */ + public function doTest() { return FALSE; } + + /** + * Perform the test on a given repository object, should be overwritten in concrete classes + * @param Repository|RepositoryInterface $repository + * @return Boolean + */ + public function doRepositoryTest($repository) { return FALSE; } + + /** + * Utilitary to convert php config to numerical values. + * + * @param String $val + * @return Integer + */ + public function returnBytes($val) + { + $val = trim($val); + $last = strtolower($val[strlen($val)-1]); + switch ($last) { + // Le modifieur 'G' est disponible depuis PHP 5.1.0 + case 'g': + $val *= 1024 * 1024 * 1024; + break; + case 'm': + $val *= 1024 * 1024; + break; + case 'k': + $val *= 1024; + break; + default: + break; + } + + return $val; + } +}; diff --git a/core/src/core/src/pydio/Tests/Client.php b/core/src/core/src/pydio/Tests/Client.php new file mode 100644 index 0000000000..0946a58e37 --- /dev/null +++ b/core/src/core/src/pydio/Tests/Client.php @@ -0,0 +1,46 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Class Client + * Display Client Browser User Agent + * @package Pydio\Tests + */ +class Client extends AbstractTest +{ + /** + * @inheritdoc + */ + public function __construct() { parent::__construct("Client Browser", "Current client ".$_SERVER['HTTP_USER_AGENT']); } + + /** + * @inheritdoc + */ + public function doTest() + { + $this->testedParams["Client"] = $_SERVER['HTTP_USER_AGENT']; + $this->failedLevel = "info"; + return FALSE; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/PHPCLI.php b/core/src/core/src/pydio/Tests/PHPCLI.php new file mode 100644 index 0000000000..76adf2a7a7 --- /dev/null +++ b/core/src/core/src/pydio/Tests/PHPCLI.php @@ -0,0 +1,147 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +use Pydio\Core\Services\ConfService; +use Pydio\Core\Controller\UnixProcess; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Check current PHP Version + * @package AjaXplorer + * @subpackage Tests + */ +class PHPCLI extends AbstractTest +{ + /** + * PHPCLI constructor. + */ + public function __construct() { parent::__construct("PHP Command Line", "Testing PHP command line (default is php)"); } + + /** + * @return bool + */ + public function doTest() + { + if (!is_writable(AJXP_CACHE_DIR)) { + $this->testedParams["Command Line Available"] = "No"; + $this->failedLevel = "warning"; + $this->failedInfo = "Php command line not detected (cache directory not writeable), this is NOT BLOCKING, but enabling it could allow to send some long tasks in background. If you do not have the ability to tweak your server, you can safely ignore this warning."; + return FALSE; + } + $windows = (PHP_OS == "WIN32" || PHP_OS == "WINNT" || PHP_OS == "Windows"); + + $sModeExecDir = ini_get("safe_mode_exec_dir") ; + $safeEnabled = ini_get("safe_mode") || !empty($sModeExecDir); + + $disabled_functions=explode(',',ini_get('disable_functions')); + $fName = ($windows ? "popen" : "exec"); + $notFoundFunction = in_array($fName, $disabled_functions) || !function_exists($fName) || !is_callable($fName); + + $comEnabled = class_exists("COM"); + $useCOM = false; + + if ( ( $safeEnabled || $notFoundFunction )) { + if ($comEnabled) { + $useCOM = true; + } else { + $this->testedParams["Command Line Available"] = "No"; + $this->failedLevel = "warning"; + $this->failedInfo = "Php command line not detected (there seem to be some safe_mode or a-like restriction), this is NOT BLOCKING, but enabling it could allow to send some long tasks in background. If you do not have the ability to tweak your server, you can safely ignore this warning."; + return FALSE; + } + } + + $defaultCli = ConfService::getGlobalConf("CLI_PHP"); + if($defaultCli == null) $defaultCli = "php"; + + $token = md5(time()); + $robustCacheDir = str_replace("/", DIRECTORY_SEPARATOR, AJXP_CACHE_DIR); + $logDir = $robustCacheDir.DIRECTORY_SEPARATOR."cmd_outputs"; + if(!is_dir($logDir)) mkdir($logDir, 0755); + $logFile = $logDir."/".$token.".out"; + + $testScript = AJXP_CACHE_DIR."/cli_test.php"; + file_put_contents($testScript, ""); + + $cmd = $defaultCli." ". $robustCacheDir .DIRECTORY_SEPARATOR."cli_test.php"; + + if ($windows) { + /* Next 2 lines modified by rmeske: Need to wrap the folder and file paths in double quotes. */ + $cmd = $defaultCli." ". chr(34).$robustCacheDir .DIRECTORY_SEPARATOR."cli_test.php".chr(34); + $cmd .= " > ".chr(34).$logFile.chr(34); + + $comCommand = $cmd; + if ($useCOM) { + $WshShell = new \COM("WScript.Shell"); + $res = $WshShell->Run("cmd /C $comCommand", 0, false); + } else { + $tmpBat = implode(DIRECTORY_SEPARATOR, array(str_replace("/", DIRECTORY_SEPARATOR, AJXP_INSTALL_PATH), "data","tmp", md5(time()).".bat")); + $cmd .= "\n DEL ".chr(34).$tmpBat.chr(34); + file_put_contents($tmpBat, $cmd); + /* Following 1 line modified by rmeske: The windows Start command identifies the first parameter in quotes as a title for the window. Therefore, when enclosing a command with double quotes you must include a window title first + START ["title"] [/Dpath] [/I] [/MIN] [/MAX] [/SEPARATE | /SHARED] [/LOW | /NORMAL | /HIGH | /REALTIME] [/WAIT] [/B] [command / program] [parameters] + */ + @pclose(@popen('start /b "CLI" "'.$tmpBat.'"', 'r')); + sleep(1); + // Failed, but we can try with COM + if ( ! is_file(AJXP_CACHE_DIR."/cli_result.php") && $comEnabled ) { + $useCOM = true; + $WshShell = new \COM("WScript.Shell"); + $res = $WshShell->Run("cmd /C $comCommand", 0, false); + } + } + } else { + new UnixProcess($cmd, $logFile); + } + + sleep(1); + $availability = true; + if (is_file(AJXP_CACHE_DIR."/cli_result.php")) { + $this->testedParams["Command Line Available"] = "Yes"; + unlink(AJXP_CACHE_DIR."/cli_result.php"); + if ($useCOM) { + $this->failedLevel = "warning"; + $availability = true; + $this->failedInfo = "Php command line detected, but using the windows COM extension. Just make sure to enable COM in the Pydio Core Options"; + } else { + $this->failedInfo = "Php command line detected, this will allow to send some tasks in background. Enable it in the Pydio Core Options"; + } + } else { + $log = ""; + if (is_file($logFile)) { + $log = file_get_contents($logFile); + unlink($logFile); + } + $this->testedParams["Command Line Available"] = "No : $log"; + $this->failedLevel = "warning"; + $this->failedInfo = "Php command line not detected, this is NOT BLOCKING, but enabling it could allow to send some long tasks in background. If you do not have the ability to tweak your server, you can safely ignore this warning."; + if ($windows) { + $this->failedInfo .= "
    On Windows, try to activate the php COM extension, and set correct rights to the cmd exectuble to make it runnable by the web server, this should solve the problem."; + } + $availability = false; + } + unlink(AJXP_CACHE_DIR."/cli_test.php"); + + return $availability; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/PHPDomXML.php b/core/src/core/src/pydio/Tests/PHPDomXML.php new file mode 100644 index 0000000000..568791f31f --- /dev/null +++ b/core/src/core/src/pydio/Tests/PHPDomXML.php @@ -0,0 +1,50 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Check that DOMXml is enabled + * @package Pydio\Tests + */ +class PHPDomXML extends AbstractTest +{ + + /** + * @inheritdoc + */ + public function __construct() { parent::__construct("DOM Xml enabled", "Dom XML is required, you may have to install the php-xml extension."); } + + /** + * @inheritdoc + */ + public function doTest() + { + $this->failedLevel = "error"; + if (!class_exists("DOMDocument")) { + $this->testedParams["DOM Enabled"] = "No"; + return FALSE; + } + $this->testedParams["DOM Enabled"] = "Yes"; + return TRUE; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/PHPErrorLevel.php b/core/src/core/src/pydio/Tests/PHPErrorLevel.php new file mode 100644 index 0000000000..1ed8cf0bb8 --- /dev/null +++ b/core/src/core/src/pydio/Tests/PHPErrorLevel.php @@ -0,0 +1,73 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Check current php error level + * @package Pydio\Tests + */ +class PHPErrorLevel extends AbstractTest +{ + + /** + * @inheritdoc + */ + public function __construct() { parent::__construct("PHP error level", PHPErrorLevel::error2string(error_reporting())); } + + /** + * @inheritdoc + */ + public function doTest() + { + if (error_reporting() & E_NOTICE) { + $this->failedLevel = "error"; + $this->failedInfo = "You must lower your PHP error level in php.ini NOT TO INCLUDE E_NOTICE (you have:".$this->failedInfo.")"; + return false; + } + $this->failedLevel = "info"; + return FALSE; + } + + /** + * @inheritdoc + */ + public function error2string($value) + { + $level_names = array( + E_ERROR => 'E_ERROR', E_WARNING => 'E_WARNING', + E_PARSE => 'E_PARSE', E_NOTICE => 'E_NOTICE', + E_CORE_ERROR => 'E_CORE_ERROR', E_CORE_WARNING => 'E_CORE_WARNING', + E_COMPILE_ERROR => 'E_COMPILE_ERROR', E_COMPILE_WARNING => 'E_COMPILE_WARNING', + E_USER_ERROR => 'E_USER_ERROR', E_USER_WARNING => 'E_USER_WARNING', + E_USER_NOTICE => 'E_USER_NOTICE' ); + if(defined('E_STRICT')) $level_names[E_STRICT]='E_STRICT'; + $levels=array(); + if (($value&E_ALL)==E_ALL) { + $levels[]='E_ALL'; + $value&=~E_ALL; + } + foreach($level_names as $level=>$name) + if(($value&$level)==$level) $levels[]=$name; + return implode(' | ',$levels); + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/PHPExif.php b/core/src/core/src/pydio/Tests/PHPExif.php new file mode 100644 index 0000000000..52de193452 --- /dev/null +++ b/core/src/core/src/pydio/Tests/PHPExif.php @@ -0,0 +1,52 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Check that Exif extension is enabled + * @package Pydio\Tests + */ +class PHPExif extends AbstractTest +{ + + /** + * @inheritdoc + */ + public function __construct() { + parent::__construct("Exif Extension enabled", "Installing php-exif extension is recommended if you plan to handle images"); + } + + /** + * @inheritdoc + */ + public function doTest() + { + $this->failedLevel = "warning"; + if (!function_exists("exif_read_data")) { + $this->testedParams["Exif Enabled"] = "No"; + return FALSE; + } + $this->testedParams["Exif Enabled"] = "Yes"; + return TRUE; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/PHPGDVersion.php b/core/src/core/src/pydio/Tests/PHPGDVersion.php new file mode 100644 index 0000000000..b2a35fff13 --- /dev/null +++ b/core/src/core/src/pydio/Tests/PHPGDVersion.php @@ -0,0 +1,50 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Check whether GD is installed or not + * @package Pydio\Tests + */ +class PHPGDVersion extends AbstractTest +{ + + /** + * @inheritdoc + */ + public function __construct() { parent::__construct("PHP GD version", "GD is required for generating thumbnails"); } + + /** + * @inheritdoc + */ + public function doTest() + { + $this->failedLevel = "warning"; + if (!function_exists("gd_info") || !function_exists("imagecopyresized") || !function_exists("imagecopyresampled")) { + $this->testedParams["GD Enabled"] = "No"; + return FALSE; + } + $this->testedParams["GD Enabled"] = "Yes"; + return TRUE; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/PHPLimits.php b/core/src/core/src/pydio/Tests/PHPLimits.php new file mode 100644 index 0000000000..2fa1dc53d1 --- /dev/null +++ b/core/src/core/src/pydio/Tests/PHPLimits.php @@ -0,0 +1,54 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Gather various php configurations that will limit the application + * @package Pydio\Tests + */ +class PHPLimits extends AbstractTest +{ + + /** + * @inheritdoc + */ + public function __construct() { parent::__construct("PHP Limits variables", "Testing configs"); } + + /** + * @inheritdoc + */ + public function doTest() + { + $this->testedParams["Upload Max Size"] = ini_get("upload_max_filesize"); + $this->testedParams["Memory Limit"] = ((ini_get("memory_limit")!="")?ini_get("memory_limit"):get_cfg_var("memory_limit")); + $this->testedParams["Max execution time"] = ini_get("max_execution_time"); + $this->testedParams["Safe Mode"] = (ini_get("safe_mode")?"1":"0"); + $this->testedParams["Safe Mode GID"] = (ini_get("safe_mode_gid")?"1":"0"); + $this->testedParams["Xml parser enabled"] = (function_exists("xml_parser_create")?"1":"0"); + foreach ($this->testedParams as $paramName => $paramValue) { + $this->failedInfo .= "\n$paramName=$paramValue"; + } + $this->failedLevel = "info"; + return FALSE; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/PHPMCrypt.php b/core/src/core/src/pydio/Tests/PHPMCrypt.php new file mode 100644 index 0000000000..b6d7eb5a4c --- /dev/null +++ b/core/src/core/src/pydio/Tests/PHPMCrypt.php @@ -0,0 +1,50 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Check whether mcrypt is enabled + * @package Pydio\Tests + */ +class PHPMCrypt extends AbstractTest +{ + + /** + * @inheritdoc + */ + public function __construct() { parent::__construct("MCrypt enabled", "MCrypt is required by all security functions."); } + + /** + * @inheritdoc + */ + public function doTest() + { + $this->failedLevel = "error"; + if (!function_exists("mcrypt_create_iv")) { + $this->testedParams["MCrypt Enabled"] = "No"; + return FALSE; + } + $this->testedParams["MCrypt Enabled"] = "Yes"; + return TRUE; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/PHPOS.php b/core/src/core/src/pydio/Tests/PHPOS.php new file mode 100644 index 0000000000..5c43bb083a --- /dev/null +++ b/core/src/core/src/pydio/Tests/PHPOS.php @@ -0,0 +1,46 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Check server OS + * @package Pydio\Tests + */ +class PHPOS extends AbstractTest +{ + + /** + * @inheritdoc + */ + public function __construct() { parent::__construct("PHP operating system", "Current operating system ".PHP_OS); } + + /** + * @inheritdoc + */ + public function doTest() + { + $this->testedParams["Server OS"] = PHP_OS; + $this->failedLevel = "info"; + return FALSE; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/PHPSession.php b/core/src/core/src/pydio/Tests/PHPSession.php new file mode 100644 index 0000000000..01e5912500 --- /dev/null +++ b/core/src/core/src/pydio/Tests/PHPSession.php @@ -0,0 +1,65 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Check php session writeability + * @package Pydio\Tests + */ +class PHPSession extends AbstractTest +{ + + /** + * @inheritdoc + */ + public function __construct() { parent::__construct("PHP Session", "Testing configs"); } + + /** + * @inheritdoc + */ + public function doTest() + { + $handler = ini_get("session.save_handler"); + if ($handler != "files") { + $this->testedParams["Session Save Path"] = "Handler is not file based"; + return TRUE; + } + $tmpDir = session_save_path(); + $this->testedParams["Session Save Path"] = $tmpDir; + if ($tmpDir != "") { + $this->testedParams["Session Save Path Writeable"] = @is_writable($tmpDir); + if (!$this->testedParams["Session Save Path Writeable"]) { + $this->failedLevel = "error"; + $this->failedInfo = "The temporary folder used by PHP to save the session data is either incorrect or not writeable! Please check : ".session_save_path(); + $this->failedInfo .= "

    Suggestion : create your own temporary folder for sessions and set the session.save_path parameter in the conf/bootstrap_conf.php

    "; + return FALSE; + } + } else { + $this->failedLevel = "warning"; + $this->failedInfo = "Warning, it seems that your temporary folder used to save session data is not set. If you are encountering troubles with logging and sessions, please check session.save_path in your php.ini. Otherwise you can ignore this."; + return FALSE; + } + $this->failedLevel = "info"; + return FALSE; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/PHPVersion.php b/core/src/core/src/pydio/Tests/PHPVersion.php new file mode 100644 index 0000000000..97d3f95758 --- /dev/null +++ b/core/src/core/src/pydio/Tests/PHPVersion.php @@ -0,0 +1,51 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Check current PHP Version + * @package Pydio\Tests + */ +class PHPVersion extends AbstractTest +{ + + /** + * @inheritdoc + */ + public function __construct() { parent::__construct("PHP version", "Minimum required version is PHP 5.5.9"); } + + /** + * @inheritdoc + */ + public function doTest() + { + $version = phpversion(); + $this->testedParams["PHP Version"] = $version; + if (version_compare($version, '5.5.9') < 0 ) return FALSE; + $locale = setlocale(LC_CTYPE, 0); + $dirSep = DIRECTORY_SEPARATOR; + $this->testedParams["Locale"] = $locale; + $this->testedParams["Directory Separator"] = $dirSep; + return TRUE; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/PHP_APC.php b/core/src/core/src/pydio/Tests/PHP_APC.php new file mode 100644 index 0000000000..5798410a36 --- /dev/null +++ b/core/src/core/src/pydio/Tests/PHP_APC.php @@ -0,0 +1,57 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Check that DOMXml is enabled + * @package Pydio\Tests + */ +class PHP_APC extends AbstractTest +{ + /** + * @inheritdoc + */ + public function __construct() { parent::__construct("PHP Opcode Cache extension", "Pydio framework loads a lot of PHP files at each query, and using a PHP accelerator is greatly recommanded."); } + + /** + * @inheritdoc + */ + public function doTest() + { + $this->failedLevel = "warning"; + $v = @extension_loaded('apc'); + if (isSet($v) && (is_numeric($v) || strtolower($v) == "on")) { + $this->testedParams["PHP APC extension loaded"] = "No"; + $v = false; + } else if (!isSet($v)) { + $v = false; + } + if(!$v && !@extension_loaded("Zend OPcache")){ + $this->failedInfo = "Pydio framework loads a lot of PHP files at each query, and using a PHP accelerator is greatly recommanded."; + return FALSE; + } + $this->failedInfo = "PHP accelerator extension detected, this is good for performances"; + $this->testedParams["PHP Opcode Cache extension loaded"] = "Yes"; + return TRUE; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/PHP_INTL.php b/core/src/core/src/pydio/Tests/PHP_INTL.php new file mode 100644 index 0000000000..da0d4e83c7 --- /dev/null +++ b/core/src/core/src/pydio/Tests/PHP_INTL.php @@ -0,0 +1,55 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Check that intl is enabled + * @package AjaXplorer + * @subpackage Tests + */ +class PHP_INTL extends AbstractTest +{ + /** + * PHP_INTL constructor. + * @inheritdoc + */ + public function __construct() { parent::__construct("PHP INTL extension", "Pydio used intl to localize month names."); } + + /** + * @inheritdoc + */ + public function doTest() + { + $this->failedLevel = "warning"; + + if (extension_loaded('intl')) { + $this->failedInfo = "PHP INTL extension detected. Month names can be localized depending on the users language."; + $this->testedParams["PHP INTL extension loaded"] = "Yes"; + return TRUE; + } else { + $this->failedInfo = "PHP INTL extension missing. English month names will be used for all languages."; + $this->testedParams["PHP INTL extension loaded"] = "No"; + return FALSE; + } + } +} diff --git a/core/src/core/src/pydio/Tests/PHP_OB.php b/core/src/core/src/pydio/Tests/PHP_OB.php new file mode 100644 index 0000000000..799e522cef --- /dev/null +++ b/core/src/core/src/pydio/Tests/PHP_OB.php @@ -0,0 +1,55 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Check that output buffering is enabled + * @package Pydio\Tests + */ +class PHP_OB extends AbstractTest +{ + + /** + * @inheritdoc + */ + public function __construct() { parent::__construct("PHP Output Buffer disabled", "You should disable php output_buffering parameter for better performances with Pydio."); } + + /** + * @inheritdoc + */ + public function doTest() + { + $this->failedLevel = "warning"; + $v = @ini_get("output_buffering"); + if (isSet($v) && (is_numeric($v) || strtolower($v) == "on")) { + $this->testedParams["PHP Output Buffer disabled"] = "No"; + return FALSE; + } else if (!isSet($v)) { + $this->failedInfo = "Unable to detect the output_buffering value, please make sure that it is disabled (Off) in your php.ini or your virtual host."; + return FALSE; + } + $this->failedInfo = "PHP Output Buffering is disabled, this is good for better performances"; + $this->testedParams["PHP Output Buffer disabled"] = "Yes"; + return TRUE; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/PHP_file_uploads.php b/core/src/core/src/pydio/Tests/PHP_file_uploads.php new file mode 100644 index 0000000000..7771fddd80 --- /dev/null +++ b/core/src/core/src/pydio/Tests/PHP_file_uploads.php @@ -0,0 +1,57 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Check that file uploads is enabled + * @package Pydio\Tests + */ +class PHP_file_uploads extends AbstractTest +{ + protected static $testKey = 'PHP File Uploads enabled'; + + /** + * @inheritdoc + */ + public function __construct() { parent::__construct(self::$testKey, "You should enable php file_uploads parameter for uploading files with Pydio."); } + + /** + * @inheritdoc + */ + public function doTest() + { + $this->failedLevel = "warning"; + $v = @ini_get("file_uploads"); + if (isSet($v) && (is_numeric($v) || strtolower($v) == "on")) { + $this->failedInfo = "PHP File Uploads is enabled"; + $this->testedParams[self::$testKey] = "Yes"; + return TRUE; + } else if (!isSet($v)) { + $this->failedInfo = "Unable to detect the file_uploads value, please make sure that it is disabled (Off) in your php.ini or your virtual host."; + return FALSE; + } + $this->failedInfo = "PHP File Uploads is disabled"; + $this->testedParams[self::$testKey] = "Yes"; + return false; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/PHP_magic_quotes.php b/core/src/core/src/pydio/Tests/PHP_magic_quotes.php new file mode 100644 index 0000000000..04bb35b947 --- /dev/null +++ b/core/src/core/src/pydio/Tests/PHP_magic_quotes.php @@ -0,0 +1,50 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Check whether mcrypt is enabled + * @package Pydio\Tests + */ +class PHP_magic_quotes extends AbstractTest +{ + + /** + * @inheritdoc + */ + public function __construct() { parent::__construct("Magic quotes disabled", "Magic quotes need to be disabled, only relevent for php 5.3"); } + + /** + * @inheritdoc + */ + public function doTest() + { + $this->failedLevel = "error"; + if (get_magic_quotes_gpc()) { + $this->testedParams["Magic quotes disabled"] = "No"; + return FALSE; + } + $this->testedParams["Magic quotes disabled"] = "Yes"; + return TRUE; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/PydioVersion.php b/core/src/core/src/pydio/Tests/PydioVersion.php new file mode 100644 index 0000000000..e82ce3bc4c --- /dev/null +++ b/core/src/core/src/pydio/Tests/PydioVersion.php @@ -0,0 +1,45 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Class AJXPVersion + * Get the current ajaxplorer version + * @package Pydio\Tests + */ +class PydioVersion extends AbstractTest +{ + /** + * @inheritdoc + */ + public function __construct() { parent::__construct("Pydio version", "Version : ".AJXP_VERSION); } + + /** + * @inheritdoc + */ + public function doTest() + { + $this->failedLevel = "info"; + return FALSE; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/SSLEncryption.php b/core/src/core/src/pydio/Tests/SSLEncryption.php new file mode 100644 index 0000000000..479854fd06 --- /dev/null +++ b/core/src/core/src/pydio/Tests/SSLEncryption.php @@ -0,0 +1,57 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Detect HTTPS protocol + * @package Pydio + * @subpackage Tests + */ +class SSLEncryption extends AbstractTest +{ + + /** + * @inheritdoc + */ + public function __construct() { parent::__construct("SSL Encryption", "You are not using SSL encryption, or it was not detected by the server. Be aware that it is strongly recommended to secure all communication of data over the network."); } + + /** + * @inheritdoc + */ + public function doTest() + { + // Get the locale + $ssl = false; + if (isSet($_SERVER["HTTPS"]) && strtolower($_SERVER["HTTPS"]) == "on") { + $ssl = true; + } + if (!$ssl) { + $this->failedLevel = "warning"; + $this->failedInfo .= "

    Suggestion : if your server supports HTTPS, make sure to configure the automatic redirection from http to https.

    "; + return FALSE; + } else { + $this->failedInfo .= "Https protocol detected"; + return TRUE; + } + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/ServerEncoding.php b/core/src/core/src/pydio/Tests/ServerEncoding.php new file mode 100644 index 0000000000..82dc102fe9 --- /dev/null +++ b/core/src/core/src/pydio/Tests/ServerEncoding.php @@ -0,0 +1,69 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Test Server Encoding + * @package Pydio\Tests + */ +class ServerEncoding extends AbstractTest +{ + + /** + * @inheritdoc + */ + public function __construct() { parent::__construct("Server charset encoding", "You must set a correct charset encoding + in your locale definition in the form: en_us.UTF-8. Please refer to setlocale man page. + If your detected locale is C, simply type echo \$LANG on your server command line to read the correct value."); } + + /** + * @inheritdoc + */ + public function doTest() + { + // Get the locale + $locale = setlocale(LC_CTYPE, 0); + if ($locale == 'C') { + $this->failedLevel = "warning"; + $this->failedInfo .= "Detected locale: $locale (using UTF-8)"; + $this->failedInfo .= "

    Suggestion : Set the AJXP_LOCALE parameter to the correct value in the conf/bootstrap_conf.php file. You can also set this value using the installer form (next step).

    "; + return FALSE; + } + if (strpos($locale, '.') === FALSE) { + $this->failedLevel = "warning"; + $this->failedInfo .= "Locale doesn't contain encoding: $locale (so using UTF-8)"; + $this->failedInfo .= "

    Suggestion : Set the AJXP_LOCALE parameter to the correct value in the conf/bootstrap_conf.php file. You can also set this value using the installer form (next step).

    "; + return FALSE; + } + // Check if we have iconv + if (!function_exists("iconv") && floatval(phpversion()) > 5.0) { $this->failedInfo .= "Couldn't find iconv. Please use a PHP version with iconv support"; return FALSE; } + if (floatval(phpversion()) > 5.0) { + // Try converting from a known UTF-8 string to ISO8859-1 string and back to make sure it works. + $string = "aéàç"; + $iso = iconv("UTF-8", "ISO-8859-1", $string); + $back = iconv("ISO-8859-1", "UTF-8", $iso); + if (strlen($iso) != 4 || ord($iso[1]) != 233 || $back != $string) { $this->failedInfo .= "iconv doesn't work on your system: $string $iso $back"; return FALSE; } + } + return TRUE; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/Upload.php b/core/src/core/src/pydio/Tests/Upload.php new file mode 100644 index 0000000000..46a5ed5c10 --- /dev/null +++ b/core/src/core/src/pydio/Tests/Upload.php @@ -0,0 +1,68 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +use Pydio\Core\Services\ConfService; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Test upload tmp dir + * @package Pydio + * @subpackage Tests + */ +class Upload extends AbstractTest +{ + /** + * Upload constructor. + */ + public function __construct() { parent::__construct("Upload particularities", "Testing configs"); } + + /** + * @return bool + */ + public function doTest() + { + $tmpDir = ini_get("upload_tmp_dir"); + if (!$tmpDir) $tmpDir = realpath(sys_get_temp_dir()); + if (ConfService::getGlobalConf("AJXP_TMP_DIR") != "") { + $tmpDir = ConfService::getGlobalConf("AJXP_TMP_DIR"); + } + if (defined("AJXP_TMP_DIR") && AJXP_TMP_DIR !="") { + $tmpDir = AJXP_TMP_DIR; + } + $this->testedParams["Upload Tmp Dir Writeable"] = @is_writable($tmpDir); + $this->testedParams["PHP Upload Max Size"] = $this->returnBytes(ini_get("upload_max_filesize")); + $this->testedParams["PHP Post Max Size"] = $this->returnBytes(ini_get("post_max_size")); + foreach ($this->testedParams as $paramName => $paramValue) { + $this->failedInfo .= "\n$paramName=$paramValue"; + } + if (!$this->testedParams["Upload Tmp Dir Writeable"]) { + $this->failedLevel = "error"; + $this->failedInfo = "The temporary folder used by PHP to upload files is either incorrect or not writeable! Upload will not work. Please check : ".ini_get("upload_tmp_dir"); + $this->failedInfo .= "

    Suggestion : Set the AJXP_TMP_DIR parameter in the conf/bootstrap_conf.php file

    "; + return FALSE; + } + + $this->failedLevel = "info"; + return FALSE; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/UsersConfig.php b/core/src/core/src/pydio/Tests/UsersConfig.php new file mode 100644 index 0000000000..7c742e2557 --- /dev/null +++ b/core/src/core/src/pydio/Tests/UsersConfig.php @@ -0,0 +1,52 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + + +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\UsersService; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Class UsersConfig + * @package Pydio\Tests + */ +class UsersConfig extends AbstractTest +{ + /** + * UsersConfig constructor. + */ + public function __construct() { parent::__construct("Users Configuration", "Current config for users"); } + + /** + * Perform the test, should be overwritten in concrete classes + * @abstract + * @return Boolean + */ + public function doTest() + { + $this->testedParams["Users enabled"] = UsersService::usersEnabled(); + $this->testedParams["Guest enabled"] = ConfService::getGlobalConf("ALLOW_GUEST_BROWSING", "auth"); + $this->failedLevel = "info"; + return FALSE; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/Writeability.php b/core/src/core/src/pydio/Tests/Writeability.php new file mode 100644 index 0000000000..e4def5adcc --- /dev/null +++ b/core/src/core/src/pydio/Tests/Writeability.php @@ -0,0 +1,70 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +use Pydio\Core\Model\Context; +use Pydio\Core\Utils\Vars\VarsFilter; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Check the various plugins folders writeability + * @package Pydio + * @subpackage Tests + */ +class Writeability extends AbstractTest +{ + + /** + * @inheritdoc + */ + public function __construct() { parent::__construct("Required writeable folder", "One of the following folder should be writeable and is not : "); } + + /** + * @inheritdoc + */ + public function doTest() + { + $checks = array(); + $checks[] = AJXP_CACHE_DIR; + $checks[] = AJXP_DATA_PATH; + $checked = array(); + $success = true; + foreach ($checks as $check) { + $w = false; + $check = VarsFilter::filter($check, Context::emptyContext()); + if (!is_dir($check)) {// Check parent + $check = dirname($check); + } + $w = is_writable($check); + $checked[basename($check)] = "".basename($check).":".($w?'true':'false'); + $success = $success & $w; + } + $this->testedParams["Writeable Folders"] = "[".implode(',
    ', array_values($checked))."]"; + if (!$success) { + $this->failedInfo .= implode(",", $checks); + return FALSE; + } + $this->failedLevel = "info"; + $this->failedInfo = "[".implode(',
    ', array_values($checked))."]"; + return FALSE; + } +} \ No newline at end of file diff --git a/core/src/core/src/pydio/Tests/Zlib.php b/core/src/core/src/pydio/Tests/Zlib.php new file mode 100644 index 0000000000..c5cbbc8165 --- /dev/null +++ b/core/src/core/src/pydio/Tests/Zlib.php @@ -0,0 +1,47 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Tests; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Check that Zlib is enabled + * @package Pydio + * @subpackage Tests + */ +class Zlib extends AbstractTest +{ + + /** + * @inheritdoc + */ + public function __construct() { parent::__construct("Zlib extension (ZIP)", "Extension enabled : ".((function_exists('gzopen')||function_exists('gzopen64'))?"1":"0")); } + + /** + * @inheritdoc + */ + public function doTest() + { + $this->testedParams["Zlib Enabled"] = ((function_exists('gzopen')||function_exists('gzopen64'))?"Yes":"No"); + $this->failedLevel = "info"; + return false; + } +} \ No newline at end of file diff --git a/core/src/core/tests/api_test.phtml b/core/src/core/src/pydio/Tests/api_test.phtml similarity index 100% rename from core/src/core/tests/api_test.phtml rename to core/src/core/src/pydio/Tests/api_test.phtml diff --git a/core/src/core/tests/startup.phtml b/core/src/core/src/pydio/Tests/startup.phtml similarity index 96% rename from core/src/core/tests/startup.phtml rename to core/src/core/src/pydio/Tests/startup.phtml index 92af0aefef..cfe6e437c9 100644 --- a/core/src/core/tests/startup.phtml +++ b/core/src/core/src/pydio/Tests/startup.phtml @@ -1,201 +1,201 @@ - - - - Pydio - - - - - - - - - -

    Pydio Diagnostic Tool

    - -
    - Status : You have some warnings, but no fatal error detected, Pydio should run ok.
    Please review the warnings, and click here to continue to Pydio.

    "; - } else if(count($ALL_ROWS["error"])) { - echo "

    Status : You have some errors that may prevent Pydio from running. Please review the error lines to check what action you should do. If you want to skip this step, simply click here to continue to Pydio.

    "; - }else{ - echo "

    Status : No specific errors were detected, your Pydio installation should run smoothly!
    Click there to continue to Pydio

    "; - } - } - ?> - -
    - $allTests){ - - foreach($allTests as $testKey => $testInfo){ - - $status = ($testsType == "passed"?"ok":$testsType); - echo "
    -
    ".($testsType != "passed"?"-":"+")." $testKey
    -
    $status
    -
    $testInfo
    -
    -
    "; - - } - - } - - ?> -
    - - -
    - - - + + + + Pydio + + + + + + + + + +

    Pydio Diagnostic Tool

    + +
    + Status : You have some warnings, but no fatal error detected, Pydio should run ok.
    Please review the warnings, and click here to continue to Pydio.

    "; + } else if(count($ALL_ROWS["error"])) { + echo "

    Status : You have some errors that may prevent Pydio from running. Please review the error lines to check what action you should do. If you want to skip this step, simply click here to continue to Pydio.

    "; + }else{ + echo "

    Status : No specific errors were detected, your Pydio installation should run smoothly!
    Click there to continue to Pydio

    "; + } + } + ?> + +
    + $allTests){ + + foreach($allTests as $testKey => $testInfo){ + + $status = ($testsType == "passed"?"ok":$testsType); + echo "
    +
    ".($testsType != "passed"?"-":"+")." $testKey
    +
    $status
    +
    $testInfo
    +
    +
    "; + + } + + } + + ?> +
    + + +
    + + + \ No newline at end of file diff --git a/core/src/core/tests/index.html b/core/src/core/tests/index.html deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/core/src/core/tests/test.AJXPVersion.php b/core/src/core/tests/test.AJXPVersion.php deleted file mode 100644 index c01376d799..0000000000 --- a/core/src/core/tests/test.AJXPVersion.php +++ /dev/null @@ -1,38 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * @package AjaXplorer - * @subpackage Tests - * @class AJXPVersion - * Get the current ajaxplorer version - */ -class AJXPVersion extends AbstractTest -{ - public function __construct() { parent::__construct("Pydio version", "Version : ".AJXP_VERSION); } - public function doTest() - { - $this->failedLevel = "info"; - return FALSE; - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.Client.php b/core/src/core/tests/test.Client.php deleted file mode 100644 index c8c25c576d..0000000000 --- a/core/src/core/tests/test.Client.php +++ /dev/null @@ -1,39 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * @package Pydio - * @subpackage Tests - * @class Client - * Test client browser - */ -class Client extends AbstractTest -{ - public function __construct() { parent::__construct("Client Browser", "Current client ".$_SERVER['HTTP_USER_AGENT']); } - public function doTest() - { - $this->testedParams["Client"] = $_SERVER['HTTP_USER_AGENT']; - $this->failedLevel = "info"; - return FALSE; - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.PHPCLI.php b/core/src/core/tests/test.PHPCLI.php deleted file mode 100644 index f36276d7a0..0000000000 --- a/core/src/core/tests/test.PHPCLI.php +++ /dev/null @@ -1,135 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Check current PHP Version - * @package AjaXplorer - * @subpackage Tests - */ -class PHPCLI extends AbstractTest -{ - public function __construct() { parent::__construct("PHP Command Line", "Testing PHP command line (default is php)"); } - public function doTest() - { - if (!is_writable(AJXP_CACHE_DIR)) { - $this->testedParams["Command Line Available"] = "No"; - $this->failedLevel = "warning"; - $this->failedInfo = "Php command line not detected (cache directory not writeable), this is NOT BLOCKING, but enabling it could allow to send some long tasks in background. If you do not have the ability to tweak your server, you can safely ignore this warning."; - return FALSE; - } - $windows = (PHP_OS == "WIN32" || PHP_OS == "WINNT" || PHP_OS == "Windows"); - - $sModeExecDir = ini_get("safe_mode_exec_dir") ; - $safeEnabled = ini_get("safe_mode") || !empty($sModeExecDir); - - $disabled_functions=explode(',',ini_get('disable_functions')); - $fName = ($windows ? "popen" : "exec"); - $notFoundFunction = in_array($fName, $disabled_functions) || !function_exists($fName) || !is_callable($fName); - - $comEnabled = class_exists("COM"); - $useCOM = false; - - if ( ( $safeEnabled || $notFoundFunction )) { - if ($comEnabled) { - $useCOM = true; - } else { - $this->testedParams["Command Line Available"] = "No"; - $this->failedLevel = "warning"; - $this->failedInfo = "Php command line not detected (there seem to be some safe_mode or a-like restriction), this is NOT BLOCKING, but enabling it could allow to send some long tasks in background. If you do not have the ability to tweak your server, you can safely ignore this warning."; - return FALSE; - } - } - - $defaultCli = ConfService::getCoreConf("CLI_PHP"); - if($defaultCli == null) $defaultCli = "php"; - - $token = md5(time()); - $robustCacheDir = str_replace("/", DIRECTORY_SEPARATOR, AJXP_CACHE_DIR); - $logDir = $robustCacheDir.DIRECTORY_SEPARATOR."cmd_outputs"; - if(!is_dir($logDir)) mkdir($logDir, 0755); - $logFile = $logDir."/".$token.".out"; - - $testScript = AJXP_CACHE_DIR."/cli_test.php"; - file_put_contents($testScript, ""); - - $cmd = $defaultCli." ". $robustCacheDir .DIRECTORY_SEPARATOR."cli_test.php"; - - if ($windows) { - /* Next 2 lines modified by rmeske: Need to wrap the folder and file paths in double quotes. */ - $cmd = $defaultCli." ". chr(34).$robustCacheDir .DIRECTORY_SEPARATOR."cli_test.php".chr(34); - $cmd .= " > ".chr(34).$logFile.chr(34); - - $comCommand = $cmd; - if ($useCOM) { - $WshShell = new COM("WScript.Shell"); - $res = $WshShell->Run("cmd /C $comCommand", 0, false); - } else { - $tmpBat = implode(DIRECTORY_SEPARATOR, array(str_replace("/", DIRECTORY_SEPARATOR, AJXP_INSTALL_PATH), "data","tmp", md5(time()).".bat")); - $cmd .= "\n DEL ".chr(34).$tmpBat.chr(34); - file_put_contents($tmpBat, $cmd); - /* Following 1 line modified by rmeske: The windows Start command identifies the first parameter in quotes as a title for the window. Therefore, when enclosing a command with double quotes you must include a window title first - START ["title"] [/Dpath] [/I] [/MIN] [/MAX] [/SEPARATE | /SHARED] [/LOW | /NORMAL | /HIGH | /REALTIME] [/WAIT] [/B] [command / program] [parameters] - */ - @pclose(@popen('start /b "CLI" "'.$tmpBat.'"', 'r')); - sleep(1); - // Failed, but we can try with COM - if ( ! is_file(AJXP_CACHE_DIR."/cli_result.php") && $comEnabled ) { - $useCOM = true; - $WshShell = new COM("WScript.Shell"); - $res = $WshShell->Run("cmd /C $comCommand", 0, false); - } - } - } else { - new UnixProcess($cmd, $logFile); - } - - sleep(1); - $availability = true; - if (is_file(AJXP_CACHE_DIR."/cli_result.php")) { - $this->testedParams["Command Line Available"] = "Yes"; - unlink(AJXP_CACHE_DIR."/cli_result.php"); - if ($useCOM) { - $this->failedLevel = "warning"; - $availability = true; - $this->failedInfo = "Php command line detected, but using the windows COM extension. Just make sure to enable COM in the Pydio Core Options"; - } else { - $this->failedInfo = "Php command line detected, this will allow to send some tasks in background. Enable it in the Pydio Core Options"; - } - } else { - if (is_file($logFile)) { - $log = file_get_contents($logFile); - unlink($logFile); - } - $this->testedParams["Command Line Available"] = "No : $log"; - $this->failedLevel = "warning"; - $this->failedInfo = "Php command line not detected, this is NOT BLOCKING, but enabling it could allow to send some long tasks in background. If you do not have the ability to tweak your server, you can safely ignore this warning."; - if ($windows) { - $this->failedInfo .= "
    On Windows, try to activate the php COM extension, and set correct rights to the cmd exectuble to make it runnable by the web server, this should solve the problem."; - } - $availability = false; - } - unlink(AJXP_CACHE_DIR."/cli_test.php"); - - return $availability; - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.PHPDomXML.php b/core/src/core/tests/test.PHPDomXML.php deleted file mode 100644 index 2ca9e0fed1..0000000000 --- a/core/src/core/tests/test.PHPDomXML.php +++ /dev/null @@ -1,42 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Check that DOMXml is enabled - * @package Pydio - * @subpackage Tests - */ -class PHPDomXML extends AbstractTest -{ - public function __construct() { parent::__construct("DOM Xml enabled", "Dom XML is required, you may have to install the php-xml extension."); } - public function doTest() - { - $this->failedLevel = "error"; - if (!class_exists("DOMDocument")) { - $this->testedParams["DOM Enabled"] = "No"; - return FALSE; - } - $this->testedParams["DOM Enabled"] = "Yes"; - return TRUE; - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.PHPErrorLevel.php b/core/src/core/tests/test.PHPErrorLevel.php deleted file mode 100644 index 5367ff63e5..0000000000 --- a/core/src/core/tests/test.PHPErrorLevel.php +++ /dev/null @@ -1,62 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Check current php error level - * @package Pydio - * @subpackage Tests - */ -class PHPErrorLevel extends AbstractTest -{ - public function __construct() { parent::__construct("PHP error level", PHPErrorLevel::error2string(error_reporting())); } - public function doTest() - { - if (error_reporting() & E_NOTICE) { - $this->failedLevel = "error"; - $this->failedInfo = "You must lower your PHP error level in php.ini NOT TO INCLUDE E_NOTICE (you have:".$this->failedInfo.")"; - return false; - } - $this->failedLevel = "info"; - return FALSE; - } - - public function error2string($value) - { - $level_names = array( - E_ERROR => 'E_ERROR', E_WARNING => 'E_WARNING', - E_PARSE => 'E_PARSE', E_NOTICE => 'E_NOTICE', - E_CORE_ERROR => 'E_CORE_ERROR', E_CORE_WARNING => 'E_CORE_WARNING', - E_COMPILE_ERROR => 'E_COMPILE_ERROR', E_COMPILE_WARNING => 'E_COMPILE_WARNING', - E_USER_ERROR => 'E_USER_ERROR', E_USER_WARNING => 'E_USER_WARNING', - E_USER_NOTICE => 'E_USER_NOTICE' ); - if(defined('E_STRICT')) $level_names[E_STRICT]='E_STRICT'; - $levels=array(); - if (($value&E_ALL)==E_ALL) { - $levels[]='E_ALL'; - $value&=~E_ALL; - } - foreach($level_names as $level=>$name) - if(($value&$level)==$level) $levels[]=$name; - return implode(' | ',$levels); - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.PHPExif.php b/core/src/core/tests/test.PHPExif.php deleted file mode 100644 index bea215976c..0000000000 --- a/core/src/core/tests/test.PHPExif.php +++ /dev/null @@ -1,44 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Check that Exif extension is enabled - * @package Pydio - * @subpackage Tests - */ -class PHPExif extends AbstractTest -{ - public function __construct() { - parent::__construct("Exif Extension enabled", "Installing php-exif extension is recommended if you plan to handle images"); - } - public function doTest() - { - $this->failedLevel = "warning"; - if (!function_exists("exif_read_data")) { - $this->testedParams["Exif Enabled"] = "No"; - return FALSE; - } - $this->testedParams["Exif Enabled"] = "Yes"; - return TRUE; - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.PHPGDVersion.php b/core/src/core/tests/test.PHPGDVersion.php deleted file mode 100644 index d7db74e82a..0000000000 --- a/core/src/core/tests/test.PHPGDVersion.php +++ /dev/null @@ -1,42 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Check whether GD is installed or not - * @package Pydio - * @subpackage Tests - */ -class PHPGDVersion extends AbstractTest -{ - public function __construct() { parent::__construct("PHP GD version", "GD is required for generating thumbnails"); } - public function doTest() - { - $this->failedLevel = "warning"; - if (!function_exists("gd_info") || !function_exists("imagecopyresized") || !function_exists("imagecopyresampled")) { - $this->testedParams["GD Enabled"] = "No"; - return FALSE; - } - $this->testedParams["GD Enabled"] = "Yes"; - return TRUE; - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.PHPLimits.php b/core/src/core/tests/test.PHPLimits.php deleted file mode 100644 index 36674347e7..0000000000 --- a/core/src/core/tests/test.PHPLimits.php +++ /dev/null @@ -1,46 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Gather various php configurations that will limit the application - * @package Pydio - * @subpackage Tests - */ -class PHPLimits extends AbstractTest -{ - public function __construct() { parent::__construct("PHP Limits variables", "Testing configs"); } - public function doTest() - { - $this->testedParams["Upload Max Size"] = ini_get("upload_max_filesize"); - $this->testedParams["Memory Limit"] = ((ini_get("memory_limit")!="")?ini_get("memory_limit"):get_cfg_var("memory_limit")); - $this->testedParams["Max execution time"] = ini_get("max_execution_time"); - $this->testedParams["Safe Mode"] = (ini_get("safe_mode")?"1":"0"); - $this->testedParams["Safe Mode GID"] = (ini_get("safe_mode_gid")?"1":"0"); - $this->testedParams["Xml parser enabled"] = (function_exists("xml_parser_create")?"1":"0"); - foreach ($this->testedParams as $paramName => $paramValue) { - $this->failedInfo .= "\n$paramName=$paramValue"; - } - $this->failedLevel = "info"; - return FALSE; - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.PHPMCrypt.php b/core/src/core/tests/test.PHPMCrypt.php deleted file mode 100644 index 2dca6118f8..0000000000 --- a/core/src/core/tests/test.PHPMCrypt.php +++ /dev/null @@ -1,42 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Check whether mcrypt is enabled - * @package Pydio - * @subpackage Tests - */ -class PHPMCrypt extends AbstractTest -{ - public function __construct() { parent::__construct("MCrypt enabled", "MCrypt is required by all security functions."); } - public function doTest() - { - $this->failedLevel = "error"; - if (!function_exists("mcrypt_create_iv")) { - $this->testedParams["MCrypt Enabled"] = "No"; - return FALSE; - } - $this->testedParams["MCrypt Enabled"] = "Yes"; - return TRUE; - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.PHPOS.php b/core/src/core/tests/test.PHPOS.php deleted file mode 100644 index de08f0ec70..0000000000 --- a/core/src/core/tests/test.PHPOS.php +++ /dev/null @@ -1,38 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Check server OS - * @package Pydio - * @subpackage Tests - */ -class PHPOS extends AbstractTest -{ - public function __construct() { parent::__construct("PHP operating system", "Current operating system ".PHP_OS); } - public function doTest() - { - $this->testedParams["Server OS"] = PHP_OS; - $this->failedLevel = "info"; - return FALSE; - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.PHPSession.php b/core/src/core/tests/test.PHPSession.php deleted file mode 100644 index 5e665ca4a6..0000000000 --- a/core/src/core/tests/test.PHPSession.php +++ /dev/null @@ -1,57 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Check php session writeability - * @package Pydio - * @subpackage Tests - */ -class PHPSession extends AbstractTest -{ - public function __construct() { parent::__construct("PHP Session", "Testing configs"); } - public function doTest() - { - $handler = ini_get("session.save_handler"); - if ($handler != "files") { - $this->testedParams["Session Save Path"] = "Handler is not file based"; - return TRUE; - } - $tmpDir = session_save_path(); - $this->testedParams["Session Save Path"] = $tmpDir; - if ($tmpDir != "") { - $this->testedParams["Session Save Path Writeable"] = @is_writable($tmpDir); - if (!$this->testedParams["Session Save Path Writeable"]) { - $this->failedLevel = "error"; - $this->failedInfo = "The temporary folder used by PHP to save the session data is either incorrect or not writeable! Please check : ".session_save_path(); - $this->failedInfo .= "

    Suggestion : create your own temporary folder for sessions and set the session.save_path parameter in the conf/bootstrap_conf.php

    "; - return FALSE; - } - } else { - $this->failedLevel = "warning"; - $this->failedInfo = "Warning, it seems that your temporary folder used to save session data is not set. If you are encountering troubles with logging and sessions, please check session.save_path in your php.ini. Otherwise you can ignore this."; - return FALSE; - } - $this->failedLevel = "info"; - return FALSE; - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.PHPVersion.php b/core/src/core/tests/test.PHPVersion.php deleted file mode 100644 index 198c439a59..0000000000 --- a/core/src/core/tests/test.PHPVersion.php +++ /dev/null @@ -1,45 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Check current PHP Version - * @package Pydio - * @subpackage Tests - */ -class PHPVersion extends AbstractTest -{ - public function __construct() { parent::__construct("PHP version", "Minimum required version is PHP 5.4.0"); } - public function doTest() - { - $version = phpversion(); - $this->testedParams["PHP Version"] = $version; - //return false; - if (floatval($version) < 5.4) return FALSE; - $locale = setlocale(LC_CTYPE, 0); - $dirSep = DIRECTORY_SEPARATOR; - $this->testedParams["Locale"] = $locale; - $this->testedParams["Directory Separator"] = $dirSep; - if (floatval($version) < 5.4 && $locale != "C" && $dirSep != '\\') { $this->failedLevel = "warning"; return FALSE; } // PHP4 doesn't work well with foreign encoding - return TRUE; - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.PHP_APC.php b/core/src/core/tests/test.PHP_APC.php deleted file mode 100644 index 4533ea1447..0000000000 --- a/core/src/core/tests/test.PHP_APC.php +++ /dev/null @@ -1,46 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Check that DOMXml is enabled - * @package AjaXplorer - * @subpackage Tests - */ -class PHP_APC extends AbstractTest -{ - public function __construct() { parent::__construct("PHP APC extension", "Pydio framework loads a lot of PHP files at each query, and using a PHP accelerator is greatly recommanded."); } - public function doTest() - { - $this->failedLevel = "warning"; - - if (extension_loaded('apc')) { - $this->failedInfo = "PHP APC extension detected, this is good for better performances"; - $this->testedParams["PHP APC extension loaded"] = "Yes"; - return TRUE; - } else { - $this->testedParams["PHP APC extension loaded"] = "No"; - $this->failedInfo = "Pydio framework loads a lot of PHP files at each query, and using a PHP accelerator is greatly recommanded."; - return FALSE; - } - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.PHP_INTL.php b/core/src/core/tests/test.PHP_INTL.php deleted file mode 100644 index 6125b631cc..0000000000 --- a/core/src/core/tests/test.PHP_INTL.php +++ /dev/null @@ -1,46 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Check that intl is enabled - * @package AjaXplorer - * @subpackage Tests - */ -class PHP_INTL extends AbstractTest -{ - public function __construct() { parent::__construct("PHP INTL extension", "Pydio used intl to localize month names."); } - public function doTest() - { - $this->failedLevel = "warning"; - - if (extension_loaded('intl')) { - $this->failedInfo = "PHP INTL extension detected. Month names can be localized depending on the users language."; - $this->testedParams["PHP INTL extension loaded"] = "Yes"; - return TRUE; - } else { - $this->failedInfo = "PHP INTL extension missing. English month names will be used for all languages."; - $this->testedParams["PHP INTL extension loaded"] = "No"; - return FALSE; - } - } -} diff --git a/core/src/core/tests/test.PHP_OB.php b/core/src/core/tests/test.PHP_OB.php deleted file mode 100644 index 63afbfab7c..0000000000 --- a/core/src/core/tests/test.PHP_OB.php +++ /dev/null @@ -1,47 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Check that output buffering is enabled - * @package AjaXplorer - * @subpackage Tests - */ -class PHP_OB extends AbstractTest -{ - public function __construct() { parent::__construct("PHP Output Buffer disabled", "You should disable php output_buffering parameter for better performances with Pydio."); } - public function doTest() - { - $this->failedLevel = "warning"; - $v = @ini_get("output_buffering"); - if (isSet($v) && (is_numeric($v) || strtolower($v) == "on")) { - $this->testedParams["PHP Output Buffer disabled"] = "No"; - return FALSE; - } else if (!isSet($v)) { - $this->failedInfo = "Unable to detect the output_buffering value, please make sure that it is disabled (Off) in your php.ini or your virtual host."; - return FALSE; - } - $this->failedInfo = "PHP Output Buffering is disabled, this is good for better performances"; - $this->testedParams["PHP Output Buffer disabled"] = "Yes"; - return TRUE; - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.PHP_file_uploads.php b/core/src/core/tests/test.PHP_file_uploads.php deleted file mode 100644 index 532279ddc9..0000000000 --- a/core/src/core/tests/test.PHP_file_uploads.php +++ /dev/null @@ -1,50 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Check that file uploads is enabled - * @package AjaXplorer - * @subpackage Tests - */ -class PHP_file_uploads extends AbstractTest -{ - protected static $testKey = 'PHP File Uploads enabled'; - - public function __construct() { parent::__construct(self::$testKey, "You should enable php file_uploads parameter for uploading files with Pydio."); } - public function doTest() - { - $this->failedLevel = "warning"; - $v = @ini_get("file_uploads"); - if (isSet($v) && (is_numeric($v) || strtolower($v) == "on")) { - $this->failedInfo = "PHP File Uploads is enabled"; - $this->testedParams[self::$testKey] = "Yes"; - return TRUE; - } else if (!isSet($v)) { - $this->failedInfo = "Unable to detect the file_uploads value, please make sure that it is disabled (Off) in your php.ini or your virtual host."; - return FALSE; - } - $this->failedInfo = "PHP File Uploads is disabled"; - $this->testedParams[self::$testKey] = "Yes"; - return false; - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.PHP_magic_quotes.php b/core/src/core/tests/test.PHP_magic_quotes.php deleted file mode 100644 index 5320a2090e..0000000000 --- a/core/src/core/tests/test.PHP_magic_quotes.php +++ /dev/null @@ -1,42 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Check whether mcrypt is enabled - * @package Pydio - * @subpackage Tests - */ -class PHP_magic_quotes extends AbstractTest -{ - public function __construct() { parent::__construct("Magic quotes disabled", "Magic quotes need to be disabled, only relevent for php 5.3"); } - public function doTest() - { - $this->failedLevel = "error"; - if (get_magic_quotes_gpc()) { - $this->testedParams["Magic quotes disabled"] = "No"; - return FALSE; - } - $this->testedParams["Magic quotes disabled"] = "Yes"; - return TRUE; - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.SSLEncryption.php b/core/src/core/tests/test.SSLEncryption.php deleted file mode 100644 index e2a6514fac..0000000000 --- a/core/src/core/tests/test.SSLEncryption.php +++ /dev/null @@ -1,48 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Detect HTTPS protocol - * @package Pydio - * @subpackage Tests - */ -class SSLEncryption extends AbstractTest -{ - public function __construct() { parent::__construct("SSL Encryption", "You are not using SSL encryption, or it was not detected by the server. Be aware that it is strongly recommended to secure all communication of data over the network."); } - public function doTest() - { - // Get the locale - $ssl = false; - if (isSet($_SERVER["HTTPS"]) && strtolower($_SERVER["HTTPS"]) == "on") { - $ssl = true; - } - if (!$ssl) { - $this->failedLevel = "warning"; - $this->failedInfo .= "

    Suggestion : if your server supports HTTPS, set the AJXP_FORCE_SSL_REDIRECT parameter in the conf/bootstrap_conf.php file.

    "; - return FALSE; - } else { - $this->failedInfo .= "Https protocol detected"; - return TRUE; - } - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.ServerEncoding.php b/core/src/core/tests/test.ServerEncoding.php deleted file mode 100644 index 6de7c0c8e1..0000000000 --- a/core/src/core/tests/test.ServerEncoding.php +++ /dev/null @@ -1,61 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Test Server Encoding - * @package Pydio - * @subpackage Tests - */ -class ServerEncoding extends AbstractTest -{ - public function __construct() { parent::__construct("Server charset encoding", "You must set a correct charset encoding - in your locale definition in the form: en_us.UTF-8. Please refer to setlocale man page. - If your detected locale is C, simply type echo \$LANG on your server command line to read the correct value."); } - public function doTest() - { - // Get the locale - $locale = setlocale(LC_CTYPE, 0); - if ($locale == 'C') { - $this->failedLevel = "warning"; - $this->failedInfo .= "Detected locale: $locale (using UTF-8)"; - $this->failedInfo .= "

    Suggestion : Set the AJXP_LOCALE parameter to the correct value in the conf/bootstrap_conf.php file. You can also set this value using the installer form (next step).

    "; - return FALSE; - } - if (strpos($locale, '.') === FALSE) { - $this->failedLevel = "warning"; - $this->failedInfo .= "Locale doesn't contain encoding: $locale (so using UTF-8)"; - $this->failedInfo .= "

    Suggestion : Set the AJXP_LOCALE parameter to the correct value in the conf/bootstrap_conf.php file. You can also set this value using the installer form (next step).

    "; - return FALSE; - } - // Check if we have iconv - if (!function_exists("iconv") && floatval(phpversion()) > 5.0) { $this->failedInfo .= "Couldn't find iconv. Please use a PHP version with iconv support"; return FALSE; } - if (floatval(phpversion()) > 5.0) { - // Try converting from a known UTF-8 string to ISO8859-1 string and back to make sure it works. - $string = "aéàç"; - $iso = iconv("UTF-8", "ISO-8859-1", $string); - $back = iconv("ISO-8859-1", "UTF-8", $iso); - if (strlen($iso) != 4 || ord($iso[1]) != 233 || $back != $string) { $this->failedInfo .= "iconv doesn't work on your system: $string $iso $back"; return FALSE; } - } - return TRUE; - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.Upload.php b/core/src/core/tests/test.Upload.php deleted file mode 100644 index 8ae92fd904..0000000000 --- a/core/src/core/tests/test.Upload.php +++ /dev/null @@ -1,58 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Test upload tmp dir - * @package Pydio - * @subpackage Tests - */ -class Upload extends AbstractTest -{ - public function __construct() { parent::__construct("Upload particularities", "Testing configs"); } - public function doTest() - { - $tmpDir = ini_get("upload_tmp_dir"); - if (!$tmpDir) $tmpDir = realpath(sys_get_temp_dir()); - if (ConfService::getCoreConf("AJXP_TMP_DIR") != "") { - $tmpDir = ConfService::getCoreConf("AJXP_TMP_DIR"); - } - if (defined("AJXP_TMP_DIR") && AJXP_TMP_DIR !="") { - $tmpDir = AJXP_TMP_DIR; - } - $this->testedParams["Upload Tmp Dir Writeable"] = @is_writable($tmpDir); - $this->testedParams["PHP Upload Max Size"] = $this->returnBytes(ini_get("upload_max_filesize")); - $this->testedParams["PHP Post Max Size"] = $this->returnBytes(ini_get("post_max_size")); - foreach ($this->testedParams as $paramName => $paramValue) { - $this->failedInfo .= "\n$paramName=$paramValue"; - } - if (!$this->testedParams["Upload Tmp Dir Writeable"]) { - $this->failedLevel = "error"; - $this->failedInfo = "The temporary folder used by PHP to upload files is either incorrect or not writeable! Upload will not work. Please check : ".ini_get("upload_tmp_dir"); - $this->failedInfo .= "

    Suggestion : Set the AJXP_TMP_DIR parameter in the conf/bootstrap_conf.php file

    "; - return FALSE; - } - - $this->failedLevel = "info"; - return FALSE; - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.UsersConfig.php b/core/src/core/tests/test.UsersConfig.php deleted file mode 100644 index 7d0eb8459c..0000000000 --- a/core/src/core/tests/test.UsersConfig.php +++ /dev/null @@ -1,39 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Gather the users configuration - * @package Pydio - * @subpackage Tests - */ -class UsersConfig extends AbstractTest -{ - public function __construct() { parent::__construct("Users Configuration", "Current config for users"); } - public function doTest() - { - $this->testedParams["Users enabled"] = AuthService::usersEnabled(); - $this->testedParams["Guest enabled"] = ConfService::getCoreConf("ALLOW_GUEST_BROWSING", "auth"); - $this->failedLevel = "info"; - return FALSE; - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.Writeability.php b/core/src/core/tests/test.Writeability.php deleted file mode 100644 index 154c19b0f4..0000000000 --- a/core/src/core/tests/test.Writeability.php +++ /dev/null @@ -1,58 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Check the various plugins folders writeability - * @package Pydio - * @subpackage Tests - */ -class Writeability extends AbstractTest -{ - public function __construct() { parent::__construct("Required writeable folder", "One of the following folder should be writeable and is not : "); } - public function doTest() - { - $checks = array(); - $checks[] = AJXP_CACHE_DIR; - $checks[] = AJXP_DATA_PATH; - $checked = array(); - $success = true; - foreach ($checks as $check) { - $w = false; - $check = AJXP_VarsFilter::filter($check); - if (!is_dir($check)) {// Check parent - $check = dirname($check); - } - $w = is_writable($check); - $checked[basename($check)] = "".basename($check).":".($w?'true':'false'); - $success = $success & $w; - } - $this->testedParams["Writeable Folders"] = "[".implode(',
    ', array_values($checked))."]"; - if (!$success) { - $this->failedInfo .= implode(",", $checks); - return FALSE; - } - $this->failedLevel = "info"; - $this->failedInfo = "[".implode(',
    ', array_values($checked))."]"; - return FALSE; - } -} \ No newline at end of file diff --git a/core/src/core/tests/test.Zlib.php b/core/src/core/tests/test.Zlib.php deleted file mode 100644 index 8df48ee272..0000000000 --- a/core/src/core/tests/test.Zlib.php +++ /dev/null @@ -1,38 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -require_once('../classes/class.AbstractTest.php'); - -/** - * Check that Zlib is enabled - * @package Pydio - * @subpackage Tests - */ -class Zlib extends AbstractTest -{ - public function __construct() { parent::__construct("Zlib extension (ZIP)", "Extension enabled : ".((function_exists('gzopen')||function_exists('gzopen64'))?"1":"0")); } - public function doTest() - { - $this->testedParams["Zlib Enabled"] = ((function_exists('gzopen')||function_exists('gzopen64'))?"Yes":"No"); - $this->failedLevel = "info"; - return false; - } -} \ No newline at end of file diff --git a/core/src/data/cache/.htaccess b/core/src/data/cache/.htaccess deleted file mode 100644 index 3418e55a68..0000000000 --- a/core/src/data/cache/.htaccess +++ /dev/null @@ -1 +0,0 @@ -deny from all \ No newline at end of file diff --git a/core/src/data/plugins/.htaccess b/core/src/data/plugins/.htaccess deleted file mode 100644 index 3418e55a68..0000000000 --- a/core/src/data/plugins/.htaccess +++ /dev/null @@ -1 +0,0 @@ -deny from all \ No newline at end of file diff --git a/core/src/data/plugins/auth.serial/.htaccess b/core/src/data/plugins/auth.serial/.htaccess deleted file mode 100644 index 3418e55a68..0000000000 --- a/core/src/data/plugins/auth.serial/.htaccess +++ /dev/null @@ -1 +0,0 @@ -deny from all \ No newline at end of file diff --git a/core/src/data/plugins/conf.serial/.htaccess b/core/src/data/plugins/conf.serial/.htaccess deleted file mode 100644 index 3418e55a68..0000000000 --- a/core/src/data/plugins/conf.serial/.htaccess +++ /dev/null @@ -1 +0,0 @@ -deny from all \ No newline at end of file diff --git a/core/src/data/web.config b/core/src/data/web.config new file mode 100644 index 0000000000..5381d13aa6 --- /dev/null +++ b/core/src/data/web.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/core/src/dav.php b/core/src/dav.php deleted file mode 100644 index 630f289611..0000000000 --- a/core/src/dav.php +++ /dev/null @@ -1,134 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - * - * Description : Command line access of the framework. - * DAV controller, loads the ezComponent webDav Server - */ -include_once("base.conf.php"); - -//set_error_handler(array("AJXP_XMLWriter", "catchError"), E_ALL & ~E_NOTICE ); -//set_exception_handler(array("AJXP_XMLWriter", "catchException")); -$pServ = AJXP_PluginsService::getInstance(); - -ConfService::init(); -ConfService::start(); - -if (!ConfService::getCoreConf("WEBDAV_ENABLE")) { - die('You are not allowed to access this service'); -} - -$confStorageDriver = ConfService::getConfStorageImpl(); -require_once($confStorageDriver->getUserClassFileName()); - -AJXP_PluginsService::getInstance()->initActivePlugins(); - -/** - * @param string $className - * @return void - */ -function AJXP_Sabre_autoload($className) -{ - if (strpos($className,'AJXP_Sabre_')===0) { - include AJXP_BIN_FOLDER. '/sabredav/ajaxplorer/class.' . $className . '.php'; - } -} -spl_autoload_register('AJXP_Sabre_autoload'); - - - -include 'core/classes/sabredav/lib/Sabre/autoload.php'; - -if (ConfService::getCoreConf("WEBDAV_BASEHOST") != "") { - $baseURL = ConfService::getCoreConf("WEBDAV_BASEHOST"); -} else { - $baseURL = AJXP_Utils::detectServerURL(); -} -$baseURI = ConfService::getCoreConf("WEBDAV_BASEURI"); - -$requestUri = $_SERVER["REQUEST_URI"]; -if (substr($requestUri, 0, strlen($baseURI)) != $baseURI) -{ - $baseURI = substr($requestUri, 0, stripos($requestUri, $baseURI)) . $baseURI; -} -$end = trim(substr($requestUri, strlen($baseURI."/"))); -$rId = null; -if ((!empty($end) || $end ==="0") && $end[0] != "?") { - - $parts = explode("/", $end); - $pathBase = $parts[0]; - $repositoryId = $pathBase; - - $repository = ConfService::getRepositoryById($repositoryId); - if ($repository == null) { - $repository = ConfService::getRepositoryByAlias($repositoryId); - if ($repository != null) { - $repositoryId = $repository->getId(); - } - } - if ($repository == null) { - AJXP_Logger::debug("not found, dying $repositoryId"); - die('You are not allowed to access this service'); - } - - $rId = $repositoryId; - $rootDir = new AJXP_Sabre_Collection("/", $repository, null); - $server = new Sabre\DAV\Server($rootDir); - $server->setBaseUri($baseURI."/".$pathBase); - - -} else { - - $rootDir = new AJXP_Sabre_RootCollection("root"); - $server = new Sabre\DAV\Server($rootDir); - $server->setBaseUri($baseURI); - -} - -if((AJXP_Sabre_AuthBackendBasic::detectBasicHeader() || ConfService::getCoreConf("WEBDAV_FORCE_BASIC")) - && ConfService::getAuthDriverImpl()->getOptionAsBool("TRANSMIT_CLEAR_PASS")){ - $authBackend = new AJXP_Sabre_AuthBackendBasic($rId); -} else { - $authBackend = new AJXP_Sabre_AuthBackendDigest($rId); -} -$authPlugin = new Sabre\DAV\Auth\Plugin($authBackend, ConfService::getCoreConf("WEBDAV_DIGESTREALM")); -$server->addPlugin($authPlugin); - -if (!is_dir(AJXP_DATA_PATH."/plugins/server.sabredav")) { - mkdir(AJXP_DATA_PATH."/plugins/server.sabredav", 0755); - $fp = fopen(AJXP_DATA_PATH."/plugins/server.sabredav/locks", "w"); - fwrite($fp, ""); - fclose($fp); -} - -$lockBackend = new Sabre\DAV\Locks\Backend\File(AJXP_DATA_PATH."/plugins/server.sabredav/locks"); -$lockPlugin = new Sabre\DAV\Locks\Plugin($lockBackend); -$server->addPlugin($lockPlugin); - -if (ConfService::getCoreConf("WEBDAV_BROWSER_LISTING")) { - $browerPlugin = new AJXP_Sabre_BrowserPlugin((isSet($repository)?$repository->getDisplay():null)); - $extPlugin = new Sabre\DAV\Browser\GuessContentType(); - $server->addPlugin($browerPlugin); - $server->addPlugin($extPlugin); -} -try { - $server->exec(); -} catch ( Exception $e ) { - AJXP_Logger::error(__CLASS__,"Exception",$e->getMessage()); -} diff --git a/core/src/index.php b/core/src/index.php index ec5ee0467d..133994c9f0 100644 --- a/core/src/index.php +++ b/core/src/index.php @@ -1,6 +1,6 @@ + * Copyright 2007-2016 Charles du Jeu - Abstrium SAS * This file is part of Pydio. * * Pydio is free software: you can redistribute it and/or modify @@ -16,152 +16,15 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . * * Description : main access point of the application, this script is called by any Ajax query. * Will dispatch the actions on the plugins. */ -include_once("base.conf.php"); - -if( !isSet($_GET["action"]) && !isSet($_GET["get_action"]) - && !isSet($_POST["action"]) && !isSet($_POST["get_action"]) - && defined("AJXP_FORCE_SSL_REDIRECT") && AJXP_FORCE_SSL_REDIRECT === true - && $_SERVER['HTTPS'] != "on") { - header("HTTP/1.1 301 Moved Permanently"); - header("Location: https://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']); - exit(); -} - -if (isSet($_GET["ajxp_sessid"])) { - // Don't overwrite cookie - if (!isSet($_COOKIE["AjaXplorer"])) - $_COOKIE["AjaXplorer"] = $_GET["ajxp_sessid"]; -} -header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); -header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); -header("Cache-Control: no-cache, must-revalidate"); -header("Pragma: no-cache"); - -if (is_file(TESTS_RESULT_FILE)) { - set_error_handler(array("AJXP_XMLWriter", "catchError"), E_ALL & ~E_NOTICE & ~E_STRICT ); - set_exception_handler(array("AJXP_XMLWriter", "catchException")); -} - -ConfService::init(); -ConfService::start(); - -$confStorageDriver = ConfService::getConfStorageImpl(); -require_once($confStorageDriver->getUserClassFileName()); - -// Custom Session Handler -if(defined("AJXP_SESSION_HANDLER_PATH") && defined("AJXP_SESSION_HANDLER_CLASSNAME") && file_exists(AJXP_SESSION_HANDLER_PATH)){ - require_once(AJXP_SESSION_HANDLER_PATH); - if(class_exists(AJXP_SESSION_HANDLER_CLASSNAME, false)){ - $sessionHandlerClass = AJXP_SESSION_HANDLER_CLASSNAME; - $sessionHandler = new $sessionHandlerClass(); - session_set_save_handler($sessionHandler, false); - } -} -if (!isSet($OVERRIDE_SESSION)) { - session_name("AjaXplorer"); -} -session_start(); +use Pydio\Core\Http\TopLevelRouter; -if (isSet($_GET["tmp_repository_id"]) || isSet($_POST["tmp_repository_id"])) { - try{ - ConfService::switchRootDir(isset($_GET["tmp_repository_id"])?$_GET["tmp_repository_id"]:$_POST["tmp_repository_id"], true); - }catch(AJXP_Exception $e){ - //$requireAuth = true; - } -} else if (isSet($_SESSION["SWITCH_BACK_REPO_ID"])) { - ConfService::switchRootDir($_SESSION["SWITCH_BACK_REPO_ID"]); - unset($_SESSION["SWITCH_BACK_REPO_ID"]); -} -$action = "ping"; -if (preg_match('/MSIE 7/',$_SERVER['HTTP_USER_AGENT']) || preg_match('/MSIE 8/',$_SERVER['HTTP_USER_AGENT'])) { - $action = "get_boot_gui"; -} else { - $action = (strpos($_SERVER["HTTP_ACCEPT"], "text/html") !== false ? "get_boot_gui" : "ping"); -} -if(isSet($_GET["action"]) || isSet($_GET["get_action"])) $action = (isset($_GET["get_action"])?$_GET["get_action"]:$_GET["action"]); -else if(isSet($_POST["action"]) || isSet($_POST["get_action"])) $action = (isset($_POST["get_action"])?$_POST["get_action"]:$_POST["action"]); - -$pluginsUnSecureActions = ConfService::getDeclaredUnsecureActions(); -$unSecureActions = array_merge($pluginsUnSecureActions, array("get_secure_token")); -if (!in_array($action, $unSecureActions) && AuthService::getSecureToken()) { - $token = ""; - if(isSet($_GET["secure_token"])) $token = $_GET["secure_token"]; - else if(isSet($_POST["secure_token"])) $token = $_POST["secure_token"]; - if ( $token == "" || !AuthService::checkSecureToken($token)) { - throw new Exception("You are not allowed to access this resource."); - } -} - -if (AuthService::usersEnabled()) { - $httpVars = array_merge($_GET, $_POST); - - AuthService::logUser(null, null); - // Check that current user can access current repository, try to switch otherwise. - $loggedUser = AuthService::getLoggedUser(); - if ($loggedUser == null || $loggedUser->getId() == "guest") { - // Try prelogging user if the session expired but the logging data is in fact still present - // For example, for basic_http auth. - AJXP_PluginsService::getInstance()->initActivePlugins(); - AuthService::preLogUser($httpVars); - $loggedUser = AuthService::getLoggedUser(); - if($loggedUser == null) $requireAuth = true; - } - if ($loggedUser != null) { - $res = ConfService::switchUserToActiveRepository($loggedUser, (isSet($httpVars["tmp_repository_id"])?$httpVars["tmp_repository_id"]:"-1")); - if (!$res) { - AuthService::disconnect(); - $requireAuth = true; - } - } - -} else { - AJXP_Logger::debug(ConfService::getCurrentRepositoryId()); -} - -//Set language -$loggedUser = AuthService::getLoggedUser(); -if($loggedUser != null && $loggedUser->getPref("lang") != "") ConfService::setLanguage($loggedUser->getPref("lang")); -else if(isSet($_COOKIE["AJXP_lang"])) ConfService::setLanguage($_COOKIE["AJXP_lang"]); - -//------------------------------------------------------------ -// SPECIAL HANDLING FOR FANCY UPLOADER RIGHTS FOR THIS ACTION -//------------------------------------------------------------ -if (AuthService::usersEnabled()) { - $loggedUser = AuthService::getLoggedUser(); - if ($action == "upload" && ($loggedUser == null || !$loggedUser->canWrite(ConfService::getCurrentRepositoryId()."")) && isSet($_FILES['Filedata'])) { - header('HTTP/1.0 ' . '410 Not authorized'); - die('Error 410 Not authorized!'); - } -} +include_once("base.conf.php"); -// THIS FIRST DRIVERS DO NOT NEED ID CHECK -//$ajxpDriver = AJXP_PluginsService::findPlugin("gui", "ajax"); -$authDriver = ConfService::getAuthDriverImpl(); -// DRIVERS BELOW NEED IDENTIFICATION CHECK -if (!AuthService::usersEnabled() || ConfService::getCoreConf("ALLOW_GUEST_BROWSING", "auth") || AuthService::getLoggedUser()!=null) { - $confDriver = ConfService::getConfStorageImpl(); - try{ - $Driver = ConfService::loadRepositoryDriver(); - }catch(Exception $e){ - //AuthService::disconnect(); - } -} -AJXP_PluginsService::getInstance()->initActivePlugins(); -require_once(AJXP_BIN_FOLDER."/class.AJXP_Controller.php"); -$xmlResult = AJXP_Controller::findActionAndApply($action, array_merge($_GET, $_POST), $_FILES); -if ($xmlResult !== false && $xmlResult != "") { - AJXP_XMLWriter::header(); - print($xmlResult); - AJXP_XMLWriter::close(); -} else if (isset($requireAuth) && AJXP_Controller::$lastActionNeedsAuth) { - AJXP_XMLWriter::header(); - AJXP_XMLWriter::requireAuth(); - AJXP_XMLWriter::close(); -} -session_write_close(); +$router = new TopLevelRouter(); +$router->route(); \ No newline at end of file diff --git a/core/src/index_shared.php b/core/src/index_shared.php deleted file mode 100644 index 6e37553617..0000000000 --- a/core/src/index_shared.php +++ /dev/null @@ -1,28 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -$OVERRIDE_SESSION = true; -$h = ''; -if(isSet($_GET['minisite_session'])){ - $h = $_GET['minisite_session']; -} - -session_name("AjaXplorer_Shared".str_replace(".","_",$h)); -include("index.php"); diff --git a/core/src/opencollab.php b/core/src/opencollab.php deleted file mode 100644 index 95ae2054f5..0000000000 --- a/core/src/opencollab.php +++ /dev/null @@ -1,79 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -define('AJXP_EXEC', true); -include_once("base.conf.php"); - -$pServ = AJXP_PluginsService::getInstance(); -ConfService::$useSession = false; -AuthService::$useSession = false; - -ConfService::init(); -ConfService::start(); - -$confStorageDriver = ConfService::getConfStorageImpl(); -require_once($confStorageDriver->getUserClassFileName()); - -$pServ->initActivePlugins(); - -/** - * @var Pydio\OCS\OCSPlugin $coreLoader - */ -$coreLoader = $pServ->getPluginById("core.ocs"); -$configs = $coreLoader->getConfigs(); - -$uri = $_SERVER["REQUEST_URI"]; -$parts = explode("/", trim(parse_url($uri, PHP_URL_PATH), "/")); -$baseUri = array(); -$root = array_shift($parts); -while(!in_array($root, array("ocs-provider", "ocs")) && count($parts)){ - $baseUri[] = $root; - $root = array_shift($parts); -} -if( $root == "ocs-provider"){ - - $services = array(); - $coreLoader->publishServices(); - -}else if($root == "ocs"){ - - if(count($parts) < 2){ - $d = new \Pydio\OCS\Server\Dummy(); - $response = $d->buildResponse("fail", "400", "Wrong URI"); - $d->sendResponse($response); - return; - } - - $version = array_shift($parts); - if($version != "v2"){ - $d = new \Pydio\OCS\Server\Dummy(); - $response = $d->buildResponse("fail", "400", "Api version not supported - Please switch to v2."); - $d->sendResponse($response); - return; - } - $endpoint = array_shift($parts); - if(count($baseUri)){ - $baseUriStr = "/".implode("/", $baseUri); - }else{ - $baseUriStr = ""; - } - $coreLoader->route($baseUriStr, $endpoint, $parts, array_merge($_GET, $_POST)); - -} \ No newline at end of file diff --git a/core/src/phpdoc.dist.xml b/core/src/phpdoc.dist.xml new file mode 100644 index 0000000000..d10390505b --- /dev/null +++ b/core/src/phpdoc.dist.xml @@ -0,0 +1,28 @@ + + + + core/doc/php + + + core/doc/php + + + core/src/pydio + plugins + *phpseclib* + *vendor* + *vendor/phpmailer* + *lib/class.phpmailer-lite.php + *openstack-sdk-php* + *CAS* + *duo_php* + *Elastica* + *Zend* + *i18n* + *dropbox-php* + *dibi* + *phpws* + *etherpad-client* + *SFTPPSL_StreamWrapper* + + diff --git a/core/src/phpunit/AJXP/Tests/Atomics/PermissionMaskTest.php b/core/src/phpunit/AJXP/Tests/Atomics/PermissionMaskTest.php deleted file mode 100755 index 4c5f71c3ff..0000000000 --- a/core/src/phpunit/AJXP/Tests/Atomics/PermissionMaskTest.php +++ /dev/null @@ -1,187 +0,0 @@ -assertTrue($permission->canRead()); - $this->assertTrue($permission->canWrite()); - $this->assertFalse($permission->denies()); - } - - /** - * @param \AJXP_Permission $permission - */ - protected function isReadonly($permission){ - $this->assertTrue($permission->canRead()); - $this->assertFalse($permission->canWrite()); - $this->assertFalse($permission->denies()); - } - - /** - * @param \AJXP_Permission $permission - */ - protected function isWriteonly($permission){ - - $this->assertFalse($permission->canRead()); - $this->assertTrue($permission->canWrite()); - $this->assertFalse($permission->denies()); - - } - - /** - * @param \AJXP_Permission $permission - */ - protected function isDenied($permission){ - - $this->assertTrue($permission->denies()); - $this->assertFalse($permission->canRead()); - $this->assertFalse($permission->canWrite()); - - } - - - public function testSimplePermission(){ - - $perm = new \AJXP_Permission(); - $this->isDenied($perm); - - $perm->setRead(); - $this->isReadonly($perm); - $perm->setWrite(); - $this->isRW($perm); - $perm->setDeny(); - $this->isDenied($perm); - - $perm->setDeny(false); - - // When perm is empty, => denied by default - $this->isDenied($perm); - $perm->setRead(); - $perm->setRead(false); - $this->isDenied($perm); - $perm->setWrite(); - $perm->setWrite(false); - $this->isDenied($perm); - - } - - - public function testPermissionOverride(){ - - $readonly = new \AJXP_Permission("r"); - $writeonly = new \AJXP_Permission("w"); - $readwrite = new \AJXP_Permission("rw"); - $deny = new \AJXP_Permission("d"); - $empty = new \AJXP_Permission(); - - $this->isRW($writeonly->override($readonly)); - $this->isRW($readonly->override($writeonly)); - $this->isRW($readwrite->override($readonly)); - $this->isRW($readwrite->override($writeonly)); - $this->isRW($readonly->override($readwrite)); - $this->isRW($writeonly->override($readwrite)); - - $this->isDenied($deny->override($readonly)); - $this->isDenied($deny->override($writeonly)); - $this->isDenied($deny->override($readwrite)); - - $this->isDenied($empty->override($readonly)); - $this->isDenied($empty->override($writeonly)); - $this->isDenied($empty->override($readwrite)); - $this->isRW($writeonly->override($readonly)); - } - - /** - * @parra - * - */ - public function testPermissionMask(){ - $mask = new \AJXP_PermissionMask(); - $mask->updateBranch("/a1/b1/c1", new \AJXP_Permission("r")); - echo "\n"; - $mask->toStr($mask->getTree(), 1); - $mask->updateBranch("/a1/b1/c2", new \AJXP_Permission("rw")); - $mask->updateBranch("/a1/b1/c3", new \AJXP_Permission()); - echo "\n"; - $mask->toStr($mask->getTree(), 1); - $mask->updateBranch("/a1/b2", new \AJXP_Permission("rw")); - echo "\n"; - $mask->toStr($mask->getTree(), 1); - - $mask->updateBranch("/a1/b3", new \AJXP_Permission()); - - $mask->updateBranch("a2/b1", new \AJXP_Permission()); - - $this->assertFalse($mask->match("/", \AJXP_Permission::WRITE)); - $this->assertTrue($mask->match("/", \AJXP_Permission::READ)); - $this->assertTrue($mask->match("/a1/b1/c1", \AJXP_Permission::READ)); - $this->assertTrue($mask->match("/a1/b1", \AJXP_Permission::READ)); - $this->assertFalse($mask->match("/a1/b1", \AJXP_Permission::WRITE)); - - $this->assertFalse($mask->match("/a1/b1/c3", \AJXP_Permission::READ)); - $this->assertTrue($mask->match("/a1/b1/c1/d1/e1/f1", \AJXP_Permission::READ)); - $this->assertFalse($mask->match("/a1/b1/c1/d1/e1/f1", \AJXP_Permission::WRITE)); - - // This overrides the whole /a1/b1 branch >> do we want that? - $mask2 = new \AJXP_PermissionMask(); - $mask2->updateBranch("/a1/b1", new \AJXP_Permission("d")); - //$mask->override($mask2); - - echo "mask 2\n"; - $mask2->toStr($mask2->getTree(), 0); - echo "mask 1\n"; - $mask->toStr($mask->getTree(), 0); - echo "Mask 2 override mask 1\n"; - $mask->override($mask2); - $mask->toStr($mask->getTree(), 0); - - //$this->assertTrue($mask->match("/a1/b1", \AJXP_Permission::WRITE)); - // Todo write more tests - - $mask->updateBranch("/a1/b1/c1", new \AJXP_Permission("w")); - - //$this->assertFalse($mask->match("/a1/b1/c1", \AJXP_Permission::READ)); - $this->assertTrue($mask->match("/a1/b1/c1", \AJXP_Permission::WRITE)); - - // - // - // $this->assertTrue($mask->match("/a1/b1/c2", \AJXP_Permission::READ)); - //$this->assertTrue($mask->match("/a1/b1/c2", \AJXP_Permission::WRITE)); - - //$this->assertTrue($mask->match("/a1", \AJXP_Permission::DENY)); - - // Test that a deny is cutting the sub branches - $mask1 = new \AJXP_PermissionMask(); - $mask1->updateBranch("/a1/b1", new \AJXP_Permission("rw")); - $mask1->updateBranch("/a1/b2", new \AJXP_Permission("rw")); - $mask1->updateBranch("/a1/b3/c1", new \AJXP_Permission("rw")); - $mask1->updateBranch("/a1/b3/c2", new \AJXP_Permission("rw")); - - $mask2 = new \AJXP_PermissionMask(); - $mask2->updateBranch("/a1", new \AJXP_Permission("d")); - - $result = $mask1->override($mask2); - $this->assertFalse($result->match("/a1", \AJXP_Permission::READ)); - $this->assertFalse($result->match("/a1/b2", \AJXP_Permission::READ)); - $this->assertFalse($result->match("/a1/b3", \AJXP_Permission::READ)); - $this->assertFalse($result->match("/a1/b3/c1", \AJXP_Permission::READ)); - $this->assertFalse($result->match("/a1/any", \AJXP_Permission::READ)); - - } - -} - - - diff --git a/core/src/phpunit/AJXP/Tests/Suite/Atomics.php b/core/src/phpunit/AJXP/Tests/Suite/Atomics.php deleted file mode 100644 index 6ab2b2e20a..0000000000 --- a/core/src/phpunit/AJXP/Tests/Suite/Atomics.php +++ /dev/null @@ -1,43 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -namespace AJXP\Tests\Suite; - -class Atomics extends \PHPUnit_Framework_TestSuite -{ - public static function suite() - { - $s = new Atomics(); - $s->addTestFile("AJXP/Tests/Atomics/RolesTest.php"); - $s->addTestFile("AJXP/Tests/Atomics/UtilsTest.php"); - $s->addTestFile("AJXP/Tests/Atomics/PermissionMaskTest.php"); - return $s; - - } - - protected function setUp() - { - } - - protected function tearDown() - { - } - -} diff --git a/core/src/phpunit/HOWTO b/core/src/phpunit/HOWTO deleted file mode 100644 index 00cc4beace..0000000000 --- a/core/src/phpunit/HOWTO +++ /dev/null @@ -1,4 +0,0 @@ -We pass the base.conf.php file as the PHPUnit "bootstrap" file. For example, running a given -test suite, knowing AjaXplorer is installed at /var/www/ajaxplorer : - -> phpunit --bootstrap /var/www/ajaxplorer/base.conf.php AJXP_Suite_Atomics diff --git a/core/src/plugins/access.ajxp_conf/ajxp_confActions.xml b/core/src/plugins/access.ajxp_conf/ajxp_confActions.xml index d39f980e23..b5825f1960 100644 --- a/core/src/plugins/access.ajxp_conf/ajxp_confActions.xml +++ b/core/src/plugins/access.ajxp_conf/ajxp_confActions.xml @@ -1,20 +1,22 @@ - - - - - - - - - - + + + + + + + + + + 0){ path = window.actionArguments[0]; @@ -29,76 +31,276 @@ ajaxplorer.updateContextData(path); } ]]> - + - - - - - - - - - - - - - - - - + - + - + - + + + + + + + - + + - + + + + + + - + + + + + + ]]> + + + + - + + + + + + + + + + + +
    +
    AJXP_MESSAGE[ajxp_conf.95]
    +
    AJXP_MESSAGE[ajxp_conf.96]
    +
    +
    AJXP_MESSAGE[ajxp_conf.8]* :
    +
    AJXP_MESSAGE[ajxp_conf.116]* :
    + +
    +
    +
    AJXP_MESSAGE[ajxp_conf.32]* :
    +
    AJXP_MESSAGE[ajxp_conf.117]* :
    + +
    +
    +
    +
    + + +
    + + ]]>
    + + + +
    +
    + + + + + + + + + + + + + + + + + - - + + + + - + - - - + + + + + + + + + - + - - - - + + + + - - - - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + -
    AJXP_MESSAGE[94] :
    @@ -141,52 +343,28 @@
    ]]>
    - + > - -
    -
    - - - - - - - - - -
    -
    AJXP_MESSAGE[ajxp_conf.76] :
    -
    - - ]]>
    - - - -
    -
    - - - - - - - - + + + + + + + + + + -
    Group ID :
    @@ -196,107 +374,37 @@
    ]]>
    - + - -
    -
    - - - - - - - - - - - - - - - - - - - - -
    -
    AJXP_MESSAGE[ajxp_conf.95]
    -
    AJXP_MESSAGE[ajxp_conf.96]
    -
    -
    AJXP_MESSAGE[ajxp_conf.8]* :
    -
    AJXP_MESSAGE[ajxp_conf.116]* :
    - -
    -
    -
    AJXP_MESSAGE[ajxp_conf.32]* :
    -
    AJXP_MESSAGE[ajxp_conf.117]* :
    - -
    -
    -
    -
    - - -
    - - ]]>
    - - - -
    -
    - - - - - - - - + + + + + + + + + + + + - - - - - - - - - + + + + + + + + ]]> - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ]]> - - - - - - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    AJXP_MESSAGE[ajxp_conf.76] :
    +
    + + ]]>
    + + + +
    - + - - - - - - - - - + - + - + - + + + @@ -561,10 +546,68 @@ conn.setParameters($H({get_action:'clear_plugins_cache'})); conn.sendAsync(); ]]>
    - + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -
    + +
    diff --git a/core/src/plugins/access.ajxp_conf/class.ConfigEditor.js b/core/src/plugins/access.ajxp_conf/class.ConfigEditor.js index b2a5affd93..ba05195e89 100644 --- a/core/src/plugins/access.ajxp_conf/class.ConfigEditor.js +++ b/core/src/plugins/access.ajxp_conf/class.ConfigEditor.js @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ /** diff --git a/core/src/plugins/access.ajxp_conf/class.ajxp_confAccessDriver.php b/core/src/plugins/access.ajxp_conf/class.ajxp_confAccessDriver.php deleted file mode 100644 index 0d77bc2ca6..0000000000 --- a/core/src/plugins/access.ajxp_conf/class.ajxp_confAccessDriver.php +++ /dev/null @@ -1,2938 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - * - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * @package AjaXplorer_Plugins - * @subpackage Access - * @class ajxp_confAccessDriver - * AJXP_Plugin to access the configurations data - */ -class ajxp_confAccessDriver extends AbstractAccessDriver -{ - - protected $listSpecialRoles = AJXP_SERVER_DEBUG; - protected $currentBookmarks = array(); - - protected $rootNodes = array( - "data" => array( - "LABEL" => "ajxp_conf.110", - "ICON" => "user.png", - "DESCRIPTION" => "ajxp_conf.137", - "CHILDREN" => array( - "repositories" => array( - "AJXP_MIME" => "workspaces_zone", - "LABEL" => "ajxp_conf.3", - "DESCRIPTION" => "ajxp_conf.138", - "ICON" => "hdd_external_unmount.png", - "LIST" => "listRepositories"), - "users" => array( - "AJXP_MIME" => "users_zone", - "LABEL" => "ajxp_conf.2", - "DESCRIPTION" => "ajxp_conf.139", - "ICON" => "users-folder.png", - "LIST" => "listUsers" - ), - "roles" => array( - "AJXP_MIME" => "roles_zone", - "LABEL" => "ajxp_conf.69", - "DESCRIPTION" => "ajxp_conf.140", - "ICON" => "user-acl.png", - "LIST" => "listRoles"), - ) - ), - "config" => array( - "AJXP_MIME" => "plugins_zone", - "LABEL" => "ajxp_conf.109", - "ICON" => "preferences_desktop.png", - "DESCRIPTION" => "ajxp_conf.136", - "CHILDREN" => array( - "core" => array( - "AJXP_MIME" => "plugins_zone", - "LABEL" => "ajxp_conf.98", - "DESCRIPTION" => "ajxp_conf.133", - "ICON" => "preferences_desktop.png", - "LIST" => "listPlugins"), - "plugins" => array( - "AJXP_MIME" => "plugins_zone", - "LABEL" => "ajxp_conf.99", - "DESCRIPTION" => "ajxp_conf.134", - "ICON" => "folder_development.png", - "LIST" => "listPlugins"), - "core_plugins" => array( - "AJXP_MIME" => "plugins_zone", - "LABEL" => "ajxp_conf.123", - "DESCRIPTION" => "ajxp_conf.135", - "ICON" => "folder_development.png", - "LIST" => "listPlugins"), - ) - ), - "admin" => array( - "LABEL" => "ajxp_conf.111", - "ICON" => "toggle_log.png", - "DESCRIPTION" => "ajxp_conf.141", - "CHILDREN" => array( - "logs" => array( - "LABEL" => "ajxp_conf.4", - "DESCRIPTION" => "ajxp_conf.142", - "ICON" => "toggle_log.png", - "LIST" => "listLogFiles"), - "diagnostic" => array( - "LABEL" => "ajxp_conf.5", - "DESCRIPTION" => "ajxp_conf.143", - "ICON" => "susehelpcenter.png", "LIST" => "printDiagnostic") - ) - ), - "developer" => array( - "LABEL" => "ajxp_conf.144", - "ICON" => "applications_engineering.png", - "DESCRIPTION" => "ajxp_conf.145", - "CHILDREN" => array( - "actions" => array( - "LABEL" => "ajxp_conf.146", - "DESCRIPTION" => "ajxp_conf.147", - "ICON" => "book.png", - "LIST" => "listActions"), - "hooks" => array( - "LABEL" => "ajxp_conf.148", - "DESCRIPTION" => "ajxp_conf.149", - "ICON" => "book.png", - "LIST" => "listHooks") - ) - ) - ); - - - protected function filterReservedRoles($key){ - return (strpos($key, "AJXP_GRP_/") === FALSE && strpos($key, "AJXP_USR_/") === FALSE); - } - - protected function getEditableParameters($withLabel = false){ - $currentUserIsGroupAdmin = (AuthService::getLoggedUser() != null && AuthService::getLoggedUser()->getGroupPath() != "/"); - $query = "//param|//global_param"; - if($currentUserIsGroupAdmin){ - $query = "//param[@scope]|//global_param[@scope]"; - } - - $nodes = AJXP_PluginsService::getInstance()->searchAllManifests($query, "node", false, true, true); - $actions = array(); - foreach ($nodes as $node) { - if($node->parentNode->nodeName != "server_settings") continue; - $parentPlugin = $node->parentNode->parentNode; - $pId = $parentPlugin->attributes->getNamedItem("id")->nodeValue; - if (empty($pId)) { - $pId = $parentPlugin->nodeName ."."; - if($pId == "ajxpdriver.") $pId = "access."; - $pId .= $parentPlugin->attributes->getNamedItem("name")->nodeValue; - } - if(!is_array($actions[$pId])) $actions[$pId] = array(); - $actionName = $node->attributes->getNamedItem("name")->nodeValue; - $attributes = array(); - for( $i = 0; $i < $node->attributes->length; $i ++){ - $att = $node->attributes->item($i); - $value = $att->nodeValue; - if(in_array($att->nodeName, array("choices", "description", "group", "label"))) { - $value = AJXP_XMLWriter::replaceAjxpXmlKeywords($value); - } - $attributes[$att->nodeName] = $value; - } - if($withLabel){ - $actions[$pId][$actionName] = array( - "parameter" => $actionName , - "label" => $attributes["label"], - "attributes" => $attributes - ); - }else{ - $actions[$pId][] = $actionName; - } - - } - foreach ($actions as $actPid => $actionGroup) { - ksort($actionGroup, SORT_STRING); - $actions[$actPid] = array(); - foreach ($actionGroup as $v) { - $actions[$actPid][] = $v; - } - } - return $actions; - } - - public function listAllActions($action, $httpVars, $fileVars) - { - parent::accessPreprocess($action, $httpVars, $fileVars); - $loggedUser = AuthService::getLoggedUser(); - if(AuthService::usersEnabled() && !$loggedUser->isAdmin()) return ; - switch ($action) { - //------------------------------------ - // BASIC LISTING - //------------------------------------ - case "list_all_repositories_json": - - $repositories = ConfService::getRepositoriesList("all", false); - $repoOut = array(); - foreach ($repositories as $repoObject) { - $repoOut[$repoObject->getId()] = $repoObject->getDisplay(); - } - HTMLWriter::charsetHeader("application/json"); - $mess = ConfService::getMessages(); - echo json_encode(array("LEGEND" => $mess["ajxp_conf.150"], "LIST" => $repoOut)); - - break; - - case "list_all_plugins_actions": - - $currentUserIsGroupAdmin = (AuthService::getLoggedUser() != null && AuthService::getLoggedUser()->getGroupPath() != "/"); - if($currentUserIsGroupAdmin){ - // Group admin : do not allow actions edition - HTMLWriter::charsetHeader("application/json"); - echo json_encode(array("LIST" => array(), "HAS_GROUPS" => true)); - return; - } - if(isSet($_SESSION["ALL_ACTIONS_CACHE"])){ - $actions = $_SESSION["ALL_ACTIONS_CACHE"]; - }else{ - - $nodes = AJXP_PluginsService::getInstance()->searchAllManifests("//action", "node", false, true, true); - $actions = array(); - foreach ($nodes as $node) { - $xPath = new DOMXPath($node->ownerDocument); - $proc = $xPath->query("processing", $node); - if(!$proc->length) continue; - $txt = $xPath->query("gui/@text", $node); - if ($txt->length) { - $messId = $txt->item(0)->nodeValue; - } else { - $messId = ""; - } - $parentPlugin = $node->parentNode->parentNode->parentNode; - $pId = $parentPlugin->attributes->getNamedItem("id")->nodeValue; - if (empty($pId)) { - $pId = $parentPlugin->nodeName ."."; - if($pId == "ajxpdriver.") $pId = "access."; - $pId .= $parentPlugin->attributes->getNamedItem("name")->nodeValue; - } - //echo($pId." : ". $node->attributes->getNamedItem("name")->nodeValue . " (".$messId.")
    "); - if(!is_array($actions[$pId])) $actions[$pId] = array(); - $actionName = $node->attributes->getNamedItem("name")->nodeValue; - $actions[$pId][$actionName] = array( "action" => $actionName , "label" => $messId); - - } - ksort($actions, SORT_STRING); - foreach ($actions as $actPid => $actionGroup) { - ksort($actionGroup, SORT_STRING); - $actions[$actPid] = array(); - foreach ($actionGroup as $v) { - $actions[$actPid][] = $v; - } - } - $_SESSION["ALL_ACTIONS_CACHE"] = $actions; - } - HTMLWriter::charsetHeader("application/json"); - echo json_encode(array("LIST" => $actions, "HAS_GROUPS" => true)); - break; - - case "list_all_plugins_parameters": - - if(isSet($_SESSION["ALL_PARAMS_CACHE"])){ - $actions = $_SESSION["ALL_PARAMS_CACHE"]; - }else{ - $actions = $this->getEditableParameters(true); - $_SESSION["ALL_PARAMS_CACHE"] = $actions; - } - HTMLWriter::charsetHeader("application/json"); - echo json_encode(array("LIST" => $actions, "HAS_GROUPS" => true)); - break; - - case "parameters_to_form_definitions" : - - $data = json_decode(SystemTextEncoding::magicDequote($httpVars["json_parameters"]), true); - AJXP_XMLWriter::header("standard_form"); - foreach ($data as $repoScope => $pluginsData) { - echo(""); - foreach ($pluginsData as $pluginId => $paramData) { - foreach ($paramData as $paramId => $paramValue) { - $query = "//param[@name='$paramId']|//global_param[@name='$paramId']"; - $nodes = AJXP_PluginsService::getInstance()->searchAllManifests($query, "node", false, true, true); - if(!count($nodes)) continue; - $n = $nodes[0]; - if ($n->attributes->getNamedItem("group") != null) { - $n->attributes->getNamedItem("group")->nodeValue = "$pluginId"; - } else { - $n->appendChild($n->ownerDocument->createAttribute("group")); - $n->attributes->getNamedItem("group")->nodeValue = "$pluginId"; - } - if(is_bool($paramValue)) $paramValue = ($paramValue ? "true" : "false"); - if ($n->attributes->getNamedItem("default") != null) { - $n->attributes->getNamedItem("default")->nodeValue = $paramValue; - } else { - $n->appendChild($n->ownerDocument->createAttribute("default")); - $n->attributes->getNamedItem("default")->nodeValue = $paramValue; - } - echo(AJXP_XMLWriter::replaceAjxpXmlKeywords($n->ownerDocument->saveXML($n))); - } - } - echo(""); - } - AJXP_XMLWriter::close("standard_form"); - break; - - default: - break; - } - } - - public function parseSpecificContributions(&$contribNode) - { - parent::parseSpecificContributions($contribNode); - if($contribNode->nodeName != "actions") return; - $currentUserIsGroupAdmin = (AuthService::getLoggedUser() != null && AuthService::getLoggedUser()->getGroupPath() != "/"); - if(!$currentUserIsGroupAdmin) return; - $actionXpath=new DOMXPath($contribNode->ownerDocument); - $publicUrlNodeList = $actionXpath->query('action[@name="create_repository"]/subMenu', $contribNode); - if ($publicUrlNodeList->length) { - $publicUrlNode = $publicUrlNodeList->item(0); - $publicUrlNode->parentNode->removeChild($publicUrlNode); - } - } - - public function preProcessBookmarkAction($action, &$httpVars, $fileVars) - { - if (isSet($httpVars["bm_action"]) && $httpVars["bm_action"] == "add_bookmark" && AuthService::usersEnabled()) { - $bmUser = AuthService::getLoggedUser(); - $bookmarks = $bmUser->getBookmarks(); - foreach ($bookmarks as $bm) { - if ($bm["PATH"] == $httpVars["bm_path"]) { - $httpVars["bm_action"] = "delete_bookmark"; - break; - } - } - } - - } - - public function recursiveSearchGroups($baseGroup, $term, $offset=-1, $limit=-1) - { - $groups = AuthService::listChildrenGroups($baseGroup); - foreach ($groups as $groupId => $groupLabel) { - - if (preg_match("/$term/i", $groupLabel) == TRUE ) { - $trimmedG = trim($baseGroup, "/"); - if(!empty($trimmedG)) $trimmedG .= "/"; - $nodeKey = "/data/users/".$trimmedG.ltrim($groupId,"/"); - $meta = array( - "icon" => "users-folder.png", - "ajxp_mime" => "group_editable" - ); - if(in_array($nodeKey, $this->currentBookmarks)) $meta = array_merge($meta, array("ajxp_bookmarked" => "true", "overlay_icon" => "bookmark.png")); - echo AJXP_XMLWriter::renderNode($nodeKey, $groupLabel, false, $meta, true, false); - } - $this->recursiveSearchGroups(rtrim($baseGroup, "/")."/".ltrim($groupId, "/"), $term); - - } - - $users = AuthService::listUsers($baseGroup, $term, $offset, $limit); - foreach ($users as $userId => $userObject) { - $gPath = $userObject->getGroupPath(); - $realGroup = AuthService::filterBaseGroup(AuthService::getLoggedUser()->getGroupPath()); - if(strlen($realGroup) > 1 && strpos($gPath, $realGroup) === 0){ - $gPath = substr($gPath, strlen($realGroup)); - } - $trimmedG = trim($gPath, "/"); - if(!empty($trimmedG)) $trimmedG .= "/"; - - $nodeKey = "/data/users/".$trimmedG.$userId; - $meta = array( - "icon" => "user.png", - "ajxp_mime" => "user_editable" - ); - if(in_array($nodeKey, $this->currentBookmarks)) $meta = array_merge($meta, array("ajxp_bookmarked" => "true", "overlay_icon" => "bookmark.png")); - $userDisplayName = ConfService::getUserPersonalParameter("USER_DISPLAY_NAME", $userObject, "core.conf", $userId); - echo AJXP_XMLWriter::renderNode($nodeKey, $userDisplayName, true, $meta, true, false); - } - - } - - - public function searchAction($action, $httpVars, $fileVars) - { - if(! AJXP_Utils::decodeSecureMagic($httpVars["dir"]) == "/data/users") return; - $query = AJXP_Utils::decodeSecureMagic($httpVars["query"]); - $limit = $offset = -1; - if(isSet($httpVars["limit"])) $limit = intval(AJXP_Utils::sanitize($httpVars["limit"], AJXP_SANITIZE_ALPHANUM)); - if(isSet($httpVars["offset"])) $offset = intval(AJXP_Utils::sanitize($httpVars["offset"], AJXP_SANITIZE_ALPHANUM)); - AJXP_XMLWriter::header(); - $this->recursiveSearchGroups("/", $query, $offset, $limit); - AJXP_XMLWriter::close(); - - } - - protected function getMainTree(){ - $rootNodes = $this->rootNodes; - if (AuthService::getLoggedUser() != null && AuthService::getLoggedUser()->getGroupPath() != "/") { - // Group Admin - unset($rootNodes["config"]); - unset($rootNodes["admin"]); - unset($rootNodes["developer"]); - } - AJXP_Controller::applyHook("ajxp_conf.list_config_nodes", array(&$rootNodes)); - return $rootNodes; - } - - protected function renderNode($path, $data, $messages){ - if(isSet($messages[$data["LABEL"]])) $data["LABEL"] = $messages[$data["LABEL"]]; - if(isSet($messages[$data["DESCRIPTION"]])) $data["DESCRIPTION"] = $messages[$data["DESCRIPTION"]]; - $nodeLabel = $data["LABEL"]; - $attributes = array( - "description" => $data["DESCRIPTION"], - "icon" => $data["ICON"] - ); - if(in_array($path, $this->currentBookmarks)) { - $attributes["ajxp_bookmarked"]="true"; - $attributes["overlay_icon"] = "bookmark.png"; - } - if(basename($path) == "users") { - $attributes["remote_indexation"] = "admin_search"; - } - if(isSet($data["AJXP_MIME"])) { - $attributes["ajxp_mime"] = $data["AJXP_MIME"]; - } - if(isSet($data["METADATA"]) && is_array($data["METADATA"])){ - $attributes = array_merge($attributes, $data["METADATA"]); - } - $hasChildren = isSet($data["CHILDREN"]); - AJXP_XMLWriter::renderNode($path, $nodeLabel, false, $attributes, false); - if($hasChildren){ - foreach($data["CHILDREN"] as $cKey => $cData){ - $this->renderNode($path."/".$cKey, $cData, $messages); - } - } - AJXP_XMLWriter::close(); - } - - public function switchAction($action, $httpVars, $fileVars) - { - parent::accessPreprocess($action, $httpVars, $fileVars); - $loggedUser = AuthService::getLoggedUser(); - if(AuthService::usersEnabled() && !$loggedUser->isAdmin()) return ; - if (AuthService::usersEnabled()) { - $currentBookmarks = AuthService::getLoggedUser()->getBookmarks(); - // FLATTEN - foreach ($currentBookmarks as $bm) { - $this->currentBookmarks[] = $bm["PATH"]; - } - } - - if ($action == "edit") { - if (isSet($httpVars["sub_action"])) { - $action = $httpVars["sub_action"]; - } - } - $mess = ConfService::getMessages(); - $currentUserIsGroupAdmin = (AuthService::getLoggedUser() != null && AuthService::getLoggedUser()->getGroupPath() != "/"); - if ($currentUserIsGroupAdmin && ConfService::getAuthDriverImpl()->isAjxpAdmin(AuthService::getLoggedUser()->getId())) { - $currentUserIsGroupAdmin = false; - } - $currentAdminBasePath = "/"; - if (AuthService::getLoggedUser()!=null && AuthService::getLoggedUser()->getGroupPath()!=null) { - $currentAdminBasePath = AuthService::getLoggedUser()->getGroupPath(); - } - - - switch ($action) { - //------------------------------------ - // BASIC LISTING - //------------------------------------ - case "ls": - - $rootNodes = $this->getMainTree(); - $rootAttributes = array(); - if(isSet($rootNodes["__metadata__"])){ - $rootAttributes = $rootNodes["__metadata__"]; - unset($rootNodes["__metadata__"]); - } - $parentName = ""; - $dir = trim(AJXP_Utils::decodeSecureMagic((isset($httpVars["dir"])?$httpVars["dir"]:"")), " /"); - if ($dir != "") { - $hash = null; - if (strstr(urldecode($dir), "#") !== false) { - list($dir, $hash) = explode("#", urldecode($dir)); - } - $splits = explode("/", $dir); - $root = array_shift($splits); - if (count($splits)) { - $returnNodes = false; - if (isSet($httpVars["file"])) { - $returnNodes = true; - } - $child = $splits[0]; - if (isSet($rootNodes[$root]["CHILDREN"][$child])) { - $atts = array(); - if ($child == "users") { - $atts["remote_indexation"] = "admin_search"; - } - $childData = $rootNodes[$root]["CHILDREN"][$child]; - $callback = $childData["LIST"]; - if(isSet($childData["ALIAS"])){ - $reSplits = explode("/", ltrim($childData["ALIAS"], "/")); - $root = array_shift($reSplits); - // additional part? - array_shift($splits); - foreach($splits as $vS) $reSplits[] = $vS; - $splits = $reSplits; - } - if (is_string($callback) && method_exists($this, $callback)) { - if(!$returnNodes) AJXP_XMLWriter::header("tree", $atts); - $res = call_user_func(array($this, $callback), implode("/", $splits), $root, $hash, $returnNodes, isSet($httpVars["file"])?$httpVars["file"]:'', "/".$dir, $httpVars); - if(!$returnNodes) AJXP_XMLWriter::close(); - } else if (is_array($callback)) { - $res = call_user_func($callback, implode("/", $splits), $root, $hash, $returnNodes, isSet($httpVars["file"])?$httpVars["file"]:'', "/".$dir, $httpVars); - } - if ($returnNodes) { - AJXP_XMLWriter::header("tree", $atts); - if (isSet($res["/".$dir."/".$httpVars["file"]])) { - print $res["/".$dir."/".$httpVars["file"]]; - } - AJXP_XMLWriter::close(); - } - return; - } - } else { - $parentName = "/".$root."/"; - $nodes = $rootNodes[$root]["CHILDREN"]; - } - } else { - $parentName = "/"; - $nodes = $rootNodes; - if($currentUserIsGroupAdmin){ - $rootAttributes["group_admin"] = "1"; - } - } - if (isSet($httpVars["file"])) { - $parentName = $httpVars["dir"]."/"; - $nodes = array(basename($httpVars["file"]) => array("LABEL" => basename($httpVars["file"]))); - } - if (isSet($nodes)) { - AJXP_XMLWriter::header("tree", $rootAttributes); - if(!isSet($httpVars["file"])) AJXP_XMLWriter::sendFilesListComponentConfig(''); - foreach ($nodes as $key => $data) { - $this->renderNode($parentName.$key, $data, $mess); - } - AJXP_XMLWriter::close(); - - } - - break; - - case "stat" : - - header("Content-type:application/json"); - print '{"mode":true}'; - return; - - break; - - case "clear_plugins_cache": - AJXP_XMLWriter::header(); - ConfService::clearAllCaches(); - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.".(AJXP_SKIP_CACHE?"132":"131")], null); - AJXP_XMLWriter::reloadDataNode(); - AJXP_XMLWriter::close(); - break; - - - case "create_group": - - if (isSet($httpVars["group_path"])) { - $basePath = AJXP_Utils::forwardSlashDirname($httpVars["group_path"]); - if(empty($basePath)) $basePath = "/"; - $gName = AJXP_Utils::sanitize(AJXP_Utils::decodeSecureMagic(basename($httpVars["group_path"])), AJXP_SANITIZE_ALPHANUM); - } else { - $basePath = substr($httpVars["dir"], strlen("/data/users")); - $gName = AJXP_Utils::sanitize(SystemTextEncoding::magicDequote($httpVars["group_name"]), AJXP_SANITIZE_ALPHANUM); - } - $gLabel = AJXP_Utils::decodeSecureMagic($httpVars["group_label"]); - AuthService::createGroup($basePath, $gName, $gLabel); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.160"], null); - AJXP_XMLWriter::reloadDataNode(); - AJXP_XMLWriter::close(); - - break; - - case "create_role": - $roleId = AJXP_Utils::sanitize(SystemTextEncoding::magicDequote($httpVars["role_id"]), AJXP_SANITIZE_HTML_STRICT); - if (!strlen($roleId)) { - throw new Exception($mess[349]); - } - if (AuthService::getRole($roleId) !== false) { - throw new Exception($mess["ajxp_conf.65"]); - } - $r = new AJXP_Role($roleId); - if (AuthService::getLoggedUser()!=null && AuthService::getLoggedUser()->getGroupPath()!=null) { - $r->setGroupPath(AuthService::getLoggedUser()->getGroupPath()); - } - AuthService::updateRole($r); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.66"], null); - AJXP_XMLWriter::reloadDataNode("", $httpVars["role_id"]); - AJXP_XMLWriter::close(); - break; - - case "edit_role" : - $roleId = SystemTextEncoding::magicDequote($httpVars["role_id"]); - $roleGroup = false; - $userObject = null; - $groupLabel = null; - if (strpos($roleId, "AJXP_GRP_") === 0) { - $groupPath = substr($roleId, strlen("AJXP_GRP_")); - $filteredGroupPath = AuthService::filterBaseGroup($groupPath); - if($filteredGroupPath == "/"){ - $roleId = "AJXP_GRP_/"; - $groupLabel = $mess["ajxp_conf.151"]; - $roleGroup = true; - }else{ - $groups = AuthService::listChildrenGroups(AJXP_Utils::forwardSlashDirname($groupPath)); - $key = "/".basename($groupPath); - if (!array_key_exists($key, $groups)) { - throw new Exception("Cannot find group with this id!"); - } - $roleId = "AJXP_GRP_".$filteredGroupPath; - $groupLabel = $groups[$key]; - $roleGroup = true; - } - } - if (strpos($roleId, "AJXP_USR_") === 0) { - $usrId = str_replace("AJXP_USR_/", "", $roleId); - $userObject = ConfService::getConfStorageImpl()->createUserObject($usrId); - if(!AuthService::canAdministrate($userObject)){ - throw new Exception("Cant find user!"); - } - $role = $userObject->personalRole; - } else { - $role = AuthService::getRole($roleId, $roleGroup); - } - if ($role === false) { - throw new Exception("Cant find role! "); - } - if (isSet($httpVars["format"]) && $httpVars["format"] == "json") { - HTMLWriter::charsetHeader("application/json"); - $roleData = $role->getDataArray(true); - $allReps = ConfService::getRepositoriesList("all", false); - $sharedRepos = array(); - if(isSet($userObject)){ - // Add User shared Repositories as well - $acls = $userObject->mergedRole->listAcls(); - if(count($acls)) { - $sharedRepos = ConfService::getConfStorageImpl()->listRepositoriesWithCriteria(array( - "uuid" => array_keys($acls), - "parent_uuid" => AJXP_FILTER_NOT_EMPTY, - "owner_user_id" => AJXP_FILTER_NOT_EMPTY - )); - $allReps = array_merge($allReps, $sharedRepos); - } - } - - $repos = array(); - $repoDetailed = array(); - // USER - foreach ($allReps as $repositoryId => $repositoryObject) { - if (!empty($userObject) && - ( - !AuthService::canAssign($repositoryObject, $userObject) || $repositoryObject->isTemplate - || ($repositoryObject->getAccessType()=="ajxp_conf" && !$userObject->isAdmin()) - || ($repositoryObject->getUniqueUser() != null && $repositoryObject->getUniqueUser() != $userObject->getId()) - ) - ){ - continue; - }else if(empty($userObject) && - ( - !AuthService::canAdministrate($repositoryObject) || $repositoryObject->isTemplate - )){ - continue; - } - $meta = array(); - try{ - if($repositoryObject->getOption("META_SOURCES") != null){ - $meta = array_keys($repositoryObject->getOption("META_SOURCES")); - } - }catch(Exception $e){ - if(isSet($sharedRepos[$repositoryId])) unset($sharedRepos[$repositoryId]); - $this->logError("Invalid Share", "Repository $repositoryId has no more parent. Should be deleted."); - continue; - } - $repoDetailed[$repositoryId] = array( - "label" => SystemTextEncoding::toUTF8($repositoryObject->getDisplay()), - "driver" => $repositoryObject->getAccessType(), - "scope" => $repositoryObject->securityScope(), - "meta" => $meta - ); - - if(array_key_exists($repositoryId, $sharedRepos)){ - $sharedRepos[$repositoryId] = SystemTextEncoding::toUTF8($repositoryObject->getDisplay()); - $repoParentLabel = $repoParentId = $repositoryObject->getParentId(); - $repoOwnerId = $repositoryObject->getOwner(); - if(isSet($allReps[$repoParentId])){ - $repoParentLabel = SystemTextEncoding::toUTF8($allReps[$repoParentId]->getDisplay()); - } - $repoOwnerLabel = ConfService::getUserPersonalParameter("USER_DISPLAY_NAME", $repoOwnerId, "core.conf", $repoOwnerId); - $repoDetailed[$repositoryId]["share"] = array( - "parent_user" => $repoOwnerId, - "parent_user_label" => $repoOwnerLabel, - "parent_repository" => $repoParentId, - "parent_repository_label" => $repoParentLabel - ); - }else{ - $repos[$repositoryId] = SystemTextEncoding::toUTF8($repositoryObject->getDisplay()); - } - - } - // Make sure it's utf8 - $data = array( - "ROLE" => $roleData, - "ALL" => array( - "PLUGINS_SCOPES" => array( - "GLOBAL_TYPES" => array("conf", "auth", "authfront", "log", "mq", "notifications", "gui", "sec"), - "GLOBAL_PLUGINS" => array("action.avatar", "action.disclaimer", "action.scheduler", "action.skeleton", "action.updater") - ), - "REPOSITORIES" => $repos, - "SHARED_REPOSITORIES" => $sharedRepos, - "REPOSITORIES_DETAILS" => $repoDetailed, - "PROFILES" => array("standard|".$mess["ajxp_conf.156"],"admin|".$mess["ajxp_conf.157"],"shared|".$mess["ajxp_conf.158"],"guest|".$mess["ajxp_conf.159"]) - ) - ); - if (isSet($userObject)) { - $data["USER"] = array(); - $data["USER"]["LOCK"] = $userObject->getLock(); - $data["USER"]["PROFILE"] = $userObject->getProfile(); - $data["USER"]["ROLES"] = array_keys($userObject->getRoles()); - $rolesList = AuthService::getRolesList(array(), true); - $data["ALL"]["ROLES"] = array_keys($rolesList); - $data["ALL"]["ROLES_DETAILS"] = array(); - foreach($rolesList as $rId => $rObj){ - $data["ALL"]["ROLES_DETAILS"][$rId] = array("label" => $rObj->getLabel(), "sticky" => $rObj->alwaysOverrides()); - } - if (isSet($userObject->parentRole)) { - $data["PARENT_ROLE"] = $userObject->parentRole->getDataArray(); - } - } else if (isSet($groupPath)) { - $data["GROUP"] = array("PATH" => $groupPath, "LABEL" => $groupLabel); - if($roleId != "AJXP_GRP_/"){ - $parentGroupRoles = array(); - $parentPath = AJXP_Utils::forwardSlashDirname($roleId); - while($parentPath != "AJXP_GRP_"){ - $parentRole = AuthService::getRole($parentPath); - if($parentRole != null) { - array_unshift($parentGroupRoles, $parentRole); - } - $parentPath = AJXP_Utils::forwardSlashDirname($parentPath); - } - $rootGroup = AuthService::getRole("AJXP_GRP_/"); - if($rootGroup != null) array_unshift($parentGroupRoles, $rootGroup); - if(count($parentGroupRoles)){ - $parentRole = clone array_shift($parentGroupRoles); - foreach($parentGroupRoles as $pgRole){ - $parentRole = $pgRole->override($parentRole); - } - $data["PARENT_ROLE"] = $parentRole->getDataArray(); - } - } - } - - - $scope = "role"; - if($roleGroup) { - $scope = "group"; - if($roleId == "AJXP_GRP_/") $scope = "role"; - } - else if(isSet($userObject)) $scope = "user"; - $data["SCOPE_PARAMS"] = array(); - $nodes = AJXP_PluginsService::getInstance()->searchAllManifests("//param[contains(@scope,'".$scope."')]|//global_param[contains(@scope,'".$scope."')]", "node", false, true, true); - foreach ($nodes as $node) { - $pId = $node->parentNode->parentNode->attributes->getNamedItem("id")->nodeValue; - $origName = $node->attributes->getNamedItem("name")->nodeValue; - if($roleId == "AJXP_GRP_/" && strpos($origName, "ROLE_") ===0 ) continue; - $node->attributes->getNamedItem("name")->nodeValue = "AJXP_REPO_SCOPE_ALL/".$pId."/".$origName; - $nArr = array(); - foreach ($node->attributes as $attrib) { - $nArr[$attrib->nodeName] = AJXP_XMLWriter::replaceAjxpXmlKeywords($attrib->nodeValue); - } - $data["SCOPE_PARAMS"][] = $nArr; - } - - echo json_encode($data); - } - break; - - case "post_json_role" : - - $roleId = SystemTextEncoding::magicDequote($httpVars["role_id"]); - $roleGroup = false; - $userObject = $usrId = $filteredGroupPath = null; - if (strpos($roleId, "AJXP_GRP_") === 0) { - $groupPath = substr($roleId, strlen("AJXP_GRP_")); - $filteredGroupPath = AuthService::filterBaseGroup($groupPath); - $roleId = "AJXP_GRP_".$filteredGroupPath; - if($roleId != "AJXP_GRP_/"){ - $groups = AuthService::listChildrenGroups(AJXP_Utils::forwardSlashDirname($groupPath)); - $key = "/".basename($groupPath); - if (!array_key_exists($key, $groups)) { - throw new Exception("Cannot find group with this id!"); - } - $groupLabel = $groups[$key]; - }else{ - $groupLabel = $mess["ajxp_conf.151"]; - } - $roleGroup = true; - } - if (strpos($roleId, "AJXP_USR_") === 0) { - $usrId = str_replace("AJXP_USR_/", "", $roleId); - $userObject = ConfService::getConfStorageImpl()->createUserObject($usrId); - if(!AuthService::canAdministrate($userObject)){ - throw new Exception("Cannot post role for user ".$usrId); - } - $originalRole = $userObject->personalRole; - } else { - // second param = create if not exists. - $originalRole = AuthService::getRole($roleId, $roleGroup); - } - if ($originalRole === false) { - throw new Exception("Cant find role! "); - } - - $jsonData = SystemTextEncoding::magicDequote($httpVars["json_data"]); - $data = json_decode($jsonData, true); - $roleData = $data["ROLE"]; - $binariesContext = array(); - if (isset($userObject)) { - $binariesContext = array("USER" => $userObject->getId()); - } - if(isSet($data["FORMS"])){ - $forms = $data["FORMS"]; - foreach ($forms as $repoScope => $plugData) { - foreach ($plugData as $plugId => $formsData) { - $parsed = array(); - AJXP_Utils::parseStandardFormParameters( - $formsData, - $parsed, - ($userObject!=null?$usrId:null), - "ROLE_PARAM_", - $binariesContext, - AJXP_Role::$cypheredPassPrefix - ); - $roleData["PARAMETERS"][$repoScope][$plugId] = $parsed; - } - } - }else{ - AJXP_Utils::filterFormElementsFromMeta( - $data["METADATA"], - $roleData, - ($userObject!=null?$usrId:null), - $binariesContext, - AJXP_Role::$cypheredPassPrefix - ); - } - $existingParameters = $originalRole->listParameters(true); - $this->mergeExistingParameters($roleData["PARAMETERS"], $existingParameters); - if (isSet($userObject) && isSet($data["USER"]) && isSet($data["USER"]["PROFILE"])) { - $userObject->setAdmin(($data["USER"]["PROFILE"] == "admin")); - $userObject->setProfile($data["USER"]["PROFILE"]); - } - if (isSet($data["GROUP_LABEL"]) && isSet($groupLabel) && $groupLabel != $data["GROUP_LABEL"]) { - ConfService::getConfStorageImpl()->relabelGroup($filteredGroupPath, $data["GROUP_LABEL"]); - } - - if($currentUserIsGroupAdmin){ - // FILTER DATA FOR GROUP ADMINS - $params = $this->getEditableParameters(false); - foreach($roleData["PARAMETERS"] as $scope => &$plugsParameters){ - foreach($plugsParameters as $paramPlugin => &$parameters){ - foreach($parameters as $pName => $pValue){ - if(!isSet($params[$paramPlugin]) || !in_array($pName, $params[$paramPlugin])){ - unset($parameters[$pName]); - } - } - if(!count($parameters)){ - unset($plugsParameters[$paramPlugin]); - } - } - if(!count($plugsParameters)){ - unset($roleData["PARAMETERS"][$scope]); - } - } - // Remerge from parent - $roleData["PARAMETERS"] = $originalRole->array_merge_recursive2($originalRole->listParameters(), $roleData["PARAMETERS"]); - // Changing Actions is not allowed - $roleData["ACTIONS"] = $originalRole->listActionsStates(); - } - - if(isSet($roleData["MASKS"])){ - foreach($roleData["MASKS"] as $repoId => $serialMask){ - $roleData["MASKS"][$repoId] = new AJXP_PermissionMask($serialMask); - } - } - - try { - $originalRole->bunchUpdate($roleData); - if (isSet($userObject)) { - $userObject->personalRole = $originalRole; - $userObject->save("superuser"); - } else { - AuthService::updateRole($originalRole); - } - // Reload Role - $savedValue = AuthService::getRole($originalRole->getId()); - $output = array("ROLE" => $savedValue->getDataArray(true), "SUCCESS" => true); - } catch (Exception $e) { - $output = array("ERROR" => $e->getMessage()); - } - HTMLWriter::charsetHeader("application/json"); - echo(json_encode($output)); - - break; - - - case "user_set_lock" : - - $userId = AJXP_Utils::decodeSecureMagic($httpVars["user_id"]); - $lock = ($httpVars["lock"] == "true" ? true : false); - $lockType = $httpVars["lock_type"]; - if (AuthService::userExists($userId)) { - $userObject = ConfService::getConfStorageImpl()->createUserObject($userId); - if(!AuthService::canAdministrate($userObject)){ - throw new Exception("Cannot update user data for ".$userId); - } - if ($lock) { - $userObject->setLock($lockType); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage("Successfully set lock on user ($lockType)", null); - AJXP_XMLWriter::close(); - } else { - $userObject->removeLock(); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage("Successfully unlocked user", null); - AJXP_XMLWriter::close(); - } - $userObject->save("superuser"); - } - - break; - - case "create_user" : - - if (!isset($httpVars["new_user_login"]) || $httpVars["new_user_login"] == "" ||!isset($httpVars["new_user_pwd"]) || $httpVars["new_user_pwd"] == "") { - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage(null, $mess["ajxp_conf.61"]); - AJXP_XMLWriter::close(); - return; - } - $original_login = SystemTextEncoding::magicDequote($httpVars["new_user_login"]); - $new_user_login = AJXP_Utils::sanitize($original_login, AJXP_SANITIZE_EMAILCHARS); - if($original_login != $new_user_login){ - throw new Exception(str_replace("%s", $new_user_login, $mess["ajxp_conf.127"])); - } - if (AuthService::userExists($new_user_login, "w") || AuthService::isReservedUserId($new_user_login)) { - throw new Exception($mess["ajxp_conf.43"]); - } - - AuthService::createUser($new_user_login, $httpVars["new_user_pwd"]); - $confStorage = ConfService::getConfStorageImpl(); - $newUser = $confStorage->createUserObject($new_user_login); - if (!empty($httpVars["group_path"])) { - $newUser->setGroupPath(rtrim($currentAdminBasePath, "/")."/".ltrim($httpVars["group_path"], "/")); - } else { - $newUser->setGroupPath($currentAdminBasePath); - } - - $newUser->save("superuser"); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.44"], null); - AJXP_XMLWriter::reloadDataNode("", $new_user_login); - AJXP_XMLWriter::close(); - - break; - - case "change_admin_right" : - $userId = $httpVars["user_id"]; - if (!AuthService::userExists($userId)) { - throw new Exception("Invalid user id!"); - } - $confStorage = ConfService::getConfStorageImpl(); - $user = $confStorage->createUserObject($userId); - if(!AuthService::canAdministrate($user)){ - throw new Exception("Cannot update user with id ".$userId); - } - $user->setAdmin(($httpVars["right_value"]=="1"?true:false)); - $user->save("superuser"); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.45"].$httpVars["user_id"], null); - AJXP_XMLWriter::reloadDataNode(); - AJXP_XMLWriter::close(); - - break; - - case "role_update_right" : - if(!isSet($httpVars["role_id"]) - || !isSet($httpVars["repository_id"]) - || !isSet($httpVars["right"])) - { - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage(null, $mess["ajxp_conf.61"]); - AJXP_XMLWriter::close(); - break; - } - $rId = AJXP_Utils::sanitize($httpVars["role_id"]); - $role = AuthService::getRole($rId); - if($role === false){ - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage(null, $mess["ajxp_conf.61"]."(".$rId.")"); - AJXP_XMLWriter::close(); - break; - } - $role->setAcl(AJXP_Utils::sanitize($httpVars["repository_id"], AJXP_SANITIZE_ALPHANUM), AJXP_Utils::sanitize($httpVars["right"], AJXP_SANITIZE_ALPHANUM)); - AuthService::updateRole($role); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.46"].$httpVars["role_id"], null); - AJXP_XMLWriter::close(); - - break; - - case "user_update_right" : - if(!isSet($httpVars["user_id"]) - || !isSet($httpVars["repository_id"]) - || !isSet($httpVars["right"]) - || !AuthService::userExists($httpVars["user_id"])) - { - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage(null, $mess["ajxp_conf.61"]); - print(""); - AJXP_XMLWriter::close(); - return; - } - $confStorage = ConfService::getConfStorageImpl(); - $userId = AJXP_Utils::sanitize($httpVars["user_id"], AJXP_SANITIZE_EMAILCHARS); - $user = $confStorage->createUserObject($userId); - if(!AuthService::canAdministrate($user)){ - throw new Exception("Cannot update user with id ".$userId); - } - $user->personalRole->setAcl(AJXP_Utils::sanitize($httpVars["repository_id"], AJXP_SANITIZE_ALPHANUM), AJXP_Utils::sanitize($httpVars["right"], AJXP_SANITIZE_ALPHANUM)); - $user->save(); - $loggedUser = AuthService::getLoggedUser(); - if ($loggedUser->getId() == $user->getId()) { - AuthService::updateUser($user); - } - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.46"].$httpVars["user_id"], null); - print("canRead($httpVars["repository_id"])."\" write=\"".$user->canWrite($httpVars["repository_id"])."\"/>"); - AJXP_XMLWriter::reloadRepositoryList(); - AJXP_XMLWriter::close(); - return ; - break; - - case "user_update_group": - - $userSelection = new UserSelection(); - $userSelection->initFromHttpVars($httpVars); - $dir = $httpVars["dir"]; - $dest = $httpVars["dest"]; - if (isSet($httpVars["group_path"])) { - // API Case - $groupPath = $httpVars["group_path"]; - } else { - if (strpos($dir, "/data/users",0)!==0 || strpos($dest, "/data/users",0)!==0) { - break; - } - $groupPath = substr($dest, strlen("/data/users")); - } - - $confStorage = ConfService::getConfStorageImpl(); - $userId = null; - $usersMoved = array(); - - if (!empty($groupPath)) { - $targetPath = rtrim($currentAdminBasePath, "/")."/".ltrim($groupPath, "/"); - } else { - $targetPath = $currentAdminBasePath; - } - - foreach ($userSelection->getFiles() as $selectedUser) { - $userId = basename($selectedUser); - if (!AuthService::userExists($userId)) { - continue; - } - $user = $confStorage->createUserObject($userId); - if( ! AuthService::canAdministrate($user) ){ - continue; - } - $user->setGroupPath($targetPath, true); - $user->save("superuser"); - $usersMoved[] = $user->getId(); - } - AJXP_XMLWriter::header(); - if(count($usersMoved)){ - AJXP_XMLWriter::sendMessage(count($usersMoved)." user(s) successfully moved to ".$targetPath, null); - AJXP_XMLWriter::reloadDataNode($dest, $userId); - AJXP_XMLWriter::reloadDataNode(); - }else{ - AJXP_XMLWriter::sendMessage(null, "No users moved, there must have been something wrong."); - } - AJXP_XMLWriter::close(); - - break; - - case "user_add_role" : - case "user_delete_role": - - if (!isSet($httpVars["user_id"]) || !isSet($httpVars["role_id"]) || !AuthService::userExists($httpVars["user_id"]) || !AuthService::getRole($httpVars["role_id"])) { - throw new Exception($mess["ajxp_conf.61"]); - } - if ($action == "user_add_role") { - $act = "add"; - $messId = "73"; - } else { - $act = "remove"; - $messId = "74"; - } - $this->updateUserRole(AJXP_Utils::sanitize($httpVars["user_id"], AJXP_SANITIZE_EMAILCHARS), $httpVars["role_id"], $act); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.".$messId].$httpVars["user_id"], null); - AJXP_XMLWriter::close(); - - break; - - case "user_reorder_roles": - - if (!isSet($httpVars["user_id"]) || !AuthService::userExists($httpVars["user_id"]) || !isSet($httpVars["roles"])) { - throw new Exception($mess["ajxp_conf.61"]); - } - $roles = json_decode($httpVars["roles"], true); - $userId = AJXP_Utils::sanitize($httpVars["user_id"], AJXP_SANITIZE_EMAILCHARS); - $confStorage = ConfService::getConfStorageImpl(); - $user = $confStorage->createUserObject($userId); - if(!AuthService::canAdministrate($user)){ - throw new Exception("Cannot update user data for ".$userId); - } - $user->updateRolesOrder($roles); - $user->save("superuser"); - $loggedUser = AuthService::getLoggedUser(); - if ($loggedUser->getId() == $user->getId()) { - AuthService::updateUser($user); - } - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage("Roles reordered for user ".$httpVars["user_id"], null); - AJXP_XMLWriter::close(); - - break; - - case "users_bulk_update_roles": - - $data = json_decode($httpVars["json_data"], true); - $userIds = $data["users"]; - $rolesOperations = $data["roles"]; - foreach($userIds as $userId){ - $userId = AJXP_Utils::sanitize($userId, AJXP_SANITIZE_EMAILCHARS); - if(!AuthService::userExists($userId)) continue; - $userObject = ConfService::getConfStorageImpl()->createUserObject($userId); - if(!AuthService::canAdministrate($userObject)) continue; - foreach($rolesOperations as $addOrRemove => $roles){ - if(!in_array($addOrRemove, array("add", "remove"))) { - continue; - } - foreach($roles as $roleId){ - if(strpos($roleId, "AJXP_USR_/") === 0 || strpos($roleId,"AJXP_GRP_/") === 0){ - continue; - } - $roleId = AJXP_Utils::sanitize($roleId, AJXP_SANITIZE_FILENAME); - if ($addOrRemove == "add") { - $roleObject = AuthService::getRole($roleId); - $userObject->addRole($roleObject); - } else { - $userObject->removeRole($roleId); - } - } - } - $userObject->save("superuser"); - $loggedUser = AuthService::getLoggedUser(); - if ($loggedUser->getId() == $userObject->getId()) { - AuthService::updateUser($userObject); - } - } - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage("Successfully updated roles", null); - AJXP_XMLWriter::close(); - - break; - - case "user_update_role" : - - $confStorage = ConfService::getConfStorageImpl(); - $selection = new UserSelection(); - $selection->initFromHttpVars($httpVars); - $files = $selection->getFiles(); - $detectedRoles = array(); - $roleId = null; - - if (isSet($httpVars["role_id"]) && isset($httpVars["update_role_action"])) { - $update = $httpVars["update_role_action"]; - $roleId = $httpVars["role_id"]; - if (AuthService::getRole($roleId) === false) { - throw new Exception("Invalid role id"); - } - } - foreach ($files as $index => $file) { - $userId = basename($file); - if (isSet($update)) { - $userObject = $this->updateUserRole($userId, $roleId, $update); - } else { - $userObject = $confStorage->createUserObject($userId); - if(!AuthService::canAdministrate($userObject)){ - continue; - } - } - if ($userObject->hasParent()) { - unset($files[$index]); - continue; - } - $userRoles = $userObject->getRoles(); - foreach ($userRoles as $roleIndex => $bool) { - if(!isSet($detectedRoles[$roleIndex])) $detectedRoles[$roleIndex] = 0; - if($bool === true) $detectedRoles[$roleIndex] ++; - } - } - $count = count($files); - AJXP_XMLWriter::header("admin_data"); - print(""); - foreach ($detectedRoles as $roleId => $roleCount) { - if($roleCount < $count) continue; - print(""); - } - print(""); - print(""); - foreach (AuthService::getRolesList(array(), !$this->listSpecialRoles) as $roleId => $roleObject) { - print(""); - } - print(""); - AJXP_XMLWriter::close("admin_data"); - - break; - - case "save_custom_user_params" : - $userId = AJXP_Utils::sanitize($httpVars["user_id"], AJXP_SANITIZE_EMAILCHARS); - if ($userId == $loggedUser->getId()) { - $user = $loggedUser; - } else { - $confStorage = ConfService::getConfStorageImpl(); - $user = $confStorage->createUserObject($userId); - } - if(!AuthService::canAdministrate($user)){ - throw new Exception("Cannot update user with id ".$userId); - } - - $custom = $user->getPref("CUSTOM_PARAMS"); - if(!is_array($custom)) $custom = array(); - - $options = $custom; - $this->parseParameters($httpVars, $options, $userId, false, $custom); - $custom = $options; - $user->setPref("CUSTOM_PARAMS", $custom); - $user->save(); - - if ($loggedUser->getId() == $user->getId()) { - AuthService::updateUser($user); - } - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.47"].$httpVars["user_id"], null); - AJXP_XMLWriter::close(); - - break; - - case "save_repository_user_params" : - $userId = AJXP_Utils::sanitize($httpVars["user_id"], AJXP_SANITIZE_EMAILCHARS); - if ($userId == $loggedUser->getId()) { - $user = $loggedUser; - } else { - $confStorage = ConfService::getConfStorageImpl(); - $user = $confStorage->createUserObject($userId); - } - if(!AuthService::canAdministrate($user)){ - throw new Exception("Cannot update user with id ".$userId); - } - - $wallet = $user->getPref("AJXP_WALLET"); - if(!is_array($wallet)) $wallet = array(); - $repoID = $httpVars["repository_id"]; - if (!array_key_exists($repoID, $wallet)) { - $wallet[$repoID] = array(); - } - $options = $wallet[$repoID]; - $existing = $options; - $this->parseParameters($httpVars, $options, $userId, false, $existing); - $wallet[$repoID] = $options; - $user->setPref("AJXP_WALLET", $wallet); - $user->save(); - - if ($loggedUser->getId() == $user->getId()) { - AuthService::updateUser($user); - } - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.47"].$httpVars["user_id"], null); - AJXP_XMLWriter::close(); - - break; - - case "update_user_pwd" : - if (!isSet($httpVars["user_id"]) || !isSet($httpVars["user_pwd"]) || !AuthService::userExists($httpVars["user_id"]) || trim($httpVars["user_pwd"]) == "") { - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage(null, $mess["ajxp_conf.61"]); - AJXP_XMLWriter::close(); - return; - } - $userId = AJXP_Utils::sanitize($httpVars["user_id"], AJXP_SANITIZE_EMAILCHARS); - $user = ConfService::getConfStorageImpl()->createUserObject($userId); - if(!AuthService::canAdministrate($user)){ - throw new Exception("Cannot update user data for ".$userId); - } - $res = AuthService::updatePassword($userId, $httpVars["user_pwd"]); - AJXP_XMLWriter::header(); - if ($res === true) { - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.48"].$userId, null); - } else { - AJXP_XMLWriter::sendMessage(null, $mess["ajxp_conf.49"]." : $res"); - } - AJXP_XMLWriter::close(); - - break; - - case "save_user_preference": - - if (!isSet($httpVars["user_id"]) || !AuthService::userExists($httpVars["user_id"]) ) { - throw new Exception($mess["ajxp_conf.61"]); - } - $userId = AJXP_Utils::sanitize($httpVars["user_id"], AJXP_SANITIZE_EMAILCHARS); - if ($userId == $loggedUser->getId()) { - $userObject = $loggedUser; - } else { - $confStorage = ConfService::getConfStorageImpl(); - $userObject = $confStorage->createUserObject($userId); - } - if(!AuthService::canAdministrate($userObject)){ - throw new Exception("Cannot update user data for ".$userId); - } - - $i = 0; - while (isSet($httpVars["pref_name_".$i]) && isSet($httpVars["pref_value_".$i])) { - $prefName = AJXP_Utils::sanitize($httpVars["pref_name_".$i], AJXP_SANITIZE_ALPHANUM); - $prefValue = AJXP_Utils::sanitize(SystemTextEncoding::magicDequote($httpVars["pref_value_".$i])); - if($prefName == "password") continue; - if ($prefName != "pending_folder" && $userObject == null) { - $i++; - continue; - } - $userObject->setPref($prefName, $prefValue); - $userObject->save("user"); - $i++; - } - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage("Succesfully saved user preference", null); - AJXP_XMLWriter::close(); - - break; - - case "get_drivers_definition": - - AJXP_XMLWriter::header("drivers", array("allowed" => $currentUserIsGroupAdmin ? "false" : "true")); - print(AJXP_XMLWriter::replaceAjxpXmlKeywords(ConfService::availableDriversToXML("param", "", true))); - AJXP_XMLWriter::close("drivers"); - - - break; - - case "get_templates_definition": - - AJXP_XMLWriter::header("repository_templates"); - $count = 0; - $repositories = ConfService::listRepositoriesWithCriteria(array( - "isTemplate" => '1' - ), $count); - foreach ($repositories as $repo) { - if(!$repo->isTemplate) continue; - $repoId = $repo->getUniqueId(); - $repoLabel = SystemTextEncoding::toUTF8($repo->getDisplay()); - $repoType = $repo->getAccessType(); - print(""); - } - AJXP_XMLWriter::close("repository_templates"); - - - break; - - case "create_repository" : - - $repDef = $httpVars; - $isTemplate = isSet($httpVars["sf_checkboxes_active"]); - unset($repDef["get_action"]); - unset($repDef["sf_checkboxes_active"]); - if (isSet($httpVars["json_data"])) { - $repDef = json_decode(SystemTextEncoding::magicDequote($httpVars["json_data"]), true); - $options = $repDef["DRIVER_OPTIONS"]; - } else { - $options = array(); - $this->parseParameters($repDef, $options, null, true); - } - if (count($options)) { - $repDef["DRIVER_OPTIONS"] = $options; - unset($repDef["DRIVER_OPTIONS"]["AJXP_GROUP_PATH_PARAMETER"]); - if(isSet($options["AJXP_SLUG"])){ - $repDef["AJXP_SLUG"] = $options["AJXP_SLUG"]; - unset($repDef["DRIVER_OPTIONS"]["AJXP_SLUG"]); - } - } - if (strstr($repDef["DRIVER"], "ajxp_template_") !== false) { - $templateId = substr($repDef["DRIVER"], 14); - $templateRepo = ConfService::getRepositoryById($templateId); - $newRep = $templateRepo->createTemplateChild($repDef["DISPLAY"], $repDef["DRIVER_OPTIONS"]); - if(isSet($repDef["AJXP_SLUG"])){ - $newRep->setSlug($repDef["AJXP_SLUG"]); - } - } else { - if ($currentUserIsGroupAdmin) { - throw new Exception("You are not allowed to create a workspace from a driver. Use a template instead."); - } - $pServ = AJXP_PluginsService::getInstance(); - $driver = $pServ->getPluginByTypeName("access", $repDef["DRIVER"]); - - $newRep = ConfService::createRepositoryFromArray(0, $repDef); - $testFile = $driver->getBaseDir()."/test.".$newRep->getAccessType()."Access.php"; - if (!$isTemplate && is_file($testFile)) { - //chdir(AJXP_TESTS_FOLDER."/plugins"); - $className = $newRep->getAccessType()."AccessTest"; - if (!class_exists($className)) - include($testFile); - $class = new $className(); - $result = $class->doRepositoryTest($newRep); - if (!$result) { - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage(null, $class->failedInfo); - AJXP_XMLWriter::close(); - return; - } - } - // Apply default metasource if any - if ($driver != null && $driver->getConfigs()!=null ) { - $confs = $driver->getConfigs(); - if (!empty($confs["DEFAULT_METASOURCES"])) { - $metaIds = AJXP_Utils::parseCSL($confs["DEFAULT_METASOURCES"]); - $metaSourceOptions = array(); - foreach ($metaIds as $metaID) { - $metaPlug = $pServ->getPluginById($metaID); - if($metaPlug == null) continue; - $pNodes = $metaPlug->getManifestRawContent("//param[@default]", "nodes"); - $defaultParams = array(); - foreach ($pNodes as $domNode) { - $defaultParams[$domNode->getAttribute("name")] = $domNode->getAttribute("default"); - } - $metaSourceOptions[$metaID] = $defaultParams; - } - $newRep->addOption("META_SOURCES", $metaSourceOptions); - } - } - } - - if ($this->repositoryExists($newRep->getDisplay())) { - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage(null, $mess["ajxp_conf.50"]); - AJXP_XMLWriter::close(); - return; - } - if ($isTemplate) { - $newRep->isTemplate = true; - } - if ($currentUserIsGroupAdmin) { - $newRep->setGroupPath(AuthService::getLoggedUser()->getGroupPath()); - } else if (!empty($options["AJXP_GROUP_PATH_PARAMETER"])) { - $value = AJXP_Utils::securePath(rtrim($currentAdminBasePath, "/")."/".ltrim($options["AJXP_GROUP_PATH_PARAMETER"], "/")); - $newRep->setGroupPath($value); - } - - $res = ConfService::addRepository($newRep); - AJXP_XMLWriter::header(); - if ($res == -1) { - AJXP_XMLWriter::sendMessage(null, $mess["ajxp_conf.51"]); - } else { - $defaultRights = $newRep->getDefaultRight(); - if(!empty($defaultRights)){ - $groupRole = AuthService::getRole("AJXP_GRP_".$currentAdminBasePath, true); - $groupRole->setAcl($newRep->getId(), $defaultRights); - } - $loggedUser = AuthService::getLoggedUser(); - $loggedUser->personalRole->setAcl($newRep->getUniqueId(), "rw"); - $loggedUser->recomputeMergedRole(); - $loggedUser->save("superuser"); - AuthService::updateUser($loggedUser); - - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.52"], null); - AJXP_XMLWriter::reloadDataNode("", $newRep->getUniqueId()); - AJXP_XMLWriter::reloadRepositoryList(); - } - AJXP_XMLWriter::close(); - - - - break; - - case "edit_repository" : - $repId = $httpVars["repository_id"]; - $repository = ConfService::getRepositoryById($repId); - if ($repository == null) { - throw new Exception("Cannot find workspace with id $repId"); - } - if (!AuthService::canAdministrate($repository)) { - throw new Exception("You are not allowed to edit this workspace!"); - } - $pServ = AJXP_PluginsService::getInstance(); - $plug = $pServ->getPluginById("access.".$repository->accessType); - if ($plug == null) { - throw new Exception("Cannot find access driver (".$repository->accessType.") for workspace!"); - } - AJXP_XMLWriter::header("admin_data"); - $slug = $repository->getSlug(); - if ($slug == "" && $repository->isWriteable()) { - $repository->setSlug(); - ConfService::replaceRepository($repId, $repository); - } - if (AuthService::getLoggedUser()!=null && AuthService::getLoggedUser()->getGroupPath() != null) { - $rgp = $repository->getGroupPath(); - if($rgp == null) $rgp = "/"; - if (strlen($rgp) < strlen(AuthService::getLoggedUser()->getGroupPath())) { - $repository->setWriteable(false); - } - } - $nested = array(); - $definitions = $plug->getConfigsDefinitions(); - print("securityScope()."\""); - foreach ($repository as $name => $option) { - if(strstr($name, " ")>-1) continue; - if ($name == "driverInstance") continue; - if (!is_array($option)) { - if (is_bool($option)) { - $option = ($option?"true":"false"); - } - print(" $name=\"".SystemTextEncoding::toUTF8(AJXP_Utils::xmlEntities($option))."\" "); - } else if (is_array($option)) { - $nested[] = $option; - } - } - if (count($nested)) { - print(">"); - foreach ($nested as $option) { - foreach ($option as $key => $optValue) { - if (is_array($optValue) && count($optValue)) { - print(""); - } else if (is_object($optValue)){ - print(""); - } else { - if (is_bool($optValue)) { - $optValue = ($optValue?"true":"false"); - } else if(isSet($definitions[$key]) && $definitions[$key]["type"] == "password" && !empty($optValue)){ - $optValue = "__AJXP_VALUE_SET__"; - } - - $optValue = AJXP_Utils::xmlEntities($optValue, true); - print(""); - } - } - } - // Add SLUG - if(!$repository->isTemplate) print("getSlug()."\"/>"); - if ($repository->getGroupPath() != null) { - $groupPath = $repository->getGroupPath(); - if($currentAdminBasePath != "/") $groupPath = substr($repository->getGroupPath(), strlen($currentAdminBasePath)); - print(""); - } - - print(""); - } else { - print("/>"); - } - if ($repository->hasParent()) { - $parent = ConfService::getRepositoryById($repository->getParentId()); - if (isSet($parent) && $parent->isTemplate) { - $parentLabel = $parent->getDisplay(); - $parentType = $parent->getAccessType(); - print(""); - } - } - $manifest = $plug->getManifestRawContent("server_settings/param"); - $manifest = AJXP_XMLWriter::replaceAjxpXmlKeywords($manifest); - $clientSettings = $plug->getManifestRawContent("client_settings", "xml"); - $iconClass = "";$descriptionTemplate = ""; - if($clientSettings->length){ - $iconClass = $clientSettings->item(0)->getAttribute("iconClass"); - $descriptionTemplate = $clientSettings->item(0)->getAttribute("description_template"); - } - print("accessType."\" label=\"".AJXP_Utils::xmlEntities($plug->getManifestLabel())."\" iconClass=\"$iconClass\" description_template=\"$descriptionTemplate\" description=\"".AJXP_Utils::xmlEntities($plug->getManifestDescription())."\">$manifest"); - print(""); - $metas = $pServ->getPluginsByType("metastore"); - $metas = array_merge($metas, $pServ->getPluginsByType("meta")); - $metas = array_merge($metas, $pServ->getPluginsByType("index")); - foreach ($metas as $metaPlug) { - print("getId()."\" label=\"".AJXP_Utils::xmlEntities($metaPlug->getManifestLabel())."\" description=\"".AJXP_Utils::xmlEntities($metaPlug->getManifestDescription())."\">"); - $manifest = $metaPlug->getManifestRawContent("server_settings/param"); - $manifest = AJXP_XMLWriter::replaceAjxpXmlKeywords($manifest); - print($manifest); - print(""); - } - print(""); - if(!$repository->isTemplate){ - print ""; - $users = AuthService::countUsersForRepository($repId, false, true); - $shares = ConfService::getConfStorageImpl()->simpleStoreList("share", null, "", "serial", '', $repId); - print(''); - print(''); - $rootGroup = AuthService::getRole("AJXP_GRP_/"); - if($rootGroup !== false && $rootGroup->hasMask($repId)){ - print("getMask($repId))."]]>"); - } - print ""; - } - AJXP_XMLWriter::close("admin_data"); - - break; - - case "edit_repository_label" : - case "edit_repository_data" : - $repId = $httpVars["repository_id"]; - $repo = ConfService::getRepositoryById($repId); - $initialDefaultRights = $repo->getDefaultRight(); - if(!$repo->isWriteable()){ - if (isSet($httpVars["permission_mask"]) && !empty($httpVars["permission_mask"])){ - $mask = json_decode($httpVars["permission_mask"], true); - $rootGroup = AuthService::getRole("AJXP_GRP_/"); - if(count($mask)){ - $perm = new AJXP_PermissionMask($mask); - $rootGroup->setMask($repId, $perm); - }else{ - $rootGroup->clearMask($repId); - } - AuthService::updateRole($rootGroup); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage("The permission mask was updated for this workspace", null); - AJXP_XMLWriter::close(); - break; - }else{ - throw new Exception("This workspace is not writeable. Please edit directly the conf/bootstrap_repositories.php file."); - } - } - $res = 0; - if (isSet($httpVars["newLabel"])) { - $newLabel = AJXP_Utils::sanitize(AJXP_Utils::securePath($httpVars["newLabel"]), AJXP_SANITIZE_HTML); - if ($this->repositoryExists($newLabel)) { - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage(null, $mess["ajxp_conf.50"]); - AJXP_XMLWriter::close(); - return; - } - $repo->setDisplay($newLabel); - $res = ConfService::replaceRepository($repId, $repo); - } else { - $options = array(); - $existing = $repo->getOptionsDefined(); - $existingValues = array(); - if(!$repo->isTemplate){ - foreach($existing as $exK) $existingValues[$exK] = $repo->getOption($exK, true); - } - $this->parseParameters($httpVars, $options, null, true, $existingValues); - if (count($options)) { - foreach ($options as $key=>$value) { - if ($key == "AJXP_SLUG") { - $repo->setSlug($value); - continue; - } else if ($key == "WORKSPACE_LABEL" || $key == "TEMPLATE_LABEL"){ - $newLabel = AJXP_Utils::sanitize($value, AJXP_SANITIZE_HTML); - if($repo->getDisplay() != $newLabel){ - if ($this->repositoryExists($newLabel)) { - throw new Exception($mess["ajxp_conf.50"]); - }else{ - $repo->setDisplay($newLabel); - } - } - } elseif ($key == "AJXP_GROUP_PATH_PARAMETER") { - $value = AJXP_Utils::securePath(rtrim($currentAdminBasePath, "/")."/".ltrim($value, "/")); - $repo->setGroupPath($value); - continue; - } - $repo->addOption($key, $value); - } - } - if($repo->isTemplate){ - foreach($existing as $definedOption){ - if($definedOption == "META_SOURCES" || $definedOption == "CREATION_TIME" || $definedOption == "CREATION_USER"){ - continue; - } - if(!isSet($options[$definedOption]) && isSet($repo->options[$definedOption])){ - unset($repo->options[$definedOption]); - } - } - } - if ($repo->getOption("DEFAULT_RIGHTS")) { - $gp = $repo->getGroupPath(); - if (empty($gp) || $gp == "/") { - $defRole = AuthService::getRole("AJXP_GRP_/"); - } else { - $defRole = AuthService::getRole("AJXP_GRP_".$gp, true); - } - if ($defRole !== false) { - $defRole->setAcl($repId, $repo->getOption("DEFAULT_RIGHTS")); - AuthService::updateRole($defRole); - } - } - if (is_file(AJXP_TESTS_FOLDER."/plugins/test.ajxp_".$repo->getAccessType().".php")) { - chdir(AJXP_TESTS_FOLDER."/plugins"); - include(AJXP_TESTS_FOLDER."/plugins/test.ajxp_".$repo->getAccessType().".php"); - $className = "ajxp_".$repo->getAccessType(); - $class = new $className(); - $result = $class->doRepositoryTest($repo); - if (!$result) { - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage(null, $class->failedInfo); - AJXP_XMLWriter::close(); - return; - } - } - - $rootGroup = AuthService::getRole("AJXP_GRP_".$currentAdminBasePath, true); - if (isSet($httpVars["permission_mask"]) && !empty($httpVars["permission_mask"])){ - $mask = json_decode($httpVars["permission_mask"], true); - if(count($mask)){ - $perm = new AJXP_PermissionMask($mask); - $rootGroup->setMask($repId, $perm); - }else{ - $rootGroup->clearMask($repId); - } - AuthService::updateRole($rootGroup); - } - $defaultRights = $repo->getDefaultRight(); - if($defaultRights != $initialDefaultRights){ - $currentDefaultRights = $rootGroup->getAcl($repId); - if(!empty($defaultRights) || !empty($currentDefaultRights)){ - $rootGroup->setAcl($repId, empty($defaultRights) ? "" : $defaultRights); - AuthService::updateRole($rootGroup); - } - } - ConfService::replaceRepository($repId, $repo); - } - AJXP_XMLWriter::header(); - if ($res == -1) { - AJXP_XMLWriter::sendMessage(null, $mess["ajxp_conf.53"]); - } else { - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.54"], null); - if (isSet($httpVars["newLabel"])) { - AJXP_XMLWriter::reloadDataNode("", $repId); - } - AJXP_XMLWriter::reloadRepositoryList(); - } - AJXP_XMLWriter::close(); - - break; - - case "meta_source_add" : - $repId = $httpVars["repository_id"]; - $repo = ConfService::getRepositoryById($repId); - if (!is_object($repo)) { - throw new Exception("Invalid workspace id! $repId"); - } - $metaSourceType = AJXP_Utils::sanitize($httpVars["new_meta_source"], AJXP_SANITIZE_ALPHANUM); - if (isSet($httpVars["json_data"])) { - $options = json_decode(SystemTextEncoding::magicDequote($httpVars["json_data"]), true); - } else { - $options = array(); - $this->parseParameters($httpVars, $options, null, true); - } - $repoOptions = $repo->getOption("META_SOURCES"); - if (is_array($repoOptions) && isSet($repoOptions[$metaSourceType])) { - throw new Exception($mess["ajxp_conf.55"]); - } - if (!is_array($repoOptions)) { - $repoOptions = array(); - } - $repoOptions[$metaSourceType] = $options; - uksort($repoOptions, array($this,"metaSourceOrderingFunction")); - $repo->addOption("META_SOURCES", $repoOptions); - ConfService::replaceRepository($repId, $repo); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.56"],null); - AJXP_XMLWriter::close(); - break; - - case "meta_source_delete" : - - $repId = $httpVars["repository_id"]; - $repo = ConfService::getRepositoryById($repId); - if (!is_object($repo)) { - throw new Exception("Invalid workspace id! $repId"); - } - $metaSourceId = $httpVars["plugId"]; - $repoOptions = $repo->getOption("META_SOURCES"); - if (is_array($repoOptions) && array_key_exists($metaSourceId, $repoOptions)) { - unset($repoOptions[$metaSourceId]); - uksort($repoOptions, array($this,"metaSourceOrderingFunction")); - $repo->addOption("META_SOURCES", $repoOptions); - ConfService::replaceRepository($repId, $repo); - }else{ - throw new Exception("Cannot find meta source ".$metaSourceId); - } - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.57"],null); - AJXP_XMLWriter::close(); - - break; - - case "meta_source_edit" : - $repId = $httpVars["repository_id"]; - $repo = ConfService::getRepositoryById($repId); - if (!is_object($repo)) { - throw new Exception("Invalid workspace id! $repId"); - } - if(isSet($httpVars["plugId"])){ - $metaSourceId = $httpVars["plugId"]; - $repoOptions = $repo->getOption("META_SOURCES"); - if (!is_array($repoOptions)) { - $repoOptions = array(); - } - if (isSet($httpVars["json_data"])) { - $options = json_decode(SystemTextEncoding::magicDequote($httpVars["json_data"]), true); - } else { - $options = array(); - $this->parseParameters($httpVars, $options, null, true); - } - if(isset($repoOptions[$metaSourceId])){ - $this->mergeExistingParameters($options, $repoOptions[$metaSourceId]); - } - $repoOptions[$metaSourceId] = $options; - }else if(isSet($httpVars["bulk_data"])){ - $bulkData = json_decode(SystemTextEncoding::magicDequote($httpVars["bulk_data"]), true); - $repoOptions = $repo->getOption("META_SOURCES"); - if (!is_array($repoOptions)) { - $repoOptions = array(); - } - if(isSet($bulkData["delete"]) && count($bulkData["delete"])){ - foreach($bulkData["delete"] as $key){ - if(isSet($repoOptions[$key])) unset($repoOptions[$key]); - } - } - if(isSet($bulkData["add"]) && count($bulkData["add"])){ - foreach($bulkData["add"] as $key => $value){ - if(isSet($repoOptions[$key])) $this->mergeExistingParameters($value, $repoOptions[$key]); - $repoOptions[$key] = $value; - } - } - if(isSet($bulkData["edit"]) && count($bulkData["edit"])){ - foreach($bulkData["edit"] as $key => $value){ - if(isSet($repoOptions[$key])) $this->mergeExistingParameters($value, $repoOptions[$key]); - $repoOptions[$key] = $value; - } - } - } - uksort($repoOptions, array($this,"metaSourceOrderingFunction")); - $repo->addOption("META_SOURCES", $repoOptions); - ConfService::replaceRepository($repId, $repo); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.58"],null); - AJXP_XMLWriter::close(); - break; - - - case "delete" : - // REST API mapping - if (isSet($httpVars["data_type"])) { - switch ($httpVars["data_type"]) { - case "repository": - $httpVars["repository_id"] = basename($httpVars["data_id"]); - break; - case "role": - $httpVars["role_id"] = basename($httpVars["data_id"]); - break; - case "user": - $httpVars["user_id"] = basename($httpVars["data_id"]); - break; - case "group": - $httpVars["group"] = "/data/users".$httpVars["data_id"]; - break; - default: - break; - } - unset($httpVars["data_type"]); - unset($httpVars["data_id"]); - } - if (isSet($httpVars["repository_id"])) { - $repId = $httpVars["repository_id"]; - $repo = ConfService::getRepositoryById($repId); - if(!is_object($repo)){ - $res = -1; - }else{ - $res = ConfService::deleteRepository($repId); - } - AJXP_XMLWriter::header(); - if ($res == -1) { - AJXP_XMLWriter::sendMessage(null, $mess[427]); - } else { - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.59"], null); - AJXP_XMLWriter::reloadDataNode(); - AJXP_XMLWriter::reloadRepositoryList(); - } - AJXP_XMLWriter::close(); - return; - } else if (isSet($httpVars["role_id"])) { - $roleId = $httpVars["role_id"]; - if (AuthService::getRole($roleId) === false) { - throw new Exception($mess["ajxp_conf.67"]); - } - AuthService::deleteRole($roleId); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.68"], null); - AJXP_XMLWriter::reloadDataNode(); - AJXP_XMLWriter::close(); - } else if (isSet($httpVars["group"])) { - $groupPath = $httpVars["group"]; - $basePath = substr(AJXP_Utils::forwardSlashDirname($groupPath), strlen("/data/users")); - $gName = basename($groupPath); - AuthService::deleteGroup($basePath, $gName); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.128"], null); - AJXP_XMLWriter::reloadDataNode(); - AJXP_XMLWriter::close(); - } else { - if(!isset($httpVars["user_id"]) || $httpVars["user_id"]=="" - || AuthService::isReservedUserId($httpVars["user_id"]) - || $loggedUser->getId() == $httpVars["user_id"]) - { - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage(null, $mess["ajxp_conf.61"]); - AJXP_XMLWriter::close(); - } - AuthService::deleteUser($httpVars["user_id"]); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.60"], null); - AJXP_XMLWriter::reloadDataNode(); - AJXP_XMLWriter::close(); - - } - break; - - case "get_plugin_manifest" : - - $ajxpPlugin = AJXP_PluginsService::getInstance()->getPluginById($httpVars["plugin_id"]); - AJXP_XMLWriter::header("admin_data"); - - $fullManifest = $ajxpPlugin->getManifestRawContent("", "xml"); - $xPath = new DOMXPath($fullManifest->ownerDocument); - $addParams = ""; - $instancesDefinitions = array(); - $pInstNodes = $xPath->query("server_settings/global_param[contains(@type, 'plugin_instance:')]"); - foreach ($pInstNodes as $pInstNode) { - $type = $pInstNode->getAttribute("type"); - $instType = str_replace("plugin_instance:", "", $type); - $fieldName = $pInstNode->getAttribute("name"); - $pInstNode->setAttribute("type", "group_switch:".$fieldName); - $typePlugs = AJXP_PluginsService::getInstance()->getPluginsByType($instType); - foreach ($typePlugs as $typePlug) { - if(!$typePlug->isEnabled()) continue; - if($typePlug->getId() == "auth.multi") continue; - $checkErrorMessage = ""; - try { - $typePlug->performChecks(); - } catch (Exception $e) { - $checkErrorMessage = " (Warning : ".$e->getMessage().")"; - } - $tParams = AJXP_XMLWriter::replaceAjxpXmlKeywords($typePlug->getManifestRawContent("server_settings/param[not(@group_switch_name)]")); - $addParams .= ''; - $addParams .= str_replace("getManifestLabel().$checkErrorMessage."\" group_switch_value=\"".$typePlug->getId()."\" ", $tParams); - $addParams .= str_replace("getManifestRawContent("server_settings/param[@group_switch_name]"))); - $addParams .= AJXP_XMLWriter::replaceAjxpXmlKeywords($typePlug->getManifestRawContent("server_settings/global_param")); - $instancesDefs = $typePlug->getConfigsDefinitions(); - if(!empty($instancesDefs) && is_array($instancesDefs)) { - foreach($instancesDefs as $defKey => $defData){ - $instancesDefinitions[$fieldName."/".$defKey] = $defData; - } - } - } - } - $allParams = AJXP_XMLWriter::replaceAjxpXmlKeywords($fullManifest->ownerDocument->saveXML($fullManifest)); - $allParams = str_replace('type="plugin_instance:', 'type="group_switch:', $allParams); - $allParams = str_replace("", $addParams."", $allParams); - - echo($allParams); - $definitions = $instancesDefinitions; - $configsDefs = $ajxpPlugin->getConfigsDefinitions(); - if(is_array($configsDefs)){ - $definitions = array_merge($configsDefs, $instancesDefinitions); - } - $values = $ajxpPlugin->getConfigs(); - if(!is_array($values)) $values = array(); - echo(""); - // First flatten keys - $flattenedKeys = array(); - foreach ($values as $key => $value){ - $type = $definitions[$key]["type"]; - if ((strpos($type, "group_switch:") === 0 || strpos($type, "plugin_instance:") === 0 ) && is_array($value)) { - $res = array(); - $this->flattenKeyValues($res, $definitions, $value, $key); - $flattenedKeys += $res; - // Replace parent key by new flat value - $values[$key] = $flattenedKeys[$key]; - } - } - $values += $flattenedKeys; - - foreach ($values as $key => $value) { - $attribute = true; - $type = $definitions[$key]["type"]; - if ($type == "array" && is_array($value)) { - $value = implode(",", $value); - } else if ($type == "boolean") { - $value = ($value === true || $value === "true" || $value == 1?"true":"false"); - } else if ($type == "textarea") { - $attribute = false; - } else if ($type == "password" && !empty($value)){ - $value = "__AJXP_VALUE_SET__"; - } - if ($attribute) { - echo(""); - } else { - echo(""); - } - } - if ($ajxpPlugin->getType() != "core") { - echo("isEnabled()?"true":"false")."\"/>"); - } - echo(""); - echo("".$ajxpPlugin->getPluginInformationHTML("Charles du Jeu", "https://pydio.com/en/docs/references/plugins/")."

    "); - if (file_exists($ajxpPlugin->getBaseDir()."/plugin_doc.html")) { - echo(file_get_contents($ajxpPlugin->getBaseDir()."/plugin_doc.html")); - } - echo("]]>
    "); - AJXP_XMLWriter::close("admin_data"); - - break; - - case "run_plugin_action": - - $options = array(); - $this->parseParameters($httpVars, $options, null, true); - $pluginId = $httpVars["action_plugin_id"]; - if (isSet($httpVars["button_key"])) { - $options = $options[$httpVars["button_key"]]; - } - $plugin = AJXP_PluginsService::getInstance()->softLoad($pluginId, $options); - if (method_exists($plugin, $httpVars["action_plugin_method"])) { - try { - $res = call_user_func(array($plugin, $httpVars["action_plugin_method"]), $options); - } catch (Exception $e) { - echo("ERROR:" . $e->getMessage()); - break; - } - echo($res); - } else { - echo 'ERROR: Plugin '.$httpVars["action_plugin_id"].' does not implement '.$httpVars["action_plugin_method"].' method!'; - } - - break; - - case "edit_plugin_options": - - $options = array(); - $this->parseParameters($httpVars, $options, null, true); - $confStorage = ConfService::getConfStorageImpl(); - $pluginId = AJXP_Utils::sanitize($httpVars["plugin_id"], AJXP_SANITIZE_ALPHANUM); - list($pType, $pName) = explode(".", $pluginId); - $existing = $confStorage->loadPluginConfig($pType, $pName); - $this->mergeExistingParameters($options, $existing); - $confStorage->savePluginConfig($pluginId, $options); - ConfService::clearAllCaches(); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage($mess["ajxp_conf.97"], null); - AJXP_XMLWriter::close(); - - - break; - - case "generate_api_docs": - - PydioSdkGenerator::analyzeRegistry(isSet($httpVars["version"])?$httpVars["version"]:AJXP_VERSION); - - break; - - - // Action for update all Pydio's user from ldap in CLI mode - case "cli_update_user_list": - if((php_sapi_name() == "cli")){ - $progressBar = new AJXP_ProgressBarCLI(); - $countCallback = array($progressBar, "init"); - $loopCallback = array($progressBar, "update"); - AuthService::listUsers("/", null, -1 , -1, true, true, $countCallback, $loopCallback); - } - break; - - default: - break; - } - - return; - } - - /** - * @param $actionName - * @param $httpVars - * @param $fileVars - * @throws Exception - */ - public function displayEnterprise($actionName, $httpVars, $fileVars){ - - $touchFile = $this->getPluginWorkDir(true).DIRECTORY_SEPARATOR."enterprise-display"; - HTMLWriter::charsetHeader("application/json"); - if(file_exists($touchFile)){ - $lastDisplay = intval(file_get_contents($touchFile)); - if(time() - $lastDisplay > 60 * 60 * 24 * 15){ - echo json_encode(["display" => true]); - file_put_contents($touchFile, time()); - }else{ - echo json_encode(["display" => false]); - } - }else{ - echo json_encode(["display" => true]); - file_put_contents($touchFile, time()); - } - } - - /** - * @param $dir - * @param null $root - * @param null $hash - * @param bool $returnNodes - * @param string $file - * @param null $aliasedDir - * @return array - */ - public function listPlugins($dir, $root = NULL, $hash = null, $returnNodes = false, $file="", $aliasedDir=null) - { - $dir = "/$dir"; - if($aliasedDir != null && $aliasedDir != "/".$root.$dir){ - $baseDir = $aliasedDir; - }else{ - $baseDir = "/".$root.$dir; - } - $allNodes = array(); - $this->logInfo("Listing plugins",""); // make sure that the logger is started! - $pServ = AJXP_PluginsService::getInstance(); - $types = $pServ->getDetectedPlugins(); - $mess = ConfService::getMessages(); - $uniqTypes = array("core"); - $coreTypes = array("auth", "conf", "boot", "feed", "log", "mailer", "mq"); - if ($dir == "/plugins" || $dir == "/core_plugins" || $dir=="/all") { - if($dir == "/core_plugins") $uniqTypes = $coreTypes; - else if($dir == "/plugins") $uniqTypes = array_diff(array_keys($types), $coreTypes); - else if($dir == "/all") $uniqTypes = array_keys($types); - if(!$returnNodes) AJXP_XMLWriter::sendFilesListComponentConfig(' - - - - '); - ksort($types); - foreach ($types as $t => $tPlugs) { - if(!in_array($t, $uniqTypes))continue; - if($t == "core") continue; - $nodeKey = $baseDir."/".$t; - $meta = array( - "icon" => "folder_development.png", - "plugin_id" => $t, - "text" => $mess["plugtype.title.".$t], - "plugin_description" => $mess["plugtype.desc.".$t] - ); - if(in_array($nodeKey, $this->currentBookmarks)) $meta = array_merge($meta, array("ajxp_bookmarked" => "true", "overlay_icon" => "bookmark.png")); - $xml = AJXP_XMLWriter::renderNode($nodeKey, ucfirst($t), false, $meta, true, false); - if($returnNodes) $allNodes[$nodeKey] = $xml; - else print($xml); - } - } else if ($dir == "/core") { - if(!$returnNodes) AJXP_XMLWriter::sendFilesListComponentConfig(' - - - - '); - $all = $first = ""; - foreach ($uniqTypes as $type) { - if(!isset($types[$type])) continue; - foreach ($types[$type] as $pObject) { - $isMain = ($pObject->getId() == "core.ajaxplorer"); - $meta = array( - "icon" => ($isMain?"preferences_desktop.png":"desktop.png"), - "ajxp_mime" => "ajxp_plugin", - "plugin_id" => $pObject->getId(), - "plugin_description" => $pObject->getManifestDescription() - ); - // Check if there are actually any parameters to display! - if($pObject->getManifestRawContent("server_settings", "xml")->length == 0) continue; - $label = $pObject->getManifestLabel(); - $nodeKey = $baseDir."/".$pObject->getId(); - if(in_array($nodeKey, $this->currentBookmarks)) $meta = array_merge($meta, array("ajxp_bookmarked" => "true", "overlay_icon" => "bookmark.png")); - $nodeString =AJXP_XMLWriter::renderNode($nodeKey, $label, true, $meta, true, false); - if($returnNodes) $allNodes[$nodeKey] = $nodeString; - if ($isMain) { - $first = $nodeString; - } else { - $all .= $nodeString; - } - } - } - if(!$returnNodes) print($first.$all); - } else { - $split = explode("/", $dir); - if(empty($split[0])) array_shift($split); - $type = $split[1]; - if(!$returnNodes) AJXP_XMLWriter::sendFilesListComponentConfig(' - - - - - - '); - $mess = ConfService::getMessages(); - foreach ($types[$type] as $pObject) { - $errors = "OK"; - try { - $pObject->performChecks(); - } catch (Exception $e) { - $errors = "ERROR : ".$e->getMessage(); - } - $meta = array( - "icon" => "preferences_plugin.png", - "ajxp_mime" => "ajxp_plugin", - "can_active" => $errors, - "enabled" => ($pObject->isEnabled()?$mess[440]:$mess[441]), - "plugin_id" => $pObject->getId(), - "plugin_description" => $pObject->getManifestDescription() - ); - $nodeKey = $baseDir."/".$pObject->getId(); - if(in_array($nodeKey, $this->currentBookmarks)) $meta = array_merge($meta, array("ajxp_bookmarked" => "true", "overlay_icon" => "bookmark.png")); - $xml = AJXP_XMLWriter::renderNode($nodeKey, $pObject->getManifestLabel(), true, $meta, true, false); - if($returnNodes) $allNodes[$nodeKey] = $xml; - else print $xml; - } - } - return $allNodes; - } - - public function listUsers($root, $child, $hashValue = null, $returnNodes = false, $findNodePosition=null) - { - $USER_PER_PAGE = 50; - if($root == "users") $baseGroup = "/"; - else $baseGroup = substr($root, strlen("users")); - - if ($findNodePosition != null && $hashValue == null) { - - // Add groups offset - $groups = AuthService::listChildrenGroups($baseGroup); - $offset = 0; - if(count($groups)){ - $offset = count($groups); - } - $position = AuthService::findUserPage($baseGroup, $findNodePosition, $USER_PER_PAGE); - if($position != -1){ - - $key = "/data/".$root."/".$findNodePosition; - $data = array($key => AJXP_XMLWriter::renderNode($key, $findNodePosition, true, array( - "page_position" => $position - ), true, false)); - return $data; - - }else{ - // Loop on each page to find the correct page. - $count = AuthService::authCountUsers($baseGroup); - $pages = ceil($count / $USER_PER_PAGE); - for ($i = 0; $i < $pages ; $i ++) { - - $tests = $this->listUsers($root, $child, $i+1, true, $findNodePosition); - if (is_array($tests) && isSet($tests["/data/".$root."/".$findNodePosition])) { - return array("/data/".$root."/".$findNodePosition => str_replace("ajxp_mime", "page_position='".($i+1)."' ajxp_mime", $tests["/data/".$root."/".$findNodePosition])); - } - - } - } - - - return array(); - - } - - $allNodes = array(); - $columns = ' - - - - - - '; - if (AuthService::driverSupportsAuthSchemes()) { - $columns = ' - - - - - - - '; - } - if(!$returnNodes) AJXP_XMLWriter::sendFilesListComponentConfig($columns); - if(!AuthService::usersEnabled()) return array(); - if(empty($hashValue)) $hashValue = 1; - - $count = AuthService::authCountUsers($baseGroup, "", null, null, false); - if (AuthService::authSupportsPagination() && $count >= $USER_PER_PAGE) { - $offset = ($hashValue - 1) * $USER_PER_PAGE; - if(!$returnNodes) AJXP_XMLWriter::renderPaginationData($count, $hashValue, ceil($count/$USER_PER_PAGE)); - $users = AuthService::listUsers($baseGroup, "", $offset, $USER_PER_PAGE, true, false); - if ($hashValue == 1) { - $groups = AuthService::listChildrenGroups($baseGroup); - } else { - $groups = array(); - } - } else { - $users = AuthService::listUsers($baseGroup, "", -1, -1, true, false); - $groups = AuthService::listChildrenGroups($baseGroup); - } - $groupAdmin = (AuthService::getLoggedUser() != null && AuthService::getLoggedUser()->getGroupPath() != "/"); - if($this->getName() == "ajxp_admin" && $baseGroup == "/" && $hashValue == 1 && !$groupAdmin){ - $nodeKey = "/data/".$root."/"; - $meta = array( - "icon" => "users-folder.png", - "icon_class" => "icon-home", - "ajxp_mime" => "group", - "object_id" => "/" - ); - $mess = ConfService::getMessages(); - $xml = AJXP_XMLWriter::renderNode($nodeKey, $mess["ajxp_conf.151"], true, $meta, true, false); - if(!$returnNodes) print($xml); - else $allNodes[$nodeKey] = $xml; - } - - foreach ($groups as $groupId => $groupLabel) { - - $nodeKey = "/data/".$root."/".ltrim($groupId,"/"); - $meta = array( - "icon" => "users-folder.png", - "icon_class" => "icon-folder-close", - "ajxp_mime" => "group", - "object_id" => $groupId - ); - if(in_array($nodeKey, $this->currentBookmarks)) $meta = array_merge($meta, array("ajxp_bookmarked" => "true", "overlay_icon" => "bookmark.png")); - $xml = AJXP_XMLWriter::renderNode($nodeKey, - $groupLabel, false, $meta, true, false); - if(!$returnNodes) print($xml); - else $allNodes[$nodeKey] = $xml; - - } - $mess = ConfService::getMessages(); - $loggedUser = AuthService::getLoggedUser(); - $userArray = array(); - $logger = AJXP_Logger::getInstance(); - if(method_exists($logger, "usersLastConnection")){ - $allUserIds = array(); - } - foreach ($users as $userObject) { - $label = $userObject->getId(); - if(isSet($allUserIds)) $allUserIds[] = $label; - if ($userObject->hasParent()) { - $label = $userObject->getParent()."000".$label; - }else{ - $children = ConfService::getConfStorageImpl()->getUserChildren($label); - foreach($children as $addChild){ - $userArray[$label."000".$addChild->getId()] = $addChild; - } - } - $userArray[$label] = $userObject; - } - if(isSet($allUserIds) && count($allUserIds)){ - $connections = $logger->usersLastConnection($allUserIds); - } - ksort($userArray); - foreach ($userArray as $userObject) { - $repos = ConfService::getConfStorageImpl()->listRepositories($userObject); - $isAdmin = $userObject->isAdmin(); - $userId = $userObject->getId(); - $icon = "user".($userId=="guest"?"_guest":($isAdmin?"_admin":"")); - $iconClass = "icon-user"; - if ($userObject->hasParent()) { - $icon = "user_child"; - $iconClass = "icon-angle-right"; - } - if ($isAdmin) { - $rightsString = $mess["ajxp_conf.63"]; - } else { - $r = array(); - foreach ($repos as $repoId => $repository) { - if($repository->getAccessType() == "ajxp_shared") continue; - if(!$userObject->canRead($repoId) && !$userObject->canWrite($repoId)) continue; - $rs = ($userObject->canRead($repoId) ? "r" : ""); - $rs .= ($userObject->canWrite($repoId) ? "w" : ""); - $r[] = $repository->getDisplay()." (".$rs.")"; - } - $rightsString = implode(", ", $r); - } - $nodeLabel = ConfService::getUserPersonalParameter("USER_DISPLAY_NAME", $userObject, "core.conf", $userId); - $scheme = AuthService::getAuthScheme($userId); - $nodeKey = "/data/$root/".$userId; - $roles = array_filter(array_keys($userObject->getRoles()), array($this, "filterReservedRoles")); - $meta = array( - "isAdmin" => $mess[($isAdmin?"ajxp_conf.14":"ajxp_conf.15")], - "icon" => $icon.".png", - "icon_class" => $iconClass, - "object_id" => $userId, - "auth_scheme" => ($scheme != null? $scheme : ""), - "rights_summary" => $rightsString, - "ajxp_roles" => implode(", ", $roles), - "ajxp_mime" => "user".(($userId!="guest"&&$userId!=$loggedUser->getId())?"_editable":""), - "json_merged_role" => json_encode($userObject->mergedRole->getDataArray(true)) - ); - if($userObject->hasParent()) $meta["shared_user"] = "true"; - if(isSet($connections) && isSet($connections[$userObject->getId()]) && !empty($connections[$userObject->getId()])) { - $meta["last_connection"] = strtotime($connections[$userObject->getId()]); - $meta["last_connection_readable"] = AJXP_Utils::relativeDate($meta["last_connection"], $mess); - } - if(in_array($nodeKey, $this->currentBookmarks)) $meta = array_merge($meta, array("ajxp_bookmarked" => "true", "overlay_icon" => "bookmark.png")); - $xml = AJXP_XMLWriter::renderNode($nodeKey, $nodeLabel, true, $meta, true, false); - if(!$returnNodes) print($xml); - else $allNodes[$nodeKey] = $xml; - } - return $allNodes; - } - - public function listRoles($root, $child, $hashValue = null, $returnNodes = false) - { - $allNodes = array(); - if(!$returnNodes) AJXP_XMLWriter::sendFilesListComponentConfig(' - - - - '); - if(!AuthService::usersEnabled()) return array(); - $currentUserIsGroupAdmin = (AuthService::getLoggedUser() != null && AuthService::getLoggedUser()->getGroupPath() != "/"); - $roles = AuthService::getRolesList(array(), !$this->listSpecialRoles); - ksort($roles); - $mess = ConfService::getMessages(); - if(!$this->listSpecialRoles && $this->getName() != "ajxp_admin" && !$currentUserIsGroupAdmin){ - $rootGroupRole = AuthService::getRole("AJXP_GRP_/", true); - if($rootGroupRole->getLabel() == "AJXP_GRP_/"){ - $rootGroupRole->setLabel($mess["ajxp_conf.151"]); - AuthService::updateRole($rootGroupRole); - } - array_unshift($roles, $rootGroupRole); - } - foreach ($roles as $roleObject) { - //if(strpos($roleId, "AJXP_GRP_") === 0 && !$this->listSpecialRoles) continue; - $r = array(); - if(!AuthService::canAdministrate($roleObject)) continue; - $count = 0; - $repos = ConfService::listRepositoriesWithCriteria(array("role" => $roleObject), $count); - foreach ($repos as $repoId => $repository) { - if($repository->getAccessType() == "ajxp_shared") continue; - if(!$roleObject->canRead($repoId) && !$roleObject->canWrite($repoId)) continue; - $rs = ($roleObject->canRead($repoId) ? "r" : ""); - $rs .= ($roleObject->canWrite($repoId) ? "w" : ""); - $r[] = $repository->getDisplay()." (".$rs.")"; - } - $rightsString = implode(", ", $r); - $nodeKey = "/data/roles/".$roleObject->getId(); - $appliesToDefault = implode(",", $roleObject->listAutoApplies()); - if($roleObject->getId() == "AJXP_GRP_/"){ - $appliesToDefault = $mess["ajxp_conf.153"]; - } - $meta = array( - "icon" => "user-acl.png", - "rights_summary" => $rightsString, - "is_default" => $appliesToDefault, //($roleObject->autoAppliesTo("standard") ? $mess[440]:$mess[441]), - "ajxp_mime" => "role", - "role_id" => $roleObject->getId(), - "text" => $roleObject->getLabel() - ); - if(in_array($nodeKey, $this->currentBookmarks)) $meta = array_merge($meta, array("ajxp_bookmarked" => "true", "overlay_icon" => "bookmark.png")); - $xml = AJXP_XMLWriter::renderNode($nodeKey, $roleObject->getId(), true, $meta, true, false); - if(!$returnNodes) echo $xml; - else $allNodes[$nodeKey] = $xml; - } - return $allNodes; - } - - public function repositoryExists($name) - { - $repos = ConfService::getRepositoriesList(); - foreach ($repos as $obj) - if ($obj->getDisplay() == $name) return true; - - return false; - } - - /** - * @param Repository $a - * @param Repository $b - * @return integer - */ - public function sortReposByLabel($a, $b) - { - return strcasecmp($a->getDisplay(), $b->getDisplay()); - } - - protected function getDriverLabel($pluginId, &$labels){ - if(isSet($labels[$pluginId])){ - return $labels[$pluginId]; - } - $plugin = AJXP_PluginsService::findPluginById("access.".$pluginId); - if(!is_object($plugin)) { - $label = "access.$plugin (plugin disabled!)"; - }else{ - $label = $plugin->getManifestLabel(); - } - $labels[$pluginId] = $label; - return $label; - } - - - public function listRepositories($root, $child, $hashValue = null, $returnNodes = false, $file="", $aliasedDir=null, $httpVars){ - $REPOS_PER_PAGE = 50; - $allNodes = array(); - if($hashValue == null) $hashValue = 1; - $offset = ($hashValue - 1) * $REPOS_PER_PAGE; - - $count = null; - // Load all repositories = normal, templates, and templates children - $criteria = array( - "ORDERBY" => array("KEY" => "display", "DIR"=>"ASC"), - "CURSOR" => array("OFFSET" => $offset, "LIMIT" => $REPOS_PER_PAGE) - ); - $currentUserIsGroupAdmin = (AuthService::getLoggedUser() != null && AuthService::getLoggedUser()->getGroupPath() != "/"); - if($currentUserIsGroupAdmin){ - $criteria = array_merge($criteria, array( - "owner_user_id" => AJXP_FILTER_EMPTY, - "groupPath" => "regexp:/^".str_replace("/", "\/", AuthService::getLoggedUser()->getGroupPath()).'/', - )); - }else{ - $criteria["parent_uuid"] = AJXP_FILTER_EMPTY; - } - if(isSet($httpVars) && is_array($httpVars) && isSet($httpVars["template_children_id"])){ - $criteria["parent_uuid"] = AJXP_Utils::sanitize($httpVars["template_children_id"], AJXP_SANITIZE_ALPHANUM); - } - $repos = ConfService::listRepositoriesWithCriteria($criteria, $count); - if(!$returnNodes){ - AJXP_XMLWriter::renderPaginationData($count, $hashValue, ceil($count/$REPOS_PER_PAGE)); - AJXP_XMLWriter::sendFilesListComponentConfig(' - - - - '); - } - - $driverLabels = array(); - - foreach ($repos as $repoIndex => $repoObject) { - - if($repoObject->getAccessType() == "ajxp_conf" || $repoObject->getAccessType() == "ajxp_shared") continue; - if (!AuthService::canAdministrate($repoObject))continue; - if(is_numeric($repoIndex)) $repoIndex = "".$repoIndex; - - $icon = "hdd_external_unmount.png"; - $editable = $repoObject->isWriteable(); - if ($repoObject->isTemplate) { - $icon = "hdd_external_mount.png"; - if (AuthService::getLoggedUser() != null && AuthService::getLoggedUser()->getGroupPath() != "/") { - $editable = false; - } - } - $accessType = $repoObject->getAccessType(); - $accessLabel = $this->getDriverLabel($accessType, $driverLabels); - $meta = array( - "repository_id" => $repoIndex, - "accessType" => ($repoObject->isTemplate?"Template for ":"").$repoObject->getAccessType(), - "accessLabel" => $accessLabel, - "icon" => $icon, - "owner" => ($repoObject->hasOwner()?$repoObject->getOwner():""), - "openicon" => $icon, - "slug" => $repoObject->getSlug(), - "parentname" => "/repositories", - "ajxp_mime" => "repository".($editable?"_editable":""), - "is_template" => ($repoObject->isTemplate?"true":"false") - ); - - $nodeKey = "/data/repositories/$repoIndex"; - $label = $repoObject->getDisplay(); - if(in_array($nodeKey, $this->currentBookmarks)) $meta = array_merge($meta, array("ajxp_bookmarked" => "true", "overlay_icon" => "bookmark.png")); - $xml = AJXP_XMLWriter::renderNode( - $nodeKey, - AJXP_Utils::xmlEntities(SystemTextEncoding::toUTF8($label)), - true, - $meta, - true, - false - ); - if($returnNodes) $allNodes[$nodeKey] = $xml; - else print($xml); - - if ($repoObject->isTemplate) { - // Now Load children for template repositories - $children = ConfService::listRepositoriesWithCriteria(array("parent_uuid" => $repoIndex.""), $count); - foreach($children as $childId => $childObject){ - if (!AuthService::canAdministrate($childObject))continue; - if(is_numeric($childId)) $childId = "".$childId; - $meta = array( - "repository_id" => $childId, - "accessType" => $childObject->getAccessType(), - "accessLabel" => $this->getDriverLabel($childObject->getAccessType(), $driverLabels), - "icon" => "repo_child.png", - "slug" => $childObject->getSlug(), - "owner" => ($childObject->hasOwner()?$childObject->getOwner():""), - "openicon" => "repo_child.png", - "parentname" => "/repositories", - "ajxp_mime" => "repository_editable", - "template_name" => $label - ); - $cNodeKey = "/data/repositories/$childId"; - if(in_array($cNodeKey, $this->currentBookmarks)) $meta = array_merge($meta, array("ajxp_bookmarked" => "true", "overlay_icon" => "bookmark.png")); - $xml = AJXP_XMLWriter::renderNode( - $cNodeKey, - AJXP_Utils::xmlEntities(SystemTextEncoding::toUTF8($childObject->getDisplay())), - true, - $meta, - true, - false - ); - if($returnNodes) $allNodes[$cNodeKey] = $xml; - else print($xml); - } - } - } - - } - - - public function listActions($dir, $root = NULL, $hash = null, $returnNodes = false) - { - $allNodes = array(); - $parts = explode("/",$dir); - $pServ = AJXP_PluginsService::getInstance(); - //$activePlugins = $pServ->getActivePlugins(); - $types = $pServ->getDetectedPlugins(); - if (count($parts) == 1) { - // list all types - if(!$returnNodes) AJXP_XMLWriter::sendFilesListComponentConfig(' - - '); - ksort($types); - foreach ($types as $t => $tPlugs) { - $meta = array( - "icon" => "folder_development.png", - "plugin_id" => $t - ); - $nodeKey = "/$root/actions/".$t; - if(in_array($nodeKey, $this->currentBookmarks)) $meta = array_merge($meta, array("ajxp_bookmarked" => "true", "overlay_icon" => "bookmark.png")); - $xml = AJXP_XMLWriter::renderNode($nodeKey, ucfirst($t), false, $meta, true, false); - if($returnNodes) $allNodes[$nodeKey] = $xml; - else print($xml); - } - - } else if (count($parts) == 2) { - // list plugs - $type = $parts[1]; - if(!$returnNodes) AJXP_XMLWriter::sendFilesListComponentConfig(' - - - '); - foreach ($types[$type] as $pObject) { - $actions = $pObject->getManifestRawContent("//action/@name", "xml", true); - $actLabel = array(); - if ($actions->length) { - foreach ($actions as $node) { - $actLabel[] = $node->nodeValue; - } - } - $meta = array( - "icon" => "preferences_plugin.png", - "plugin_id" => $pObject->getId(), - "actions" => implode(", ", $actLabel) - ); - $nodeKey = "/$root/actions/$type/".$pObject->getName(); - if(in_array($nodeKey, $this->currentBookmarks)) $meta = array_merge($meta, array("ajxp_bookmarked" => "true", "overlay_icon" => "bookmark.png")); - $xml = AJXP_XMLWriter::renderNode($nodeKey, $pObject->getManifestLabel(), false, $meta, true, false); - if($returnNodes) $allNodes[$nodeKey] = $xml; - else print($xml); - - } - - } else if (count($parts) == 3) { - // list actions - $type = $parts[1]; - $name = $parts[2]; - $mess = ConfService::getMessages(); - if(!$returnNodes) AJXP_XMLWriter::sendFilesListComponentConfig(' - - - '); - $pObject = $types[$type][$name]; - - $actions = $pObject->getManifestRawContent("//action", "xml", true); - $allNodesAcc = array(); - if ($actions->length) { - foreach ($actions as $node) { - $xPath = new DOMXPath($node->ownerDocument); - $callbacks = $xPath->query("processing/serverCallback", $node); - if(!$callbacks->length) continue; - $callback = $callbacks->item(0); - - $actName = $actLabel = $node->attributes->getNamedItem("name")->nodeValue; - $text = $xPath->query("gui/@text", $node); - if ($text->length) { - $actLabel = $actName ." (" . $mess[$text->item(0)->nodeValue].")"; - } - $params = $xPath->query("processing/serverCallback/input_param", $node); - $paramLabel = array(); - if ($callback->getAttribute("developerComment") != "") { - $paramLabel[] = "".$callback->getAttribute("developerComment").""; - } - $restPath = ""; - if ($callback->getAttribute("restParams")) { - $restPath = "/api/$actName/". ltrim($callback->getAttribute("restParams"), "/"); - } - if ($restPath != null) { - $paramLabel[] = ""."API Access : ".$restPath.""; - } - if ($params->length) { - $paramLabel[] = "Expected Parameters :"; - foreach ($params as $param) { - $paramLabel[]= '. ['.$param->getAttribute("type").'] '.$param->getAttribute("name").($param->getAttribute("mandatory") == "true" ? '*':'').' : '.$param->getAttribute("description"); - } - } - $meta = array( - "icon" => "preferences_plugin.png", - "action_id" => $actName, - "parameters"=> '
    '.implode("
    ", $paramLabel).'
    ', - "rest_params"=> $restPath - ); - $nodeKey = "/$root/actions/$type/".$pObject->getName()."/$actName"; - if(in_array($nodeKey, $this->currentBookmarks)) $meta = array_merge($meta, array("ajxp_bookmarked" => "true", "overlay_icon" => "bookmark.png")); - $allNodes[$nodeKey] = $allNodesAcc[$actName] = AJXP_XMLWriter::renderNode( - $nodeKey, - $actLabel, - true, - $meta, - true, - false - ); - } - ksort($allNodesAcc); - if(!$returnNodes) print(implode("", array_values($allNodesAcc))); - } - - } - return $allNodes; - } - - public function listHooks($dir, $root = NULL, $hash = null, $returnNodes = false) - { - $jsonContent = json_decode(file_get_contents(AJXP_Utils::getHooksFile()), true); - $config = ' - - - - - - '; - if(!$returnNodes) AJXP_XMLWriter::sendFilesListComponentConfig($config); - $allNodes = array(); - foreach ($jsonContent as $hookName => $hookData) { - $metadata = array( - "icon" => "preferences_plugin.png", - "description" => $hookData["DESCRIPTION"], - "sample" => $hookData["PARAMETER_SAMPLE"], - ); - $trigs = array(); - foreach ($hookData["TRIGGERS"] as $trigger) { - $trigs[] = "".$trigger["FILE"]." (".$trigger["LINE"].")"; - } - $metadata["triggers"] = implode("
    ", $trigs); - $listeners = array(); - foreach ($hookData["LISTENERS"] as $listener) { - $listeners[] = "Plugin ".$listener["PLUGIN_ID"].", in method ".$listener["METHOD"].""; - } - $metadata["listeners"] = implode("
    ", $listeners); - $nodeKey = "/$root/hooks/$hookName/$hookName"; - if(in_array($nodeKey, $this->currentBookmarks)) $metadata = array_merge($metadata, array("ajxp_bookmarked" => "true", "overlay_icon" => "bookmark.png")); - $xml = AJXP_XMLWriter::renderNode($nodeKey, $hookName, true, $metadata, true, false); - if($returnNodes) $allNodes[$nodeKey] = $xml; - else print($xml); - } - return $allNodes; - } - - public function listLogFiles($dir, $root = NULL, $hash = null, $returnNodes = false) - { - $dir = "/$dir"; - $allNodes = array(); - $logger = AJXP_Logger::getInstance(); - $parts = explode("/", $dir); - if (count($parts)>4) { - $config = ' - - - - - - - - '; - if(!$returnNodes) AJXP_XMLWriter::sendFilesListComponentConfig($config); - $date = $parts[count($parts)-1]; - $logger->xmlLogs($dir, $date, "tree", "/".$root."/logs", isSet($_POST["cursor"])?intval($_POST["cursor"]):-1); - } else { - if(!$returnNodes) AJXP_XMLWriter::sendFilesListComponentConfig(''); - $nodes = $logger->xmlListLogFiles("tree", (count($parts)>2?$parts[2]:null), (count($parts)>3?$parts[3]:null), "/".$root."/logs", false); - foreach ($nodes as $last => $nodeXML) { - if(is_numeric($last) && $last < 10) $last = "0".$last; - $key = "/$root$dir/$last"; - if (in_array($key, $this->currentBookmarks)) { - $nodeXML = str_replace("/>", ' ajxp_bookmarked="true" overlay_icon="bookmark.png"/>', $nodeXML); - } - $allNodes[$key] = $nodeXML; - if (!$returnNodes) { - print($nodeXML); - } - } - } - return $allNodes; - } - - public function printDiagnostic($dir, $root = NULL, $hash = null, $returnNodes = false) - { - $outputArray = array(); - $testedParams = array(); - $allNodes = array(); - AJXP_Utils::runTests($outputArray, $testedParams); - AJXP_Utils::testResultsToFile($outputArray, $testedParams); - if(!$returnNodes) AJXP_XMLWriter::sendFilesListComponentConfig(''); - if (is_file(TESTS_RESULT_FILE)) { - include_once(TESTS_RESULT_FILE); - if (isset($diagResults)) { - foreach ($diagResults as $id => $value) { - $value = AJXP_Utils::xmlEntities($value); - $xml = ""; - if(!$returnNodes) print($xml); - else $allNodes["/$dir/$id"] = $xml; - } - } - } - return $allNodes; - } - - public function metaSourceOrderingFunction($key1, $key2) - { - $a1 = explode(".", $key1); - $t1 = array_shift($a1); - $a2 = explode(".", $key2); - $t2 = array_shift($a2); - if($t1 == "index") return 1; - if($t1 == "metastore") return -1; - if($t2 == "index") return -1; - if($t2 == "metastore") return 1; - if($key1 == "meta.git" || $key1 == "meta.svn") return 1; - if($key2 == "meta.git" || $key2 == "meta.svn") return -1; - return strcmp($key1, $key2); - } - - public function updateUserRole($userId, $roleId, $addOrRemove, $updateSubUsers = false) - { - $confStorage = ConfService::getConfStorageImpl(); - $user = $confStorage->createUserObject($userId); - if(!AuthService::canAdministrate($user)){ - throw new Exception("Cannot update user data for ".$userId); - } - if ($addOrRemove == "add") { - $roleObject = AuthService::getRole($roleId); - $user->addRole($roleObject); - } else { - $user->removeRole($roleId); - } - $user->save("superuser"); - $loggedUser = AuthService::getLoggedUser(); - if ($loggedUser->getId() == $user->getId()) { - AuthService::updateUser($user); - } - return $user; - - } - - - protected function parseParameters(&$repDef, &$options, $userId = null, $globalBinaries = false, $existingValues = array()) - { - AJXP_Utils::parseStandardFormParameters($repDef, $options, $userId, "DRIVER_OPTION_", ($globalBinaries?array():null)); - if(!count($existingValues)){ - return; - } - $this->mergeExistingParameters($options, $existingValues); - } - - protected function mergeExistingParameters(&$parsed, $existing){ - foreach($parsed as $k => &$v){ - if($v === "__AJXP_VALUE_SET__" && isSet($existing[$k])){ - $parsed[$k] = $existing[$k]; - }else if(is_array($v) && is_array($existing[$k])){ - $this->mergeExistingParameters($v, $existing[$k]); - } - } - } - - public function flattenKeyValues(&$result, &$definitions, $values, $parent = "") - { - foreach ($values as $key => $value) { - if (is_array($value)) { - $this->flattenKeyValues($result, $definitions, $value, $parent."/".$key); - } else { - if ($key == "instance_name") { - $result[$parent] = $value; - } - if ($key == "group_switch_value") { - $result[$parent] = $value; - } else { - $result[$parent.'/'.$key] = $value; - if(isSet($definitions[$key])){ - $definitions[$parent.'/'.$key] = $definitions[$key]; - }else if(isSet($definitions[dirname($parent)."/".$key])){ - $definitions[$parent.'/'.$key] = $definitions[dirname($parent)."/".$key]; - } - } - } - } - } - -} diff --git a/core/src/plugins/access.ajxp_conf/composer.json b/core/src/plugins/access.ajxp_conf/composer.json new file mode 100644 index 0000000000..c4be4c3902 --- /dev/null +++ b/core/src/plugins/access.ajxp_conf/composer.json @@ -0,0 +1,11 @@ +{ + "name": "Configurations Features", + "description": "Provisioning of users, workspaces, etc... ", + "homepage": "https://pydio.com/", + "license":"AGPL-3.0", + "autoload": { + "psr-4": { + "Pydio\\Access\\Driver\\DataProvider\\Provisioning\\": "./src" + } + } +} diff --git a/core/src/plugins/access.ajxp_conf/i18n/conf/de.php b/core/src/plugins/access.ajxp_conf/i18n/conf/de.php index 94ac27e154..48a5dba00f 100644 --- a/core/src/plugins/access.ajxp_conf/i18n/conf/de.php +++ b/core/src/plugins/access.ajxp_conf/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ // german translation: Axel Otterstätter diff --git a/core/src/plugins/access.ajxp_conf/i18n/conf/en.php b/core/src/plugins/access.ajxp_conf/i18n/conf/en.php index 1e3e751445..429d1f2a15 100644 --- a/core/src/plugins/access.ajxp_conf/i18n/conf/en.php +++ b/core/src/plugins/access.ajxp_conf/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "Admin Driver" => "Admin Driver", diff --git a/core/src/plugins/access.ajxp_conf/i18n/conf/it.php b/core/src/plugins/access.ajxp_conf/i18n/conf/it.php index 463cab80d5..171036dd14 100644 --- a/core/src/plugins/access.ajxp_conf/i18n/conf/it.php +++ b/core/src/plugins/access.ajxp_conf/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "Admin Driver" => "Driver Amministratore", diff --git a/core/src/plugins/access.ajxp_conf/i18n/de.php b/core/src/plugins/access.ajxp_conf/i18n/de.php index 2941b679f3..d4d4f104c3 100644 --- a/core/src/plugins/access.ajxp_conf/i18n/de.php +++ b/core/src/plugins/access.ajxp_conf/i18n/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( diff --git a/core/src/plugins/access.ajxp_conf/i18n/en.php b/core/src/plugins/access.ajxp_conf/i18n/en.php index 96b9fcb56b..57588ddd37 100644 --- a/core/src/plugins/access.ajxp_conf/i18n/en.php +++ b/core/src/plugins/access.ajxp_conf/i18n/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "1" => "Configuration Data", diff --git a/core/src/plugins/access.ajxp_conf/i18n/es.php b/core/src/plugins/access.ajxp_conf/i18n/es.php index 0cf63c5d36..1e2d00be2e 100644 --- a/core/src/plugins/access.ajxp_conf/i18n/es.php +++ b/core/src/plugins/access.ajxp_conf/i18n/es.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "1" => "Datos de Configuración", diff --git a/core/src/plugins/access.ajxp_conf/i18n/fi.php b/core/src/plugins/access.ajxp_conf/i18n/fi.php index 2843a509ed..e11b203d91 100644 --- a/core/src/plugins/access.ajxp_conf/i18n/fi.php +++ b/core/src/plugins/access.ajxp_conf/i18n/fi.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ // Finnish translation by Aleksi Postari // aleksi (at) postari.net diff --git a/core/src/plugins/access.ajxp_conf/i18n/fr.php b/core/src/plugins/access.ajxp_conf/i18n/fr.php index 578bcc0900..82e7d5c883 100644 --- a/core/src/plugins/access.ajxp_conf/i18n/fr.php +++ b/core/src/plugins/access.ajxp_conf/i18n/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "1" => "Données de configuration", diff --git a/core/src/plugins/access.ajxp_conf/i18n/hu.php b/core/src/plugins/access.ajxp_conf/i18n/hu.php index 4c8d07b682..9a0a6dab48 100644 --- a/core/src/plugins/access.ajxp_conf/i18n/hu.php +++ b/core/src/plugins/access.ajxp_conf/i18n/hu.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ // hungarian translation: Gyarmati Balázs // fixes, additions: Levente Huszkó diff --git a/core/src/plugins/access.ajxp_conf/i18n/it.php b/core/src/plugins/access.ajxp_conf/i18n/it.php index 2d370e6c6a..cc9fb06e77 100644 --- a/core/src/plugins/access.ajxp_conf/i18n/it.php +++ b/core/src/plugins/access.ajxp_conf/i18n/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "1" => "Configurazioni", diff --git a/core/src/plugins/access.ajxp_conf/i18n/nl.php b/core/src/plugins/access.ajxp_conf/i18n/nl.php index 50e3cc082c..daf633c996 100644 --- a/core/src/plugins/access.ajxp_conf/i18n/nl.php +++ b/core/src/plugins/access.ajxp_conf/i18n/nl.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "1" => "Configuratie Gegevens", diff --git a/core/src/plugins/access.ajxp_conf/i18n/pt.php b/core/src/plugins/access.ajxp_conf/i18n/pt.php index fb1abae340..5e21e92d02 100644 --- a/core/src/plugins/access.ajxp_conf/i18n/pt.php +++ b/core/src/plugins/access.ajxp_conf/i18n/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "1" => "Dados de Configuração", diff --git a/core/src/plugins/access.ajxp_conf/i18n/ru.php b/core/src/plugins/access.ajxp_conf/i18n/ru.php index c72ebff8f5..9840bdd0de 100644 --- a/core/src/plugins/access.ajxp_conf/i18n/ru.php +++ b/core/src/plugins/access.ajxp_conf/i18n/ru.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "1" => "Данные конфигурации", diff --git a/core/src/plugins/access.ajxp_conf/i18n/si.php b/core/src/plugins/access.ajxp_conf/i18n/si.php index 3e3ddac713..f73142e787 100644 --- a/core/src/plugins/access.ajxp_conf/i18n/si.php +++ b/core/src/plugins/access.ajxp_conf/i18n/si.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ // Slovenian translation: April 21 2011 by Vladimir Bohinc (vladimir.bohinc@gmail.com) $mess=array( diff --git a/core/src/plugins/access.ajxp_conf/manifest.xml b/core/src/plugins/access.ajxp_conf/manifest.xml index 55434810a0..5f8bf372c2 100644 --- a/core/src/plugins/access.ajxp_conf/manifest.xml +++ b/core/src/plugins/access.ajxp_conf/manifest.xml @@ -14,78 +14,9 @@ - - - ]]> - - #workspace_toolbar{display:none;} - #global_toolbar{background-color: #30383A;} - #browser {padding: 10px 5px 0 0;background-color: #30383A;} - .action_bar.editor_action_bar {right: 7px;} - .action_bar a{margin-top:4px} - - ]]> - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - - - - ]]>
    -
    +
    @@ -97,7 +28,7 @@
    -
    +
    @@ -121,5 +52,5 @@ - + diff --git a/core/src/plugins/access.ajxp_conf/src/AbstractManager.php b/core/src/plugins/access.ajxp_conf/src/AbstractManager.php new file mode 100644 index 0000000000..72e942898d --- /dev/null +++ b/core/src/plugins/access.ajxp_conf/src/AbstractManager.php @@ -0,0 +1,202 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Driver\DataProvider\Provisioning; + +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Access\Core\Model\NodesList; +use Pydio\Core\Utils\Vars\XMLFilter; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\UsersService; +use Pydio\Core\Utils\Vars\OptionsHelper; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Parent Class for CRUD operation of application objects. + * @package Pydio\Access\Driver\DataProvider\Provisioning + */ +abstract class AbstractManager +{ + /** @var ContextInterface */ + protected $context; + + /** @var array */ + protected $bookmarks; + + /** @var string */ + protected $pluginName; + + /** @var bool */ + protected $listSpecialRoles = AJXP_SERVER_DEBUG; + + /** + * Manager constructor. + * @param ContextInterface $ctx + * @param string $pluginName + */ + public function __construct(ContextInterface $ctx, $pluginName){ + $this->context = $ctx; + $this->pluginName = $pluginName; + } + + /** + * @return bool + */ + protected function currentUserIsGroupAdmin(){ + if(ConfService::getAuthDriverImpl()->isAjxpAdmin($this->context->getUser()->getId())){ + return false; + } + return (UsersService::usersEnabled() && $this->context->getUser()->getGroupPath() !== "/"); + } + + /** + * @return array + */ + protected function getBookmarks(){ + if(!isSet($this->bookmarks)){ + $this->bookmarks = []; + if(UsersService::usersEnabled()) { + $bookmarks = $this->context->getUser()->getBookmarks($this->context->getRepositoryId()); + foreach ($bookmarks as $bm) { + $this->bookmarks[] = $bm["PATH"]; + } + } + } + return $this->bookmarks; + } + + /** + * @param string $nodePath + * @param array $meta + */ + protected function appendBookmarkMeta($nodePath, &$meta){ + if(in_array($nodePath, $this->getBookmarks())) { + $meta = array_merge($meta, array( + "ajxp_bookmarked" => "true", + "overlay_icon" => "bookmark.png" + )); + } + } + + + /** + * @param ContextInterface $ctx + * @param $repDef + * @param $options + * @param bool $globalBinaries + * @param array $existingValues + */ + protected function parseParameters(ContextInterface $ctx, &$repDef, &$options, $globalBinaries = false, $existingValues = array()) + { + OptionsHelper::parseStandardFormParameters($ctx, $repDef, $options, "DRIVER_OPTION_", ($globalBinaries ? array() : null)); + if(!count($existingValues)){ + return; + } + $this->mergeExistingParameters($options, $existingValues); + } + + /** + * @param array $parsed + * @param array $existing + */ + protected function mergeExistingParameters(&$parsed, $existing){ + foreach($parsed as $k => &$v){ + if($v === "__AJXP_VALUE_SET__" && isSet($existing[$k])){ + $parsed[$k] = $existing[$k]; + }else if(is_array($v) && is_array($existing[$k])){ + $this->mergeExistingParameters($v, $existing[$k]); + } + } + } + + + /** + * @param ContextInterface $ctx + * @param $currentUserIsGroupAdmin + * @param bool $withLabel + * @return array + */ + protected function getEditableParameters($ctx, $currentUserIsGroupAdmin, $withLabel = false){ + + $query = "//param|//global_param"; + if($currentUserIsGroupAdmin){ + $query = "//param[@scope]|//global_param[@scope]"; + } + + $nodes = PluginsService::getInstance($ctx)->searchAllManifests($query, "node", false, true, true); + $actions = array(); + foreach ($nodes as $node) { + if($node->parentNode->nodeName != "server_settings") continue; + $parentPlugin = $node->parentNode->parentNode; + $pId = $parentPlugin->attributes->getNamedItem("id")->nodeValue; + if (empty($pId)) { + $pId = $parentPlugin->nodeName ."."; + if($pId == "ajxpdriver.") $pId = "access."; + $pId .= $parentPlugin->attributes->getNamedItem("name")->nodeValue; + } + if(!is_array($actions[$pId])) $actions[$pId] = array(); + $actionName = $node->attributes->getNamedItem("name")->nodeValue; + $attributes = array(); + for( $i = 0; $i < $node->attributes->length; $i ++){ + $att = $node->attributes->item($i); + $value = $att->nodeValue; + if(in_array($att->nodeName, array("choices", "description", "group", "label"))) { + $value = XMLFilter::resolveKeywords($value); + } + $attributes[$att->nodeName] = $value; + } + if($withLabel){ + $actions[$pId][$actionName] = array( + "parameter" => $actionName , + "label" => $attributes["label"], + "attributes" => $attributes + ); + }else{ + $actions[$pId][] = $actionName; + } + + } + foreach ($actions as $actPid => $actionGroup) { + ksort($actionGroup, SORT_STRING); + $actions[$actPid] = array(); + foreach ($actionGroup as $v) { + $actions[$actPid][] = $v; + } + } + return $actions; + } + + + /** + * @param ServerRequestInterface $requestInterface + * @param string $rootPath Path to prepend to the resulting nodes + * @param string $relativePath Specific path part for this function + * @param string $paginationHash Number added to url#2 for pagination purpose. + * @param string $findNodePosition Path to a given node to try to find it + * @param string $aliasedDir Aliased path used for alternative url + * @return NodesList A populated NodesList object, eventually recursive. + */ + public abstract function listNodes(ServerRequestInterface $requestInterface, $rootPath, $relativePath, $paginationHash = null, $findNodePosition=null, $aliasedDir=null); + +} \ No newline at end of file diff --git a/core/src/plugins/access.ajxp_conf/src/ActionsManager.php b/core/src/plugins/access.ajxp_conf/src/ActionsManager.php new file mode 100644 index 0000000000..c77c79da51 --- /dev/null +++ b/core/src/plugins/access.ajxp_conf/src/ActionsManager.php @@ -0,0 +1,176 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Driver\DataProvider\Provisioning; + +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Model\NodesList; +use Pydio\Core\PluginFramework\Plugin; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Services\LocaleService; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class ActionsManager + * @package Pydio\Access\Driver\DataProvider\Provisioning + */ +class ActionsManager extends AbstractManager +{ + + /** + * @param ServerRequestInterface $requestInterface Full set of query parameters + * @param string $rootPath Path to prepend to the resulting nodes + * @param string $relativePath Specific path part for this function + * @param string $paginationHash Number added to url#2 for pagination purpose. + * @param string $findNodePosition Path to a given node to try to find it + * @param string $aliasedDir Aliased path used for alternative url + * @return NodesList A populated NodesList object, eventually recursive. + * @throws \Pydio\Core\Exception\PydioException + */ + public function listNodes(ServerRequestInterface $requestInterface, $rootPath, $relativePath, $paginationHash = null, $findNodePosition = null, $aliasedDir = null) + { + $nodesList = new NodesList("/$rootPath/$relativePath"); + $parts = explode("/",$relativePath); + $pServ = PluginsService::getInstance($this->context); + $types = $pServ->getDetectedPlugins(); + + if (count($parts) == 1) { + + $nodesList->initColumnsData("filelist", "list", "ajxp_conf.actions_list"); + $nodesList->appendColumn("ajxp_conf.101", "ajxp_label"); + ksort($types); + foreach ($types as $t => $tPlugs) { + if(!empty($findNodePosition) && $t !== $findNodePosition) continue; + + $meta = array( + "icon" => "folder_development.png", + "plugin_id" => $t, + "text" => $t, + "is_file" => false + ); + $nodeKey = "/$rootPath/actions/".$t; + $this->appendBookmarkMeta($nodeKey, $meta); + $nodesList->addBranch(new AJXP_Node($nodeKey, $meta)); + } + + } else if (count($parts) == 2) { + // list plugs + $type = $parts[1]; + $nodesList->initColumnsData("filelist", "detail", "ajxp_conf.actions_list_plug"); + $nodesList->appendColumn("ajxp_conf.101", "ajxp_label"); + $nodesList->appendColumn("ajxp_conf.103", "actions"); + /** @var Plugin $pObject */ + foreach ($types[$type] as $pObject) { + if(!empty($findNodePosition) && $pObject->getName() !== $findNodePosition) continue; + + $actions = $pObject->getManifestRawContent("//action/@name", "xml", true); + $actLabel = array(); + if ($actions->length) { + foreach ($actions as $node) { + $actLabel[] = $node->nodeValue; + } + } + $meta = array( + "icon" => "preferences_plugin.png", + "text" => $pObject->getManifestLabel(), + "plugin_id" => $pObject->getId(), + "is_file" => false, + "actions" => implode(", ", $actLabel) + ); + $nodeKey = "/$rootPath/actions/$type/".$pObject->getName(); + $this->appendBookmarkMeta($nodeKey, $meta); + $nodesList->addBranch(new AJXP_Node($nodeKey, $meta)); + } + + } else if (count($parts) == 3) { + // list actions + $type = $parts[1]; + $name = $parts[2]; + $mess = LocaleService::getMessages(); + + $nodesList->initColumnsData("full", "full", "ajxp_conf.actions_list_plugs"); + $nodesList->appendColumn("ajxp_conf.101", "ajxp_label", "String", "10%"); + $nodesList->appendColumn("ajxp_conf.103", "parameters", "String", "30%"); + $nodesList->appendColumn("Rest API", "rest_params", "String", "30%"); + + /** @var Plugin $pObject */ + $pObject = $types[$type][$name]; + + $actions = $pObject->getManifestRawContent("//action", "xml", true); + $allNodesAcc = array(); + if ($actions->length) { + foreach ($actions as $node) { + $xPath = new \DOMXPath($node->ownerDocument); + $callbacks = $xPath->query("processing/serverCallback", $node); + if(!$callbacks->length) continue; + /** @var \DOMElement $callback */ + $callback = $callbacks->item(0); + + $actName = $actLabel = $node->attributes->getNamedItem("name")->nodeValue; + if(!empty($findNodePosition) && $actName !== $findNodePosition) continue; + + $text = $xPath->query("gui/@text", $node); + if ($text->length) { + $actLabel = $actName ." (" . $mess[$text->item(0)->nodeValue].")"; + } + $params = $xPath->query("processing/serverCallback/input_param", $node); + $paramLabel = array(); + if ($callback->getAttribute("developerComment") != "") { + $paramLabel[] = "".$callback->getAttribute("developerComment").""; + } + $restPath = ""; + if ($callback->getAttribute("restParams")) { + $restPath = "/api/$actName/". ltrim($callback->getAttribute("restParams"), "/"); + } + if ($restPath != null) { + $paramLabel[] = ""."API Access : ".$restPath.""; + } + if ($params->length) { + $paramLabel[] = "Expected Parameters :"; + /** @var \DOMElement $param */ + foreach ($params as $param) { + $paramLabel[]= '. ['.$param->getAttribute("type").'] '.$param->getAttribute("name").($param->getAttribute("mandatory") == "true" ? '*':'').' : '.$param->getAttribute("description"); + } + } + $meta = array( + "icon" => "preferences_plugin.png", + "text" => $actLabel, + "action_id" => $actName, + "parameters" => '
    '.implode("
    ", $paramLabel).'
    ', + "rest_params" => $restPath + ); + $nodeKey = "/$rootPath/actions/$type/".$pObject->getName()."/$actName"; + $this->appendBookmarkMeta($nodeKey, $meta); + $node = new AJXP_Node($nodeKey, $meta); + $allNodesAcc[$actName] = $node; + } + ksort($allNodesAcc); + foreach($allNodesAcc as $path => $node){ + $nodesList->addBranch($node); + } + } + + } + return $nodesList; + + } +} \ No newline at end of file diff --git a/core/src/plugins/access.ajxp_conf/src/ConfAccessDriver.php b/core/src/plugins/access.ajxp_conf/src/ConfAccessDriver.php new file mode 100644 index 0000000000..883f4a9b19 --- /dev/null +++ b/core/src/plugins/access.ajxp_conf/src/ConfAccessDriver.php @@ -0,0 +1,468 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + * + */ +namespace Pydio\Access\Driver\DataProvider\Provisioning; + +require_once(dirname(__FILE__)."/../vendor/autoload.php"); + +use DOMXPath; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Core\Http\Response\SerializableResponseStream; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Controller\Controller; +use Pydio\Core\Services\UsersService; +use Zend\Diactoros\Response\JsonResponse; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Plugin to access the configurations data + * Class ConfAccessDriver + * @package Pydio\Access\Driver\DataProvider + */ +class ConfAccessDriver extends AbstractAccessDriver +{ + + protected $rootNodes = array( + "data" => array( + "LABEL" => "ajxp_conf.110", + "ICON" => "user.png", + "DESCRIPTION" => "ajxp_conf.137", + "CHILDREN" => array( + "repositories" => array( + "AJXP_MIME" => "workspaces_zone", + "LABEL" => "ajxp_conf.3", + "DESCRIPTION" => "ajxp_conf.138", + "ICON" => "hdd_external_unmount.png", + "MANAGER" => "Pydio\\Access\\Driver\\DataProvider\\Provisioning\\RepositoriesManager" + ), + "users" => array( + "AJXP_MIME" => "users_zone", + "LABEL" => "ajxp_conf.2", + "DESCRIPTION" => "ajxp_conf.139", + "ICON" => "users-folder.png", + "MANAGER" => "Pydio\\Access\\Driver\\DataProvider\\Provisioning\\UsersManager" + ), + "roles" => array( + "AJXP_MIME" => "roles_zone", + "LABEL" => "ajxp_conf.69", + "DESCRIPTION" => "ajxp_conf.140", + "ICON" => "user-acl.png", + "MANAGER" => "Pydio\\Access\\Driver\\DataProvider\\Provisioning\\RolesManager" + ), + ) + ), + "config" => array( + "AJXP_MIME" => "plugins_zone", + "LABEL" => "ajxp_conf.109", + "ICON" => "preferences_desktop.png", + "DESCRIPTION" => "ajxp_conf.136", + "CHILDREN" => array( + "core" => array( + "AJXP_MIME" => "plugins_zone", + "LABEL" => "ajxp_conf.98", + "DESCRIPTION" => "ajxp_conf.133", + "ICON" => "preferences_desktop.png", + "MANAGER" => "Pydio\\Access\\Driver\\DataProvider\\Provisioning\\PluginsManager" + ), + "plugins" => array( + "AJXP_MIME" => "plugins_zone", + "LABEL" => "ajxp_conf.99", + "DESCRIPTION" => "ajxp_conf.134", + "ICON" => "folder_development.png", + "MANAGER" => "Pydio\\Access\\Driver\\DataProvider\\Provisioning\\PluginsManager" + ), + "core_plugins" => array( + "AJXP_MIME" => "plugins_zone", + "LABEL" => "ajxp_conf.123", + "DESCRIPTION" => "ajxp_conf.135", + "ICON" => "folder_development.png", + "MANAGER" => "Pydio\\Access\\Driver\\DataProvider\\Provisioning\\PluginsManager" + ), + ) + ), + "admin" => array( + "LABEL" => "ajxp_conf.111", + "ICON" => "toggle_log.png", + "DESCRIPTION" => "ajxp_conf.141", + "CHILDREN" => array( + "logs" => array( + "LABEL" => "ajxp_conf.4", + "DESCRIPTION" => "ajxp_conf.142", + "ICON" => "toggle_log.png", + "MANAGER" => "Pydio\\Access\\Driver\\DataProvider\\Provisioning\\LogsManager" + ), + "diagnostic" => array( + "LABEL" => "ajxp_conf.5", + "DESCRIPTION" => "ajxp_conf.143", + "ICON" => "susehelpcenter.png", + "MANAGER" => "Pydio\\Access\\Driver\\DataProvider\\Provisioning\\DiagnosticManager" + ) + ) + ), + "developer" => array( + "LABEL" => "ajxp_conf.144", + "ICON" => "applications_engineering.png", + "DESCRIPTION" => "ajxp_conf.145", + "CHILDREN" => array( + "actions" => array( + "LABEL" => "ajxp_conf.146", + "DESCRIPTION" => "ajxp_conf.147", + "ICON" => "book.png", + "MANAGER" => "Pydio\\Access\\Driver\\DataProvider\\Provisioning\\ActionsManager" + ), + "hooks" => array( + "LABEL" => "ajxp_conf.148", + "DESCRIPTION" => "ajxp_conf.149", + "ICON" => "book.png", + "MANAGER" => "Pydio\\Access\\Driver\\DataProvider\\Provisioning\\DocumentationManager" + ) + ) + ) + ); + + /** + * Called internally to populate left menu + * @param ContextInterface $ctx + * @return array + * @throws \Exception + */ + protected function getMainTree(ContextInterface $ctx){ + $rootNodes = $this->rootNodes; + $user = $ctx->getUser(); + if ($user != null && $user->getGroupPath() != "/") { + // Group Admin + unset($rootNodes["config"]); + unset($rootNodes["admin"]); + unset($rootNodes["developer"]); + } + Controller::applyHook("ajxp_conf.list_config_nodes", array($ctx, &$rootNodes)); + return $rootNodes; + } + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @return bool + */ + public function preprocessLsApi2(ServerRequestInterface &$requestInterface, ResponseInterface &$responseInterface){ + + $uri = $requestInterface->getAttribute("api_uri"); + $vars = $requestInterface->getParsedBody(); + if($uri === "/admin/roles") { + + $vars["dir"] = "/data/roles"; + $requestInterface = $requestInterface->withParsedBody($vars); + + }else if(strpos($uri, "/admin/people") === 0){ + + $crtPath = ""; + if(isSet($vars["path"]) && !empty($vars["path"]) && $vars["path"] !== "/"){ + $crtPath = $vars["path"]; + } + $vars["dir"] = "/data/users".$crtPath; + $requestInterface = $requestInterface->withParsedBody($vars); + + } + + + } + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + */ + public function listAction(ServerRequestInterface $requestInterface, ResponseInterface &$responseInterface){ + + if($requestInterface->getAttribute("action") === "stat"){ + $responseInterface = new JsonResponse(["mode" => true]); + return; + } + + if($requestInterface->getAttribute("api") === "v2"){ + + $uri = $requestInterface->getAttribute("api_uri"); + $vars = $requestInterface->getParsedBody(); + if($uri === "/admin/roles") { + + $vars["dir"] = "/data/roles"; + $requestInterface = $requestInterface->withParsedBody($vars); + + }else if($uri === "/admin/workspaces") { + + $vars["dir"] = "/data/repositories"; + $requestInterface = $requestInterface->withParsedBody($vars); + + }else if(strpos($uri, "/admin/people") === 0){ + + $crtPath = ""; + if(isSet($vars["path"]) && !empty($vars["path"]) && $vars["path"] !== "/"){ + $crtPath = $vars["path"]; + } + $vars["dir"] = "/data/users/".ltrim($crtPath, "/"); + $requestInterface = $requestInterface->withParsedBody($vars); + + } + } + + /** @var ContextInterface $ctx */ + $ctx = $requestInterface->getAttribute("ctx"); + $treeManager = new TreeManager($ctx, $this->getName(), $this->getMainTree($ctx)); + $nodesList = $treeManager->dispatchList($requestInterface); + $responseInterface = $responseInterface->withBody(new SerializableResponseStream($nodesList)); + + } + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + */ + public function pluginsAction(ServerRequestInterface $requestInterface, ResponseInterface &$responseInterface){ + $pluginManager = new PluginsManager($requestInterface->getAttribute("ctx"), $this->getName()); + $responseInterface = $pluginManager->pluginsActions($requestInterface, $responseInterface); + } + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + */ + public function rolesAction(ServerRequestInterface $requestInterface, ResponseInterface &$responseInterface){ + $pluginManager = new RolesManager($requestInterface->getAttribute("ctx"), $this->getName()); + $responseInterface = $pluginManager->rolesActions($requestInterface, $responseInterface); + } + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + */ + public function usersAction(ServerRequestInterface $requestInterface, ResponseInterface &$responseInterface){ + $pluginManager = new UsersManager($requestInterface->getAttribute("ctx"), $this->getName()); + $action = $requestInterface->getAttribute("action"); + if(strpos($action, "people-") === 0){ + $responseInterface = $pluginManager->peopleApiActions($requestInterface, $responseInterface); + }else{ + $responseInterface = $pluginManager->usersActions($requestInterface, $responseInterface); + } + } + + /** + * Search users + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + */ + public function searchUsersAction(ServerRequestInterface $requestInterface, ResponseInterface &$responseInterface) { + $pluginManager = new UsersManager($requestInterface->getAttribute("ctx"), $this->getName()); + $responseInterface = $pluginManager->search($requestInterface, $responseInterface); + } + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + */ + public function repositoriesAction(ServerRequestInterface $requestInterface, ResponseInterface &$responseInterface){ + $pluginManager = new RepositoriesManager($requestInterface->getAttribute("ctx"), $this->getName()); + $responseInterface = $pluginManager->repositoriesActions($requestInterface, $responseInterface); + } + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + */ + public function editAction(ServerRequestInterface $requestInterface, ResponseInterface &$responseInterface){ + + $subAction = $requestInterface->getParsedBody()["sub_action"]; + $requestInterface = $requestInterface->withAttribute("action", $subAction); + + switch($subAction){ + case "edit_plugin_options": + $this->pluginsAction($requestInterface, $responseInterface); + break; + case "edit_role": + case "post_json_role": + $this->rolesAction($requestInterface, $responseInterface); + break; + case "user_set_lock": + case "change_admin_right": + case "user_add_role": + case "create_user": + case "user_delete_role": + case "user_reorder_roles": + case "users_bulk_update_roles": + case "save_custom_user_params": + case "save_repository_user_params": + case "update_user_pwd": + $this->usersAction($requestInterface, $responseInterface); + break; + case "edit_repository": + case "create_repository": + case "edit_repository_label": + case "edit_repository_data": + case "get_drivers_definition": + case "get_templates_definition": + case "meta_source_add": + case "meta_source_edit": + case "meta_source_delete": + $this->repositoriesAction($requestInterface, $responseInterface); + break; + default: + break; + } + } + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + */ + public function deleteAction(ServerRequestInterface $requestInterface, ResponseInterface &$responseInterface){ + + $httpVars = $requestInterface->getParsedBody(); + // REST API V1 mapping + if (isSet($httpVars["data_type"])) { + switch ($httpVars["data_type"]) { + case "repository": + $httpVars["repository_id"] = basename($httpVars["data_id"]); + break; + case "role": + $httpVars["role_id"] = basename($httpVars["data_id"]); + break; + case "user": + $httpVars["user_id"] = basename($httpVars["data_id"]); + break; + case "group": + $httpVars["group"] = "/data/users".$httpVars["data_id"]; + break; + default: + break; + } + unset($httpVars["data_type"]); + unset($httpVars["data_id"]); + $requestInterface = $requestInterface->withParsedBody($httpVars); + + } + + /** @var ContextInterface $ctx */ + $ctx = $requestInterface->getAttribute("ctx"); + + if (isSet($httpVars["repository_id"]) || isSet($httpVars["workspaceId"])) { + + $manager = new RepositoriesManager($ctx, $this->getName()); + + } else if (isSet($httpVars["role_id"]) || isSet($httpVars["roleId"])) { + + $manager = new RolesManager($ctx, $this->getName()); + + } else { + + $manager = new UsersManager($ctx, $this->getName()); + } + + $responseInterface = $manager->delete($requestInterface, $responseInterface); + + } + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + */ + public function documentationAction(ServerRequestInterface $requestInterface, ResponseInterface &$responseInterface) { + $docManager = new DocumentationManager($requestInterface->getAttribute("ctx"), $this->getName()); + return $docManager->docActions($requestInterface, $responseInterface); + + } + + /** + * Bookmark any page for the admin interface + * @param ServerRequestInterface $request + * @param ResponseInterface $response + */ + public function preProcessBookmarkAction(ServerRequestInterface &$request, ResponseInterface $response) + { + + $httpVars = $request->getParsedBody(); + /** @var ContextInterface $ctx */ + $ctx = $request->getAttribute("ctx"); + if (isSet($httpVars["bm_action"]) && $httpVars["bm_action"] == "add_bookmark" && UsersService::usersEnabled()) { + $bmUser = $ctx->getUser(); + $repositoryId = $ctx->getRepositoryId(); + $bookmarks = $bmUser->getBookmarks($repositoryId); + foreach ($bookmarks as $bm) { + if ($bm["PATH"] == $httpVars["bm_path"]) { + $httpVars["bm_action"] = "delete_bookmark"; + $request = $request->withParsedBody($httpVars); + break; + } + } + } + + } + + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + */ + public function displayEnterprise(ServerRequestInterface $requestInterface, ResponseInterface &$responseInterface){ + $touchFile = $this->getPluginWorkDir(true).DIRECTORY_SEPARATOR."enterprise-display"; + if(file_exists($touchFile)){ + $lastDisplay = intval(file_get_contents($touchFile)); + if(time() - $lastDisplay > 60 * 60 * 24 * 15){ + $responseInterface = new JsonResponse(["display" => true]); + file_put_contents($touchFile, time()); + }else{ + $responseInterface = new JsonResponse(["display" => false]); + echo json_encode(["display" => false]); + } + }else{ + $responseInterface = new JsonResponse(["display" => true]); + file_put_contents($touchFile, time()); + } + } + + + /********************/ + /* PLUGIN LIFECYCLE + /********************/ + /** + * @inheritdoc + */ + public function parseSpecificContributions(ContextInterface $ctx, \DOMNode &$contribNode) + { + parent::parseSpecificContributions($ctx, $contribNode); + if($contribNode->nodeName != "actions") return; + $currentUserIsGroupAdmin = ($ctx->hasUser() && $ctx->getUser()->getGroupPath() != "/"); + if(!$currentUserIsGroupAdmin) return; + $actionXpath=new DOMXPath($contribNode->ownerDocument); + $publicUrlNodeList = $actionXpath->query('action[@name="create_repository"]/subMenu', $contribNode); + if ($publicUrlNodeList->length) { + $publicUrlNode = $publicUrlNodeList->item(0); + $publicUrlNode->parentNode->removeChild($publicUrlNode); + } + } + + /** + * @param ContextInterface $ctx + */ + protected function initRepository(ContextInterface $ctx){ + } + +} \ No newline at end of file diff --git a/core/src/plugins/access.ajxp_conf/src/DiagnosticManager.php b/core/src/plugins/access.ajxp_conf/src/DiagnosticManager.php new file mode 100644 index 0000000000..835d96a782 --- /dev/null +++ b/core/src/plugins/access.ajxp_conf/src/DiagnosticManager.php @@ -0,0 +1,83 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Driver\DataProvider\Provisioning; + +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Model\NodesList; +use Pydio\Core\Utils\Reflection\DiagnosticRunner; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Class DiagnosticManager + * @package Pydio\Access\Driver\DataProvider\Provisioning + */ +class DiagnosticManager extends AbstractManager +{ + + /** + * @param ServerRequestInterface $requestInterface Full set of query parameters + * @param string $rootPath Path to prepend to the resulting nodes + * @param string $relativePath Specific path part for this function + * @param string $paginationHash Number added to url#2 for pagination purpose. + * @param string $findNodePosition Path to a given node to try to find it + * @param string $aliasedDir Aliased path used for alternative url + * @return NodesList A populated NodesList object, eventually recursive. + */ + public function listNodes(ServerRequestInterface $requestInterface, $rootPath, $relativePath, $paginationHash = null, $findNodePosition = null, $aliasedDir = null) + { + $outputArray = array(); + $testedParams = array(); + $path = "/$rootPath/$relativePath"; + $nodesList = new NodesList($path); + + DiagnosticRunner::runTests($outputArray, $testedParams); + DiagnosticRunner::testResultsToFile($outputArray, $testedParams); + + $nodesList->initColumnsData("filelist", "list", "ajxp_conf.diagnostic"); + $nodesList->appendColumn("ajxp_conf.23", "ajxp_label"); + $nodesList->appendColumn("ajxp_conf.24", "data"); + + if (is_file(TESTS_RESULT_FILE) || is_file(TESTS_RESULT_FILE_LEGACY)) { + if(is_file(TESTS_RESULT_FILE_LEGACY)){ + include_once(TESTS_RESULT_FILE_LEGACY); + }else{ + include_once(TESTS_RESULT_FILE); + } + if (isset($diagResults)) { + foreach ($diagResults as $id => $value) { + $nodeKey = $path."/".$id; + $meta = [ + "icon" => "susehelpcenter.png", + "text" => $id, + "data" => $value, + "ajxp_mime" => "testResult" + ]; + $nodesList->addBranch(new AJXP_Node($nodeKey, $meta)); + } + } + } + return $nodesList; + + } +} \ No newline at end of file diff --git a/core/src/plugins/access.ajxp_conf/src/DocumentationManager.php b/core/src/plugins/access.ajxp_conf/src/DocumentationManager.php new file mode 100644 index 0000000000..0a17a4032d --- /dev/null +++ b/core/src/plugins/access.ajxp_conf/src/DocumentationManager.php @@ -0,0 +1,98 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Driver\DataProvider\Provisioning; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Model\NodesList; +use Pydio\Core\Utils\Reflection\DocsParser; +use Pydio\Core\Utils\Reflection\PydioSdkGenerator; +use Zend\Diactoros\Response\JsonResponse; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Class HooksManager + * @package Pydio\Access\Driver\DataProvider\Provisioning + */ +class DocumentationManager extends AbstractManager +{ + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @return ResponseInterface + */ + public function docActions(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface){ + + PydioSdkGenerator::analyzeRegistry(isSet($httpVars["version"])?$httpVars["version"]:AJXP_VERSION); + + return new JsonResponse(["result"=>"ok"]); + } + + /** + * @param ServerRequestInterface $requestInterface Full set of query parameters + * @param string $rootPath Path to prepend to the resulting nodes + * @param string $relativePath Specific path part for this function + * @param string $paginationHash Number added to url#2 for pagination purpose. + * @param string $findNodePosition Path to a given node to try to find it + * @param string $aliasedDir Aliased path used for alternative url + * @return NodesList A populated NodesList object, eventually recursive. + */ + public function listNodes(ServerRequestInterface $requestInterface, $rootPath, $relativePath, $paginationHash = null, $findNodePosition = null, $aliasedDir = null) + { + $nodesList = new NodesList("/$rootPath/$relativePath"); + $jsonContent = json_decode(file_get_contents(DocsParser::getHooksFile()), true); + $nodesList->initColumnsData("full", "full", "hooks.list"); + $nodesList->appendColumn("ajxp_conf.17", "ajxp_label", "String", "20%"); + $nodesList->appendColumn("ajxp_conf.18", "description", "String", "20%"); + $nodesList->appendColumn("ajxp_conf.19", "triggers", "String", "25%"); + $nodesList->appendColumn("ajxp_conf.20", "listeners", "String", "25%"); + $nodesList->appendColumn("ajxp_conf.21", "sample", "String", "10%"); + + foreach ($jsonContent as $hookName => $hookData) { + $metadata = array( + "icon" => "preferences_plugin.png", + "text" => $hookName, + "description" => $hookData["DESCRIPTION"], + "sample" => $hookData["PARAMETER_SAMPLE"], + ); + $trigs = array(); + foreach ($hookData["TRIGGERS"] as $trigger) { + $trigs[] = "".$trigger["FILE"]." (".$trigger["LINE"].")"; + } + $metadata["triggers"] = implode("
    ", $trigs); + $listeners = array(); + foreach ($hookData["LISTENERS"] as $listener) { + $listeners[] = "Plugin ".$listener["PLUGIN_ID"].", in method ".$listener["METHOD"].""; + } + $metadata["listeners"] = implode("
    ", $listeners); + $nodeKey = "/$rootPath/hooks/$hookName/$hookName"; + $this->appendBookmarkMeta($nodeKey, $meta); + $nodesList->addBranch(new AJXP_Node($nodeKey, $meta)); + } + + return $nodesList; + + } +} \ No newline at end of file diff --git a/core/src/plugins/access.ajxp_conf/src/LogsManager.php b/core/src/plugins/access.ajxp_conf/src/LogsManager.php new file mode 100644 index 0000000000..ef615ac22c --- /dev/null +++ b/core/src/plugins/access.ajxp_conf/src/LogsManager.php @@ -0,0 +1,92 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Driver\DataProvider\Provisioning; + +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Model\NodesList; +use Pydio\Log\Core\Logger; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class LogsManager + * @package Pydio\Access\Driver\DataProvider\Provisioning + */ +class LogsManager extends AbstractManager +{ + + /** + * @param ServerRequestInterface $requestInterface Full set of query parameters + * @param string $rootPath Path to prepend to the resulting nodes + * @param string $relativePath Specific path part for this function + * @param string $paginationHash Number added to url#2 for pagination purpose. + * @param string $findNodePosition Path to a given node to try to find it + * @param string $aliasedDir Aliased path used for alternative url + * @return NodesList A populated NodesList object, eventually recursive. + */ + public function listNodes(ServerRequestInterface $requestInterface, $rootPath, $relativePath, $paginationHash = null, $findNodePosition = null, $aliasedDir = null) + { + $relativePath = "/$relativePath"; + $nodesList = new NodesList("/".$rootPath.$relativePath); + $logger = Logger::getInstance(); + $parts = explode("/", $relativePath); + $leaf = false; + + if (count($parts)>4) { + + $nodesList->initColumnsData("filelist", "list", "ajxp_conf.logs"); + $nodesList->appendColumn("ajxp_conf.17", "date", "MyDate", "18%"); + $nodesList->appendColumn("ajxp_conf.18", "ip", "String", "5%"); + $nodesList->appendColumn("ajxp_conf.19", "level", "String", "10%"); + $nodesList->appendColumn("ajxp_conf.20", "user", "String", "5%"); + $nodesList->appendColumn("ajxp_conf.124", "source", "String", "5%"); + $nodesList->appendColumn("ajxp_conf.21", "action", "String", "7%"); + $nodesList->appendColumn("ajxp_conf.22", "params", "String", "50%"); + + $leaf = true; + $date = $parts[count($parts)-1]; + $logs = $logger->listLogs($relativePath, $date, "tree", "/" . $rootPath . "/logs", isSet($_POST["cursor"]) ? intval($_POST["cursor"]) : -1); + + } else { + + $nodesList->initColumnsData("filelist", "list", "ajxp_conf.logs"); + $nodesList->appendColumn("ajxp_conf.16", "ajxp_label"); + if(count($parts) > 3) $leaf = true; + $logs = $logger->listLogFiles("tree", (count($parts) > 2 ? $parts[2] : null), (count($parts) > 3 ? $parts[3] : null), "/" . $rootPath . "/logs"); + + } + + foreach($logs as $path => $meta){ + + if(!empty($findNodePosition) && basename($path) !== $findNodePosition){ + continue; + } + if($leaf) $meta["is_file"] = true; + $this->appendBookmarkMeta($path, $meta); + $nodesList->addBranch(new AJXP_Node($path, $meta)); + + } + + return $nodesList; + + } +} \ No newline at end of file diff --git a/core/src/plugins/access.ajxp_conf/src/PluginsManager.php b/core/src/plugins/access.ajxp_conf/src/PluginsManager.php new file mode 100644 index 0000000000..8e52b7c765 --- /dev/null +++ b/core/src/plugins/access.ajxp_conf/src/PluginsManager.php @@ -0,0 +1,502 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Driver\DataProvider\Provisioning; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Model\NodesList; +use Pydio\Core\Utils\Vars\XMLFilter; +use Pydio\Core\Http\Message\ReloadMessage; +use Pydio\Core\Http\Message\UserMessage; +use Pydio\Core\Http\Message\XMLDocMessage; +use Pydio\Core\Http\Response\SerializableResponseStream; +use Pydio\Core\PluginFramework\Plugin; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Services\CacheService; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Core\Utils\Vars\StringHelper; +use Zend\Diactoros\Response\JsonResponse; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class PluginsManager + * @package Pydio\Access\Driver\DataProvider\Provisioning + */ +class PluginsManager extends AbstractManager +{ + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @return ResponseInterface + */ + public function pluginsActions(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface){ + + $action = $requestInterface->getAttribute("action"); + $ctx = $requestInterface->getAttribute("ctx"); + $httpVars = $requestInterface->getParsedBody(); + $mess = LocaleService::getMessages(); + + switch ($action){ + // PLUGINS + case "clear_plugins_cache": + + ConfService::clearAllCaches(); + $userMessage = new UserMessage($mess["ajxp_conf." . (AJXP_SKIP_CACHE ? "132" : "131")]); + $reloadMessage = new ReloadMessage(); + $responseInterface = $responseInterface->withBody(new SerializableResponseStream([$userMessage, $reloadMessage])); + + break; + + case "get_plugin_manifest" : + + $pluginId = InputFilter::sanitize($httpVars["plugin_id"], InputFilter::SANITIZE_ALPHANUM); + if($pluginId === "core") { + $pluginId = "core.ajaxplorer"; + } + $ajxpPlugin = PluginsService::getInstance($ctx)->getPluginById($pluginId); + $buffer = ""; + + $fullManifest = $ajxpPlugin->getManifestRawContent("", "xml"); + $xPath = new \DOMXPath($fullManifest->ownerDocument); + $addParams = ""; + $instancesDefinitions = array(); + $pInstNodes = $xPath->query("server_settings/global_param[contains(@type, 'plugin_instance:')]"); + /** @var \DOMElement $pInstNode */ + foreach ($pInstNodes as $pInstNode) { + $type = $pInstNode->getAttribute("type"); + $instType = str_replace("plugin_instance:", "", $type); + $fieldName = $pInstNode->getAttribute("name"); + $pInstNode->setAttribute("type", "group_switch:" . $fieldName); + $typePlugs = PluginsService::getInstance($ctx)->getPluginsByType($instType); + foreach ($typePlugs as $typePlug) { + if (!$typePlug->isEnabled()) continue; + if ($typePlug->getId() == "auth.multi") continue; + $checkErrorMessage = ""; + try { + $typePlug->performChecks(); + } catch (\Exception $e) { + $checkErrorMessage = " (Warning : " . $e->getMessage() . ")"; + } + $tParams = XMLFilter::resolveKeywords($typePlug->getManifestRawContent("server_settings/param[not(@group_switch_name)]")); + $addParams .= ''; + $addParams .= str_replace("getManifestLabel() . $checkErrorMessage . "\" group_switch_value=\"" . $typePlug->getId() . "\" ", $tParams); + $addParams .= str_replace("getManifestRawContent("server_settings/param[@group_switch_name]"))); + $addParams .= XMLFilter::resolveKeywords($typePlug->getManifestRawContent("server_settings/global_param")); + $instancesDefs = $typePlug->getConfigsDefinitions(); + if (!empty($instancesDefs) && is_array($instancesDefs)) { + foreach ($instancesDefs as $defKey => $defData) { + $instancesDefinitions[$fieldName . "/" . $defKey] = $defData; + } + } + } + } + $allParams = XMLFilter::resolveKeywords($fullManifest->ownerDocument->saveXML($fullManifest)); + $allParams = str_replace('type="plugin_instance:', 'type="group_switch:', $allParams); + $allParams = str_replace("", $addParams . "", $allParams); + + $buffer .= $allParams ; + $definitions = $instancesDefinitions; + $configsDefs = $ajxpPlugin->getConfigsDefinitions(); + if (is_array($configsDefs)) { + $definitions = array_merge($configsDefs, $instancesDefinitions); + } + $values = $ajxpPlugin->getConfigs(); + if (!is_array($values)) $values = array(); + $buffer .= ""; + // First flatten keys + $flattenedKeys = array(); + foreach ($values as $key => $value) { + $type = $definitions[$key]["type"]; + if ((strpos($type, "group_switch:") === 0 || strpos($type, "plugin_instance:") === 0) && is_array($value)) { + $res = array(); + $this->flattenKeyValues($res, $definitions, $value, $key); + $flattenedKeys += $res; + // Replace parent key by new flat value + $values[$key] = $flattenedKeys[$key]; + } + } + $values += $flattenedKeys; + + foreach ($values as $key => $value) { + $attribute = true; + $type = $definitions[$key]["type"]; + if ($type == "array" && is_array($value)) { + $value = implode(",", $value); + } else if ($type == "boolean") { + $value = ($value === true || $value === "true" || $value == 1 ? "true" : "false"); + } else if ($type == "textarea") { + $attribute = false; + } else if ($type == "password" && !empty($value)) { + $value = "__AJXP_VALUE_SET__"; + } + if ($attribute) { + $buffer .= ""; + } else { + $buffer .= ""; + } + } + if ($ajxpPlugin->getType() != "core") { + $buffer .= "isEnabled() ? "true" : "false") . "\"/>"; + } + $buffer .= ""; + $buffer .= "" . $ajxpPlugin->getPluginInformationHTML("Charles du Jeu", "https://pydio.com/en/docs/references/plugins/") . "

    "; + if (file_exists($ajxpPlugin->getBaseDir() . "/plugin_doc.html")) { + $buffer .= file_get_contents($ajxpPlugin->getBaseDir() . "/plugin_doc.html"); + } + $buffer .= "]]>
    "; + $buffer .= "
    "; + + $responseInterface = $responseInterface->withHeader("Content-type", "text/xml"); + $responseInterface->getBody()->write($buffer); + + break; + + case "run_plugin_action": + + $options = array(); + $responseInterface = $responseInterface->withHeader("Content-type", "text/plain"); + $this->parseParameters($ctx, $httpVars, $options, true); + $pluginId = $httpVars["action_plugin_id"]; + if (isSet($httpVars["button_key"])) { + $options = $options[$httpVars["button_key"]]; + } + $plugin = PluginsService::getInstance($ctx)->softLoad($pluginId, $options); + if (method_exists($plugin, $httpVars["action_plugin_method"])) { + try { + $res = call_user_func(array($plugin, $httpVars["action_plugin_method"]), $options, $ctx); + $response = $res; + } catch (\Exception $e) { + $response = "ERROR:" . $e->getMessage(); + } + } else { + $response = 'ERROR: Plugin ' . $httpVars["action_plugin_id"] . ' does not implement ' . $httpVars["action_plugin_method"] . ' method!'; + } + $responseInterface->getBody()->write($response); + + break; + + case "edit_plugin_options": + + $options = array(); + $this->parseParameters($ctx, $httpVars, $options, true); + $confStorage = ConfService::getConfStorageImpl(); + $pluginId = InputFilter::sanitize($httpVars["plugin_id"], InputFilter::SANITIZE_ALPHANUM); + if($pluginId === "core") { + $pluginId = "core.ajaxplorer"; + } + list($pType, $pName) = explode(".", $pluginId); + $existing = $confStorage->loadPluginConfig($pType, $pName); + $this->mergeExistingParameters($options, $existing); + $confStorage->savePluginConfig($pluginId, $options); + ConfService::clearAllCaches(); + $responseInterface = $responseInterface->withBody(new SerializableResponseStream([new UserMessage($mess["ajxp_conf.97"])])); + + break; + + case "list_all_plugins_actions": + + if($this->currentUserIsGroupAdmin()){ + // Group admin : do not allow actions edition + return new JsonResponse(["LIST" => array(), "HAS_GROUPS" => true]); + } + + if(CacheService::contains(AJXP_CACHE_SERVICE_NS_SHARED, "ALL_ACTIONS_CACHE")){ + + $actions = CacheService::fetch(AJXP_CACHE_SERVICE_NS_SHARED, "ALL_ACTIONS_CACHE"); + + }else{ + + $nodes = PluginsService::getInstance($ctx)->searchAllManifests("//action", "node", false, true, true); + $actions = array(); + foreach ($nodes as $node) { + $xPath = new \DOMXPath($node->ownerDocument); + $proc = $xPath->query("processing", $node); + if(!$proc->length) continue; + $txt = $xPath->query("gui/@text", $node); + if ($txt->length) { + $messId = $txt->item(0)->nodeValue; + } else { + $messId = ""; + } + $parentPlugin = $node->parentNode->parentNode->parentNode; + $pId = $parentPlugin->attributes->getNamedItem("id")->nodeValue; + if (empty($pId)) { + $pId = $parentPlugin->nodeName ."."; + if($pId == "ajxpdriver.") $pId = "access."; + $pId .= $parentPlugin->attributes->getNamedItem("name")->nodeValue; + } + if(!is_array($actions[$pId])) $actions[$pId] = array(); + $actionName = $node->attributes->getNamedItem("name")->nodeValue; + $actionData = [ + "action" => $actionName , + "label" => $messId, + "server_processing" => $xPath->query("processing/serverCallback", $node)->length ? true : false + ]; + // Parse parameters if they are defined + $params = $xPath->query("processing/serverCallback/input_param", $node); + if($params->length){ + $actionParameters = []; + /** @var \DOMElement $pNode */ + foreach($params as $pNode){ + $actionParameters[] = [ + "type" => $pNode->getAttribute("type"), + "name" => $pNode->getAttribute("name"), + "description" => $pNode->getAttribute("description") + ]; + } + $actionData["parameters"] = $actionParameters; + } + $actions[$pId][$actionName] = $actionData; + } + ksort($actions, SORT_STRING); + foreach ($actions as $actPid => $actionGroup) { + ksort($actionGroup, SORT_STRING); + $actions[$actPid] = array(); + foreach ($actionGroup as $v) { + $actions[$actPid][] = $v; + } + } + CacheService::save(AJXP_CACHE_SERVICE_NS_SHARED, "ALL_ACTIONS_CACHE", $actions); + } + $responseInterface = new JsonResponse(["LIST" => $actions, "HAS_GROUPS" => true]); + break; + + case "list_all_plugins_parameters": + + if(CacheService::contains(AJXP_CACHE_SERVICE_NS_SHARED, "ALL_PARAMS_CACHE")){ + $parameters = CacheService::fetch(AJXP_CACHE_SERVICE_NS_SHARED, "ALL_PARAMS_CACHE"); + }else{ + $currentUserIsGroupAdmin = ($ctx->hasUser() && $ctx->getUser()->getGroupPath() != "/"); + $parameters = $this->getEditableParameters($ctx, $currentUserIsGroupAdmin, true); + CacheService::save(AJXP_CACHE_SERVICE_NS_SHARED, "ALL_PARAMS_CACHE", $parameters); + } + $responseInterface = new JsonResponse(["LIST" => $parameters, "HAS_GROUPS" => true]); + break; + + case "parameters_to_form_definitions" : + + $data = json_decode(InputFilter::magicDequote($httpVars["json_parameters"]), true); + $buffer = ""; + foreach ($data as $repoScope => $pluginsData) { + $buffer .= ""; + foreach ($pluginsData as $pluginId => $paramData) { + foreach ($paramData as $paramId => $paramValue) { + $query = "//param[@name='$paramId']|//global_param[@name='$paramId']"; + $nodes = PluginsService::getInstance($ctx)->searchAllManifests($query, "node", false, true, true); + if(!count($nodes)) continue; + $n = $nodes[0]; + if ($n->attributes->getNamedItem("group") != null) { + $n->attributes->getNamedItem("group")->nodeValue = "$pluginId"; + } else { + $n->appendChild($n->ownerDocument->createAttribute("group")); + $n->attributes->getNamedItem("group")->nodeValue = "$pluginId"; + } + if(is_bool($paramValue)) $paramValue = ($paramValue ? "true" : "false"); + if ($n->attributes->getNamedItem("default") != null) { + $n->attributes->getNamedItem("default")->nodeValue = $paramValue; + } else { + $n->appendChild($n->ownerDocument->createAttribute("default")); + $n->attributes->getNamedItem("default")->nodeValue = $paramValue; + } + $buffer .= XMLFilter::resolveKeywords($n->ownerDocument->saveXML($n)); + } + } + $buffer .= ""; + } + $buffer .= ""; + $responseInterface = $responseInterface->withBody(new SerializableResponseStream(new XMLDocMessage($buffer))); + break; + + default: + break; + } + + return $responseInterface; + } + + /** + * @param ServerRequestInterface $requestInterface Full set of query parameters + * @param string $rootPath Path to prepend to the resulting nodes + * @param string $relativePath Specific path part for this function + * @param string $paginationHash Number added to url#2 for pagination purpose. + * @param string $findNodePosition Path to a given node to try to find it + * @param string $aliasedDir Aliased path used for alternative url + * @return NodesList A populated NodesList object, eventually recursive. + * @throws \Pydio\Core\Exception\PydioException + */ + public function listNodes(ServerRequestInterface $requestInterface, $rootPath, $relativePath, $paginationHash = null, $findNodePosition = null, $aliasedDir = null) + { + + $relativePath = "/$relativePath"; + if($aliasedDir != null && $aliasedDir != "/".$rootPath.$relativePath){ + $baseDir = $aliasedDir; + }else{ + $baseDir = "/".$rootPath.$relativePath; + } + $nodesList = new NodesList($baseDir); + $pServ = PluginsService::getInstance($this->context); + $types = $pServ->getDetectedPlugins(); + $mess = LocaleService::getMessages(); + $uniqTypes = array("core"); + $coreTypes = array("auth", "conf", "boot", "feed", "log", "mailer", "mq"); + + if ($relativePath == "/plugins" || $relativePath == "/core_plugins" || $relativePath=="/all") { + + if($relativePath == "/core_plugins") $uniqTypes = $coreTypes; + else if($relativePath == "/plugins") $uniqTypes = array_diff(array_keys($types), $coreTypes); + else if($relativePath == "/all") $uniqTypes = array_keys($types); + $nodesList->initColumnsData("filelist", "detail", "ajxp_conf.plugins_folder"); + $nodesList->appendColumn("ajxp_conf.101", "ajxp_label"); + $nodesList->appendColumn("ajxp_conf.103", "plugin_description"); + $nodesList->appendColumn("ajxp_conf.102", "plugin_id"); + ksort($types); + + foreach ($types as $t => $tPlugs) { + if(!empty($findNodePosition) && $t !== $findNodePosition) continue; + if(!in_array($t, $uniqTypes))continue; + if($t == "core") continue; + $nodeKey = $baseDir."/".$t; + $meta = array( + "icon" => "folder_development.png", + "plugin_id" => $t, + "text" => $mess["plugtype.title.".$t], + "plugin_description" => $mess["plugtype.desc.".$t], + "is_file" => false + ); + $this->appendBookmarkMeta($nodeKey, $meta); + $nodesList->addBranch(new AJXP_Node($nodeKey, $meta)); + } + + } else if ($relativePath == "/core") { + + $nodesList->initColumnsData("filelist", "detail", "ajxp_conf.plugins"); + $nodesList->appendColumn("ajxp_conf.101", "ajxp_label"); + $nodesList->appendColumn("ajxp_conf.102", "plugin_id"); + $nodesList->appendColumn("ajxp_conf.103", "plugin_description"); + + $all = []; $first = null; + foreach ($uniqTypes as $type) { + if(!isset($types[$type])) continue; + /** @var Plugin $pObject */ + foreach ($types[$type] as $pObject) { + if(!empty($findNodePosition) && $pObject->getId() !== $findNodePosition) continue; + + $isMain = ($pObject->getId() == "core.ajaxplorer"); + $meta = array( + "icon" => ($isMain?"preferences_desktop.png":"desktop.png"), + "ajxp_mime" => "ajxp_plugin", + "plugin_id" => $pObject->getId(), + "plugin_description" => $pObject->getManifestDescription(), + "text" => $pObject->getManifestLabel() + ); + // Check if there are actually any parameters to display! + if($pObject->getManifestRawContent("server_settings", "xml")->length == 0) { + continue; + } + $nodeKey = $baseDir."/".$pObject->getId(); + $this->appendBookmarkMeta($nodeKey, $meta); + $plugNode = new AJXP_Node($nodeKey, $meta); + if ($isMain) { + $first = $plugNode; + } else { + $all[] = $plugNode; + } + } + } + + if($first !== null) $nodesList->addBranch($first); + foreach($all as $node) $nodesList->addBranch($node); + + } else { + $split = explode("/", $relativePath); + if(empty($split[0])) array_shift($split); + $type = $split[1]; + + $nodesList->initColumnsData("filelist", "full", "ajxp_conf.plugin_detail"); + $nodesList->appendColumn("ajxp_conf.101", "ajxp_label", "String", "10%"); + $nodesList->appendColumn("ajxp_conf.102", "plugin_id", "String", "10%"); + $nodesList->appendColumn("ajxp_conf.103", "plugin_description", "String", "60%"); + $nodesList->appendColumn("ajxp_conf.104", "enabled", "String", "10%"); + $nodesList->appendColumn("ajxp_conf.105", "can_active", "String", "10%"); + + $mess = LocaleService::getMessages(); + /** @var Plugin $pObject */ + foreach ($types[$type] as $pObject) { + if(!empty($findNodePosition) && $pObject->getId() !== $findNodePosition) continue; + $errors = "OK"; + try { + $pObject->performChecks(); + } catch (\Exception $e) { + $errors = "ERROR : ".$e->getMessage(); + } + $meta = array( + "icon" => "preferences_plugin.png", + "text" => $pObject->getManifestLabel(), + "ajxp_mime" => "ajxp_plugin", + "can_active" => $errors, + "enabled" => ($pObject->isEnabled()?$mess[440]:$mess[441]), + "plugin_id" => $pObject->getId(), + "plugin_description" => $pObject->getManifestDescription() + ); + $nodeKey = $baseDir."/".$pObject->getId(); + $this->appendBookmarkMeta($nodeKey, $meta); + $nodesList->addBranch(new AJXP_Node($nodeKey, $meta)); + } + } + return $nodesList; + + } + + /** + * @param $result + * @param $definitions + * @param $values + * @param string $parent + */ + protected function flattenKeyValues(&$result, &$definitions, $values, $parent = "") + { + foreach ($values as $key => $value) { + if (is_array($value)) { + $this->flattenKeyValues($result, $definitions, $value, $parent."/".$key); + } else { + if ($key == "instance_name") { + $result[$parent] = $value; + } + if ($key == "group_switch_value") { + $result[$parent] = $value; + } else { + $result[$parent.'/'.$key] = $value; + if(isSet($definitions[$key])){ + $definitions[$parent.'/'.$key] = $definitions[$key]; + }else if(isSet($definitions[dirname($parent)."/".$key])){ + $definitions[$parent.'/'.$key] = $definitions[dirname($parent)."/".$key]; + } + } + } + } + } + +} \ No newline at end of file diff --git a/core/src/plugins/access.ajxp_conf/src/RepositoriesManager.php b/core/src/plugins/access.ajxp_conf/src/RepositoriesManager.php new file mode 100644 index 0000000000..dce6aa7990 --- /dev/null +++ b/core/src/plugins/access.ajxp_conf/src/RepositoriesManager.php @@ -0,0 +1,1187 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Driver\DataProvider\Provisioning; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Access\Core\Filter\AJXP_PermissionMask; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Model\NodesList; +use Pydio\Access\Core\Model\Repository; +use Pydio\Core\Utils\Vars\XMLFilter; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Http\Message\ReloadMessage; +use Pydio\Core\Http\Message\UserMessage; +use Pydio\Core\Http\Message\XMLDocMessage; +use Pydio\Core\Http\Response\SerializableResponseStream; +use Pydio\Core\Model\Context; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Model\RepositoryInterface; +use Pydio\Core\PluginFramework\Plugin; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Services\AuthService; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Services\RepositoryService; +use Pydio\Core\Services\RolesService; +use Pydio\Core\Services\UsersService; +use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Core\Utils\Vars\StringHelper; +use Pydio\Core\Utils\XMLHelper; +use Pydio\Tests\AbstractTest; +use Zend\Diactoros\Response\JsonResponse; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class RepositoriesManager + * @package Pydio\Access\Driver\DataProvider\Provisioning + */ +class RepositoriesManager extends AbstractManager +{ + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @return ResponseInterface + * @throws PydioException + * @throws \Exception + */ + public function repositoriesActions(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface){ + + $action = $requestInterface->getAttribute("action"); + /** @var ContextInterface $ctx */ + $ctx = $requestInterface->getAttribute("ctx"); + $httpVars = $requestInterface->getParsedBody(); + $mess = LocaleService::getMessages(); + $currentAdminBasePath = "/"; + $loggedUser = $ctx->getUser(); + if ($loggedUser!=null && $loggedUser->getGroupPath()!=null) { + $currentAdminBasePath = $loggedUser->getGroupPath(); + } + + switch ($action){ + + // REPOSITORIES + case "get_drivers_definition": + + $buffer = ""; + $buffer .= XMLFilter::resolveKeywords(self::availableDriversToXML("param", "", true)); + $buffer .= ""; + $responseInterface = $responseInterface->withBody(new SerializableResponseStream(new XMLDocMessage($buffer))); + + break; + + case "get_templates_definition": + + $buffer = ""; + $count = 0; + $repositories = RepositoryService::listRepositoriesWithCriteria(array( + "isTemplate" => '1' + ), $count); + foreach ($repositories as $repo) { + if(!$repo->isTemplate()) continue; + $repoId = $repo->getUniqueId(); + $repoLabel = $repo->getDisplay(); + $repoType = $repo->getAccessType(); + $buffer .= ""; + } + $buffer .= ""; + $responseInterface = $responseInterface->withBody(new SerializableResponseStream(new XMLDocMessage($buffer))); + + break; + + case "create_repository" : + + $repDef = $httpVars; + $isTemplate = isSet($httpVars["sf_checkboxes_active"]); + unset($repDef["get_action"]); + unset($repDef["sf_checkboxes_active"]); + if (isSet($httpVars["json_data"])) { + $repDef = json_decode(InputFilter::magicDequote($httpVars["json_data"]), true); + $options = $repDef["DRIVER_OPTIONS"]; + } else { + $options = array(); + $this->parseParameters($ctx, $repDef, $options, true); + } + if (count($options)) { + $repDef["DRIVER_OPTIONS"] = $options; + unset($repDef["DRIVER_OPTIONS"]["AJXP_GROUP_PATH_PARAMETER"]); + if(isSet($options["AJXP_SLUG"])){ + $repDef["AJXP_SLUG"] = $options["AJXP_SLUG"]; + unset($repDef["DRIVER_OPTIONS"]["AJXP_SLUG"]); + } + } + if (strstr($repDef["DRIVER"], "ajxp_template_") !== false) { + $templateId = substr($repDef["DRIVER"], 14); + $templateRepo = RepositoryService::getRepositoryById($templateId); + $newRep = $templateRepo->createTemplateChild($repDef["DISPLAY"], $repDef["DRIVER_OPTIONS"], $ctx->getUser()->getId()); + if(isSet($repDef["AJXP_SLUG"])){ + $newRep->setSlug($repDef["AJXP_SLUG"]); + } + } else { + if ($this->currentUserIsGroupAdmin()) { + throw new \Exception("You are not allowed to create a workspace from a driver. Use a template instead."); + } + $pServ = PluginsService::getInstance($ctx); + $driver = $pServ->getPluginByTypeName("access", $repDef["DRIVER"]); + + $newRep = RepositoryService::createRepositoryFromArray(0, $repDef); + $testFile = $driver->getBaseDir()."/test.".$newRep->getAccessType()."Access.php"; + if (!$isTemplate && is_file($testFile)) { + //chdir(AJXP_TESTS_FOLDER."/plugins"); + $className = "\\Pydio\\Tests\\".$newRep->getAccessType()."AccessTest"; + if (!class_exists($className)) + include($testFile); + $class = new $className(); + $result = $class->doRepositoryTest($newRep); + if (!$result) { + throw new PydioException($class->failedInfo); + } + } + // Apply default metasource if any + if ($driver != null && $driver->getConfigs()!=null ) { + $confs = $driver->getConfigs(); + if (!empty($confs["DEFAULT_METASOURCES"])) { + $metaIds = InputFilter::parseCSL($confs["DEFAULT_METASOURCES"]); + $metaSourceOptions = array(); + foreach ($metaIds as $metaID) { + $metaPlug = $pServ->getPluginById($metaID); + if($metaPlug == null) continue; + $pNodes = $metaPlug->getManifestRawContent("//param[@default]", "nodes"); + $defaultParams = array(); + /** @var \DOMElement $domNode */ + foreach ($pNodes as $domNode) { + $defaultParams[$domNode->getAttribute("name")] = $domNode->getAttribute("default"); + } + $metaSourceOptions[$metaID] = $defaultParams; + } + $newRep->addOption("META_SOURCES", $metaSourceOptions); + } + } + } + + if ($this->repositoryExists($newRep->getDisplay())) { + throw new PydioException($mess["ajxp_conf.50"]); + } + if ($isTemplate) { + $newRep->isTemplate = true; + } + if ($this->currentUserIsGroupAdmin()) { + $newRep->setGroupPath($ctx->getUser()->getGroupPath()); + } else if (!empty($options["AJXP_GROUP_PATH_PARAMETER"])) { + $value = InputFilter::securePath(rtrim($currentAdminBasePath, "/") . "/" . ltrim($options["AJXP_GROUP_PATH_PARAMETER"], "/")); + $newRep->setGroupPath($value); + } + + $res = RepositoryService::addRepository($newRep); + + if ($res == -1) { + throw new PydioException($mess["ajxp_conf.51"]); + + } + + $defaultRights = $newRep->getDefaultRight(); + if(!empty($defaultRights)){ + $groupRole = RolesService::getOrCreateRole("AJXP_GRP_" . $currentAdminBasePath, $currentAdminBasePath); + $groupRole->setAcl($newRep->getId(), $defaultRights); + } + $loggedUser = $ctx->getUser(); + $loggedUser->getPersonalRole()->setAcl($newRep->getUniqueId(), "rw"); + $loggedUser->recomputeMergedRole(); + $loggedUser->save("superuser"); + AuthService::updateSessionUser($loggedUser); + + $message = new UserMessage($mess["ajxp_conf.52"]); + $reload = new ReloadMessage("", $newRep->getUniqueId()); + $responseInterface = $responseInterface->withBody(new SerializableResponseStream([$message, $reload])); + + break; + + case "post_repository": + $jsonDataCreateWorkspace = json_decode($httpVars["payload"], true); + if ($jsonDataCreateWorkspace === null) { + throw new PydioException("Invalid JSON !!"); + } + if(!isSet($jsonDataCreateWorkspace["isTemplate"])) { + $jsonDataCreateWorkspace["isTemplate"] = false; + } + if(!isSet($jsonDataCreateWorkspace["id"])) { + $jsonDataCreateWorkspace["id"] = 0; + } + if(!isSet($jsonDataCreateWorkspace["parameters"]["CREATE"])) { + $jsonDataCreateWorkspace["parameters"]["CREATE"] = true; + } + $repo = new Repository($jsonDataCreateWorkspace["id"], $jsonDataCreateWorkspace["display"], $jsonDataCreateWorkspace["accessType"]); + foreach($jsonDataCreateWorkspace["parameters"] as $name => $value) { + $repo->addOption($name, $value); + } + $pluginService = PluginsService::getInstance($ctx); + $driver = $pluginService->getPluginByTypeName("access", $jsonDataCreateWorkspace["accessType"]); + $testFile = $driver->getBaseDir()."/test.".$repo->getAccessType()."Access.php"; + if (!$jsonDataCreateWorkspace["isTemplate"] && is_file($testFile)) { + $className = "\\Pydio\\Tests\\".$repo->getAccessType()."AccessTest"; + if (!class_exists($className)) + include($testFile); + /** @var AbstractTest $class */ + $class = new $className(); + $result = $class->doRepositoryTest($repo); + if (!$result) { + throw new PydioException($class->failedInfo); + } + } + if ($driver != null && $driver->getConfigs() != null) { + $arrayDefaultMetasources = array(); + $arrayPluginToOverWrite = array(); + $metaSourceOptions = array(); + $configsDriver = $driver->getConfigs(); + if (!empty($configsDriver["DEFAULT_METASOURCES"])) { + $arrayDefaultMetasources = InputFilter::parseCSL($configsDriver["DEFAULT_METASOURCES"]); + foreach ($arrayDefaultMetasources as $metaID) { + $metaPlug = $pluginService->getPluginById($metaID); + if($metaPlug == null) continue; + $pNodes = $metaPlug->getManifestRawContent("//param[@default]", "nodes"); + $defaultParams = array(); + /** @var \DOMElement $domNode */ + foreach ($pNodes as $domNode) { + $defaultParams[$domNode->getAttribute("name")] = $domNode->getAttribute("default"); + } + $metaSourceOptions[$metaID] = $defaultParams; + } + } + if(isSet($jsonDataCreateWorkspace["features"])) { + foreach($arrayDefaultMetasources as $defaultPluginName) { + foreach($jsonDataCreateWorkspace["features"] as $pluginName => $arrayPluginValue) { + if ($defaultPluginName === $pluginName) { + $arrayPluginToOverWrite[$pluginName] = $arrayPluginValue; + unset($jsonDataCreateWorkspace["features"][$pluginName]); + } + } + } + $arrayPluginToAdd = $jsonDataCreateWorkspace["features"]; + foreach($arrayPluginToOverWrite as $pluginName => $arrayPlugin) { + if(!empty($arrayPlugin)) { + foreach($arrayPlugin as $name => $value) { + $metaSourceOptions[$pluginName][$name] = $value; + } + } + } + $metaSourceOptions = array_merge($metaSourceOptions, $arrayPluginToAdd); + } + $repo->addOption("META_SOURCES", $metaSourceOptions); + } + if ($this->repositoryExists($repo->getDisplay())) { + throw new PydioException($mess["ajxp_conf.50"]); + } + if ($jsonDataCreateWorkspace["isTemplate"]) { + $repo->isTemplate = true; + } + if ($this->currentUserIsGroupAdmin()) { + $repo->setGroupPath($ctx->getUser()->getGroupPath()); + } else if (!empty($options["AJXP_GROUP_PATH_PARAMETER"])) { + $value = InputFilter::securePath(rtrim($currentAdminBasePath, "/") . "/" . ltrim($options["AJXP_GROUP_PATH_PARAMETER"], "/")); + $repo->setGroupPath($value); + } + $res = RepositoryService::addRepository($repo); + if ($res == -1) { + throw new PydioException($mess["ajxp_conf.51"]); + } + $defaultRights = $repo->getDefaultRight(); + if(!empty($defaultRights)){ + $groupRole = RolesService::getOrCreateRole("AJXP_GRP_" . $currentAdminBasePath, $currentAdminBasePath); + $groupRole->setAcl($repo->getId(), $defaultRights); + } + $loggedUser = $ctx->getUser(); + $loggedUser->getPersonalRole()->setAcl($repo->getUniqueId(), "rw"); + $loggedUser->recomputeMergedRole(); + $loggedUser->save("superuser"); + AuthService::updateSessionUser($loggedUser); + #@TODO: create different message because this action is doing via API + $message = new UserMessage($mess["ajxp_conf.52"]); + $reload = new ReloadMessage("", $repo->getUniqueId()); + $responseInterface = $responseInterface->withBody(new SerializableResponseStream([$message, $reload])); + break; + + case "patch_repository": + $jsonDataEditWorkspace = json_decode($httpVars["payload"], true); + if ($jsonDataEditWorkspace === null) { + throw new PydioException("Invalid JSON !!"); + } + $workspaceId = $httpVars["workspaceId"]; + $repo = RepositoryService::findRepositoryByIdOrAlias($workspaceId); + if ($repo === null) { + throw new PydioException("Workspace not found !!"); + } + foreach ($jsonDataEditWorkspace as $name => $value) { + if($name !== "parameters" && $name !== "features") { + $repo->$name = $value; + } + } + foreach($jsonDataEditWorkspace["parameters"] as $name => $value) { + $repo->addOption($name, $value); + } + $pluginService = PluginsService::getInstance($ctx); + $driver = $pluginService->getPluginByTypeName("access", $repo->getAccessType()); + if ($driver != null && $driver->getConfigs() != null) { + $arrayPluginToOverWrite = array(); + $arrayWorkspaceMetasources = $repo->getSafeOption("META_SOURCES"); + if(isSet($jsonDataEditWorkspace["features"])) { + foreach($arrayWorkspaceMetasources as $metaSourcePluginName => $metaSourcePluginArray) { + foreach($jsonDataEditWorkspace["features"] as $pluginName => $arrayPluginValue) { + if ($metaSourcePluginName === $pluginName) { + $arrayPluginToOverWrite[$pluginName] = $arrayPluginValue; + unset($jsonDataEditWorkspace["features"][$pluginName]); + } + } + } + $arrayPluginToAdd = $jsonDataEditWorkspace["features"]; + foreach($arrayPluginToOverWrite as $pluginName => $arrayPlugin) { + if(!empty($arrayPlugin)) { + foreach($arrayPlugin as $name => $value) { + $arrayWorkspaceMetasources[$pluginName][$name] = $value; + } + } + } + $arrayWorkspaceMetasources = array_merge($arrayWorkspaceMetasources, $arrayPluginToAdd); + } + $repo->addOption("META_SOURCES", $arrayWorkspaceMetasources); + } + RepositoryService::replaceRepository($workspaceId, $repo); + $message = new UserMessage("Workspace successfully edited !!"); + $reload = new ReloadMessage("", $repo->getUniqueId()); + $responseInterface = $responseInterface->withBody(new SerializableResponseStream([$message, $reload])); + break; + + case "edit_repository_label" : + case "edit_repository_data" : + + $repId = $httpVars["repository_id"]; + $repo = RepositoryService::getRepositoryById($repId); + $initialDefaultRights = $repo->getDefaultRight(); + + if(!$repo->isWriteable()){ + + if (isSet($httpVars["permission_mask"]) && !empty($httpVars["permission_mask"])){ + + $mask = json_decode($httpVars["permission_mask"], true); + $rootGroup = RolesService::getRole("AJXP_GRP_/"); + if(count($mask)){ + $perm = new AJXP_PermissionMask($mask); + $rootGroup->setMask($repId, $perm); + }else{ + $rootGroup->clearMask($repId); + } + RolesService::updateRole($rootGroup); + + $responseInterface = $responseInterface->withBody(new SerializableResponseStream(new UserMessage("The permission mask was updated for this workspace"))); + break; + + }else{ + + throw new PydioException("This workspace is not writeable. Please edit directly the conf/bootstrap_repositories.php file."); + + } + } + + $res = 0; + if (isSet($httpVars["newLabel"])) { + $newLabel = InputFilter::sanitize(InputFilter::securePath($httpVars["newLabel"]), InputFilter::SANITIZE_HTML); + if ($this->repositoryExists($newLabel)) { + throw new PydioException($mess["ajxp_conf.50"]); + } + $repo->setDisplay($newLabel); + $res = RepositoryService::replaceRepository($repId, $repo); + } else { + $options = array(); + $existing = $repo->getOptionsDefined(); + $existingValues = array(); + if(!$repo->isTemplate()){ + foreach($existing as $exK) { + $existingValues[$exK] = $repo->getSafeOption($exK); + } + } + $this->parseParameters($ctx, $httpVars, $options, true, $existingValues); + if (count($options)) { + foreach ($options as $key=>$value) { + if ($key == "AJXP_SLUG") { + $repo->setSlug($value); + continue; + } else if ($key == "WORKSPACE_LABEL" || $key == "TEMPLATE_LABEL"){ + $newLabel = InputFilter::sanitize($value, InputFilter::SANITIZE_HTML); + if($repo->getDisplay() != $newLabel){ + if ($this->repositoryExists($newLabel)) { + throw new \Exception($mess["ajxp_conf.50"]); + }else{ + $repo->setDisplay($newLabel); + } + } + } elseif ($key == "AJXP_GROUP_PATH_PARAMETER") { + $value = InputFilter::securePath(rtrim($currentAdminBasePath, "/") . "/" . ltrim($value, "/")); + $repo->setGroupPath($value); + continue; + } + $repo->addOption($key, $value); + } + } + if($repo->isTemplate()){ + foreach($existing as $definedOption){ + if($definedOption == "META_SOURCES" || $definedOption == "CREATION_TIME" || $definedOption == "CREATION_USER"){ + continue; + } + if(!isSet($options[$definedOption]) && isSet($repo->options[$definedOption])){ + unset($repo->options[$definedOption]); + } + } + } + /* + * THIS SEEM TO BE DUPLICATED LOWER IN THE CODE! + if ($repo->getContextOption($ctx, "DEFAULT_RIGHTS")) { + $gp = $repo->getGroupPath(); + if (empty($gp) || $gp == "/") { + $defRole = RolesService::getRole("AJXP_GRP_/"); + } else { + $defRole = RolesService::getOrCreateRole("AJXP_GRP_" . $gp, $currentAdminBasePath); + } + if ($defRole !== false) { + $defRole->setAcl($repId, $repo->getContextOption($ctx, "DEFAULT_RIGHTS")); + RolesService::updateRole($defRole); + } + } + */ + if (is_file(AJXP_TESTS_FOLDER."/plugins/test.ajxp_".$repo->getAccessType().".php")) { + chdir(AJXP_TESTS_FOLDER."/plugins"); + include(AJXP_TESTS_FOLDER."/plugins/test.ajxp_".$repo->getAccessType().".php"); + $className = "ajxp_".$repo->getAccessType(); + /** @var AbstractTest $class */ + $class = new $className(); + $result = $class->doRepositoryTest($repo); + if (!$result) { + throw new PydioException($class->failedInfo); + } + } + + $rootGroup = RolesService::getOrCreateRole("AJXP_GRP_" . $currentAdminBasePath, $currentAdminBasePath); + if (isSet($httpVars["permission_mask"]) && !empty($httpVars["permission_mask"])){ + $mask = json_decode($httpVars["permission_mask"], true); + if(count($mask)){ + $perm = new AJXP_PermissionMask($mask); + $rootGroup->setMask($repId, $perm); + }else{ + $rootGroup->clearMask($repId); + } + RolesService::updateRole($rootGroup); + } + $defaultRights = $repo->getDefaultRight(); + if($defaultRights != $initialDefaultRights){ + $currentDefaultRights = $rootGroup->getAcl($repId); + if(!empty($defaultRights) || !empty($currentDefaultRights)){ + $rootGroup->setAcl($repId, empty($defaultRights) ? "" : $defaultRights); + RolesService::updateRole($rootGroup); + } + } + RepositoryService::replaceRepository($repId, $repo); + } + if ($res == -1) { + throw new PydioException($mess["ajxp_conf.53"]); + } + + $chunks = []; + $chunks[] = new UserMessage($mess["ajxp_conf.54"]); + if (isSet($httpVars["newLabel"])) { + $chunks[] = new ReloadMessage("", $repId); + } + $responseInterface = $responseInterface->withBody(new SerializableResponseStream($chunks)); + + break; + + case "edit_repository" : + + if(isSet($httpVars["workspaceId"])){ + $repId = $httpVars["workspaceId"]; + }else{ + $repId = $httpVars["repository_id"]; + } + $format = isSet($httpVars["format"]) && $httpVars["format"] == "json" ? "json" : "xml"; + + $repository = RepositoryService::findRepositoryByIdOrAlias($repId); + if ($repository == null) { + throw new \Exception("Cannot find workspace with id $repId"); + } + if ($ctx->hasUser() && !$ctx->getUser()->canAdministrate($repository)) { + throw new \Exception("You are not allowed to edit this workspace!"); + } + $pServ = PluginsService::getInstance($ctx); + /** @var AbstractAccessDriver $plug */ + $plug = $pServ->getPluginById("access.".$repository->getAccessType()); + if ($plug == null) { + throw new \Exception("Cannot find access driver (".$repository->getAccessType().") for workspace!"); + } + $slug = $repository->getSlug(); + if ($slug == "" && $repository->isWriteable()) { + $repository->setSlug(); + RepositoryService::replaceRepository($repId, $repository); + } + $ctxUser = $ctx->getUser(); + if ($ctxUser!=null && $ctxUser->getGroupPath() != null) { + $rgp = $repository->getGroupPath(); + if($rgp == null) $rgp = "/"; + if (strlen($rgp) < strlen($ctxUser->getGroupPath())) { + $repository->setWriteable(false); + } + } + + $definitions = $plug->getConfigsDefinitions(); + if($format === "json"){ + $data = $this->serializeRepositoryToJSON($ctx, $repository, $definitions, $currentAdminBasePath); + if(isSet($httpVars["load_fill_values"]) && $httpVars["load_fill_values"] === "true"){ + $data["PARAMETERS_INFO"] = $this->serializeRepositoryDriverInfos($pServ, $format, $plug, $repository); + } + $responseInterface = new JsonResponse($data); + }else{ + $buffer = ""; + $buffer .= $this->serializeRepositoryToXML($ctx, $repository, $definitions, $currentAdminBasePath); + $buffer .= $this->serializeRepositoryDriverInfos($pServ, $format, $plug, $repository); + $buffer .= ""; + $responseInterface = $responseInterface->withBody(new SerializableResponseStream(new XMLDocMessage($buffer))); + } + + break; + + case "meta_source_add" : + + $repId = InputFilter::sanitize(isSet($httpVars["workspaceId"]) ? $httpVars["workspaceId"] : $httpVars["repository_id"]); + $metaId = InputFilter::sanitize(isSet($httpVars["metaId"]) ? $httpVars["metaId"] : $httpVars["new_meta_source"]); + $repo = RepositoryService::findRepositoryByIdOrAlias($repId); + + if (!is_object($repo)) { + throw new PydioException("Invalid workspace id! $repId"); + } + list($type, $name) = explode(".", $metaId); + if(PluginsService::findPluginWithoutCtxt($type, $name) === false){ + throw new PydioException("Cannot find plugin with id $metaId"); + } + if(isSet($httpVars["request_body"])){ + $options = $httpVars["request_body"]; + }else if (isSet($httpVars["json_data"])) { + $options = json_decode(InputFilter::magicDequote($httpVars["json_data"]), true); + } else { + $options = array(); + $this->parseParameters($ctx, $httpVars, $options, true); + } + + $repoOptions = $repo->getContextOption($ctx, "META_SOURCES"); + if (is_array($repoOptions) && isSet($repoOptions[$metaId])) { + throw new PydioException($mess["ajxp_conf.55"]); + } + if (!is_array($repoOptions)) { + $repoOptions = array(); + } + $repoOptions[$metaId] = $options; + uksort($repoOptions, array($this,"metaSourceOrderingFunction")); + $repo->addOption("META_SOURCES", $repoOptions); + RepositoryService::replaceRepository($repId, $repo); + + $responseInterface = $responseInterface->withBody(new SerializableResponseStream(new UserMessage($mess["ajxp_conf.56"]))); + + break; + + case "meta_source_delete" : + + $repId = InputFilter::sanitize(isSet($httpVars["workspaceId"]) ? $httpVars["workspaceId"] : $httpVars["repository_id"]); + $metaSourceId = InputFilter::sanitize(isSet($httpVars["metaId"]) ? $httpVars["metaId"] : $httpVars["plugId"]); + $repo = RepositoryService::findRepositoryByIdOrAlias($repId); + if (!is_object($repo)) { + throw new PydioException("Invalid workspace id! $repId"); + } + + $repoOptions = $repo->getContextOption($ctx, "META_SOURCES"); + if (is_array($repoOptions) && array_key_exists($metaSourceId, $repoOptions)) { + unset($repoOptions[$metaSourceId]); + uksort($repoOptions, array($this,"metaSourceOrderingFunction")); + $repo->addOption("META_SOURCES", $repoOptions); + RepositoryService::replaceRepository($repId, $repo); + }else{ + throw new PydioException("Cannot find meta source ".$metaSourceId); + } + + $responseInterface = $responseInterface->withBody(new SerializableResponseStream(new UserMessage($mess["ajxp_conf.57"]))); + + break; + + case "meta_source_edit" : + + $repId = InputFilter::sanitize(isSet($httpVars["workspaceId"]) ? $httpVars["workspaceId"] : $httpVars["repository_id"]); + $repo = RepositoryService::findRepositoryByIdOrAlias($repId); + if (!is_object($repo)) { + throw new PydioException("Invalid workspace id! $repId"); + } + if (isSet($httpVars["bulk_data"])) { + $bulkData = json_decode(InputFilter::magicDequote($httpVars["bulk_data"]), true); + $repoOptions = $repo->getContextOption($ctx, "META_SOURCES"); + if (!is_array($repoOptions)) { + $repoOptions = array(); + } + if (isSet($bulkData["delete"]) && count($bulkData["delete"])) { + foreach ($bulkData["delete"] as $key) { + if (isSet($repoOptions[$key])) unset($repoOptions[$key]); + } + } + if (isSet($bulkData["add"]) && count($bulkData["add"])) { + foreach ($bulkData["add"] as $key => $value) { + if (isSet($repoOptions[$key])) $this->mergeExistingParameters($value, $repoOptions[$key]); + $repoOptions[$key] = $value; + } + } + if (isSet($bulkData["edit"]) && count($bulkData["edit"])) { + foreach ($bulkData["edit"] as $key => $value) { + if (isSet($repoOptions[$key])) $this->mergeExistingParameters($value, $repoOptions[$key]); + $repoOptions[$key] = $value; + } + } + } else { + $metaSourceId = InputFilter::sanitize(isSet($httpVars["metaId"]) ? $httpVars["metaId"] : $httpVars["plugId"]); + $repoOptions = $repo->getContextOption($ctx, "META_SOURCES"); + if (!is_array($repoOptions)) { + $repoOptions = array(); + } + if(isSet($httpVars["request_body"])){ + $options = $httpVars["request_body"]; + }else if (isSet($httpVars["json_data"])) { + $options = json_decode(InputFilter::magicDequote($httpVars["json_data"]), true); + } else { + $options = array(); + $this->parseParameters($ctx, $httpVars, $options, true); + } + if (isset($repoOptions[$metaSourceId])) { + $this->mergeExistingParameters($options, $repoOptions[$metaSourceId]); + } + $repoOptions[$metaSourceId] = $options; + } + uksort($repoOptions, array($this,"metaSourceOrderingFunction")); + $repo->addOption("META_SOURCES", $repoOptions); + RepositoryService::replaceRepository($repId, $repo); + + $responseInterface = $responseInterface->withBody(new SerializableResponseStream(new UserMessage($mess["ajxp_conf.58"]))); + break; + + case "list_all_repositories_json": + + $repositories = RepositoryService::listAllRepositories(); + $repoOut = array(); + foreach ($repositories as $repoObject) { + $repoOut[$repoObject->getId()] = $repoObject->getDisplay(); + } + $mess = LocaleService::getMessages(); + $responseInterface = new JsonResponse(["LEGEND" => $mess["ajxp_conf.150"], "LIST" => $repoOut]); + + break; + + default: + break; + + } + + return $responseInterface; + } + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @return ResponseInterface + * @throws PydioException + */ + public function delete(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface){ + + $mess = LocaleService::getMessages(); + $httpVars = $requestInterface->getParsedBody(); + + $repId = InputFilter::sanitize(isSet($httpVars["workspaceId"]) ? $httpVars["workspaceId"] : $httpVars["repository_id"]); + $repo = RepositoryService::findRepositoryByIdOrAlias($repId); + if(!is_object($repo)){ + $res = -1; + }else{ + $res = RepositoryService::deleteRepository($repId); + } + if ($res == -1) { + throw new PydioException($mess[427]); + } + + $message = new UserMessage($mess["ajxp_conf.59"]); + $reload = new ReloadMessage(); + return $responseInterface->withBody(new SerializableResponseStream([$message, $reload])); + + } + + + /** + * @param ServerRequestInterface $requestInterface Full set of query parameters + * @param string $rootPath Path to prepend to the resulting nodes + * @param string $relativePath Specific path part for this function + * @param string $paginationHash Number added to url#2 for pagination purpose. + * @param string $findNodePosition Path to a given node to try to find it + * @param string $aliasedDir Aliased path used for alternative url + * @return NodesList A populated NodesList object, eventually recursive. + */ + public function listNodes(ServerRequestInterface $requestInterface, $rootPath, $relativePath, $paginationHash = null, $findNodePosition = null, $aliasedDir = null) + { + $fullBasePath = "/" . $rootPath . "/" . $relativePath; + $REPOS_PER_PAGE = 50; + $paginationHash = $paginationHash === null ? 1 : $paginationHash; + $offset = ($paginationHash - 1) * $REPOS_PER_PAGE; + $count = null; + $ctxUser = $this->context->getUser(); + $nodesList = new NodesList($fullBasePath); + $v2Api = $requestInterface->getAttribute("api") === "v2"; + + // Load all repositories = normal, templates, and templates children + $criteria = array( + "ORDERBY" => array("KEY" => "display", "DIR"=>"ASC"), + "CURSOR" => array("OFFSET" => $offset, "LIMIT" => $REPOS_PER_PAGE) + ); + if($this->currentUserIsGroupAdmin()){ + $criteria = array_merge($criteria, array( + "owner_user_id" => AJXP_FILTER_EMPTY, + "groupPath" => "regexp:/^".str_replace("/", "\/", $ctxUser->getGroupPath()).'/', + )); + }else{ + $criteria["parent_uuid"] = AJXP_FILTER_EMPTY; + } + if(isSet($requestInterface->getParsedBody()["template_children_id"])){ + $criteria["parent_uuid"] = InputFilter::sanitize($requestInterface->getParsedBody()["template_children_id"], InputFilter::SANITIZE_ALPHANUM); + } + + $repos = RepositoryService::listRepositoriesWithCriteria($criteria, $count); + $nodesList->initColumnsData("filelist", "list", "ajxp_conf.repositories"); + $nodesList->setPaginationData($count, $paginationHash, ceil($count / $REPOS_PER_PAGE)); + $nodesList->appendColumn("ajxp_conf.8", "ajxp_label"); + $nodesList->appendColumn("ajxp_conf.9", "accessType"); + $nodesList->appendColumn("ajxp_conf.125", "slug"); + + $driverLabels = array(); + + foreach ($repos as $repoIndex => $repoObject) { + + if($repoObject->getAccessType() == "ajxp_conf" || $repoObject->getAccessType() == "ajxp_shared") continue; + if (!empty($ctxUser) && !$ctxUser->canAdministrate($repoObject))continue; + if(is_numeric($repoIndex)) $repoIndex = "".$repoIndex; + + $icon = "hdd_external_unmount.png"; + $accessType = $repoObject->getAccessType(); + $accessLabel = $this->getDriverLabel($accessType, $driverLabels); + $label = $repoObject->getDisplay(); + $editable = $repoObject->isWriteable(); + if ($repoObject->isTemplate) { + $icon = "hdd_external_mount.png"; + if ($ctxUser != null && $ctxUser->getGroupPath() != "/") { + $editable = false; + } + } + + $meta = [ + "text" => $label, + "repository_id" => $repoIndex, + "accessType" => ($repoObject->isTemplate?"Template for ":"").$repoObject->getAccessType(), + "accessLabel" => $accessLabel, + "icon" => $icon, + "owner" => ($repoObject->hasOwner()?$repoObject->getOwner():""), + "openicon" => $icon, + "slug" => $repoObject->getSlug(), + "parentname" => "/repositories", + "ajxp_mime" => "repository".($editable?"_editable":""), + "is_template" => ($repoObject->isTemplate?"true":"false") + ]; + + $nodeKey = "/data/repositories/$repoIndex"; + $this->appendBookmarkMeta($nodeKey, $meta); + $repoNode = new AJXP_Node($v2Api ? (string)$repoIndex : $nodeKey, $meta); + $nodesList->addBranch($repoNode); + + if ($repoObject->isTemplate) { + // Now Load children for template repositories + $children = RepositoryService::listRepositoriesWithCriteria(array("parent_uuid" => $repoIndex . ""), $count); + foreach($children as $childId => $childObject){ + if (!empty($ctxUser) && !$ctxUser->canAdministrate($childObject))continue; + if(is_numeric($childId)) $childId = "".$childId; + $meta = array( + "text" => $childObject->getDisplay(), + "repository_id" => $childId, + "accessType" => $childObject->getAccessType(), + "accessLabel" => $this->getDriverLabel($childObject->getAccessType(), $driverLabels), + "icon" => "repo_child.png", + "slug" => $childObject->getSlug(), + "owner" => ($childObject->hasOwner()?$childObject->getOwner():""), + "openicon" => "repo_child.png", + "parentname" => "/repositories", + "ajxp_mime" => "repository_editable", + "template_name" => $label + ); + $cNodeKey = "/data/repositories/$childId"; + $this->appendBookmarkMeta($cNodeKey, $meta); + $repoNode = new AJXP_Node($v2Api ? $childId : $cNodeKey, $meta); + $nodesList->addBranch($repoNode); + } + } + } + + return $nodesList; + } + + /** + * Get label for an access.* plugin + * @param $pluginId + * @param $labels + * @return mixed|string + */ + protected function getDriverLabel($pluginId, &$labels){ + if(isSet($labels[$pluginId])){ + return $labels[$pluginId]; + } + $plugin = PluginsService::getInstance(Context::emptyContext())->getPluginById("access.".$pluginId); + if(!is_object($plugin)) { + $label = "access.$plugin (plugin disabled!)"; + }else{ + $label = $plugin->getManifestLabel(); + } + $labels[$pluginId] = $label; + return $label; + } + + /** + * @param $name + * @return bool + */ + public function repositoryExists($name) + { + RepositoryService::listRepositoriesWithCriteria(array("display" => $name), $count); + return $count > 0; + } + + /** + * Reorder meta sources + * @param $key1 + * @param $key2 + * @return int + */ + public function metaSourceOrderingFunction($key1, $key2) + { + $a1 = explode(".", $key1); + $t1 = array_shift($a1); + $a2 = explode(".", $key2); + $t2 = array_shift($a2); + if($t1 == "index") return 1; + if($t1 == "metastore") return -1; + if($t2 == "index") return -1; + if($t2 == "metastore") return 1; + if($key1 == "meta.git" || $key1 == "meta.svn") return 1; + if($key2 == "meta.git" || $key2 == "meta.svn") return -1; + return strcmp($key1, $key2); + } + + /** + * @param ContextInterface $ctx + * @param RepositoryInterface $repository + * @param array $definitions + * @param string $currentAdminBasePath + * @return array + */ + protected function serializeRepositoryToJSON(ContextInterface $ctx, $repository, $definitions, $currentAdminBasePath){ + $nested = []; + $buffer = [ + "id" => $repository->getId(), + "securityScope" => $repository->securityScope() + ]; + if(!$repository->isTemplate()){ + $buffer["slug"] = $repository->getSlug(); + } + $groupPath = $repository->getGroupPath(); + if ($groupPath != null) { + if($currentAdminBasePath != "/") { + $groupPath = substr($repository->getGroupPath(), strlen($currentAdminBasePath)); + } + $buffer["groupPath"]= $groupPath; + } + foreach ($repository as $name => $option) { + if(strstr($name, " ")>-1) continue; + if (in_array($name, ["driverInstance", "id", "uuid", "path", "recycle", "create", "enabled"])) continue; + if(is_array($option)) { + $nested[] = $option; + } else{ + $buffer[$name] = $option; + } + } + if (count($nested)) { + $buffer["parameters"]= []; + + foreach ($nested as $option) { + foreach ($option as $key => $optValue) { + if(isSet($definitions[$key]) && $definitions[$key]["type"] == "password" && !empty($optValue)){ + $optValue = "__AJXP_VALUE_SET__"; + } + $buffer["parameters"][$key] = $optValue; + } + } + // Add SLUG? + /* + if(!empty($buffer["slug"])) { + $buffer["PARAMETERS"]["AJXP_SLUG"] = $buffer["slug"]; + } + if(!empty($buffer["groupPath"])) { + $buffer["PARAMETERS"]["AJXP_GROUP_PATH_PARAMETER"] = $buffer["groupPath"]; + } + */ + } + if(isSet($buffer["parameters"]) && isSet($buffer["parameters"]["META_SOURCES"])){ + $buffer["features"] = $buffer["parameters"]["META_SOURCES"]; + unset($buffer["parameters"]["META_SOURCES"]); + } + if(!$repository->isTemplate()){ + $buffer["info"]= []; + $users = UsersService::countUsersForRepository($ctx, $repository->getId(), false, true); + $cursor = ["count"]; + $shares = ConfService::getConfStorageImpl()->simpleStoreList("share", $cursor, "", "serial", '', $repository->getId()); + $buffer["info"] = [ + "users" => $users, + "shares" => count($shares) + ]; + $rootGroup = RolesService::getRole("AJXP_GRP_/"); + if($rootGroup !== false && $rootGroup->hasMask($repository->getId())){ + $buffer["mask"]= $rootGroup->getMask($repository->getId()); + } + } + if ($repository->hasParent()) { + $parent = RepositoryService::getRepositoryById($repository->getParentId()); + if (isSet($parent) && $parent->isTemplate()) { + $parentLabel = $parent->getDisplay(); + $parentType = $parent->getAccessType(); + $buffer["TEMPLATE"] = [ + "id" => $repository->getParentId(), + "label" => $parentLabel, + "type" => $parentType, + "DEFINED_PARAMETERS" => [] + ]; + foreach ($parent->getOptionsDefined() as $parentOptionName) { + $buffer["TEMPLATE"]["DEFINED_PARAMETERS"][] = $parentOptionName; + } + } + } + return $buffer; + } + + /** + * @param ContextInterface $ctx + * @param RepositoryInterface $repository + * @param array $definitions + * @param string $currentAdminBasePath + * @return string + */ + protected function serializeRepositoryToXML(ContextInterface $ctx, $repository, $definitions, $currentAdminBasePath){ + $nested = []; + $buffer = "getId()."\" securityScope=\"".$repository->securityScope()."\""; + foreach ($repository as $name => $option) { + if(strstr($name, " ")>-1) continue; + if ($name == "driverInstance") continue; + if (!is_array($option)) { + if (is_bool($option)) { + $option = ($option?"true":"false"); + } + $buffer .= " $name=\"".StringHelper::xmlEntities($option, true)."\" "; + } else if (is_array($option)) { + $nested[] = $option; + } + } + if (count($nested)) { + $buffer .= ">" ; + foreach ($nested as $option) { + foreach ($option as $key => $optValue) { + if (is_array($optValue) && count($optValue)) { + $buffer .= "" ; + } else if (is_object($optValue)){ + $buffer .= ""; + } else { + if (is_bool($optValue)) { + $optValue = ($optValue?"true":"false"); + } else if(isSet($definitions[$key]) && $definitions[$key]["type"] == "password" && !empty($optValue)){ + $optValue = "__AJXP_VALUE_SET__"; + } + + $optValue = StringHelper::xmlEntities($optValue, true); + $buffer .= ""; + } + } + } + // Add SLUG + if(!$repository->isTemplate()) { + $buffer .= "getSlug()."\"/>"; + } + if ($repository->getGroupPath() != null) { + $groupPath = $repository->getGroupPath(); + if($currentAdminBasePath != "/") $groupPath = substr($repository->getGroupPath(), strlen($currentAdminBasePath)); + $buffer .= ""; + } + + $buffer .= ""; + } else { + $buffer .= "/>"; + } + if ($repository->hasParent()) { + $parent = RepositoryService::getRepositoryById($repository->getParentId()); + if (isSet($parent) && $parent->isTemplate()) { + $parentLabel = $parent->getDisplay(); + $parentType = $parent->getAccessType(); + $buffer .= ""; + } + } + if(!$repository->isTemplate()){ + $buffer .= ""; + $users = UsersService::countUsersForRepository($ctx, $repository->getId(), false, true); + $cursor = ["count"]; + $shares = ConfService::getConfStorageImpl()->simpleStoreList("share", $cursor, "", "serial", '', $repository->getId()); + $buffer .= ''; + $buffer .= ''; + $rootGroup = RolesService::getRole("AJXP_GRP_/"); + if($rootGroup !== false && $rootGroup->hasMask($repository->getId())){ + $buffer .= "getMask($repository->getId()))."]]>"; + } + $buffer .= ""; + } + return $buffer; + } + + /** + * @param PluginsService $pServ + * @param string $format + * @param AbstractAccessDriver $plug + * @param RepositoryInterface $repository + * @return string|array + */ + protected function serializeRepositoryDriverInfos(PluginsService $pServ, $format, $plug, $repository){ + $manifest = $plug->getManifestRawContent("server_settings/param"); + $manifest = XMLFilter::resolveKeywords($manifest); + $clientSettings = $plug->getManifestRawContent("client_settings", "xml"); + $iconClass = "";$descriptionTemplate = ""; + if($clientSettings->length){ + $iconClass = $clientSettings->item(0)->getAttribute("iconClass"); + $descriptionTemplate = $clientSettings->item(0)->getAttribute("description_template"); + } + $metas = $pServ->getPluginsByType("metastore"); + $metas = array_merge($metas, $pServ->getPluginsByType("meta")); + $metas = array_merge($metas, $pServ->getPluginsByType("index")); + + if($format === "xml"){ + $buffer = "getAccessType()."\" label=\"". StringHelper::xmlEntities($plug->getManifestLabel()) ."\" + iconClass=\"$iconClass\" description_template=\"$descriptionTemplate\" + description=\"". StringHelper::xmlEntities($plug->getManifestDescription()) ."\">$manifest"; + + $buffer .= ""; + /** @var Plugin $metaPlug */ + foreach ($metas as $metaPlug) { + $buffer .= "getId()."\" label=\"". StringHelper::xmlEntities($metaPlug->getManifestLabel()) ."\" description=\"". StringHelper::xmlEntities($metaPlug->getManifestDescription()) ."\">"; + $manifest = $metaPlug->getManifestRawContent("server_settings/param"); + $manifest = XMLFilter::resolveKeywords($manifest); + $buffer .= $manifest; + $buffer .= ""; + } + $buffer .= ""; + return $buffer; + }else{ + $dData = [ + "name" => $repository->getAccessType(), + "label" => $plug->getManifestLabel(), + "description" => $plug->getManifestDescription(), + "iconClass" => $iconClass, + "descriptionTemplate" => $descriptionTemplate, + "parameters" => $this->xmlServerParamsToArray($manifest) + ]; + $metaSources = []; + /** @var Plugin $metaPlug */ + foreach($metas as $metaPlug){ + $metaSources[$metaPlug->getId()] = [ + "id" => $metaPlug->getId(), + "label" => $metaPlug->getManifestLabel(), + "description" => $metaPlug->getManifestDescription(), + "parameters" => $this->xmlServerParamsToArray(XMLFilter::resolveKeywords($metaPlug->getManifestRawContent("server_settings/param"))) + ]; + } + $data = ["driver" => $dData, "metasources" => $metaSources]; + return $data; + } + } + + /** + * @param string $xmlParamsString + * @return array + */ + protected function xmlServerParamsToArray($xmlParamsString){ + $doc = new \DOMDocument(); + $doc->loadXML("$xmlParamsString"); + $result = XMLHelper::xmlToArray($doc, ["attributePrefix" => ""]); + if(isSet($result["parameters"]["param"])){ + return $result["parameters"]["param"]; + }else{ + return []; + } + } + + /** + * Search the manifests declaring ajxpdriver as their root node. Remove ajxp_* drivers + * @static + * @param string $filterByTagName + * @param string $filterByDriverName + * @param bool $limitToEnabledPlugins + * @return string + */ + protected function availableDriversToXML($filterByTagName = "", $filterByDriverName="", $limitToEnabledPlugins = false) + { + $nodeList = PluginsService::getInstance(Context::emptyContext())->searchAllManifests("//ajxpdriver", "node", false, $limitToEnabledPlugins); + $xmlBuffer = ""; + /** @var \DOMElement $node */ + foreach ($nodeList as $node) { + $dName = $node->getAttribute("name"); + if($filterByDriverName != "" && $dName != $filterByDriverName) continue; + if(strpos($dName, "ajxp_") === 0) continue; + if ($filterByTagName == "") { + $xmlBuffer .= $node->ownerDocument->saveXML($node); + continue; + } + $q = new \DOMXPath($node->ownerDocument); + $cNodes = $q->query("//".$filterByTagName, $node); + $xmlBuffer .= "attributes as $attr) $xmlBuffer.= " $attr->name=\"$attr->value\" "; + $xmlBuffer .=">"; + foreach ($cNodes as $child) { + $xmlBuffer .= $child->ownerDocument->saveXML($child); + } + $xmlBuffer .= ""; + } + return $xmlBuffer; + } + + +} \ No newline at end of file diff --git a/core/src/plugins/access.ajxp_conf/src/RolesManager.php b/core/src/plugins/access.ajxp_conf/src/RolesManager.php new file mode 100644 index 0000000000..8dae042bf6 --- /dev/null +++ b/core/src/plugins/access.ajxp_conf/src/RolesManager.php @@ -0,0 +1,598 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Driver\DataProvider\Provisioning; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Access\Core\Filter\AJXP_PermissionMask; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Model\NodesList; +use Pydio\Conf\Core\AbstractUser; +use Pydio\Conf\Core\AJXP_Role; +use Pydio\Core\Utils\Vars\XMLFilter; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Http\Message\ReloadMessage; +use Pydio\Core\Http\Message\UserMessage; +use Pydio\Core\Http\Response\SerializableResponseStream; +use Pydio\Core\Model\Context; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Services\RepositoryService; +use Pydio\Core\Services\RolesService; +use Pydio\Core\Services\UsersService; +use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Core\Utils\Vars\OptionsHelper; +use Pydio\Core\Utils\Vars\PathUtils; +use Pydio\Log\Core\Logger; +use Zend\Diactoros\Response\JsonResponse; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Class RolesManager + * @package Pydio\Access\Driver\DataProvider\Provisioning + */ +class RolesManager extends AbstractManager +{ + + /** + * @param ServerRequestInterface $requestInterface + * @return ServerRequestInterface + */ + public function preprocessApi2Actions(ServerRequestInterface $requestInterface){ + $vars = $requestInterface->getParsedBody(); + if(isSet($vars["roleId"])) $vars["role_id"] = $vars["roleId"]; + return $requestInterface->withParsedBody($vars); + + } + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @return ResponseInterface + * @throws \Exception + * @throws \Pydio\Core\Exception\UserNotFoundException + */ + public function rolesActions(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface){ + + if($requestInterface->getAttribute("api") === "v2"){ + $requestInterface = $this->preprocessApi2Actions($requestInterface); + } + + $action = $requestInterface->getAttribute("action"); + /** @var ContextInterface $ctx */ + $ctx = $requestInterface->getAttribute("ctx"); + $httpVars = $requestInterface->getParsedBody(); + $mess = LocaleService::getMessages(); + + + switch ($action){ + + // ROLES + case "create_role": + + $roleId = InputFilter::sanitize(InputFilter::magicDequote($httpVars["role_id"]), InputFilter::SANITIZE_HTML_STRICT); + if (!strlen($roleId)) { + throw new \Exception($mess[349]); + } + if (RolesService::getRole($roleId) !== false) { + throw new \Exception($mess["ajxp_conf.65"]); + } + $r = new AJXP_Role($roleId); + $user = $ctx->getUser(); + if ($user !== null && $user->getGroupPath()!== null) { + $r->setGroupPath($user->getGroupPath()); + } + RolesService::updateRole($r); + + $userMessage = new UserMessage($mess["ajxp_conf.66"]); + $reloadMessage = new ReloadMessage("", $httpVars["role_id"]); + $responseInterface = $responseInterface->withBody(new SerializableResponseStream([$userMessage, $reloadMessage])); + + break; + + case "edit_role" : + + $roleId = InputFilter::magicDequote($httpVars["role_id"]); + $roleGroup = false; + $userObject = null; + $groupLabel = null; + $currentMainUser = $ctx->getUser(); + + if (strpos($roleId, "AJXP_GRP_") === 0) { + $groupPath = substr($roleId, strlen("AJXP_GRP_")); + $filteredGroupPath = (!empty($currentMainUser) ? $currentMainUser->getRealGroupPath($groupPath) : $groupPath); + if($filteredGroupPath == "/"){ + $roleId = "AJXP_GRP_/"; + $groupLabel = $mess["ajxp_conf.151"]; + $roleGroup = true; + }else{ + $groups = UsersService::listChildrenGroups(PathUtils::forwardSlashDirname($filteredGroupPath)); + $key = "/".basename($groupPath); + if (!array_key_exists($key, $groups)) { + throw new \Exception("Cannot find group with this id!"); + } + $roleId = "AJXP_GRP_".$filteredGroupPath; + $groupLabel = $groups[$key]; + $roleGroup = true; + } + } + if (strpos($roleId, "AJXP_USR_") === 0) { + $usrId = str_replace("AJXP_USR_/", "", $roleId); + $userObject = UsersService::getUserById($usrId); + if(!empty($currentMainUser) && !$currentMainUser->canAdministrate($userObject)){ + throw new \Exception("Cant find user!"); + } + $role = $userObject->getPersonalRole(); + } else { + if($roleGroup){ + $role = RolesService::getOrCreateRole($roleId, $ctx->hasUser() ? $ctx->getUser()->getGroupPath() : "/"); + }else{ + $role = RolesService::getRole($roleId); + } + } + if ($role === false) { + throw new \Exception("Cant find role! "); + } + + $data = [ + "ROLE" => $role->getDataArray(true) + ]; + + if (isSet($userObject)) { + + $data["USER"] = array(); + $data["USER"]["LOCK"] = $userObject->getLock(); + $data["USER"]["PROFILE"] = $userObject->getProfile(); + $data["USER"]["ROLES"] = array_keys($userObject->getRoles()); + $rolesList = RolesService::getRolesList(array(), true); + $data["ALL"]["ROLES"] = array_keys($rolesList); + $data["ALL"]["ROLES_DETAILS"] = array(); + foreach($rolesList as $rId => $rObj){ + $data["ALL"]["ROLES_DETAILS"][$rId] = array("label" => $rObj->getLabel(), "sticky" => $rObj->alwaysOverrides()); + } + if ($userObject instanceof AbstractUser && isSet($userObject->parentRole)) { + $data["PARENT_ROLE"] = $userObject->parentRole->getDataArray(); + } + + } else if (isSet($groupPath)) { + + $data["GROUP"] = array("PATH" => $groupPath, "LABEL" => $groupLabel); + if($roleId != "AJXP_GRP_/"){ + $parentGroupRoles = array(); + $parentPath = PathUtils::forwardSlashDirname($roleId); + while($parentPath != "AJXP_GRP_"){ + $parentRole = RolesService::getRole($parentPath); + if($parentRole != null) { + array_unshift($parentGroupRoles, $parentRole); + } + $parentPath = PathUtils::forwardSlashDirname($parentPath); + } + $rootGroup = RolesService::getRole("AJXP_GRP_/"); + if($rootGroup != null) array_unshift($parentGroupRoles, $rootGroup); + if(count($parentGroupRoles)){ + $parentRole = clone array_shift($parentGroupRoles); + foreach($parentGroupRoles as $pgRole){ + $parentRole = $pgRole->override($parentRole); + } + $data["PARENT_ROLE"] = $parentRole->getDataArray(); + } + } + + } + + if($requestInterface->getAttribute("api") === "v2" && (empty($httpVars["load_fill_values"]) || $httpVars["load_fill_values"] !== "true")){ + $responseInterface = new JsonResponse($data["ROLE"]); + break; + } + + $allReps = RepositoryService::listAllRepositories(); + $repos = array(); + $repoDetailed = array(); + $sharedRepos = array(); + if(isSet($userObject)){ + // Add User shared Repositories as well + $acls = $userObject->getMergedRole()->listAcls(); + if(count($acls)) { + $sharedRepos = RepositoryService::listRepositoriesWithCriteria(array( + "uuid" => array_keys($acls), + "parent_uuid" => AJXP_FILTER_NOT_EMPTY, + "owner_user_id" => AJXP_FILTER_NOT_EMPTY + ), $count); + $allReps = array_merge($allReps, $sharedRepos); + } + } + + // USER + foreach ($allReps as $repositoryId => $repositoryObject) { + if (!empty($userObject) && + ( + !$userObject->canSee($repositoryObject) || $repositoryObject->isTemplate() + || ($repositoryObject->getAccessType()=="ajxp_conf" && !$userObject->isAdmin()) + || ($repositoryObject->getUniqueUser() != null && $repositoryObject->getUniqueUser() != $userObject->getId()) + ) + ){ + continue; + }else if(empty($userObject) && ( + (empty($currentMainUser) && !$currentMainUser->canAdministrate($repositoryObject)) || $repositoryObject->isTemplate() + )){ + continue; + } + $meta = array(); + try{ + $metaSources = $repositoryObject->getContextOption($ctx, "META_SOURCES"); + if($metaSources !== null){ + $meta = array_keys($metaSources); + } + }catch(\Exception $e){ + if(isSet($sharedRepos[$repositoryId])) unset($sharedRepos[$repositoryId]); + Logger::error("ConfDriver", "Invalid Share", "Repository $repositoryId has no more parent. Should be deleted."); + continue; + } + $repoDetailed[$repositoryId] = array( + "label" => $repositoryObject->getDisplay(), + "driver" => $repositoryObject->getAccessType(), + "scope" => $repositoryObject->securityScope(), + "meta" => $meta + ); + + if(array_key_exists($repositoryId, $sharedRepos)){ + $sharedRepos[$repositoryId] = $repositoryObject->getDisplay(); + $repoParentLabel = $repoParentId = $repositoryObject->getParentId(); + $repoOwnerId = $repositoryObject->getOwner(); + if(isSet($allReps[$repoParentId])){ + $repoParentLabel = $allReps[$repoParentId]->getDisplay(); + } + $repoOwnerLabel = UsersService::getUserPersonalParameter("USER_DISPLAY_NAME", $repoOwnerId, "core.conf", $repoOwnerId); + $repoDetailed[$repositoryId]["share"] = array( + "parent_user" => $repoOwnerId, + "parent_user_label" => $repoOwnerLabel, + "parent_repository" => $repoParentId, + "parent_repository_label" => $repoParentLabel + ); + }else{ + $repos[$repositoryId] = $repositoryObject->getDisplay(); + } + + } + + // Make sure it's utf8 + $data["ALL"] = array_merge((isSet($data["ALL"]) ? $data["ALL"]: []), [ + "PLUGINS_SCOPES" => [ + "GLOBAL_TYPES" => ["conf", "auth", "authfront", "log", "mq", "gui", "sec"], + "GLOBAL_PLUGINS" => ["action.avatar", "action.disclaimer", "action.scheduler", "action.skeleton", "action.updater"] + ], + "REPOSITORIES" => $repos, + "SHARED_REPOSITORIES" => $sharedRepos, + "REPOSITORIES_DETAILS" => $repoDetailed, + "PROFILES" => [ + "standard|".$mess["ajxp_conf.156"], + "admin|".$mess["ajxp_conf.157"], + "shared|".$mess["ajxp_conf.158"], + "guest|".$mess["ajxp_conf.159"] + ] + ]); + + $scope = "role"; + if($roleGroup) { + $scope = "group"; + if($roleId == "AJXP_GRP_/") { + $scope = "role"; + } + }else if(isSet($userObject)) { + $scope = "user"; + } + $data["SCOPE_PARAMS"] = array(); + $nodes = PluginsService::getInstance($ctx)->searchAllManifests("//param[contains(@scope,'".$scope."')]|//global_param[contains(@scope,'".$scope."')]", "node", false, true, true); + foreach ($nodes as $node) { + $pId = $node->parentNode->parentNode->attributes->getNamedItem("id")->nodeValue; + $origName = $node->attributes->getNamedItem("name")->nodeValue; + if($roleId == "AJXP_GRP_/" && strpos($origName, "ROLE_") ===0 ) continue; + $node->attributes->getNamedItem("name")->nodeValue = "AJXP_REPO_SCOPE_ALL/".$pId."/".$origName; + $nArr = array(); + foreach ($node->attributes as $attrib) { + $nArr[$attrib->nodeName] = XMLFilter::resolveKeywords($attrib->nodeValue); + } + $data["SCOPE_PARAMS"][] = $nArr; + } + + $responseInterface = new JsonResponse($data); + + break; + + case "post_json_role" : + + $roleId = InputFilter::magicDequote($httpVars["role_id"]); + $roleGroup = false; + $currentMainUser = $ctx->getUser(); + $userObject = $usrId = $filteredGroupPath = null; + if (strpos($roleId, "AJXP_GRP_") === 0) { + $groupPath = substr($roleId, strlen("AJXP_GRP_")); + $filteredGroupPath = (!empty($currentMainUser) ? $currentMainUser->getRealGroupPath($groupPath) : $groupPath); + $roleId = "AJXP_GRP_".$filteredGroupPath; + if($roleId != "AJXP_GRP_/"){ + $groups = UsersService::listChildrenGroups(PathUtils::forwardSlashDirname($filteredGroupPath)); + $key = "/".basename($groupPath); + if (!array_key_exists($key, $groups)) { + throw new \Exception("Cannot find group with this id!"); + } + $groupLabel = $groups[$key]; + }else{ + $groupLabel = $mess["ajxp_conf.151"]; + } + $roleGroup = true; + } + if (strpos($roleId, "AJXP_USR_") === 0) { + $usrId = str_replace("AJXP_USR_/", "", $roleId); + $userObject = UsersService::getUserById($usrId); + $ctxUser = $ctx->getUser(); + if(!empty($ctxUser) && !$ctxUser->canAdministrate($userObject)){ + throw new \Exception("Cannot post role for user ".$usrId); + } + $originalRole = $userObject->getPersonalRole(); + } else { + // second param = create if not exists. + if($roleGroup){ + $originalRole = RolesService::getOrCreateRole($roleId, $ctx->hasUser() ? $ctx->getUser()->getGroupPath() : "/"); + }else{ + $originalRole = RolesService::getRole($roleId); + } + } + if ($originalRole === false) { + throw new \Exception("Cant find role! "); + } + + if(isSet($httpVars["request_body"])){ + // This is API V2 : only the role is passed as json body + $roleData = $httpVars["request_body"]; + $data = ["METADATA" => []]; + $outputRoleOnly = true; + }else{ + // Other apis: a more complex + $jsonData = InputFilter::magicDequote($httpVars["json_data"]); + $data = json_decode($jsonData, true); + $roleData = $data["ROLE"]; + $outputRoleOnly = false; + } + $binariesContext = array(); + $parseContext = $ctx; + if (isset($userObject)) { + $parseContext = new Context(null, $ctx->getRepositoryId()); + $parseContext->setUserObject($userObject); + $binariesContext = array("USER" => $userObject->getId()); + } + if(isSet($data["FORMS"])){ + $forms = $data["FORMS"]; + foreach ($forms as $repoScope => $plugData) { + foreach ($plugData as $plugId => $formsData) { + $parsed = array(); + OptionsHelper::parseStandardFormParameters( + $parseContext, + $formsData, + $parsed, + "ROLE_PARAM_", + $binariesContext, + AJXP_Role::$cypheredPassPrefix + ); + $roleData["PARAMETERS"][$repoScope][$plugId] = $parsed; + } + } + }else{ + OptionsHelper::filterFormElementsFromMeta( + $parseContext, + $data["METADATA"], + $roleData, + ($userObject != null ? $usrId : null), + $binariesContext, + AJXP_Role::$cypheredPassPrefix + ); + } + $existingParameters = $originalRole->listParameters(true); + $this->mergeExistingParameters($roleData["PARAMETERS"], $existingParameters); + if (isSet($userObject) && isSet($data["USER"]) && isSet($data["USER"]["PROFILE"])) { + $userObject->setAdmin(($data["USER"]["PROFILE"] == "admin")); + $userObject->setProfile($data["USER"]["PROFILE"]); + } + if (isSet($data["GROUP_LABEL"]) && isSet($groupLabel) && $groupLabel != $data["GROUP_LABEL"]) { + ConfService::getConfStorageImpl()->relabelGroup($filteredGroupPath, $data["GROUP_LABEL"]); + } + + if($this->currentUserIsGroupAdmin()){ + // FILTER DATA FOR GROUP ADMINS + $params = $this->getEditableParameters($ctx, true, false); + foreach($roleData["PARAMETERS"] as $scope => &$plugsParameters){ + foreach($plugsParameters as $paramPlugin => &$parameters){ + foreach($parameters as $pName => $pValue){ + if(!isSet($params[$paramPlugin]) || !in_array($pName, $params[$paramPlugin])){ + unset($parameters[$pName]); + } + } + if(!count($parameters)){ + unset($plugsParameters[$paramPlugin]); + } + } + if(!count($plugsParameters)){ + unset($roleData["PARAMETERS"][$scope]); + } + } + // Remerge from parent + $roleData["PARAMETERS"] = $originalRole->array_merge_recursive2($originalRole->listParameters(), $roleData["PARAMETERS"]); + // Changing Actions is not allowed + $roleData["ACTIONS"] = $originalRole->listActionsStates(); + } + + if(isSet($roleData["MASKS"])){ + foreach($roleData["MASKS"] as $repoId => $serialMask){ + $roleData["MASKS"][$repoId] = new AJXP_PermissionMask($serialMask); + } + } + + try { + $originalRole->bunchUpdate($roleData); + if (isSet($userObject)) { + $userObject->updatePersonalRole($originalRole); + $userObject->save("superuser"); + } else { + RolesService::updateRole($originalRole); + } + // Reload Role + $savedValue = RolesService::getRole($originalRole->getId()); + if($outputRoleOnly){ + $output = $savedValue->getDataArray(true); + }else{ + $output = array("ROLE" => $savedValue->getDataArray(true), "SUCCESS" => true); + } + } catch (\Exception $e) { + $output = array("ERROR" => $e->getMessage()); + } + + $responseInterface = new JsonResponse($output); + + break; + + case "role_update_right" : + + if(!isSet($httpVars["role_id"]) || !isSet($httpVars["repository_id"]) || !isSet($httpVars["right"])) { + throw new PydioException($mess["ajxp_conf.61"]); + } + $rId = InputFilter::sanitize($httpVars["role_id"]); + $role = RolesService::getRole($rId); + if($role === false){ + throw new PydioException($mess["ajxp_conf.61"]."($rId)"); + } + $role->setAcl(InputFilter::sanitize($httpVars["repository_id"], InputFilter::SANITIZE_ALPHANUM), InputFilter::sanitize($httpVars["right"], InputFilter::SANITIZE_ALPHANUM)); + RolesService::updateRole($role); + + $responseInterface = $responseInterface->withBody(new SerializableResponseStream(new UserMessage($mess["ajxp_conf.46"].$httpVars["role_id"]))); + + break; + + default: + break; + + } + + return $responseInterface; + } + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @return ResponseInterface + * @throws PydioException + */ + public function delete(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface){ + + $mess = LocaleService::getMessages(); + $httpVars = $requestInterface->getParsedBody(); + + $roleId = InputFilter::sanitize(isSet($httpVars["roleId"]) ? $httpVars["roleId"] : $httpVars["role_id"], InputFilter::SANITIZE_DIRNAME); + if (RolesService::getRole($roleId) === false) { + throw new PydioException($mess["ajxp_conf.67"]); + } + RolesService::deleteRole($roleId); + + $message = new UserMessage($mess["ajxp_conf.68"]); + $reload = new ReloadMessage(); + + return $responseInterface->withBody(new SerializableResponseStream([$message, $reload])); + + } + + + /** + * @param ServerRequestInterface $requestInterface Full set of query parameters + * @param string $rootPath Path to prepend to the resulting nodes + * @param string $relativePath Specific path part for this function + * @param string $paginationHash Number added to url#2 for pagination purpose. + * @param string $findNodePosition Path to a given node to try to find it + * @param string $aliasedDir Aliased path used for alternative url + * @return NodesList A populated NodesList object, eventually recursive. + */ + public function listNodes(ServerRequestInterface $requestInterface, $rootPath, $relativePath, $paginationHash = null, $findNodePosition = null, $aliasedDir = null) + { + $nodesList = new NodesList("/$rootPath/$relativePath"); + $nodesList->initColumnsData("filelist", "list", "ajxp_conf.roles"); + $nodesList->appendColumn("ajxp_conf.76", "ajxp_label"); + $nodesList->appendColumn("ajxp_conf.114", "is_default"); + $nodesList->appendColumn("ajxp_conf.62", "rights_summary"); + + if(!UsersService::usersEnabled()) { + return $nodesList; + } + + $mess = LocaleService::getMessages(); + $ctxUser = $this->context->getUser(); + $roles = RolesService::getRolesList(array(), !$this->listSpecialRoles); + ksort($roles); + + if(!$this->listSpecialRoles && $this->pluginName != "ajxp_admin" && !$this->currentUserIsGroupAdmin()){ + $rootGroupRole = RolesService::getOrCreateRole("AJXP_GRP_/", empty($ctxUser) ? "/" : $ctxUser->getGroupPath()); + if($rootGroupRole->getLabel() == "AJXP_GRP_/"){ + $rootGroupRole->setLabel($mess["ajxp_conf.151"]); + RolesService::updateRole($rootGroupRole); + } + array_unshift($roles, $rootGroupRole); + } + + foreach ($roles as $roleObject) { + + $r = array(); + if(!empty($ctxUser) && !$ctxUser->canAdministrate($roleObject)) { + continue; + } + $count = 0; + $repos = RepositoryService::listRepositoriesWithCriteria(array("role" => $roleObject), $count); + foreach ($repos as $repoId => $repository) { + if($repository->getAccessType() == "ajxp_shared") continue; + if(!$roleObject->canRead($repoId) && !$roleObject->canWrite($repoId)) continue; + $rs = ($roleObject->canRead($repoId) ? "r" : ""); + $rs .= ($roleObject->canWrite($repoId) ? "w" : ""); + $r[] = $repository->getDisplay()." (".$rs.")"; + } + $rightsString = implode(", ", $r); + $nodeKey = "/".$rootPath."/".$relativePath."/".$roleObject->getId(); + $appliesToDefault = implode(",", $roleObject->listAutoApplies()); + if($roleObject->getId() == "AJXP_GRP_/"){ + $appliesToDefault = $mess["ajxp_conf.153"]; + } + $meta = array( + "icon" => "user-acl.png", + "rights_summary" => $rightsString, + "is_default" => $appliesToDefault, + "ajxp_mime" => "role", + "role_id" => $roleObject->getId(), + "text" => $roleObject->getLabel() + ); + $this->appendBookmarkMeta($nodeKey, $meta); + if($requestInterface->getAttribute("api") === "v2"){ + $nodeKey = $roleObject->getId(); + $meta["role"] = $roleObject->getDataArray(true); + } + $nodesList->addBranch(new AJXP_Node($nodeKey, $meta)); + } + return $nodesList; + + } +} \ No newline at end of file diff --git a/core/src/plugins/access.ajxp_conf/src/TreeManager.php b/core/src/plugins/access.ajxp_conf/src/TreeManager.php new file mode 100644 index 0000000000..315645776a --- /dev/null +++ b/core/src/plugins/access.ajxp_conf/src/TreeManager.php @@ -0,0 +1,225 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Driver\DataProvider\Provisioning; + +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Model\NodesList; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Utils\Vars\InputFilter; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class TreeManager + * @package Pydio\Access\Driver\DataProvider\Provisioning + */ +class TreeManager extends AbstractManager +{ + /** + * @var array + */ + protected $mainTree; + + /** + * @var array + */ + protected $currentBookmarks = []; + + /** + * TreeManager constructor. + * @param ContextInterface $ctx + * @param string $pluginName + * @param array $mainTree + */ + public function __construct(ContextInterface $ctx, $pluginName, $mainTree) + { + parent::__construct($ctx, $pluginName); + $this->mainTree = $mainTree; + } + + /** + * Forward call to dedicated managers, must implement AbstractManager + * @param ServerRequestInterface $requestInterface + * @param $childData + * @param $initialPath + * @param $rootSegment + * @param $otherSegments + * @param $hashValue + * @return NodesList + */ + protected function forwardToManagers(ServerRequestInterface $requestInterface, $childData, $initialPath, $rootSegment, $otherSegments, $hashValue){ + + if(!isSet($childData["LIST"]) && !isSet($childData["MANAGER"])){ + return new NodesList($initialPath); + } + + if(isSet($childData["ALIAS"])){ + $reSplits = explode("/", ltrim($childData["ALIAS"], "/")); + $rootSegment = array_shift($reSplits); + // additional part? + array_shift($otherSegments); + foreach($otherSegments as $vS) $reSplits[] = $vS; + $otherSegments = $reSplits; + } + + $httpVars = $requestInterface->getParsedBody(); + if(!isSet($childData["MANAGER"]) && isSet($childData["LIST"]) && is_callable($childData["LIST"])){ + return call_user_func($childData["LIST"], $httpVars, $rootSegment, implode("/", $otherSegments), $hashValue, isSet($httpVars["file"])?$httpVars["file"]:'', $initialPath); + } + + $callback = $childData["MANAGER"]; + /** @var AbstractManager $manager */ + if($callback === "TreeManager") { + $manager = $this; + }else{ + $manager = new $callback($this->context, $this->pluginName); + } + + return $manager->listNodes($requestInterface, $rootSegment, implode("/", $otherSegments), $hashValue, isSet($httpVars["file"])?$httpVars["file"]:'', $initialPath); + + } + + /** + * Render a bookmark node + * @param NodesList $nodesList + * @param $path + * @param $data + * @param $messages + */ + protected function appendNodeFromTree($nodesList, $path, $data, $messages){ + + if(!isSet($data["LABEL"])){ + return; + } + if(isSet($messages[$data["LABEL"]])) $data["LABEL"] = $messages[$data["LABEL"]]; + if(isSet($messages[$data["DESCRIPTION"]])) $data["DESCRIPTION"] = $messages[$data["DESCRIPTION"]]; + + $attributes = array( + "description" => $data["DESCRIPTION"], + "icon" => $data["ICON"], + "text" => $data["LABEL"], + "is_file" => false + ); + $this->appendBookmarkMeta($path, $attributes); + if(basename($path) == "users") { + $attributes["remote_indexation"] = "admin_search_users"; + } + if(isSet($data["AJXP_MIME"])) { + $attributes["ajxp_mime"] = $data["AJXP_MIME"]; + } + if(isSet($data["METADATA"]) && is_array($data["METADATA"])){ + $attributes = array_merge($attributes, $data["METADATA"]); + } + $node = new AJXP_Node($path, $attributes); + $hasChildren = isSet($data["CHILDREN"]); + if($hasChildren){ + $branch = new NodesList(); + $branch->setParentNode($node); + foreach($data["CHILDREN"] as $cKey => $cData){ + $this->appendNodeFromTree($branch, $path."/".$cKey, $cData, $messages); + } + $nodesList->addBranch($branch); + }else{ + $nodesList->addBranch($node); + } + + } + + + /** + * @param ServerRequestInterface $requestInterface + * @return NodesList + */ + public function dispatchList(ServerRequestInterface $requestInterface){ + + $httpVars = $requestInterface->getParsedBody(); + $messages = LocaleService::getMessages(); + $rootAttributes = array(); + $rootNodes = $this->mainTree; + + if(isSet($rootNodes["__metadata__"])){ + $rootAttributes = $rootNodes["__metadata__"]; + unset($rootNodes["__metadata__"]); + } + $parentName = ""; + $dir = trim(InputFilter::decodeSecureMagic((isset($httpVars["dir"]) ? $httpVars["dir"] : "")), " /"); + if (!empty($dir)) { + $hash = null; + if (strstr(urldecode($dir), "#") !== false) { + list($dir, $hash) = explode("#", urldecode($dir)); + } + $splits = explode("/", $dir); + $root = array_shift($splits); + if (count($splits)) { + $child = $splits[0]; + if (isSet($rootNodes[$root]["CHILDREN"][$child])) { + $childData = $rootNodes[$root]["CHILDREN"][$child]; + return $this->forwardToManagers($requestInterface, $childData, "/" . $dir, $root, $splits, $hash); + } + } else { + $parentName = "/".$root."/"; + $nodes = $rootNodes[$root]["CHILDREN"]; + } + } else { + $parentName = "/"; + $nodes = $rootNodes; + if($this->currentUserIsGroupAdmin()){ + $rootAttributes["group_admin"] = "1"; + } + } + if (isSet($httpVars["file"])) { + // = LS with Find Node Position + $parentName = $httpVars["dir"]."/"; + $nodes = array(basename($httpVars["file"]) => array("LABEL" => basename($httpVars["file"]))); + } + $nodesList = new NodesList(); + if (isSet($nodes)) { + $nodesList->setParentNode(new AJXP_Node("/", $rootAttributes)); + if(!isSet($httpVars["file"])){ + $nodesList->initColumnsData("", "detail"); + $nodesList->appendColumn("ajxp_conf.1", "ajxp_label"); + $nodesList->appendColumn("ajxp_conf.102", "description"); + } + foreach($nodes as $key => $data){ + $this->appendNodeFromTree($nodesList, $parentName . $key, $data, $messages); + } + } + return $nodesList; + + } + + + /** + * @param array|ServerRequestInterface $requestInterface Full set of query parameters + * @param string $rootPath Path to prepend to the resulting nodes + * @param string $relativePath Specific path part for this function + * @param string $paginationHash Number added to url#2 for pagination purpose. + * @param string $findNodePosition Path to a given node to try to find it + * @param string $aliasedDir Aliased path used for alternative url + * @return NodesList A populated NodesList object, eventually recursive. + */ + public function listNodes(ServerRequestInterface $requestInterface, $rootPath, $relativePath, $paginationHash = null, $findNodePosition = null, $aliasedDir = null) + { + return new NodesList(); + } +} \ No newline at end of file diff --git a/core/src/plugins/access.ajxp_conf/src/UsersManager.php b/core/src/plugins/access.ajxp_conf/src/UsersManager.php new file mode 100644 index 0000000000..f11e527ad2 --- /dev/null +++ b/core/src/plugins/access.ajxp_conf/src/UsersManager.php @@ -0,0 +1,1115 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Driver\DataProvider\Provisioning; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Model\NodesList; +use Pydio\Access\Core\Model\UserSelection; +use Pydio\Core\Controller\ProgressBarCLI; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Exception\UserNotFoundException; +use Pydio\Core\Http\Message\ReloadMessage; +use Pydio\Core\Http\Message\UserMessage; +use Pydio\Core\Http\Message\XMLDocMessage; +use Pydio\Core\Http\Message\XMLMessage; +use Pydio\Core\Http\Response\SerializableResponseStream; +use Pydio\Core\Model\Context; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Model\UserInterface; +use Pydio\Core\Services\AuthService; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Services\RolesService; +use Pydio\Core\Services\UsersService; +use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Core\Utils\Vars\PathUtils; +use Pydio\Core\Utils\Vars\StatHelper; +use Pydio\Log\Core\Logger; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Class UsersManager + * @package Pydio\Access\Driver\DataProvider\Provisioning + */ +class UsersManager extends AbstractManager +{ + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @return ResponseInterface + */ + public function peopleApiActions(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface){ + + $a = $requestInterface->getAttribute("action"); + $vars = $requestInterface->getParsedBody(); + $path = $vars["path"]; + if($a === "people-create-resource"){ + if($vars["resourceType"] === "user"){ + // Create a user + $uLogin = basename($path); + $gPath = dirname($path); + $requestInterface = $requestInterface + ->withAttribute("action", "create_user") + ->withParsedBody([ + "new_user_login" => $uLogin, + "new_user_pwd" => $vars["userPass"], + "group_path" => $gPath + ]); + }else{ + // Create a group + $requestInterface = $requestInterface + ->withAttribute("action", "create_group") + ->withParsedBody([ + "group_path" => $path, + "group_label" => $vars["groupLabel"] + ]); + } + return $this->usersActions($requestInterface, $responseInterface); + + }else if($a === "people-delete-resource"){ + + $baseName = basename($path); + $vars = []; + if(UsersService::userExists($baseName)){ + $vars["user_id"] = $baseName; + }else{ + $vars["group"] = $path; + } + return $this->delete($requestInterface->withParsedBody($vars), $responseInterface); + + }else if($a === "people-patch-resource"){ + + $path = $vars["path"]; + $resType = $vars["request_body"]["resourceType"]; + $paramName = $vars["request_body"]["parameterName"]; + $paramValue = $vars["request_body"]["parameterValue"]; + if($resType === "group" && $paramName === "groupLabel"){ + + $requestInterface = $requestInterface + ->withAttribute("action", "update_group_label") + ->withParsedBody(["group_label" => $paramValue, "group_path" => $path]) + ; + + }else if($resType === "user"){ + + $newVars = ["user_id" => basename($path)]; + switch($paramName){ + case "userPass": + $newVars["user_pwd"] = $paramValue; + $newAction = "update_user_pwd"; + break; + case "userProfile": + $newAction = "update_user_profile"; + $newVars["profile"] = $paramValue; + break; + case "userLock": + list($lockType, $lockValue) = explode(":", $paramValue); + $newVars["lock_type"] = $lockType; + $newVars["lock"] = $lockValue; + $newAction = "user_set_lock"; + break; + case "userAddRole": + $newVars["role_id"] = $paramValue; + $newAction = "user_add_role"; + break; + case "userRemoveRole": + $newVars["role_id"] = $paramValue; + $newAction = "user_delete_role"; + break; + case "userRoles": + $newVars["roles"] = json_encode($paramValue); // REENCODE JSON LIST OF ROLES + $newAction = "user_reorder_roles"; + break; + case "userPreferences": + $newAction = "save_user_preference"; + $i = 0; + foreach ($paramValue as $key => $val){ + $newVars["pref_name_".$i] = $key; + $newVars["pref_value_".$i] = $val; + $i++; + } + break; + default: + throw new PydioException("Arguments mismatch"); + break; + } + $requestInterface = $requestInterface + ->withAttribute("action", $newAction) + ->withParsedBody($newVars); + + }else{ + + throw new PydioException("Arguments mismatch"); + + } + return $this->usersActions($requestInterface, $responseInterface); + + } + + return $responseInterface; + } + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @return ResponseInterface + * @throws \Exception + * @throws \Pydio\Core\Exception\UserNotFoundException + */ + public function usersActions(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface){ + + $action = $requestInterface->getAttribute("action"); + /** @var ContextInterface $ctx */ + $ctx = $requestInterface->getAttribute("ctx"); + $httpVars = $requestInterface->getParsedBody(); + $mess = LocaleService::getMessages(); + $currentAdminBasePath = "/"; + $loggedUser = $ctx->getUser(); + if ($loggedUser!=null && $loggedUser->getGroupPath()!=null) { + $currentAdminBasePath = $loggedUser->getGroupPath(); + } + + switch ($action){ + + // USERS & GROUPS + case "create_user" : + + if (!isset($httpVars["new_user_login"]) || $httpVars["new_user_login"] == "" + || !isset($httpVars["new_user_pwd"]) || $httpVars["new_user_pwd"] == "") { + + throw new PydioException($mess["ajxp_conf.61"]); + + } + $original_login = InputFilter::magicDequote($httpVars["new_user_login"]); + $new_user_login = InputFilter::sanitize($original_login, InputFilter::SANITIZE_EMAILCHARS); + if($original_login != $new_user_login){ + throw new \Exception(str_replace("%s", $new_user_login, $mess["ajxp_conf.127"])); + } + if (UsersService::userExists($new_user_login, "w") || UsersService::isReservedUserId($new_user_login)) { + throw new \Exception($mess["ajxp_conf.43"]); + } + + $newUser = UsersService::createUser($new_user_login, $httpVars["new_user_pwd"]); + if (!empty($httpVars["group_path"])) { + $newUser->setGroupPath(rtrim($currentAdminBasePath, "/")."/".ltrim($httpVars["group_path"], "/")); + } else { + $newUser->setGroupPath($currentAdminBasePath); + } + + $newUser->save("superuser"); + + $reloadMessage = new ReloadMessage("", $new_user_login); + $userMessage = new UserMessage($mess["ajxp_conf.44"]); + $responseInterface = $responseInterface->withBody(new SerializableResponseStream([$reloadMessage, $userMessage])); + + break; + + case "create_group": + + if (isSet($httpVars["group_path"])) { + $basePath = PathUtils::forwardSlashDirname($httpVars["group_path"]); + if(empty($basePath)) $basePath = "/"; + $gName = InputFilter::sanitize(InputFilter::decodeSecureMagic(basename($httpVars["group_path"])), InputFilter::SANITIZE_ALPHANUM); + } else { + $basePath = substr($httpVars["dir"], strlen("/data/users")); + $gName = InputFilter::sanitize(InputFilter::magicDequote($httpVars["group_name"]), InputFilter::SANITIZE_ALPHANUM); + } + $gLabel = InputFilter::decodeSecureMagic($httpVars["group_label"]); + $basePath = ($ctx->hasUser() ? $ctx->getUser()->getRealGroupPath($basePath) : $basePath); + + UsersService::createGroup($basePath, $gName, $gLabel); + + $reloadMessage = new ReloadMessage(); + $userMessage = new UserMessage($mess["ajxp_conf.160"]); + $responseInterface = $responseInterface->withBody(new SerializableResponseStream([$reloadMessage, $userMessage])); + + break; + + case "update_group_label": + + $currentMainUser = $ctx->getUser(); + $groupPath = InputFilter::securePath(InputFilter::sanitize($httpVars["group_path"], InputFilter::SANITIZE_DIRNAME)); + $filteredGroupPath = (!empty($currentMainUser) ? $currentMainUser->getRealGroupPath($groupPath) : $groupPath); + ConfService::getConfStorageImpl()->relabelGroup($filteredGroupPath, InputFilter::sanitize($httpVars["group_label"], InputFilter::SANITIZE_FILENAME)); + $responseInterface = $responseInterface->withBody(new SerializableResponseStream(new UserMessage("Updated label for group ".$filteredGroupPath))); + + break; + + case "update_user_profile": + + if (!isSet($httpVars["user_id"]) || !isSet($httpVars["profile"]) || !UsersService::userExists($httpVars["user_id"]) || trim($httpVars["profile"]) == "") { + throw new PydioException($mess["ajxp_conf.61"]); + } + $profile = InputFilter::sanitize($httpVars["profile"], InputFilter::SANITIZE_ALPHANUM); + $userId = InputFilter::sanitize($httpVars["user_id"], InputFilter::SANITIZE_EMAILCHARS); + $user = UsersService::getUserById($userId); + if($ctx->hasUser() && !$ctx->getUser()->canAdministrate($user)){ + throw new PydioException("Cannot update user data for ".$userId); + } + $user->setProfile($profile); + $responseInterface = $responseInterface->withBody(new SerializableResponseStream(new UserMessage("Updated profile for user ".$userId))); + + break; + + case "user_set_lock" : + + $userId = InputFilter::decodeSecureMagic($httpVars["user_id"]); + $lock = ($httpVars["lock"] == "true" ? true : false); + $lockType = $httpVars["lock_type"]; + $ctxUser = $ctx->getUser(); + if (UsersService::userExists($userId)) { + $userObject = UsersService::getUserById($userId, false); + if( !empty($ctxUser) && !$ctxUser->canAdministrate($userObject)){ + throw new \Exception("Cannot update user data for ".$userId); + } + if ($lock) { + $userObject->setLock($lockType); + $userMessage = new UserMessage("Successfully set lock on user ($lockType)"); + $responseInterface = $responseInterface->withBody(new SerializableResponseStream([$userMessage])); + } else { + $userObject->removeLock($lockType); + $userMessage = new UserMessage("Successfully unlocked user"); + $responseInterface = $responseInterface->withBody(new SerializableResponseStream([$userMessage])); + } + $userObject->save("superuser"); + } + + break; + + case "change_admin_right" : + + $userId = $httpVars["user_id"]; + $user = UsersService::getUserById($userId); + if($ctx->hasUser() && !$ctx->getUser()->canAdministrate($user)){ + throw new \Exception("Cannot update user with id ".$userId); + } + $user->setAdmin(($httpVars["right_value"]=="1"?true:false)); + $user->save("superuser"); + + $reloadMessage = new ReloadMessage(); + $userMessage = new UserMessage($mess["ajxp_conf.45"].$httpVars["user_id"]); + $responseInterface = $responseInterface->withBody(new SerializableResponseStream([$reloadMessage, $userMessage])); + + break; + + case "user_update_right" : + + if(!isSet($httpVars["user_id"]) || !isSet($httpVars["repository_id"]) || !isSet($httpVars["right"]) || !UsersService::userExists($httpVars["user_id"])) { + + $userMessage = new UserMessage($mess["ajxp_conf.61"], LOG_LEVEL_ERROR); + $xmlMessage = new XMLMessage(""); + $responseInterface = $responseInterface->withBody(new SerializableResponseStream([$userMessage, $xmlMessage])); + + break; + } + $userId = InputFilter::sanitize($httpVars["user_id"], InputFilter::SANITIZE_EMAILCHARS); + $user = UsersService::getUserById($userId); + if($ctx->hasUser() && !$ctx->getUser()->canAdministrate($user)){ + throw new \Exception("Cannot update user with id ".$userId); + } + $user->getPersonalRole()->setAcl(InputFilter::sanitize($httpVars["repository_id"], InputFilter::SANITIZE_ALPHANUM), InputFilter::sanitize($httpVars["right"], InputFilter::SANITIZE_ALPHANUM)); + $user->save(); + $loggedUser = $ctx->getUser(); + if ($loggedUser->getId() == $user->getId()) { + AuthService::updateSessionUser($user); + } + + $userMessage = new UserMessage($mess["ajxp_conf.46"].$httpVars["user_id"]); + $xmlMessage = new XMLMessage("canRead($httpVars["repository_id"])."\" write=\"".$user->canWrite($httpVars["repository_id"])."\"/>"); + $responseInterface = $responseInterface->withBody(new SerializableResponseStream([$userMessage, $xmlMessage])); + + break; + + case "user_update_group": + + $userSelection = UserSelection::fromContext($ctx, $httpVars); + $dir = $httpVars["dir"]; + $dest = $httpVars["dest"]; + if (isSet($httpVars["group_path"])) { + // API Case + $groupPath = $httpVars["group_path"]; + } else { + if (strpos($dir, "/data/users",0)!==0 || strpos($dest, "/data/users",0)!==0) { + break; + } + $groupPath = substr($dest, strlen("/data/users")); + } + + $userId = null; + $usersMoved = array(); + + if (!empty($groupPath)) { + $targetPath = rtrim($currentAdminBasePath, "/")."/".ltrim($groupPath, "/"); + } else { + $targetPath = $currentAdminBasePath; + } + + foreach ($userSelection->getFiles() as $selectedUser) { + $userId = basename($selectedUser); + try{ + $user = UsersService::getUserById($userId); + if($ctx->hasUser() && !$ctx->getUser()->canAdministrate($user)){ + continue; + } + $user->setGroupPath($targetPath, true); + $user->save("superuser"); + $usersMoved[] = $user->getId(); + }catch (UserNotFoundException $u){ + continue; + } + } + $chunks = []; + if(count($usersMoved)){ + $chunks[] = new UserMessage(count($usersMoved)." user(s) successfully moved to ".$targetPath); + $chunks[] = new ReloadMessage($dest, $userId); + $chunks[] = new ReloadMessage(); + }else{ + $chunks[] = new UserMessage("No users moved, there must have been something wrong.", LOG_LEVEL_ERROR); + } + $responseInterface = $responseInterface->withBody(new SerializableResponseStream($chunks)); + + break; + + case "user_add_role" : + case "user_delete_role": + + if (!isSet($httpVars["user_id"]) || !isSet($httpVars["role_id"]) || !UsersService::userExists($httpVars["user_id"]) || !RolesService::getRole($httpVars["role_id"])) { + throw new \Exception($mess["ajxp_conf.61"]); + } + if ($action == "user_add_role") { + $act = "add"; + $messId = "73"; + } else { + $act = "remove"; + $messId = "74"; + } + $this->updateUserRole($ctx->getUser(), InputFilter::sanitize($httpVars["user_id"], InputFilter::SANITIZE_EMAILCHARS), $httpVars["role_id"], $act); + $responseInterface = $responseInterface->withBody(new SerializableResponseStream(new UserMessage($mess["ajxp_conf.".$messId].$httpVars["user_id"]))); + + break; + + case "user_reorder_roles": + + if (!isSet($httpVars["user_id"]) || !UsersService::userExists($httpVars["user_id"]) || !isSet($httpVars["roles"])) { + throw new \Exception($mess["ajxp_conf.61"]); + } + $roles = json_decode($httpVars["roles"], true); + $userId = InputFilter::sanitize($httpVars["user_id"], InputFilter::SANITIZE_EMAILCHARS); + $user = UsersService::getUserById($userId); + if($ctx->hasUser() && !$ctx->getUser()->canAdministrate($user)){ + throw new \Exception("Cannot update user data for ".$userId); + } + + + // UPDATE ROLES + $currentRoles = array_filter(array_keys($user->getRoles()), function($rId){return strpos($rId, "AJXP_GRP_/")!==0 && strpos($rId, "AJXP_USR_/")!==0;}); + $newRoles = array_diff($roles, $currentRoles); + foreach($newRoles as $r) $user->addRole(RolesService::getRole($r)); + $removeRoles = array_diff($currentRoles, $roles); + foreach($removeRoles as $r) { + $user->removeRole($r); + } + // REORDER ROLES + $user->updateRolesOrder($roles); + $user->save("superuser"); + $loggedUser = $ctx->getUser(); + if ($loggedUser->getId() == $user->getId()) { + AuthService::updateSessionUser($user); + } + + $responseInterface = $responseInterface->withBody(new SerializableResponseStream(new UserMessage("Roles reordered for user ".$httpVars["user_id"]))); + break; + + case "users_bulk_update_roles": + + $data = json_decode($httpVars["json_data"], true); + $userIds = $data["users"]; + $rolesOperations = $data["roles"]; + foreach($userIds as $userId){ + $userId = InputFilter::sanitize($userId, InputFilter::SANITIZE_EMAILCHARS); + if(!UsersService::userExists($userId)) continue; + $userObject = UsersService::getUserById($userId, false); + if($ctx->hasUser() && !$ctx->getUser()->canAdministrate($userObject)) continue; + foreach($rolesOperations as $addOrRemove => $roles){ + if(!in_array($addOrRemove, array("add", "remove"))) { + continue; + } + foreach($roles as $roleId){ + if(strpos($roleId, "AJXP_USR_/") === 0 || strpos($roleId,"AJXP_GRP_/") === 0){ + continue; + } + $roleId = InputFilter::sanitize($roleId, InputFilter::SANITIZE_FILENAME); + if ($addOrRemove == "add") { + $roleObject = RolesService::getRole($roleId); + $userObject->addRole($roleObject); + } else { + $userObject->removeRole($roleId); + } + } + } + $userObject->save("superuser"); + $loggedUser = $ctx->getUser(); + if ($loggedUser->getId() == $userObject->getId()) { + AuthService::updateSessionUser($userObject); + } + } + + $responseInterface = $responseInterface->withBody(new SerializableResponseStream(new UserMessage("Successfully updated roles"))); + + break; + + case "user_update_role" : + + $selection = UserSelection::fromContext($ctx, $httpVars); + $files = $selection->getFiles(); + $detectedRoles = array(); + $roleId = null; + + if (isSet($httpVars["role_id"]) && isset($httpVars["update_role_action"])) { + $update = $httpVars["update_role_action"]; + $roleId = $httpVars["role_id"]; + if (RolesService::getRole($roleId) === false) { + throw new \Exception("Invalid role id"); + } + } + foreach ($files as $index => $file) { + $userId = basename($file); + if (isSet($update)) { + $userObject = $this->updateUserRole($ctx->getUser(), $userId, $roleId, $update); + } else { + try{ + $userObject = UsersService::getUserById($userId); + }catch(UserNotFoundException $u){ + continue; + } + if($ctx->hasUser() && !$ctx->getUser()->canAdministrate($userObject)){ + continue; + } + } + if ($userObject->hasParent()) { + unset($files[$index]); + continue; + } + $userRoles = $userObject->getRoles(); + foreach ($userRoles as $roleIndex => $bool) { + if(!isSet($detectedRoles[$roleIndex])) $detectedRoles[$roleIndex] = 0; + if($bool === true) $detectedRoles[$roleIndex] ++; + } + } + $count = count($files); + $buffer = ""; + + $buffer .= ""; + foreach ($detectedRoles as $roleId => $roleCount) { + if($roleCount < $count) continue; + $buffer .= ""; + } + $buffer .= ""; + $buffer .= ""; + foreach (RolesService::getRolesList(array(), !$this->listSpecialRoles) as $roleId => $roleObject) { + $buffer .= ""; + } + $buffer .= ""; + $buffer .= ""; + + $responseInterface = $responseInterface->withBody(new SerializableResponseStream(new XMLDocMessage($buffer))); + break; + + case "save_custom_user_params" : + + $userId = InputFilter::sanitize($httpVars["user_id"], InputFilter::SANITIZE_EMAILCHARS); + if ($userId == $loggedUser->getId()) { + $user = $loggedUser; + } else { + $user = UsersService::getUserById($userId); + } + if($ctx->hasUser() && !$ctx->getUser()->canAdministrate($user)){ + throw new \Exception("Cannot update user with id ".$userId); + } + + $custom = $user->getPref("CUSTOM_PARAMS"); + if(!is_array($custom)) $custom = array(); + + $options = $custom; + $newCtx = new Context($userId, $ctx->getRepositoryId()); + $this->parseParameters($newCtx, $httpVars, $options, false, $custom); + $custom = $options; + $user->setPref("CUSTOM_PARAMS", $custom); + $user->save(); + + if ($loggedUser->getId() == $user->getId()) { + AuthService::updateSessionUser($user); + } + + $responseInterface = $responseInterface->withBody(new SerializableResponseStream(new UserMessage($mess["ajxp_conf.47"].$httpVars["user_id"]))); + + break; + + case "save_repository_user_params" : + + $userId = InputFilter::sanitize($httpVars["user_id"], InputFilter::SANITIZE_EMAILCHARS); + if ($userId == $loggedUser->getId()) { + $user = $loggedUser; + } else { + $user = UsersService::getUserById($userId); + } + if($ctx->hasUser() && !$ctx->getUser()->canAdministrate($user)){ + throw new \Exception("Cannot update user with id ".$userId); + } + + $wallet = $user->getPref("AJXP_WALLET"); + if(!is_array($wallet)) $wallet = array(); + $repoID = $httpVars["repository_id"]; + if (!array_key_exists($repoID, $wallet)) { + $wallet[$repoID] = array(); + } + $options = $wallet[$repoID]; + $existing = $options; + $newCtx = new Context($userId, $ctx->getRepositoryId()); + $this->parseParameters($newCtx, $httpVars, $options, false, $existing); + $wallet[$repoID] = $options; + $user->setPref("AJXP_WALLET", $wallet); + $user->save(); + + if ($loggedUser->getId() == $user->getId()) { + AuthService::updateSessionUser($user); + } + + $responseInterface = $responseInterface->withBody(new SerializableResponseStream(new UserMessage($mess["ajxp_conf.47"].$httpVars["user_id"]))); + + break; + + case "update_user_pwd" : + + if (!isSet($httpVars["user_id"]) || !isSet($httpVars["user_pwd"]) || !UsersService::userExists($httpVars["user_id"]) || trim($httpVars["user_pwd"]) == "") { + + throw new PydioException($mess["ajxp_conf.61"]); + + } + $userId = InputFilter::sanitize($httpVars["user_id"], InputFilter::SANITIZE_EMAILCHARS); + $user = UsersService::getUserById($userId); + if($ctx->hasUser() && !$ctx->getUser()->canAdministrate($user)){ + throw new PydioException("Cannot update user data for ".$userId); + } + $res = UsersService::updatePassword($userId, $httpVars["user_pwd"]); + if($res !== true){ + throw new PydioException($mess["ajxp_conf.49"].": $res"); + } + $responseInterface = $responseInterface->withBody(new SerializableResponseStream(new UserMessage($mess["ajxp_conf.48"].$userId))); + + break; + + case "save_user_preference": + + if (!isSet($httpVars["user_id"]) || !UsersService::userExists($httpVars["user_id"])) { + throw new \Exception($mess["ajxp_conf.61"]); + } + $userId = InputFilter::sanitize($httpVars["user_id"], InputFilter::SANITIZE_EMAILCHARS); + if ($userId == $loggedUser->getId()) { + $userObject = $loggedUser; + } else { + $userObject = UsersService::getUserById($userId); + } + if($ctx->hasUser() && !$ctx->getUser()->canAdministrate($userObject)){ + throw new \Exception("Cannot update user data for ".$userId); + } + + $i = 0; + while (isSet($httpVars["pref_name_".$i]) && isSet($httpVars["pref_value_".$i])) { + $prefName = InputFilter::sanitize($httpVars["pref_name_" . $i], InputFilter::SANITIZE_ALPHANUM); + $prefValue = InputFilter::sanitize(InputFilter::magicDequote($httpVars["pref_value_" . $i])); + if($prefName == "password") continue; + if ($prefName != "pending_folder" && $userObject == null) { + $i++; + continue; + } + $userObject->setPref($prefName, $prefValue); + $userObject->save("user"); + $i++; + } + + $responseInterface = $responseInterface->withBody(new SerializableResponseStream(new UserMessage("Succesfully saved user preference"))); + + break; + + case "cli_update_user_list": + // Action for updating all Pydio's user from ldap in CLI mode + if((php_sapi_name() == "cli")){ + // TODO : UPGRADE THIS TO NEW CLI FORMAT + $progressBar = new ProgressBarCLI(); + $countCallback = array($progressBar, "init"); + $loopCallback = array($progressBar, "update"); + $bGroup = "/"; + if($ctx->hasUser()) $bGroup = $ctx->getUser()->getGroupPath(); + // Todo: switch to UsersService::browserUserGroupWithCallback() + UsersService::listUsers($bGroup, null, -1, -1, true, true, $countCallback, $loopCallback); + } + + break; + + default: + break; + + } + + return $responseInterface; + } + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @return ResponseInterface + * @throws PydioException + */ + public function delete(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface){ + + $mess = LocaleService::getMessages(); + $httpVars = $requestInterface->getParsedBody(); + /** @var ContextInterface $ctx */ + $ctx = $requestInterface->getAttribute("ctx"); + + if (isSet($httpVars["group"])) { + + $groupPath = $httpVars["group"]; + $groupPath = preg_replace('/^\/data\/users/', '', $groupPath); + $basePath = PathUtils::forwardSlashDirname($groupPath); + $basePath = ($ctx->hasUser() ? $ctx->getUser()->getRealGroupPath($basePath) : $basePath); + $gName = basename($groupPath); + UsersService::deleteGroup($basePath, $gName); + + $resultMessage = $mess["ajxp_conf.128"]; + + } else { + if(empty($httpVars["user_id"]) || UsersService::isReservedUserId($httpVars["user_id"]) + || $ctx->getUser()->getId() === $httpVars["user_id"]) { + throw new PydioException($mess["ajxp_conf.61"]); + } + UsersService::deleteUser($httpVars["user_id"]); + $resultMessage = $mess["ajxp_conf.60"]; + } + + $message = new UserMessage($resultMessage); + $reload = new ReloadMessage(); + return $responseInterface->withBody(new SerializableResponseStream([$message, $reload])); + + } + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @return ResponseInterface + */ + public function search(ServerRequestInterface $requestInterface, ResponseInterface $responseInterface){ + + $httpVars = $requestInterface->getParsedBody(); + $ctx = $requestInterface->getAttribute("ctx"); + $nodesList = new NodesList(); + + if(!InputFilter::decodeSecureMagic($httpVars["dir"]) == "/data/users") { + return $responseInterface->withBody(new SerializableResponseStream($nodesList)); + } + + $query = InputFilter::decodeSecureMagic($httpVars["query"]); + $limit = $offset = -1; + if(isSet($httpVars["limit"])) $limit = intval(InputFilter::sanitize($httpVars["limit"], InputFilter::SANITIZE_ALPHANUM)); + if(isSet($httpVars["offset"])) $offset = intval(InputFilter::sanitize($httpVars["offset"], InputFilter::SANITIZE_ALPHANUM)); + $this->recursiveSearchGroups($ctx, $nodesList, "/", $query, $offset, $limit); + + return $responseInterface->withBody(new SerializableResponseStream($nodesList)); + + } + + /** + * @param ServerRequestInterface $requestInterface Full set of query parameters + * @param string $rootPath Path to prepend to the resulting nodes + * @param string $relativePath Specific path part for this function + * @param string $paginationHash Number added to url#2 for pagination purpose. + * @param string $findNodePosition Path to a given node to try to find it + * @param string $aliasedDir Aliased path used for alternative url + * @return NodesList A populated NodesList object, eventually recursive. + */ + public function listNodes(ServerRequestInterface $requestInterface, $rootPath, $relativePath, $paginationHash = null, $findNodePosition = null, $aliasedDir = null) + { + $fullBasePath = "/" . $rootPath . "/" . $relativePath; + $USER_PER_PAGE = 50; + $messages = LocaleService::getMessages(); + $nodesList = new NodesList(); + $parentNode = new AJXP_Node($fullBasePath, [ + "remote_indexation" => "admin_search_users", + "is_file" => false, + "text" => "" + ]); + if(isSet($requestInterface->getParsedBody()["format"])){ + $format = $requestInterface->getParsedBody()["format"]; + }else if($requestInterface->getAttribute("api") === "v2"){ + $format = "json"; + }else{ + $format = "xml"; + } + + + $nodesList->setParentNode($parentNode); + + $baseGroup = ($relativePath === "users" ? "/" : substr($relativePath, strlen("users"))); + if($this->context->hasUser()){ + $baseGroup = $this->context->getUser()->getRealGroupPath($baseGroup); + } + + if ($findNodePosition != null && $paginationHash == null) { + + $findNodePositionPath = $fullBasePath."/".$findNodePosition; + $position = UsersService::findUserPage($baseGroup, $findNodePosition, $USER_PER_PAGE); + + if($position != -1){ + $nodesList->addBranch(new AJXP_Node($findNodePositionPath, [ + "text" => $findNodePosition, + "page_position" => $position + ])); + }else{ + // Loop on each page to find the correct page. + $count = UsersService::authCountUsers($baseGroup); + $pages = ceil($count / $USER_PER_PAGE); + for ($i = 0; $i < $pages ; $i ++) { + + $newList = $this->listNodes($requestInterface, $rootPath, $relativePath, $i+1, true, $findNodePosition); + $foundNode = $newList->findChildByPath($findNodePositionPath); + if ($foundNode !== null) { + $foundNode->mergeMetadata(["page_position" => $i+1]); + $nodesList->addBranch($foundNode); + break; + } + } + } + return $nodesList; + + } + + $nodesList->initColumnsData("filelist", "list", "ajxp_conf.users"); + $nodesList->appendColumn("ajxp_conf.6", "ajxp_label", "String", "40%"); + $nodesList->appendColumn("ajxp_conf.102", "object_id", "String", "10%"); + if(UsersService::driverSupportsAuthSchemes()){ + $nodesList->appendColumn("ajxp_conf.115", "auth_scheme", "String", "5%"); + $nodesList->appendColumn("ajxp_conf.7", "isAdmin", "String", "5%"); + }else{ + $nodesList->appendColumn("ajxp_conf.7", "isAdmin", "String", "10%"); + } + $nodesList->appendColumn("ajxp_conf.70", "ajxp_roles", "String", "15%"); + $nodesList->appendColumn("ajxp_conf.62", "rights_summary", "String", "15%"); + + if(!UsersService::usersEnabled()) return $nodesList; + + if(empty($paginationHash)) $paginationHash = 1; + $count = UsersService::authCountUsers($baseGroup, "", null, null, false); + if (UsersService::authSupportsPagination() && $count >= $USER_PER_PAGE) { + + $offset = ($paginationHash - 1) * $USER_PER_PAGE; + $nodesList->setPaginationData($count, $paginationHash, ceil($count / $USER_PER_PAGE)); + $users = UsersService::listUsers($baseGroup, "", $offset, $USER_PER_PAGE, true, false); + if ($paginationHash == 1) { + $groups = UsersService::listChildrenGroups($baseGroup); + } else { + $groups = array(); + } + + } else { + + $users = UsersService::listUsers($baseGroup, "", -1, -1, true, false); + $groups = UsersService::listChildrenGroups($baseGroup); + + } + + if($format === "json" && $baseGroup !== "/"){ + + $siblingGroups = UsersService::listChildrenGroups(dirname($baseGroup)); + $gKey = "/".basename($baseGroup); + $baseGroupLabel = isset($siblingGroups[$gKey]) ? $siblingGroups[$gKey] : null; + if(!count($users) && !count($groups) && $baseGroupLabel === null){ + // Group does not seem to exist. Maybe we are getting info about a user here + try{ + $testUser = UsersService::getUserById(basename($baseGroup)); + if($testUser->getGroupPath() === dirname($baseGroup)){ + $userMeta = $this->serializeUserMetadata($testUser, $format, $messages); + $nodesList->setParentNode(new AJXP_Node($testUser->getId(), $userMeta)); + return $nodesList; + } + } catch (UserNotFoundException $unf){} + } else if($baseGroupLabel !== null) { + $parentNode = new AJXP_Node($baseGroup, $this->serializeGroupMetadata($baseGroup, $baseGroupLabel)); + $nodesList->setParentNode($parentNode); + } + + } + + + // Append Root Group + if($this->pluginName === "ajxp_admin" && $baseGroup == "/" && $paginationHash == 1 && !$this->currentUserIsGroupAdmin()){ + + $topMeta = $this->serializeGroupMetadata("/", $messages["ajxp_conf.151"]); + $topMeta["icon_class"] = "icon-home"; + $rootGroupNode = new AJXP_Node($fullBasePath ."/", $topMeta); + $nodesList->addBranch($rootGroupNode); + + } + + // LIST GROUPS + foreach ($groups as $groupId => $groupLabel) { + + $nodeKey = $fullBasePath ."/".ltrim($groupId,"/"); + $meta = $this->serializeGroupMetadata($groupId, $groupLabel); + $this->appendBookmarkMeta($nodeKey, $meta); + if($requestInterface->getAttribute("api") === "v2"){ + $meta["group_role_id"] = "/AJXP_GRP_".rtrim($baseGroup, "/")."/".ltrim($groupId, "/"); + $nodeKey = InputFilter::securePath("/".$baseGroup.$groupId); + } + $nodesList->addBranch(new AJXP_Node($nodeKey, $meta)); + } + + // LIST USERS + $userArray = array(); + $logger = Logger::getInstance(); + if(method_exists($logger, "usersLastConnection")){ + $allUserIds = array(); + } + foreach ($users as $userObject) { + $label = $userObject->getId(); + if(isSet($allUserIds)) $allUserIds[] = $label; + if ($userObject->hasParent()) { + $label = $userObject->getParent()."000".$label; + }else{ + $children = ConfService::getConfStorageImpl()->getUserChildren($label); + foreach($children as $addChild){ + $userArray[$label."000".$addChild->getId()] = $addChild; + } + } + $userArray[$label] = $userObject; + } + if(isSet($allUserIds) && count($allUserIds)){ + $connections = $logger->usersLastConnection($allUserIds); + }else{ + $connections = []; + } + + ksort($userArray); + + /** @var UserInterface $userObject */ + foreach ($userArray as $userObject) { + + $userId = $userObject->getId(); + $bmKey = $fullBasePath. "/" .$userId; + $nodeKey = $format === "json" ? $userId : $bmKey; + $meta = $this->serializeUserMetadata($userObject, $format, $messages, $connections); + $this->appendBookmarkMeta($bmKey, $meta); + $nodesList->addBranch(new AJXP_Node($nodeKey, $meta)); + + } + return $nodesList; + } + + /** + * @param string $groupId + * @param string $groupLabel + * @return array + */ + protected function serializeGroupMetadata($groupId, $groupLabel){ + return [ + "icon" => "users-folder.png", + "icon_class" => "icon-folder-close", + "ajxp_mime" => "group", + "object_id" => $groupId, + "text" => $groupLabel, + "is_file" => false + ]; + } + + /** + * @param UserInterface $userObject + * @param string $format + * @param array $messages + * @param array + * @return array + */ + protected function serializeUserMetadata($userObject, $format, $messages, $connections = []){ + + $repos = ConfService::getConfStorageImpl()->listRepositories($userObject); + $isAdmin = $userObject->isAdmin(); + $userId = $userObject->getId(); + $icon = "user".($userId=="guest"?"_guest":($isAdmin?"_admin":"")); + $iconClass = "icon-user"; + if ($userObject->hasParent()) { + $icon = "user_child"; + $iconClass = "icon-angle-right"; + } + if ($isAdmin) { + $rightsString = $messages["ajxp_conf.63"]; + } else { + $r = array(); + foreach ($repos as $repoId => $repository) { + if($repository->getAccessType() == "ajxp_shared") continue; + if(!$userObject->canRead($repoId) && !$userObject->canWrite($repoId)) continue; + $rs = ($userObject->canRead($repoId) ? "r" : ""); + $rs .= ($userObject->canWrite($repoId) ? "w" : ""); + $r[] = $repository->getDisplay()." (".$rs.")"; + } + $rightsString = implode(", ", $r); + } + $nodeLabel = UsersService::getUserPersonalParameter("USER_DISPLAY_NAME", $userObject, "core.conf", $userId); + $scheme = UsersService::getAuthScheme($userId); + $roles = array_filter(array_keys($userObject->getRoles()), array($this, "filterReservedRoles")); + $mergedRole = $userObject->getMergedRole()->getDataArray(true); + $meta = []; + if($format !== "json"){ + $mergedRole = json_encode($mergedRole); + $currentRoles = implode(", ", $roles); + }else{ + $currentRoles = $roles; + $meta["personal_role_id"] = "/AJXP_USR_/".$userId; + $meta["lock"] = $userObject->getLock(); + $meta["profile"] = $userObject->getProfile(); + } + $meta = array_merge($meta, [ + "text" => $nodeLabel, + "is_file" => true, + "isAdmin" => ($format === "json" ? $isAdmin : $messages[($isAdmin?"ajxp_conf.14":"ajxp_conf.15")]), + "icon" => $icon.".png", + "icon_class" => $iconClass, + "object_id" => $userId, + "auth_scheme" => ($scheme != null? $scheme : ""), + "rights_summary" => $rightsString, + "ajxp_roles" => $currentRoles, + "ajxp_mime" => "user".(($userId!="guest"&&$userId!=$this->context->getUser()->getId())?"_editable":""), + "json_merged_role" => $mergedRole + ]); + if($userObject->hasParent()) { + $meta["shared_user"] = "true"; + } + if(isSet($connections) && isSet($connections[$userObject->getId()]) && !empty($connections[$userObject->getId()])) { + $meta["last_connection"] = strtotime($connections[$userObject->getId()]); + $meta["last_connection_readable"] = StatHelper::relativeDate($meta["last_connection"], $messages); + } + + return $meta; + + } + + /** + * Do not display AJXP_GRP_/ and AJXP_USR_/ roles if not in server debug mode + * @param $key + * @return bool + */ + protected function filterReservedRoles($key){ + return (strpos($key, "AJXP_GRP_/") === FALSE && strpos($key, "AJXP_USR_/") === FALSE); + } + + /** + * @param UserInterface $ctxUser + * @param $userId + * @param $roleId + * @param $addOrRemove + * @param bool $updateSubUsers + * @return UserInterface + * @throws UserNotFoundException + * @throws PydioException + */ + protected function updateUserRole(UserInterface $ctxUser, $userId, $roleId, $addOrRemove, $updateSubUsers = false) + { + $user = UsersService::getUserById($userId); + if(!empty($ctxUser) && !$ctxUser->canAdministrate($user)){ + throw new PydioException("Cannot update user data for ".$userId); + } + if ($addOrRemove == "add") { + $roleObject = RolesService::getRole($roleId); + $user->addRole($roleObject); + } else { + $user->removeRole($roleId); + } + $user->save("superuser"); + if ($ctxUser->getId() == $user->getId()) { + AuthService::updateSessionUser($user); + } + return $user; + + } + + /** + * @param ContextInterface $ctx + * @param NodesList $nodesList + * @param string $baseGroup + * @param string $term + * @param int $offset + * @param int $limit + */ + public function recursiveSearchGroups(ContextInterface $ctx, &$nodesList, $baseGroup, $term, $offset=-1, $limit=-1) + { + if($ctx->hasUser()){ + $baseGroup = $ctx->getUser()->getRealGroupPath($baseGroup); + } + + $groups = UsersService::listChildrenGroups($baseGroup); + foreach ($groups as $groupId => $groupLabel) { + + if (preg_match("/$term/i", $groupLabel) == TRUE ) { + $trimmedG = trim($baseGroup, "/"); + if(!empty($trimmedG)) $trimmedG .= "/"; + $nodeKey = "/data/users/".$trimmedG.ltrim($groupId,"/"); + $meta = array( + "icon" => "users-folder.png", + "text" => $groupLabel, + "is_file" => false, + "ajxp_mime" => "group_editable" + ); + $this->appendBookmarkMeta($nodeKey, $meta); + $nodesList->addBranch(new AJXP_Node($nodeKey, $meta)); + } + $this->recursiveSearchGroups($ctx, $nodesList, rtrim($baseGroup, "/")."/".ltrim($groupId, "/"), $term); + + } + + $users = UsersService::listUsers($baseGroup, $term, $offset, $limit); + foreach ($users as $userId => $userObject) { + $gPath = $userObject->getGroupPath(); + $realGroup = $ctx->getUser()->getRealGroupPath($ctx->getUser()->getGroupPath()); + if(strlen($realGroup) > 1 && strpos($gPath, $realGroup) === 0){ + $gPath = substr($gPath, strlen($realGroup)); + } + $trimmedG = trim($gPath, "/"); + if(!empty($trimmedG)) $trimmedG .= "/"; + + $userDisplayName = UsersService::getUserPersonalParameter("USER_DISPLAY_NAME", $userObject, "core.conf", $userId); + $nodeKey = "/data/users/".$trimmedG.$userId; + $meta = array( + "icon" => "user.png", + "text" => $userDisplayName, + "is_file" => true, + "ajxp_mime" => "user_editable" + ); + $this->appendBookmarkMeta($nodeKey, $meta); + $nodesList->addBranch(new AJXP_Node($nodeKey, $meta)); + + } + + } + + +} \ No newline at end of file diff --git a/core/src/plugins/access.ajxp_home/HomePagePlugin.php b/core/src/plugins/access.ajxp_home/HomePagePlugin.php new file mode 100644 index 0000000000..17949bf32e --- /dev/null +++ b/core/src/plugins/access.ajxp_home/HomePagePlugin.php @@ -0,0 +1,71 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + * + */ +namespace Pydio\Access\Driver\DataProvider; +use DOMXPath; +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Core\Model\ContextInterface; + +defined('AJXP_EXEC') or die( 'Access not allowed'); +/** + * @package AjaXplorer_Plugins + * @subpackage Access + * @class userHome + * Plugin to access the shared elements of the current user + */ +class HomePagePlugin extends AbstractAccessDriver +{ + + /** + * @param ContextInterface $ctx + * @param \DOMNode $contribNode + */ + public function parseSpecificContributions(ContextInterface $ctx, \DOMNode &$contribNode){ + parent::parseSpecificContributions($ctx, $contribNode); + if($contribNode->nodeName == "client_configs"){ + $actionXpath=new DOMXPath($contribNode->ownerDocument); + $gettingStartedList = $actionXpath->query('template[@name="tutorial_pane"]', $contribNode); + if(!$gettingStartedList->length) return ; + if($this->getContextualOption($ctx, "ENABLE_GETTING_STARTED") === false){ + $contribNode->removeChild($gettingStartedList->item(0)); + }else{ + $cdata = $gettingStartedList->item(0)->firstChild; + $keys = array("URL_APP_IOSAPPSTORE", "URL_APP_ANDROID", "URL_APP_SYNC_WIN", "URL_APP_SYNC_MAC"); + $values = array(); + foreach($keys as $k) $values[] = $this->getContextualOption($ctx, $k); + $newData = str_replace($keys, $values, $cdata->nodeValue); + $newCData = $contribNode->ownerDocument->createCDATASection($newData); + $gettingStartedList->item(0)->appendChild($newCData); + $gettingStartedList->item(0)->replaceChild($newCData, $cdata); + } + }else if($contribNode->nodeName == "actions" && $this->getContextualOption($ctx, "DISPLAY_SERVER_QRCODE_ACTION") === false){ + $actionXpath = new DOMXPath($contribNode->ownerDocument); + $actionList = $actionXpath->query('action[@name="display-server-qrcode"]', $contribNode); + $contribNode->removeChild($actionList->item(0)); + } + } + + /** + * @param ContextInterface $ctx + */ + protected function initRepository(ContextInterface $ctx){ + } +} diff --git a/core/src/plugins/access.ajxp_home/class.HomePagePlugin.php b/core/src/plugins/access.ajxp_home/class.HomePagePlugin.php deleted file mode 100644 index 12e420e6d7..0000000000 --- a/core/src/plugins/access.ajxp_home/class.HomePagePlugin.php +++ /dev/null @@ -1,62 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - * - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -/** - * @package AjaXplorer_Plugins - * @subpackage Access - * @class userHome - * AJXP_Plugin to access the shared elements of the current user - */ -class HomePagePlugin extends AbstractAccessDriver -{ - - public function initRepository() - { - //require_once AJXP_INSTALL_PATH."/".AJXP_PLUGINS_FOLDER."/action.share/class.ShareCenter.php"; - } - - public function parseSpecificContributions(&$contribNode){ - parent::parseSpecificContributions($contribNode); - if($contribNode->nodeName == "client_configs"){ - $actionXpath=new DOMXPath($contribNode->ownerDocument); - $gettingStartedList = $actionXpath->query('template[@name="tutorial_pane"]', $contribNode); - if(!$gettingStartedList->length) return ; - if($this->getFilteredOption("ENABLE_GETTING_STARTED") === false){ - $contribNode->removeChild($gettingStartedList->item(0)); - }else{ - $cdata = $gettingStartedList->item(0)->firstChild; - $keys = array("URL_APP_IOSAPPSTORE", "URL_APP_ANDROID", "URL_APP_SYNC_WIN", "URL_APP_SYNC_MAC"); - $values = array(); - foreach($keys as $k) $values[] = $this->getFilteredOption($k); - $newData = str_replace($keys, $values, $cdata->nodeValue); - $newCData = $contribNode->ownerDocument->createCDATASection($newData); - $gettingStartedList->item(0)->appendChild($newCData); - $gettingStartedList->item(0)->replaceChild($newCData, $cdata); - } - }else if($contribNode->nodeName == "actions" && $this->getFilteredOption("DISPLAY_SERVER_QRCODE_ACTION") === false){ - $actionXpath = new DOMXPath($contribNode->ownerDocument); - $actionList = $actionXpath->query('action[@name="display-server-qrcode"]', $contribNode); - $contribNode->removeChild($actionList->item(0)); - } - } - -} diff --git a/core/src/plugins/access.ajxp_home/manifest.xml b/core/src/plugins/access.ajxp_home/manifest.xml index 8624cb0f40..291fd789c1 100644 --- a/core/src/plugins/access.ajxp_home/manifest.xml +++ b/core/src/plugins/access.ajxp_home/manifest.xml @@ -13,6 +13,8 @@ description="CONF_MESSAGE[Open workspace by simple click instead of double click.]" default="true" expose="true"/> + - + @@ -42,92 +44,10 @@ -
    - - ]]> -
    - - ]]> - -
    -
    -
    -
    -
      -
      -
      - - -
      -
      -
      -
      -
      -
      -
      -
      - ]]>
      ]]>
      - + diff --git a/core/src/plugins/access.ajxp_home/package.json b/core/src/plugins/access.ajxp_home/package.json index 3b93f080dd..ef7ce7498e 100644 --- a/core/src/plugins/access.ajxp_home/package.json +++ b/core/src/plugins/access.ajxp_home/package.json @@ -8,7 +8,7 @@ "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Abstrium SAS", - "license": "proprietary", + "license": "AGPL", "devDependencies": { "grunt": "~0.4.5", "grunt-babel": "~5.0.3", diff --git a/core/src/plugins/access.ajxp_home/res/home.css b/core/src/plugins/access.ajxp_home/res/home.css index 5fc8e6683a..3ba8724c0d 100644 --- a/core/src/plugins/access.ajxp_home/res/home.css +++ b/core/src/plugins/access.ajxp_home/res/home.css @@ -97,7 +97,7 @@ } #home_account_pane #home_left_bar #workspaces_center .workspaces .workspace-entry .workspace-label { padding: 0; - font-weight: 500; + font-weight: 400; font-size: 18px; margin: 0; line-height: 26px; @@ -200,7 +200,7 @@ } #home_account_pane #home_center_panel div#logo_div { transition: all 550ms cubic-bezier(0.23, 1, 0.32, 1) 0ms; - margin-top: 20%; + margin-top: 17%; position: relative; } #home_account_pane #home_center_panel.legend_visible div#logo_div { @@ -647,6 +647,96 @@ div#tutorial_dl_apps_pane div#dl_pydio_mac div { div#tutorial_dl_apps_pane div#dl_pydio_android a.icon-android { top: 30px; } +div.home_search { + text-align: center; + position: relative; + top: 120px; + background-color: transparent; + width: 710px; + margin: 0 auto; + height: 40px; + overflow: visible; +} +div.home_search div.react-autosuggest { + position: absolute; +} +div.home_search div.react-autosuggest input.react-autosuggest__input { + width: 340px; + font-size: 16px; + padding: 8px 0; + position: relative; + border-width: 0 0 1px 0; + border-color: white; + border-radius: 0; + background-color: transparent; + color: white; +} +div.home_search div.react-autosuggest input.react-autosuggest__input::-webkit-input-placeholder { + color: rgba(255, 255, 255, 0.73); +} +div.home_search div.react-autosuggest input.react-autosuggest__input:-moz-placeholder { + /* Firefox 18- */ + color: rgba(255, 255, 255, 0.73); +} +div.home_search div.react-autosuggest input.react-autosuggest__input::-moz-placeholder { + /* Firefox 19+ */ + color: rgba(255, 255, 255, 0.73); +} +div.home_search div.react-autosuggest input.react-autosuggest__input:-ms-input-placeholder { + color: rgba(255, 255, 255, 0.73); +} +div.home_search div.react-autosuggest .react-autosuggest__suggestions { + width: 340px; + background-color: rgba(255, 255, 255, 0.98); + max-height: 235px; +} +div.home_search div.react-autosuggest .react-autosuggest__suggestions li.react-autosuggest__suggestion { + text-align: left; +} +div.home_search div.react-autosuggest .react-autosuggest__suggestions li.react-autosuggest__suggestion span.nodeSuggestion { + display: block; + padding: 16px 8px; + border-bottom: 1px solid #e0e0e0; + font-size: 14px; + color: rgba(0, 0, 0, 0.93); + cursor: pointer; +} +div.home_search div.react-autosuggest .react-autosuggest__suggestions li.react-autosuggest__suggestion span.nodeSuggestion span.highlight { + display: inline-block; + background-color: rgba(255, 255, 0, 0.26); +} +div.home_search div.react-autosuggest .react-autosuggest__suggestions li.react-autosuggest__suggestion span.nodeSuggestion span.icon { + font-size: 16px; + display: inline-block; + margin: 0 12px 0 5px; + color: rgba(0, 0, 0, 0.73); + text-align: center; + width: 25px; +} +div.home_search div.react-autosuggest .react-autosuggest__suggestions li.react-autosuggest__suggestion span.groupHeader { + display: block; + padding: 8px 8px; + border-bottom: 0px; + font-size: 13px; + color: white; + cursor: pointer; + background-color: #009688; +} +div.home_search div.react-autosuggest .react-autosuggest__suggestions li.react-autosuggest__suggestion span.groupHeader span.openicon { + display: none; + float: right; +} +div.home_search div.react-autosuggest .react-autosuggest__suggestions li.react-autosuggest__suggestion span.groupHeader:hover span.openicon { + margin-top: 3px; + display: inline-block; +} +div.home_search span.suggest-search { + font-size: 16px; + position: absolute; + color: white; + right: 380px; + top: 10px; +} .form-qrcode_dialog_form { min-width: 296px; } @@ -668,7 +758,10 @@ div#tutorial_dl_apps_pane div#dl_pydio_android a.icon-android { width: 100px; } } -@media only screen and (max-width: 800px) { +@media only screen and (max-width: 800px) and (min-width: 420px) { + #logo_div { + margin-top: 25% !important; + } #logo_div img { width: 210px; } @@ -682,14 +775,26 @@ div#tutorial_dl_apps_pane div#dl_pydio_android a.icon-android { margin-right: 3%; margin-left: 3%; } - #dl_pydio_cont { - width: 400px !important; + div#dl_pydio_cont { + width: 650px !important; + } + div.home_search { + width: 650px !important; + } + div.home_search span.suggest-search { + right: 320px; } #dl_pydio_for { display: none; } - #home_left_bar { - width: 380px; + #home_account_pane #home_left_bar { + width: 300px; + } + #home_account_pane #home_left_bar .workspace-entry { + padding: 8px 10px 8px 23px !important; + } + #home_account_pane #home_left_bar .workspace-entry span.workspace-badge { + display: none !important; } #welcome { font-size: 1em; @@ -703,9 +808,9 @@ div#tutorial_dl_apps_pane div#dl_pydio_android a.icon-android { padding: 0 30px; } } -@media only screen and (max-width: 400px) { +@media only screen and (max-width: 420px) { #home_left_bar { - width: 100%; + width: 100% !important; position: absolute; top: 0; height: 100%; diff --git a/core/src/plugins/access.ajxp_home/res/home.less b/core/src/plugins/access.ajxp_home/res/home.less index e3e9b8956d..b65f5a2fa9 100644 --- a/core/src/plugins/access.ajxp_home/res/home.less +++ b/core/src/plugins/access.ajxp_home/res/home.less @@ -101,7 +101,7 @@ .workspace-label{ padding: 0; - font-weight: 500; + font-weight: 400; font-size: 18px; margin: 0; line-height: 26px; @@ -216,7 +216,7 @@ height: 100%; div#logo_div{ transition: @bezier_transition; - margin-top:20%; + margin-top:17%; position: relative; } &.legend_visible div#logo_div{ @@ -615,7 +615,8 @@ div#tutorial_dl_apps_pane{ } div#dl_pydio_cont{ - width: 750px; margin: 0 auto; + width: 750px; + margin: 0 auto; background-color: transparent; } div#dl_pydio_for{ @@ -679,6 +680,99 @@ div#tutorial_dl_apps_pane{ } +div.home_search{ + text-align: center; + position: relative; + top: 120px; + background-color: transparent; + width: 710px; + margin: 0 auto; + height: 40px; + overflow: visible; + + div.react-autosuggest{ + position: absolute; + input.react-autosuggest__input { + width: 340px; + font-size:16px; + padding: 8px 0; + position: relative; + border-width:0 0 1px 0; + border-color: white; + border-radius: 0; + background-color: transparent; + color: white; + @placeholder: rgba(255, 255, 255, 0.73); + &::-webkit-input-placeholder { + color: @placeholder; + } + &:-moz-placeholder { /* Firefox 18- */ + color: @placeholder; + } + &::-moz-placeholder { /* Firefox 19+ */ + color: @placeholder; + } + &:-ms-input-placeholder { + color: @placeholder; + } + } + .react-autosuggest__suggestions{ + width: 340px; + background-color: rgba(255, 255, 255, 0.98); + max-height: 235px; + li.react-autosuggest__suggestion{ + text-align: left; + span.nodeSuggestion{ + display: block; + padding: 16px 8px; + border-bottom: 1px solid #e0e0e0; + font-size:14px; + color: rgba(0,0,0,0.93); + cursor: pointer; + span.highlight{ + display: inline-block; + background-color: rgba(255, 255, 0, 0.26); + } + span.icon{ + font-size:16px; + display: inline-block; + margin: 0 12px 0 5px; + color: rgba(0,0,0,0.73); + text-align: center; + width: 25px; + } + } + span.groupHeader{ + display: block;; + padding: 8px 8px; + border-bottom: 0px; + font-size:13px; + color: white; + cursor: pointer; + background-color: #009688; + span.openicon{ + display: none; + float: right; + } + &:hover{ + span.openicon{ + margin-top: 3px; + display: inline-block; + } + } + } + } + } + } + span.suggest-search{ + font-size:16px; + position: absolute; + color: white; + right: 380px; + top: 10px + } + +} .form-qrcode_dialog_form{ min-width: 296px; @@ -705,10 +799,13 @@ and (max-width : 980px){ } @media only screen -and (max-width : 800px){ +and (max-width : 800px) and (min-width: 420px){ - #logo_div img{ - width: 210px; + #logo_div{ + margin-top: 25% !important; + img{ + width: 210px; + } } .tutorial_legend{ @@ -722,15 +819,30 @@ and (max-width : 800px){ margin-right: 3%; margin-left: 3%; } - #dl_pydio_cont{ - width: 400px !important; + div#dl_pydio_cont { + width: 650px !important; + } + div.home_search{ + width: 650px !important; + span.suggest-search{ + right:320px; + } } + #dl_pydio_for{ display: none; } - #home_left_bar{ - width: 380px; + #home_account_pane{ + #home_left_bar{ + width: 300px; + .workspace-entry{ + padding: 8px 10px 8px 23px !important; + span.workspace-badge{ + display: none !important; + } + } + } } #welcome{ @@ -750,9 +862,9 @@ and (max-width : 800px){ } @media only screen -and (max-width : 400px){ +and (max-width : 420px){ #home_left_bar{ - width: 100%; + width: 100% !important; position: absolute; top:0; height: 100%; diff --git a/core/src/plugins/access.ajxp_home/res/i18n/ca.php b/core/src/plugins/access.ajxp_home/res/i18n/ca.php index 874d5314ee..cfb180ec3b 100644 --- a/core/src/plugins/access.ajxp_home/res/i18n/ca.php +++ b/core/src/plugins/access.ajxp_home/res/i18n/ca.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ // catalan translation: Salva Gómez , 2015 @@ -90,5 +90,7 @@ "72" => "Server QRCode", "73" => "Easily connect your mobile applications", "74" => "Scan this QRCode with your mobile application to easily configure the connection", - + "75"=> "Search all files...", + "76" => "Access the %1 through the top-right menu", + "77" => "admin dashboard", ); \ No newline at end of file diff --git a/core/src/plugins/access.ajxp_home/res/i18n/conf/de.php b/core/src/plugins/access.ajxp_home/res/i18n/conf/de.php index 83cf385436..f66fa253b1 100644 --- a/core/src/plugins/access.ajxp_home/res/i18n/conf/de.php +++ b/core/src/plugins/access.ajxp_home/res/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Welcome Page"=> "Willkommen-Seite", @@ -26,11 +26,15 @@ "Enable Getting Started" => "'Erste Schritte' anzeigen", "Enable addition tabulation to access getting started videos" => "Link auf Videos, welche die ersten Schritte mit Pydio zeigen", "iOS App URL" => "URL zur iOS-App", -"URL of the iOS application" => "URL zur iOS-App", +"URL of the iOS application" => "URL zur iOS-App (leave empty if you want to hide the block)", "Android App URL" => "URL zur Android-App", -"URL of the Android application" => "URL zur Android-App", +"URL of the Android application" => "URL zur Android-App (leave empty if you want to hide the block)", "Windows PydioSync URL" => "URL zum Windows-Sync-Client", -"URL of the windows sync application" => "URL zum Windows-Sync-Client", +"URL of the windows sync application" => "URL zum Windows-Sync-Client (leave empty if you want to hide the block)", "Mac PydioSync URL" => "URL zum Mac-Sync-Client", -"URL of the mac sync application" => "URL zum Mac-Sync-Client", +"URL of the mac sync application" => "URL zum Mac-Sync-Client (leave empty if you want to hide the block)", +"Enable Global Search" => "Enable Global Search", +"Enable cross-workspaces search form on home page" => "Enable cross-workspaces search form on home page", +"Display Server QRCode"=>"Display Server QRCode", +"Let users connect easily to the latest mobile applications by displaying a QRCode"=>"Let users connect easily to the latest mobile applications by displaying a QRCode", ); diff --git a/core/src/plugins/access.ajxp_home/res/i18n/conf/en.php b/core/src/plugins/access.ajxp_home/res/i18n/conf/en.php index 2ca329ec84..700871fd15 100644 --- a/core/src/plugins/access.ajxp_home/res/i18n/conf/en.php +++ b/core/src/plugins/access.ajxp_home/res/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "Welcome Page"=> "Welcome Page", @@ -26,11 +26,15 @@ "Enable Getting Started" => "Enable Getting Started", "Enable addition tabulation to access getting started videos" => "Enable addition tabulation to access getting started videos", "iOS App URL" => "URL to iOS App", -"URL of the iOS application" => "URL to iOS App", +"URL of the iOS application" => "URL to iOS App (leave empty if you want to hide the block)", "Android App URL" => "URL to Android App", -"URL of the Android application" => "URL to Android App", +"URL of the Android application" => "URL to Android App (leave empty if you want to hide the block)", "Windows PydioSync URL" => "Windows PydioSync URL", -"URL of the windows sync application" => "URL to Windows sync application", +"URL of the windows sync application" => "URL to Windows sync application (leave empty if you want to hide the block)", "Mac PydioSync URL" => "Mac PydioSync URL", -"URL of the mac sync application" => "URL to Mac sync application", +"URL of the mac sync application" => "URL to Mac sync application (leave empty if you want to hide the block)", +"Enable Global Search" => "Enable Global Search", +"Enable cross-workspaces search form on home page" => "Enable cross-workspaces search form on home page", +"Display Server QRCode"=>"Display Server QRCode", +"Let users connect easily to the latest mobile applications by displaying a QRCode"=>"Let users connect easily to the latest mobile applications by displaying a QRCode", ); diff --git a/core/src/plugins/access.ajxp_home/res/i18n/conf/fr.php b/core/src/plugins/access.ajxp_home/res/i18n/conf/fr.php new file mode 100644 index 0000000000..4fc30f0fe5 --- /dev/null +++ b/core/src/plugins/access.ajxp_home/res/i18n/conf/fr.php @@ -0,0 +1,40 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +$mess=array( +"Welcome Page"=> "Page d'accueil", +"Welcome Page displaying a list of workspaces" => "Ecran d'accueil des utilisateurs après authentification.", +"Open workspace by simple click" => "Ouvrir les workspaces par clic simple", +"Open workspace by simple click instead of double click." => "Ouvrir les workspaces par clic simple plutôt que double-clic.", +"Enable Getting Started" => "Activer 'Pour Débuter'", +"Enable addition tabulation to access getting started videos" => "Activer le panneau d'information affichant des vidéos pour débuter avec la plateforme.", +"iOS App URL" => "URL de l'application iOS", +"URL of the iOS application" => "URL de l'application native iOS (laisser vide pour cacher le bloc)", +"Android App URL" => "URL de l'application Android", +"URL of the Android application" => "URL de l'application native Android (laisser vide pour cacher le bloc)", +"Windows PydioSync URL" => "PydioSync Windows", +"URL of the windows sync application" => "URL de l'application de synchronisation pour Windows Desktop (laisser vide pour cacher le bloc)", +"Mac PydioSync URL" => "PydioSync Mac", +"URL of the mac sync application" => "URL de l'application de synchronisation pour MacOS (laisser vide pour cacher le bloc)", +"Enable Global Search" => "Activer la recherche globale", +"Enable cross-workspaces search form on home page" => "Activer le formulaire de recherche inter-workspace sur la page d'accueil", +"Display Server QRCode"=>"Afficher le QRCode du serveur", +"Let users connect easily to the latest mobile applications by displaying a QRCode"=>"Simplifiez le processus de connection pour les utilisateurs par l'utilisation d'un QRCode.", +); diff --git a/core/src/plugins/access.ajxp_home/res/i18n/conf/it.php b/core/src/plugins/access.ajxp_home/res/i18n/conf/it.php index fa65141360..c2917d40f3 100644 --- a/core/src/plugins/access.ajxp_home/res/i18n/conf/it.php +++ b/core/src/plugins/access.ajxp_home/res/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "Welcome Page"=> "Pagina di Benvenuto", @@ -26,11 +26,15 @@ "Enable Getting Started" => "Abilita 'Getting Started'", "Enable addition tabulation to access getting started videos" => "Abilita tabulazione addizionale per accedere ai video 'getting started'", "iOS App URL" => "URL per App iOS", -"URL of the iOS application" => "URL per App iOS", +"URL of the iOS application" => "URL per App iOS (leave empty if you want to hide the block)", "Android App URL" => "URL per App Android", -"URL of the Android application" => "URL per App Android", +"URL of the Android application" => "URL per App Android (leave empty if you want to hide the block)", "Windows PydioSync URL" => "URL PydioSync Windows", -"URL of the windows sync application" => "URL all'applicazione Windows per sincronizzazzione", +"URL of the windows sync application" => "URL all'applicazione Windows per sincronizzazzione (leave empty if you want to hide the block)", "Mac PydioSync URL" => "URL PydioSync Mac", -"URL of the mac sync application" => "URL all'applicazione Mac per sincronizzazzione", +"URL of the mac sync application" => "URL all'applicazione Mac per sincronizzazzione (leave empty if you want to hide the block)", +"Enable Global Search" => "Enable Global Search", +"Enable cross-workspaces search form on home page" => "Enable cross-workspaces search form on home page", +"Display Server QRCode"=>"Display Server QRCode", +"Let users connect easily to the latest mobile applications by displaying a QRCode"=>"Let users connect easily to the latest mobile applications by displaying a QRCode", ); diff --git a/core/src/plugins/access.ajxp_home/res/i18n/de.php b/core/src/plugins/access.ajxp_home/res/i18n/de.php index ad2308f8c5..989502045b 100644 --- a/core/src/plugins/access.ajxp_home/res/i18n/de.php +++ b/core/src/plugins/access.ajxp_home/res/i18n/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "title"=> "Home", @@ -87,4 +87,7 @@ "72" => "Server QRCode", "73" => "Easily connect your mobile applications", "74" => "Scan this QRCode with your mobile application to easily configure the connection", + "75"=> "Search all files...", + "76" => "Access the %1 through the top-right menu", + "77" => "admin dashboard", ); diff --git a/core/src/plugins/access.ajxp_home/res/i18n/en.php b/core/src/plugins/access.ajxp_home/res/i18n/en.php index 3aef108d7f..05661db03d 100644 --- a/core/src/plugins/access.ajxp_home/res/i18n/en.php +++ b/core/src/plugins/access.ajxp_home/res/i18n/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "title"=> "Home", @@ -86,5 +86,8 @@ "71"=> "Native application for Android Devices", "72" => "Server QRCode", "73" => "Easily connect your mobile applications", -"74" => "Scan this QRCode with your mobile application to easily configure the connection", +"74" => "Scan this QRCode with your mobile application to easily configure the connection", +"75"=> "Search all files...", +"76" => "Access the %1 through the top-right menu", +"77" => "admin dashboard", ); diff --git a/core/src/plugins/access.ajxp_home/res/i18n/es.php b/core/src/plugins/access.ajxp_home/res/i18n/es.php index 568cec5a93..e2bbdb8257 100644 --- a/core/src/plugins/access.ajxp_home/res/i18n/es.php +++ b/core/src/plugins/access.ajxp_home/res/i18n/es.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ // spanish translation: Ion Rey Bakaikoa , 2010 // spanish corrections: Cristóbal Sabroe Yde , 2010 @@ -91,5 +91,7 @@ "72" => "Server QRCode", "73" => "Easily connect your mobile applications", "74" => "Scan this QRCode with your mobile application to easily configure the connection", - + "75"=> "Search all files...", + "76" => "Access the %1 through the top-right menu", + "77" => "admin dashboard", ); diff --git a/core/src/plugins/access.ajxp_home/res/i18n/fr.php b/core/src/plugins/access.ajxp_home/res/i18n/fr.php index f8e86218ef..dd65fc589c 100644 --- a/core/src/plugins/access.ajxp_home/res/i18n/fr.php +++ b/core/src/plugins/access.ajxp_home/res/i18n/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "title" => "Page d'accueil", @@ -86,4 +86,7 @@ "72" => "QRCode du serveur", "73" => "Connexion aux applications mobiles", "74" => "Scannez ce QRCode avec votre application mobile pour configurer facilement la connexion.", + "75"=> "Chercher dans tous les fichiers...", + "76" => "Utilisez le menu en haut à droite pour acceder au %1.", + "77" => "panneau d'administration", ); diff --git a/core/src/plugins/access.ajxp_home/res/i18n/hu.php b/core/src/plugins/access.ajxp_home/res/i18n/hu.php index d13c8e0e3e..0230a1c79f 100644 --- a/core/src/plugins/access.ajxp_home/res/i18n/hu.php +++ b/core/src/plugins/access.ajxp_home/res/i18n/hu.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ // hungarian translation: Gyarmati Balázs // updates: Levente Huszkó @@ -90,5 +90,7 @@ "72" => "Server QRCode", "73" => "Easily connect your mobile applications", "74" => "Scan this QRCode with your mobile application to easily configure the connection", - + "75"=> "Search all files...", + "76" => "Access the %1 through the top-right menu", + "77" => "admin dashboard", ); diff --git a/core/src/plugins/access.ajxp_home/res/i18n/it.php b/core/src/plugins/access.ajxp_home/res/i18n/it.php index 81e6c29e43..179b6d7b67 100644 --- a/core/src/plugins/access.ajxp_home/res/i18n/it.php +++ b/core/src/plugins/access.ajxp_home/res/i18n/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "title"=> "Bacheca", @@ -87,5 +87,7 @@ "72" => "Server QRCode", "73" => "Easily connect your mobile applications", "74" => "Scan this QRCode with your mobile application to easily configure the connection", - + "75"=> "Search all files...", + "76" => "Access the %1 through the top-right menu", + "77" => "admin dashboard", ); diff --git a/core/src/plugins/access.ajxp_home/res/i18n/nl.php b/core/src/plugins/access.ajxp_home/res/i18n/nl.php index bcfd5dd929..f47aca1fa6 100644 --- a/core/src/plugins/access.ajxp_home/res/i18n/nl.php +++ b/core/src/plugins/access.ajxp_home/res/i18n/nl.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "title"=> "Home", @@ -87,6 +87,8 @@ "72" => "Server QRCode", "73" => "Easily connect your mobile applications", "74" => "Scan this QRCode with your mobile application to easily configure the connection", - + "75"=> "Search all files...", + "76" => "Access the %1 through the top-right menu", + "77" => "admin dashboard", ); diff --git a/core/src/plugins/access.ajxp_home/res/i18n/pt.php b/core/src/plugins/access.ajxp_home/res/i18n/pt.php index 0f57def1e8..2f3fe9ed8b 100644 --- a/core/src/plugins/access.ajxp_home/res/i18n/pt.php +++ b/core/src/plugins/access.ajxp_home/res/i18n/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "title"=> "Home", @@ -87,4 +87,7 @@ "72" => "Server QRCode", "73" => "Easily connect your mobile applications", "74" => "Scan this QRCode with your mobile application to easily configure the connection", + "75"=> "Search all files...", + "76" => "Access the %1 through the top-right menu", + "77" => "admin dashboard", ); diff --git a/core/src/plugins/access.ajxp_home/res/i18n/ru.php b/core/src/plugins/access.ajxp_home/res/i18n/ru.php index a71c7b7ce4..c9c5ac4e1d 100644 --- a/core/src/plugins/access.ajxp_home/res/i18n/ru.php +++ b/core/src/plugins/access.ajxp_home/res/i18n/ru.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "title"=> "Начало", @@ -87,5 +87,7 @@ "72" => "Server QRCode", "73" => "Easily connect your mobile applications", "74" => "Scan this QRCode with your mobile application to easily configure the connection", - + "75"=> "Search all files...", + "76" => "Access the %1 through the top-right menu", + "77" => "admin dashboard", ); diff --git a/core/src/plugins/access.ajxp_home/res/i18n/si.php b/core/src/plugins/access.ajxp_home/res/i18n/si.php index b50079a611..72f609acb6 100644 --- a/core/src/plugins/access.ajxp_home/res/i18n/si.php +++ b/core/src/plugins/access.ajxp_home/res/i18n/si.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ // Slovenian translation: April 21 2011 by Vladimir Bohinc (vladimir.bohinc@gmail.com) $mess=array( @@ -88,5 +88,7 @@ "72" => "Server QRCode", "73" => "Easily connect your mobile applications", "74" => "Scan this QRCode with your mobile application to easily configure the connection", - + "75"=> "Search all files...", + "76" => "Access the %1 through the top-right menu", + "77" => "admin dashboard", ); diff --git a/core/src/plugins/access.ajxp_home/res/react/WelcomeComponents.js b/core/src/plugins/access.ajxp_home/res/react/WelcomeComponents.js index 662143911c..9390b328e2 100644 --- a/core/src/plugins/access.ajxp_home/res/react/WelcomeComponents.js +++ b/core/src/plugins/access.ajxp_home/res/react/WelcomeComponents.js @@ -18,6 +18,117 @@ } }); + var HomeSearchEngine = React.createClass({ + + getInitialState:function(){ + return { + loading : 0, + value : '' + }; + }, + + suggestionLoader:function(input, callback) { + + let nodeProvider = new RemoteNodeProvider(); + nodeProvider.initProvider({get_action:'multisearch', query:input}); + let rootNode = new AjxpNode('/', false); + this.setState({loading:true, value: input}); + nodeProvider.loadNode(rootNode, function(){ + let results = []; + let previousRepo = -1; + rootNode.getChildren().forEach(function(v){ + let repoId = v.getMetadata().get("repository_id"); + if(repoId != previousRepo){ + let node = new AjxpNode('/', false, v.getMetadata().get("repository_display")); + node.setRoot(); + node.getMetadata().set("repository_id", repoId); + results.push(node); + } + results.push(v); + previousRepo = repoId; + }); + // Hack : force suggestions display + if(this.refs.autosuggest.lastSuggestionsInputValue && this.refs.autosuggest.lastSuggestionsInputValue.indexOf(input) === 0){ + this.refs.autosuggest.lastSuggestionsInputValue = input; + } + callback(null, results); + this.setState({loading:false}); + }.bind(this)); + }, + + getSuggestions(input, callback){ + if(input.length < 3){ + callback(null, []); + return; + } + bufferCallback('suggestion-loader-search', 350, function(){ + this.suggestionLoader(input, callback); + }.bind(this)); + }, + + suggestionValue: function(suggestion){ + return ''; + }, + + onSuggestionSelected: function(resultNode, event){ + if(typeof resultNode === "string"){ + return ; + } + pydio.goTo(resultNode); + }, + + renderSuggestion(resultNode){ + if(typeof resultNode === "string"){ + return {resultNode}; + } + if(resultNode.isRoot()){ + return {resultNode.getLabel()}; + }else{ + let isLeaf = resultNode.isLeaf(); + let label = resultNode.getLabel(); + let value = this.state.value; + let r = new RegExp(value, 'gi'); + label = label.replace(r, function(m){return ''+m+''}); + let htmlFunc = function(){return {__html:label}}; + return ( + + + + ); + } + }, + + render: function(){ + + const inputAttributes = { + id: 'search-autosuggest', + name: 'search-autosuggest', + className: 'react-autosuggest__input', + placeholder: pydio.MessageHash['user_home.75'], + onBlur: event => pydio.UI.enableAllKeyBindings(), + onFocus: event => pydio.UI.disableAllKeyBindings(), + value: '' // Initial value + }; + return ( +
      + + true } + inputAttributes={inputAttributes} + suggestions={this.getSuggestions} + suggestionRenderer={this.renderSuggestion} + suggestionValue={this.suggestionValue} + onSuggestionSelected={this.onSuggestionSelected} + /> +
      + + ); + } + + }); + var ConfigLogo = React.createClass({ render: function(){ var logo = this.props.pydio.Registry.getPluginConfigs(this.props.pluginName).get(this.props.pluginParameter); @@ -215,7 +326,8 @@ ) } - var gettingStartedBlock = ''; + let gettingStartedBlock = null; + let adminAccessBlock = null; var gettingStartedPanel; if(this.props.enableGettingStarted){ var dgs = function(){ @@ -229,7 +341,15 @@ }.bind(this); gettingStartedPanel = ; } - + let a = this.props.controller.getActionByName('switch_to_settings'); + if(!a.deny){ + let func = function(){ + this.props.controller.fireAction('switch_to_settings'); + }.bind(this); + let sentenceParts = MessageHash['user_home.76'].split("%1"); + let dashName = MessageHash['user_home.77']; + adminAccessBlock = {sentenceParts[0]} {dashName} {sentenceParts.length > 1 ? sentenceParts[1] : null}; + } return (
      @@ -238,6 +358,7 @@

      {loginLink} {gettingStartedBlock} + {adminAccessBlock}

      ) @@ -311,12 +432,16 @@ if(mobileBlocks.length && syncBlocks.length){ blocksSep =
      ; } - + let searchBlock; + if(pydio.getPluginConfigs('access.ajxp_home').get("ENABLE_GLOBAL_SEARCH")){ + searchBlock = ; + } return (
      {syncBlocks}{blocksSep}{mobileBlocks}
      + {searchBlock}
      ); } @@ -548,7 +673,9 @@ }, onHoverLink:function(event, ws){ bufferCallback('hoverWorkspaceTimer', 400, function(){ - this.refs.legend.setWorkspace(ws); + if(this.refs && this.refs.legend){ + this.refs.legend.setWorkspace(ws); + } }.bind(this)); }, onOutLink:function(event, ws){ diff --git a/core/src/plugins/access.ajxp_user/Gruntfile.js b/core/src/plugins/access.ajxp_user/Gruntfile.js new file mode 100644 index 0000000000..20d2438170 --- /dev/null +++ b/core/src/plugins/access.ajxp_user/Gruntfile.js @@ -0,0 +1,47 @@ +module.exports = function(grunt) { + grunt.initConfig({ + babel: { + options: {}, + + dist: { + files: [ + { + expand: true, + cwd: 'react/', + src: ['**/*.js'], + dest: 'build/', + ext: '.js' + } + ] + } + }, + less: { + development: { + options: { + plugins: [ + new (require('less-plugin-autoprefix'))({browsers: ["last 2 versions, > 10%"]}) + ] + }, + files: { + "dashboard.css": "dashboard.less" + } + } + }, + watch: { + js: { + files: [ + "react/**/*" + ], + tasks: ['babel'], + options: { + spawn: false + } + } + } + }); + grunt.loadNpmTasks('grunt-babel'); + grunt.loadNpmTasks('grunt-contrib-watch'); + /*grunt.loadNpmTasks('assemble-less');*/ + grunt.registerTask('default', ['babel']); + +}; diff --git a/core/src/plugins/access.ajxp_user/UserDashboardDriver.php b/core/src/plugins/access.ajxp_user/UserDashboardDriver.php new file mode 100644 index 0000000000..a227a6732c --- /dev/null +++ b/core/src/plugins/access.ajxp_user/UserDashboardDriver.php @@ -0,0 +1,315 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + * + */ +namespace Pydio\Access\Driver\DataProvider; + +use DOMXPath; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Model\NodesList; +use Pydio\Access\Core\Model\UserSelection; +use Pydio\Core\Http\Message\ReloadMessage; +use Pydio\Core\Http\Message\UserMessage; +use Pydio\Core\Http\Response\SerializableResponseStream; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\ConfService; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Services\RepositoryService; +use Pydio\Core\Services\UsersService; +use Pydio\Share\ShareCenter; +use Zend\Diactoros\Response\EmptyResponse; + +defined('AJXP_EXEC') or die( 'Access not allowed'); +/** + * @package AjaXplorer_Plugins + * @subpackage Access + * @class ajxpSharedAccessDriver + * Plugin to access the shared elements of the current user + */ +class UserDashboardDriver extends AbstractAccessDriver +{ + + /** + * @param ContextInterface $contextInterface + */ + protected function initRepository(ContextInterface $contextInterface) + { + require_once AJXP_INSTALL_PATH . "/" . AJXP_PLUGINS_FOLDER . "/action.share/vendor/autoload.php"; + } + + /** + * @param ContextInterface $ctx + * @param \DOMNode $contribNode + */ + public function parseSpecificContributions(ContextInterface $ctx, \DOMNode &$contribNode){ + $disableAddressBook = $this->getContextualOption($ctx, "DASH_DISABLE_ADDRESS_BOOK") === true; + if($contribNode->nodeName == "client_configs" && $disableAddressBook){ + // remove template_part for orbit_content + $xPath=new DOMXPath($contribNode->ownerDocument); + $tplNodeList = $xPath->query('component_config[@className="AjxpTabulator::userdashboard_main_tab"]', $contribNode); + if(!$tplNodeList->length) return ; + $contribNode->removeChild($tplNodeList->item(0)); + } + parent::parseSpecificContributions($ctx, $contribNode); + } + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @throws \Exception + */ + public function switchAction(ServerRequestInterface $requestInterface, ResponseInterface &$responseInterface) + { + parent::accessPreprocess($requestInterface); + if(!UsersService::usersEnabled()) return ; + $action = $requestInterface->getAttribute("action"); + $httpVars = $requestInterface->getParsedBody(); + $ctx = $requestInterface->getAttribute("ctx"); + + if ($action == "edit") { + if (isSet($httpVars["sub_action"])) { + $action = $httpVars["sub_action"]; + } + } + $mess = LocaleService::getMessages(); + + $x = new SerializableResponseStream(); + $responseInterface = $responseInterface->withBody($x); + + switch ($action) { + //------------------------------------ + // BASIC LISTING + //------------------------------------ + case "ls": + $rootNodes = array( + "settings" => array( + "LABEL" => $mess["user_dash.36"], + "ICON" => "user_shared.png", + "ICON-CLASS" => "icon-cog", + "DESCRIPTION" => $mess["user_dash.37"] + ), + "users" => array( + "LABEL" => $mess["user_dash.1"], + "ICON" => "user_shared.png", + "ICON-CLASS" => "icon-book", + "DESCRIPTION" => $mess["user_dash.30"] + ), + "teams" => array( + "LABEL" => "Teams", + "ICON" => "user_shared.png", + "ICON-CLASS" => "icon-group", + "DESCRIPTION" => "My Teams" + ) + ); + $dir = (isset($httpVars["dir"])?$httpVars["dir"]:""); + $splits = explode("/", $dir); + if (count($splits)) { + if($splits[0] == "") array_shift($splits); + if(count($splits)) $strippedDir = strtolower(urldecode($splits[0])); + else $strippedDir = ""; + } + if (!empty($strippedDir) && array_key_exists($strippedDir, $rootNodes)) { + if ($strippedDir == "users") { + $x->addChunk($this->listUsers($ctx)); + } else if ($strippedDir == "teams") { + $x->addChunk($this->listTeams($ctx)); + } + } else { + $responseInterface = new EmptyResponse(); + } + break; + + case "stat" : + + header("Content-type:application/json"); + print '{"mode":true}'; + + break; + + case "delete" : + + $mime = $httpVars["ajxp_mime"]; + $selection = UserSelection::fromContext($ctx, $httpVars); + $files = $selection->getFiles(); + $minisites = $this->listMinisites($ctx); + + /** + * @var ShareCenter $shareCenter + */ + $shareCenter = PluginsService::getInstance($ctx)->getPluginById("action.share"); + foreach ($files as $index => $element) { + $element = basename($element); + $ar = explode("shared_", $mime); + $mime = array_pop($ar); + if($mime == "repository" && isSet($minisites[$element])){ + $mime = "minisite"; + $element = $minisites[$element]; + } + + $shareCenter->getShareStore($ctx)->deleteShare($mime, $element); + $out = ""; + if($mime == "repository" || $mime == "minisite") $out = $mess["ajxp_conf.59"]; + else if($mime == "user") $out = $mess["ajxp_conf.60"]; + else if($mime == "file") $out = $mess["user_dash.13"]; + if(!empty($out)){ + $x->addChunk(new UserMessage($out, LOG_LEVEL_INFO)); + } + } + $x->addChunk(new ReloadMessage()); + break; + + default: + break; + } + + return; + } + + /** + * @param $ctx ContextInterface + * @return array + */ + public function listMinisites(ContextInterface $ctx) + { + /** + * @var ShareCenter $shareCenter + */ + $shareCenter = PluginsService::getInstance($ctx)->getPluginById("action.share"); + $publicLets = $shareCenter->getShareStore($ctx)->listShares($ctx->hasUser() ? $ctx->getUser()->getId() : "shared", ""); + $minisites = array(); + foreach ($publicLets as $hash => $publicletData) { + if(!isSet($publicletData["AJXP_APPLICATION_BASE"]) && !isSet($publicletData["TRAVEL_PATH_TO_ROOT"])) continue; + $minisites[$publicletData["REPOSITORY"]] = $hash; + } + return $minisites; + } + + /** + * @param $metaIcon + * @return string + */ + private function metaIcon($metaIcon) + { + return " "; + } + + /** + * @param ContextInterface $ctx + * @return NodesList + */ + public function listTeams(ContextInterface $ctx) + { + $conf = ConfService::getConfStorageImpl(); + $nodesList = new NodesList(); + if(!method_exists($conf, "listUserTeams")) { + return $nodesList; + } + + $nodesList->initColumnsData('fileList', 'detail'); + $nodesList->appendColumn('ajxp_conf.6', 'ajxp_label'); + $nodesList->appendColumn('user_dash.10', 'repo_accesses'); + + $teams = $conf->listUserTeams($ctx->getUser()); + foreach ($teams as $teamId => $team) { + if(empty($team["LABEL"])) continue; + $team["USERS_LABELS"] = array(); + foreach(array_values($team["USERS"]) as $userId){ + $team["USERS_LABELS"][] = UsersService::getUserPersonalParameter("USER_DISPLAY_NAME", $userId, "core.conf", $userId); + } + $metaData = [ + "text" => $team["LABEL"], + "icon" => "users-folder.png", + "fonticon" => "account-multiple", + "ajxp_mime" => "ajxp_team", + "users" => " ".implode(",", array_values($team["USERS"])), + "users_labels" => " ".implode(", ", $team["USERS_LABELS"]) + ]; + $n = new AJXP_Node("/teams/".$teamId, $metaData); + $nodesList->addBranch($n); + } + return $nodesList; + } + + /** + * @param ContextInterface $ctx + * @return NodesList + */ + public function listUsers(ContextInterface $ctx) + { + $nodesList = new NodesList(); + $nodesList->initColumnsData('fileList'); + $nodesList->appendColumn('ajxp_conf.6', 'ajxp_label'); + $nodesList->appendColumn('user_dash.10', 'repo_accesses'); + + if(!UsersService::usersEnabled()) { + return $nodesList; + } + $loggedUser = $ctx->getUser(); + $users = ConfService::getConfStorageImpl()->getUserChildren($loggedUser->getId()); // AuthService::listUsers(); + $mess = LocaleService::getMessages(); + $count = 0; + $repoList = RepositoryService::listRepositoriesWithCriteria(array( + "owner_user_id" => $loggedUser->getId() + ), $count); + $userArray = array(); + foreach ($users as $userIndex => $userObject) { + $label = $userObject->getId(); + if(!$userObject->hasParent() || $userObject->getParent() != $loggedUser->getId()) continue; + if ($userObject->hasParent()) { + $label = $userObject->getParent()."000".$label; + } + $userArray[$label] = $userObject; + } + ksort($userArray); + foreach ($userArray as $userObject) { + $label = UsersService::getUserPersonalParameter("USER_DISPLAY_NAME", $userObject); + $isAdmin = $userObject->isAdmin(); + $userId = $userObject->getId(); + $repoAccesses = array(); + foreach ($repoList as $repoObject) { + if ($repoObject->hasOwner() && $repoObject->getOwner() == $loggedUser->getId()) { + $acl = $userObject->mergedRole->getAcl($repoObject->getId()); + if(!empty($acl)) $repoAccesses[] = $repoObject->getDisplay()." ($acl)"; + } + } + if(!empty($label)) $label .= " ($userId)"; + else $label = $userId; + $metaData = [ + "text" => $label, + "isAdmin" => $mess[($isAdmin?"ajxp_conf.14":"ajxp_conf.15")], + "icon" => "user_shared.png", + "fonticon" => "account-circle", + "openicon" => "user_shared.png", + "repo_accesses" => count($repoAccesses) ? $this->metaIcon("share-sign"). implode(", ", $repoAccesses):"", + "parentname" => "/users", + "is_file" => "1", + "ajxp_mime" => "shared_user" + ]; + $node = new AJXP_Node("/users/".$userId, $metaData); + $nodesList->addBranch($node); + } + return $nodesList; + } + +} \ No newline at end of file diff --git a/core/src/plugins/access.ajxp_user/class.UserDashboardDriver.php b/core/src/plugins/access.ajxp_user/class.UserDashboardDriver.php deleted file mode 100644 index 6395fb9b7c..0000000000 --- a/core/src/plugins/access.ajxp_user/class.UserDashboardDriver.php +++ /dev/null @@ -1,399 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - * - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); -/** - * @package AjaXplorer_Plugins - * @subpackage Access - * @class ajxpSharedAccessDriver - * AJXP_Plugin to access the shared elements of the current user - */ -class UserDashboardDriver extends AbstractAccessDriver -{ - - public function initRepository() - { - require_once AJXP_INSTALL_PATH."/".AJXP_PLUGINS_FOLDER."/action.share/class.ShareCenter.php"; - } - - public function parseSpecificContributions(&$contribNode){ - $disableAddressBook = $this->getFilteredOption("DASH_DISABLE_ADDRESS_BOOK") === true; - if($contribNode->nodeName == "client_configs" && $disableAddressBook){ - // remove template_part for orbit_content - $xPath=new DOMXPath($contribNode->ownerDocument); - $tplNodeList = $xPath->query('component_config[@className="AjxpTabulator::userdashboard_main_tab"]', $contribNode); - if(!$tplNodeList->length) return ; - $contribNode->removeChild($tplNodeList->item(0)); - } - parent::parseSpecificContributions($contribNode); - } - - public function switchAction($action, $httpVars, $fileVars) - { - parent::accessPreprocess($action, $httpVars, $fileVars); - if(!AuthService::usersEnabled()) return ; - - if ($action == "edit") { - if (isSet($httpVars["sub_action"])) { - $action = $httpVars["sub_action"]; - } - } - $mess = ConfService::getMessages(); - - switch ($action) { - //------------------------------------ - // BASIC LISTING - //------------------------------------ - case "ls": - $rootNodes = array( - "users" => array( - "LABEL" => $mess["user_dash.1"], - "ICON" => "user_shared.png", - "ICON-CLASS" => "icon-book", - "DESCRIPTION" => $mess["user_dash.30"] - ), - "files" => array( - "LABEL" => $mess["user_dash.34"], - "ICON" => "user_shared.png", - "ICON-CLASS" => "mdi mdi-share-variant", - "DESCRIPTION" => $mess["user_dash.35"] - ), - "settings" => array( - "LABEL" => $mess["user_dash.36"], - "ICON" => "user_shared.png", - "ICON-CLASS" => "icon-cog", - "DESCRIPTION" => $mess["user_dash.37"] - ), - "repositories" => array( - "LABEL" => $mess["user_dash.36"], - "ICON" => "user_shared.png", - "ICON-CLASS" => "icon-cog", - "DESCRIPTION" => $mess["user_dash.37"] - ), - "teams" => array( - "LABEL" => "Teams", - "ICON" => "user_shared.png", - "ICON-CLASS" => "icon-group", - "DESCRIPTION" => "My Teams" - ) - ); - $dir = (isset($httpVars["dir"])?$httpVars["dir"]:""); - $splits = explode("/", $dir); - if (count($splits)) { - if($splits[0] == "") array_shift($splits); - if(count($splits)) $strippedDir = strtolower(urldecode($splits[0])); - else $strippedDir = ""; - } - if (array_key_exists($strippedDir, $rootNodes)) { - AJXP_XMLWriter::header(); - if ($strippedDir == "users") { - $this->listUsers(); - } else if ($strippedDir == "teams") { - $this->listTeams(); - } else if ($strippedDir == "repositories") { - $this->listRepositories(); - } else if ($strippedDir == "files") { - $this->listSharedFiles("files"); - } - AJXP_XMLWriter::close(); - } else { - AJXP_XMLWriter::header(); - /* - AJXP_XMLWriter::sendFilesListComponentConfig(''); - foreach ($rootNodes as $key => $data) { - $l = $data["LABEL"]; - print ''; - } - */ - AJXP_XMLWriter::close(); - } - break; - - case "stat" : - - header("Content-type:application/json"); - print '{"mode":true}'; - - break; - - case "delete" : - $mime = $httpVars["ajxp_mime"]; - $selection = new UserSelection(); - $selection->initFromHttpVars($httpVars); - $files = $selection->getFiles(); - AJXP_XMLWriter::header(); - $minisites = $this->listSharedFiles("minisites"); - /** - * @var ShareCenter $shareCenter - */ - $shareCenter = AJXP_PluginsService::findPluginById("action.share"); - foreach ($files as $index => $element) { - $element = basename($element); - $ar = explode("shared_", $mime); - $mime = array_pop($ar); - if($mime == "repository" && isSet($minisites[$element])){ - $mime = "minisite"; - $element = $minisites[$element]; - } - $shareCenter->getShareStore()->deleteShare($mime, $element); - if($mime == "repository" || $mime == "minisite") $out = $mess["ajxp_conf.59"]; - else if($mime == "user") $out = $mess["ajxp_conf.60"]; - else if($mime == "file") $out = $mess["user_dash.13"]; - } - AJXP_XMLWriter::sendMessage($out, null); - AJXP_XMLWriter::reloadDataNode(); - AJXP_XMLWriter::close(); - break; - - case "clear_expired" : - - /** - * @var ShareCenter $shareCenter - */ - $shareCenter = AJXP_PluginsService::getInstance()->findPluginById("action.share"); - $deleted = $shareCenter->getShareStore()->clearExpiredFiles(true); - AJXP_XMLWriter::header(); - if (count($deleted)) { - AJXP_XMLWriter::sendMessage(sprintf($mess["user_dash.23"], count($deleted).""), null); - AJXP_XMLWriter::reloadDataNode(); - } else { - AJXP_XMLWriter::sendMessage($mess["user_dash.24"], null); - } - AJXP_XMLWriter::close(); - - break; - - case "reset_download_counter" : - - $selection = new UserSelection(); - $selection->initFromHttpVars($httpVars); - $elements = $selection->getFiles(); - foreach ($elements as $element) { - PublicletCounter::reset(str_replace(".php", "", basename($element))); - } - AJXP_XMLWriter::header(); - AJXP_XMLWriter::reloadDataNode(); - AJXP_XMLWriter::close(); - - break; - - default: - break; - } - - return; - } - - /** - * @param string $mode - * @return array|void - */ - public function listSharedFiles($mode = "files") - { - if($mode == "files"){ - AJXP_XMLWriter::sendFilesListComponentConfig(' - - - - - - - '); - } - - $mess = ConfService::getMessages(); - $loggedUser = AuthService::getLoggedUser(); - $userId = $loggedUser->getId(); - - $shareCenter = AJXP_PluginsService::getInstance()->findPluginById("action.share"); - $downloadBase = $shareCenter->getPublicAccessManager()->buildPublicDlURL(); - $publicLets = $shareCenter->listShares(true, null); - - $minisites = array(); - foreach ($publicLets as $hash => $publicletData) { - if($mode == "files"){ - if(isSet($publicletData["AJXP_APPLICATION_BASE"]) || isSet($publicletData["TRAVEL_PATH_TO_ROOT"]) - || (isset($publicletData["OWNER_ID"]) && $publicletData["OWNER_ID"] != $userId) - || empty($publicletData["FILE_PATH"]) - ){ - continue; - } - $expired = ($publicletData["EXPIRE_TIME"]!=0?($publicletData["EXPIRE_TIME"]getDisplay()).":/".SystemTextEncoding::toUTF8($publicletData["FILE_PATH"]), true, array( - "icon" => "html.png", - "password" => ($publicletData["PASSWORD"]!=""?$this->metaIcon("key").$publicletData["PASSWORD"]:""), - "expiration" => ($publicletData["EXPIRE_TIME"]!=0?($expired?$this->metaIcon("time"):"").date($mess["date_format"], $publicletData["EXPIRE_TIME"]):""), - "download_count" => !empty($publicletData["DOWNLOAD_COUNT"])?$this->metaIcon("download-alt").$publicletData["DOWNLOAD_COUNT"]:"", - "download_limit" => ($publicletData["DOWNLOAD_LIMIT"] == 0 ? "" : $this->metaIcon("cloud-download").$publicletData["DOWNLOAD_LIMIT"] ), - "download_url" => $this->metaIcon("link").$downloadBase . "/".$hash, - "ajxp_mime" => "shared_file") - ); - }else if($mode == "minisites"){ - if(!isSet($publicletData["AJXP_APPLICATION_BASE"]) && !isSet($publicletData["TRAVEL_PATH_TO_ROOT"])) continue; - $minisites[$publicletData["REPOSITORY"]] = $hash; - } - } - if($mode == "minisites"){ - return $minisites; - } - } - - private function metaIcon($metaIcon) - { - return " "; - } - - public function listTeams() - { - $conf = ConfService::getConfStorageImpl(); - if(!method_exists($conf, "listUserTeams")) return; - AJXP_XMLWriter::sendFilesListComponentConfig(' - - - '); - $teams = $conf->listUserTeams(); - foreach ($teams as $teamId => $team) { - if(empty($team["LABEL"])) continue; - $team["USERS_LABELS"] = array(); - foreach(array_values($team["USERS"]) as $userId){ - $team["USERS_LABELS"][] = ConfService::getUserPersonalParameter("USER_DISPLAY_NAME", $userId, "core.conf", $userId); - } - AJXP_XMLWriter::renderNode("/teams/".$teamId, $team["LABEL"], true, array( - "icon" => "users-folder.png", - "ajxp_mime" => "ajxp_team", - "users" => " ".implode(",", array_values($team["USERS"])), - "users_labels" => " ".implode(", ", $team["USERS_LABELS"]) - ), true, true); - } - } - - public function listUsers() - { - AJXP_XMLWriter::sendFilesListComponentConfig(''); - if(!AuthService::usersEnabled()) return ; - $loggedUser = AuthService::getLoggedUser(); - $users = ConfService::getConfStorageImpl()->getUserChildren($loggedUser->getId()); // AuthService::listUsers(); - $mess = ConfService::getMessages(); - $count = 0; - $repoList = ConfService::listRepositoriesWithCriteria(array( - "owner_user_id" => $loggedUser->getId() - ), $count); - $userArray = array(); - foreach ($users as $userIndex => $userObject) { - $label = $userObject->getId(); - if(!$userObject->hasParent() || $userObject->getParent() != $loggedUser->getId()) continue; - if ($userObject->hasParent()) { - $label = $userObject->getParent()."000".$label; - } - $userArray[$label] = $userObject; - } - ksort($userArray); - foreach ($userArray as $userObject) { - $label = ConfService::getUserPersonalParameter("USER_DISPLAY_NAME", $userObject); - $isAdmin = $userObject->isAdmin(); - $userId = $userObject->getId(); - $repoAccesses = array(); - foreach ($repoList as $repoObject) { - if ($repoObject->hasOwner() && $repoObject->getOwner() == $loggedUser->getId()) { - $acl = $userObject->mergedRole->getAcl($repoObject->getId()); - if(!empty($acl)) $repoAccesses[] = $repoObject->getDisplay()." ($acl)"; - } - } - if(!empty($label)) $label .= " ($userId)"; - else $label = $userId; - print ''; - } - } - - public function listRepositories() - { - AJXP_XMLWriter::sendFilesListComponentConfig(''); - $repoArray = array(); - $loggedUser = AuthService::getLoggedUser(); - $count = 0; - $repos = ConfService::listRepositoriesWithCriteria(array( - "owner_user_id" => $loggedUser->getId() - ), $count); - - $searchAll = ConfService::getCoreConf("CROSSUSERS_ALLGROUPS", "conf"); - $displayAll = ConfService::getCoreConf("CROSSUSERS_ALLGROUPS_DISPLAY", "conf"); - if($searchAll || $displayAll){ - $baseGroup = "/"; - }else{ - $baseGroup = AuthService::filterBaseGroup("/"); - } - AuthService::setGroupFiltering(false); - $users = AuthService::listUsers($baseGroup); - - $minisites = $this->listSharedFiles("minisites"); - - foreach ($repos as $repoIndex => $repoObject) { - if($repoObject->getAccessType() == "ajxp_conf") continue; - if (!$repoObject->hasOwner() || $repoObject->getOwner() != $loggedUser->getId()) { - continue; - } - if(is_numeric($repoIndex)) $repoIndex = "".$repoIndex; - $name = (isSet($minisites[$repoIndex]) ? "[Minisite] ":""). AJXP_Utils::xmlEntities(SystemTextEncoding::toUTF8($repoObject->getDisplay())); - $repoArray[$name] = $repoIndex; - } - // Sort the list now by name - ksort($repoArray); - foreach ($repoArray as $name => $repoIndex) { - $repoObject =& $repos[$repoIndex]; - $repoAccesses = array(); - foreach ($users as $userId => $userObject) { - if($userObject->getId() == $loggedUser->getId()) continue; - $label = ConfService::getUserPersonalParameter("USER_DISPLAY_NAME", $userObject, "core.conf", $userId); - $acl = $userObject->mergedRole->getAcl($repoObject->getId()); - if(!empty($acl)) $repoAccesses[] = $label. " (".$acl.")"; - } - $parent = $repoObject->getParentId(); - $parentRepo =& $repos[$parent]; - $parentLabel = $this->metaIcon("folder-open").$parentRepo->getDisplay(); - $repoPath = $repoObject->getOption("PATH"); - $parentPath = $parentRepo->getOption("PATH"); - $parentLabel .= " (".str_replace($parentPath, "", $repoPath).")"; - - $metaData = array( - "repository_id" => $repoIndex, - "icon" => "document_open_remote.png", - "openicon" => "document_open_remote.png", - "parentname" => "/repositories", - "parent_label" => $parentLabel, - "repo_accesses" => count($repoAccesses) ? $this->metaIcon("share-sign").implode(", ", $repoAccesses) : "", - "ajxp_mime" => "shared_repository" - ); - AJXP_XMLWriter::renderNode("/repositories/$repoIndex", $name, true, $metaData); - } - } - -} diff --git a/core/src/plugins/access.ajxp_user/class.UserProfileEditor.js b/core/src/plugins/access.ajxp_user/class.UserProfileEditor.js index 5bdeb6dd61..43738ab293 100644 --- a/core/src/plugins/access.ajxp_user/class.UserProfileEditor.js +++ b/core/src/plugins/access.ajxp_user/class.UserProfileEditor.js @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . * */ Class.create("UserProfileEditor", AjxpPane, { diff --git a/core/src/plugins/access.ajxp_user/class.WebDAVprefsEditor.js b/core/src/plugins/access.ajxp_user/class.WebDAVprefsEditor.js index 2ca0aff911..ec620f5402 100644 --- a/core/src/plugins/access.ajxp_user/class.WebDAVprefsEditor.js +++ b/core/src/plugins/access.ajxp_user/class.WebDAVprefsEditor.js @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . * */ Class.create("WebDAVprefsEditor", AjxpPane, { diff --git a/core/src/plugins/access.ajxp_user/dashboard.css b/core/src/plugins/access.ajxp_user/dashboard.css index c52dcbea6c..e046b0df56 100644 --- a/core/src/plugins/access.ajxp_user/dashboard.css +++ b/core/src/plugins/access.ajxp_user/dashboard.css @@ -1,285 +1,312 @@ -#userdashboard_main_tab{ - font-family: Roboto, sans-serif; - -webkit-font-smoothing: antialiased; -} - -#userdashboard_main_tab h3{ - font-weight: 500; -} - -#userdashboard_main_tab #content_pane.droparea{ - background: url("../../../../../../index.php?get_action=get_drop_bg") no-repeat scroll left bottom transparent; - background-position: 50%; - border: 5px dashed #5c5c5c !important; - border-radius: 5px !important; - margin: 5px !important; -} - -#userdashboard_main_tab div.tabbed_editor{ - border:0; -} - -#userdashboard_main_tab div.header_resizer div.header_label { - padding-top: 7px; -} - -#userdashboard_main_tab div.class-FetchedResultPane{ - background-color: #ffffff; -} -#userdashboard_main_tab div.class-FetchedResultPane div.thumbnail_selectable_cell.detailed{ - padding: 8px 4px; - border-bottom: 1px solid rgba(0,0,0,0.03); -} - -#userdashboard_main_tab div.class-FetchedResultPane div.thumbnail_selectable_cell.detailed div.thumbLabel{ - padding-top: 7px; - font-size: 14px; -} - -#userdashboard_main_tab div.class-FetchedResultPane div.thumbnail_selectable_cell.selected-focus{ - background-color: #fb725c !important; -} - -#userdashboard_main_tab #content_pane.dropareaHover{ - border-color: #e35d52 !important; -} - -#userdashboard_main_tab span.toggleHeader span[class^="icon-"], -#userdashboard_main_tab span.toggleHeader span[class*=" icon-"]{ - display: inline; -} - - -div#userdashboard_main_tab.horizontal_tabulator div.tabulatorContainer{ - padding-top: 63px; - background-color: #444; -} - -div#userdashboard_main_tab.horizontal_tabulator div.tabbed_editor{ - background-color: #ffffff; - box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23); -} - -div#userdashboard_myparams_title{ - position: absolute; - color: #ffffff; - font-weight: 300; - padding: 25px 16px; - height: 63px; - font-size: 13px; - width: 100%; - border-bottom: 1px solid rgba(255,255,255,0.15); - box-sizing: border-box; -} - -div#userdashboard_myparams_title span#dash_title{ +#userdashboard_main_tab { + font-family: Roboto, sans-serif; + -webkit-font-smoothing: antialiased; +} +#userdashboard_main_tab.horizontal_tabulator { + display: block; + position: relative; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorContainer { + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 240px; + padding-top: 10px; + background-color: #444; + z-index: 1; + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); + transition: all 550ms cubic-bezier(0.23, 1, 0.32, 1) 0ms; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorContainer span.toggleHeader { + padding: 16px; + font-size: 13px; + color: #ff7043 !important; + background-color: transparent !important; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorContainer span.toggleHeader.toggleInactive { + color: white !important; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorContainer span.toggleHeader span[class^="icon-"], +#userdashboard_main_tab.horizontal_tabulator div.tabulatorContainer span.toggleHeader span[class*=" icon-"], +#userdashboard_main_tab.horizontal_tabulator div.tabulatorContainer span.toggleHeader span.mdi { + display: inline; + float: right; + font-size: 18px; + margin-right: 0; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer { + transition: all 550ms cubic-bezier(0.23, 1, 0.32, 1) 0ms; + position: absolute; + left: 240px; + top: 0; + bottom: 0; + right: 0; + padding: 16px; + background-color: #f4f4f4; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div { + position: relative; + background-color: #ffffff; + border-radius: 2px; + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div#account_pane, +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div#webdav_pane { + overflow-y: auto; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div .title-flex { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div .title-flex h3 { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div .title-flex h3 .legend { + font-size: 12px; + font-weight: 300; + line-height: 1.3em; + margin-top: 3px; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div .title-flex h3.dashboard_panel_title { + margin-top: 16px; + padding-left: 20px; + font-size: 24px; + line-height: 32px; + font-weight: 400; + color: rgba(0, 0, 0, 0.87); +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div .title-flex div.actionBar, +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div .title-flex div.action_bar { + margin: 20px 20px 0 0; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div .action_bar, +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div #buttons_bar { + background-color: white !important; + padding-bottom: 20px; + border-bottom: 0; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div .action_bar a, +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div #buttons_bar a { + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.24), 0 1px 6px rgba(0, 0, 0, 0.12); + margin: 0 3px; + display: inline-block; + border-radius: 2px !important; + padding: 7px 16px; + height: auto; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div .action_bar a span.actionbar_button_label, +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div #buttons_bar a span.actionbar_button_label { + font-size: 14px; +} +@media only screen and (max-width: 720px) { + #userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div .action_bar a span.ajxp_icon_span, + #userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div #buttons_bar a span.ajxp_icon_span { + display: block !important; + margin: 0 0 10px; + } +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div div.class-FetchedResultPane { + border-top: 1px solid #fafafa; + background-color: #ffffff; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div div.class-FetchedResultPane#shared_files_list { + border-top: 1px solid #eaeaea; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div div.class-FetchedResultPane div.thumbnail_selectable_cell.detailed { + padding: 10px 5px; + border-bottom: 1px solid rgba(0, 0, 0, 0.03); + transition: all 550ms cubic-bezier(0.23, 1, 0.32, 1) 0ms; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div div.class-FetchedResultPane div.thumbnail_selectable_cell.detailed div.mimefont { + line-height: 28px; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div div.class-FetchedResultPane div.thumbnail_selectable_cell.detailed div.thumbLabel { + padding-top: 7px; + font-size: 14px; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div div.class-FetchedResultPane div.thumbnail_selectable_cell.detailed div.thumbnail_selectable_cell { + padding-right: 11px; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div div.class-FetchedResultPane div.thumbnail_selectable_cell.selected-focus { + background-color: #ff6a3c !important; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div div.class-FetchedResultPane div.header_resizer div.header_label { + padding-left: 10px; + padding-top: 7px; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div div.class-FetchedResultPane .sort-table tr { + background-color: white !important; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div div.class-FetchedResultPane .sort-table tr td { + border-bottom: 1px solid #eaeaea; + padding: 10px 0; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div div.class-FetchedResultPane .sort-table tr td:first-of-type { + padding: 10px 5px; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div div.class-FetchedResultPane .sort-table tr td:first-of-type span.mimefont { + margin-right: 10px; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div div.class-FetchedResultPane .sort-table tr.selected-focus { + background-color: #ff6a3c !important; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div div.class-FetchedResultPane .sort-table tr.selected-focus td { + border-bottom: 0; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div div.class-FetchedResultPane .sort-table tr[data-groupByValue].selected-focus { + background-color: white !important; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div div.class-FetchedResultPane .sort-table tr[data-groupByValue].selected-focus td { + border-bottom: 1px solid #eaeaea; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer > div div.class-FetchedResultPane .sort-table tr[data-groupByValue] h3 { + padding: 5px 5px 10px; + font-size: 1em; + color: #00897B; + border-bottom: 0; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer div#webdav_pane div.dav-url { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer div#webdav_pane div.dav-url div.mui-text-field { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + width: auto; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer div#webdav_pane div.dav-url button.mui-icon-button { + margin-top: 30px; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer div#webdav_pane div.dav-url button.mui-icon-button span.mui-font-icon { + font-size: 16px; + color: rgba(0, 0, 0, 0.33); +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer div#webdav_pane div.dav-url button.mui-icon-button span.mui-font-icon:hover { + color: rgba(0, 0, 0, 0.73); +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer div#webdav_pane div.copy_legend { + color: #00897B; + margin-top: -10px; +} +#userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer div#webdav_pane div.dav-password-legend { + color: rgba(0, 0, 0, 0.33); +} +@media only screen and (max-width: 800px) { + #userdashboard_main_tab.horizontal_tabulator div.tabulatorContainer { + width: 200px; + } + #userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer { + left: 200px; + } +} +@media only screen and (max-width: 630px) { + #userdashboard_main_tab.horizontal_tabulator div.tabulatorContainer { + width: 40px; + } + #userdashboard_main_tab.horizontal_tabulator div.tabulatorContainer span.toggleHeader { + padding-right: 11px !important; + } + #userdashboard_main_tab.horizontal_tabulator div.tabulatorContainer span.tab_label { display: none; -} - -div#userdashboard_myparams_title a#dash_back_workspace{ + } + #userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer { + left: 40px; + } + #userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer .title-flex { + display: block !important; + } + #userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer .title-flex h3 { + padding-right: 20px; + } + #userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer .title-flex .actionBar, + #userdashboard_main_tab.horizontal_tabulator div.tabulatorPanelsContainer .title-flex .action_bar { + text-align: right !important; + } +} +div#userdashboard_myparams_title { + position: absolute; + color: #ffffff; + padding: 25px 16px; + height: 63px; + font-size: 12px; + width: 240px; + box-sizing: border-box; + bottom: 0; + z-index: 2; + letter-spacing: 1px; + font-weight: 500; + white-space: nowrap; + overflow-x: hidden; +} +div#userdashboard_myparams_title span#dash_title { + display: none; +} +div#userdashboard_myparams_title a#dash_back_workspace { + display: block; + cursor: pointer; +} +@media only screen and (max-width: 800px) { + div#userdashboard_myparams_title { + width: 200px !important; + } +} +@media only screen and (max-width: 630px) { + div#userdashboard_myparams_title { + width: 40px !important; + } + div#userdashboard_myparams_title a#dash_back_workspace { display: block; - cursor: pointer; -} - -div#userdashboard_myparams_title a#dash_back_workspace:hover{ - color:rgb(204, 204, 204); -} - -div#userdashboard_main_tab div#team_pane, -div#userdashboard_main_tab div#my-users-pane, -div#userdashboard_main_tab div#account_pane, -div#userdashboard_main_tab div#webdav_pane, -div#userdashboard_main_tab div#shared_files_pane, -div#userdashboard_main_tab div#shared_pane, -div#userdashboard_main_tab div#shared_pane2, -div#userdashboard_main_tab div#home_account_pane{ - position: relative; -} - -div#userdashboard_main_tab h3.dashboard_panel_title{ - position: absolute; - top: 18px; - left: 16px; - font-size: 24px; - font-weight: 500; - margin: 0; - color: #444; -} - - -div#team_edit_container { - max-height: 200px; - overflow: auto; - background-color: white; - border: 1px solid #ccc; - border-radius: 2px; - z-index: 10; -} - -#team_edit_container ul { - margin: 0; - padding: 0; -} - -#team_edit_container ul li{ - list-style: none; - padding: 8px; - color: rgba(0,0,0,0.87); -} - -#team_edit_container ul li.selected{ - background-color: #eee !important; -} - -#team_edit_entries .user_entry{ - padding: 8px; - border-bottom: 1px solid rgba(0,0,0,0.03); -} -#team_edit_entries .user_entry .delete_user_entry{ - float: right; - cursor: pointer; -} - -.wallet_pane{ - /* - background-color:#EFEFEF; - border-left:1px solid; - */ - margin-left:2px; - margin-right:2px; - padding:0 10px 0 0; -} -.wallet_pane div.driver_form{ - width:215px; -} -.wallet_pane input.dialogButton{ - float:right; - border: 1px solid #676965; -} - -/****************************************/ -/* WEBDAV PREFERENCES */ -/****************************************/ -#webdav_main_access{ - font-size: 20px; - padding: 5px; - text-align: center; - border-radius: 3px; - background-color: white; - width: 97%; - margin-bottom: 5px; - color: rgb(111, 121, 131); -} - -#perworkspace-urls-toggle{ - cursor: pointer; -} -#perworkspace-urls-toggle span{ - font-size: 13px; - margin: 0 7px; - text-decoration: none; -} - -#webdav_repo_list{ - height: auto !important; - background-color: white; -} - -#webdav_repo_list div{ - padding: 7px; -} - -#webdav_repo_list span{ + } + div#userdashboard_myparams_title a#dash_back_workspace span.icon-chevron-left { display: inline-block; - width: 24%; -} - -#webdav_repo_list input { - width: 92% !important; - margin-top: 11px; -} - -input#webdav_password{ - height: 22px !important; -} - - -@media only screen -and (max-width : 980px){ - #notifications_center div.notif_event_container{ - width: 100px; - } -} - -@media only screen -and (max-width : 800px){ - - #notifications_center li{ - text-align: center; - } - - #notifications_center li > div:first-of-type{ - float: none !important; - position: absolute; - bottom: 5px; - left: 45%; - } - - #notifications_center li > img{ - float: none !important; - margin: auto; - } - - #notifications_center div.notif_event_container{ - float: none; - margin-bottom: 2px; - width: 100%; - text-align: center; - } - - #notifications_center div[data-is_loaded]{ - margin: 0 auto; - float: none !important; - min-height: 70px; - } - - #notifications_center div.notif_event_label{ - font-size: 11px; - } - - .tutorial_legend{ - clear: left; - width: 97%; - } - - .tutorial_video{ - width: 94%; - height: 300px; - margin-right: 3%; - margin-left: 3%; - } - #dl_pydio_cont{ - width: 400px !important; - } - #dl_pydio_for{ - display: none; - } - - -} - -@media only screen -and (max-width : 400px){ - - #notifications_center li > div:first-of-type, - #notifications_center div.notif_event_repository, - #notifications_center div.notif_event_description, - #notifications_center div.notif_event_date{ - display: none; - } - - #logo_div img{ - width: 100px; - } - + margin-right: 20px; + } +} +div#team_edit_form span.user_entry_label { + color: rgba(0, 0, 0, 0.87); +} +div#team_edit_form span.user_entry_label::before { + content: "\f007"; + font-family: FontAwesome; + display: inline-block; + margin-right: 5px; +} +div#team_edit_form div#team_edit_container { + max-height: 200px; + overflow: auto; + background-color: white; + border: 1px solid #ccc; + border-radius: 2px; + z-index: 10; +} +div#team_edit_form div#team_edit_container ul { + margin: 0; + padding-left: 0; +} +div#team_edit_form div#team_edit_container ul li { + list-style: none; + padding: 8px; + color: rgba(0, 0, 0, 0.87); +} +div#team_edit_form div#team_edit_container ul li.selected { + background-color: #eee !important; +} +div#team_edit_form #team_edit_entries .user_entry { + padding: 8px; + border-bottom: 1px solid rgba(0, 0, 0, 0.03); +} +div#team_edit_form #team_edit_entries .user_entry .delete_user_entry { + float: right; + cursor: pointer; +} +@media only screen and (max-width: 400px) { + #logo_div img { + width: 100px; + } } diff --git a/core/src/plugins/access.ajxp_user/dashboard.less b/core/src/plugins/access.ajxp_user/dashboard.less new file mode 100644 index 0000000000..598302ec49 --- /dev/null +++ b/core/src/plugins/access.ajxp_user/dashboard.less @@ -0,0 +1,348 @@ +@bezier_transition:all 550ms cubic-bezier(0.23, 1, 0.32, 1) 0ms; +@break1: 40px; +@break2: 200px; +@break3: 240px; + +#userdashboard_main_tab{ + font-family: Roboto, sans-serif; + -webkit-font-smoothing: antialiased; + + &.horizontal_tabulator { + display: block; + position: relative; + + div.tabulatorContainer{ + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: @break3; + padding-top: 10px; + background-color: #444; + z-index: 1; + box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23); + transition: @bezier_transition; + span.toggleHeader{ + + padding: 16px; + font-size: 13px; + color: #ff7043 !important; + background-color: transparent !important; + &.toggleInactive{ + color: white !important; + } + span[class^="icon-"], span[class*=" icon-"], span.mdi{ + display: inline; + float: right; + font-size: 18px; + margin-right: 0; + } + } + } + + div.tabulatorPanelsContainer{ + transition: @bezier_transition; + + position: absolute; + left: @break3; + top: 0; + bottom: 0; + right:0; + padding:16px; + background-color: #f4f4f4; + > div{ + position: relative; + background-color: #ffffff; + border-radius: 2px; + box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23); + &#account_pane, &#webdav_pane{ + overflow-y: auto; + } + .title-flex{ + display: flex; + h3{ + flex: 1; + .legend{ + font-size: 12px; + font-weight: 300; + line-height: 1.3em; + margin-top: 3px; + } + &.dashboard_panel_title{ + margin-top: 16px; + padding-left: 20px; + font-size: 24px; + line-height: 32px; + font-weight: 400; + color: rgba(0, 0, 0, 0.87); + } + } + div.actionBar, div.action_bar{ + margin: 20px 20px 0 0; + } + } + .action_bar, #buttons_bar{ + background-color: white !important; + padding-bottom: 20px; + border-bottom: 0; + a{ + box-shadow:0 1px 4px rgba(0, 0, 0, 0.24), 0 1px 6px rgba(0, 0, 0, 0.12); + margin: 0 3px; + display: inline-block; + border-radius: 2px !important; + padding: 7px 16px; + height: auto; + span.actionbar_button_label{ + font-size: 14px; + } + @media only screen and (max-width: 720px){ + span.ajxp_icon_span { + display: block !important; + margin: 0 0 10px; + } + } + } + } + div.class-FetchedResultPane{ + border-top: 1px solid rgb(250,250,250); + &#shared_files_list{ + border-top:1px solid #eaeaea; + } + background-color: #ffffff; + div.thumbnail_selectable_cell.detailed{ + padding: 10px 5px; + border-bottom: 1px solid rgba(0,0,0,0.03); + transition: @bezier_transition; + div.mimefont{ + line-height: 28px; + } + div.thumbLabel{ + padding-top: 7px; + font-size: 14px; + } + div.thumbnail_selectable_cell{ + padding-right: 11px; + } + } + div.thumbnail_selectable_cell.selected-focus{ + background-color: lighten(#ff5722, 5) !important; + } + + div.header_resizer div.header_label { + padding-left: 10px; + padding-top: 7px; + } + .sort-table tr{ + background-color: white !important; + td{ + border-bottom: 1px solid rgb(234,234,234); + padding: 10px 0; + &:first-of-type{ + padding: 10px 5px; + span.mimefont{ + margin-right: 10px; + } + } + } + &.selected-focus{ + background-color: lighten(#ff5722, 5) !important; + td{ + border-bottom: 0; + } + } + &[data-groupByValue]{ + &.selected-focus{ + background-color: white !important; + td{ + border-bottom: 1px solid rgb(234,234,234); + } + } + h3 { + padding: 5px 5px 10px; + font-size: 1em; + color: #00897B; + border-bottom: 0; + } + } + } + } + } + div#webdav_pane{ + div.dav-url{ + display: flex; + div.mui-text-field{ + flex: 1; + width: auto; + } + button.mui-icon-button{ + margin-top: 30px; + span.mui-font-icon{ + font-size: 16px; + color: rgba(0,0,0,0.33); + &:hover{ + color: rgba(0,0,0,0.73); + } + } + } + } + div.copy_legend{ + color: #00897B; + margin-top: -10px; + } + div.dav-password-legend{ + color: rgba(0,0,0,0.33); + } + } + } + + @media only screen + and (max-width : 800px){ + div.tabulatorContainer{ + width: @break2; + } + div.tabulatorPanelsContainer{ + left: @break2; + } + } + @media only screen + and (max-width : 630px){ + div.tabulatorContainer{ + width: @break1; + span.toggleHeader{ + padding-right: 11px !important; + } + span.tab_label{ + display: none; + } + } + div.tabulatorPanelsContainer{ + left: @break1; + .title-flex{ + display: block !important; + h3{ + padding-right: 20px; + } + .actionBar, .action_bar{ + text-align: right !important; + } + } + } + } + } + +} + + +div#userdashboard_myparams_title{ + + position: absolute; + color: #ffffff; + padding: 25px 16px; + height: 63px; + font-size: 12px; + width: @break3; + box-sizing: border-box; + bottom: 0; + z-index: 2; + letter-spacing: 1px; + font-weight: 500; + + white-space: nowrap; + overflow-x: hidden; + + span#dash_title{ + display: none; + } + + a#dash_back_workspace{ + display: block; + cursor: pointer; + } + + @media only screen + and (max-width : 800px){ + &{ + width: @break2 !important; + } + } + @media only screen + and (max-width : 630px){ + &{ + width: @break1 !important; + } + a#dash_back_workspace{ + display: block; + span.icon-chevron-left{ + display: inline-block; + margin-right: 20px; + } + } + } + +} + +div#team_edit_form{ + + span.user_entry_label{ + color: rgba(0,0,0,0.87); + &::before { + content: "\f007"; + font-family: FontAwesome; + display: inline-block; + margin-right: 5px; + } + } + + div#team_edit_container { + max-height: 200px; + overflow: auto; + background-color: white; + border: 1px solid #ccc; + border-radius: 2px; + z-index: 10; + ul { + margin: 0; + padding-left: 0; + li{ + list-style: none; + padding: 8px; + color: rgba(0,0,0,0.87); + &.selected{ + background-color: #eee !important; + } + } + } + + } + + + + #team_edit_entries { + + .user_entry{ + padding: 8px; + border-bottom: 1px solid rgba(0,0,0,0.03); + .delete_user_entry{ + float: right; + cursor: pointer; + } + } + + } + + +} + +@media only screen +and (max-width : 630px){ + +} + +@media only screen +and (max-width : 400px){ + + #logo_div img{ + width: 100px; + } + +} diff --git a/core/src/plugins/access.ajxp_user/i18n/ca.php b/core/src/plugins/access.ajxp_user/i18n/ca.php index 046388e1db..fecdbaf531 100644 --- a/core/src/plugins/access.ajxp_user/i18n/ca.php +++ b/core/src/plugins/access.ajxp_user/i18n/ca.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ // catalan translation: Salva Gómez , 2015 diff --git a/core/src/plugins/access.ajxp_user/i18n/conf/de.php b/core/src/plugins/access.ajxp_user/i18n/conf/de.php index 2e503f4be3..d4fd81f935 100644 --- a/core/src/plugins/access.ajxp_user/i18n/conf/de.php +++ b/core/src/plugins/access.ajxp_user/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "User Dashboard"=> "Mein Dashboard", diff --git a/core/src/plugins/access.ajxp_user/i18n/conf/en.php b/core/src/plugins/access.ajxp_user/i18n/conf/en.php index 7a80772796..d62c658218 100644 --- a/core/src/plugins/access.ajxp_user/i18n/conf/en.php +++ b/core/src/plugins/access.ajxp_user/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "User Dashboard"=> "User Dashboard", diff --git a/core/src/plugins/access.ajxp_user/i18n/conf/it.php b/core/src/plugins/access.ajxp_user/i18n/conf/it.php index a1927f610c..3bd60f2866 100644 --- a/core/src/plugins/access.ajxp_user/i18n/conf/it.php +++ b/core/src/plugins/access.ajxp_user/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "User Dashboard"=> "Dashboard Utente", diff --git a/core/src/plugins/access.ajxp_user/i18n/de.php b/core/src/plugins/access.ajxp_user/i18n/de.php index 8a373ac4bb..74d270a9d5 100644 --- a/core/src/plugins/access.ajxp_user/i18n/de.php +++ b/core/src/plugins/access.ajxp_user/i18n/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "title"=> "Mein Dashboard", diff --git a/core/src/plugins/access.ajxp_user/i18n/en.php b/core/src/plugins/access.ajxp_user/i18n/en.php index 3f02a2febc..178e059270 100644 --- a/core/src/plugins/access.ajxp_user/i18n/en.php +++ b/core/src/plugins/access.ajxp_user/i18n/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "title"=> "My Account", diff --git a/core/src/plugins/access.ajxp_user/i18n/es.php b/core/src/plugins/access.ajxp_user/i18n/es.php index 811c644358..1eb48a8139 100644 --- a/core/src/plugins/access.ajxp_user/i18n/es.php +++ b/core/src/plugins/access.ajxp_user/i18n/es.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ // spanish translation: Ion Rey Bakaikoa , 2010 // spanish corrections: Cristóbal Sabroe Yde , 2010 diff --git a/core/src/plugins/access.ajxp_user/i18n/fr.php b/core/src/plugins/access.ajxp_user/i18n/fr.php index f7320120de..7e530f2e56 100644 --- a/core/src/plugins/access.ajxp_user/i18n/fr.php +++ b/core/src/plugins/access.ajxp_user/i18n/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "title" => "Mon Compte", diff --git a/core/src/plugins/access.ajxp_user/i18n/hu.php b/core/src/plugins/access.ajxp_user/i18n/hu.php index 1540433393..8adce4a5b2 100644 --- a/core/src/plugins/access.ajxp_user/i18n/hu.php +++ b/core/src/plugins/access.ajxp_user/i18n/hu.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ // hungarian translation: Gyarmati Balázs // updates: Levente Huszkó diff --git a/core/src/plugins/access.ajxp_user/i18n/it.php b/core/src/plugins/access.ajxp_user/i18n/it.php index 6a4cf6cd32..2b13caeccb 100644 --- a/core/src/plugins/access.ajxp_user/i18n/it.php +++ b/core/src/plugins/access.ajxp_user/i18n/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "title"=> "Bacheca", diff --git a/core/src/plugins/access.ajxp_user/i18n/pt.php b/core/src/plugins/access.ajxp_user/i18n/pt.php index 798bb133c5..6c365e6c76 100644 --- a/core/src/plugins/access.ajxp_user/i18n/pt.php +++ b/core/src/plugins/access.ajxp_user/i18n/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "1" => "Utilizadores partilhados", diff --git a/core/src/plugins/access.ajxp_user/i18n/ru.php b/core/src/plugins/access.ajxp_user/i18n/ru.php index 008ddfafaf..b2f0d65a61 100644 --- a/core/src/plugins/access.ajxp_user/i18n/ru.php +++ b/core/src/plugins/access.ajxp_user/i18n/ru.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "title"=> "Настройки", diff --git a/core/src/plugins/access.ajxp_user/i18n/si.php b/core/src/plugins/access.ajxp_user/i18n/si.php index b0399aa3f8..aad6aa7a4f 100644 --- a/core/src/plugins/access.ajxp_user/i18n/si.php +++ b/core/src/plugins/access.ajxp_user/i18n/si.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ // Slovenian translation: April 21 2011 by Vladimir Bohinc (vladimir.bohinc@gmail.com) $mess=array( diff --git a/core/src/plugins/access.ajxp_user/manifest.xml b/core/src/plugins/access.ajxp_user/manifest.xml index 08dbb6e6d5..4002e4c377 100644 --- a/core/src/plugins/access.ajxp_user/manifest.xml +++ b/core/src/plugins/access.ajxp_user/manifest.xml @@ -6,6 +6,7 @@ + @@ -15,140 +16,24 @@ - - - ]]> - - - ]]> - -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      - - - ]]>
      - div.flat_tabulator span.toggleHeader{ - background-color: #fb725c; - } - .largeButton, .SF_input.SF_inlineButton{ - background-color: #fb725c; - } - #account_pane .action_bar{ - text-align: right; - } - .action_bar a{ - cursor: pointer; - } - div.class-FetchedResultPane{ - background-color: #f8f8f8; - } - .action_bar, #buttons_bar{ - background-color: #f8f8f8; - height: 32px; - border-bottom: 1px solid rgba(0,0,0,0.08); - padding:18px 14px 12px; - text-align: right; - } - .action_bar span.actionbar_button_label{ - font-size: 11px; - } - .flat_tabulator div.panelHeader.tabulatorContainer{ - background-color: #30383A; - padding-top: 10px; - } -
      AJXP_MESSAGE[user_dash.53] AJXP_MESSAGE[user_dash.title]
      -
      -
      -
      -
      -
      -

      AJXP_MESSAGE[user_dash.43]

      -
      -
      +
      +
      ]]> -
      -

      AJXP_MESSAGE[user_dash.46]

      +
      +

      AJXP_MESSAGE[user_dash.46]
      AJXP_MESSAGE[user_dash.49]

      +
      +
      ]]> @@ -232,5 +117,5 @@
      - + diff --git a/core/src/plugins/access.ajxp_user/package.json b/core/src/plugins/access.ajxp_user/package.json new file mode 100644 index 0000000000..9166966cac --- /dev/null +++ b/core/src/plugins/access.ajxp_user/package.json @@ -0,0 +1,19 @@ +{ + "name": "access.ajxp_user", + "version": "6.5.1", + "description": "User account panels", + "source_path":"react", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Abstrium SAS", + "license": "agpl", + "devDependencies": { + "grunt": "~0.4.5", + "grunt-babel": "~5.0.3", + "grunt-contrib-watch": "~0.6.1", + "assemble-less": "~0.7.0", + "less-plugin-autoprefix": "~1.5.1" + } +} diff --git a/core/src/plugins/access.ajxp_user/react/UserAccount.js b/core/src/plugins/access.ajxp_user/react/UserAccount.js new file mode 100644 index 0000000000..fb2f7bd6dc --- /dev/null +++ b/core/src/plugins/access.ajxp_user/react/UserAccount.js @@ -0,0 +1,293 @@ +(function(global){ + + var ProfilePane = React.createClass({ + + componentDidMount: function(){ + this.props.pydio.UI.disableAllKeyBindings(); + }, + componentWillUnmount: function(){ + this.props.pydio.UI.enableAllKeyBindings(); + }, + + getInitialState: function(){ + let objValues = {}; + let pydio = this.props.pydio; + pydio.user.preferences.forEach(function(v, k){ + if(k === 'gui_preferences') return; + objValues[k] = v; + }); + return { + definitions:PydioForm.Manager.parseParameters(pydio.getXmlRegistry(), "user/preferences/pref[@exposed='true']|//param[contains(@scope,'user') and @expose='true']"), + values:objValues, + originalValues:LangUtils.deepCopy(objValues), + dirty: false + }; + }, + + onFormChange: function(newValues, dirty, removeValues){ + this.setState({dirty: dirty, values: newValues}); + }, + + getButton: function(actionName, messageId){ + let pydio = this.props.pydio; + if(!pydio.Controller.getActionByName(actionName)){ + return null; + } + let func = function(){ + pydio.Controller.fireAction(actionName); + }; + return ( + + ); + }, + + revert: function(){ + this.setState({ + values: LangUtils.deepCopy(this.state.originalValues) + }); + }, + + saveForm: function(){ + if(!this.state.dirty){ + this.setState({dirty: false}); + return; + } + let pydio = this.props.pydio; + let postValues = PydioForm.Manager.getValuesForPOST(this.state.definitions, this.state.values, 'PREFERENCES_'); + postValues['get_action'] = 'custom_data_edit'; + PydioApi.getClient().request(postValues, function(transport){ + PydioApi.getClient().parseXmlMessage(transport.responseXML); + global.document.observeOnce("ajaxplorer:registry_part_loaded", function(event){ + if(event.memo != "user/preferences") return; + pydio.Registry.logXmlUser(false); + }); + pydio.loadXmlRegistry(false, "user/preferences"); + this.setState({dirty: false}); + }); + }, + + render: function(){ + let pydio = this.props.pydio; + + let saveButton = ; + return ( +
      +
      +

      + {pydio.MessageHash['user_dash.43']} +
      {pydio.MessageHash['user_dash.43t']}
      +

      +
      + {saveButton}   + {this.getButton('pass_change', 194)} +
      +
      + +
      + ); + } + + }); + + var WebDAVURL = React.createClass({ + + propTypes:{ + url: React.PropTypes.string, + label: React.PropTypes.string, + getMessage: React.PropTypes.func + }, + + componentDidUpdate: function(prevProps, prevState){ + this.attachClipboard(); + }, + + componentDidMount: function(){ + this.attachClipboard(); + }, + + componentWillUnmount: function(){ + this.detachClipboard(); + }, + + detachClipboard: function(){ + if(this._clip){ + this._clip.destroy(); + } + }, + + clearCopyMessage:function(){ + global.setTimeout(function(){ + this.setState({copyMessage:''}); + }.bind(this), 5000); + }, + + attachClipboard: function(){ + this.detachClipboard(); + if(this.refs['copy-button']){ + this._clip = new Clipboard(this.refs['copy-button'].getDOMNode(), { + text: function(trigger) { + return this.props.url; + }.bind(this) + }); + this._clip.on('success', function(){ + this.setState({copyMessage:this.props.getMessage('share_center.192')}, this.clearCopyMessage); + }.bind(this)); + this._clip.on('error', function(){ + var copyMessage; + if( global.navigator.platform.indexOf("Mac") === 0 ){ + copyMessage = this.props.getMessage('share_center.144'); + }else{ + copyMessage = this.props.getMessage('share_center.143'); + } + this.setState({copyMessage:copyMessage}, this.clearCopyMessage); + }.bind(this)); + } + }, + + render: function(){ + let copy; + if(this.state && this.state.copyMessage){ + copy =
      {this.state.copyMessage}
      ; + } + return ( +
      +
      + + +
      + {copy} +
      + ); + } + + }); + + var WebDAVPane = React.createClass({ + + componentDidMount: function(){ + this.props.pydio.UI.disableAllKeyBindings(); + this.loadPrefs(); + }, + componentWillUnmount: function(){ + this.props.pydio.UI.enableAllKeyBindings(); + }, + getMessage: function(id){ + return this.props.pydio.MessageHash[id]; + }, + onToggleChange: function(event, newValue){ + PydioApi.getClient().request({ + get_action : 'webdav_preferences', + activate : newValue ? "true":"false" + }, function(t){ + this.setState({preferences: t.responseJSON}); + this.props.pydio.displayMessage("SUCCESS", this.props.pydio.MessageHash[newValue?408:409]); + }.bind(this)); + }, + savePassword: function(event){ + PydioApi.getClient().request({ + get_action : 'webdav_preferences', + webdav_pass: this.refs['passfield'].getValue() + }, function(t){ + this.setState({preferences: t.responseJSON}); + this.props.pydio.displayMessage("SUCCESS", this.props.pydio.MessageHash[410]); + }.bind(this)); + }, + loadPrefs: function(){ + if(!this.isMounted()) return; + PydioApi.getClient().request({ + get_action:'webdav_preferences' + }, function(t){ + this.setState({preferences: t.responseJSON}); + }.bind(this)); + }, + + renderPasswordField: function(){ + + if(this.state.preferences.digest_set || !this.state.preferences.webdav_force_basic){ + return null; + } + return ( +
      +     + +
      {this.getMessage(407)}
      +
      + ); + }, + + renderURLList: function(){ + + let base = this.state.preferences.webdav_base_url; + let userRepos = this.props.pydio.user.getRepositoriesList(); + let webdavRepos = this.state.preferences.webdav_repositories; + let otherUrls = []; + if(!!this.state.toggler){ + userRepos.forEach(function(repo, key){ + if(!webdavRepos[key]) return; + otherUrls.push(); + }.bind(this)); + } + + let toggler = function(){ + this.setState({toggler: !this.state.toggler}); + }.bind(this); + + return ( +
      +

      WebDAV shares adresses

      +
      {this.getMessage(405)}
      + +
      + +
      + {otherUrls} +
      + ); + + }, + render: function(){ + let webdavActive = this.state && this.state.preferences.webdav_active; + return ( +
      +
      +

      + {this.getMessage(403)} +
      {this.getMessage(404)}
      +

      +
      +
      + +
      + {webdavActive ? this.renderPasswordField() : null} + {webdavActive ? this.renderURLList() : null} +
      +
      +
      + ); + } + + }); + + let ns = global.UserAccount || {}; + ns.ProfilePane = ProfilePane; + ns.WebDAVPane = WebDAVPane; + global.UserAccount = ns; + + +})(window); \ No newline at end of file diff --git a/core/src/plugins/access.demo/DemoAccessDriver.php b/core/src/plugins/access.demo/DemoAccessDriver.php new file mode 100644 index 0000000000..27d28497b5 --- /dev/null +++ b/core/src/plugins/access.demo/DemoAccessDriver.php @@ -0,0 +1,88 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + * + */ +namespace Pydio\Access\Driver\StreamProvider\FS; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Access\Core\Model\Repository; + +use Pydio\Core\Exception\PydioException; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * @package AjaXplorer_Plugins + * @subpackage Access + * @class demoAccessDriver + * Plugin to access a filesystem with all write actions disabled + */ +class DemoAccessDriver extends FsAccessDriver +{ + /** + * @var Repository + */ + public $repository; + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @throws PydioException + * @throws \Exception + * @return array|void + */ + public function switchAction(ServerRequestInterface &$request, ResponseInterface &$response) + { + $errorMessage = "This is a demo, all 'write' actions are disabled!"; + switch ($request->getAttribute("action")) { + //------------------------------------ + // WRITE ACTIONS + //------------------------------------ + case "put_content": + case "copy": + case "move": + case "rename": + case "delete": + case "mkdir": + case "mkfile": + case "chmod": + case "compress": + throw new PydioException($errorMessage); + break; + + //------------------------------------ + // UPLOAD + //------------------------------------ + case "upload": + + return array("ERROR" => array("CODE" => "", "MESSAGE" => $errorMessage)); + + break; + + default: + break; + } + + return parent::switchAction($request, $response); + + } + +} diff --git a/core/src/plugins/access.demo/class.demoAccessDriver.php b/core/src/plugins/access.demo/class.demoAccessDriver.php deleted file mode 100644 index 6f858c13cd..0000000000 --- a/core/src/plugins/access.demo/class.demoAccessDriver.php +++ /dev/null @@ -1,73 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - * - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * @package AjaXplorer_Plugins - * @subpackage Access - * @class demoAccessDriver - * AJXP_Plugin to access a filesystem with all write actions disabled - */ -class demoAccessDriver extends fsAccessDriver -{ - /** - * @var Repository - */ - public $repository; - - public function switchAction($action, $httpVars, $fileVars) - { - $errorMessage = "This is a demo, all 'write' actions are disabled!"; - switch ($action) { - //------------------------------------ - // WRITE ACTIONS - //------------------------------------ - case "put_content": - case "copy": - case "move": - case "rename": - case "delete": - case "mkdir": - case "mkfile": - case "chmod": - case "compress": - return AJXP_XMLWriter::sendMessage(null, $errorMessage, false); - break; - - //------------------------------------ - // UPLOAD - //------------------------------------ - case "upload": - - return array("ERROR" => array("CODE" => "", "MESSAGE" => $errorMessage)); - - break; - - default: - break; - } - - return parent::switchAction($action, $httpVars, $fileVars); - - } - -} diff --git a/core/src/plugins/access.demo/i18n/conf/cs.php b/core/src/plugins/access.demo/i18n/conf/cs.php index c29ba37360..8cae5c1de3 100644 --- a/core/src/plugins/access.demo/i18n/conf/cs.php +++ b/core/src/plugins/access.demo/i18n/conf/cs.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Demo (no write actions)" => "Demo (žádné akce zápisu)", diff --git a/core/src/plugins/access.demo/i18n/conf/de.php b/core/src/plugins/access.demo/i18n/conf/de.php index a6de32a923..d6d3f53c6e 100644 --- a/core/src/plugins/access.demo/i18n/conf/de.php +++ b/core/src/plugins/access.demo/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Demo (no write actions)" => "Demo (ohne Schreibrechte)", diff --git a/core/src/plugins/access.demo/i18n/conf/en.php b/core/src/plugins/access.demo/i18n/conf/en.php index 1f7590de7f..e1f72698a0 100644 --- a/core/src/plugins/access.demo/i18n/conf/en.php +++ b/core/src/plugins/access.demo/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Demo (no write actions)" => "Demo (no write actions)", diff --git a/core/src/plugins/access.demo/i18n/conf/fr.php b/core/src/plugins/access.demo/i18n/conf/fr.php index ac0354b625..8ef8fcbce3 100644 --- a/core/src/plugins/access.demo/i18n/conf/fr.php +++ b/core/src/plugins/access.demo/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Demo (no write actions)" => "Demo (pas d'écriture)", diff --git a/core/src/plugins/access.demo/i18n/conf/it.php b/core/src/plugins/access.demo/i18n/conf/it.php index 5eb5f466f8..c66e4ca50f 100644 --- a/core/src/plugins/access.demo/i18n/conf/it.php +++ b/core/src/plugins/access.demo/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Demo (no write actions)" => "Demo (nessuna azione di scrittura)", diff --git a/core/src/plugins/access.demo/manifest.xml b/core/src/plugins/access.demo/manifest.xml index a347853a2a..75892cb23c 100644 --- a/core/src/plugins/access.demo/manifest.xml +++ b/core/src/plugins/access.demo/manifest.xml @@ -14,7 +14,7 @@ - + @@ -25,5 +25,5 @@ - + diff --git a/core/src/plugins/access.dropbox/class.dropboxAccessDriver.php b/core/src/plugins/access.dropbox/class.dropboxAccessDriver.php deleted file mode 100644 index ede0b3d783..0000000000 --- a/core/src/plugins/access.dropbox/class.dropboxAccessDriver.php +++ /dev/null @@ -1,151 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - * - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * AJXP_Plugin to access a dropbox account - * @package AjaXplorer_Plugins - * @subpackage Access - */ -class dropboxAccessDriver extends fsAccessDriver -{ - /** - * @var Repository - */ - public $repository; - public $driverConf; - protected $wrapperClassName; - protected $urlBase; - - public function initRepository() - { - if (is_array($this->pluginConf)) { - $this->driverConf = $this->pluginConf; - } else { - $this->driverConf = array(); - } - - $this->detectStreamWrapper(true); - $this->urlBase = "pydio://".$this->repository->getId(); - - if (!AJXP_Utils::searchIncludePath('HTTP/OAuth/Consumer.php')) { - $this->logError("Dropbox", "The PEAR HTTP_OAuth package must be installed!"); - return; - } - - $consumerKey = $this->repository->getOption("CONSUMER_KEY"); - $consumerSecret = $this->repository->getOption("CONSUMER_SECRET"); - $oauth = new Dropbox_OAuth_PEAR($consumerKey, $consumerSecret); - - // TOKENS IN SESSION? - if(!empty($_SESSION["OAUTH_DROPBOX_TOKENS"])) return; - - // TOKENS IN FILE ? - $tokens = $this->getTokens(); - if (!empty($tokens)) { - $_SESSION["OAUTH_DROPBOX_TOKENS"] = $tokens; - return; - } - - // OAUTH NEGOCIATION - if (isset($_SESSION['DROPBOX_NEGOCIATION_STATE'])) { - $state = $_SESSION['DROPBOX_NEGOCIATION_STATE']; - } else { - $state = 1; - } - switch ($state) { - - case 1 : - $tokens = $oauth->getRequestToken(); - //print_r($tokens); - - // Note that if you want the user to automatically redirect back, you can - // add the 'callback' argument to getAuthorizeUrl. - //echo "Step 2: You must now redirect the user to:\n"; - $_SESSION['DROPBOX_NEGOCIATION_STATE'] = 2; - $_SESSION['oauth_tokens'] = $tokens; - throw new AJXP_UserAlertException("Please go to getAuthorizeUrl()."\">".$oauth->getAuthorizeUrl()." to authorize the access to your dropbox. Then try again to switch to this workspace."); - - case 2 : - $oauth->setToken($_SESSION['oauth_tokens']); - try{ - $tokens = $oauth->getAccessToken(); - }catch(Exception $oauthEx){ - throw new AJXP_UserAlertException($oauthEx->getMessage() . ". Please go to getAuthorizeUrl()."\">".$oauth->getAuthorizeUrl()." to authorize the access to your dropbox. Then try again to switch to this workspace."); - } - $_SESSION['DROPBOX_NEGOCIATION_STATE'] = 3; - $_SESSION['OAUTH_DROPBOX_TOKENS'] = $tokens; - $this->setTokens($tokens); - return; - } - - throw new Exception("Impossible to find the dropbox tokens for accessing this workspace"); - - } - - public function performChecks() - { - if (!AJXP_Utils::searchIncludePath('HTTP/OAuth/Consumer.php')) { - throw new Exception("The PEAR HTTP_OAuth package must be installed!"); - } - } - - public function isWriteable($dir, $type = "dir") - { - return true; - } - - public function getTokens() - { - if($this->repository->getOption("DROPBOX_OAUTH_TOKENS") !== null && is_array($this->repository->getOption("DROPBOX_OAUTH_TOKENS"))){ - return $this->repository->getOption("DROPBOX_OAUTH_TOKENS"); - } - $repositoryId = $this->repository->getId(); - if(AuthService::usersEnabled()) { - $u = AuthService::getLoggedUser(); - $userId = $u->getId(); - if($u->getResolveAsParent()){ - $userId = $u->getParent(); - } - }else { - $userId = "shared"; - } - return AJXP_Utils::loadSerialFile(AJXP_DATA_PATH."/plugins/access.dropbox/".$repositoryId."_".$userId."_tokens"); - } - - public function setTokens($oauth_tokens) - { - $repositoryId = $this->repository->getId(); - if(AuthService::usersEnabled()) $userId = AuthService::getLoggedUser()->getId(); - else $userId = "shared"; - return AJXP_Utils::saveSerialFile(AJXP_DATA_PATH."/plugins/access.dropbox/".$repositoryId."_".$userId."_tokens", $oauth_tokens, true); - } - - public function makeSharedRepositoryOptions($httpVars, $repository) - { - $newOptions = parent::makeSharedRepositoryOptions($httpVars, $repository); - $newOptions["DROPBOX_OAUTH_TOKENS"] = $this->getTokens(); - return $newOptions; - } - - -} diff --git a/core/src/plugins/access.dropbox/class.dropboxWrapper.php b/core/src/plugins/access.dropbox/class.dropboxWrapper.php deleted file mode 100644 index 240d2a4781..0000000000 --- a/core/src/plugins/access.dropbox/class.dropboxWrapper.php +++ /dev/null @@ -1,301 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - * - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - - -require_once (AJXP_INSTALL_PATH.'/plugins/access.dropbox/dropbox-php/autoload.php'); -require_once (AJXP_BIN_FOLDER.'/interface.AjxpWrapper.php'); - -/** - * AjxpWrapper encapsulation the PHP Dropbox client - * @package AjaXplorer_Plugins - * @subpackage Access - */ -class dropboxWrapper implements AjxpWrapper -{ - /** - * - * @var Dropbox_API - */ - private static $dropbox; - private static $oauth; - - private static $crtDirContent = array(); - private static $crtDirIndex = 0; - - private static $crtHandle; - private static $crtTmpFile; - private static $crtWritePath; - - public function __construct() - { - } - - public function initPath($ajxpPath) - { - $repo = ConfService::getRepository(); - if (empty(self::$dropbox)) { - $consumerKey = $repo->getOption('CONSUMER_KEY'); - $consumerSecret = $repo->getOption('CONSUMER_SECRET'); - - self::$oauth = new Dropbox_OAuth_PEAR($consumerKey, $consumerSecret); - self::$oauth->setToken($_SESSION["OAUTH_DROPBOX_TOKENS"]); - self::$dropbox = new Dropbox_API(self::$oauth); - } - $basePath = $repo->getOption("PATH"); - if(empty($basePath)) $basePath = ""; - $parts = AJXP_Utils::safeParseUrl($ajxpPath); - $path = $basePath."/".ltrim($parts["path"], "/"); - - if($path == "") return "/"; - return $path; - } - - public static function staticInitPath($ajxpPath) - { - $tmpObject = new dropboxWrapper(); - return $tmpObject->initPath($ajxpPath); - } - - protected function metadataToStat($metaEntry) - { - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"Stating ", $metaEntry); - $mode = 0666; - if(intval($metaEntry["is_dir"]) == 1) $mode += 0040000; - else $mode += 0100000; - $time = strtotime($metaEntry["modified"]); - $size = intval($metaEntry["bytes"]); - $keys = array( - 'dev' => 0, - 'ino' => 0, - 'mode' => $mode, - 'nlink' => 0, - 'uid' => 0, - 'gid' => 0, - 'rdev' => 0, - 'size' => $size, - 'atime' => $time, - 'mtime' => $time, - 'ctime' => $time, - 'blksize' => 0, - 'blocks' => 0 - ); - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"Stat value", $keys); - return $keys; - } - - public static function copyFileInStream($path, $stream) - { - $path = self::staticInitPath($path); - $data = self::$dropbox->getFile($path); - fwrite($stream, $data, strlen($data)); - } - - public static function isRemote() - { - return true; - } - - public static function getRealFSReference($path, $persistent = false) - { - $tmpFile = AJXP_Utils::getAjxpTmpDir()."/".rand(); - $path = self::staticInitPath($path); - file_put_contents($tmpFile, self::$dropbox->getFile($path)); - return $tmpFile; - } - - public static function changeMode($path, $chmodValue) - { - } - - - public function rename($path_from, $path_to) - { - $path1 = $this->initPath($path_from); - $path2 = $this->initPath($path_to); - self::$dropbox->copy($path1, $path2); - self::$dropbox->delete($path1); - } - - public function mkdir($path, $mode, $options) - { - $path = $this->initPath($path); - try { - self::$dropbox->createFolder($path); - } catch (Dropbox_Exception $e) { - return false; - } - return true; - } - - public function rmdir($path, $options) - { - $path = $this->initPath($path); - try { - self::$dropbox->delete($path); - } catch (Dropbox_Exception $e) { - return false; - } - return true; - } - - public function unlink($path) - { - $path = $this->initPath($path); - try { - self::$dropbox->delete($path); - } catch (Dropbox_Exception $e) { - return false; - } - return true; - } - - public function url_stat($path, $flags) - { - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"STATING $path"); - $path = $this->initPath($path); - $meta = null; - if (self::$crtDirContent != null) { - foreach (self::$crtDirContent as $metaEntry) { - if ($metaEntry["path"] == $path) { - $metaEntry = $meta; - break; - } - } - } - if (empty($meta)) { - try { - $meta = self::$dropbox->getMetaData($path); - } catch (Dropbox_Exception_NotFound $nf) { - return false; - } - } - return $this->metadataToStat($meta); - } - - public function dir_opendir($path, $options) - { - $path = $this->initPath($path); - $metadata = self::$dropbox->getMetaData($path); - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"CONTENT for $path", $metadata); - self::$crtDirContent = $metadata["contents"]; - if (!is_array(self::$crtDirContent)) { - return false; - } - return true; - } - - public function dir_readdir() - { - //return false; - if(self::$crtDirIndex == count(self::$crtDirContent)) return false; - $meta = self::$crtDirContent[self::$crtDirIndex]; - self::$crtDirIndex ++; - return basename($meta["path"]); - } - - public function dir_rewinddir() - { - self::$crtDirIndex = 0; - } - - public function dir_closedir() - { - self::$crtDirContent = array(); - self::$crtDirIndex = 0; - } - - - public function stream_flush() - { - return fflush(self::$crtHandle); - } - - public function stream_read($count) - { - return fread(self::$crtHandle, $count); - } - - public function stream_seek($offset, $whence = SEEK_SET) - { - return fseek(self::$crtHandle, $offset, $whence); - } - - public function stream_write($data) - { - return fwrite(self::$crtHandle, $data); - } - - public function stream_close() - { - $res = fclose(self::$crtHandle); - if (self::$crtWritePath != null) { - $path = $this->initPath(self::$crtWritePath); - try { - $postRes = self::$dropbox->putFile($path, self::$crtTmpFile); - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"Post to $path succeeded:"); - } catch (Dropbox_Exception $dE) { - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"Post to $path failed :".$dE->getMessage()); - } - } - unlink(self::$crtTmpFile); - return $res; - } - - public function stream_tell() - { - return ftell(self::$crtHandle); - } - - public function stream_eof() - { - return feof(self::$crtHandle); - } - - public function stream_stat() - { - return true; - } - - public function stream_open($path, $mode, $options, &$opened_path) - { - if (strstr($mode, "r") !== false) { - self::$crtTmpFile = self::getRealFSReference($path); - self::$crtWritePath = null; - } else { - self::$crtTmpFile = AJXP_Utils::getAjxpTmpDir()."/".rand(); - self::$crtWritePath = $path; - } - self::$crtHandle = fopen(self::$crtTmpFile, $mode); - return true; - } - - /** - * Describe whether the current wrapper can rewind a stream or not. - * @static - * @return boolean - */ - public static function isSeekable($url) - { - return true; - } -} diff --git a/core/src/plugins/access.dropbox/composer.json b/core/src/plugins/access.dropbox/composer.json new file mode 100644 index 0000000000..17e1019122 --- /dev/null +++ b/core/src/plugins/access.dropbox/composer.json @@ -0,0 +1,11 @@ +{ + "name": "AccessDropBox", + "description": "Pydio DropBox access wrapper", + "homepage": "http://pydio.com/", + "autoload": { + "psr-4": { + "Pydio\\Access\\Core\\": "../core.access/src", + "Pydio\\Access\\DropBox\\": "./src/" + } + } +} diff --git a/core/src/plugins/access.dropbox/dropbox-php/API.php b/core/src/plugins/access.dropbox/dropbox-php/API.php deleted file mode 100644 index e52f1a8306..0000000000 --- a/core/src/plugins/access.dropbox/dropbox-php/API.php +++ /dev/null @@ -1,346 +0,0 @@ -oauth = $oauth; - $this->root = $root; - $this->useSSL = $useSSL; - if (!$this->useSSL) - { - throw new Dropbox_Exception('Dropbox REST API now requires that all requests use SSL'); - } - - } - - /** - * Returns OAuth tokens based on an email address and passwords - * - * This can be used to bypass the regular oauth workflow. - * - * This method returns an array with 2 elements: - * * token - * * secret - * - * @param string $email - * @param string $password - * @deprecated This method is no longer supported - * @return array - */ - public function getToken($email, $password) { - - throw new Dropbox_Exception('This API method is deprecated as of the version 1 API'); - - } - - /** - * Returns information about the current dropbox account - * - * @return stdclass - */ - public function getAccountInfo() { - - $data = $this->oauth->fetch($this->api_url . 'account/info'); - return json_decode($data['body'],true); - - } - - /** - * Creates a new Dropbox account - * - * @param string $email - * @param string $first_name - * @param string $last_name - * @param string $password - * @deprecated This method is no longer supported - * @return bool - */ - public function createAccount($email, $first_name, $last_name, $password) { - - throw new Dropbox_Exception('This API method is deprecated as of the version 1 API'); - - } - - - /** - * Returns a file's contents - * - * @param string $path path - * @param string $root Use this to override the default root path (sandbox/dropbox) - * @return string - */ - public function getFile($path = '', $root = null) { - - if (is_null($root)) $root = $this->root; - $path = str_replace(array('%2F','~'), array('/','%7E'), rawurlencode($path)); - $result = $this->oauth->fetch($this->api_content_url . 'files/' . $root . '/' . ltrim($path,'/')); - return $result['body']; - - } - - /** - * Uploads a new file - * - * @param string $path Target path (including filename) - * @param string $file Either a path to a file or a stream resource - * @param string $root Use this to override the default root path (sandbox/dropbox) - * @return bool - */ - public function putFile($path, $file, $root = null) { - - $directory = dirname($path); - $filename = basename($path); - - if($directory==='.') $directory = ''; - if (is_null($root)) $root = $this->root; - - if (is_string($file)) { - - $file = fopen($file,'r'); - - } elseif (!is_resource($file)) { - throw new Dropbox_Exception('File must be a file-resource or a string'); - } - $result=$this->multipartFetch($this->api_content_url . 'files/' . - $root . '/' . trim($directory,'/'), $file, $filename); - - if(!isset($result["httpStatus"]) || $result["httpStatus"] != 200) - throw new Dropbox_Exception("Uploading file to Dropbox failed"); - - return true; - } - - - /** - * Copies a file or directory from one location to another - * - * This method returns the file information of the newly created file. - * - * @param string $from source path - * @param string $to destination path - * @param string $root Use this to override the default root path (sandbox/dropbox) - * @return stdclass - */ - public function copy($from, $to, $root = null) { - - if (is_null($root)) $root = $this->root; - $response = $this->oauth->fetch($this->api_url . 'fileops/copy', array('from_path' => $from, 'to_path' => $to, 'root' => $root)); - - return json_decode($response['body'],true); - - } - - /** - * Creates a new folder - * - * This method returns the information from the newly created directory - * - * @param string $path - * @param string $root Use this to override the default root path (sandbox/dropbox) - * @return stdclass - */ - public function createFolder($path, $root = null) { - - if (is_null($root)) $root = $this->root; - - // Making sure the path starts with a / - $path = '/' . ltrim($path,'/'); - - $response = $this->oauth->fetch($this->api_url . 'fileops/create_folder', array('path' => $path, 'root' => $root),'POST'); - return json_decode($response['body'],true); - - } - - /** - * Deletes a file or folder. - * - * This method will return the metadata information from the deleted file or folder, if successful. - * - * @param string $path Path to new folder - * @param string $root Use this to override the default root path (sandbox/dropbox) - * @return array - */ - public function delete($path, $root = null) { - - if (is_null($root)) $root = $this->root; - $response = $this->oauth->fetch($this->api_url . 'fileops/delete', array('path' => $path, 'root' => $root)); - return json_decode($response['body']); - - } - - /** - * Moves a file or directory to a new location - * - * This method returns the information from the newly created directory - * - * @param mixed $from Source path - * @param mixed $to destination path - * @param string $root Use this to override the default root path (sandbox/dropbox) - * @return stdclass - */ - public function move($from, $to, $root = null) { - - if (is_null($root)) $root = $this->root; - $response = $this->oauth->fetch($this->api_url . 'fileops/move', array('from_path' => rawurldecode($from), 'to_path' => rawurldecode($to), 'root' => $root)); - - return json_decode($response['body'],true); - - } - - /** - * Returns a list of links for a directory - * - * The links can be used to securely open files throug a browser. The links are cookie protected - * so a user is asked to login if there's no valid session cookie. - * - * @param string $path Path to directory or file - * @param string $root Use this to override the default root path (sandbox/dropbox) - * @deprecated This method is no longer supported - * @return array - */ - public function getLinks($path, $root = null) { - - throw new Dropbox_Exception('This API method is currently broken, and dropbox documentation about this is no longer online. Please ask Dropbox support if you really need this.'); - - /* - if (is_null($root)) $root = $this->root; - - $response = $this->oauth->fetch($this->api_url . 'links/' . $root . '/' . ltrim($path,'/')); - return json_decode($response,true); - */ - - } - - /** - * Returns file and directory information - * - * @param string $path Path to receive information from - * @param bool $list When set to true, this method returns information from all files in a directory. When set to false it will only return infromation from the specified directory. - * @param string $hash If a hash is supplied, this method simply returns true if nothing has changed since the last request. Good for caching. - * @param int $fileLimit Maximum number of file-information to receive - * @param string $root Use this to override the default root path (sandbox/dropbox) - * @return array|true - */ - public function getMetaData($path, $list = true, $hash = null, $fileLimit = null, $root = null) { - - if (is_null($root)) $root = $this->root; - - $args = array( - 'list' => $list, - ); - - if (!is_null($hash)) $args['hash'] = $hash; - if (!is_null($fileLimit)) $args['file_limit'] = $fileLimit; - - $path = str_replace(array('%2F','~'), array('/','%7E'), rawurlencode($path)); - $response = $this->oauth->fetch($this->api_url . 'metadata/' . $root . '/' . ltrim($path,'/'), $args); - - /* 304 is not modified */ - if ($response['httpStatus']==304) { - return true; - } else { - return json_decode($response['body'],true); - } - - } - - /** - * Returns a thumbnail (as a string) for a file path. - * - * @param string $path Path to file - * @param string $size small, medium or large - * @param string $root Use this to override the default root path (sandbox/dropbox) - * @return string - */ - public function getThumbnail($path, $size = 'small', $root = null) { - - if (is_null($root)) $root = $this->root; - $response = $this->oauth->fetch($this->api_content_url . 'thumbnails/' . $root . '/' . ltrim($path,'/'),array('size' => $size)); - - return $response['body']; - - } - - /** - * This method is used to generate multipart POST requests for file upload - * - * @param string $uri - * @param array $arguments - * @return bool - */ - protected function multipartFetch($uri, $file, $filename) { - - /* random string */ - $boundary = 'R50hrfBj5JYyfR3vF3wR96GPCC9Fd2q2pVMERvEaOE3D8LZTgLLbRpNwXek3'; - - $headers = array( - 'Content-Type' => 'multipart/form-data; boundary=' . $boundary, - ); - - $body="--" . $boundary . "\r\n"; - $body.="Content-Disposition: form-data; name=file; filename=".rawurldecode($filename)."\r\n"; - $body.="Content-type: application/octet-stream\r\n"; - $body.="\r\n"; - $body.=stream_get_contents($file); - $body.="\r\n"; - $body.="--" . $boundary . "--"; - - // Dropbox requires the filename to also be part of the regular arguments, so it becomes - // part of the signature. - $uri.='?file=' . $filename; - - return $this->oauth->fetch($uri, $body, 'POST', $headers); - - } - - -} diff --git a/core/src/plugins/access.dropbox/dropbox-php/Exception.php b/core/src/plugins/access.dropbox/dropbox-php/Exception.php deleted file mode 100644 index 50cbc4c791..0000000000 --- a/core/src/plugins/access.dropbox/dropbox-php/Exception.php +++ /dev/null @@ -1,15 +0,0 @@ -oauth_token = $token['token']; - $this->oauth_token_secret = $token['token_secret']; - } else { - $this->oauth_token = $token; - $this->oauth_token_secret = $token_secret; - } - - } - - /** - * Returns the oauth request tokens as an associative array. - * - * The array will contain the elements 'token' and 'token_secret'. - * - * @return array - */ - public function getToken() { - - return array( - 'token' => $this->oauth_token, - 'token_secret' => $this->oauth_token_secret, - ); - - } - - /** - * Returns the authorization url - * - * @param string $callBack Specify a callback url to automatically redirect the user back - * @return string - */ - public function getAuthorizeUrl($callBack = null) { - - // Building the redirect uri - $token = $this->getToken(); - $uri = self::URI_AUTHORIZE . '?oauth_token=' . $token['token']; - if ($callBack) $uri.='&oauth_callback=' . $callBack; - return $uri; - } - - /** - * Fetches a secured oauth url and returns the response body. - * - * @param string $uri - * @param mixed $arguments - * @param string $method - * @param array $httpHeaders - * @return string - */ - public abstract function fetch($uri, $arguments = array(), $method = 'GET', $httpHeaders = array()); - - /** - * Requests the OAuth request token. - * - * @return array - */ - abstract public function getRequestToken(); - - /** - * Requests the OAuth access tokens. - * - * @return array - */ - abstract public function getAccessToken(); - -} diff --git a/core/src/plugins/access.dropbox/dropbox-php/OAuth/PEAR.php b/core/src/plugins/access.dropbox/dropbox-php/OAuth/PEAR.php deleted file mode 100644 index a33f044a51..0000000000 --- a/core/src/plugins/access.dropbox/dropbox-php/OAuth/PEAR.php +++ /dev/null @@ -1,198 +0,0 @@ -consumerRequest instanceof HTTP_OAuth_Consumer_Request) { - $this->consumerRequest = new HTTP_OAuth_Consumer_Request; - } - - $this->consumerRequest->setConfig(array( - 'ssl_verify_peer' => false, - 'ssl_verify_host' => false - )); - - return $this->consumerRequest; - } -} - -/** - * This class is used to sign all requests to dropbox - * - * This classes use the PEAR HTTP_OAuth package. Make sure this is installed. - */ -class Dropbox_OAuth_PEAR extends Dropbox_OAuth { - - /** - * OAuth object - * - * @var OAuth - */ - protected $oAuth; - - /** - * OAuth consumer key - * - * We need to keep this around for later. - * - * @var string - */ - protected $consumerKey; - - /** - * Constructor - * - * @param string $consumerKey - * @param string $consumerSecret - */ - public function __construct($consumerKey, $consumerSecret) { - - $this->OAuth = new HTTP_OAuth_Consumer_Dropbox($consumerKey, $consumerSecret); - $this->consumerKey = $consumerKey; - - } - - /** - * Sets the request token and secret. - * - * The tokens can also be passed as an array into the first argument. - * The array must have the elements token and token_secret. - * - * @param string|array $token - * @param string $token_secret - * @return void - */ - public function setToken($token, $token_secret = null) { - - parent::setToken($token,$token_secret); - $this->OAuth->setToken($this->oauth_token); - $this->OAuth->setTokenSecret($this->oauth_token_secret); - - } - - /** - * Fetches a secured oauth url and returns the response body. - * - * @param string $uri - * @param mixed $arguments - * @param string $method - * @param array $httpHeaders - * @return string - */ - public function fetch($uri, $arguments = array(), $method = 'GET', $httpHeaders = array()) - { - $httpRequest = new HTTP_Request2(null, - HTTP_Request2::METHOD_GET, - array( - 'ssl_verify_peer' => false, - 'ssl_verify_host' => false - ) - ); - - $consumerRequest = new HTTP_OAuth_Consumer_Request(); - $consumerRequest->accept($httpRequest); - $consumerRequest->setUrl($uri); - $consumerRequest->setMethod($method); - $consumerRequest->setSecrets($this->OAuth->getSecrets()); - - $parameters = array( - 'oauth_consumer_key' => $this->consumerKey, - 'oauth_signature_method' => 'HMAC-SHA1', - 'oauth_token' => $this->oauth_token, - ); - - - if (is_array($arguments)) { - $parameters = array_merge($parameters,$arguments); - } elseif (is_string($arguments)) { - $consumerRequest->setBody($arguments); - } - $consumerRequest->setParameters($parameters); - - - if (count($httpHeaders)) { - foreach($httpHeaders as $k=>$v) { - $consumerRequest->setHeader($k, $v); - } - } - - $response = $consumerRequest->send(); - - switch($response->getStatus()) { - - // Not modified - case 304 : - return array( - 'httpStatus' => 304, - 'body' => null, - ); - break; - case 403 : - throw new Dropbox_Exception_Forbidden('Forbidden. This could mean a bad OAuth request, or a file or folder already existing at the target location.'); - case 404 : - throw new Dropbox_Exception_NotFound('Resource at uri: ' . $uri . ' could not be found'); - case 507 : - throw new Dropbox_Exception_OverQuota('This dropbox is full'); - case 400 : - throw new Exception('Bad Request : '. $response->getBody()); - - } - - return array( - 'httpStatus' => $response->getStatus(), - 'body' => $response->getBody() - ); - - } - - /** - * Requests the OAuth request token. - * - * @return void - */ - public function getRequestToken() { - - $this->OAuth->getRequestToken(self::URI_REQUEST_TOKEN); - $this->setToken($this->OAuth->getToken(), $this->OAuth->getTokenSecret()); - return $this->getToken(); - - } - - /** - * Requests the OAuth access tokens. - * - * This method requires the 'unauthorized' request tokens - * and, if successful will set the authorized request tokens. - * - * @return void - */ - public function getAccessToken() { - - $this->OAuth->getAccessToken(self::URI_ACCESS_TOKEN); - $this->setToken($this->OAuth->getToken(), $this->OAuth->getTokenSecret()); - return $this->getToken(); - - } - - -} diff --git a/core/src/plugins/access.dropbox/dropbox-php/OAuth/PHP.php b/core/src/plugins/access.dropbox/dropbox-php/OAuth/PHP.php deleted file mode 100644 index 48ce66f103..0000000000 --- a/core/src/plugins/access.dropbox/dropbox-php/OAuth/PHP.php +++ /dev/null @@ -1,147 +0,0 @@ -OAuth = new OAuth($consumerKey, $consumerSecret,OAUTH_SIG_METHOD_HMACSHA1,OAUTH_AUTH_TYPE_URI); - $this->OAuth->enableDebug(); - - } - - /** - * Sets the request token and secret. - * - * The tokens can also be passed as an array into the first argument. - * The array must have the elements token and token_secret. - * - * @param string|array $token - * @param string $token_secret - * @return void - */ - public function setToken($token, $token_secret = null) { - - parent::setToken($token,$token_secret); - $this->OAuth->setToken($this->oauth_token, $this->oauth_token_secret); - - } - - - /** - * Fetches a secured oauth url and returns the response body. - * - * @param string $uri - * @param mixed $arguments - * @param string $method - * @param array $httpHeaders - * @return string - */ - public function fetch($uri, $arguments = array(), $method = 'GET', $httpHeaders = array()) { - - try { - $this->OAuth->fetch($uri, $arguments, $method, $httpHeaders); - $result = $this->OAuth->getLastResponse(); - $lastResponseInfo = $this->OAuth->getLastResponseInfo(); - return array( - 'httpStatus' => $lastResponseInfo['http_code'], - 'body' => $result, - ); - } catch (OAuthException $e) { - - $lastResponseInfo = $this->OAuth->getLastResponseInfo(); - switch($lastResponseInfo['http_code']) { - - // Not modified - case 304 : - return array( - 'httpStatus' => 304, - 'body' => null, - ); - break; - case 403 : - throw new Dropbox_Exception_Forbidden('Forbidden. This could mean a bad OAuth request, or a file or folder already existing at the target location.'); - case 404 : - throw new Dropbox_Exception_NotFound('Resource at uri: ' . $uri . ' could not be found'); - case 507 : - throw new Dropbox_Exception_OverQuota('This dropbox is full'); - default: - // rethrowing - throw $e; - } - - } - - } - - /** - * Requests the OAuth request token. - * - * @return void - */ - public function getRequestToken() { - - try { - - $tokens = $this->OAuth->getRequestToken(self::URI_REQUEST_TOKEN); - $this->setToken($tokens['oauth_token'], $tokens['oauth_token_secret']); - return $this->getToken(); - - } catch (OAuthException $e) { - - throw new Dropbox_Exception_RequestToken('We were unable to fetch request tokens. This likely means that your consumer key and/or secret are incorrect.',0,$e); - - } - - } - - - /** - * Requests the OAuth access tokens. - * - * This method requires the 'unauthorized' request tokens - * and, if successful will set the authorized request tokens. - * - * @return void - */ - public function getAccessToken() { - - $uri = self::URI_ACCESS_TOKEN; - $tokens = $this->OAuth->getAccessToken($uri); - $this->setToken($tokens['oauth_token'], $tokens['oauth_token_secret']); - return $this->getToken(); - - } - - -} diff --git a/core/src/plugins/access.dropbox/dropbox-php/OAuth/Wordpress.php b/core/src/plugins/access.dropbox/dropbox-php/OAuth/Wordpress.php deleted file mode 100644 index 66ee595fd9..0000000000 --- a/core/src/plugins/access.dropbox/dropbox-php/OAuth/Wordpress.php +++ /dev/null @@ -1,223 +0,0 @@ -consumerKey = $consumerKey; - $this->consumerSecret = $consumerSecret; - } - - /** - * Fetches a secured oauth url and returns the response body. - * - * @param string $uri - * @param mixed $arguments - * @param string $method - * @param array $httpHeaders - * @return string - */ - public function fetch($uri, $arguments = array(), $method = 'GET', $httpHeaders = array()) { - - $requestParams = array(); - - $requestParams['method'] = $method; - $oAuthHeader = $this->getOAuthHeader($uri, $arguments, $method); - $requestParams['headers'] = array_merge($httpHeaders, $oAuthHeader); - - // arguments will be passed to uri for GET, to body for POST etc. - if ($method == 'GET') { - $uri .= '?' . http_build_query($arguments); - } else { - if (count($arguments)) { - $requestParams['body'] = $arguments; - } - } - - $request = new WP_Http; - - //$uri = str_replace('api.dropbox.com', 'localhost:12346', $uri); - - $result = $request->request($uri, $requestParams); - - return array( - 'httpStatus' => $result['response']['code'], - 'body' => $result['body'], - ); - } - - /** - * Returns named array with oauth parameters for further use - * @return array Array with oauth_ parameters - */ - private function getOAuthBaseParams() { - $params['oauth_version'] = '1.0'; - $params['oauth_signature_method'] = 'HMAC-SHA1'; - - $params['oauth_consumer_key'] = $this->consumerKey; - $tokens = $this->getToken(); - if (isset($tokens['token']) && $tokens['token']) { - $params['oauth_token'] = $tokens['token']; - } - $params['oauth_timestamp'] = time(); - $params['oauth_nonce'] = md5(microtime() . mt_rand()); - return $params; - } - - /** - * Creates valid Authorization header for OAuth, based on URI and Params - * - * @param string $uri - * @param array $params - * @param string $method GET or POST, standard is GET - * @param array $oAuthParams optional, pass your own oauth_params here - * @return array Array for request's headers section like - * array('Authorization' => 'OAuth ...'); - */ - private function getOAuthHeader($uri, $params, $method = 'GET', $oAuthParams = null) { - $oAuthParams = $oAuthParams ? $oAuthParams : $this->getOAuthBaseParams(); - - // create baseString to encode for the sent parameters - $baseString = $method . '&'; - $baseString .= $this->oauth_urlencode($uri) . "&"; - - // OAuth header does not include GET-Parameters - $signatureParams = array_merge($params, $oAuthParams); - - // sorting the parameters - ksort($signatureParams); - - $encodedParams = array(); - foreach ($signatureParams as $key => $value) { - $encodedParams[] = $this->oauth_urlencode($key) . '=' . $this->oauth_urlencode($value); - } - - $baseString .= $this->oauth_urlencode(implode('&', $encodedParams)); - - // encode the signature - $tokens = $this->getToken(); - $hash = $this->hash_hmac_sha1($this->consumerSecret.'&'.$tokens['token_secret'], $baseString); - $signature = base64_encode($hash); - - // add signature to oAuthParams - $oAuthParams['oauth_signature'] = $signature; - - $oAuthEncoded = array(); - foreach ($oAuthParams as $key => $value) { - $oAuthEncoded[] = $key . '="' . $this->oauth_urlencode($value) . '"'; - } - - return array('Authorization' => 'OAuth ' . implode(', ', $oAuthEncoded)); - } - - /** - * Requests the OAuth request token. - * - * @return void - */ - public function getRequestToken() { - $result = $this->fetch(self::URI_REQUEST_TOKEN, array(), 'POST'); - if ($result['httpStatus'] == "200") { - $tokens = array(); - parse_str($result['body'], $tokens); - $this->setToken($tokens['oauth_token'], $tokens['oauth_token_secret']); - return $this->getToken(); - } else { - throw new Dropbox_Exception_RequestToken('We were unable to fetch request tokens. This likely means that your consumer key and/or secret are incorrect.'); - } - } - - /** - * Requests the OAuth access tokens. - * - * This method requires the 'unauthorized' request tokens - * and, if successful will set the authorized request tokens. - * - * @return void - */ - public function getAccessToken() { - $result = $this->fetch(self::URI_ACCESS_TOKEN, array(), 'POST'); - if ($result['httpStatus'] == "200") { - $tokens = array(); - parse_str($result['body'], $tokens); - $this->setToken($tokens['oauth_token'], $tokens['oauth_token_secret']); - return $this->getToken(); - } else { - throw new Dropbox_Exception_RequestToken('We were unable to fetch request tokens. This likely means that your consumer key and/or secret are incorrect.'); - } - } - - /** - * Helper function to properly urlencode parameters. - * See http://php.net/manual/en/function.oauth-urlencode.php - * - * @param string $string - * @return string - */ - private function oauth_urlencode($string) { - return str_replace('%E7', '~', rawurlencode($string)); - } - - /** - * Hash function for hmac_sha1; uses native function if available. - * - * @param string $key - * @param string $data - * @return string - */ - private function hash_hmac_sha1($key, $data) { - if (function_exists('hash_hmac') && in_array('sha1', hash_algos())) { - return hash_hmac('sha1', $data, $key, true); - } else { - $blocksize = 64; - $hashfunc = 'sha1'; - if (strlen($key) > $blocksize) { - $key = pack('H*', $hashfunc($key)); - } - - $key = str_pad($key, $blocksize, chr(0x00)); - $ipad = str_repeat(chr(0x36), $blocksize); - $opad = str_repeat(chr(0x5c), $blocksize); - $hash = pack('H*', $hashfunc(( $key ^ $opad ) . pack('H*', $hashfunc(($key ^ $ipad) . $data)))); - - return $hash; - } - } - -} \ No newline at end of file diff --git a/core/src/plugins/access.dropbox/dropbox-php/OAuth/Zend.php b/core/src/plugins/access.dropbox/dropbox-php/OAuth/Zend.php deleted file mode 100644 index 9cdf229f48..0000000000 --- a/core/src/plugins/access.dropbox/dropbox-php/OAuth/Zend.php +++ /dev/null @@ -1,244 +0,0 @@ - - * @license http://code.google.com/p/dropbox-php/wiki/License MIT - */ - -/** - * This class is used to sign all requests to dropbox - * - * This classes use the Zend_Oauth package. - */ -class Dropbox_OAuth_Zend extends Dropbox_OAuth { - - /** - * OAuth object - * - * @var Zend_Oauth_Consumer - */ - protected $oAuth; - /** - * OAuth consumer key - * - * We need to keep this around for later. - * - * @var string - */ - protected $consumerKey; - /** - * - * @var Zend_Oauth_Token - */ - protected $zend_oauth_token; - - /** - * Constructor - * - * @param string $consumerKey - * @param string $consumerSecret - */ - public function __construct($consumerKey, $consumerSecret) { - if (!class_exists('Zend_Oauth_Consumer')) { - // We're going to try to load in manually - include 'Zend/Oauth/Consumer.php'; - } - if (!class_exists('Zend_Oauth_Consumer')) - throw new Dropbox_Exception('The Zend_Oauth_Consumer class could not be found!'); - $this->OAuth = new Zend_Oauth_Consumer(array( - "consumerKey" => $consumerKey, - "consumerSecret" => $consumerSecret, - "requestTokenUrl" => self::URI_REQUEST_TOKEN, - "accessTokenUrl" => self::URI_ACCESS_TOKEN, - "authorizeUrl" => self::URI_AUTHORIZE, - "signatureMethod" => "HMAC-SHA1", - )); - $this->consumerKey = $consumerKey; - } - - /** - * Sets the request token and secret. - * - * The tokens can also be passed as an array into the first argument. - * The array must have the elements token and token_secret. - * - * @param string|array $token - * @param string $token_secret - * @return void - */ - public function setToken($token, $token_secret = null) { - if (is_a($token, "Zend_Oauth_Token")) { - if (is_a($token, "Zend_Oauth_Token_Access")) { - $this->OAuth->setToken($token); - } - $this->zend_oauth_token = $token; - return parent::setToken($token->getToken(), $token->getTokenSecret()); - } elseif (is_string($token) && is_null($token_secret)) { - return $this->setToken(unserialize($token)); - } elseif (isset($token['zend_oauth_token'])) { - return $this->setToken(unserialize($token['zend_oauth_token'])); - } else { - parent::setToken($token, $token_secret); - return; - } - } - - /** - * Fetches a secured oauth url and returns the response body. - * - * @param string $uri - * @param mixed $arguments - * @param string $method - * @param array $httpHeaders - * @return string - */ - public function fetch($uri, $arguments = array(), $method = 'GET', $httpHeaders = array()) { - $token = $this->OAuth->getToken(); - if (!is_a($token, "Zend_Oauth_Token")) { - if (is_a($this->zend_oauth_token, "Zend_Oauth_Token_Access")) { - $token = $this->zend_oauth_token; - } else { - $token = new Zend_Oauth_Token_Access(); - $token->setToken($this->oauth_token); - $token->setTokenSecret($this->oauth_token_secret); - } - } - /* @var $token Zend_Oauth_Token_Access */ - $oauthOptions = array( - 'consumerKey' => $this->consumerKey, - 'signatureMethod' => "HMAC-SHA1", - 'consumerSecret' => $this->OAuth->getConsumerSecret(), - ); - $config = array("timeout" => 15); - - /* @var $consumerRequest Zend_Oauth_Client */ - $consumerRequest = $token->getHttpClient($oauthOptions); - $consumerRequest->setMethod($method); - if (is_array($arguments)) { - $consumerRequest->setUri($uri); - if ($method == "GET") { - foreach ($arguments as $param => $value) { - $consumerRequest->setParameterGet($param, $value); - } - } else { - foreach ($arguments as $param => $value) { - $consumerRequest->setParameterPost($param, $value); - } - } - } elseif (is_string($arguments)) { - preg_match("/\?file=(.*)$/i", $uri, $matches); - if (isset($matches[1])) { - $uri = str_replace($matches[0], "", $uri); - $filename = $matches[1]; - $uri = Zend_Uri::factory($uri); - $uri->addReplaceQueryParameters(array("file" => $filename)); - $consumerRequest->setParameterGet("file", $filename); - } - $consumerRequest->setUri($uri); - $consumerRequest->setRawData($arguments); - } elseif (is_resource($arguments)) { - $consumerRequest->setUri($uri); - /** Placeholder for Oauth streaming support. */ - } - if (count($httpHeaders)) { - foreach ($httpHeaders as $k => $v) { - $consumerRequest->setHeaders($k, $v); - } - } - $response = $consumerRequest->request(); - $body = Zend_Json::decode($response->getBody()); - switch ($response->getStatus()) { - // Not modified - case 304 : - return array( - 'httpStatus' => 304, - 'body' => null, - ); - break; - case 403 : - throw new Dropbox_Exception_Forbidden('Forbidden. - This could mean a bad OAuth request, or a file or folder already existing at the target location. - ' . $body["error"] . "\n"); - case 404 : - throw new Dropbox_Exception_NotFound('Resource at uri: ' . $uri . ' could not be found. ' . - $body["error"] . "\n"); - case 507 : - throw new Dropbox_Exception_OverQuota('This dropbox is full. ' . - $body["error"] . "\n"); - } - - return array( - 'httpStatus' => $response->getStatus(), - 'body' => $response->getBody(), - ); - } - - /** - * Requests the OAuth request token. - * - * @return void - */ - public function getRequestToken() { - $token = $this->OAuth->getRequestToken(); - $this->setToken($token); - return $this->getToken(); - } - - /** - * Requests the OAuth access tokens. - * - * This method requires the 'unauthorized' request tokens - * and, if successful will set the authorized request tokens. - * - * @return void - */ - public function getAccessToken() { - if (is_a($this->zend_oauth_token, "Zend_Oauth_Token_Request")) { - $requestToken = $this->zend_oauth_token; - } else { - $requestToken = new Zend_Oauth_Token_Request(); - $requestToken->setToken($this->oauth_token); - $requestToken->setTokenSecret($this->oauth_token_secret); - } - $token = $this->OAuth->getAccessToken($_GET, $requestToken); - $this->setToken($token); - return $this->getToken(); - } - - /** - * Returns the oauth request tokens as an associative array. - * - * The array will contain the elements 'token' and 'token_secret' and the serialized - * Zend_Oauth_Token object. - * - * @return array - */ - public function getToken() { - //$token = $this->OAuth->getToken(); - //return serialize($token); - return array( - 'token' => $this->oauth_token, - 'token_secret' => $this->oauth_token_secret, - 'zend_oauth_token' => serialize($this->zend_oauth_token), - ); - } - - /** - * Returns the authorization url - * - * Overloading Dropbox_OAuth to use the built in functions in Zend_Oauth - * - * @param string $callBack Specify a callback url to automatically redirect the user back - * @return string - */ - public function getAuthorizeUrl($callBack = null) { - if ($callBack) - $this->OAuth->setCallbackUrl($callBack); - return $this->OAuth->getRedirectUrl(); - } - -} diff --git a/core/src/plugins/access.dropbox/dropbox-php/autoload.php b/core/src/plugins/access.dropbox/dropbox-php/autoload.php deleted file mode 100644 index 5388ea6334..0000000000 --- a/core/src/plugins/access.dropbox/dropbox-php/autoload.php +++ /dev/null @@ -1,29 +0,0 @@ -. * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Dropbox" => "Dropbox", diff --git a/core/src/plugins/access.dropbox/i18n/conf/de.php b/core/src/plugins/access.dropbox/i18n/conf/de.php index 921afe97fa..5766d1bcc7 100644 --- a/core/src/plugins/access.dropbox/i18n/conf/de.php +++ b/core/src/plugins/access.dropbox/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Dropbox" => "Dropbox", diff --git a/core/src/plugins/access.dropbox/i18n/conf/en.php b/core/src/plugins/access.dropbox/i18n/conf/en.php index 7d7904ac20..db20e6c363 100644 --- a/core/src/plugins/access.dropbox/i18n/conf/en.php +++ b/core/src/plugins/access.dropbox/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Dropbox" => "Dropbox", diff --git a/core/src/plugins/access.dropbox/i18n/conf/fr.php b/core/src/plugins/access.dropbox/i18n/conf/fr.php index 52c0da1c95..3b80b3c2c6 100644 --- a/core/src/plugins/access.dropbox/i18n/conf/fr.php +++ b/core/src/plugins/access.dropbox/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Dropbox" => "Dropbox", diff --git a/core/src/plugins/access.dropbox/i18n/conf/it.php b/core/src/plugins/access.dropbox/i18n/conf/it.php index be340b1bf9..0ccd749483 100644 --- a/core/src/plugins/access.dropbox/i18n/conf/it.php +++ b/core/src/plugins/access.dropbox/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Dropbox" => "Dropbox", diff --git a/core/src/plugins/access.dropbox/i18n/conf/pt.php b/core/src/plugins/access.dropbox/i18n/conf/pt.php index c2abe9750e..302781c5ee 100644 --- a/core/src/plugins/access.dropbox/i18n/conf/pt.php +++ b/core/src/plugins/access.dropbox/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Dropbox" => "Dropbox", diff --git a/core/src/plugins/access.dropbox/manifest.xml b/core/src/plugins/access.dropbox/manifest.xml index 9c2e537a82..371761ee05 100644 --- a/core/src/plugins/access.dropbox/manifest.xml +++ b/core/src/plugins/access.dropbox/manifest.xml @@ -1,16 +1,25 @@ - + + + - + + + + + + + + @@ -19,7 +28,6 @@ - - + diff --git a/core/src/plugins/access.dropbox/src/Driver.php b/core/src/plugins/access.dropbox/src/Driver.php new file mode 100644 index 0000000000..4dab5e2dc3 --- /dev/null +++ b/core/src/plugins/access.dropbox/src/Driver.php @@ -0,0 +1,126 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + * + */ + +namespace Pydio\Access\DropBox; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +require_once(__DIR__ . '/../vendor/autoload.php'); + +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Stream\OAuthStream; +use Pydio\Access\Core\Stream\Stream; +use Pydio\Access\Driver\StreamProvider\FS\FsAccessDriver; +use Pydio\Access\DropBox\Listener\DropBoxSubscriber; +use Pydio\Core\Model\ContextInterface; + +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * AJXP_Plugin to access a DropBox enabled server + * @package AjaXplorer_Plugins + * @subpackage Access + */ +class Driver extends FsAccessDriver +{ + const PROTOCOL = "access.dropbox"; + const RESOURCES_PATH = "Resources"; + const RESOURCES_FILE = "dropbox.json"; + + public $driverType = "dropbox"; + + /** + * Repository Initialization + * @param ContextInterface $context + * @return bool|void + * @internal param ContextInterface $contextInterface + */ + protected function initRepository(ContextInterface $context) + { + $this->detectStreamWrapper(true); + + $repository = $context->getRepository(); + $resourcesFile = $repository->getContextOption($context, "API_RESOURCES_FILE", __DIR__ . "/" . self::RESOURCES_PATH . "/" . self::RESOURCES_FILE); + + Stream::addContextOption($context, [ + "resources" => $resourcesFile, + "subscribers" => [ + new DropBoxSubscriber() + ] + ]); + + return true; + } + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @throws \Exception + * @throws \Pydio\Core\Exception\PydioException + */ + public function switchAction(ServerRequestInterface &$request, ResponseInterface &$response) { + $httpVars = $request->getParsedBody(); + + if (isset($httpVars["code"])) { + $context = $request->getAttribute("ctx"); + + Stream::addContextOption($context, [ + "oauth_code" => $httpVars["code"] + ]); + + // Simulate the creation of a stream to ensure we store the oauth in the stream context + $stream = new OAuthStream(Stream::factory('php://memory'), $context); + $stream->close(); + } + + return parent::switchAction($request, $response); + } + + /******************************************************** + * Static functions used in the JSON service description + ******************************************************* + * @param AJXP_Node $node + * @return string + */ + + public static function convertPath(AJXP_Node $node) { + $path = $node->getPath(); + + if (isset($path)) { + return $path; + } + return ""; + } + + /** + * @param $key + * @param $value + * @return string + */ + public static function convertToJSON($key, $value) { + $key = '' . $key->getName(); + $value = '' . $value; + $arr = [$key => $value]; + return json_encode($arr); + } +} \ No newline at end of file diff --git a/core/src/plugins/access.dropbox/src/Listener/DropBoxSubscriber.php b/core/src/plugins/access.dropbox/src/Listener/DropBoxSubscriber.php new file mode 100644 index 0000000000..0955db9e4b --- /dev/null +++ b/core/src/plugins/access.dropbox/src/Listener/DropBoxSubscriber.php @@ -0,0 +1,141 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ + +namespace Pydio\Access\DropBox\Listener; + +use GuzzleHttp\Event\BeforeEvent; +use GuzzleHttp\Event\CompleteEvent; +use GuzzleHttp\Event\ErrorEvent; +use GuzzleHttp\Event\RequestEvents; +use GuzzleHttp\Event\SubscriberInterface; +use GuzzleHttp\Message\Response; +use GuzzleHttp\Stream\Stream; + +/** + * Listener used to change the way the paths are given (no urlencode for the slash) + */ +class DropBoxSubscriber implements SubscriberInterface +{ + + /** + * Get the list of events this subscriber is being triggered on + * + * @return array Events + */ + public function getEvents() + { + return [ + 'before' => ['onBefore'], + 'complete' => ['onComplete'], + 'error' => ['onError', RequestEvents::EARLY], + ]; + } + + /** + * @param BeforeEvent $event + */ + public function onBefore(BeforeEvent $event) + { + $request = $event->getRequest(); + $host = $request->getHost(); + $target = "content.dropboxapi.com"; + + $count = $event->getRetryCount(); + $path = $request->getPath(); + $found = strpos($path, "download"); + + if ( $host != $target && $found && $count == 0) { + $request->setHost("content.dropboxapi.com"); + $newResponse = $event->getClient()->send($request); + $event->intercept($newResponse); + } + } + + /** + * @param CompleteEvent $event + */ + public function onComplete(CompleteEvent $event, $name) + { + $response = $event->getResponse(); + + $contentType = $response->getHeader('Content-Type'); + + if (!isset($contentType) || $contentType != "application/json") { + return; + } + + $json = $response->json(); + + if (isset($json["entries"])) { + $json = $json["entries"]; + + $message = json_encode($json); + $stream = Stream::factory(fopen("php://memory", 'w')); + $stream->write($message); + + $event->intercept(new Response(200, $response->getHeaders(), $stream)); + } + } + + /** + * Handle the before trigger + * + * @param ErrorEvent $event + * @internal param ErrorEvent $e + */ + public function onError(ErrorEvent $event) + { + $response = $event->getResponse(); + + if ($response && 400 == $response->getStatusCode()) { + $body = $response->getBody(); + + $reason = $body->getContents(); + + if (strpos($reason, "The root folder is unsupported.")) { + $msg = '{".tag": "folder", "size": 1}'; + + $stream = Stream::factory(fopen("php://memory", 'w')); + $stream->write($msg); + + $response = new Response("200",[ + "Content-Length" => 10 + ], $stream); + + $event->intercept($response); + } + } elseif ($response && 409 == $response->getStatusCode()) { + $body = $response->getBody(); + + $result = json_decode($body->getContents()); + + $summary = $result->error_summary; + + if (isset($summary) && strpos($summary, "path/not_found") == 0) { + $response = new Response("404",[ + "Content-Length" => 0 + ], null); + + $event->intercept($response); + } + } + } +} diff --git a/core/src/plugins/access.dropbox/src/Resources/dropbox.json b/core/src/plugins/access.dropbox/src/Resources/dropbox.json new file mode 100644 index 0000000000..b2f80cbdff --- /dev/null +++ b/core/src/plugins/access.dropbox/src/Resources/dropbox.json @@ -0,0 +1,192 @@ +{ + "name" : "DropBox Client", + "description" : "DropBox REST API client", + "operations" : { + "Ls" : { + "httpMethod" : "POST", + "uri" : "files/list_folder", + "summary" : "Open the object as a stream for the StreamWrapper to access", + "responseClass" : "Directory", + "parameters" : { + "path" : { + "required" : true, + "type" : "object", + "location" : "json", + "sentAs": "path", + "filters": [{ + "method": "\\Pydio\\Access\\DropBox\\Driver::convertPath", + "args": [ "@value" ] + }] + } + } + }, + "Get" : { + "httpMethod" : "POST", + "uri" : "files/download", + "summary" : "Open the object as a stream for the StreamWrapper to access", + "responseClass" : "File", + "parameters" : { + "path" : { + "required" : true, + "type" : "object", + "location" : "header", + "sentAs": "Dropbox-API-Arg", + "filters": [{ + "method": "\\Pydio\\Access\\DropBox\\Driver::convertToJSON", + "args": [ "@api", "@value" ] + }] + } + } + }, + "Stat" : { + "httpMethod" : "POST", + "uri" : "files/get_metadata", + "summary" : "Returns the stat of a specific object", + "responseClass" : "Stat", + "exceptions" : false, + "parameters" : { + "path" : { + "required" : true, + "type" : "object", + "location" : "json", + "sentAs": "path", + "filters": [{ + "method": "\\Pydio\\Access\\DropBox\\Driver::convertPath", + "args": [ "@value" ] + }] + } + } + }, + "Mkdir" : { + "httpMethod" : "POST", + "uri" : "files/create_folder", + "parameters" : { + "path" : { + "required" : true, + "type" : "object", + "location" : "json", + "sentAs": "path", + "filters": [{ + "method": "\\Pydio\\Access\\DropBox\\Driver::convertPath", + "args": [ "@value" ] + }] + } + } + }, + "Rmdir" : { + "httpMethod" : "POST", + "uri" : "files/delete", + "parameters" : { + "path" : { + "required" : true, + "type" : "object", + "location" : "json", + "sentAs": "path", + "filters": [{ + "method": "\\Pydio\\Access\\DropBox\\Driver::convertPath", + "args": [ "@value" ] + }] + } + } + }, + "Rename" : { + "httpMethod" : "POST", + "uri" : "files/move", + "parameters" : { + "frompath/fullpath" : { + "required" : true, + "type" : "string", + "location" : "json", + "sentAs": "from_path" + }, + "topath/fullpath" : { + "required" : true, + "type" : "string", + "location" : "json", + "sentAs": "to_path" + } + } + }, + "Put" : { + "httpMethod" : "POST", + "uri": "files/upload", + "parameters" : { + "path/fullpath": { + "required": true, + "type": "string", + "location": "query", + "sentAs": "path" + }, + "body" : { + "location" : "body" + } + } + }, + "Delete" : { + "httpMethod" : "POST", + "uri" : "files/delete", + "parameters" : { + "path/fullpath" : { + "required" : true, + "type" : "string", + "location" : "json", + "sentAs": "path" + } + } + } + }, + "models" : { + "Directory" : { + "type" : "array", + "location" : "json", + "items": { + "type" : "object", + "location" : "json", + "properties" : { + "type": { + "type": "string", + "sentAs": ".tag" + }, + "name": { + "type": "string" + }, + "lastModified": { + "type": "string", + "sentAs": "client_modified" + }, + "size": { + "type": "string" + } + } + } + }, + "File" : { + "type": "object", + "properties" : { + "body" : { + "location" : "body" + } + } + }, + "Stat" : { + "type" : "object", + "location" : "json", + "properties" : { + "type": { + "type": "string", + "sentAs": ".tag" + }, + "name": { + "type": "string" + }, + "lastModified": { + "type": "string", + "sentAs": "client_modified" + }, + "size": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/core/src/plugins/access.fs/FsAccessDriver.php b/core/src/plugins/access.fs/FsAccessDriver.php new file mode 100644 index 0000000000..130c1f0311 --- /dev/null +++ b/core/src/plugins/access.fs/FsAccessDriver.php @@ -0,0 +1,2520 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + * + */ +namespace Pydio\Access\Driver\StreamProvider\FS; + +use DOMNode; +use DOMXPath; +use Normalizer; +use PclZip; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\UploadedFileInterface; +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Access\Core\Exception\FileNotWriteableException; +use Pydio\Access\Core\MetaStreamWrapper; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\IAjxpWrapperProvider; +use Pydio\Access\Core\Model\NodesDiff; +use Pydio\Access\Core\Model\NodesList; +use Pydio\Access\Core\RecycleBinManager; +use Pydio\Access\Core\Model\Repository; +use Pydio\Access\Core\Model\UserSelection; +use Pydio\Auth\Core\MemorySafe; +use Pydio\Core\Http\Message\ExternalUploadedFile; +use Pydio\Core\Http\Response\FileReaderResponse; +use Pydio\Core\Http\Response\SerializableResponseStream; +use Pydio\Core\Http\Message\UserMessage; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Controller\Controller; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Services\SessionService; +use Pydio\Core\Services\ApplicationState; +use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Core\Utils\Vars\PathUtils; +use Pydio\Core\Utils\Vars\StatHelper; +use Pydio\Core\Utils\Http\UserAgent; +use Pydio\Core\Controller\HTMLWriter; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Utils\TextEncoder; +use Pydio\Tasks\Schedule; +use Pydio\Tasks\Task; +use Pydio\Tasks\TaskService; +use Zend\Diactoros\Response; +use Zend\Diactoros\Response\JsonResponse; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + + +// This is used to catch exception while downloading +if (!function_exists('download_exception_handler')) { + /** + * @param $exception + */ + function download_exception_handler($exception){} +} +/** + * Plugin to access a filesystem. Most "FS" like driver (even remote ones) + * extend this one. + * @package AjaXplorer_Plugins + * @subpackage Access + */ +class FsAccessDriver extends AbstractAccessDriver implements IAjxpWrapperProvider +{ + /** + * @var Repository + */ + public $repository; + public $driverConf; + protected $wrapperClassName; + protected $urlBase; + + /** + * @param ContextInterface $contextInterface + * @throws PydioException + * @throws \Exception + */ + protected function initRepository(ContextInterface $contextInterface) + { + if (is_array($this->pluginConf)) { + $this->driverConf = $this->pluginConf; + } else { + $this->driverConf = []; + } + if ( $this->getContextualOption($contextInterface, "PROBE_REAL_SIZE") == true ) { + // PASS IT TO THE WRAPPER + ConfService::setConf("PROBE_REAL_SIZE", true); + } + $repository = $contextInterface->getRepository(); + $create = $repository->getContextOption($contextInterface, "CREATE"); + $path = $repository->getContextOption($contextInterface, "PATH"); + $storagePath = TextEncoder::toStorageEncoding($path); + $recycle = $repository->getContextOption($contextInterface, "RECYCLE_BIN"); + $chmod = $repository->getContextOption($contextInterface, "CHMOD_VALUE"); + $this->urlBase = $contextInterface->getUrlBase(); + + MetaStreamWrapper::appendMetaWrapper("pydio.encoding", "Pydio\\Access\\Core\\EncodingWrapper", 100); + + if ($create == true) { + if(!is_dir($storagePath)) @mkdir($storagePath, 0755, true); + if (!is_dir($storagePath)) { + throw new PydioException("Cannot create root path for repository (".$repository->getDisplay()."). Please check repository configuration or that your folder is writeable!"); + } + if ($recycle!= "" && !is_dir($storagePath."/".$recycle)) { + @mkdir($storagePath."/".$recycle); + if (!is_dir($storagePath."/".$recycle)) { + throw new PydioException("Cannot create recycle bin folder. Please check repository configuration or that your folder is writeable!"); + } else { + $this->setHiddenAttribute(new AJXP_Node($contextInterface->getUrlBase() ."/".$recycle)); + } + } + $dataTemplate = TextEncoder::toStorageEncoding($repository->getContextOption($contextInterface, "DATA_TEMPLATE")); + if (!empty($dataTemplate) && is_dir($dataTemplate) && !is_file($storagePath."/.ajxp_template")) { + $errs = [];$succ = []; + $repoData = ['base_url' => $contextInterface->getUrlBase(), 'chmod' => $chmod, 'recycle' => $recycle]; + $this->dircopy($dataTemplate, $storagePath, $succ, $errs, false, false, $repoData, $repoData); + touch($storagePath."/.ajxp_template"); + } + } else { + if (!is_dir($storagePath)) { + throw new PydioException("Cannot find base path for your repository! Please check the configuration!"); + } + } + if ($recycle != "") { + RecycleBinManager::init($contextInterface->getUrlBase(), "/".$recycle); + } + } + + /** + * @param String $path + * @return string + */ + public function getResourceUrl($path) + { + return $this->urlBase.$path; + } + + /** + * @param AJXP_Node $node + * @return int + */ + public function directoryUsage(AJXP_Node $node){ + + if(MetaStreamWrapper::wrapperIsRemote($node->getUrl())){ + return $this->recursiveDirUsageByListing($node->getUrl()); + } + $dir = $node->getRealFile(); + $size = -1; + if ( ( PHP_OS == "WIN32" || PHP_OS == "WINNT" || PHP_OS == "Windows") && class_exists("COM") ) { + $obj = new \COM ( 'scripting.filesystemobject' ); + if ( is_object ( $obj ) ) { + $ref = $obj->getfolder ( $dir ); + $size = floatval($ref->size); + $obj = null; + } + } else { + if((PHP_OS == "Darwin") || (PHP_OS == "FreeBSD")) $option = "-sk"; + else $option = "-sb"; + $cmd = '/usr/bin/du '.$option.' ' . escapeshellarg($dir); + $io = popen ( $cmd , 'r' ); + $size = fgets ( $io, 4096); + $size = trim(str_replace($dir, "", $size)); + $size = floatval($size); + if((PHP_OS == "Darwin") || (PHP_OS == "FreeBSD")) $size = $size * 1024; + pclose ( $io ); + } + if($size != -1){ + return $size; + }else{ + return $this->recursiveDirUsageByListing($node->getUrl()); + } + + } + + /** + * @param $path + * @return int|string + */ + protected function recursiveDirUsageByListing($path){ + $total_size = 0; + $files = scandir($path); + + foreach ($files as $t) { + if (is_dir(rtrim($path, '/') . '/' . $t)) { + if ($t <> "." && $t <> "..") { + $size = $this->recursiveDirUsageByListing(rtrim($path, '/') . '/' . $t); + $total_size += $size; + } + } else { + $size = sprintf("%u", filesize(rtrim($path, '/') . '/' . $t)); + $total_size += $size; + } + } + return $total_size; + } + + /** + * @param $contribNode + * @param $arrayActions + * @param $targetMethod + */ + public function redirectActionsToMethod(&$contribNode, $arrayActions, $targetMethod) + { + $actionXpath=new DOMXPath($contribNode->ownerDocument); + foreach ($arrayActions as $index => $value) { + $arrayActions[$index] = 'action[@name="'.$value.'"]/processing/serverCallback'; + } + $procList = $actionXpath->query(implode(" | ", $arrayActions), $contribNode); + foreach ($procList as $node) { + $node->setAttribute("methodName", $targetMethod); + } + } + + /** + * @param ServerRequestInterface $request + */ + protected function filterByApi(&$request){ + if($request->getAttribute("api") !== "v2") return; + $params = $request->getParsedBody(); + $action = $request->getAttribute("action"); + switch($action){ + case "ls": + $children = $params["children"] OR null; + $meta = $params["meta"] OR "standard"; + if(!empty($children)){ + $options = $children; + } else { + $options = "dzf"; + } + if($meta !== "minimal") $options .= "l"; + $params["options"] = $options; + $request = $request->withParsedBody($params); + break; + case "download": + + break; + + default: + break; + } + } + + /** + * @param DOMNode $contribNode + */ + public function disableArchiveBrowsingContributions(&$contribNode) + { + // Cannot use zip features on FTP ! + // Remove "compress" action + $actionXpath=new DOMXPath($contribNode->ownerDocument); + $compressNodeList = $actionXpath->query('action[@name="compress"]|action[@name="compress_ui"]|action[@name="download_all"]', $contribNode); + if(!$compressNodeList->length) return ; + foreach($compressNodeList as $compressNodeAction){ + $contribNode->removeChild($compressNodeAction); + } + // Disable "download" if selection is multiple + $nodeList = $actionXpath->query('action[@name="download"]/gui/selectionContext', $contribNode); + $selectionNode = $nodeList->item(0); + $values = ["dir" => "false", "unique" => "true"]; + foreach ($selectionNode->attributes as $attribute) { + if (isSet($values[$attribute->name])) { + $attribute->value = $values[$attribute->name]; + } + } + $nodeList = $actionXpath->query('action[@name="download"]/processing/clientListener[@name="selectionChange"]', $contribNode); + $listener = $nodeList->item(0); + $listener->parentNode->removeChild($listener); + // Disable "Explore" action on files + $nodeList = $actionXpath->query('action[@name="ls"]/gui/selectionContext', $contribNode); + $selectionNode = $nodeList->item(0); + $values = ["file" => "false", "allowedMimes" => ""]; + foreach ($selectionNode->attributes as $attribute) { + if (isSet($values[$attribute->name])) { + $attribute->value = $values[$attribute->name]; + } + } + } + + /** + * @param $selection + * @return array|string + */ + public function addSlugToPath($selection) + { + if (is_array($selection)) + // As passed by Copy/Move + $orig_files = $selection; + elseif ((is_object($selection)) && (isset($selection->files)) && (is_array($selection->files))) + // As passed by Download + $orig_files = $selection->files; + elseif (is_string($selection)) + // As passed by destination parameter + return $this->repository->slug.$selection; + else + // Unrecognized + return $selection; + + $files = []; + foreach ($orig_files as $file) + $files[] = $this->repository->slug.$file; + return $files; + } + + /** + * API V2, will get POST / PUT actions, will reroute to mkdir, mkfile, copy, move actions + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @throws \Exception + */ + public function createResourceAction(ServerRequestInterface &$request, ResponseInterface &$response){ + + /** @var ContextInterface $ctx */ + $ctx = $request->getAttribute("ctx"); + $selection = UserSelection::fromContext($ctx, $request->getParsedBody()); + if($selection->isEmpty()){ + throw new PydioException("Empty resource"); + } + $path = $selection->getUniqueFile(); + $params = $request->getParsedBody(); + $newAction = null; + $newVars = []; + if(isSet($params["copy_source"])){ + $newVars["dest"] = PathUtils::forwardSlashDirname($path); + $newVars["targetBaseName"] = PathUtils::forwardSlashBasename($path); + + $sourceParts = explode("/", trim($params["copy_source"], "/")); + $sourceRepo = array_shift($sourceParts); + $newVars["file"] = "/".implode("/", $sourceParts); + $currentRepo = $ctx->getRepositoryId().""; + if($currentRepo !== $sourceRepo){ + // Cross repo, invert parameters and forward to parent method! + throw new PydioException("Cross Repository copy is not implemented on this api."); + } + if(isSet($params["delete_source"]) && $params["delete_source"] == "true"){ + $newAction = "move"; + }else{ + $newAction = "copy"; + } + }else{ + $qPath = $params["path"]; + if(substr_compare($qPath, "/", strlen($qPath)-1, 1) === 0){ + // Ends with slash => mkdir + $newAction = "mkdir"; + $newVars["file"] = $path; + if(!empty($params["override"])) { + $newVars["ignore_exists"] = $params["override"]; + } + if(!empty($params["recursive"])) { + $newVars["recursive"] = $params["recursive"]; + } + }else{ + $newAction = "mkfile"; + $newVars["node"] = $path; + if(!empty($params["content"])) { + $newVars["content"] = $params["content"]; + } + if(!empty($params["override"])) { + $newVars["force"] = $params["override"]; + } + } + } + $request = $request->withParsedBody($newVars)->withAttribute("action", $newAction); + $this->switchAction($request, $response); + + } + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @throws \Exception + */ + public function uploadAction(ServerRequestInterface &$request, ResponseInterface &$response){ + + $httpVars = $request->getParsedBody(); + $dir = InputFilter::sanitize($httpVars["dir"], InputFilter::SANITIZE_DIRNAME) OR ""; + /** @var ContextInterface $ctx */ + $ctx = $request->getAttribute("ctx"); + if (MetaStreamWrapper::actualRepositoryWrapperClass(new AJXP_Node($ctx->getUrlBase())) === "Pydio\\Access\\Driver\\StreamProvider\\FS\\FsAccessWrapper") { + $dir = PathUtils::patchPathForBaseDir($dir); + } + $dir = InputFilter::securePath($dir); + $selection = UserSelection::fromContext($ctx, $httpVars); + if (!$selection->isEmpty()) { + $this->filterUserSelectionToHidden($selection->getContext(), $selection->getFiles()); + if(empty($dir) && $selection->isUnique()){ + $dir = PathUtils::forwardSlashDirname($selection->getUniqueFile()); + } + } + $mess = LocaleService::getMessages(); + + $repoData = [ + 'chmod' => $ctx->getRepository()->getContextOption($ctx, 'CHMOD_VALUE'), + 'recycle' => $ctx->getRepository()->getContextOption($ctx, 'RECYCLE_BIN') + ]; + $this->logDebug("Upload Files Data", $request->getUploadedFiles()); + + $destNode = $selection->nodeForPath(InputFilter::decodeSecureMagic($dir)); + $destination = $destNode->getUrl(); + $this->logDebug("Upload inside", ["destination"=>$this->addSlugToPath($destNode->getUrl())]); + if (!$this->isWriteable($destNode)) { + $errorCode = 412; + $errorMessage = "$mess[38] ".$dir." $mess[99]."; + $this->logDebug("Upload error 412", ["destination"=>$this->addSlugToPath($destination)]); + $this->writeUploadError($request, $errorMessage, $errorCode); + return; + } + + $partialUpload = false; + $partialTargetSize = -1; + $originalAppendTo = ""; + $createdNode = null; + + /** @var UploadedFileInterface[] $uploadedFiles */ + $uploadedFiles = $request->getUploadedFiles(); + if(!count($uploadedFiles)){ + $this->writeUploadError($request, "Could not find any uploaded file", 411); + return; + } + $uploadedFile = array_shift($uploadedFiles); + + try{ + // CHECK PHP UPLOAD ERRORS + InputFilter::parseFileDataErrors($uploadedFile, true); + + // FIND PROPER FILE NAME / FILTER IF NECESSARY + if (isSet($httpVars["urlencoded_filename"])) { + $userfile_name = InputFilter::sanitize(urldecode($httpVars["urlencoded_filename"]), InputFilter::SANITIZE_FILENAME, true); + }else{ + $userfile_name= InputFilter::sanitize(InputFilter::fromPostedFileName($uploadedFile->getClientFileName()), InputFilter::SANITIZE_FILENAME, true); + } + $userfile_name = substr($userfile_name, 0, ConfService::getContextConf($ctx, "NODENAME_MAX_LENGTH")); + $this->logDebug("User filename ".$userfile_name); + if(class_exists("Normalizer")){ + $userfile_name = Normalizer::normalize($userfile_name, Normalizer::FORM_C); + } + // Chec if it's forbidden + $this->filterUserSelectionToHidden($selection->getContext(), [$userfile_name]); + + if($uploadedFile instanceof ExternalUploadedFile && $uploadedFile->getStatus() == ExternalUploadedFile::STATUS_UPLOAD_FINISHED){ + // GO DIRECTLY TO POST_PROCESSING AND RETURN. + $createdNode = new AJXP_Node($destination."/".$userfile_name); + $this->uploadPostProcess($request, $createdNode, false, (isset($httpVars["exists"]) && $httpVars["exists"] === "true"), $repoData["chmod"]); + return; + } + + // MODIFY TARGET IF AUTO RENAME + if (isSet($httpVars["auto_rename"])) { + $userfile_name = self::autoRenameForDest($destination, $userfile_name); + } + + // APPLY PRE-UPLOAD HOOKS + $already_existed = false; + try { + $newFileSize = $uploadedFile->getSize(); + $targetUrl = $destination."/".$userfile_name; + $targetNode = new AJXP_Node($targetUrl); + if (file_exists($targetUrl)) { + $already_existed = true; + Controller::applyHook("node.before_change", [$targetNode, $newFileSize]); + } else { + Controller::applyHook("node.before_create", [$targetNode, $newFileSize]); + } + Controller::applyHook("node.before_change", [new AJXP_Node($destination)]); + } catch (\Exception $e) { + throw new \Exception($e->getMessage(), 507); + } + + // PARTIAL UPLOAD CASE - PREPPEND .dlpart extension + if(isSet($httpVars["partial_upload"]) && $httpVars["partial_upload"] == 'true' && isSet($httpVars["partial_target_bytesize"])) { + $partialUpload = true; + $partialTargetSize = intval($httpVars["partial_target_bytesize"]); + if(!isSet($httpVars["appendto_urlencoded_part"])) { + $userfile_name .= ".dlpart"; + $targetUrl = $destination."/".$userfile_name; + } + } + + if($uploadedFile instanceof ExternalUploadedFile && $uploadedFile->getStatus() == ExternalUploadedFile::STATUS_REQUEST_OPTIONS){ + // Now build options and send that as a reponse + // Add Callback data : exists=true + $createdNode = new AJXP_Node($targetUrl); + $options = MetaStreamWrapper::getResolvedOptionsForNode($createdNode); + $response = new JsonResponse([ + "CONTEXT" => $createdNode->getContext()->getStringIdentifier(), + "OPTIONS" => $options, + "PATH" => $createdNode->getPath(), + "CALLBACK_PARAMETERS" => ["exists" => $already_existed] + ]); + return; + } + + // NOW DO THE ACTUAL COPY + $this->copyUploadedData($uploadedFile, $targetUrl, $mess); + + // PARTIAL UPLOAD - PART II: APPEND DATA TO EXISTING PART + if (isSet($httpVars["appendto_urlencoded_part"])) { + $appendTo = InputFilter::sanitize(urldecode($httpVars["appendto_urlencoded_part"]), InputFilter::SANITIZE_FILENAME); + if(isSet($httpVars["partial_upload"]) && $httpVars["partial_upload"] == 'true'){ + $originalAppendTo = $appendTo; + $appendTo .= ".dlpart"; + } + $this->logDebug("AppendTo FILE".$appendTo); + $already_existed = $this->appendUploadedData($destination, $userfile_name, $appendTo); + $userfile_name = $appendTo; + $targetAppended = new AJXP_Node($destination."/".$userfile_name); + clearstatcache(true, $targetAppended->getUrl()); + $targetAppended->loadNodeInfo(true); + if($partialUpload && $partialTargetSize == filesize($destination."/".$userfile_name)){ + // This was the last part. We can now rename to the original name. + if(is_file($destination."/".$originalAppendTo)){ + unlink($destination."/".$originalAppendTo); + } + $result = @rename($destination."/".$userfile_name, $destination."/".$originalAppendTo); + if($result === false){ + throw new \Exception("Error renaming ".$destination."/".$userfile_name." to ".$destination."/".$originalAppendTo); + } + $userfile_name = $originalAppendTo; + $partialUpload = false; + // Send a create event! + $already_existed = false; + $lastPartAppended = true; + } + } + + $createdNode = new AJXP_Node($destination."/".$userfile_name); + $this->uploadPostProcess($request, $createdNode, $partialUpload, $already_existed, $repoData["chmod"]); + + }catch(\Exception $e){ + $errorCode = $e->getCode(); + if(empty($errorCode)) $errorCode = 411; + $this->writeUploadError($request, $e->getMessage(), $errorCode); + } + + } + + /** + * @param ServerRequestInterface $request + * @param AJXP_Node $createdNode + * @param $partialUpload + * @param $nodeOverriden + * @param string $chmodValue + */ + protected function uploadPostProcess(&$request, $createdNode, $partialUpload = false, $nodeOverriden = false, $chmodValue = ""){ + + // NOW PREPARE POST-UPLOAD EVENTS + $this->changeMode($createdNode->getUrl(),["chmod" => $chmodValue]); + clearstatcache(true, $createdNode->getUrl()); + $createdNode->loadNodeInfo(true); + $logFile = $this->addSlugToPath($createdNode->getParent()->getPath())."/".$createdNode->getLabel(); + $this->logInfo("Upload File", ["file"=>$logFile, "files"=> $logFile]); + + if($partialUpload){ + $this->logDebug("Return Partial Upload: SUCESS but no event yet"); + // Make sure to clear cache for parent + $createdNode->getParent()->loadNodeInfo(true); + $this->writeUploadSuccess($request, ["PARTIAL_NODE" => $createdNode]); + } else { + $this->logDebug("Return success"); + $createdNode->loadHash(); + if($nodeOverriden){ + $this->writeUploadSuccess($request, ["UPDATED_NODE" => $createdNode]); + }else{ + $this->writeUploadSuccess($request, ["CREATED_NODE" => $createdNode]); + } + } + + } + + /** + * @param ServerRequestInterface $request + * @param $message + * @param $code + */ + protected function writeUploadError(ServerRequestInterface &$request, $message, $code){ + $request = $request->withAttribute("upload_process_result", ["ERROR" => ["CODE" => $code, "MESSAGE" => $message]]); + } + + /** + * @param ServerRequestInterface $request + * @param $nodeData + */ + protected function writeUploadSuccess(ServerRequestInterface &$request, $nodeData){ + $arr = array_merge(["SUCCESS" => true], $nodeData); + $request = $request->withAttribute("upload_process_result", $arr); + } + + + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @throws PydioException + * @throws \Exception + */ + public function downloadAction(ServerRequestInterface &$request, ResponseInterface &$response){ + + /** @var ContextInterface $ctx */ + $ctx = $request->getAttribute("ctx"); + $httpVars = $request->getParsedBody(); + $selection = UserSelection::fromContext($ctx, $httpVars); + if (!$selection->isEmpty()) { + $this->filterUserSelectionToHidden($ctx, $selection->getFiles()); + } + + $action = $request->getAttribute("action"); + + switch ($action){ + + case "download": + + $this->logInfo("Download", ["files"=>$this->addSlugToPath($selection)]); + @set_error_handler(["Pydio\\Core\\Controller\\HTMLWriter", "javascriptErrorHandler"], E_ALL & ~ E_NOTICE); + @register_shutdown_function("restore_error_handler"); + $zip = false; + $dir = ""; + if ($selection->isUnique()) { + if (is_dir($selection->getUniqueNode()->getUrl())) { + $zip = true; + $base = basename($selection->getUniqueFile()); + $uniqDir = PathUtils::forwardSlashDirname($selection->getUniqueFile()); + if(!empty($uniqDir) && $uniqDir != "/"){ + $dir = PathUtils::forwardSlashDirname($selection->getUniqueFile()); + } + } else { + if (!file_exists($selection->getUniqueNode()->getUrl())) { + throw new \Exception("Cannot find file!"); + } + } + $node = $selection->getUniqueNode(); + } else { + if(isset($httpVars["dir"])){ + $dir = InputFilter::decodeSecureMagic($httpVars["dir"], InputFilter::SANITIZE_DIRNAME); + } + $base = basename(PathUtils::forwardSlashDirname($selection->getUniqueFile())); + $zip = true; + } + if ($zip) { + // Make a temp zip and send it as download + $loggedUser = $ctx->getUser(); + $file = ApplicationState::getAjxpTmpDir() ."/".($loggedUser?$loggedUser->getId():"shared")."_".time()."tmpDownload.zip"; + $zipFile = $this->makeZip($selection, $file, empty($dir)?"/":$dir); + if(!$zipFile) throw new PydioException("Error while compressing"); + $localName = (empty($base)?"Files":$base).".zip"; + if(isSet($httpVars["archive_name"])){ + $localName = InputFilter::decodeSecureMagic($httpVars["archive_name"]); + } + $fileReader = new FileReaderResponse($file); + $fileReader->setUnlinkAfterRead(); + $fileReader->setLocalName($localName); + $response = $response->withBody($fileReader); + } else { + $localName = ""; + Controller::applyHook("dl.localname", [$selection->getUniqueNode(), &$localName]); + $fileReader = new FileReaderResponse($selection->getUniqueNode()); + $fileReader->setLocalName($localName); + $response = $response->withBody($fileReader); + } + if (isSet($node)) { + Controller::applyHook("node.read", [&$node]); + } + + break; + + case "get_content": + + $node = $selection->getUniqueNode(); + $dlFile = $node->getUrl(); + if(!is_readable($dlFile)){ + throw new \Exception("Cannot access file!"); + } + $this->logInfo("Get_content", ["files"=>$this->addSlugToPath($selection)]); + + if (StatHelper::getStreamingMimeType(basename($dlFile)) !==false) { + $readMode = "stream_content"; + } else { + $readMode = "plain"; + } + $fileReader = new FileReaderResponse($node); + $fileReader->setHeaderType($readMode); + $response = $response->withBody($fileReader); + Controller::applyHook("node.read", [&$node]); + + break; + + case "prepare_chunk_dl" : + + $chunkCount = intval($httpVars["chunk_count"]); + $node = $selection->getUniqueNode(); + + $fileId = $node->getUrl(); + $sessionKey = "chunk_file_".md5($fileId.time()); + $totalSize = filesize($fileId); + $chunkSize = intval ( $totalSize / $chunkCount ); + $realFile = MetaStreamWrapper::getRealFSReference($fileId, true); + $chunkData = [ + "localname" => basename($fileId), + "chunk_count" => $chunkCount, + "chunk_size" => $chunkSize, + "total_size" => $totalSize, + "file_id" => $sessionKey + ]; + SessionService::save($sessionKey, array_merge($chunkData, ["file" => $realFile])); + $response = $response->withHeader("Content-type", "application/json; charset=UTF-8"); + $response->getBody()->write(json_encode($chunkData)); + + Controller::applyHook("node.read", [&$node]); + + break; + + case "download_chunk" : + + $chunkIndex = intval($httpVars["chunk_index"]); + $chunkKey = $httpVars["file_id"]; + $sessData = SessionService::fetch($chunkKey); + $realFile = $sessData["file"]; + $chunkSize = $sessData["chunk_size"]; + $offset = $chunkSize * $chunkIndex; + if ($chunkIndex == $sessData["chunk_count"]-1) { + // Compute the last chunk real length + $chunkSize = $sessData["total_size"] - ($chunkSize * ($sessData["chunk_count"]-1)); + if ($selection->nodeForPath("/")->wrapperIsRemote()) { + register_shutdown_function("unlink", $realFile); + } + } + $fileReader = new FileReaderResponse($realFile); + $fileReader->setLocalName($sessData["localname"].".".sprintf("%03d", $chunkIndex+1)); + $fileReader->setPartial($offset, $chunkSize); + $response = $response->withBody($fileReader); + + break; + + default: + break; + } + + } + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @throws PydioException + * @throws \Exception + */ + public function switchAction(ServerRequestInterface &$request, ResponseInterface &$response) + { + parent::accessPreprocess($request); + $this->filterByApi($request); + + /** @var ContextInterface $ctx */ + $ctx = $request->getAttribute("ctx"); + $action = $request->getAttribute("action"); + $httpVars = $request->getParsedBody(); + + $selection = UserSelection::fromContext($ctx, $httpVars); + if (!$selection->isEmpty()) { + $this->filterUserSelectionToHidden($ctx, $selection->getFiles()); + RecycleBinManager::filterActions($action, $selection, $httpVars); + } + $mess = LocaleService::getMessages(); + $nodesDiffs = new NodesDiff(); + + switch ($action) { + + case "compress" : + + $taskId = $request->getAttribute("pydio-task-id"); + if($request->getAttribute("pydio-task-id") === null){ + $task = TaskService::actionAsTask($ctx, $action, $httpVars); + $task->setActionLabel($mess, '313'); + $task->setFlags(Task::FLAG_STOPPABLE); + $response = TaskService::getInstance()->enqueueTask($task, $request, $response); + break; + } + + if($taskId !== null){ + TaskService::getInstance()->updateTaskStatus($taskId, Task::STATUS_RUNNING, "Starting compression in background"); + } + $dir = InputFilter::decodeSecureMagic($httpVars["dir"], InputFilter::SANITIZE_DIRNAME); + $currentDirNode = $selection->nodeForPath($dir); + // Make a temp zip + $loggedUser = $ctx->getUser(); + if (isSet($httpVars["archive_name"])) { + $localName = InputFilter::decodeSecureMagic($httpVars["archive_name"]); + $this->filterUserSelectionToHidden($ctx, [$localName]); + } else { + $localName = (basename($dir)==""?"Files":basename($dir)).".zip"; + } + $file = ApplicationState::getAjxpTmpDir() ."/".($loggedUser?$loggedUser->getId():"shared")."_".time()."tmpCompression.zip"; + if(isSet($httpVars["compress_flat"])) $baseDir = "__AJXP_ZIP_FLAT__/"; + else $baseDir = $dir; + $zipFile = $this->makeZip($selection, $file, $baseDir, $taskId); + if(!$zipFile) throw new PydioException("Error while compressing file $localName"); + register_shutdown_function("unlink", $file); + $urlBase = $selection->currentBaseUrl(); + $tmpFNAME = $urlBase.$dir."/".str_replace(".zip", ".tmp", $localName); + copy($file, $tmpFNAME); + try { + Controller::applyHook("node.before_create", [new AJXP_Node($tmpFNAME), filesize($tmpFNAME)]); + } catch (\Exception $e) { + @unlink($tmpFNAME); + throw $e; + } + @rename($tmpFNAME, $urlBase.$dir."/".$localName); + $newArchiveNode = $currentDirNode->createChildNode($localName); + Controller::applyHook("node.change", [null, $newArchiveNode, false], true); + if($taskId !== null){ + TaskService::getInstance()->updateTaskStatus($taskId, Task::STATUS_COMPLETE, "Finished compression in background"); + } + + break; + + case "stat" : + + clearstatcache(); + $jsonData = new \stdClass; + if($selection->isUnique()){ + $stat = @stat($selection->getUniqueNode()->getUrl()); + if ($stat !== false && is_readable($selection->getUniqueNode()->getUrl())) { + $jsonData = $stat; + } + }else{ + $nodes = $selection->buildNodes(); + foreach($nodes as $node){ + $stat = @stat($node->getUrl()); + if(!$stat || !is_readable($node->getUrl())) { + $stat = new \stdClass(); + } + $path = $node->getPath(); + $jsonData->$path = $stat; + } + } + $response = new JsonResponse($jsonData); + + break; + + + //------------------------------------ + // ONLINE EDIT + //------------------------------------ + case "put_content": + + if(!isset($httpVars["content"])) break; + // Load "code" variable directly from POST array, do not "securePath" or "sanitize"... + $code = $httpVars["content"]; + $currentNode = $selection->getUniqueNode(); + $fileName = $currentNode->getUrl(); + $this->logInfo("Online Edition", ["files"=> $this->addSlugToPath($fileName)]); + if (isSet($httpVars["encode"]) && $httpVars["encode"] == "base64") { + $code = base64_decode($code); + } else { + $code=str_replace("<","<", InputFilter::magicDequote($code)); + } + Controller::applyHook("node.before_change", [&$currentNode, strlen($code)]); + if (!is_file($fileName) || !$this->isWriteable($currentNode)) { + throw new FileNotWriteableException($currentNode); + } + $fp=fopen($fileName,"w"); + fputs ($fp,$code); + fclose($fp); + clearstatcache(true, $fileName); + Controller::applyHook("node.change", [$currentNode, $currentNode, false]); + $logMessage = new UserMessage($mess[115]); + $nodesDiffs->update([$currentNode]); + + break; + + //------------------------------------ + // DELETE + // Warning, must be kept BEFORE copy/move + // as recyclebin filtering can transform + // it move action. + //------------------------------------ + case "delete": + + if ($selection->isEmpty()) { + throw new PydioException("", 113); + } + $size = 0; + $nodes = $selection->buildNodes(); + $bgSizeThreshold = 10*1024*1024; + $bgWorkerThreshold = 80*1024*1024; + if(!MetaStreamWrapper::wrapperIsRemote($selection->currentBaseUrl())){ + foreach($nodes as $node){ + $size += $node->getSizeRecursive(); + } + }else if(!$selection->isUnique() || !$selection->getUniqueNode()->isLeaf()){ + $size = -1; + } + $taskId = $request->getAttribute("pydio-task-id"); + if($taskId === null && ($size === -1 || $size > $bgSizeThreshold)){ + $task = TaskService::actionAsTask($ctx, $action, $httpVars); + $task->setActionLabel($mess, '7'); + if($size === -1 || $size > $bgWorkerThreshold){ + $task->setSchedule(new Schedule(Schedule::TYPE_ONCE_DEFER)); + } + $response = TaskService::getInstance()->enqueueTask($task, $request, $response); + break; + } + + $logMessages = []; + $errorMessage = $this->delete($selection, $logMessages, $taskId); + if (count($logMessages)) { + $logMessage = new UserMessage(join("\n", $logMessages)); + } + if($errorMessage) { + throw new PydioException($errorMessage); + } + $this->logInfo("Delete", ["files"=>$this->addSlugToPath($selection)]); + $nodesDiffs->remove($selection->getFiles()); + + break; + + case "empty_recycle": + + $taskId = $request->getAttribute("pydio-task-id"); + if($taskId === null && !ApplicationState::sapiIsCli()){ + $task = TaskService::actionAsTask($ctx, $action, $httpVars); + $task->setActionLabel($mess, '221'); + $response = TaskService::getInstance()->enqueueTask($task, $request, $response); + break; + } + // List recycle content + $fakeResp = new Response(); + $recycleBin = RecycleBinManager::getRelativeRecycle(); + $newRequest = $request->withAttribute("action", "ls"); + $newRequest = $newRequest->withParsedBody(["dir" => $recycleBin]); + $this->switchAction($newRequest, $fakeResp); + $b = $fakeResp->getBody(); + if($b instanceof SerializableResponseStream){ + foreach($b->getChunks() as $chunk){ + if($chunk instanceof NodesList){ + $list = $chunk; + } + } + } + if(!isSet($list)){ + throw new PydioException("Could not retrieve recycle bin content"); + } + $selection = UserSelection::fromContext($ctx, []); + $selection->initFromNodes($list->getChildren()); + $logMessages = []; + $errorMessage = $this->delete($selection, $logMessages, $taskId); + if (count($logMessages)) { + $logMessage = new UserMessage(join("\n", $logMessages)); + } + if($errorMessage) { + throw new PydioException($errorMessage); + } + $this->logInfo("Delete", ["files"=>$this->addSlugToPath($selection)]); + $nodesDiffs->remove($selection->getFiles()); + + break; + + //------------------------------------ + // COPY / MOVE + //------------------------------------ + case "copy": + case "move": + + if ($selection->isEmpty()) { + throw new PydioException("", 113); + } + $taskId = $request->getAttribute("pydio-task-id"); + // Compute copy size + $size = 0; + $nodes = $selection->buildNodes(); + $bgSizeThreshold = 10*1024*1024; + $bgWorkerThreshold = 80*1024*1024; + if(!MetaStreamWrapper::wrapperIsRemote($selection->currentBaseUrl())){ + foreach($nodes as $node){ + $size += $node->getSizeRecursive(); + } + }else if(!$selection->isUnique() || !$selection->getUniqueNode()->isLeaf()){ + $size = -1; + } + if($taskId === null && ($size === -1 || $size > $bgSizeThreshold)){ + $task = TaskService::actionAsTask($ctx, $action, $httpVars); + $task->setActionLabel($mess, $action === 'copy' ? '66' : '70'); + $task->setFlags(Task::FLAG_STOPPABLE); + if($size === -1 || $size > $bgWorkerThreshold){ + $task->setSchedule(new Schedule(Schedule::TYPE_ONCE_DEFER)); + } + $response = TaskService::getInstance()->enqueueTask($task, $request, $response); + break; + } + if(!empty($taskId)){ + TaskService::getInstance()->updateTaskStatus($taskId, Task::STATUS_RUNNING, "Starting operation in background"); + } + $loggedUser = $ctx->getUser(); + if($loggedUser != null && !$loggedUser->canWrite($ctx->getRepositoryId())){ + throw new PydioException("You are not allowed to write", 207); + } + $success = $error = []; + $destPath = InputFilter::decodeSecureMagic($httpVars["dest"]); + $targetBaseName = null; + if($selection->isUnique() && isSet($httpVars["targetBaseName"])){ + $targetBaseName = $httpVars["targetBaseName"]; + } + if(!file_exists($destPath) && isSet($httpVars["recycle_restore"])){ + $this->mkDir($selection->nodeForPath(PathUtils::forwardSlashDirname($destPath)), basename($destPath), false, true); + } + $this->filterUserSelectionToHidden($ctx, [$httpVars["dest"]]); + if ($selection->inZip()) { + // Set action to copy anycase (cannot move from the zip). + $action = "copy"; + $this->extractArchive($destPath, $selection, $error, $success, $taskId); + } else { + $move = ($action == "move" ? true : false); + if ($move && isSet($httpVars["force_copy_delete"])) { + $move = false; + } + $this->copyOrMove($destPath, $selection, $error, $success, $move, $targetBaseName, $taskId); + + } + + if (count($error)) { + if(!empty($taskId)) TaskService::getInstance()->updateTaskStatus($taskId, Task::STATUS_FAILED, "Error while copy/move: ".join("\n", $error)); + throw new PydioException(join("\n", $error)); + } else { + if (isSet($httpVars["force_copy_delete"])) { + $errorMessage = $this->delete($selection, $logMessages, $taskId); + if($errorMessage) { + if(!empty($taskId)) { + TaskService::getInstance()->updateTaskStatus($taskId, Task::STATUS_FAILED, "Error while deleting data: ".$errorMessage); + } + throw new PydioException($errorMessage); + } + $this->logInfo("Copy/Delete", ["files"=>$this->addSlugToPath($selection), "destination" => $this->addSlugToPath($destPath)]); + } else { + $this->logInfo(($action=="move"?"Move":"Copy"), ["files"=>$this->addSlugToPath($selection), "destination"=>$this->addSlugToPath($destPath)]); + } + $logMessage = new UserMessage(join("\n", $success)); + } + // Assume new nodes are correctly created + $destNode = $selection->nodeForPath($destPath); + foreach ($nodes as $selectedNode) { + $newNode = $destNode->createChildNode((isSet($targetBaseName)?$targetBaseName : $selectedNode->getLabel())); + if($action == "move"){ + $nodesDiffs->update($newNode, $selectedNode->getPath()); + }else{ + $nodesDiffs->add($newNode); + } + } + + if(!empty($taskId)) { + TaskService::getInstance()->updateTaskStatus($taskId, Task::STATUS_COMPLETE, ""); + Controller::applyHook("msg.instant", [$ctx, $nodesDiffs->toXML()]); + } + + break; + + + case "purge" : + + + $hardPurgeTime = intval($ctx->getRepository()->getContextOption($ctx, "PURGE_AFTER"))*3600*24; + $softPurgeTime = intval($ctx->getRepository()->getContextOption($ctx, "PURGE_AFTER_SOFT"))*3600*24; + $shareCenter = PluginsService::getInstance($ctx)->getPluginById('action.share'); + if( !($shareCenter && $shareCenter->isEnabled()) ) { + //action.share is disabled, don't look at the softPurgeTime + $softPurgeTime = 0; + } + if ($hardPurgeTime > 0 || $softPurgeTime > 0) { + $this->recursivePurge($selection->currentBaseUrl(), $hardPurgeTime, $softPurgeTime); + } + + break; + + //------------------------------------ + // RENAME + //------------------------------------ + case "rename": + + $originalNode = $selection->getUniqueNode(); + $destNode = null; + $filename_new = ""; + if (isSet($httpVars["dest"])) { + $dest = InputFilter::decodeSecureMagic($httpVars["dest"]); + $destNode = $selection->nodeForPath($dest); + $this->filterUserSelectionToHidden($ctx, [$destNode->getLabel()]); + }else if(isSet($httpVars["filename_new"])){ + $filename_new = InputFilter::decodeSecureMagic($httpVars["filename_new"]); + $this->filterUserSelectionToHidden($ctx, [$filename_new]); + } + $renamedNode = $this->rename($originalNode, $destNode, $filename_new); + + $logMessage = new UserMessage($originalNode->getLabel()." $mess[41] ".$renamedNode->getLabel()); + $nodesDiffs->update($renamedNode, $originalNode->getPath()); + $this->logInfo("Rename", [ + "files" => $this->addSlugToPath($originalNode->getUrl()), + "original" => $this->addSlugToPath($originalNode->getUrl()), + "new" => $this->addSlugToPath($renamedNode->getUrl()) + ]); + + break; + + //------------------------------------ + // CREER UN REPERTOIRE / CREATE DIR + //------------------------------------ + case "mkdir": + + $messtmp=""; + $files = $selection->getFiles(); + if(isSet($httpVars["dir"]) && isSet($httpVars["dirname"])){ + $files[] = + rtrim(InputFilter::decodeSecureMagic($httpVars["dir"], InputFilter::SANITIZE_DIRNAME), "/") + ."/". + InputFilter::decodeSecureMagic($httpVars["dirname"], InputFilter::SANITIZE_FILENAME); + } + $messages = []; + $errors = []; + $max_length = ConfService::getContextConf($ctx, "NODENAME_MAX_LENGTH"); + foreach($files as $newDirPath){ + $parentDir = PathUtils::forwardSlashDirname($newDirPath); + $basename = PathUtils::forwardSlashBasename($newDirPath); + $basename = substr($basename, 0, $max_length); + $this->filterUserSelectionToHidden($ctx, [$basename]); + $parentNode = $selection->nodeForPath($parentDir); + try{ + Controller::applyHook("node.before_create", [$parentNode->createChildNode($basename), -2]); + }catch (PydioException $e){ + $errors[] = $e->getMessage(); + continue; + } + try{ + $newNode = $this->mkDir( + $parentNode, + $basename, + (isSet($httpVars["ignore_exists"]) && $httpVars["ignore_exists"] === "true"), + (isSet($httpVars["recursive"]) && $httpVars["recursive"] === "true") + ); + }catch(PydioException $ex){ + $errors[] = $ex->getMessage(); + continue; + } + if(empty($newNode)){ + continue; + } + $messtmp.="$mess[38] ".$basename." $mess[39] "; + if ($parentDir=="") {$messtmp.="/";} else {$messtmp.= $parentDir;} + $messages[] = $messtmp; + $nodesDiffs->add($newNode); + $this->logInfo("Create Dir", ["dir"=>$this->addSlugToPath($parentDir)."/".$basename, "files"=>$this->addSlugToPath($parentDir)."/".$basename]); + } + if(count($errors)){ + if(!count($messages)){ + throw new PydioException(implode('', $errors)); + }else{ + $messages = array_merge($messages, $errors); + } + } + $logMessage = new UserMessage(implode("
      ", $messages)); + + + break; + + //------------------------------------ + // CREER UN FICHIER / CREATE FILE + //------------------------------------ + case "mkfile": + + if(empty($httpVars["filename"]) && isSet($httpVars["node"])){ + $filename= InputFilter::decodeSecureMagic($httpVars["node"]); + }else{ + $parent = rtrim(InputFilter::decodeSecureMagic($httpVars["dir"], InputFilter::SANITIZE_DIRNAME), "/"); + $filename = $parent ."/" . InputFilter::decodeSecureMagic($httpVars["filename"], InputFilter::SANITIZE_FILENAME); + } + $filename = substr($filename, 0, ConfService::getContextConf($ctx, "NODENAME_MAX_LENGTH")); + $this->filterUserSelectionToHidden($ctx, [$filename]); + $node = $selection->nodeForPath($filename); + $content = ""; + if (isSet($httpVars["content"])) { + $content = $httpVars["content"]; + } + $forceCreation = false; + if (isSet($httpVars["force"]) && $httpVars["force"] == "true"){ + $forceCreation = true; + } + $this->createEmptyFile($node, $content, $forceCreation); + $logMessage = new UserMessage($mess[34]." ".$node->getLabel()." ".$mess[39]." ". $node->getParent()->getPath()); + $this->logInfo("Create File", ["files"=>$this->addSlugToPath($node->getPath())]); + $node->loadNodeInfo(); + $nodesDiffs->add($node); + + break; + + //------------------------------------ + // CHANGE FILE PERMISSION + //------------------------------------ + case "chmod": + + $nodes = $selection->buildNodes(); + $changedFiles = []; + $chmod_value = $httpVars["chmod_value"]; + $recursive = $httpVars["recursive"]; + $recur_apply_to = $httpVars["recur_apply_to"]; + foreach ($nodes as $node) { + $this->chmod($node, $chmod_value, ($recursive=="on"), ($recursive=="on"?$recur_apply_to:"both"), $changedFiles); + } + $logMessage= new UserMessage("Successfully changed permission to ".$chmod_value." for ".count($changedFiles)." files or folders"); + $this->logInfo("Chmod", [ + "files" => array_map([$this, "addSlugToPath"], $selection->getFiles()), + "filesCount" =>count($changedFiles) + ]); + $nodesDiffs->update($nodes); + + break; + + case "lsync" : + + $fromNode = null; + $toNode = null; + $copyOrMove = false; + if (isSet($httpVars["from"])) { + $fromNode = $selection->nodeForPath(InputFilter::decodeSecureMagic($httpVars["from"])); + } + if (isSet($httpVars["to"])) { + $toNode = $selection->nodeForPath(InputFilter::decodeSecureMagic($httpVars["to"])); + } + if (isSet($httpVars["copy"]) && $httpVars["copy"] == "true") { + $copyOrMove = true; + } + Controller::applyHook("node.change", [$fromNode, $toNode, $copyOrMove]); + + break; + + //------------------------------------ + // XML LISTING + //------------------------------------ + case "ls": + + $nodesList = new NodesList(); + + if($selection->isUnique() && $request->getAttribute("api") == "v2" && !empty($httpVars["children"])){ + $dir = $selection->getUniqueFile(); + $selection->setFiles([]); + }else{ + $dir = InputFilter::sanitize($httpVars["dir"], InputFilter::SANITIZE_DIRNAME) OR ""; + } + $patch = false; + if (MetaStreamWrapper::actualRepositoryWrapperClass(new AJXP_Node($selection->currentBaseUrl())) === "Pydio\\Access\\Driver\\StreamProvider\\FS\\FsAccessWrapper") { + $dir = PathUtils::patchPathForBaseDir($dir); + $patch = true; + } + $dir = InputFilter::securePath($dir); + + // FILTER DIR PAGINATION ANCHOR + $page = null; + if (isSet($dir) && strstr($dir, "%23")!==false) { + $parts = explode("%23", $dir); + $dir = $parts[0]; + $page = $parts[1]; + } + + if(!isSet($dir) || $dir == "/") $dir = ""; + $lsOptions = $this->parseLsOptions((isSet($httpVars["options"])?$httpVars["options"]:"a")); + + $startTime = microtime(); + $path = $selection->nodeForPath(($dir!= ""?($dir[0]=="/"?"":"/").$dir:""))->getUrl(); + $nonPatchedPath = $path; + if ($patch) { + $nonPatchedPath = PathUtils::unPatchPathForBaseDir($path); + } + $testPath = @stat($path); + if($testPath === null || $testPath === false){ + throw new \Exception("There was a problem trying to open folder ". $path. ", please check your Administrator"); + } + if(!is_readable($path) && !is_writeable($path)){ + throw new \Exception("You are not allowed to access folder " . $path); + } + // Backward compat + if($selection->isUnique() && strpos($selection->getUniqueFile(), "/") !== 0){ + $selection->setFiles([$dir . "/" . $selection->getUniqueFile()]); + } + + $orderField = $orderDirection = null; + $threshold = 500; + $limitPerPage = 200; + $defaultOrder = $ctx->getRepository()->getContextOption($ctx, "REMOTE_SORTING_DEFAULT_COLUMN"); + $defaultDirection = $ctx->getRepository()->getContextOption($ctx, "REMOTE_SORTING_DEFAULT_DIRECTION"); + if ($ctx->getRepository()->getContextOption($ctx, "REMOTE_SORTING")) { + $orderDirection = isSet($httpVars["order_direction"])?strtolower($httpVars["order_direction"]):$defaultDirection; + $orderField = isSet($httpVars["order_column"])?$httpVars["order_column"]:$defaultOrder; + if ($orderField != null && !in_array($orderField, ["ajxp_label", "filesize", "ajxp_modiftime", "mimestring"])) { + $orderField = $defaultOrder; + } + } + if(!isSet($httpVars["recursive"]) || $httpVars["recursive"] != "true"){ + $threshold = $ctx->getRepository()->getContextOption($ctx, "PAGINATION_THRESHOLD"); + if(!isSet($threshold) || intval($threshold) == 0) $threshold = 500; + $limitPerPage = $ctx->getRepository()->getContextOption($ctx, "PAGINATION_NUMBER"); + if(!isset($limitPerPage) || intval($limitPerPage) == 0) $limitPerPage = 200; + } + + if(!$selection->isEmpty()){ + $uniqueNodes = $selection->buildNodes(); + $parentAjxpNode = $selection->nodeForPath("/"); + Controller::applyHook("node.read", [&$parentAjxpNode]); + $nodesList->setParentNode($parentAjxpNode); + foreach($uniqueNodes as $node){ + if(!file_exists($node->getUrl()) || (!is_readable($node->getUrl()) && !is_writable($node->getUrl()))) continue; + $nodeName = $node->getLabel(); + if (!$this->filterNodeName($ctx, $node->getPath(), $nodeName, $isLeaf, $lsOptions)) { + continue; + } + if (RecycleBinManager::recycleEnabled() && $node->getPath() == RecycleBinManager::getRecyclePath()) { + continue; + } + $node->loadNodeInfo(false, false, ($lsOptions["l"]?"all":"minimal")); + if (!empty($node->metaData["nodeName"]) && $node->metaData["nodeName"] != $nodeName) { + $node->setUrl(PathUtils::forwardSlashDirname($node->getUrl())."/".$node->metaData["nodeName"]); + } + if (!empty($node->metaData["hidden"]) && $node->metaData["hidden"] === true) { + continue; + } + if (!empty($node->metaData["mimestring_id"]) && array_key_exists($node->metaData["mimestring_id"], $mess)) { + $node->mergeMetadata(["mimestring" => $mess[$node->metaData["mimestring_id"]]]); + } + if(isSet($httpVars["page_position"]) && $httpVars["page_position"] == "true"){ + // Detect page position: we have to loading "siblings" + $parentPath = PathUtils::forwardSlashDirname($node->getPath()); + $siblings = scandir($selection->currentBaseUrl().$parentPath); + foreach($siblings as $i => $s){ + if($this->filterFile($ctx, $s, true)) unset($siblings[$i]); + if($this->filterFolder($ctx, $s)) unset($siblings[$i]); + } + if(count($siblings) > $threshold){ + //usort($siblings, "strcasecmp"); + $siblings = $this->orderNodes($siblings, $selection->currentBaseUrl().$parentPath, $orderField, $orderDirection); + $index = array_search($node->getLabel(), $siblings); + $node->mergeMetadata(["page_position" => floor($index / $limitPerPage) +1]); + } + } + $nodesList->addBranch($node); + } + break; + } + + $metaData = []; + if (RecycleBinManager::recycleEnabled() && $dir == "") { + $metaData["repo_has_recycle"] = RecycleBinManager::getRelativeRecycle(); + } + $parentAjxpNode = new AJXP_Node($nonPatchedPath, $metaData); + $parentAjxpNode->loadNodeInfo(false, true, ($lsOptions["l"]?"all":"minimal")); + Controller::applyHook("node.read", [&$parentAjxpNode]); + + $streamIsSeekable = MetaStreamWrapper::wrapperIsSeekable($path); + + $sharedHandle = null; $handle = null; + if($streamIsSeekable){ + $handle = opendir($path); + $sharedHandle = $handle; + } + $countFiles = $this->countChildren($parentAjxpNode, !$lsOptions["f"], false, $sharedHandle); + if(isSet($sharedHandle)){ + rewind($handle); + } + $totalPages = $crtPage = 1; + if (isSet($threshold) && isSet($limitPerPage) && $countFiles > $threshold) { + $offset = 0; + $crtPage = 1; + if (isSet($page)) { + $offset = (intval($page)-1)*$limitPerPage; + $crtPage = $page; + } + $totalPages = floor($countFiles / $limitPerPage) + 1; + } else { + $offset = $limitPerPage = 0; + } + + $nodesList->setParentNode($parentAjxpNode); + if (isSet($totalPages) && isSet($crtPage) && ($totalPages > 1 || !UserAgent::userAgentIsNativePydioApp())) { + $remoteOptions = null; + if ($this->getContextualOption($ctx, "REMOTE_SORTING")) { + $remoteOptions = [ + "remote_order" => "true", + "currentOrderCol" => isSet($orderField)?$orderField:$defaultOrder, + "currentOrderDir"=> isSet($orderDirection)?$orderDirection:$defaultDirection + ]; + } + $foldersCounts = $this->countChildren($parentAjxpNode, TRUE, false, $sharedHandle); + if(isSet($sharedHandle)) { + rewind($sharedHandle); + } + $nodesList->setPaginationData($countFiles, $crtPage, $totalPages, $foldersCounts, $remoteOptions); + if ($totalPages > 1 && !$lsOptions["f"]) { + if(isSet($sharedHandle)) { + closedir($sharedHandle); + } + break; + } + } + + $cursor = 0; + if(isSet($sharedHandle)){ + $handle = $sharedHandle; + }else{ + $handle = opendir($path); + } + if (!$handle) { + throw new PydioException("Cannot open dir ".$nonPatchedPath); + } + $nodes = []; + while(false !== ($file = readdir($handle))){ + $nodes[] = $file; + } + closedir($handle); + $fullList = ["d" => [], "z" => [], "f" => []]; + + //$nodes = scandir($path); + $nodes = $this->orderNodes($nodes, $nonPatchedPath, $orderField, $orderDirection); + + foreach ($nodes as $nodeName) { + if($nodeName == "." || $nodeName == "..") { + continue; + } + $isLeaf = ""; + if (!$this->filterNodeName($ctx, $path, $nodeName, $isLeaf, $lsOptions)) { + continue; + } + if (RecycleBinManager::recycleEnabled() && $dir == "" && "/".$nodeName == RecycleBinManager::getRecyclePath()) { + continue; + } + if ($offset > 0 && $cursor < $offset) { + $cursor ++; + continue; + } + + if ($limitPerPage > 0 && ($cursor - $offset) >= $limitPerPage) { + break; + } + + $currentFile = $nonPatchedPath."/".$nodeName; + $meta = []; + if($isLeaf != "") $meta = ["is_file" => ($isLeaf?"1":"0")]; + $node = new AJXP_Node($currentFile, $meta); + $node->setLabel($nodeName); + $node->loadNodeInfo(false, false, ($lsOptions["l"]?"all":"minimal")); + if (!empty($node->metaData["nodeName"]) && $node->metaData["nodeName"] != $nodeName) { + $node->setUrl($nonPatchedPath."/".$node->metaData["nodeName"]); + } + if (!empty($node->metaData["hidden"]) && $node->metaData["hidden"] === true) { + continue; + } + if (!empty($node->metaData["mimestring_id"]) && array_key_exists($node->metaData["mimestring_id"], $mess)) { + $node->mergeMetadata(["mimestring" => $mess[$node->metaData["mimestring_id"]]]); + } + if (isSet($originalLimitPerPage) && $cursor > $originalLimitPerPage) { + $node->mergeMetadata(["page_position" => floor($cursor / $originalLimitPerPage) +1]); + } + + $nodeType = "d"; + if ($node->isLeaf()) { + if (StatHelper::isBrowsableArchive($nodeName)) { + if ($lsOptions["f"] && $lsOptions["z"]) { + $nodeType = "f"; + } else { + $nodeType = "z"; + } + } else $nodeType = "f"; + } + // There is a special sorting, cancel the reordering of files & folders. + if(isSet($orderField) && $orderField != "ajxp_label" && !(isSet($httpVars["recursive"]) && $httpVars["recursive"] == "true" )) { + $nodeType = "f"; + } + $fullList[$nodeType][$nodeName] = $node; + $cursor ++; + } + if (isSet($httpVars["recursive"]) && $httpVars["recursive"] == "true") { + + $max_depth = (isSet($httpVars["max_depth"])?intval($httpVars["max_depth"]):0); + $max_nodes = (isSet($httpVars["max_nodes"])?intval($httpVars["max_nodes"]):0); + $crt_depth = (isSet($httpVars["crt_depth"])?intval($httpVars["crt_depth"])+1:1); + $crt_nodes = (isSet($httpVars["crt_nodes"])?intval($httpVars["crt_nodes"]):0); + $crt_nodes += $countFiles; + + $breakNow = false; + if(isSet($max_depth) && $max_depth > 0 && $crt_depth >= $max_depth) $breakNow = true; + if(isSet($max_nodes) && $max_nodes > 0 && $crt_nodes >= $max_nodes) $breakNow = true; + /** + * @var $nodeDir AJXP_Node + */ + foreach ($fullList["d"] as &$nodeDir) { + if($breakNow){ + $nodeDir->mergeMetadata(["ajxp_has_children" => $this->countChildren($nodeDir, false, true)?"true":"false"]); + $nodesList->addBranch($nodeDir); + continue; + } + $newBody = [ + "dir" => $nodeDir->getPath(), + "options"=> $httpVars["options"], + "recursive" => "true", + "max_depth"=> $max_depth, + "max_nodes"=> $max_nodes, + "crt_depth"=> $crt_depth, + "crt_nodes"=> $crt_nodes, + ]; + $fakeRequest = Controller::executableRequest($request->getAttribute("ctx"), "ls", $newBody); + $fakeRequest = $fakeRequest->withAttribute("parent_node_list", $nodesList); + $this->switchAction($fakeRequest, new Response()); + } + + } else { + + array_map([$nodesList, "addBranch"], $fullList["d"]); + + } + array_map([$nodesList, "addBranch"], $fullList["z"]); + array_map([$nodesList, "addBranch"], $fullList["f"]); + + // ADD RECYCLE BIN TO THE LIST + if ($dir == "" && $lsOptions["d"] && RecycleBinManager::recycleEnabled() && $this->getContextualOption($ctx, "HIDE_RECYCLE") !== true) { + $recycleBinOption = RecycleBinManager::getRelativeRecycle(); + $recycleNode = $selection->nodeForPath("/".$recycleBinOption); + if (file_exists($recycleNode->getUrl())) { + $recycleNode->loadNodeInfo(); + $nodesList->addBranch($recycleNode); + } + } + + $this->logDebug("LS Time : ".intval((microtime()-$startTime)*1000)."ms"); + + $parentList = $request->getAttribute("parent_node_list", null); + if($parentList !== null){ + $parentList->addBranch($nodesList); + } + + break; + } + + + if(isSet($logMessage) || !$nodesDiffs->isEmpty() || isSet($nodesList)){ + $body = new SerializableResponseStream(); + if(isSet($logMessage)) { + $body->addChunk($logMessage); + } + if(!$nodesDiffs->isEmpty()) { + $body->addChunk($nodesDiffs); + } + if(isSet($nodesList)) { + $body->addChunk($nodesList); + } + $response = $response->withBody($body); + } + + } + + /** + * @param $nodes + * @param $path + * @param $orderField + * @param $orderDirection + * @return array + */ + protected function orderNodes($nodes, $path, $orderField, $orderDirection){ + + usort($nodes, "strcasecmp"); + if (!empty($orderField) && !empty($orderDirection) && $orderField == "ajxp_label" && $orderDirection == "desc") { + $nodes = array_reverse($nodes); + } + if (!empty($this->driverConf["SCANDIR_RESULT_SORTFONC"])) { + usort($nodes, $this->driverConf["SCANDIR_RESULT_SORTFONC"]); + } + if (!empty($orderField) && !empty($orderDirection) && $orderField != "ajxp_label") { + $toSort = []; + foreach ($nodes as $node) { + if($orderField == "filesize") $toSort[$node] = is_file($path."/".$node) ? filesize($path."/".$node) : 0; + else if($orderField == "ajxp_modiftime") $toSort[$node] = filemtime($path."/".$node); + else if($orderField == "mimestring") $toSort[$node] = pathinfo($node, PATHINFO_EXTENSION); + } + if($orderDirection == "asc") asort($toSort); + else arsort($toSort); + $nodes = array_keys($toSort); + } + return $nodes; + + } + + /** + * @param $optionString + * @return array + */ + public function parseLsOptions($optionString) + { + // LS OPTIONS : dz , a, d, z, all of these with or without l + // d : directories + // z : archives + // f : files + // => a : all, alias to dzf + // l : list metadata + $allowed = ["a", "d", "z", "f", "l"]; + $lsOptions = []; + foreach ($allowed as $key) { + if (strchr($optionString, $key)!==false) { + $lsOptions[$key] = true; + } else { + $lsOptions[$key] = false; + } + } + if ($lsOptions["a"]) { + $lsOptions["d"] = $lsOptions["z"] = $lsOptions["f"] = true; + } + return $lsOptions; + } + + /** + * Update node metadata with core FS metadata. + * @param \Pydio\Access\Core\Model\AJXP_Node $ajxpNode + * @param bool $parentNode + * @param bool $details + * @return void + */ + public function loadNodeInfo(&$ajxpNode, $parentNode = false, $details = false) + { + $nodeName = basename($ajxpNode->getPath()); + $metaData = $ajxpNode->metadata; + if (!isSet($metaData["is_file"])) { + $isLeaf = is_file($ajxpNode->getUrl()) || StatHelper::isBrowsableArchive($nodeName); + $metaData["is_file"] = ($isLeaf?"1":"0"); + } else { + $isLeaf = $metaData["is_file"] == "1" ? true : false; + } + $metaData["filename"] = $ajxpNode->getPath(); + + if (RecycleBinManager::recycleEnabled() && $ajxpNode->getPath() == RecycleBinManager::getRelativeRecycle()) { + $recycleIcon = ($this->countChildren($ajxpNode, false, true)>0?"trashcan_full.png":"trashcan.png"); + $metaData["icon"] = $recycleIcon; + $metaData["fonticon"] = "delete"; + $metaData["mimestring_id"] = 122; + //$ajxpNode->setLabel($mess[122]); + $metaData["ajxp_mime"] = "ajxp_recycle"; + } else { + $mimeData = StatHelper::getMimeInfo($ajxpNode, !$isLeaf); + $metaData["mimestring_id"] = $mimeData[0]; + $metaData["icon"] = $mimeData[1]; + if(!empty($mimeData[2])){ + $metaData["fonticon"] = $mimeData[2]; + } + if ($metaData["icon"] == "folder.png") { + $metaData["openicon"] = "folder_open.png"; + } + if (!$isLeaf) { + $metaData["ajxp_mime"] = "ajxp_folder"; + } + } + + $metaData["file_group"] = @filegroup($ajxpNode->getUrl()) || "unknown"; + $metaData["file_owner"] = @fileowner($ajxpNode->getUrl()) || "unknown"; + $metaData["ajxp_readonly"] = "false"; + if (!@$this->isWriteable($ajxpNode)) { + $metaData["ajxp_readonly"] = "true"; + } + $fPerms = @fileperms($ajxpNode->getUrl()); + if ($fPerms !== false) { + $fPerms = substr(decoct( $fPerms ), ($isLeaf?2:1)); + } else { + $fPerms = '0000'; + } + $metaData["file_perms"] = $fPerms; + $datemodif = $this->date_modif($ajxpNode->getUrl()); + $metaData["ajxp_modiftime"] = ($datemodif ? $datemodif : "0"); + //$metaData["ajxp_description"] =$metaData["ajxp_relativetime"] = $mess[4]." ". StatHelper::relativeDate($datemodif, $mess); + $metaData["bytesize"] = 0; + if ($isLeaf) { + $metaData["bytesize"] = filesize($ajxpNode->getUrl()); + } + //$metaData["filesize"] = StatHelper::roundSize($metaData["bytesize"]); + if (StatHelper::isBrowsableArchive($nodeName)) { + $metaData["ajxp_mime"] = "ajxp_browsable_archive"; + } + + if ($details == "minimal") { + $miniMeta = [ + "is_file" => $metaData["is_file"], + "filename" => $metaData["filename"], + "bytesize" => $metaData["bytesize"], + "ajxp_modiftime" => $metaData["ajxp_modiftime"], + ]; + $ajxpNode->mergeMetadata($miniMeta); + } else { + $ajxpNode->mergeMetadata($metaData); + } + + } + + /** + * Update nodes metadata with localized info (will NOT be cached) + * Hooked to node.info.nocache + * @param AJXP_Node $ajxpNode + * @param bool $parentNode + * @param bool $details + */ + public function localizeNodeInfo(&$ajxpNode, $parentNode = false, $details = false){ + + $messages = LocaleService::getMessages(); + $localMeta = []; + + // Recompute "Modifed on ... " string + $currentMeta = $ajxpNode->getNodeInfoMeta(); + if(!empty($currentMeta["ajxp_modiftime"])){ + $dateModif = $currentMeta["ajxp_modiftime"]; + $localMeta["ajxp_description"] = $localMeta["ajxp_relativetime"] = $messages[4]." ". StatHelper::relativeDate($dateModif, $messages); + } + + // Recompute human readable size + if(!empty($currentMeta["bytesize"])){ + $localMeta["filesize"] = StatHelper::roundSize($currentMeta["bytesize"]); + } + + // Update Recycle Bin label + if ($currentMeta["ajxp_mime"] === "ajxp_recycle"){ + $ajxpNode->setLabel($messages[122]); + } + + // Now remerge in node + if(count($localMeta)){ + $ajxpNode->mergeMetadata($localMeta); + } + + } + + /** + * @param array|UploadedFileInterface $uploadData Php-upload array + * @param String $destination Full path to destination file, including stream data + * @param array $messages Application messages table + * @return bool + * @throws \Exception + */ + protected function copyUploadedData($uploadData, $destination, $messages){ + if(is_array($uploadData)){ + $isInputStream = isSet($uploadData["input_upload"]); + $newFileSize = $uploadData["size"]; + }else{ + $isInputStream = $uploadData->getStream() !== null; + $newFileSize = $uploadData->getSize(); + } + + if ($isInputStream) { + try { + $this->logDebug("Begining reading INPUT stream"); + if(is_array($uploadData)){ + $input = fopen("php://input", "r"); + }else{ + $input = $uploadData->getStream()->detach(); + } + $output = fopen($destination, "w"); + $sizeRead = 0; + while ($sizeRead < intval($newFileSize)) { + $chunk = fread($input, 4096); + $sizeRead += strlen($chunk); + fwrite($output, $chunk, strlen($chunk)); + } + fclose($input); + fclose($output); + $this->logDebug("End reading INPUT stream"); + } catch (\Exception $e) { + throw new \Exception($e->getMessage(), 411); + } + } else { + if(is_array($uploadData)){ + $result = @move_uploaded_file($uploadData["tmp_name"], $destination); + if (!$result) { + $realPath = MetaStreamWrapper::getRealFSReference($destination); + $result = move_uploaded_file($uploadData["tmp_name"], $realPath); + } + }else{ + $clone = clone $uploadData; + try{ + $uploadData->moveTo($destination); + $result = true; + }catch(\Exception $e){ + // Can be blocked by open_basedir, try to perform the move again, with the + // real FS reference. + $realPath = MetaStreamWrapper::getRealFSReference($destination); + try{ + $clone->moveTo($realPath); + $result = true; + }catch(\Exception $e){ + $result = false; + } + } + } + if (!$result) { + $errorMessage="$messages[33] ". PathUtils::forwardSlashBasename($destination); + throw new \Exception($errorMessage, 411); + } + } + return true; + } + + /** + * @param String $folder Folder destination + * @param String $source Maybe updated by the function + * @param String $target Existing part to append data + * @return bool If the target file already existed or not. + * @throws \Exception + */ + protected function appendUploadedData($folder, $source, $target){ + + $already_existed = false; + if($source == $target){ + throw new \Exception("Something nasty happened: trying to copy $source into itself, it will create a loop!"); + } + if (file_exists($folder ."/" . $target)) { + $already_existed = true; + $this->logDebug("Should copy stream from $source to $target"); + $partO = fopen($folder."/".$source, "r"); + $appendF = fopen($folder ."/". $target, "a+"); + while (!feof($partO)) { + $buf = fread($partO, 1024); + fwrite($appendF, $buf, strlen($buf)); + } + fclose($partO); + fclose($appendF); + $this->logDebug("Done, closing streams!"); + } + @unlink($folder."/".$source); + return $already_existed; + + } + + /** + * @param AJXP_Node $dirNode + * @param bool $foldersOnly + * @param bool $nonEmptyCheckOnly + * @param null $dirHANDLE + * @return int + * @throws \Exception + */ + public function countChildren(AJXP_Node $dirNode, $foldersOnly = false, $nonEmptyCheckOnly = false, $dirHANDLE = null) + { + $dirName = $dirNode->getUrl(); + if(is_resource($dirHANDLE)){ + $handle = $dirHANDLE; + }else{ + $handle=@opendir($dirName); + } + if ($handle === false) { + throw new \Exception("Error while trying to open directory ".$dirName); + } + if ($foldersOnly && !$dirNode->wrapperIsRemote()) { + if($dirHANDLE == null || !is_resource($dirHANDLE)){ + closedir($handle); + } + $path = $dirNode->getRealFile(); + $dirs = glob($path."/*", GLOB_ONLYDIR|GLOB_NOSORT); + if($dirs === false) return 0; + return count($dirs); + } + $count = 0; + $showHiddenFiles = $this->getContextualOption($dirNode->getContext(), "SHOW_HIDDEN_FILES"); + while (false !== ($file = readdir($handle))) { + if($file != "." && $file !=".." + && !(StatHelper::isHidden($file) && !$showHiddenFiles)){ + if($foldersOnly && is_file($dirName."/".$file)) continue; + $count++; + if($nonEmptyCheckOnly) break; + } + } + if($dirHANDLE == null || !is_resource($dirHANDLE)){ + closedir($handle); + } + return $count; + } + + /** + * @param $file + * @return int + */ + public function date_modif($file) + { + $tmp = @filemtime($file) or 0; + return $tmp;// date("d,m L Y H:i:s",$tmp); + } + + /** + * @param $crtUrlBase + * @param $status + * @param $data + * @param null $taskId + * @return int + * @throws \Exception + */ + public function extractArchiveItemPreCallback($crtUrlBase, $status, $data, $taskId = null){ + $fullname = $data['filename']; + $size = $data['size']; + $realBase = MetaStreamWrapper::getRealFSReference($crtUrlBase); + $realBase = str_replace("\\", "/", $realBase); + $repoName = $crtUrlBase.str_replace($realBase, "", $fullname); + + $toNode = new AJXP_Node($repoName); + $toNode->setLeaf($data['folder'] ? false:true); + if(file_exists($toNode->getUrl())){ + Controller::applyHook("node.before_change", [$toNode, $size]); + }else{ + Controller::applyHook("node.before_create", [$toNode, $size]); + } + return 1; + } + + /** + * @param $crtUrlBase + * @param $status + * @param $data + * @param null $taskId + * @return int + * @throws PydioException + * @throws \Exception + */ + public function extractArchiveItemPostCallback($crtUrlBase, $status, $data, $taskId = null){ + $fullname = $data['filename']; + $realBase = MetaStreamWrapper::getRealFSReference($crtUrlBase); + $repoName = str_replace($realBase, "", $fullname); + try{ + $this->filterUserSelectionToHidden(AJXP_Node::contextFromUrl($crtUrlBase), [$repoName]); + }catch(\Exception $e){ + @unlink($this->urlBase.$repoName); + return 1; + } + if($taskId !== null){ + TaskService::getInstance()->updateTaskStatus($taskId, Task::STATUS_RUNNING, "Extracted file ".$repoName); + } + $toNode = new AJXP_Node($crtUrlBase.$repoName); + $toNode->setLeaf($data['folder'] ? false:true); + Controller::applyHook("node.change", [null, $toNode, false]); + return 1; + } + + /** + * Extract an archive directly inside the dest directory. + * + * @param string $destDir + * @param UserSelection $selection + * @param array $error + * @param array $success + * @param string $taskId + */ + public function extractArchive($destDir, $selection, &$error, &$success, $taskId = null) + { + require_once(AJXP_BIN_FOLDER."/lib/pclzip.lib.php"); + $zipPath = $selection->getZipPath(true); + $zipLocalPath = $selection->getZipLocalPath(true); + if(strlen($zipLocalPath)>1 && $zipLocalPath[0] == "/") $zipLocalPath = substr($zipLocalPath, 1)."/"; + $files = $selection->getFiles(); + $currentUrlBase = $selection->currentBaseUrl(); + + $realZipFile = MetaStreamWrapper::getRealFSReference($currentUrlBase.$zipPath); + $archive = new PclZip($realZipFile); + $content = $archive->listContent(); + foreach ($files as $key => $item) {// Remove path + $item = substr($item, strlen($zipPath)); + if($item[0] == "/") $item = substr($item, 1); + foreach ($content as $zipItem) { + if ($zipItem["stored_filename"] == $item || $zipItem["stored_filename"] == $item."/") { + $files[$key] = $zipItem["stored_filename"]; + break; + } else { + unset($files[$key]); + } + } + } + $this->logDebug("Archive", $this->addSlugToPath($files)); + $realDestination = MetaStreamWrapper::getRealFSReference($currentUrlBase.$destDir); + $this->logDebug("Extract", [$realDestination, $realZipFile, $this->addSlugToPath($files), $zipLocalPath]); + + $result = $archive->extract(PCLZIP_OPT_BY_NAME, $files, + PCLZIP_OPT_PATH, $realDestination, + PCLZIP_OPT_REMOVE_PATH, $zipLocalPath, + PCLZIP_CB_PRE_EXTRACT, function($status, $data) use ($currentUrlBase, $taskId) { return $this->extractArchiveItemPreCallback($currentUrlBase, $status, $data, $taskId); }, + PCLZIP_CB_POST_EXTRACT, function($status, $data) use ($currentUrlBase, $taskId) { return $this->extractArchiveItemPostCallback($currentUrlBase, $status, $data, $taskId); }, + PCLZIP_OPT_STOP_ON_ERROR + ); + + if ($result <= 0) { + $error[] = $archive->errorInfo(true); + } else { + $mess = LocaleService::getMessages(); + $success[] = sprintf($mess[368], basename($zipPath), $destDir); + } + } + + /** + * @param string $destDir + * @param UserSelection $selection + * @param array $error + * @param array $success + * @param bool $move + * @param string|null $targetBaseName + * @param string|null $taskId + * @throws \Exception + */ + public function copyOrMove($destDir, $selection, &$error, &$success, $move = false, $targetBaseName = null, $taskId = null) + { + $selectedNodes = $selection->buildNodes(); + $selectedFiles = $selection->getFiles(); + $this->logDebug("CopyMove", ["dest"=>$this->addSlugToPath($destDir), "selection" => $this->addSlugToPath($selectedFiles)]); + $mess = LocaleService::getMessages(); + if (!$this->isWriteable($selection->nodeForPath($destDir))) { + $error[] = $mess[38]." ".$destDir." ".$mess[99]; + return ; + } + $repoData = [ + 'base_url' => $selection->currentBaseUrl(), + 'chmod' => $selection->getContext()->getRepository()->getContextOption($selection->getContext(), 'CHMOD_VALUE'), + 'recycle' => $selection->getContext()->getRepository()->getContextOption($selection->getContext(), 'RECYCLE_BIN') + ]; + foreach ($selectedNodes as $selectedNode) { + $selectedFile = $selectedNode->getPath(); + if ($move && !$this->isWriteable($selection->nodeForPath(PathUtils::forwardSlashDirname($selectedFile)))) { + $error[] = "\n".$mess[38]." ".PathUtils::forwardSlashDirname($selectedFile)." ".$mess[99]; + continue; + } + if( !empty ($targetBaseName)){ + $destFile = $destDir ."/" . $targetBaseName; + }else{ + $bName = basename($selectedFile); + $localName = ''; + Controller::applyHook("dl.localname", [$selectedNode, &$localName]); + if(!empty($localName)) $bName = $localName; + $destFile = $destDir ."/". $bName; + } + $this->copyOrMoveFile($destFile, $selectedFile, $error, $success, $move, $repoData, $repoData, $taskId); + } + } + + /** + * @param AJXP_Node $originalNode + * @param AJXP_Node $dest + * @param string $filename_new + * @return AJXP_Node + * @throws PydioException + * @throws \Exception + */ + public function rename($originalNode, $dest = null, $filename_new = null) + { + $mess = LocaleService::getMessages(); + + if(!empty($filename_new)){ + $filename_new= InputFilter::sanitize(InputFilter::magicDequote($filename_new), InputFilter::SANITIZE_FILENAME, true); + $filename_new = substr($filename_new, 0, ConfService::getContextConf($originalNode->getContext(), "NODENAME_MAX_LENGTH")); + } + + if (empty($filename_new) && empty($dest)) { + throw new PydioException("$mess[37]"); + } + + if (!$this->isWriteable($originalNode)) { + throw new PydioException($mess[34]." ".$originalNode->getLabel()." ".$mess[99]); + } + + if($dest == null) { + $newNode = $originalNode->getParent()->createChildNode($filename_new); + } else { + $newNode = $dest; + } + if (file_exists($newNode->getUrl())) { + throw new PydioException($newNode->getPath()." $mess[43]"); + } + if (!file_exists($originalNode->getUrl())) { + throw new PydioException($mess[100]." ".$originalNode->getPath()); + } + Controller::applyHook("node.before_path_change", [&$originalNode]); + $test = @rename($originalNode->getUrl(),$newNode->getUrl()); + if($test === false){ + throw new \Exception("Error while renaming ".$originalNode->getPath()." to ".$newNode->getPath()); + } + Controller::applyHook("node.change", [$originalNode, $newNode, false]); + return $newNode; + + } + + /** + * @param $destination + * @param $fileName + * @return string + */ + public static function autoRenameForDest($destination, $fileName) + { + if(!is_file($destination."/".$fileName)) return $fileName; + $i = 1; + $ext = ""; + $split = explode(".", $fileName); + if (count($split) > 1) { + $ext = ".".$split[count($split)-1]; + array_pop($split); + $name = join(".", $split); + } else { + $name = $fileName; + } + while (is_file($destination."/".$name."-$i".$ext)) { + $i++; // increment i until finding a non existing file. + } + return $name."-$i".$ext; + } + + /** + * @param AJXP_Node $parentNode + * @param String $newDirName + * @param bool $ignoreExists + * @param bool $createRecursive + * @return AJXP_Node + * @throws PydioException + * @throws \Exception + */ + public function mkDir($parentNode, $newDirName, $ignoreExists = false, $createRecursive = false) + { + if(!file_exists($parentNode->getUrl()) && $createRecursive){ + $this->mkDir($parentNode->getParent(), basename($parentNode->getUrl()), $ignoreExists, true); + } + Controller::applyHook("node.before_change", [&$parentNode]); + + $mess = LocaleService::getMessages(); + if ($newDirName=="") { + throw new PydioException($mess[37]); + } + if (file_exists($parentNode->getUrl()."/".$newDirName)) { + if($ignoreExists) { + return $parentNode->createChildNode($newDirName); + } + throw new PydioException($mess[40]); + } + if (!file_exists($parentNode->getUrl())){ + throw new PydioException($mess[103]." ".$parentNode->getPath()); + } + if (!$this->isWriteable($parentNode)) { + throw new PydioException($mess[38]." ".$parentNode->getPath()." ".$mess[99]); + } + + $dirMode = 0775; + $ctx = $parentNode->getContext(); + $chmodValue = $ctx->getRepository()->getContextOption($ctx, "CHMOD_VALUE"); + if (isSet($chmodValue) && $chmodValue != "") { + $dirMode = octdec(ltrim($chmodValue, "0")); + if ($dirMode & 0400) $dirMode |= 0100; // User is allowed to read, allow to list the directory + if ($dirMode & 0040) $dirMode |= 0010; // Group is allowed to read, allow to list the directory + if ($dirMode & 0004) $dirMode |= 0001; // Other are allowed to read, allow to list the directory + } + $old = umask(0); + mkdir($parentNode->getUrl()."/".$newDirName, $dirMode); + umask($old); + $newNode = $parentNode->createChildNode($newDirName); + $newNode->setLeaf(false); + Controller::applyHook("node.change", [null, $newNode, false]); + return $newNode; + + } + + /** + * @param AJXP_Node $node + * @param string $content + * @param bool $force + * @throws \Exception + */ + public function createEmptyFile(AJXP_Node $node, $content = "", $force = false) + { + Controller::applyHook("node.before_change", [$node->getParent()]); + $mess = LocaleService::getMessages(); + + if (!$force && file_exists($node->getUrl())) { + throw new PydioException($mess[71], 71); + } + if (!$this->isWriteable($node->getParent())) { + throw new PydioException("$mess[38] ".$node->getParent()->getPath()." $mess[99]", 71); + } + $ctx = $node->getContext(); + $repoData = [ + 'chmod' => $ctx->getRepository()->getContextOption($ctx, 'CHMOD_VALUE'), + 'recycle' => $ctx->getRepository()->getContextOption($ctx, 'RECYCLE_BIN') + ]; + $fp=fopen($node->getUrl(),"w"); + if ($fp) { + if ($content != "") { + fputs($fp, $content); + } + $this->changeMode($node->getUrl(), $repoData); + fclose($fp); + $node->loadNodeInfo(); + Controller::applyHook("node.change", [null, $node, false]); + } else { + throw new PydioException("$mess[102] ".$node->getPath()." (".$fp.")"); + } + } + + + /** + * @param UserSelection $selection + * @param $logMessages + * @param null $taskId + * @return null + * @throws PydioException + * @throws \Exception + */ + public function delete(UserSelection $selection, &$logMessages, $taskId = null) + { + $ctx = $selection->getContext(); + $repoData = [ + 'chmod' => $ctx->getRepository()->getContextOption($ctx, 'CHMOD_VALUE'), + 'recycle' => $ctx->getRepository()->getContextOption($ctx, 'RECYCLE_BIN') + ]; + $mess = LocaleService::getMessages(); + $selectedNodes = $selection->buildNodes(); + foreach ($selectedNodes as $selectedNode) { + + $selectedNode->loadNodeInfo(); + $fileUrl = $selectedNode->getUrl(); + $filePath = $selectedNode->getPath(); + + if (!file_exists($fileUrl)) { + $logMessages[]=$mess[100]." ".$filePath; + continue; + } + $this->deldir($fileUrl, $repoData, $taskId); + if ($selectedNode->isLeaf()) { + $logMessages[]="$mess[38] ".$filePath." $mess[44]."; + } else { + $logMessages[]="$mess[34] ".$filePath." $mess[44]."; + } + Controller::applyHook("node.change", [$selectedNode]); + } + if($taskId != null){ + TaskService::getInstance()->updateTaskStatus($taskId, Task::STATUS_COMPLETE, "Done"); + $nodesDiff = new NodesDiff(); + $nodesDiff->remove($selection->getFiles()); + $t = TaskService::getInstance()->getTaskById($taskId); + Controller::applyHook("msg.instant", [$t->getContext(), $nodesDiff->toXML()]); + } + return null; + } + + /** + * @param $origFile + * @param $destFile + * @return bool + */ + public function simpleCopy($origFile, $destFile) + { + return copy($origFile, $destFile); + } + + /** + * @param AJXP_Node $node + * @return bool + */ + public function isWriteable(AJXP_Node $node) + { + if ( $this->getContextualOption($node->getContext(), "USE_POSIX") == true && extension_loaded('posix')) { + $real = $node->getRealFile(); + return posix_access($real, POSIX_W_OK); + } + //clearstatcache(); + return is_writable($node->getUrl()); + } + + /** + * Change file permissions + * + * @param AJXP_Node $node + * @param String $chmodValue + * @param Boolean $recursive + * @param String $nodeType "both", "file", "dir" + * @param $changedFiles + * @return void + */ + public function chmod(AJXP_Node $node, $chmodValue, $recursive, $nodeType, &$changedFiles) + { + $realValue = octdec(ltrim($chmodValue, "0")); + $nodeUrl = $node->getUrl(); + if (is_file($nodeUrl)) { + if ($nodeType=="both" || $nodeType=="file") { + MetaStreamWrapper::changeMode($nodeUrl, $realValue); + $changedFiles[] = $node->getPath(); + } + } else { + if ($nodeType=="both" || $nodeType=="dir") { + MetaStreamWrapper::changeMode($nodeUrl, $realValue); + $changedFiles[] = $node->getPath(); + } + if ($recursive) { + $handler = opendir($nodeUrl); + while ($child=readdir($handler)) { + if($child == "." || $child == "..") continue; + // do not pass realValue or it will be re-decoded. + $this->chmod($node->createChildNode($child), $chmodValue, $recursive, $nodeType, $changedFiles); + } + closedir($handler); + } + } + } + + /** + * @param AJXP_Node $fromNode + * @param AJXP_Node $toNode + * @param Boolean $copy + */ + public function nodeChanged(&$fromNode = null, &$toNode = null, $copy = false) + { + Controller::applyHook("node.change", [$fromNode, $toNode, $copy]); + } + + /** + * @param AJXP_Node $node + * @param null $newSize + */ + public function nodeWillChange($node, $newSize = null) + { + if ($newSize != null) { + Controller::applyHook("node.before_change", [$node, $newSize]); + } else { + Controller::applyHook("node.before_path_change", [$node]); + } + } + + + /** + * @param UserSelection $selection + * @param string $dest + * @param string $basedir + * @param string $taskId + * @throws \Exception + * @return PclZip + */ + public function makeZip (UserSelection $selection, $dest, $basedir, $taskId = null) + { + $zipEncoding = ConfService::getContextConf($selection->getContext(), "ZIP_ENCODING"); + + @set_time_limit(0); + require_once(AJXP_BIN_FOLDER."/lib/pclzip.lib.php"); + $filePaths = []; + $selectedNodes = $selection->buildNodes(); + foreach ($selectedNodes as $node) { + $realFile = $node->getRealFile(); + if (basename($node->getPath()) == "") { + $filePaths[] = [PCLZIP_ATT_FILE_NAME => $realFile]; + } else { + $shortName = $node->getLabel(); + if(!empty($zipEncoding)){ + $test = iconv(TextEncoder::getEncoding(), $zipEncoding, $shortName); + if($test !== false) $shortName = $test; + } + $filePaths[] = [PCLZIP_ATT_FILE_NAME => $realFile, + PCLZIP_ATT_FILE_NEW_SHORT_NAME => $shortName]; + } + } + $this->logDebug("Pathes", $filePaths); + $archive = new PclZip($dest); + $zipEncoding = ConfService::getContextConf($selection->getContext(), "ZIP_ENCODING"); + $fsEncoding = TextEncoder::getEncoding(); + $ctx = $selection->getContext(); + + $preAddCallback = function($value, &$header) use ($ctx, $taskId, $zipEncoding, $fsEncoding){ + if($taskId !== null){ + TaskService::getInstance()->updateTaskStatus($taskId, Task::STATUS_RUNNING, "Adding ".$header["stored_filename"]." to archive"); + } + $search = $header["filename"]; + if(!empty($zipEncoding)){ + $test = iconv($fsEncoding, $zipEncoding, $header["stored_filename"]); + if($test !== false){ + $header["stored_filename"] = $test; + } + } + return !($this->filterFile($ctx, $search, true) || $this->filterFolder($ctx, $search, "contains")); + }; + + if($basedir == "__AJXP_ZIP_FLAT__/"){ + $vList = $archive->create($filePaths, PCLZIP_OPT_REMOVE_ALL_PATH, PCLZIP_OPT_NO_COMPRESSION, PCLZIP_OPT_ADD_TEMP_FILE_ON, PCLZIP_CB_PRE_ADD, $preAddCallback); + }else{ + $basedir = MetaStreamWrapper::getRealFSReference($selection->currentBaseUrl()).trim($basedir); + $this->logDebug("Basedir", [$basedir]); + $vList = $archive->create($filePaths, PCLZIP_OPT_REMOVE_PATH, $basedir, PCLZIP_OPT_NO_COMPRESSION, PCLZIP_OPT_ADD_TEMP_FILE_ON, PCLZIP_CB_PRE_ADD, $preAddCallback); + } + + if (!$vList) { + if($taskId !== null){ + TaskService::getInstance()->updateTaskStatus($taskId, Task::STATUS_FAILED, "Zip creation error : ($dest) ".$archive->errorInfo(true)); + } + throw new \Exception("Zip creation error : ($dest) ".$archive->errorInfo(true)); + } + return $vList; + } + + + /** + * @param $dirName + * @param $hardPurgeTime + * @param int $softPurgeTime + */ + public function recursivePurge($dirName, $hardPurgeTime, $softPurgeTime = 0) + { + $handle=opendir($dirName); + $shareCenter = false; + if(class_exists("\\Pydio\\Share\\ShareCenter")){ + $shareCenter = \Pydio\Share\ShareCenter::getShareCenter(AJXP_Node::contextFromUrl($dirName)); + } + if($handle === false){ + $this->logError(__FUNCTION__, "Cannot open folder ".$dirName); + return; + } + while (false !== ($entry = readdir($handle))) { + if ($entry == "" || $entry == ".." || StatHelper::isHidden($entry)) { + continue; + } + $fileName = $dirName."/".$entry; + if (is_file($fileName)) { + $docAge = time() - filemtime($fileName); + if ($hardPurgeTime > 0 && $docAge > $hardPurgeTime) { + $this->purge($fileName); + } elseif ($softPurgeTime > 0 && $docAge > $softPurgeTime) { + if($shareCenter !== false && $shareCenter->isShared(new AJXP_Node($fileName))) { + $this->purge($fileName); + } + } + } else { + $this->recursivePurge($fileName, $hardPurgeTime, $softPurgeTime); + } + } + closedir($handle); + } + + /** + * Apply specific operation to set a node as hidden. + * Can be overwritten, or will probably do nothing. + * @param \Pydio\Access\Core\Model\AJXP_Node $node + */ + public function setHiddenAttribute($node){ + if(MetaStreamWrapper::actualRepositoryWrapperClass($node) === "Pydio\\Access\\Driver\\StreamProvider\\FS\\FsAccessWrapper" && strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'){ + $realPath = MetaStreamWrapper::getRealFSReference($node->getUrl()); + @shell_exec("attrib +H " . escapeshellarg($realPath)); + } + } + + /** + * @param $fileName + * @throws \Exception + */ + private function purge($fileName) + { + $node = new AJXP_Node($fileName); + Controller::applyHook("node.before_path_change", [$node]); + unlink($fileName); + Controller::applyHook("node.change", [$node]); + $this->logInfo("Purge", ["file" => $fileName, "files" => $fileName]); + print(" - Purging document : ".$fileName."\n"); + } + + /** The publiclet URL making */ + public function makePublicletOptions($filePath, $password, $expire, $downloadlimit, $repository) + { + $data = [ + "DRIVER"=>$repository->getAccessType(), + "OPTIONS"=>NULL, + "FILE_PATH"=>$filePath, + "ACTION"=>"download", + "EXPIRE_TIME"=>$expire ? (time() + $expire * 86400) : 0, + "DOWNLOAD_LIMIT"=>$downloadlimit ? $downloadlimit : 0, + "PASSWORD"=>$password + ]; + return $data; + } + + /** + * @param ContextInterface $ctx + * @param array $httpVars + * @return array + * @throws \Exception + */ + public function makeSharedRepositoryOptions(ContextInterface $ctx, $httpVars) + { + $repository = $ctx->getRepository(); + $newOptions = [ + "PATH" => "AJXP_PARENT_OPTION:PATH:". InputFilter::decodeSecureMagic($httpVars["file"]), + "CREATE" => $repository->getContextOption($ctx, "CREATE"), + "RECYCLE_BIN" => isSet($httpVars["inherit_recycle"])? $repository->getContextOption($ctx, "RECYCLE_BIN") : "", + "DEFAULT_RIGHTS" => "", + "DATA_TEMPLATE" => "" + ]; + if ($repository->getContextOption($ctx, "USE_SESSION_CREDENTIALS") === true) { + $newOptions["ENCODED_CREDENTIALS"] = MemorySafe::getEncodedCredentialString(); + } + $customData = []; + foreach ($httpVars as $key => $value) { + if (substr($key, 0, strlen("PLUGINS_DATA_")) == "PLUGINS_DATA_") { + $customData[substr($key, strlen("PLUGINS_DATA_"))] = $value; + } + } + if (count($customData)) { + $newOptions["PLUGINS_DATA"] = $customData; + } + if ($repository->getContextOption($ctx, "META_SOURCES")) { + $newOptions["META_SOURCES"] = $repository->getContextOption($ctx, "META_SOURCES"); + foreach ($newOptions["META_SOURCES"] as $index => &$data) { + if (isSet($data["USE_SESSION_CREDENTIALS"]) && $data["USE_SESSION_CREDENTIALS"] === true) { + $newOptions["META_SOURCES"][$index]["ENCODED_CREDENTIALS"] = MemorySafe::getEncodedCredentialString(); + } + } + Controller::applyHook("workspace.share_metasources", [$ctx, &$newOptions["META_SOURCES"]]); + } + return $newOptions; + } + + +} \ No newline at end of file diff --git a/core/src/plugins/access.fs/FsAccessWrapper.php b/core/src/plugins/access.fs/FsAccessWrapper.php new file mode 100644 index 0000000000..75463ad436 --- /dev/null +++ b/core/src/plugins/access.fs/FsAccessWrapper.php @@ -0,0 +1,627 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + * + */ +namespace Pydio\Access\Driver\StreamProvider\FS; + +use PclZip; + +use Pydio\Access\Core\IAjxpWrapper; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Model\UserSelection; + +use Pydio\Core\Services\ConfService; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Services\ApplicationState; +use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Core\Utils\Vars\PathUtils; +use Pydio\Core\Utils\Vars\UrlUtils; +use Pydio\Core\Utils\TextEncoder; +use Pydio\Log\Core\Logger; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Wrapper for a local filesystem + * @package AjaXplorer_Plugins + * @subpackage Access + */ +class FsAccessWrapper implements IAjxpWrapper +{ + /** + * FileHandle resource + * + * @var resource + */ + protected $fp; + /** + * DirHandle resource + * + * @var resource + */ + protected $dH; + + /** + * If dH is not used but an array containing the listing + * instead. dH == -1 in that case. + * + * @var array() + */ + protected static $currentListing; + protected static $currentListingKeys; + protected static $currentListingIndex; + protected static $currentFileKey; + protected static $crtZip; + protected $realPath; + protected static $lastRealSize; + + /** + * Initialize the stream from the given path. + * + * @param string $path + * @param $streamType + * @param bool $storeOpenContext + * @param bool $skipZip + * @return mixed Real path or -1 if currentListing contains the listing : original path converted to real path + * @throws \Pydio\Core\Exception\PydioException + * @throws \Exception + */ + protected static function initPath($path, $streamType, $storeOpenContext = false, $skipZip = false) + { + $path = PathUtils::unPatchPathForBaseDir($path); + $url = UrlUtils::safeParseUrl($path); + $node = new AJXP_Node($path); + $repoObject = $node->getRepository(); + $repoId = $node->getRepositoryId(); + + if(!isSet($repoObject)) { + throw new \Exception("Cannot find repository with id ".$repoId); + } + $split = UserSelection::detectZip($url["path"]); + $insideZip = false; + if($split && $streamType == "file" && $split[1] != "/") $insideZip = true; + if($split && $streamType == "dir") $insideZip = true; + if($skipZip) $insideZip = false; + + $resolvedOptions = self::getResolvedOptionsForNode($node); + $resolvedPath = realpath(TextEncoder::toStorageEncoding($resolvedOptions["PATH"])); + + //var_dump($path); + //var_dump($skipZip); + // Inside a zip : copy the file to a tmp file and return a reference to it + if ($insideZip) { + $zipPath = $split[0]; + $localPath = $split[1]; + require_once(AJXP_BIN_FOLDER."/lib/pclzip.lib.php"); + //print($streamType.$path); + if ($streamType == "file") { + if (self::$crtZip == null || !is_array(self::$currentListingKeys)) { + $tmpDir = ApplicationState::getAjxpTmpDir() . DIRECTORY_SEPARATOR . md5(time()-rand()); + mkdir($tmpDir); + $tmpFileName = $tmpDir.DIRECTORY_SEPARATOR.basename($localPath); + Logger::debug(__CLASS__,__FUNCTION__,"Tmp file $tmpFileName"); + register_shutdown_function(function () use($tmpDir, $tmpFileName){ + if(is_file($tmpFileName)) unlink($tmpFileName); + if(is_dir($tmpDir)) rmdir($tmpDir); + }); + $crtZip = new PclZip(InputFilter::securePath($resolvedPath . $zipPath)); + $content = $crtZip->listContent(); + if(is_array($content)){ + foreach ($content as $item) { + $fName = InputFilter::securePath($item["stored_filename"]); + if ($fName == $localPath || "/".$fName == $localPath) { + $localPath = $fName; + break; + } + } + } + $crtZip->extract(PCLZIP_OPT_BY_NAME, $localPath, PCLZIP_OPT_PATH, $tmpDir, PCLZIP_OPT_REMOVE_ALL_PATH); + Logger::debug(__CLASS__,__FUNCTION__,"Extracted ".$path." to ".dirname($localPath)); + if($storeOpenContext) self::$crtZip = $crtZip; + return $tmpFileName; + } else { + $key = basename($localPath); + if (array_key_exists($key, self::$currentListing)) { + self::$currentFileKey = $key; + return -1; + } else { + throw new PydioException("Cannot find key"); + } + } + } else { + $crtZip = new PclZip(InputFilter::securePath($resolvedPath . $zipPath)); + $liste = $crtZip->listContent(); + if(!is_array($liste)) $liste = array(); + if($storeOpenContext) self::$crtZip = $crtZip; + $folders = array(); $files = array();$builtFolders = array(); + if($localPath[strlen($localPath)-1] != "/") $localPath.="/"; + foreach ($liste as $item) { + $stored = $item["stored_filename"]; + if($stored[0] != "/") $stored = "/".$stored; + $pathPos = strpos($stored, $localPath); + if ($pathPos !== false) { + $afterPath = substr($stored, $pathPos+strlen($localPath)); + if ($afterPath != "" && substr_count($afterPath, "/") < 2) { + $statValue = array(); + if (substr_count($afterPath, "/") == 0) { + $statValue[2] = $statValue["mode"] = ($item["folder"]?00040555:0100555); + $statValue[7] = $statValue["size"] = $item["size"]; + $statValue[8] = $statValue["atime"] = $item["mtime"]; + $statValue[9] = $statValue["mtime"] = $item["mtime"]; + $statValue[10] = $statValue["ctime"] = $item["mtime"]; + if (strpos($afterPath, "/") == strlen($afterPath)-1) { + $afterPath = substr($afterPath, 0, strlen($afterPath)-1); + } + //$statValue["filename"] = $zipPath.$localPath.$afterPath; + if ($item["folder"]) { + $folders[$afterPath] = $statValue; + } else { + $files[$afterPath] = $statValue; + } + } else { + $arr = explode("/", $afterPath); + $afterPath = array_shift($arr); + if(isSet($folders[$afterPath]) || isSet($builtFolders[$afterPath])) continue; + $statValue[2] = $statValue["mode"] = 00040555; + $statValue[7] = $statValue["size"] = 0; + $statValue[8] = $statValue["atime"] = $item["mtime"]; + $statValue[9] = $statValue["mtime"] = $item["mtime"]; + $statValue[10] = $statValue["ctime"] = $item["mtime"]; + $builtFolders[$afterPath] = $statValue; + } + } + } + } + self::$currentListing = array_merge($folders, $builtFolders, $files); + self::$currentListingKeys = array_keys(self::$currentListing); + self::$currentListingIndex = 0; + return -1; + } + } else { + return $resolvedPath.$url["path"]; + } + } + + /** + * @param AJXP_Node $node + * @return array + */ + public static function getResolvedOptionsForNode($node) + { + return [ + "TYPE" => "fs", + "PATH" => $node->getRepository()->getContextOption($node->getContext(), "PATH"), + "CHARSET" => TextEncoder::getEncoding() + ]; + } + + /** + * @param string $tmpDir + * @param string $tmpFile + */ + public static function removeTmpFile($tmpDir, $tmpFile) + { + if(is_file($tmpFile)) unlink($tmpFile); + if(is_dir($tmpDir)) rmdir($tmpDir); + } + + protected static function closeWrapper() + { + if (self::$crtZip != null) { + self::$crtZip = null; + self::$currentListing = null; + self::$currentListingKeys = null; + self::$currentListingIndex = null; + self::$currentFileKey = null; + } + } + + /** + * @param string $path + * @param bool $persistent + * @return mixed + * @throws PydioException + * @throws \Exception + */ + public static function getRealFSReference($path, $persistent = false) + { + if (self::$crtZip != null) { + $crtZip = self::$crtZip; + self::$crtZip = null; + } + $realPath = self::initPath($path, "file"); + if (isSet($crtZip)) { + self::$crtZip = $crtZip; + } else { + self::closeWrapper(); + } + return $realPath; + } + + /** + * @return bool + */ + public static function isRemote() + { + return false; + } + + /** + * @param String $url + * @return bool + */ + public static function isSeekable($url) + { + if(strpos($url, ".zip/") !== false) return false; + return true; + } + + /** + * @param string $path + * @param resource $stream + */ + public static function copyFileInStream($path, $stream) + { + $fp = fopen(self::getRealFSReference($path), "rb"); + if(!is_resource($fp)) return; + while (!feof($fp)) { + if(!ini_get("safe_mode")) @set_time_limit(60); + $data = fread($fp, 4096); + fwrite($stream, $data, strlen($data)); + } + fclose($fp); + } + + /** + * @param string $path + * @param number $chmodValue + * @throws PydioException + * @throws \Exception + */ + public static function changeMode($path, $chmodValue) + { + $realPath = self::initPath($path, "file"); + @chmod($realPath, $chmodValue); + } + + /** + * Opens the strem + * + * @param String $path Maybe in the form "ajxp.fs://repositoryId/pathToFile" + * @param String $mode + * @param string $options + * @param resource $context + * @return bool + */ + public function stream_open($path, $mode, $options, &$context) + { + try { + $this->realPath = InputFilter::securePath(self::initPath($path, "file")); + } catch (\Exception $e) { + Logger::error(__CLASS__,"stream_open", "Error while opening stream $path (".$e->getMessage().")"); + return false; + } + if ($this->realPath == -1) { + $this->fp = -1; + return true; + } else { + $this->fp = fopen($this->realPath, $mode, $options); + return ($this->fp !== false); + } + } + + /** + * @param int $offset + * @param int $whence + * @return bool + */ + public function stream_seek($offset , $whence = SEEK_SET) + { + return fseek($this->fp, $offset, $whence); + } + + /** + * @return int + */ + public function stream_tell() + { + return ftell($this->fp); + } + + /** + * @return array|mixed|null + */ + public function stream_stat() + { + $PROBE_REAL_SIZE = ConfService::getConf("PROBE_REAL_SIZE"); + if (is_resource($this->fp)) { + $statValue = fstat($this->fp); + FsAccessWrapper::$lastRealSize = false; + if ($statValue[2] > 0 && $PROBE_REAL_SIZE && !ini_get("safe_mode")) { + FsAccessWrapper::$lastRealSize = floatval(trim($this->getTrueSizeOnFileSystem($this->realPath))); + } + return $statValue; + } + if (is_resource($this->dH)) { + return fstat($this->dH); + } + if ($this->fp == -1) { + return self::$currentListing[self::$currentFileKey]; + } + return null; + } + + /** + * @param string $path + * @param int $flags + * @return array|null + * @throws PydioException + * @throws \Exception + */ + public function url_stat($path, $flags) + { + // File and zip case + $patchedPath = PathUtils::patchPathForBaseDir($path); + if (ini_get("open_basedir") && preg_match('/__ZIP_EXTENSION__/', $patchedPath)) { + // Zip Folder case + self::$lastRealSize = false; + $search = basename($path); + $realBase = $this->initPath(dirname($path), "dir"); + if ($realBase == -1) { + if (array_key_exists($search, self::$currentListing)) { + return self::$currentListing[$search]; + } + } + } + if ($fp = @fopen($path, "r")) { + $stat = fstat($fp); + fclose($fp); + return $stat; + } + // Folder case + $real = $this->initPath($path, "dir", false, true); + if ($real!=-1 && is_dir($real)) { + return stat($real); + } + // Zip Folder case + $search = basename($path); + $realBase = $this->initPath(dirname($path), "dir"); + if ($realBase == -1) { + if (array_key_exists($search, self::$currentListing)) { + return self::$currentListing[$search]; + } + } + // 000 permission file + if ($real != -1 && is_file($real)) { + return stat($real); + } + // Handle symlinks! + if ($real != -1 && is_link($real)) { + $realFile = @readlink($real); + if (is_file($realFile) || is_dir($realFile)) { + return stat($realFile); + } else { + // symlink is broken, delete it. + @unlink($real); + return null; + } + } + + // Non existing file + return null; + } + + /** + * @param string $from + * @param string $to + * @return bool + * @throws PydioException + * @throws \Exception + */ + public function rename($from, $to) + { + return rename($this->initPath($from, "file", false, true), $this->initPath($to, "file", false, true)); + } + + /** + * @param int $count + * @return string + */ + public function stream_read($count) + { + return fread($this->fp, $count); + } + + /** + * @param string $data + * @return int + */ + public function stream_write($data) + { + fwrite($this->fp, $data, strlen($data)); + return strlen($data); + } + + /** + * @return bool + */ + public function stream_eof() + { + return feof($this->fp); + } + + public function stream_close() + { + if (isSet($this->fp) && $this->fp!=-1 && $this->fp!==false) { + fclose($this->fp); + } + } + + /** + * + */ + public function stream_flush() + { + if (isSet($this->fp) && $this->fp!=-1 && $this->fp!==false) { + fflush($this->fp); + } + } + + /** + * @param string $path + * @return bool + * @throws PydioException + * @throws \Exception + */ + public function unlink($path) + { + $this->realPath = $this->initPath($path, "file", false, true); + return unlink($this->realPath); + } + + /** + * @param string $path + * @param int $options + * @return bool + * @throws PydioException + * @throws \Exception + */ + public function rmdir($path, $options) + { + $this->realPath = $this->initPath($path, "file", false, true); + return rmdir($this->realPath); + } + + /** + * @param string $path + * @param int $mode + * @param int $options + * @return bool + * @throws PydioException + * @throws \Exception + */ + public function mkdir($path, $mode, $options) + { + return mkdir($this->initPath($path, "file"), $mode); + } + + /** + * Readdir functions + * + * @param string $path + * @param int $options + * @return bool + */ + public function dir_opendir ($path , $options ) + { + $this->realPath = $this->initPath($path, "dir", true); + if (is_string($this->realPath)) { + $this->dH = @opendir($this->realPath); + } else if ($this->realPath == -1) { + $this->dH = -1; + } + return $this->dH !== false; + } + + /** + * Close dir handle + */ + public function dir_closedir () + { + $this->closeWrapper(); + if ($this->dH == -1) { + return; + } else { + closedir($this->dH); + } + } + + /** + * @return bool|string + */ + public function dir_readdir () + { + if ($this->dH == -1) { + if (isSet(self::$currentListingKeys[self::$currentListingIndex])) { + self::$currentListingIndex++; + return self::$currentListingKeys[self::$currentListingIndex - 1]; + } else { + return false; + } + } else { + return readdir($this->dH); + } + } + + /** + * + */ + public function dir_rewinddir () + { + if ($this->dH == -1) { + self::$currentListingIndex = 0; + } else { + rewinddir($this->dH); + } + } + + /** + * @return bool|float + */ + public static function getLastRealSize() + { + if(empty(self::$lastRealSize)) return false; + return self::$lastRealSize; + } + + /** + * @param $file + * @return float|string + */ + protected function getTrueSizeOnFileSystem($file) + { + if (!(strtoupper(substr(PHP_OS, 0, 3)) == 'WIN')) { + $cmd = "stat -L -c%s ".escapeshellarg($file); + $val = trim(`$cmd`); + if (!is_numeric($val) || $val == -1) { + // No stat on system + $cmd = "ls -1s --block-size=1 ".escapeshellarg($file); + $val = trim(`$cmd`); + if (strlen($val) == 0 || floatval($val) == 0) { + // No block-size on system (probably busybox), try long output + $cmd = "ls -l ".escapeshellarg($file).""; + $arr = explode("/[\s]+/", `$cmd`); + $val = trim($arr[4]); + if (strlen($val) == 0 || floatval($val) == 0) { + // Still not working, get a value at least, not 0... + $val = sprintf("%u", filesize($file)); + } + } + } + return floatval($val); + } else if (class_exists("COM")) { + $fsobj = new \COM("Scripting.FileSystemObject"); + $f = $fsobj->GetFile($file); + return floatval($f->Size); + } else if (is_file($file)) { + return exec('FOR %A IN ("'.$file.'") DO @ECHO %~zA'); + } else return sprintf("%u", filesize($file)); + } +} diff --git a/core/src/plugins/access.fs/class.fsAccessDriver.php b/core/src/plugins/access.fs/class.fsAccessDriver.php deleted file mode 100644 index c8f5b91a0b..0000000000 --- a/core/src/plugins/access.fs/class.fsAccessDriver.php +++ /dev/null @@ -1,2145 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - * - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - - -// This is used to catch exception while downloading -if (!function_exists('download_exception_handler')) { - function download_exception_handler($exception){} -} -/** - * AJXP_Plugin to access a filesystem. Most "FS" like driver (even remote ones) - * extend this one. - * @package AjaXplorer_Plugins - * @subpackage Access - */ -class fsAccessDriver extends AbstractAccessDriver implements AjxpWrapperProvider -{ - /** - * @var Repository - */ - public $repository; - public $driverConf; - protected $wrapperClassName; - protected $urlBase; - - public function initRepository() - { - if (is_array($this->pluginConf)) { - $this->driverConf = $this->pluginConf; - } else { - $this->driverConf = array(); - } - if ( $this->getFilteredOption("PROBE_REAL_SIZE", $this->repository) == true ) { - // PASS IT TO THE WRAPPER - ConfService::setConf("PROBE_REAL_SIZE", $this->getFilteredOption("PROBE_REAL_SIZE", $this->repository)); - } - $create = $this->repository->getOption("CREATE"); - $path = SystemTextEncoding::toStorageEncoding($this->repository->getOption("PATH")); - $recycle = $this->repository->getOption("RECYCLE_BIN"); - $chmod = $this->repository->getOption("CHMOD_VALUE"); - $this->detectStreamWrapper(true); - $this->urlBase = "pydio://".$this->repository->getId(); - - - if ($create == true) { - if(!is_dir($path)) @mkdir($path, 0755, true); - if (!is_dir($path)) { - throw new AJXP_Exception("Cannot create root path for repository (".$this->repository->getDisplay()."). Please check repository configuration or that your folder is writeable!"); - } - if ($recycle!= "" && !is_dir($path."/".$recycle)) { - @mkdir($path."/".$recycle); - if (!is_dir($path."/".$recycle)) { - throw new AJXP_Exception("Cannot create recycle bin folder. Please check repository configuration or that your folder is writeable!"); - } else { - $this->setHiddenAttribute(new AJXP_Node($this->urlBase ."/".$recycle)); - } - } - $dataTemplate = $this->repository->getOption("DATA_TEMPLATE"); - if (!empty($dataTemplate) && is_dir($dataTemplate) && !is_file($path."/.ajxp_template")) { - $errs = array();$succ = array(); - $repoData = array('base_url' => $this->urlBase, 'chmod' => $chmod, 'recycle' => $recycle); - $this->dircopy($dataTemplate, $path, $succ, $errs, false, false, $repoData, $repoData); - touch($path."/.ajxp_template"); - } - } else { - if (!is_dir($path)) { - throw new AJXP_Exception("Cannot find base path for your repository! Please check the configuration!"); - } - } - if ($recycle != "") { - RecycleBinManager::init($this->urlBase, "/".$recycle); - } - } - - public function getResourceUrl($path) - { - return $this->urlBase.$path; - } - - /** - * @param String $directoryPath - * @param Repository $repositoryResolvedOptions - * @return int - */ - public function directoryUsage($directoryPath, $repositoryResolvedOptions){ - - $dir = $repositoryResolvedOptions["PATH"].$directoryPath; - $size = -1; - if ( ( PHP_OS == "WIN32" || PHP_OS == "WINNT" || PHP_OS == "Windows") && class_exists("COM") ) { - $obj = new COM ( 'scripting.filesystemobject' ); - if ( is_object ( $obj ) ) { - $ref = $obj->getfolder ( $dir ); - $size = floatval($ref->size); - $obj = null; - } - } else { - if((PHP_OS == "Darwin") || (PHP_OS == "FreeBSD")) $option = "-sk"; - else $option = "-sb"; - $cmd = '/usr/bin/du '.$option.' ' . escapeshellarg($dir); - $io = popen ( $cmd , 'r' ); - $size = fgets ( $io, 4096); - $size = trim(str_replace($dir, "", $size)); - $size = floatval($size); - if((PHP_OS == "Darwin") || (PHP_OS == "FreeBSD")) $size = $size * 1024; - pclose ( $io ); - } - if($size != -1){ - return $size; - }else{ - return $this->recursiveDirUsageByListing($directoryPath); - } - - } - - protected function recursiveDirUsageByListing($path){ - $total_size = 0; - $files = scandir($path); - - foreach ($files as $t) { - if (is_dir(rtrim($path, '/') . '/' . $t)) { - if ($t <> "." && $t <> "..") { - $size = $this->recursiveDirUsageByListing(rtrim($path, '/') . '/' . $t); - $total_size += $size; - } - } else { - $size = sprintf("%u", filesize(rtrim($path, '/') . '/' . $t)); - $total_size += $size; - } - } - return $total_size; - } - - public function redirectActionsToMethod(&$contribNode, $arrayActions, $targetMethod) - { - $actionXpath=new DOMXPath($contribNode->ownerDocument); - foreach ($arrayActions as $index => $value) { - $arrayActions[$index] = 'action[@name="'.$value.'"]/processing/serverCallback'; - } - $procList = $actionXpath->query(implode(" | ", $arrayActions), $contribNode); - foreach ($procList as $node) { - $node->setAttribute("methodName", $targetMethod); - } - } - - /** - * @param DOMNode $contribNode - */ - public function disableArchiveBrowsingContributions(&$contribNode) - { - // Cannot use zip features on FTP ! - // Remove "compress" action - $actionXpath=new DOMXPath($contribNode->ownerDocument); - $compressNodeList = $actionXpath->query('action[@name="compress"]|action[@name="compress_ui"]|action[@name="download_all"]', $contribNode); - if(!$compressNodeList->length) return ; - foreach($compressNodeList as $compressNodeAction){ - $contribNode->removeChild($compressNodeAction); - } - // Disable "download" if selection is multiple - $nodeList = $actionXpath->query('action[@name="download"]/gui/selectionContext', $contribNode); - $selectionNode = $nodeList->item(0); - $values = array("dir" => "false", "unique" => "true"); - foreach ($selectionNode->attributes as $attribute) { - if (isSet($values[$attribute->name])) { - $attribute->value = $values[$attribute->name]; - } - } - $nodeList = $actionXpath->query('action[@name="download"]/processing/clientListener[@name="selectionChange"]', $contribNode); - $listener = $nodeList->item(0); - $listener->parentNode->removeChild($listener); - // Disable "Explore" action on files - $nodeList = $actionXpath->query('action[@name="ls"]/gui/selectionContext', $contribNode); - $selectionNode = $nodeList->item(0); - $values = array("file" => "false", "allowedMimes" => ""); - foreach ($selectionNode->attributes as $attribute) { - if (isSet($values[$attribute->name])) { - $attribute->value = $values[$attribute->name]; - } - } - } - - protected function getNodesDiffArray() - { - return array("REMOVE" => array(), "ADD" => array(), "UPDATE" => array()); - } - - public function addSlugToPath($selection) - { - if (is_array($selection)) - // As passed by Copy/Move - $orig_files = $selection; - elseif ((is_object($selection)) && (isset($selection->files)) && (is_array($selection->files))) - // As passed by Download - $orig_files = $selection->files; - elseif (is_string($selection)) - // As passed by destination parameter - return $this->repository->slug.$selection; - else - // Unrecognized - return $selection; - - $files = array(); - foreach ($orig_files as $file) - $files[] = $this->repository->slug.$file; - return $files; - } - - public function switchAction($action, $httpVars, $fileVars) - { - parent::accessPreprocess($action, $httpVars, $fileVars); - $selection = new UserSelection($this->repository); - $dir = AJXP_Utils::sanitize($httpVars["dir"], AJXP_SANITIZE_DIRNAME) OR ""; - if (AJXP_MetaStreamWrapper::actualRepositoryWrapperClass($this->repository->getId()) == "fsAccessWrapper") { - $dir = fsAccessWrapper::patchPathForBaseDir($dir); - } - $dir = AJXP_Utils::securePath($dir); - if ($action != "upload") { - $dir = AJXP_Utils::decodeSecureMagic($dir); - } - $selection->initFromHttpVars($httpVars); - if (!$selection->isEmpty()) { - $this->filterUserSelectionToHidden($selection->getFiles()); - } - $mess = ConfService::getMessages(); - - $newArgs = RecycleBinManager::filterActions($action, $selection, $dir, $httpVars); - if(isSet($newArgs["action"])) $action = $newArgs["action"]; - if(isSet($newArgs["dest"])) $httpVars["dest"] = SystemTextEncoding::toUTF8($newArgs["dest"]);//Re-encode! - // FILTER DIR PAGINATION ANCHOR - $page = null; - if (isSet($dir) && strstr($dir, "%23")!==false) { - $parts = explode("%23", $dir); - $dir = $parts[0]; - $page = $parts[1]; - } - - $pendingSelection = ""; - $logMessage = null; - $reloadContextNode = false; - - switch ($action) { - //------------------------------------ - // DOWNLOAD - //------------------------------------ - case "download": - $this->logInfo("Download", array("files"=>$this->addSlugToPath($selection))); - @set_error_handler(array("HTMLWriter", "javascriptErrorHandler"), E_ALL & ~ E_NOTICE); - @register_shutdown_function("restore_error_handler"); - $zip = false; - if ($selection->isUnique()) { - if (is_dir($this->urlBase.$selection->getUniqueFile())) { - $zip = true; - $base = basename($selection->getUniqueFile()); - $uniqDir = dirname($selection->getUniqueFile()); - if(!empty($uniqDir) && $uniqDir != "/"){ - $dir = dirname($selection->getUniqueFile()); - } - } else { - if (!file_exists($this->urlBase.$selection->getUniqueFile())) { - throw new Exception("Cannot find file!"); - } - } - $node = $selection->getUniqueNode(); - } else { - $zip = true; - $base = basename(dirname($selection->getUniqueFile())); - } - if ($zip) { - $localName = (empty($base)?"Files":$base).".zip"; - if(isSet($httpVars["archive_name"])){ - $localName = AJXP_Utils::decodeSecureMagic($httpVars["archive_name"]); - } - if ($this->getFilteredOption("ZIP_ON_THE_FLY", $this->repository)) { - // Make a zip on the fly and send stream as download - $this->prepareReadStream("force-download", $localName); - $zipFile = $this->makeZip($selection->getFiles(), "php://output", empty($dir)?"/":$dir); - if(!$zipFile) throw new AJXP_Exception("Error while compressing"); - } else { - // Make a temp zip and send it as download - $loggedUser = AuthService::getLoggedUser(); - $file = AJXP_Utils::getAjxpTmpDir()."/".($loggedUser?$loggedUser->getId():"shared")."_".time()."tmpDownload.zip"; - $zipFile = $this->makeZip($selection->getFiles(), $file, empty($dir)?"/":$dir); - if(!$zipFile) throw new AJXP_Exception("Error while compressing"); - if(!$this->getFilteredOption("USE_XSENDFILE", $this->repository) - && !$this->getFilteredOption("USE_XACCELREDIRECT", $this->repository)){ - register_shutdown_function("unlink", $file); - } - $this->readFile($file, "force-download", $localName, false, false, true); - } - } else { - $localName = ""; - AJXP_Controller::applyHook("dl.localname", array($this->urlBase.$selection->getUniqueFile(), &$localName)); - $this->readFile($this->urlBase.$selection->getUniqueFile(), "force-download", $localName); - } - if (isSet($node)) { - AJXP_Controller::applyHook("node.read", array(&$node)); - } - - - break; - - case "prepare_chunk_dl" : - - $chunkCount = intval($httpVars["chunk_count"]); - $node = $selection->getUniqueNode(); - - $fileId = $node->getUrl(); - $sessionKey = "chunk_file_".md5($fileId.time()); - $totalSize = filesize($fileId); - $chunkSize = intval ( $totalSize / $chunkCount ); - $realFile = AJXP_MetaStreamWrapper::getRealFSReference($fileId, true); - $chunkData = array( - "localname" => basename($fileId), - "chunk_count" => $chunkCount, - "chunk_size" => $chunkSize, - "total_size" => $totalSize, - "file_id" => $sessionKey - ); - - $_SESSION[$sessionKey] = array_merge($chunkData, array("file"=>$realFile)); - HTMLWriter::charsetHeader("application/json"); - print(json_encode($chunkData)); - - AJXP_Controller::applyHook("node.read", array(&$node)); - - break; - - case "download_chunk" : - - $chunkIndex = intval($httpVars["chunk_index"]); - $chunkKey = $httpVars["file_id"]; - $sessData = $_SESSION[$chunkKey]; - $realFile = $sessData["file"]; - $chunkSize = $sessData["chunk_size"]; - $offset = $chunkSize * $chunkIndex; - if ($chunkIndex == $sessData["chunk_count"]-1) { - // Compute the last chunk real length - $chunkSize = $sessData["total_size"] - ($chunkSize * ($sessData["chunk_count"]-1)); - if (AJXP_MetaStreamWrapper::wrapperIsRemote($this->urlBase)) { - register_shutdown_function("unlink", $realFile); - } - } - $this->readFile($realFile, "force-download", $sessData["localname"].".".sprintf("%03d", $chunkIndex+1), false, false, true, $offset, $chunkSize); - - - break; - - case "compress" : - // Make a temp zip and send it as download - $loggedUser = AuthService::getLoggedUser(); - if (isSet($httpVars["archive_name"])) { - $localName = AJXP_Utils::decodeSecureMagic($httpVars["archive_name"]); - $this->filterUserSelectionToHidden(array($localName)); - } else { - $localName = (basename($dir)==""?"Files":basename($dir)).".zip"; - } - $file = AJXP_Utils::getAjxpTmpDir()."/".($loggedUser?$loggedUser->getId():"shared")."_".time()."tmpCompression.zip"; - if(isSet($httpVars["compress_flat"])) $baseDir = "__AJXP_ZIP_FLAT__/"; - else $baseDir = $dir; - $zipFile = $this->makeZip($selection->getFiles(), $file, $baseDir); - if(!$zipFile) throw new AJXP_Exception("Error while compressing file $localName"); - register_shutdown_function("unlink", $file); - $tmpFNAME = $this->urlBase.$dir."/".str_replace(".zip", ".tmp", $localName); - copy($file, $tmpFNAME); - try { - AJXP_Controller::applyHook("node.before_create", array(new AJXP_Node($tmpFNAME), filesize($tmpFNAME))); - } catch (Exception $e) { - @unlink($tmpFNAME); - throw $e; - } - @rename($tmpFNAME, $this->urlBase.$dir."/".$localName); - AJXP_Controller::applyHook("node.change", array(null, new AJXP_Node($this->urlBase.$dir."/".$localName), false)); - //$reloadContextNode = true; - //$pendingSelection = $localName; - $newNode = new AJXP_Node($this->urlBase.$dir."/".$localName); - if(!isset($nodesDiffs)) $nodesDiffs = $this->getNodesDiffArray(); - $nodesDiffs["ADD"][] = $newNode; - break; - - case "stat" : - clearstatcache(); - header("Content-type:application/json"); - if($selection->isUnique()){ - $stat = @stat($this->urlBase.$selection->getUniqueFile()); - if (!$stat || !is_readable($selection->getUniqueNode()->getUrl())) { - print '{}'; - } else { - print json_encode($stat); - } - }else{ - $files = $selection->getFiles(); - print '{'; - foreach($files as $index => $path){ - $stat = @stat($this->urlBase.$path); - if(!$stat || !is_readable($this->urlBase.$path)) $stat = '{}'; - else $stat = json_encode($stat); - print json_encode($path).':'.$stat . (($index < count($files) -1) ? "," : ""); - } - print '}'; - } - - break; - - - //------------------------------------ - // ONLINE EDIT - //------------------------------------ - case "get_content": - - $node = $selection->getUniqueNode(); - $dlFile = $node->getUrl(); - if(!is_readable($dlFile)){ - throw new Exception("Cannot access file!"); - } - $this->logInfo("Get_content", array("files"=>$this->addSlugToPath($selection))); - if (AJXP_Utils::getStreamingMimeType(basename($dlFile))!==false) { - $this->readFile($node->getUrl(), "stream_content"); - } else { - $this->readFile($node->getUrl(), "plain"); - } - AJXP_Controller::applyHook("node.read", array(&$node)); - - break; - - case "put_content": - if(!isset($httpVars["content"])) break; - // Load "code" variable directly from POST array, do not "securePath" or "sanitize"... - $code = $httpVars["content"]; - $currentNode = $selection->getUniqueNode(); - $fileName = $currentNode->getUrl(); - $this->logInfo("Online Edition", array("files"=> $this->addSlugToPath($fileName))); - if (isSet($httpVars["encode"]) && $httpVars["encode"] == "base64") { - $code = base64_decode($code); - } else { - $code=str_replace("<","<",SystemTextEncoding::magicDequote($code)); - } - try { - AJXP_Controller::applyHook("node.before_change", array(&$currentNode, strlen($code))); - } catch (Exception $e) { - header("Content-Type:text/plain"); - print $e->getMessage(); - break; - } - if (!is_file($fileName) || !$this->isWriteable($fileName, "file")) { - header("Content-Type:text/plain"); - print((!$this->isWriteable($fileName, "file")?"1001":"1002")); - break; - } - $fp=fopen($fileName,"w"); - fputs ($fp,$code); - fclose($fp); - clearstatcache(true, $fileName); - AJXP_Controller::applyHook("node.change", array($currentNode, $currentNode, false)); - header("Content-Type:text/plain"); - print($mess[115]); - - break; - - //------------------------------------ - // COPY / MOVE - //------------------------------------ - case "copy": - case "move": - - if ($selection->isEmpty()) { - throw new AJXP_Exception("", 113); - } - $loggedUser = AuthService::getLoggedUser(); - if($loggedUser != null && !$loggedUser->canWrite(ConfService::getCurrentRepositoryId())){ - throw new AJXP_Exception("You are not allowed to write", 207); - } - $success = $error = array(); - $dest = AJXP_Utils::decodeSecureMagic($httpVars["dest"]); - $this->filterUserSelectionToHidden(array($httpVars["dest"])); - if ($selection->inZip()) { - // Set action to copy anycase (cannot move from the zip). - $action = "copy"; - $this->extractArchive($dest, $selection, $error, $success); - } else { - $move = ($action == "move" ? true : false); - if ($move && isSet($httpVars["force_copy_delete"])) { - $move = false; - } - $this->copyOrMove($dest, $selection->getFiles(), $error, $success, $move); - - } - - if (count($error)) { - throw new AJXP_Exception(SystemTextEncoding::toUTF8(join("\n", $error))); - } else { - if (isSet($httpVars["force_copy_delete"])) { - $errorMessage = $this->delete($selection->getFiles(), $logMessages); - if($errorMessage) throw new AJXP_Exception(SystemTextEncoding::toUTF8($errorMessage)); - $this->logInfo("Copy/Delete", array("files"=>$this->addSlugToPath($selection), "destination" => $this->addSlugToPath($dest))); - } else { - $this->logInfo(($action=="move"?"Move":"Copy"), array("files"=>$this->addSlugToPath($selection), "destination"=>$this->addSlugToPath($dest))); - } - $logMessage = join("\n", $success); - } - if(!isSet($nodesDiffs)) $nodesDiffs = $this->getNodesDiffArray(); - // Assume new nodes are correctly created - $selectedItems = $selection->getFiles(); - foreach ($selectedItems as $selectedPath) { - $newPath = $this->urlBase.$dest ."/". basename($selectedPath); - $newNode = new AJXP_Node($newPath); - $nodesDiffs["ADD"][] = $newNode; - if($action == "move") $nodesDiffs["REMOVE"][] = $selectedPath; - } - if (!(RecycleBinManager::getRelativeRecycle() ==$dest && $this->getFilteredOption("HIDE_RECYCLE", $this->repository) == true)) { - //$reloadDataNode = $dest; - } - - break; - - //------------------------------------ - // DELETE - //------------------------------------ - case "delete": - - if ($selection->isEmpty()) { - throw new AJXP_Exception("", 113); - } - $logMessages = array(); - $errorMessage = $this->delete($selection->getFiles(), $logMessages); - if (count($logMessages)) { - $logMessage = join("\n", $logMessages); - } - if($errorMessage) throw new AJXP_Exception(SystemTextEncoding::toUTF8($errorMessage)); - $this->logInfo("Delete", array("files"=>$this->addSlugToPath($selection))); - if(!isSet($nodesDiffs)) $nodesDiffs = $this->getNodesDiffArray(); - $nodesDiffs["REMOVE"] = array_merge($nodesDiffs["REMOVE"], $selection->getFiles()); - - break; - - - case "purge" : - - - $hardPurgeTime = intval($this->repository->getOption("PURGE_AFTER"))*3600*24; - $softPurgeTime = intval($this->repository->getOption("PURGE_AFTER_SOFT"))*3600*24; - $shareCenter = AJXP_PluginsService::findPluginById('action.share'); - if( !($shareCenter && $shareCenter->isEnabled()) ) { - //action.share is disabled, don't look at the softPurgeTime - $softPurgeTime = 0; - } - if ($hardPurgeTime > 0 || $softPurgeTime > 0) { - $this->recursivePurge($this->urlBase, $hardPurgeTime, $softPurgeTime); - } - - break; - - //------------------------------------ - // RENAME - //------------------------------------ - case "rename": - - $file = $selection->getUniqueFile(); - $filename_new = AJXP_Utils::decodeSecureMagic($httpVars["filename_new"]); - $dest = null; - if (isSet($httpVars["dest"])) { - $dest = AJXP_Utils::decodeSecureMagic($httpVars["dest"]); - $filename_new = ""; - } - $this->filterUserSelectionToHidden(array($filename_new)); - $this->rename($file, $filename_new, $dest); - $logMessage= SystemTextEncoding::toUTF8($file)." $mess[41] ".SystemTextEncoding::toUTF8($filename_new); - //$reloadContextNode = true; - //$pendingSelection = $filename_new; - if(!isSet($nodesDiffs)) $nodesDiffs = $this->getNodesDiffArray(); - if($dest == null) $dest = AJXP_Utils::safeDirname($file); - $nodesDiffs["UPDATE"][$file] = new AJXP_Node($this->urlBase.$dest."/".$filename_new); - $this->logInfo("Rename", array("files"=>$this->addSlugToPath($file), "original"=>$this->addSlugToPath($file), "new"=>$filename_new)); - - break; - - //------------------------------------ - // CREER UN REPERTOIRE / CREATE DIR - //------------------------------------ - case "mkdir": - - $messtmp=""; - $files = $selection->getFiles(); - if(isSet($httpVars["dirname"])){ - $files[] = $dir ."/". AJXP_Utils::decodeSecureMagic($httpVars["dirname"], AJXP_SANITIZE_FILENAME); - } - if(!isSet($nodesDiffs)) $nodesDiffs = $this->getNodesDiffArray(); - $messages = array(); - $errors = array(); - $max_length = ConfService::getCoreConf("NODENAME_MAX_LENGTH"); - foreach($files as $newDirPath){ - $parentDir = AJXP_Utils::safeDirname($newDirPath); - $basename = AJXP_Utils::safeBasename($newDirPath); - $basename = substr($basename, 0, $max_length); - $this->filterUserSelectionToHidden(array($basename)); - try{ - AJXP_Controller::applyHook("node.before_create", array(new AJXP_Node($parentDir."/".$basename), -2)); - }catch (AJXP_Exception $e){ - $errors[] = $e->getMessage(); - continue; - } - $error = $this->mkDir($parentDir, $basename, isSet($httpVars["ignore_exists"])?true:false); - if (isSet($error)) { - //throw new AJXP_Exception($error); - $errors[] = $error; - continue; - } - $messtmp.="$mess[38] ".SystemTextEncoding::toUTF8($basename)." $mess[39] "; - if ($parentDir=="") {$messtmp.="/";} else {$messtmp.= SystemTextEncoding::toUTF8($parentDir);} - $messages[] = $messtmp; - $newNode = new AJXP_Node($this->urlBase.$parentDir."/".$basename); - array_push($nodesDiffs["ADD"], $newNode); - $this->logInfo("Create Dir", array("dir"=>$this->addSlugToPath($parentDir)."/".$basename, "files"=>$this->addSlugToPath($parentDir)."/".$basename)); - } - if(count($errors)){ - if(!count($messages)){ - throw new AJXP_Exception(implode('', $errors)); - }else{ - $errorMessage = implode("
      ", $errors); - } - } - $logMessage = implode("
      ", $messages); - - - break; - - //------------------------------------ - // CREER UN FICHIER / CREATE FILE - //------------------------------------ - case "mkfile": - - $messtmp=""; - if(empty($httpVars["filename"]) && isSet($httpVars["node"])){ - $filename=AJXP_Utils::decodeSecureMagic($httpVars["node"]); - }else{ - $filename=AJXP_Utils::decodeSecureMagic($httpVars["filename"], AJXP_SANITIZE_FILENAME); - } - $filename = substr($filename, 0, ConfService::getCoreConf("NODENAME_MAX_LENGTH")); - $this->filterUserSelectionToHidden(array($filename)); - $content = ""; - if (isSet($httpVars["content"])) { - $content = $httpVars["content"]; - } - $forceCreation = false; - if (isSet($httpVars["force"]) && $httpVars["force"] == "true"){ - $forceCreation = true; - } - $error = $this->createEmptyFile($dir, $filename, $content, $forceCreation); - if (isSet($error)) { - throw new AJXP_Exception($error); - } - $messtmp.="$mess[34] ".SystemTextEncoding::toUTF8($filename)." $mess[39] "; - if ($dir=="") {$messtmp.="/";} else {$messtmp.=SystemTextEncoding::toUTF8($dir);} - $logMessage = $messtmp; - //$reloadContextNode = true; - //$pendingSelection = $dir."/".$filename; - $this->logInfo("Create File", array("files"=>$this->addSlugToPath($dir)."/".$filename)); - $newNode = new AJXP_Node($this->urlBase.$dir."/".$filename); - if(!isSet($nodesDiffs)) $nodesDiffs = $this->getNodesDiffArray(); - array_push($nodesDiffs["ADD"], $newNode); - - break; - - //------------------------------------ - // CHANGE FILE PERMISSION - //------------------------------------ - case "chmod": - - $files = $selection->getFiles(); - $changedFiles = array(); - $chmod_value = $httpVars["chmod_value"]; - $recursive = $httpVars["recursive"]; - $recur_apply_to = $httpVars["recur_apply_to"]; - foreach ($files as $fileName) { - $this->chmod($fileName, $chmod_value, ($recursive=="on"), ($recursive=="on"?$recur_apply_to:"both"), $changedFiles); - } - $logMessage="Successfully changed permission to ".$chmod_value." for ".count($changedFiles)." files or folders"; - $this->logInfo("Chmod", array("dir"=>$this->addSlugToPath($dir), "files"=>$this->addSlugToPath($dir), "filesCount"=>count($changedFiles))); - if(!isSet($nodesDiffs)) $nodesDiffs = $this->getNodesDiffArray(); - $nodesDiffs["UPDATE"] = array_merge($nodesDiffs["UPDATE"], $selection->buildNodes()); - - break; - - //------------------------------------ - // UPLOAD - //------------------------------------ - case "upload": - - $repoData = array( - 'base_url' => $this->urlBase, - 'chmod' => $this->repository->getOption('CHMOD_VALUE'), - 'recycle' => $this->repository->getOption('RECYCLE_BIN') - ); - $this->logDebug("Upload Files Data", $fileVars); - $destination=$this->urlBase.AJXP_Utils::decodeSecureMagic($dir); - $this->logDebug("Upload inside", array("destination"=>$this->addSlugToPath($destination))); - if (!$this->isWriteable($destination)) { - $errorCode = 412; - $errorMessage = "$mess[38] ".SystemTextEncoding::toUTF8($dir)." $mess[99]."; - $this->logDebug("Upload error 412", array("destination"=>$this->addSlugToPath($destination))); - return array("ERROR" => array("CODE" => $errorCode, "MESSAGE" => $errorMessage)); - } - - $partialUpload = false; - $partialTargetSize = -1; - $originalAppendTo = ""; - $createdNode = null; - - foreach ($fileVars as $boxName => $boxData) { - if(substr($boxName, 0, 9) != "userfile_") continue; - - try{ - // CHECK PHP UPLOAD ERRORS - AJXP_Utils::parseFileDataErrors($boxData, true); - - // FIND PROPER FILE NAME - $userfile_name=AJXP_Utils::sanitize(SystemTextEncoding::fromPostedFileName($boxData["name"]), AJXP_SANITIZE_FILENAME); - if (isSet($httpVars["urlencoded_filename"])) { - $userfile_name = AJXP_Utils::sanitize(SystemTextEncoding::fromUTF8(urldecode($httpVars["urlencoded_filename"])), AJXP_SANITIZE_FILENAME); - } - $userfile_name = substr($userfile_name, 0, ConfService::getCoreConf("NODENAME_MAX_LENGTH")); - if (isSet($httpVars["auto_rename"])) { - $userfile_name = self::autoRenameForDest($destination, $userfile_name); - } - $this->logDebug("User filename ".$userfile_name); - if(class_exists("Normalizer")){ - $userfile_name = Normalizer::normalize($userfile_name, Normalizer::FORM_C); - } - - // CHECK IF THIS IS A FORBIDDEN FILENAME - $this->filterUserSelectionToHidden(array($userfile_name)); - - // APPLY PRE-UPLOAD HOOKS - $already_existed = false; - try { - if (file_exists($destination."/".$userfile_name)) { - $already_existed = true; - AJXP_Controller::applyHook("node.before_change", array(new AJXP_Node($destination."/".$userfile_name), $boxData["size"])); - } else { - AJXP_Controller::applyHook("node.before_create", array(new AJXP_Node($destination."/".$userfile_name), $boxData["size"])); - } - AJXP_Controller::applyHook("node.before_change", array(new AJXP_Node($destination))); - } catch (Exception $e) { - throw new Exception($e->getMessage(), 507); - } - - // PARTIAL UPLOAD CASE - PREPPEND .dlpart extension - if(isSet($httpVars["partial_upload"]) && $httpVars["partial_upload"] == 'true' && isSet($httpVars["partial_target_bytesize"])){ - $partialUpload = true; - $partialTargetSize = intval($httpVars["partial_target_bytesize"]); - if(!isSet($httpVars["appendto_urlencoded_part"])){ - $userfile_name .= ".dlpart"; - } - } - - // NOW DO THE ACTUAL COPY - $this->copyUploadedData($boxData, $destination, $userfile_name, $mess); - - // PARTIAL UPLOAD - PART II: APPEND DATA TO EXISTING PART - if (isSet($httpVars["appendto_urlencoded_part"])) { - $appendTo = AJXP_Utils::sanitize(SystemTextEncoding::fromUTF8(urldecode($httpVars["appendto_urlencoded_part"])), AJXP_SANITIZE_FILENAME); - if(isSet($httpVars["partial_upload"]) && $httpVars["partial_upload"] == 'true'){ - $originalAppendTo = $appendTo; - $appendTo .= ".dlpart"; - } - $this->logDebug("AppendTo FILE".$appendTo); - $already_existed = $this->appendUploadedData($destination, $userfile_name, $appendTo); - $userfile_name = $appendTo; - if($partialUpload && $partialTargetSize == filesize($destination."/".$userfile_name)){ - // This was the last part. We can now rename to the original name. - if(is_file($destination."/".$originalAppendTo)){ - unlink($destination."/".$originalAppendTo); - } - $result = @rename($destination."/".$userfile_name, $destination."/".$originalAppendTo); - if($result === false){ - throw new Exception("Error renaming ".$destination."/".$userfile_name." to ".$destination."/".$originalAppendTo); - } - $userfile_name = $originalAppendTo; - $partialUpload = false; - // Send a create event! - $already_existed = false; - } - } - - // NOW PREPARE POST-UPLOAD EVENTS - $this->changeMode($destination."/".$userfile_name,$repoData); - $createdNode = new AJXP_Node($destination."/".$userfile_name); - clearstatcache(true, $createdNode->getUrl()); - $createdNode->loadNodeInfo(true); - $logMessage.="$mess[34] ".SystemTextEncoding::toUTF8($userfile_name)." $mess[35] $dir"; - $logFile = $this->addSlugToPath(SystemTextEncoding::fromUTF8($dir))."/".$userfile_name; - $this->logInfo("Upload File", array("file"=>$logFile, "files"=> $logFile ) ); - - if($partialUpload){ - $this->logDebug("Return Partial Upload: SUCESS but no event yet"); - if(isSet($already_existed) && $already_existed === true){ - return array("SUCCESS" => true, "PARTIAL_NODE" => $createdNode); - } - } else { - $this->logDebug("Return success"); - if(isSet($already_existed) && $already_existed === true){ - return array("SUCCESS" => true, "UPDATED_NODE" => $createdNode); - }else{ - return array("SUCCESS" => true, "CREATED_NODE" => $createdNode); - } - } - - }catch(Exception $e){ - $errorCode = $e->getCode(); - if(empty($errorCode)) $errorCode = 411; - return array("ERROR" => array("CODE" => $errorCode, "MESSAGE" => $e->getMessage())); - } - - } - - - break; - - case "lsync" : - - if (!ConfService::currentContextIsCommandLine()) { - //die("This command must be accessed via CLI only."); - } - $fromNode = null; - $toNode = null; - $copyOrMove = false; - if (isSet($httpVars["from"])) { - $fromNode = new AJXP_Node($this->urlBase.AJXP_Utils::decodeSecureMagic($httpVars["from"])); - } - if (isSet($httpVars["to"])) { - $toNode = new AJXP_Node($this->urlBase.AJXP_Utils::decodeSecureMagic($httpVars["to"])); - } - if (isSet($httpVars["copy"]) && $httpVars["copy"] == "true") { - $copyOrMove = true; - } - AJXP_Controller::applyHook("node.change", array($fromNode, $toNode, $copyOrMove)); - - break; - - //------------------------------------ - // XML LISTING - //------------------------------------ - case "ls": - - if(!isSet($dir) || $dir == "/") $dir = ""; - $lsOptions = $this->parseLsOptions((isSet($httpVars["options"])?$httpVars["options"]:"a")); - - $startTime = microtime(); - $dir = AJXP_Utils::securePath($dir); - $path = $this->urlBase.($dir!= ""?($dir[0]=="/"?"":"/").$dir:""); - $nonPatchedPath = $path; - if (AJXP_MetaStreamWrapper::actualRepositoryWrapperClass($this->repository->getId()) == "fsAccessWrapper") { - $nonPatchedPath = fsAccessWrapper::unPatchPathForBaseDir($path); - } - $testPath = @stat($path); - if($testPath === null || $testPath === false){ - throw new Exception("There was a problem trying to open folder ". $path. ", please check your Administrator"); - } - if(!is_readable($path) && !is_writeable($path)){ - throw new Exception("You are not allowed to access folder " . $path); - } - // Backward compat - if($selection->isUnique() && strpos($selection->getUniqueFile(), "/") !== 0){ - $selection->setFiles(array($dir . "/" . $selection->getUniqueFile())); - } - - $orderField = $orderDirection = null; - $threshold = 500; $limitPerPage = 200; - $defaultOrder = $this->repository->getOption("REMOTE_SORTING_DEFAULT_COLUMN"); - $defaultDirection = $this->repository->getOption("REMOTE_SORTING_DEFAULT_DIRECTION"); - if ($this->repository->getOption("REMOTE_SORTING")) { - $orderDirection = isSet($httpVars["order_direction"])?strtolower($httpVars["order_direction"]):$defaultDirection; - $orderField = isSet($httpVars["order_column"])?$httpVars["order_column"]:$defaultOrder; - if ($orderField != null && !in_array($orderField, array("ajxp_label", "filesize", "ajxp_modiftime", "mimestring"))) { - $orderField = $defaultOrder; - } - } - if(isSet($httpVars["recursive"]) && $httpVars["recursive"] == "true"){ - $max_depth = (isSet($httpVars["max_depth"])?intval($httpVars["max_depth"]):0); - $max_nodes = (isSet($httpVars["max_nodes"])?intval($httpVars["max_nodes"]):0); - $crt_depth = (isSet($httpVars["crt_depth"])?intval($httpVars["crt_depth"])+1:1); - $crt_nodes = (isSet($httpVars["crt_nodes"])?intval($httpVars["crt_nodes"]):0); - }else{ - $threshold = $this->repository->getOption("PAGINATION_THRESHOLD"); - if(!isSet($threshold) || intval($threshold) == 0) $threshold = 500; - $limitPerPage = $this->repository->getOption("PAGINATION_NUMBER"); - if(!isset($limitPerPage) || intval($limitPerPage) == 0) $limitPerPage = 200; - } - - if(!$selection->isEmpty()){ - $uniqueNodes = $selection->buildNodes(); - $parentAjxpNode = new AJXP_Node($this->urlBase."/", array()); - AJXP_Controller::applyHook("node.read", array(&$parentAjxpNode)); - if (AJXP_XMLWriter::$headerSent == "tree") { - AJXP_XMLWriter::renderAjxpNode($parentAjxpNode, false); - } else { - AJXP_XMLWriter::renderAjxpHeaderNode($parentAjxpNode); - } - foreach($uniqueNodes as $node){ - if(!file_exists($node->getUrl()) || (!is_readable($node->getUrl()) && !is_writable($node->getUrl()))) continue; - $nodeName = $node->getLabel(); - if (!$this->filterNodeName($node->getPath(), $nodeName, $isLeaf, $lsOptions)) { - continue; - } - if (RecycleBinManager::recycleEnabled() && $node->getPath() == RecycleBinManager::getRecyclePath()) { - continue; - } - $node->loadNodeInfo(false, false, ($lsOptions["l"]?"all":"minimal")); - if (!empty($node->metaData["nodeName"]) && $node->metaData["nodeName"] != $nodeName) { - $node->setUrl(dirname($node->getUrl())."/".$node->metaData["nodeName"]); - } - if (!empty($node->metaData["hidden"]) && $node->metaData["hidden"] === true) { - continue; - } - if (!empty($node->metaData["mimestring_id"]) && array_key_exists($node->metaData["mimestring_id"], $mess)) { - $node->mergeMetadata(array("mimestring" => $mess[$node->metaData["mimestring_id"]])); - } - if(isSet($httpVars["page_position"]) && $httpVars["page_position"] == "true"){ - // Detect page position: we have to loading "siblings" - $parentPath = AJXP_Utils::safeDirname($node->getPath()); - $siblings = scandir($this->urlBase.$parentPath); - foreach($siblings as $i => $s){ - if($this->filterFile($s, true)) unset($siblings[$i]); - if($this->filterFolder($s)) unset($siblings[$i]); - } - if(count($siblings) > $threshold){ - //usort($siblings, "strcasecmp"); - $siblings = $this->orderNodes($siblings, $this->urlBase.$parentPath, $orderField, $orderDirection); - $index = array_search($node->getLabel(), $siblings); - $node->mergeMetadata(array("page_position" => floor($index / $limitPerPage) +1)); - } - } - AJXP_XMLWriter::renderAjxpNode($node); - } - AJXP_XMLWriter::close(); - break; - } - - $streamIsSeekable = AJXP_MetaStreamWrapper::wrapperIsSeekable($path); - - $sharedHandle = null; $handle = null; - if($streamIsSeekable){ - $handle = opendir($path); - $sharedHandle = $handle; - } - $countFiles = $this->countFiles($path, !$lsOptions["f"], false, $sharedHandle); - if(isSet($sharedHandle)){ - rewind($handle); - } - if(isSet($crt_nodes)){ - $crt_nodes += $countFiles; - } - $totalPages = $crtPage = 1; - if (isSet($threshold) && isSet($limitPerPage) && $countFiles > $threshold) { - $offset = 0; - $crtPage = 1; - if (isSet($page)) { - $offset = (intval($page)-1)*$limitPerPage; - $crtPage = $page; - } - $totalPages = floor($countFiles / $limitPerPage) + 1; - } else { - $offset = $limitPerPage = 0; - } - - $metaData = array(); - if (RecycleBinManager::recycleEnabled() && $dir == "") { - $metaData["repo_has_recycle"] = "true"; - } - $parentAjxpNode = new AJXP_Node($nonPatchedPath, $metaData); - $parentAjxpNode->loadNodeInfo(false, true, ($lsOptions["l"]?"all":"minimal")); - AJXP_Controller::applyHook("node.read", array(&$parentAjxpNode)); - if (AJXP_XMLWriter::$headerSent == "tree") { - AJXP_XMLWriter::renderAjxpNode($parentAjxpNode, false); - } else { - AJXP_XMLWriter::renderAjxpHeaderNode($parentAjxpNode); - } - if (isSet($totalPages) && isSet($crtPage) && ($totalPages > 1 || ! AJXP_Utils::userAgentIsNativePydioApp())) { - $remoteOptions = null; - if ($this->getFilteredOption("REMOTE_SORTING")) { - $remoteOptions = array( - "remote_order" => "true", - "currentOrderCol" => isSet($orderField)?$orderField:$defaultOrder, - "currentOrderDir"=> isSet($orderDirection)?$orderDirection:$defaultDirection - ); - } - $foldersCounts = $this->countFiles($path, TRUE, false, $sharedHandle); - if(isSet($sharedHandle)) { - rewind($sharedHandle); - } - AJXP_XMLWriter::renderPaginationData( - $countFiles, - $crtPage, - $totalPages, - $foldersCounts, - $remoteOptions - ); - if (!$lsOptions["f"]) { - AJXP_XMLWriter::close(); - if(isSet($sharedHandle)) { - closedir($sharedHandle); - } - break; - } - } - - $cursor = 0; - if(isSet($sharedHandle)){ - $handle = $sharedHandle; - }else{ - $handle = opendir($path); - } - if (!$handle) { - throw new AJXP_Exception("Cannot open dir ".$nonPatchedPath); - } - $nodes = array(); - while(false !== ($file = readdir($handle))){ - $nodes[] = $file; - } - closedir($handle); - $fullList = array("d" => array(), "z" => array(), "f" => array()); - - //$nodes = scandir($path); - $nodes = $this->orderNodes($nodes, $nonPatchedPath, $orderField, $orderDirection); - - foreach ($nodes as $nodeName) { - if($nodeName == "." || $nodeName == "..") { - continue; - } - $isLeaf = ""; - if (!$this->filterNodeName($path, $nodeName, $isLeaf, $lsOptions)) { - continue; - } - if (RecycleBinManager::recycleEnabled() && $dir == "" && "/".$nodeName == RecycleBinManager::getRecyclePath()) { - continue; - } - if ($offset > 0 && $cursor < $offset) { - $cursor ++; - continue; - } - - if ($limitPerPage > 0 && ($cursor - $offset) >= $limitPerPage) { - break; - } - - $currentFile = $nonPatchedPath."/".$nodeName; - $meta = array(); - if($isLeaf != "") $meta = array("is_file" => ($isLeaf?"1":"0")); - $node = new AJXP_Node($currentFile, $meta); - $node->setLabel($nodeName); - $node->loadNodeInfo(false, false, ($lsOptions["l"]?"all":"minimal")); - if (!empty($node->metaData["nodeName"]) && $node->metaData["nodeName"] != $nodeName) { - $node->setUrl($nonPatchedPath."/".$node->metaData["nodeName"]); - } - if (!empty($node->metaData["hidden"]) && $node->metaData["hidden"] === true) { - continue; - } - if (!empty($node->metaData["mimestring_id"]) && array_key_exists($node->metaData["mimestring_id"], $mess)) { - $node->mergeMetadata(array("mimestring" => $mess[$node->metaData["mimestring_id"]])); - } - if (isSet($originalLimitPerPage) && $cursor > $originalLimitPerPage) { - $node->mergeMetadata(array("page_position" => floor($cursor / $originalLimitPerPage) +1)); - } - - $nodeType = "d"; - if ($node->isLeaf()) { - if (AJXP_Utils::isBrowsableArchive($nodeName)) { - if ($lsOptions["f"] && $lsOptions["z"]) { - $nodeType = "f"; - } else { - $nodeType = "z"; - } - } else $nodeType = "f"; - } - // There is a special sorting, cancel the reordering of files & folders. - if(isSet($orderField) && $orderField != "ajxp_label" && !(isSet($httpVars["recursive"]) && $httpVars["recursive"] == "true" )) { - $nodeType = "f"; - } - $fullList[$nodeType][$nodeName] = $node; - $cursor ++; - } - if (isSet($httpVars["recursive"]) && $httpVars["recursive"] == "true") { - $breakNow = false; - if(isSet($max_depth) && $max_depth > 0 && $crt_depth >= $max_depth) $breakNow = true; - if(isSet($max_nodes) && $max_nodes > 0 && $crt_nodes >= $max_nodes) $breakNow = true; - /** - * @var $nodeDir AJXP_Node - */ - foreach ($fullList["d"] as &$nodeDir) { - if($breakNow){ - $nodeDir->mergeMetadata(array("ajxp_has_children" => $this->countFiles($nodeDir->getUrl(), false, true)?"true":"false")); - AJXP_XMLWriter::renderAjxpNode($nodeDir, true); - continue; - } - $this->switchAction("ls", array( - "dir" => SystemTextEncoding::toUTF8($nodeDir->getPath()), - "options"=> $httpVars["options"], - "recursive" => "true", - "max_depth"=> $max_depth, - "max_nodes"=> $max_nodes, - "crt_depth"=> $crt_depth, - "crt_nodes"=> $crt_nodes, - ), array()); - } - } else { - array_map(array("AJXP_XMLWriter", "renderAjxpNode"), $fullList["d"]); - } - array_map(array("AJXP_XMLWriter", "renderAjxpNode"), $fullList["z"]); - array_map(array("AJXP_XMLWriter", "renderAjxpNode"), $fullList["f"]); - - // ADD RECYCLE BIN TO THE LIST - if ($dir == "" && RecycleBinManager::recycleEnabled() && $this->getFilteredOption("HIDE_RECYCLE", $this->repository) !== true) { - $recycleBinOption = RecycleBinManager::getRelativeRecycle(); - if (file_exists($this->urlBase.$recycleBinOption)) { - $recycleNode = new AJXP_Node($this->urlBase.$recycleBinOption); - $recycleNode->loadNodeInfo(); - AJXP_XMLWriter::renderAjxpNode($recycleNode); - } - } - - $this->logDebug("LS Time : ".intval((microtime()-$startTime)*1000)."ms"); - - AJXP_XMLWriter::close(); - - break; - } - - - $xmlBuffer = ""; - if (isset($logMessage) || isset($errorMessage)) { - $xmlBuffer .= AJXP_XMLWriter::sendMessage((isSet($logMessage)?$logMessage:null), (isSet($errorMessage)?$errorMessage:null), false); - } - if ($reloadContextNode) { - if(!isSet($pendingSelection)) $pendingSelection = ""; - $xmlBuffer .= AJXP_XMLWriter::reloadDataNode("", $pendingSelection, false); - } - if (isSet($reloadDataNode)) { - $xmlBuffer .= AJXP_XMLWriter::reloadDataNode($reloadDataNode, "", false); - } - if (isSet($nodesDiffs)) { - $xmlBuffer .= AJXP_XMLWriter::writeNodesDiff($nodesDiffs, false); - } - - return $xmlBuffer; - } - - protected function orderNodes($nodes, $path, $orderField, $orderDirection){ - - usort($nodes, "strcasecmp"); - if (!empty($orderField) && !empty($orderDirection) && $orderField == "ajxp_label" && $orderDirection == "desc") { - $nodes = array_reverse($nodes); - } - if (!empty($this->driverConf["SCANDIR_RESULT_SORTFONC"])) { - usort($nodes, $this->driverConf["SCANDIR_RESULT_SORTFONC"]); - } - if (!empty($orderField) && !empty($orderDirection) && $orderField != "ajxp_label") { - $toSort = array(); - foreach ($nodes as $node) { - if($orderField == "filesize") $toSort[$node] = is_file($path."/".$node) ? filesize($path."/".$node) : 0; - else if($orderField == "ajxp_modiftime") $toSort[$node] = filemtime($path."/".$node); - else if($orderField == "mimestring") $toSort[$node] = pathinfo($node, PATHINFO_EXTENSION); - } - if($orderDirection == "asc") asort($toSort); - else arsort($toSort); - $nodes = array_keys($toSort); - } - return $nodes; - - } - - public function parseLsOptions($optionString) - { - // LS OPTIONS : dz , a, d, z, all of these with or without l - // d : directories - // z : archives - // f : files - // => a : all, alias to dzf - // l : list metadata - $allowed = array("a", "d", "z", "f", "l"); - $lsOptions = array(); - foreach ($allowed as $key) { - if (strchr($optionString, $key)!==false) { - $lsOptions[$key] = true; - } else { - $lsOptions[$key] = false; - } - } - if ($lsOptions["a"]) { - $lsOptions["d"] = $lsOptions["z"] = $lsOptions["f"] = true; - } - return $lsOptions; - } - - /** - * @param AJXP_Node $ajxpNode - * @param bool $parentNode - * @param bool $details - * @return void - */ - public function loadNodeInfo(&$ajxpNode, $parentNode = false, $details = false) - { - $mess = ConfService::getMessages(); - - $nodeName = basename($ajxpNode->getPath()); - $metaData = $ajxpNode->metadata; - if (!isSet($metaData["is_file"])) { - $isLeaf = is_file($ajxpNode->getUrl()) || AJXP_Utils::isBrowsableArchive($nodeName); - $metaData["is_file"] = ($isLeaf?"1":"0"); - } else { - $isLeaf = $metaData["is_file"] == "1" ? true : false; - } - $metaData["filename"] = $ajxpNode->getPath(); - - if (RecycleBinManager::recycleEnabled() && $ajxpNode->getPath() == RecycleBinManager::getRelativeRecycle()) { - $recycleIcon = ($this->countFiles($ajxpNode->getUrl(), false, true)>0?"trashcan_full.png":"trashcan.png"); - $metaData["icon"] = $recycleIcon; - $metaData["fonticon"] = "delete"; - $metaData["mimestring"] = $mess[122]; - $ajxpNode->setLabel($mess[122]); - $metaData["ajxp_mime"] = "ajxp_recycle"; - } else { - $mimeData = AJXP_Utils::mimeData($ajxpNode->getUrl(), !$isLeaf); - $metaData["mimestring_id"] = $mimeData[0]; //AJXP_Utils::mimetype($ajxpNode->getUrl(), "type", !$isLeaf); - $metaData["icon"] = $mimeData[1]; //AJXP_Utils::mimetype($nodeName, "image", !$isLeaf); - if(!empty($mimeData[2])){ - $metaData["fonticon"] = $mimeData[2]; - } - if ($metaData["icon"] == "folder.png") { - $metaData["openicon"] = "folder_open.png"; - } - if (!$isLeaf) { - $metaData["ajxp_mime"] = "ajxp_folder"; - } - } - //if ($lsOptions["l"]) { - - $metaData["file_group"] = @filegroup($ajxpNode->getUrl()) || "unknown"; - $metaData["file_owner"] = @fileowner($ajxpNode->getUrl()) || "unknown"; - $crtPath = $ajxpNode->getPath(); - $vRoots = $this->repository->listVirtualRoots(); - $metaData["ajxp_readonly"] = "false"; - if (!@$this->isWriteable($ajxpNode->getUrl())) { - $metaData["ajxp_readonly"] = "true"; - } - if (!empty($crtPath)) { - if (isSet($vRoots[ltrim($crtPath, "/")])) { - $metaData["ajxp_readonly"] = $vRoots[ltrim($crtPath, "/")]["right"] == "r" ? "true" : "false"; - } - } else { - if (count($vRoots)) { - $metaData["ajxp_readonly"] = "true"; - } - } - $fPerms = @fileperms($ajxpNode->getUrl()); - if ($fPerms !== false) { - $fPerms = substr(decoct( $fPerms ), ($isLeaf?2:1)); - } else { - $fPerms = '0000'; - } - $metaData["file_perms"] = $fPerms; - $datemodif = $this->date_modif($ajxpNode->getUrl()); - $metaData["ajxp_modiftime"] = ($datemodif ? $datemodif : "0"); - $metaData["ajxp_description"] =$metaData["ajxp_relativetime"] = $mess[4]." ".AJXP_Utils::relativeDate($datemodif, $mess); - $metaData["bytesize"] = 0; - if ($isLeaf) { - $metaData["bytesize"] = filesize($ajxpNode->getUrl()); - } - $metaData["filesize"] = AJXP_Utils::roundSize($metaData["bytesize"]); - if (AJXP_Utils::isBrowsableArchive($nodeName)) { - $metaData["ajxp_mime"] = "ajxp_browsable_archive"; - } - - if ($details == "minimal") { - $miniMeta = array( - "is_file" => $metaData["is_file"], - "filename" => $metaData["filename"], - "bytesize" => $metaData["bytesize"], - "ajxp_modiftime" => $metaData["ajxp_modiftime"], - ); - $ajxpNode->mergeMetadata($miniMeta); - } else { - $ajxpNode->mergeMetadata($metaData); - } - - } - - /** - * @param array $uploadData Php-upload array - * @param String $destination Destination folder, including stream data - * @param String $filename Destination filename - * @param array $messages Application messages table - * @return bool - * @throws Exception - */ - protected function copyUploadedData($uploadData, $destination, $filename, $messages){ - if (isSet($uploadData["input_upload"])) { - try { - $this->logDebug("Begining reading INPUT stream"); - $input = fopen("php://input", "r"); - $output = fopen("$destination/".$filename, "w"); - $sizeRead = 0; - while ($sizeRead < intval($uploadData["size"])) { - $chunk = fread($input, 4096); - $sizeRead += strlen($chunk); - fwrite($output, $chunk, strlen($chunk)); - } - fclose($input); - fclose($output); - $this->logDebug("End reading INPUT stream"); - } catch (Exception $e) { - throw new Exception($e->getMessage(), 411); - } - } else { - $result = @move_uploaded_file($uploadData["tmp_name"], "$destination/".$filename); - if (!$result) { - $realPath = AJXP_MetaStreamWrapper::getRealFSReference("$destination/".$filename); - $result = move_uploaded_file($uploadData["tmp_name"], $realPath); - } - if (!$result) { - $errorMessage="$messages[33] ".$filename; - throw new Exception($errorMessage, 411); - } - } - return true; - } - - /** - * @param String $folder Folder destination - * @param String $target Existing part to append data - * @param String $source Maybe updated by the function - * @return bool If the target file already existed or not. - */ - protected function appendUploadedData($folder, $source, $target){ - - $already_existed = false; - if($source == $target){ - throw new Exception("Something nasty happened: trying to copy $source into itself, it will create a loop!"); - } - if (file_exists($folder ."/" . $target)) { - $already_existed = true; - $this->logDebug("Should copy stream from $source to $target"); - $partO = fopen($folder."/".$source, "r"); - $appendF = fopen($folder ."/". $target, "a+"); - while (!feof($partO)) { - $buf = fread($partO, 1024); - fwrite($appendF, $buf, strlen($buf)); - } - fclose($partO); - fclose($appendF); - $this->logDebug("Done, closing streams!"); - } - @unlink($folder."/".$source); - return $already_existed; - - } - - public function prepareReadStream($headerType, $localName) - { - session_write_close(); - - restore_error_handler(); - restore_exception_handler(); - - set_exception_handler('download_exception_handler'); - set_error_handler('download_exception_handler'); - // required for IE, otherwise Content-disposition is ignored - if (ini_get('zlib.output_compression')) { - AJXP_Utils::safeIniSet('zlib.output_compression', 'Off'); - } - - if ($headerType == "plain") { - header("Content-type:text/plain"); - } else if ($headerType == "image") { - header("Content-Type: ".AJXP_Utils::getImageMimeType(basename($localName))."; name=\"".$localName."\""); - header('Cache-Control: public'); - } else { - HTMLWriter::generateAttachmentsHeader($localName, -1, false, false); - } - } - - public function readFile($filePathOrData, $headerType="plain", $localName="", $data=false, $gzip=null, $realfileSystem=false, $byteOffset=-1, $byteLength=-1) - { - if(!$data && !$gzip && !file_exists($filePathOrData)){ - throw new Exception("File $filePathOrData not found!"); - } - if ($gzip === null) { - $gzip = ConfService::getCoreConf("GZIP_COMPRESSION"); - } - if (!$realfileSystem && AJXP_MetaStreamWrapper::actualRepositoryWrapperClass($this->repository->getId()) == "fsAccessWrapper") { - $originalFilePath = $filePathOrData; - $filePathOrData = fsAccessWrapper::patchPathForBaseDir($filePathOrData); - } - session_write_close(); - - restore_error_handler(); - restore_exception_handler(); - - set_exception_handler('download_exception_handler'); - set_error_handler('download_exception_handler'); - // required for IE, otherwise Content-disposition is ignored - if (ini_get('zlib.output_compression')) { - AJXP_Utils::safeIniSet('zlib.output_compression', 'Off'); - } - - $isFile = !$data && !$gzip; - if ($byteLength == -1) { - if ($data) { - $size = strlen($filePathOrData); - } else if ($realfileSystem) { - $size = sprintf("%u", filesize($filePathOrData)); - } else { - $size = filesize($filePathOrData); - } - } else { - $size = $byteLength; - } - if ($gzip && ($size > ConfService::getCoreConf("GZIP_LIMIT") || !function_exists("gzencode") || @strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') === FALSE)) { - $gzip = false; // disable gzip - } - - $localName = ($localName=="" ? basename((isSet($originalFilePath)?$originalFilePath:$filePathOrData)) : $localName); - if ($headerType == "plain") { - header("Content-type:text/plain"); - } else if ($headerType == "image") { - header("Content-Type: ".AJXP_Utils::getImageMimeType(basename($filePathOrData))."; name=\"".$localName."\""); - header("Content-Length: ".$size); - header('Cache-Control: public'); - } else { - if ($isFile) { - header("Accept-Ranges: 0-$size"); - $this->logDebug("Sending accept range 0-$size"); - } - - // Check if we have a range header (we are resuming a transfer) - if ( isset($_SERVER['HTTP_RANGE']) && $isFile && $size != 0 ) { - if ($headerType == "stream_content") { - if (extension_loaded('fileinfo') && AJXP_MetaStreamWrapper::actualRepositoryWrapperClass($this->repository->getId()) == "fsAccessWrapper") { - $fInfo = new fInfo( FILEINFO_MIME ); - $realfile = AJXP_MetaStreamWrapper::getRealFSReference($filePathOrData); - $mimeType = $fInfo->file( $realfile); - $splitChar = explode(";", $mimeType); - $mimeType = trim($splitChar[0]); - $this->logDebug("Detected mime $mimeType for $realfile"); - } else { - $mimeType = AJXP_Utils::getStreamingMimeType(basename($filePathOrData)); - } - header('Content-type: '.$mimeType); - } - // multiple ranges, which can become pretty complex, so ignore it for now - $ranges = explode('=', $_SERVER['HTTP_RANGE']); - $offsets = explode('-', $ranges[1]); - $offset = floatval($offsets[0]); - - $length = floatval($offsets[1]) - $offset; - if (!$length) $length = $size - $offset; - if ($length + $offset > $size || $length < 0) $length = $size - $offset; - $this->logDebug('Content-Range: bytes ' . $offset . '-' . $length . '/' . $size); - header('HTTP/1.1 206 Partial Content'); - header('Content-Range: bytes ' . $offset . '-' . ($offset + $length) . '/' . $size); - - header("Content-Length: ". $length); - $file = fopen($filePathOrData, 'rb'); - if(!is_resource($file)){ - throw new Exception("Failed opening file ".$filePathOrData); - } - fseek($file, 0); - $relOffset = $offset; - while ($relOffset > 2.0E9) { - // seek to the requested offset, this is 0 if it's not a partial content request - fseek($file, 2000000000, SEEK_CUR); - $relOffset -= 2000000000; - // This works because we never overcome the PHP 32 bit limit - } - fseek($file, $relOffset, SEEK_CUR); - - while(ob_get_level()) ob_end_flush(); - $readSize = 0.0; - $bufferSize = 1024 * 8; - while (!feof($file) && $readSize < $length && connection_status() == 0) { - $this->logDebug("dl reading $readSize to $length", $_SERVER["HTTP_RANGE"]); - echo fread($file, $bufferSize); - $readSize += $bufferSize; - flush(); - } - - fclose($file); - return; - } else { - if ($gzip) { - $gzippedData = ($data?gzencode($filePathOrData,9):gzencode(file_get_contents($filePathOrData), 9)); - $size = strlen($gzippedData); - } - HTMLWriter::generateAttachmentsHeader($localName, $size, $isFile, $gzip); - if ($gzip && isSet($gzippedData)) { - print $gzippedData; - return; - } - } - } - - if ($data) { - print($filePathOrData); - } else { - if ($this->getFilteredOption("USE_XSENDFILE", $this->repository) && AJXP_MetaStreamWrapper::actualRepositoryWrapperClass($this->repository->getId()) == "fsAccessWrapper") { - if(!$realfileSystem) $filePathOrData = AJXP_MetaStreamWrapper::getRealFSReference($filePathOrData); - $filePathOrData = str_replace("\\", "/", $filePathOrData); - $server_name = $_SERVER["SERVER_SOFTWARE"]; - $regex = '/^(lighttpd\/1.4).([0-9]{2}$|[0-9]{3}$|[0-9]{4}$)+/'; - if(preg_match($regex, $server_name)) - $header_sendfile = "X-LIGHTTPD-send-file"; - else - $header_sendfile = "X-Sendfile"; - - - header($header_sendfile.": ".SystemTextEncoding::toUTF8($filePathOrData)); - header("Content-type: application/octet-stream"); - header('Content-Disposition: attachment; filename="' . basename($filePathOrData) . '"'); - return; - } - if ($this->getFilteredOption("USE_XACCELREDIRECT", $this->repository->getId()) && AJXP_MetaStreamWrapper::actualRepositoryWrapperClass($this->repository->getId()) == "fsAccessWrapper" && array_key_exists("X-Accel-Mapping",$_SERVER)) { - if(!$realfileSystem) $filePathOrData = AJXP_MetaStreamWrapper::getRealFSReference($filePathOrData); - $filePathOrData = str_replace("\\", "/", $filePathOrData); - $filePathOrData = SystemTextEncoding::toUTF8($filePathOrData); - $mapping = explode('=',$_SERVER['X-Accel-Mapping']); - $replacecount = 0; - $accelfile = str_replace($mapping[0],$mapping[1],$filePathOrData,$replacecount); - if ($replacecount == 1) { - header("X-Accel-Redirect: $accelfile"); - header("Content-type: application/octet-stream"); - header('Content-Disposition: attachment; filename="' . basename($accelfile) . '"'); - return; - } else { - $this->logError("X-Accel-Redirect","Problem with X-Accel-Mapping for file $filePathOrData"); - } - } - $stream = fopen("php://output", "a"); - if ($realfileSystem) { - $this->logDebug("realFS!", array("file"=>$filePathOrData)); - $fp = fopen($filePathOrData, "rb"); - if(!is_resource($fp)){ - throw new Exception("Failed opening file ".$filePathOrData); - } - if ($byteOffset != -1) { - fseek($fp, $byteOffset); - } - $sentSize = 0; - $readChunk = 4096; - while (!feof($fp)) { - if ( $byteLength != -1 && ($sentSize + $readChunk) >= $byteLength) { - // compute last chunk and break after - $readChunk = $byteLength - $sentSize; - $break = true; - } - $data = fread($fp, $readChunk); - $dataSize = strlen($data); - fwrite($stream, $data, $dataSize); - $sentSize += $dataSize; - if (isSet($break)) { - break; - } - } - fclose($fp); - } else { - AJXP_MetaStreamWrapper::copyFileInStream($filePathOrData, $stream); - } - fflush($stream); - fclose($stream); - } - } - - public function countFiles($dirName, $foldersOnly = false, $nonEmptyCheckOnly = false, $dirHANDLE = null) - { - if(is_resource($dirHANDLE)){ - $handle = $dirHANDLE; - }else{ - $handle=@opendir($dirName); - } - if ($handle === false) { - throw new Exception("Error while trying to open directory ".$dirName); - } - if ($foldersOnly && !AJXP_MetaStreamWrapper::wrapperIsRemote($dirName)) { - if($dirHANDLE == null || !is_resource($dirHANDLE)){ - closedir($handle); - } - $path = AJXP_MetaStreamWrapper::getRealFSReference($dirName, true); - $dirs = glob($path."/*", GLOB_ONLYDIR|GLOB_NOSORT); - if($dirs === false) return 0; - return count($dirs); - } - $count = 0; - $showHiddenFiles = $this->getFilteredOption("SHOW_HIDDEN_FILES", $this->repository); - while (false !== ($file = readdir($handle))) { - if($file != "." && $file !=".." - && !(AJXP_Utils::isHidden($file) && !$showHiddenFiles)){ - if($foldersOnly && is_file($dirName."/".$file)) continue; - $count++; - if($nonEmptyCheckOnly) break; - } - } - if($dirHANDLE == null || !is_resource($dirHANDLE)){ - closedir($handle); - } - return $count; - } - - public function date_modif($file) - { - $tmp = @filemtime($file) or 0; - return $tmp;// date("d,m L Y H:i:s",$tmp); - } - - public static $currentZipOperationHandler; - public function extractArchiveItemPreCallback($status, $data){ - $fullname = $data['filename']; - $size = $data['size']; - $realBase = AJXP_MetaStreamWrapper::getRealFSReference($this->urlBase); - $realBase = str_replace("\\", "/", $realBase); - $repoName = $this->urlBase.str_replace($realBase, "", $fullname); - $toNode = new AJXP_Node($repoName); - $toNode->setLeaf($data['folder'] ? false:true); - if(file_exists($toNode->getUrl())){ - AJXP_Controller::applyHook("node.before_change", array($toNode, $size)); - }else{ - AJXP_Controller::applyHook("node.before_create", array($toNode, $size)); - } - return 1; - } - - public function extractArchiveItemPostCallback($status, $data){ - $fullname = $data['filename']; - $realBase = AJXP_MetaStreamWrapper::getRealFSReference($this->urlBase); - $repoName = str_replace($realBase, "", $fullname); - try{ - $this->filterUserSelectionToHidden([$repoName]); - }catch(Exception $e){ - @unlink($this->urlBase.$repoName); - return 1; - } - $toNode = new AJXP_Node($this->urlBase.$repoName); - $toNode->setLeaf($data['folder'] ? false:true); - AJXP_Controller::applyHook("node.change", array(null, $toNode, false)); - return 1; - } - - /** - * Extract an archive directly inside the dest directory. - * - * @param string $destDir - * @param UserSelection $selection - * @param array $error - * @param array $success - */ - public function extractArchive($destDir, $selection, &$error, &$success) - { - require_once(AJXP_BIN_FOLDER."/pclzip.lib.php"); - $zipPath = $selection->getZipPath(true); - $zipLocalPath = $selection->getZipLocalPath(true); - if(strlen($zipLocalPath)>1 && $zipLocalPath[0] == "/") $zipLocalPath = substr($zipLocalPath, 1)."/"; - $files = $selection->getFiles(); - - $realZipFile = AJXP_MetaStreamWrapper::getRealFSReference($this->urlBase.$zipPath); - $archive = new PclZip($realZipFile); - $content = $archive->listContent(); - foreach ($files as $key => $item) {// Remove path - $item = substr($item, strlen($zipPath)); - if($item[0] == "/") $item = substr($item, 1); - foreach ($content as $zipItem) { - if ($zipItem["stored_filename"] == $item || $zipItem["stored_filename"] == $item."/") { - $files[$key] = $zipItem["stored_filename"]; - break; - } else { - unset($files[$key]); - } - } - } - $this->logDebug("Archive", $this->addSlugToPath($files)); - $realDestination = AJXP_MetaStreamWrapper::getRealFSReference($this->urlBase.$destDir); - $this->logDebug("Extract", array($realDestination, $realZipFile, $this->addSlugToPath($files), $zipLocalPath)); - self::$currentZipOperationHandler = &$this; - $result = $archive->extract(PCLZIP_OPT_BY_NAME, $files, - PCLZIP_OPT_PATH, $realDestination, - PCLZIP_OPT_REMOVE_PATH, $zipLocalPath, - PCLZIP_CB_PRE_EXTRACT, "staticExtractArchiveItemPreCallback", - PCLZIP_CB_POST_EXTRACT, "staticExtractArchiveItemPostCallback", - PCLZIP_OPT_STOP_ON_ERROR - ); - self::$currentZipOperationHandler = null; - if ($result <= 0) { - $error[] = $archive->errorInfo(true); - } else { - $mess = ConfService::getMessages(); - $success[] = sprintf($mess[368], basename($zipPath), $destDir); - } - } - - public function copyOrMove($destDir, $selectedFiles, &$error, &$success, $move = false) - { - $this->logDebug("CopyMove", array("dest"=>$this->addSlugToPath($destDir), "selection" => $this->addSlugToPath($selectedFiles))); - $mess = ConfService::getMessages(); - if (!$this->isWriteable($this->urlBase.$destDir)) { - $error[] = $mess[38]." ".$destDir." ".$mess[99]; - return ; - } - $repoData = array( - 'base_url' => $this->urlBase, - 'chmod' => $this->repository->getOption('CHMOD_VALUE'), - 'recycle' => $this->repository->getOption('RECYCLE_BIN') - ); - - foreach ($selectedFiles as $selectedFile) { - if ($move && !$this->isWriteable(dirname($this->urlBase.$selectedFile))) { - $error[] = "\n".$mess[38]." ".dirname($selectedFile)." ".$mess[99]; - continue; - } - $this->copyOrMoveFile($destDir, $selectedFile, $error, $success, $move, $repoData, $repoData); - } - } - - public function rename($filePath, $filename_new, $dest = null) - { - $nom_fic=basename($filePath); - $mess = ConfService::getMessages(); - $filename_new=AJXP_Utils::sanitize(SystemTextEncoding::magicDequote($filename_new), AJXP_SANITIZE_FILENAME); - $filename_new = substr($filename_new, 0, ConfService::getCoreConf("NODENAME_MAX_LENGTH")); - $old=$this->urlBase.$filePath; - if (!$this->isWriteable($old)) { - throw new AJXP_Exception($mess[34]." ".$nom_fic." ".$mess[99]); - } - if($dest == null) $new=dirname($old)."/".$filename_new; - else $new = $this->urlBase.$dest; - if ($filename_new=="" && $dest == null) { - throw new AJXP_Exception("$mess[37]"); - } - if (file_exists($new)) { - throw new AJXP_Exception("$filename_new $mess[43]"); - } - if (!file_exists($old)) { - throw new AJXP_Exception($mess[100]." $nom_fic"); - } - $oldNode = new AJXP_Node($old); - AJXP_Controller::applyHook("node.before_path_change", array(&$oldNode)); - $test = @rename($old,$new); - if($test === false){ - throw new Exception("Error while renaming ".$old." to ".$new); - } - AJXP_Controller::applyHook("node.change", array($oldNode, new AJXP_Node($new), false)); - } - - public static function autoRenameForDest($destination, $fileName) - { - if(!is_file($destination."/".$fileName)) return $fileName; - $i = 1; - $ext = ""; - $split = explode(".", $fileName); - if (count($split) > 1) { - $ext = ".".$split[count($split)-1]; - array_pop($split); - $name = join(".", $split); - } else { - $name = $fileName; - } - while (is_file($destination."/".$name."-$i".$ext)) { - $i++; // increment i until finding a non existing file. - } - return $name."-$i".$ext; - } - - public function mkDir($crtDir, $newDirName, $ignoreExists = false) - { - $currentNodeDir = new AJXP_Node($this->urlBase.$crtDir); - AJXP_Controller::applyHook("node.before_change", array(&$currentNodeDir)); - - $mess = ConfService::getMessages(); - if ($newDirName=="") { - return "$mess[37]"; - } - if (file_exists($this->urlBase."$crtDir/$newDirName")) { - if($ignoreExists) return null; - return "$mess[40]"; - } - if (!$this->isWriteable($this->urlBase."$crtDir")) { - return $mess[38]." $crtDir ".$mess[99]; - } - - $dirMode = 0775; - $chmodValue = $this->repository->getOption("CHMOD_VALUE"); - if (isSet($chmodValue) && $chmodValue != "") { - $dirMode = octdec(ltrim($chmodValue, "0")); - if ($dirMode & 0400) $dirMode |= 0100; // User is allowed to read, allow to list the directory - if ($dirMode & 0040) $dirMode |= 0010; // Group is allowed to read, allow to list the directory - if ($dirMode & 0004) $dirMode |= 0001; // Other are allowed to read, allow to list the directory - } - $old = umask(0); - mkdir($this->urlBase."$crtDir/$newDirName", $dirMode); - umask($old); - $newNode = new AJXP_Node($this->urlBase.$crtDir."/".$newDirName); - $newNode->setLeaf(false); - AJXP_Controller::applyHook("node.change", array(null, $newNode, false)); - return null; - } - - public function createEmptyFile($crtDir, $newFileName, $content = "", $force = false) - { - AJXP_Controller::applyHook("node.before_change", array(new AJXP_Node($this->urlBase.$crtDir))); - $mess = ConfService::getMessages(); - if ($newFileName=="") { - return "$mess[37]"; - } - if (!$force && file_exists($this->urlBase."$crtDir/$newFileName")) { - return "$mess[71]"; - } - if (!$this->isWriteable($this->urlBase."$crtDir")) { - return "$mess[38] $crtDir $mess[99]"; - } - $repoData = array( - 'base_url' => $this->urlBase, - 'chmod' => $this->repository->getOption('CHMOD_VALUE'), - 'recycle' => $this->repository->getOption('RECYCLE_BIN') - ); - $fp=fopen($this->urlBase."$crtDir/$newFileName","w"); - if ($fp) { - if ($content != "") { - fputs($fp, $content); - } - $this->changeMode($this->urlBase."$crtDir/$newFileName", $repoData); - fclose($fp); - $newNode = new AJXP_Node($this->urlBase."$crtDir/$newFileName"); - AJXP_Controller::applyHook("node.change", array(null, $newNode, false)); - return null; - } else { - return "$mess[102] $crtDir/$newFileName (".$fp.")"; - } - } - - - public function delete($selectedFiles, &$logMessages) - { - $repoData = array( - 'base_url' => $this->urlBase, - 'chmod' => $this->repository->getOption('CHMOD_VALUE'), - 'recycle' => $this->repository->getOption('RECYCLE_BIN') - ); - $mess = ConfService::getMessages(); - foreach ($selectedFiles as $selectedFile) { - if ($selectedFile == "" || $selectedFile == DIRECTORY_SEPARATOR) { - return $mess[120]; - } - $fileToDelete=$this->urlBase.$selectedFile; - - if (!file_exists($fileToDelete)) { - $logMessages[]=$mess[100]." ".SystemTextEncoding::toUTF8($selectedFile); - continue; - } - - $node = new AJXP_Node($fileToDelete); - $node->setLeaf(!is_dir($fileToDelete)); - - $this->deldir($fileToDelete, $repoData); - - if ($node->isLeaf()) { - $logMessages[]="$mess[38] ".SystemTextEncoding::toUTF8($selectedFile)." $mess[44]."; - } else { - $logMessages[]="$mess[34] ".SystemTextEncoding::toUTF8($selectedFile)." $mess[44]."; - } - AJXP_Controller::applyHook("node.change", [$node]); - } - return null; - } - - public function simpleCopy($origFile, $destFile) - { - return copy($origFile, $destFile); - } - - public function isWriteable($dir, $type="dir") - { - if ( $this->getFilteredOption("USE_POSIX", $this->repository) == true && extension_loaded('posix')) { - $real = AJXP_MetaStreamWrapper::getRealFSReference($dir); - return posix_access($real, POSIX_W_OK); - } - //clearstatcache(); - return is_writable($dir); - } - - /** - * Change file permissions - * - * @param String $path - * @param String $chmodValue - * @param Boolean $recursive - * @param String $nodeType "both", "file", "dir" - * @param $changedFiles - * @return void - */ - public function chmod($path, $chmodValue, $recursive, $nodeType, &$changedFiles) - { - $realValue = octdec(ltrim($chmodValue, "0")); - if (is_file($this->urlBase.$path)) { - if ($nodeType=="both" || $nodeType=="file") { - AJXP_MetaStreamWrapper::changeMode($this->urlBase.$path, $realValue); - $changedFiles[] = $path; - } - } else { - if ($nodeType=="both" || $nodeType=="dir") { - AJXP_MetaStreamWrapper::changeMode($this->urlBase.$path, $realValue); - $changedFiles[] = $path; - } - if ($recursive) { - $handler = opendir($this->urlBase.$path); - while ($child=readdir($handler)) { - if($child == "." || $child == "..") continue; - // do not pass realValue or it will be re-decoded. - $this->chmod($path."/".$child, $chmodValue, $recursive, $nodeType, $changedFiles); - } - closedir($handler); - } - } - } - - /** - * @param String $from - * @param String $to - * @param Boolean $copy - */ - public function nodeChanged(&$from, &$to, $copy = false) - { - $fromNode = $toNode = null; - if($from != null) $fromNode = new AJXP_Node($this->urlBase.$from); - if($to != null) $toNode = new AJXP_Node($this->urlBase.$to); - AJXP_Controller::applyHook("node.change", array($fromNode, $toNode, $copy)); - } - - /** - * @param String $node - * @param null $newSize - */ - public function nodeWillChange($node, $newSize = null) - { - if ($newSize != null) { - AJXP_Controller::applyHook("node.before_change", array(new AJXP_Node($this->urlBase.$node), $newSize)); - } else { - AJXP_Controller::applyHook("node.before_path_change", array(new AJXP_Node($this->urlBase.$node))); - } - } - - - /** - * @var fsAccessDriver - */ - public static $filteringDriverInstance; - - /** - * @param $src - * @param $dest - * @param $basedir - * @throws Exception - * @return PclZip - */ - public function makeZip ($src, $dest, $basedir) - { - $zipEncoding = ConfService::getCoreConf("ZIP_ENCODING"); - - @set_time_limit(0); - require_once(AJXP_BIN_FOLDER."/pclzip.lib.php"); - $filePaths = array(); - foreach ($src as $item) { - $realFile = AJXP_MetaStreamWrapper::getRealFSReference($this->urlBase."/".$item); - $realFile = AJXP_Utils::securePath($realFile); - if (basename($item) == "") { - $filePaths[] = array(PCLZIP_ATT_FILE_NAME => $realFile); - } else { - $shortName = basename($item); - if(!empty($zipEncoding)){ - $test = iconv(SystemTextEncoding::getEncoding(), $zipEncoding, $shortName); - if($test !== false) $shortName = $test; - } - $filePaths[] = array(PCLZIP_ATT_FILE_NAME => $realFile, - PCLZIP_ATT_FILE_NEW_SHORT_NAME => $shortName); - } - } - $this->logDebug("Pathes", $filePaths); - self::$filteringDriverInstance = $this; - $archive = new PclZip($dest); - - if($basedir == "__AJXP_ZIP_FLAT__/"){ - $vList = $archive->create($filePaths, PCLZIP_OPT_REMOVE_ALL_PATH, PCLZIP_OPT_NO_COMPRESSION, PCLZIP_OPT_ADD_TEMP_FILE_ON, PCLZIP_CB_PRE_ADD, 'zipPreAddCallback'); - }else{ - $basedir = AJXP_MetaStreamWrapper::getRealFSReference($this->urlBase).trim($basedir); - $this->logDebug("Basedir", array($basedir)); - $vList = $archive->create($filePaths, PCLZIP_OPT_REMOVE_PATH, $basedir, PCLZIP_OPT_NO_COMPRESSION, PCLZIP_OPT_ADD_TEMP_FILE_ON, PCLZIP_CB_PRE_ADD, 'zipPreAddCallback'); - } - - if (!$vList) { - throw new Exception("Zip creation error : ($dest) ".$archive->errorInfo(true)); - } - self::$filteringDriverInstance = null; - return $vList; - } - - - public function recursivePurge($dirName, $hardPurgeTime, $softPurgeTime = 0) - { - $handle=opendir($dirName); - $shareCenter = false; - if(class_exists("ShareCenter")){ - $shareCenter = ShareCenter::getShareCenter(); - } - if($handle === false){ - $this->logError(__FUNCTION__, "Cannot open folder ".$dirName); - return; - } - while (false !== ($entry = readdir($handle))) { - if ($entry == "" || $entry == ".." || AJXP_Utils::isHidden($entry) ) { - continue; - } - $fileName = $dirName."/".$entry; - if (is_file($fileName)) { - $docAge = time() - filemtime($fileName); - if ($hardPurgeTime > 0 && $docAge > $hardPurgeTime) { - $this->purge($fileName); - } elseif ($softPurgeTime > 0 && $docAge > $softPurgeTime) { - if($shareCenter !== false && $shareCenter->isShared(new AJXP_Node($fileName))) { - $this->purge($fileName); - } - } - } else { - $this->recursivePurge($fileName, $hardPurgeTime, $softPurgeTime); - } - } - closedir($handle); - } - - /** - * Apply specific operation to set a node as hidden. - * Can be overwritten, or will probably do nothing. - * @param AJXP_Node $node - */ - public function setHiddenAttribute($node){ - if(AJXP_MetaStreamWrapper::actualRepositoryWrapperClass($node->getRepositoryId()) == "fsAccessWrapper" && strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'){ - $realPath = AJXP_MetaStreamWrapper::getRealFSReference($node->getUrl()); - @shell_exec("attrib +H " . escapeshellarg($realPath)); - } - } - - private function purge($fileName) - { - $node = new AJXP_Node($fileName); - AJXP_Controller::applyHook("node.before_path_change", array($node)); - unlink($fileName); - AJXP_Controller::applyHook("node.change", array($node)); - $this->logInfo("Purge", array("file" => $fileName, "files" => $fileName)); - print(" - Purging document : ".$fileName."\n"); - } - - /** The publiclet URL making */ - public function makePublicletOptions($filePath, $password, $expire, $downloadlimit, $repository) - { - $data = array( - "DRIVER"=>$repository->getAccessType(), - "OPTIONS"=>NULL, - "FILE_PATH"=>$filePath, - "ACTION"=>"download", - "EXPIRE_TIME"=>$expire ? (time() + $expire * 86400) : 0, - "DOWNLOAD_LIMIT"=>$downloadlimit ? $downloadlimit : 0, - "PASSWORD"=>$password - ); - return $data; - } - - public function makeSharedRepositoryOptions($httpVars, $repository) - { - $newOptions = array( - "PATH" => SystemTextEncoding::toStorageEncoding($repository->getOption("PATH")).AJXP_Utils::decodeSecureMagic($httpVars["file"]), - "CREATE" => $repository->getOption("CREATE"), - "RECYCLE_BIN" => isSet($httpVars["inherit_recycle"])? $repository->getOption("RECYCLE_BIN") : "", - "DEFAULT_RIGHTS" => "", - "DATA_TEMPLATE" => "" - ); - if ($repository->getOption("USE_SESSION_CREDENTIALS")===true) { - $newOptions["ENCODED_CREDENTIALS"] = AJXP_Safe::getEncodedCredentialString(); - } - $customData = array(); - foreach ($httpVars as $key => $value) { - if (substr($key, 0, strlen("PLUGINS_DATA_")) == "PLUGINS_DATA_") { - $customData[substr($key, strlen("PLUGINS_DATA_"))] = $value; - } - } - if (count($customData)) { - $newOptions["PLUGINS_DATA"] = $customData; - } - if ($repository->getOption("META_SOURCES")) { - $newOptions["META_SOURCES"] = $repository->getOption("META_SOURCES"); - foreach ($newOptions["META_SOURCES"] as $index => &$data) { - if (isSet($data["USE_SESSION_CREDENTIALS"]) && $data["USE_SESSION_CREDENTIALS"] === true) { - $newOptions["META_SOURCES"][$index]["ENCODED_CREDENTIALS"] = AJXP_Safe::getEncodedCredentialString(); - } - } - AJXP_Controller::applyHook("workspace.share_metasources", array(&$newOptions["META_SOURCES"])); - } - return $newOptions; - } - - -} - -function zipPreAddCallback($value, &$header) -{ - if(fsAccessDriver::$filteringDriverInstance == null) return true; - $search = $header["filename"]; - $zipEncoding = ConfService::getCoreConf("ZIP_ENCODING"); - if(!empty($zipEncoding)){ - $test = iconv(SystemTextEncoding::getEncoding(), $zipEncoding, $header["stored_filename"]); - if($test !== false){ - $header["stored_filename"] = $test; - } - } - return !(fsAccessDriver::$filteringDriverInstance->filterFile($search, true) - || fsAccessDriver::$filteringDriverInstance->filterFolder($search, "contains")); -} - -if (!function_exists('staticExtractArchiveItemCallback')){ - function staticExtractArchiveItemPostCallback($status, $data){ - return fsAccessDriver::$currentZipOperationHandler->extractArchiveItemPostCallback($status, $data); - } - function staticExtractArchiveItemPreCallback($status, $data){ - return fsAccessDriver::$currentZipOperationHandler->extractArchiveItemPreCallback($status, $data); - } -} diff --git a/core/src/plugins/access.fs/class.fsAccessWrapper.php b/core/src/plugins/access.fs/class.fsAccessWrapper.php deleted file mode 100644 index 3653eb4098..0000000000 --- a/core/src/plugins/access.fs/class.fsAccessWrapper.php +++ /dev/null @@ -1,519 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - * - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Wrapper for a local filesystem - * @package AjaXplorer_Plugins - * @subpackage Access - */ -class fsAccessWrapper implements AjxpWrapper -{ - /** - * FileHandle resource - * - * @var resource - */ - protected $fp; - /** - * DirHandle resource - * - * @var resource - */ - protected $dH; - - /** - * If dH is not used but an array containing the listing - * instead. dH == -1 in that case. - * - * @var array() - */ - protected static $currentListing; - protected static $currentListingKeys; - protected static $currentListingIndex; - protected static $currentFileKey; - protected static $crtZip; - protected $realPath; - protected static $lastRealSize; - - /** - * Initialize the stream from the given path. - * - * @param string $path - * @param $streamType - * @param bool $storeOpenContext - * @param bool $skipZip - * @return mixed Real path or -1 if currentListing contains the listing : original path converted to real path - * @throws AJXP_Exception - * @throws Exception - */ - protected static function initPath($path, $streamType, $storeOpenContext = false, $skipZip = false) - { - $path = self::unPatchPathForBaseDir($path); - $url = AJXP_Utils::safeParseUrl($path); - $repoId = $url["host"]; - $test = trim($url["path"], "/"); - $atRoot = empty($test); - $repoObject = ConfService::getRepositoryById($repoId); - if(!isSet($repoObject)) throw new Exception("Cannot find repository with id ".$repoId); - $split = UserSelection::detectZip($url["path"]); - $insideZip = false; - if($split && $streamType == "file" && $split[1] != "/") $insideZip = true; - if($split && $streamType == "dir") $insideZip = true; - if($skipZip) $insideZip = false; - - $resolveUser = null; - if(isSet($url["user"]) && AuthService::usersEnabled()){ - $resolveUser = ConfService::getConfStorageImpl()->createUserObject($url["user"]); - } - $resolvedPath = realpath(SystemTextEncoding::toStorageEncoding($repoObject->getOption("PATH", false, $resolveUser))); - - //var_dump($path); - //var_dump($skipZip); - // Inside a zip : copy the file to a tmp file and return a reference to it - if ($insideZip) { - $zipPath = $split[0]; - $localPath = $split[1]; - require_once(AJXP_BIN_FOLDER."/pclzip.lib.php"); - //print($streamType.$path); - if ($streamType == "file") { - if (self::$crtZip == null || !is_array(self::$currentListingKeys)) { - $tmpDir = AJXP_Utils::getAjxpTmpDir() . DIRECTORY_SEPARATOR . md5(time()-rand()); - mkdir($tmpDir); - $tmpFileName = $tmpDir.DIRECTORY_SEPARATOR.basename($localPath); - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"Tmp file $tmpFileName"); - register_shutdown_function(array("fsAccessWrapper", "removeTmpFile"), $tmpDir, $tmpFileName); - $crtZip = new PclZip(AJXP_Utils::securePath($resolvedPath.$repoObject->resolveVirtualRoots($zipPath))); - $content = $crtZip->listContent(); - if(is_array($content)){ - foreach ($content as $item) { - $fName = AJXP_Utils::securePath($item["stored_filename"]); - if ($fName == $localPath || "/".$fName == $localPath) { - $localPath = $fName; - break; - } - } - } - $crtZip->extract(PCLZIP_OPT_BY_NAME, $localPath, PCLZIP_OPT_PATH, $tmpDir, PCLZIP_OPT_REMOVE_ALL_PATH); - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"Extracted ".$path." to ".dirname($localPath)); - if($storeOpenContext) self::$crtZip = $crtZip; - return $tmpFileName; - } else { - $key = basename($localPath); - if (array_key_exists($key, self::$currentListing)) { - self::$currentFileKey = $key; - return -1; - } else { - throw new AJXP_Exception("Cannot find key"); - } - } - } else { - $crtZip = new PclZip(AJXP_Utils::securePath($resolvedPath.$repoObject->resolveVirtualRoots($zipPath))); - $liste = $crtZip->listContent(); - if(!is_array($liste)) $liste = array(); - if($storeOpenContext) self::$crtZip = $crtZip; - $folders = array(); $files = array();$builtFolders = array(); - if($localPath[strlen($localPath)-1] != "/") $localPath.="/"; - foreach ($liste as $item) { - $stored = $item["stored_filename"]; - if($stored[0] != "/") $stored = "/".$stored; - $pathPos = strpos($stored, $localPath); - if ($pathPos !== false) { - $afterPath = substr($stored, $pathPos+strlen($localPath)); - if ($afterPath != "" && substr_count($afterPath, "/") < 2) { - $statValue = array(); - if (substr_count($afterPath, "/") == 0) { - $statValue[2] = $statValue["mode"] = ($item["folder"]?00040555:0100555); - $statValue[7] = $statValue["size"] = $item["size"]; - $statValue[8] = $statValue["atime"] = $item["mtime"]; - $statValue[9] = $statValue["mtime"] = $item["mtime"]; - $statValue[10] = $statValue["ctime"] = $item["mtime"]; - if (strpos($afterPath, "/") == strlen($afterPath)-1) { - $afterPath = substr($afterPath, 0, strlen($afterPath)-1); - } - //$statValue["filename"] = $zipPath.$localPath.$afterPath; - if ($item["folder"]) { - $folders[$afterPath] = $statValue; - } else { - $files[$afterPath] = $statValue; - } - } else { - $arr = explode("/", $afterPath); - $afterPath = array_shift($arr); - if(isSet($folders[$afterPath]) || isSet($builtFolders[$afterPath])) continue; - $statValue[2] = $statValue["mode"] = 00040555; - $statValue[7] = $statValue["size"] = 0; - $statValue[8] = $statValue["atime"] = $item["mtime"]; - $statValue[9] = $statValue["mtime"] = $item["mtime"]; - $statValue[10] = $statValue["ctime"] = $item["mtime"]; - $builtFolders[$afterPath] = $statValue; - } - } - } - } - self::$currentListing = array_merge($folders, $builtFolders, $files); - self::$currentListingKeys = array_keys(self::$currentListing); - self::$currentListingIndex = 0; - return -1; - } - } else { - if ($atRoot) { - $virtual = $repoObject->listVirtualRoots(); - if (count($virtual)) { - self::$currentListing = array(); - foreach ($virtual as $rootKey => $rootValue) { - $statValue = array(); - $statValue[2] = $statValue["mode"] = 00040000;//($rootValue["right"] == "rw" ? "00040000" : "00070000"); - self::$currentListing[$rootKey] = $statValue; - } - self::$currentListingKeys = array_keys(self::$currentListing); - self::$currentListingIndex = 0; - return -1; - } - } - return $resolvedPath.$repoObject->resolveVirtualRoots($url["path"]); - } - } - - public static function patchPathForBaseDir($dirPath) - { - if(!ini_get("open_basedir") || !preg_match('/\.zip/i', $dirPath)) return $dirPath; - return str_replace(".zip", "__ZIP_EXTENSION__", $dirPath); - - } - - public static function unPatchPathForBaseDir($dirPath) - { - if(!ini_get("open_basedir")) return $dirPath; - return str_replace("__ZIP_EXTENSION__", ".zip", $dirPath); - } - - public static function removeTmpFile($tmpDir, $tmpFile) - { - if(is_file($tmpFile)) unlink($tmpFile); - if(is_dir($tmpDir)) rmdir($tmpDir); - } - - protected static function closeWrapper() - { - if (self::$crtZip != null) { - self::$crtZip = null; - self::$currentListing = null; - self::$currentListingKeys = null; - self::$currentListingIndex = null; - self::$currentFileKey = null; - } - } - - public static function getRealFSReference($path, $persistent = false) - { - $contextOpened =false; - if (self::$crtZip != null) { - $contextOpened = true; - $crtZip = self::$crtZip; - self::$crtZip = null; - } - $realPath = self::initPath($path, "file"); - if (!$contextOpened) { - self::closeWrapper(); - } else { - self::$crtZip = $crtZip; - } - return $realPath; - } - - public static function isRemote() - { - return false; - } - - public static function isSeekable($url) - { - if(strpos($url, ".zip/") !== false) return false; - return true; - } - - public static function copyFileInStream($path, $stream) - { - $fp = fopen(self::getRealFSReference($path), "rb"); - if(!is_resource($fp)) return; - while (!feof($fp)) { - if(!ini_get("safe_mode")) @set_time_limit(60); - $data = fread($fp, 4096); - fwrite($stream, $data, strlen($data)); - } - fclose($fp); - } - - public static function changeMode($path, $chmodValue) - { - $realPath = self::initPath($path, "file"); - @chmod($realPath, $chmodValue); - } - - /** - * Opens the strem - * - * @param String $path Maybe in the form "ajxp.fs://repositoryId/pathToFile" - * @param String $mode - * @param string $options - * @param resource $context - * @return bool - */ - public function stream_open($path, $mode, $options, &$context) - { - try { - $this->realPath = AJXP_Utils::securePath(self::initPath($path, "file")); - } catch (Exception $e) { - AJXP_Logger::error(__CLASS__,"stream_open", "Error while opening stream $path (".$e->getMessage().")"); - return false; - } - if ($this->realPath == -1) { - $this->fp = -1; - return true; - } else { - $this->fp = fopen($this->realPath, $mode, $options); - return ($this->fp !== false); - } - } - - public function stream_seek($offset , $whence = SEEK_SET) - { - fseek($this->fp, $offset, $whence); - } - - public function stream_tell() - { - return ftell($this->fp); - } - - public function stream_stat() - { - $PROBE_REAL_SIZE = ConfService::getConf("PROBE_REAL_SIZE"); - if (is_resource($this->fp)) { - $statValue = fstat($this->fp); - fsAccessWrapper::$lastRealSize = false; - if ($statValue[2] > 0 && $PROBE_REAL_SIZE && !ini_get("safe_mode")) { - fsAccessWrapper::$lastRealSize = floatval(trim($this->getTrueSizeOnFileSystem($this->realPath))); - } - return $statValue; - } - if (is_resource($this->dH)) { - return fstat($this->dH); - } - if ($this->fp == -1) { - return self::$currentListing[self::$currentFileKey]; - } - return null; - } - - public function url_stat($path, $flags) - { - // File and zip case - $patchedPath = self::patchPathForBaseDir($path); - if (ini_get("open_basedir") && preg_match('/__ZIP_EXTENSION__/', $patchedPath)) { - // Zip Folder case - self::$lastRealSize = false; - $search = basename($path); - $realBase = $this->initPath(dirname($path), "dir"); - if ($realBase == -1) { - if (array_key_exists($search, self::$currentListing)) { - return self::$currentListing[$search]; - } - } - } - if ($fp = @fopen($path, "r")) { - $stat = fstat($fp); - fclose($fp); - return $stat; - } - // Folder case - $real = $this->initPath($path, "dir", false, true); - if ($real!=-1 && is_dir($real)) { - return stat($real); - } - // Zip Folder case - $search = basename($path); - $realBase = $this->initPath(dirname($path), "dir"); - if ($realBase == -1) { - if (array_key_exists($search, self::$currentListing)) { - return self::$currentListing[$search]; - } - } - // 000 permission file - if ($real != -1 && is_file($real)) { - return stat($real); - } - // Handle symlinks! - if ($real != -1 && is_link($real)) { - $realFile = @readlink($real); - if (is_file($realFile) || is_dir($realFile)) { - return stat($realFile); - } else { - // symlink is broken, delete it. - @unlink($real); - return null; - } - } - - // Non existing file - return null; - } - - public function rename($from, $to) - { - return rename($this->initPath($from, "file", false, true), $this->initPath($to, "file", false, true)); - } - - public function stream_read($count) - { - return fread($this->fp, $count); - } - - public function stream_write($data) - { - fwrite($this->fp, $data, strlen($data)); - return strlen($data); - } - - public function stream_eof() - { - return feof($this->fp); - } - - public function stream_close() - { - if (isSet($this->fp) && $this->fp!=-1 && $this->fp!==false) { - fclose($this->fp); - } - } - - public function stream_flush() - { - if (isSet($this->fp) && $this->fp!=-1 && $this->fp!==false) { - fflush($this->fp); - } - } - - public function unlink($path) - { - $this->realPath = $this->initPath($path, "file", false, true); - return unlink($this->realPath); - } - - public function rmdir($path, $options) - { - $this->realPath = $this->initPath($path, "file", false, true); - return rmdir($this->realPath); - } - - public function mkdir($path, $mode, $options) - { - return mkdir($this->initPath($path, "file"), $mode); - } - - /** - * Readdir functions - * - * @param string $path - * @param int $options - */ - public function dir_opendir ($path , $options ) - { - $this->realPath = $this->initPath($path, "dir", true); - if (is_string($this->realPath)) { - $this->dH = @opendir($this->realPath); - } else if ($this->realPath == -1) { - $this->dH = -1; - } - return $this->dH !== false; - } - public function dir_closedir () - { - $this->closeWrapper(); - if ($this->dH == -1) { - return; - } else { - closedir($this->dH); - } - } - public function dir_readdir () - { - if ($this->dH == -1) { - if (isSet(self::$currentListingKeys[self::$currentListingIndex])) { - self::$currentListingIndex++; - return self::$currentListingKeys[self::$currentListingIndex - 1]; - } else { - return false; - } - } else { - return readdir($this->dH); - } - } - public function dir_rewinddir () - { - if ($this->dH == -1) { - self::$currentListingIndex = 0; - } else { - rewinddir($this->dH); - } - } - - /** - * @return bool|float - */ - public static function getLastRealSize() - { - if(empty(self::$lastRealSize)) return false; - return self::$lastRealSize; - } - - protected function getTrueSizeOnFileSystem($file) - { - if (!(strtoupper(substr(PHP_OS, 0, 3)) == 'WIN')) { - $cmd = "stat -L -c%s ".escapeshellarg($file); - $val = trim(`$cmd`); - if (!is_numeric($val) || $val == -1) { - // No stat on system - $cmd = "ls -1s --block-size=1 ".escapeshellarg($file); - $val = trim(`$cmd`); - if (strlen($val) == 0 || floatval($val) == 0) { - // No block-size on system (probably busybox), try long output - $cmd = "ls -l ".escapeshellarg($file).""; - $arr = explode("/[\s]+/", `$cmd`); - $val = trim($arr[4]); - if (strlen($val) == 0 || floatval($val) == 0) { - // Still not working, get a value at least, not 0... - $val = sprintf("%u", filesize($file)); - } - } - } - return floatval($val); - } else if (class_exists("COM")) { - $fsobj = new COM("Scripting.FileSystemObject"); - $f = $fsobj->GetFile($file); - return floatval($f->Size); - } else if (is_file($file)) { - return exec('FOR %A IN ("'.$file.'") DO @ECHO %~zA'); - } else return sprintf("%u", filesize($file)); - } -} diff --git a/core/src/plugins/access.fs/fsActions.xml b/core/src/plugins/access.fs/fsActions.xml index 61e0415e80..84534af0f4 100644 --- a/core/src/plugins/access.fs/fsActions.xml +++ b/core/src/plugins/access.fs/fsActions.xml @@ -46,6 +46,12 @@ + + + + + + - + @@ -189,18 +194,28 @@ - + + - + modal.showDialogForm('EmptyRecycle', 'empty_recycle_form', null, function(){ + PydioApi.getClient().request({get_action:'empty_recycle'}, function(transport){ + PydioApi.getClient().parseXmlMessage(transport.responseXML); + }); + hideLightBox(true); + return false; + }); + ]]> + + AJXP_MESSAGE[177] + + ]]> + @@ -272,7 +287,7 @@ } } ]]> - + @@ -519,7 +534,7 @@ - + @@ -607,6 +622,11 @@ + + + + + + ]]> - + @@ -907,7 +928,7 @@ dObject.insert({before:'\
      \ '+MessageHash[400]+' \ - \ + OK\
      \ '}); $("dl_form_submit").observe("click", function(e){ @@ -948,7 +969,7 @@ ]]> - + @@ -993,43 +1014,6 @@ ]]> - - - - - - - - - - #{preview_rich} - - - ]]> - @@ -1067,33 +1051,6 @@ ]]> - - - - - - - - - - - ]]> - diff --git a/core/src/plugins/access.fs/fsTemplatePart.xml b/core/src/plugins/access.fs/fsTemplatePart.xml index bca9a8ad64..1f12363275 100644 --- a/core/src/plugins/access.fs/fsTemplatePart.xml +++ b/core/src/plugins/access.fs/fsTemplatePart.xml @@ -6,7 +6,7 @@
      -
      +
      @@ -29,9 +29,9 @@
      -
      +
      -
      +
      diff --git a/core/src/plugins/access.fs/i18n/conf/de.php b/core/src/plugins/access.fs/i18n/conf/de.php index 120b9010f8..bd25eb8e8a 100644 --- a/core/src/plugins/access.fs/i18n/conf/de.php +++ b/core/src/plugins/access.fs/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "File System (Standard)" => "Dateisystem (Standard)", @@ -45,4 +45,4 @@ "Delegates all download operations to nginx using the X-Accel-Redirect header. Warning, you have to add some configuration in nginx, like X-Accel-Mapping" => "Überträgt alle Download-Operationen über den X-Accel-Redirect Header an nginx. Warnung: Sie müssen Ihre nginx-Konfiguration erweitern, etwa X-Accel-Mapping", "Zip downloading files on the fly" => "Zip downloading files on the fly", "Directly write the zip file to an output stream which is connected to the user's browser." => "Directly write the zip file to an output stream which is connected to the user's browser.", -); \ No newline at end of file +); diff --git a/core/src/plugins/access.fs/i18n/conf/en.php b/core/src/plugins/access.fs/i18n/conf/en.php index f42d1abdf8..67fde69ce2 100644 --- a/core/src/plugins/access.fs/i18n/conf/en.php +++ b/core/src/plugins/access.fs/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "File System (Standard)" => "File System (Standard)", diff --git a/core/src/plugins/access.fs/i18n/conf/fr.php b/core/src/plugins/access.fs/i18n/conf/fr.php index 6aed623899..d664af968a 100644 --- a/core/src/plugins/access.fs/i18n/conf/fr.php +++ b/core/src/plugins/access.fs/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "File System (Standard)" => "Fichiers locaux (Standard)", @@ -45,4 +45,4 @@ "Delegates all download operations to nginx using the X-Accel-Redirect header. Warning, you have to add some configuration in nginx, like X-Accel-Mapping" => "Déléguer l'ensemble des opérations de téléchargement à nginx en utilisant l'entête X-SendFile. Attention, il faut ajouter une configuration à nginx, par exemple X-Accel-Mapping", "Zip downloading files on the fly" => "Télécharger les zips à la construction", "Directly write the zip file to an output stream which is connected to the user's browser." => "Ecrire directement l'archive Zip dans le flux de sortie, sans passer par un fichier temporaire.", -); \ No newline at end of file +); diff --git a/core/src/plugins/access.fs/i18n/conf/it.php b/core/src/plugins/access.fs/i18n/conf/it.php index 48d530e119..b437c7aba2 100644 --- a/core/src/plugins/access.fs/i18n/conf/it.php +++ b/core/src/plugins/access.fs/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "File System (Standard)" => "File System (Standard)", @@ -45,4 +45,4 @@ "Delegates all download operations to nginx using the X-Accel-Redirect header. Warning, you have to add some configuration in nginx, like X-Accel-Mapping" => "Delega tutte le operazioni di download a nginx, usando l'header X-Accel-Redirect. Attenzione: devi aggiungere manualmente qualche configurazione a nginx, come l'X-Accel Mapping.", "Zip downloading files on the fly" => "Zip downloading files on the fly", "Directly write the zip file to an output stream which is connected to the user's browser." => "Directly write the zip file to an output stream which is connected to the user's browser.", -); \ No newline at end of file +); diff --git a/core/src/plugins/access.fs/i18n/conf/pt.php b/core/src/plugins/access.fs/i18n/conf/pt.php index 7abca83aef..b1f1574b9c 100644 --- a/core/src/plugins/access.fs/i18n/conf/pt.php +++ b/core/src/plugins/access.fs/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "File System (Standard)" => "Sistema de Ficheiros (padrão)", @@ -45,4 +45,4 @@ "Delegates all download operations to nginx using the X-Accel-Redirect header. Warning, you have to add some configuration in nginx, like X-Accel-Mapping" => "Delegates all download operations to nginx using the X-Accel-Redirect header. Warning, you have to add some configuration in nginx, like X-Accel-Mapping", "Zip downloading files on the fly" => "Zip downloading files on the fly", "Directly write the zip file to an output stream which is connected to the user's browser." => "Directly write the zip file to an output stream which is connected to the user's browser.", -); \ No newline at end of file +); diff --git a/core/src/plugins/access.fs/i18n/conf/ru.php b/core/src/plugins/access.fs/i18n/conf/ru.php index c55b056c0e..c76cd9a00e 100644 --- a/core/src/plugins/access.fs/i18n/conf/ru.php +++ b/core/src/plugins/access.fs/i18n/conf/ru.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "File System (Standard)" => "Файловая система (Стандарт)", @@ -45,4 +45,4 @@ "Delegates all download operations to nginx using the X-Accel-Redirect header. Warning, you have to add some configuration in nginx, like X-Accel-Mapping" => "Делегировать все операции скачивания для nginx с использованием заголовка X-Accel-Redirect. Необходима настройка nginx, например X-Accel-Mapping", "Zip downloading files on the fly" => "Zip downloading files on the fly", "Directly write the zip file to an output stream which is connected to the user's browser." => "Directly write the zip file to an output stream which is connected to the user's browser.", -); \ No newline at end of file +); diff --git a/core/src/plugins/access.fs/manifest.xml b/core/src/plugins/access.fs/manifest.xml index 2f02122873..dae6fcea75 100644 --- a/core/src/plugins/access.fs/manifest.xml +++ b/core/src/plugins/access.fs/manifest.xml @@ -6,7 +6,7 @@ - + @@ -14,12 +14,9 @@ - - - - + @@ -39,7 +36,8 @@ + - + diff --git a/core/src/plugins/access.fs/test.fsAccess.php b/core/src/plugins/access.fs/test.fsAccess.php index b8412b02fa..e7aa8a2384 100644 --- a/core/src/plugins/access.fs/test.fsAccess.php +++ b/core/src/plugins/access.fs/test.fsAccess.php @@ -16,11 +16,15 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ -defined('AJXP_EXEC') or die( 'Access not allowed'); +namespace Pydio\Tests; + + +use Pydio\Core\Model\Context; +use Pydio\Core\Utils\TextEncoder; -require_once(AJXP_BIN_FOLDER . '/class.AbstractTest.php'); +defined('AJXP_EXEC') or die( 'Access not allowed'); /** * @package AjaXplorer_Plugins @@ -28,12 +32,15 @@ */ class fsAccessTest extends AbstractTest { + /** + * fsAccessTest constructor. + */ public function __construct() { parent::__construct("Filesystem Plugin", ""); } /** * Test Repository * - * @param Repository $repo + * @param \Pydio\Access\Core\Model\Repository $repo * @return Boolean */ public function doRepositoryTest($repo) @@ -41,17 +48,17 @@ public function doRepositoryTest($repo) if ($repo->accessType != 'fs' ) return -1; // Check the destination path $this->failedInfo = ""; - $safePath = $repo->getOption("PATH", true); - if(strstr($safePath, "AJXP_USER")!==false) return TRUE; // CANNOT TEST THIS CASE! - $path = $repo->getOption("PATH", false); - $createOpt = $repo->getOption("CREATE"); - $create = (($createOpt=="true"||$createOpt===true)?true:false); - if (!$create && !@is_dir($path)) { + $safePath = $repo->getSafeOption("PATH"); + if(strstr($safePath, "AJXP_USER")!==false) { + return TRUE; + } // CANNOT TEST THIS CASE! + $ctx = Context::emptyContext(); + $path = $repo->getContextOption($ctx, "PATH"); + $createOpt = $repo->getContextOption($ctx, "CREATE"); + $create = (($createOpt=="true"||$createOpt===true)?true:false); + if (!$create && !is_dir(TextEncoder::toStorageEncoding($path))) { $this->failedInfo .= "Selected repository path ".$path." doesn't exist, and the CREATE option is false"; return FALSE; - }/* else if ($create && !is_writeable($path)) { - $this->failedInfo .= "Selected repository path ".$path." isn't writeable"; return FALSE; - }*/ - // Do more tests here + } return TRUE; } diff --git a/core/src/plugins/access.ftp/FtpAccessDriver.php b/core/src/plugins/access.ftp/FtpAccessDriver.php new file mode 100644 index 0000000000..db5f1581d2 --- /dev/null +++ b/core/src/plugins/access.ftp/FtpAccessDriver.php @@ -0,0 +1,382 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + * + */ +namespace Pydio\Access\Driver\StreamProvider\FTP; + +use DOMNode; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\UploadedFileInterface; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\RecycleBinManager; +use Pydio\Access\Driver\StreamProvider\FS\FsAccessDriver; +use Pydio\Core\Http\Response\SerializableResponseStream; +use Pydio\Core\Model\ContextInterface; + +use Pydio\Core\Controller\Controller; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Core\Utils\Vars\XMLFilter; +use Pydio\Tasks\Task; +use Pydio\Tasks\TaskService; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Plugin to access a remote server using the File Transfer Protocol + * @package AjaXplorer_Plugins + * @subpackage Access + */ +class FtpAccessDriver extends FsAccessDriver +{ + /** + * Load manifest + * @throws \Exception + */ + public function loadManifest() + { + parent::loadManifest(); + // BACKWARD COMPATIBILITY! + $res = $this->getXPath()->query('//param[@name="USER"] | //param[@name="PASS"] | //user_param[@name="USER"] | //user_param[@name="PASS"]'); + /** @var \DOMElement $node */ + foreach ($res as $node) { + if ($node->getAttribute("name") == "USER") { + $node->setAttribute("name", "FTP_USER"); + } else if ($node->getAttribute("name") == "PASS") { + $node->setAttribute("name", "FTP_PASS"); + } + } + $this->reloadXPath(); + } + + /** + * Parse + * @param ContextInterface $ctx + * @param DOMNode $contribNode + */ + protected function parseSpecificContributions(ContextInterface $ctx, \DOMNode &$contribNode) + { + parent::parseSpecificContributions($ctx, $contribNode); + if($contribNode->nodeName != "actions") return ; + $this->disableArchiveBrowsingContributions($contribNode); + $this->redirectActionsToMethod($contribNode, array("upload", "next_to_remote", "trigger_remote_copy"), "uploadActions"); + } + + /** + * @param ContextInterface $contextInterface + * @throws PydioException + * @throws \Exception + */ + protected function initRepository(ContextInterface $contextInterface) + { + + if (is_array($this->pluginConf)) { + $this->driverConf = $this->pluginConf; + } else { + $this->driverConf = array(); + } + $this->urlBase = $contextInterface->getUrlBase(); + $recycle = $contextInterface->getRepository()->getContextOption($contextInterface, "RECYCLE_BIN"); + if ($recycle != "") { + RecycleBinManager::init($contextInterface->getUrlBase(), "/".$recycle); + } + + } + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @return null + * @throws PydioException + */ + public function uploadActions(ServerRequestInterface &$request, ResponseInterface &$response) + { + /** @var ContextInterface $ctx */ + $ctx = $request->getAttribute("ctx"); + + switch ($request->getAttribute("action")) { + + case "next_to_remote": + $taskId = $request->getAttribute("pydio-task-id"); + if(!$this->hasFilesToCopy($ctx)) { + TaskService::getInstance()->updateTaskStatus($taskId, Task::STATUS_COMPLETE, ""); + break; + } + + $fData = $this->getNextFileToCopy($ctx); + $nextFile = ''; + if ($this->hasFilesToCopy($ctx)) { + $nextFile = $this->getFileNameToCopy($ctx); + } + $this->logDebug("Base64 : ", array("from"=>$fData["destination"], "to"=>base64_decode($fData['destination']))); + $destPath = $ctx->getUrlBase().base64_decode($fData['destination'])."/".$fData['name']; + //$destPath = AJXP_Utils::decodeSecureMagic($destPath); + // DO NOT "SANITIZE", THE URL IS ALREADY IN THE FORM ajxp.ftp://repoId/filename + $destPath = InputFilter::fromPostedFileName($destPath); + $node = new AJXP_Node($destPath); + $this->logDebug("Copying file to server", array("from"=>$fData["tmp_name"], "to"=>$destPath, "name"=>$fData["name"])); + TaskService::getInstance()->updateTaskStatus($taskId, Task::STATUS_RUNNING, "Uploading file ".$fData["name"]); + try { + Controller::applyHook("node.before_create", array(&$node)); + $fp = fopen($destPath, "w"); + $fSource = fopen($fData["tmp_name"], "r"); + while (!feof($fSource)) { + fwrite($fp, fread($fSource, 4096)); + } + fclose($fSource); + $this->logDebug("Closing target : begin ftp copy"); + // Make sur the script does not time out! + @set_time_limit(240); + fclose($fp); + $this->logDebug("FTP Upload : end of ftp copy"); + @unlink($fData["tmp_name"]); + Controller::applyHook("node.change", array(null, &$node)); + + } catch (\Exception $e) { + TaskService::getInstance()->updateTaskStatus($taskId, Task::STATUS_FAILED, ""); + $this->logDebug("Error during ftp copy", array($e->getMessage(), $e->getTrace())); + } + $this->logDebug("FTP Upload : shoud trigger next or reload nextFile=$nextFile"); + $x = new SerializableResponseStream(); + $response = $response->withBody($x); + if ($nextFile!='') { + //$x->addChunk(new BgActionTrigger("next_to_remote", array(), "Copying file ".TextEncoder::toUTF8($nextFile)." to remote server")); + $newTask = TaskService::actionAsTask($ctx, "next_to_remote", []); + $newTask->setLabel("Copying file " . $nextFile . " to remote server"); + $response = TaskService::getInstance()->enqueueTask($newTask, $request, $response); + } else { + //$x->addChunk(new BgActionTrigger("reload_node", array(), "Upload done, reloading client.")); + TaskService::getInstance()->updateTaskStatus($taskId, Task::STATUS_COMPLETE, ""); + } + break; + + case "upload": + $httpVars = $request->getParsedBody(); + $destinationFolder = InputFilter::securePath("/" . $httpVars['dir']); + + /** @var UploadedFileInterface[] $uploadedFiles */ + $uploadedFiles = $request->getUploadedFiles(); + if(!count($uploadedFiles)){ + $this->writeUploadError($request, "Could not find any uploaded file", 411); + } + foreach ($uploadedFiles as $parameterName => $uploadedFile) { + + if (substr($parameterName, 0, 9) != "userfile_") continue; + try { + + $this->logDebug("Upload : rep_source ", array($destinationFolder)); + $err = InputFilter::parseFileDataErrors($uploadedFile, true); + if ($err != null) { + $errorCode = $err[0]; + $errorMessage = $err[1]; + throw new \Exception($errorMessage, $errorCode); + } + $fileName = $uploadedFile->getClientFilename(); + + if (isSet($httpVars["auto_rename"])) { + $destination = $ctx->getUrlBase() . $destinationFolder; + $fileName = FsAccessDriver::autoRenameForDest($destination, $fileName); + } + $boxData = [ + "name" => $fileName, + "destination" => base64_encode($destinationFolder) + ]; + + $destCopy = XMLFilter::resolveKeywords($ctx->getRepository()->getContextOption($ctx, "TMP_UPLOAD")); + $this->logDebug("Upload : tmp upload folder", array($destCopy)); + if (!is_dir($destCopy)) { + if (!@mkdir($destCopy)) { + $this->logDebug("Upload error : cannot create temporary folder", array($destCopy)); + throw new PydioException("Warning, cannot create folder for temporary copy", false, 413); + } + } + if (!is_writable($destCopy)) { + $this->logDebug("Upload error: cannot write into temporary folder"); + throw new PydioException("Warning, cannot write into temporary folder", false, 414); + break; + } + $this->logDebug("Upload : tmp upload folder", array($destCopy)); + + $mess = LocaleService::getMessages(); + $destName = tempnam($destCopy, ""); + $boxData["tmp_name"] = $destName; + $this->copyUploadedData($uploadedFile, $destName, $mess); + $this->storeFileToCopy($ctx, $boxData); + $this->writeUploadSuccess($request, ["PREVENT_NOTIF" => true]); + + $task = TaskService::actionAsTask($ctx, "next_to_remote", []); + $task->setLabel("Copying file to remote server"); + TaskService::getInstance()->enqueueTask($task, $request, $response); + + } catch (\Exception $e) { + $errorCode = $e->getCode(); + if(empty($errorCode)) $errorCode = 411; + $this->writeUploadError($request, $e->getMessage(), $errorCode); + } + } + break; + + default: + break; + } + return null; + } + + /** + * Specific isWriteable implementation + * @param AJXP_Node $node + * @return bool + */ + public function isWriteable(AJXP_Node $node) + { + if ($node->isRoot()) { // ROOT, WE ARE NOT SURE TO BE ABLE TO READ THE PARENT + return true; + } else { + return is_writable($node->getUrl()); + } + + } + + /** + * Recursive del dir + * @param string $location + * @param array $repoData + * @throws \Exception + */ + public function deldir($location, $repoData, $taskId = NULL) + { + if (is_dir($location)) { + $dirsToRecurse = array(); + $all=opendir($location); + while ($file=readdir($all)) { + if (is_dir("$location/$file") && $file !=".." && $file!=".") { + $dirsToRecurse[] = "$location/$file"; + } elseif (!is_dir("$location/$file")) { + if (file_exists("$location/$file")) { + unlink("$location/$file"); + } + unset($file); + } + } + closedir($all); + foreach ($dirsToRecurse as $recurse) { + $this->deldir($recurse, $repoData); + } + rmdir($location); + } else { + if (file_exists("$location")) { + $test = @unlink("$location"); + if(!$test) throw new \Exception("Cannot delete file ".$location); + } + } + if (isSet($repoData["recycle"]) && basename(dirname($location)) == $repoData["recycle"]) { + // DELETING FROM RECYCLE + RecycleBinManager::deleteFromRecycle($location); + } + } + + + /** + * @param ContextInterface $ctx + * @param $fileData + */ + public function storeFileToCopy(ContextInterface $ctx, $fileData) + { + $user = $ctx->getUser(); + $files = $user->getTemporaryData("tmp_upload"); + $this->logDebug("Saving user temporary data", array($fileData)); + $files[] = $fileData; + $user->saveTemporaryData("tmp_upload", $files); + } + + /** + * @param ContextInterface $ctx + * @return mixed + */ + public function getFileNameToCopy(ContextInterface $ctx) + { + $user = $ctx->getUser(); + $files = $user->getTemporaryData("tmp_upload"); + return $files[0]["name"]; + } + + /** + * @param ContextInterface $ctx + * @return string + */ + public function getNextFileToCopy(ContextInterface $ctx) + { + if(!$this->hasFilesToCopy($ctx)) return ""; + $user = $ctx->getUser(); + $files = $user->getTemporaryData("tmp_upload"); + $fData = $files[0]; + array_shift($files); + $user->saveTemporaryData("tmp_upload", $files); + return $fData; + } + + /** + * @param ContextInterface $ctx + * @return bool + */ + public function hasFilesToCopy(ContextInterface $ctx) + { + $user = $ctx->getUser(); + $files = $user->getTemporaryData("tmp_upload"); + return (count($files)?true:false); + } + + /** + * @param $params + * @return string + * @throws PydioException + */ + public function testParameters($params) + { + if (empty($params["FTP_USER"])) { + throw new PydioException("Even if you intend to use the credentials stored in the session, temporarily set a user and password to perform the connexion test."); + } + if ($params["FTP_SECURE"]) { + $link = @ftp_ssl_connect($params["FTP_HOST"], $params["FTP_PORT"]); + } else { + $link = @ftp_connect($params["FTP_HOST"], $params["FTP_PORT"]); + } + if (!$link) { + throw new PydioException("Cannot connect to FTP server (".$params["FTP_HOST"].",". $params["FTP_PORT"].")"); + } + @ftp_set_option($link, FTP_TIMEOUT_SEC, 10); + if (!@ftp_login($link,$params["FTP_USER"],$params["FTP_PASS"])) { + ftp_close($link); + throw new PydioException("Cannot login to FTP server with user ".$params["FTP_USER"]); + } + if (!$params["FTP_DIRECT"]) { + @ftp_pasv($link, true); + global $_SESSION; + $_SESSION["ftpPasv"]="true"; + } + ftp_close($link); + + return "SUCCESS: Could succesfully connect to the FTP server with user '".$params["FTP_USER"]."'."; + } + + +} diff --git a/core/src/plugins/access.ftp/FtpAccessWrapper.php b/core/src/plugins/access.ftp/FtpAccessWrapper.php new file mode 100644 index 0000000000..1bd2f2160b --- /dev/null +++ b/core/src/plugins/access.ftp/FtpAccessWrapper.php @@ -0,0 +1,794 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + * + */ +namespace Pydio\Access\Driver\StreamProvider\FTP; + +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Access\Core\IAjxpWrapper; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Auth\Core\MemorySafe; +use Pydio\Core\Model\ContextInterface; + + +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Services\SessionService; +use Pydio\Core\Services\ApplicationState; +use Pydio\Core\Utils\FileHelper; +use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Core\Utils\Vars\PathUtils; +use Pydio\Core\Utils\Vars\UrlUtils; + +use Pydio\Log\Core\Logger; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + + +/** + * Wrapper for encapsulation FTP accesses + * @package AjaXplorer_Plugins + * @subpackage Access + */ +class FtpAccessWrapper implements IAjxpWrapper +{ + // Instance vars $this-> + protected $host; + protected $port; + protected $secure; + protected $path; + protected $user; + protected $password; + protected $ftpActive; + protected $repoCharset; + protected $repositoryId; + protected $fp; + + protected $crtMode; + protected $crtLink; + protected $crtTarget; + + // Shared vars self:: + private static $dirContentLoopPath = array(); + private static $dirContent = array(); + private static $dirContentKeys = array(); + private static $dirContentIndex = array(); + + private $monthes = array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Juil", "Aug", "Sep", "Oct", "Nov", "Dec"); + + /** + * @param string $path + * @param bool $persistent + * @return string + */ + public static function getRealFSReference($path, $persistent = false) + { + $tmpFile = ApplicationState::getAjxpTmpDir() ."/".md5(time()); + $tmpHandle = fopen($tmpFile, "wb"); + self::copyFileInStream($path, $tmpHandle); + fclose($tmpHandle); + if (!$persistent) { + register_shutdown_function(function() use($tmpFile){ + FileHelper::silentUnlink($tmpFile); + }); + } + return $tmpFile; + } + + /** + * @return bool + */ + public static function isRemote() + { + return true; + } + + /** + * @param String $url + * @return bool + */ + public static function isSeekable($url) + { + return true; + } + + /** + * @param string $path + * @param resource $stream + * @throws PydioException + * @throws \Exception + */ + public static function copyFileInStream($path, $stream) + { + $fake = new FtpAccessWrapper(); + $parts = $fake->parseUrl($path); + $link = $fake->createFTPLink(); + $serverPath = InputFilter::securePath($fake->path . "/" . $parts["path"]); + Logger::debug($serverPath); + ftp_fget($link, $stream, $serverPath, FTP_BINARY); + } + + /** + * @param string $path + * @param number $chmodValue + * @throws PydioException + * @throws \Exception + */ + public static function changeMode($path, $chmodValue) + { + $fake = new FtpAccessWrapper(); + $parts = $fake->parseUrl($path); + $link = $fake->createFTPLink(); + $serverPath = InputFilter::securePath($fake->path . "/" . $parts["path"]); + ftp_chmod($link, $chmodValue, $serverPath); + } + + /** + * @param string $url + * @param string $mode + * @param int $options + * @param string $context + * @return bool + * @throws PydioException + * @throws \Exception + */ + public function stream_open($url, $mode, $options, &$context) + { + if (stripos($mode, "w") !== false) { + $this->crtMode = 'write'; + $parts = $this->parseUrl($url); + $this->crtTarget = InputFilter::securePath($this->path . "/" . $parts["path"]); + $this->crtLink = $this->createFTPLink(); + $this->fp = tmpfile(); + } else { + $this->crtMode = 'read'; + $this->fp = tmpfile(); + $this->copyFileInStream($url, $this->fp); + rewind($this->fp); + } + /* + if ($context) { + $this->fp = @fopen($this->buildRealUrl($url), $mode, $options, $context); + } else { + $this->fp = @fopen($this->buildRealUrl($url), $mode); + } + */ + return ($this->fp !== false); + } + + /** + * @return array + */ + public function stream_stat() + { + return fstat($this->fp); + } + + /** + * @param int $offset + * @param int $whence + */ + public function stream_seek($offset , $whence = SEEK_SET) + { + fseek($this->fp, $offset, SEEK_SET); + } + + /** + * @return int + */ + public function stream_tell() + { + return ftell($this->fp); + } + + /** + * @param int $count + * @return string + */ + public function stream_read($count) + { + return fread($this->fp, $count); + } + + /** + * @param string $data + * @return int + */ + public function stream_write($data) + { + fwrite($this->fp, $data, strlen($data)); + return strlen($data); + } + + /** + * @return bool + */ + public function stream_eof() + { + return feof($this->fp); + } + + public function stream_close() + { + if (isSet($this->fp) && $this->fp!=-1 && $this->fp!==false) { + fclose($this->fp); + } + } + + // PHP bug #62035 + /** + * @param $errno + * @param $errstr + * @param $errfile + * @param $errline + * @param $errcontext + * @throws PydioException + */ + public static function fput_quota_hack($errno, $errstr, $errfile, $errline, $errcontext) + { + if (strpos($errstr, "Opening BINARY mode data connection") !== false) + $errstr = "Transfer failed. Please check available disk space (quota)"; + throw new PydioException("$errno - $errstr"); + } + + /** + * + */ + public function stream_flush() + { + if (isSet($this->fp) && $this->fp!=-1 && $this->fp!==false) { + if ($this->crtMode == 'write') { + rewind($this->fp); + Logger::debug(__CLASS__,__FUNCTION__,"Ftp_fput", array("target"=>$this->crtTarget)); + set_error_handler(array("\\Pydio\\Access\\Driver\\StreamProvider\\FTP\\FtpAccessWrapper", "fput_quota_hack"), E_ALL & ~E_NOTICE ); + ftp_fput($this->crtLink, $this->crtTarget, $this->fp, FTP_BINARY); + restore_error_handler(); + Logger::debug(__CLASS__,__FUNCTION__,"Ftp_fput end", array("target"=>$this->crtTarget)); + } else { + fflush($this->fp); + } + } + } + + /** + * @param string $url + * @return bool + */ + public function unlink($url) + { + return unlink($this->buildRealUrl($url)); + } + + /** + * @param string $url + * @param int $options + * @return bool + */ + public function rmdir($url, $options) + { + return rmdir($this->buildRealUrl($url)); + } + + /** + * @param string $url + * @param int $mode + * @param int $options + * @return bool + */ + public function mkdir($url, $mode, $options) + { + return mkdir($this->buildRealUrl($url), $mode); + } + + /** + * @param string $from + * @param string $to + * @return bool + */ + public function rename($from, $to) + { + return rename($this->buildRealUrl($from), $this->buildRealUrl($to)); + } + + /** + * @param string $path + * @param int $flags + * @return array|mixed|null + * @throws PydioException + * @throws \Exception + */ + public function url_stat($path, $flags) + { + // We are in an opendir loop + Logger::debug(__CLASS__,__FUNCTION__,"URL_STAT", $path); + $node = new AJXP_Node($path); + $testLoopPath = PathUtils::forwardSlashDirname($path); + if (is_array(self::$dirContent[$testLoopPath])) { + $search = PathUtils::forwardSlashBasename($path); + //if($search == "") $search = "."; + if (array_key_exists($search, self::$dirContent[$testLoopPath])) { + return self::$dirContent[$testLoopPath][$search]; + } + } + $parts = $this->parseUrl($path); + $link = $this->createFTPLink(); + $serverPath = InputFilter::securePath($this->path . "/" . $parts["path"]); + if(empty($parts["path"])) $parts["path"] = "/"; + if ($parts["path"] == "/") { + $basename = "."; + } else { + $basename = PathUtils::forwardSlashBasename($serverPath); + } + + $serverParent = PathUtils::forwardSlashDirname($parts["path"]); + $serverParent = InputFilter::securePath($this->path . "/" . $serverParent); + + $testCd = @ftp_chdir($link, $serverPath); + if ($testCd === true) { + // DIR + $contents = $this->rawList($link, $serverParent, 'd'); + foreach ($contents as $entry) { + $res = $this->rawListEntryToStat($entry); + Logger::debug(__CLASS__,__FUNCTION__,"RAWLISTENTRY ".$res["name"]. " (searching ".$basename.")", $res["stat"]); + if ($res["name"] == $basename) { + AbstractAccessDriver::fixPermissions($node, $res["stat"], array($this, "getRemoteUserId")); + $statValue = $res["stat"]; + return $statValue; + } + } + // Not found : is it the "." + if($basename == "."){ + // Make at least a readable fake stat + $fakeStat = stat(AJXP_DATA_PATH); + return $fakeStat; + } + } else { + // FILE + $contents = $this->rawList($link, $serverPath, 'f'); + if (count($contents) == 1) { + $res = $this->rawListEntryToStat($contents[0]); + AbstractAccessDriver::fixPermissions($node, $res["stat"], array($this, "getRemoteUserId")); + $statValue = $res["stat"]; + Logger::debug(__CLASS__,__FUNCTION__,"STAT FILE $serverPath", $statValue); + return $statValue; + } + } + return null; + } + + /** + * @param string $url + * @param int $options + * @return bool + * @throws PydioException + * @throws \Exception + */ + public function dir_opendir ($url , $options ) + { + if(isSet(self::$dirContent[$url])){ + array_push(self::$dirContentLoopPath, $url); + return true; + } + $parts = $this->parseUrl($url); + $link = $this->createFTPLink(); + $serverPath = InputFilter::securePath($this->path . "/" . $parts["path"]); + $contents = $this->rawList($link, $serverPath); + $folders = $files = array(); + foreach ($contents as $entry) { + $result = $this->rawListEntryToStat($entry); + AbstractAccessDriver::fixPermissions(new AJXP_Node($url), $result["stat"], array($this, "getRemoteUserId")); + $isDir = $result["dir"]; + $statValue = $result["stat"]; + $file = $result["name"]; + if ($isDir) { + $folders[$file] = $statValue; + } else { + $files[$file] = $statValue; + } + } + // Append all files keys to folders. Do not use array_merge. + foreach ($files as $key => $value) { + $folders[$key] = $value; + } + Logger::debug(__CLASS__, __FUNCTION__, "OPENDIR ", $folders); + array_push(self::$dirContentLoopPath, $url); + self::$dirContent[$url] = $folders; + self::$dirContentKeys[$url] = array_keys($folders); + self::$dirContentIndex[$url] = 0; + return true; + } + + /** + * + */ + public function dir_closedir () + { + Logger::debug(__CLASS__,__FUNCTION__,"CLOSEDIR"); + $loopPath = array_pop(self::$dirContentLoopPath); + self::$dirContentIndex[$loopPath] = 0; + //Make a simple rewind, keep in cache + //self::$dirContent[$loopPath] = null; + //self::$dirContentKeys[$loopPath] = null; + } + + /** + * @return bool + */ + public function dir_readdir () + { + $loopPath = self::$dirContentLoopPath[count(self::$dirContentLoopPath)-1]; + $index = self::$dirContentIndex[$loopPath]; + self::$dirContentIndex[$loopPath] ++; + if (isSet(self::$dirContentKeys[$loopPath][$index])) { + return self::$dirContentKeys[$loopPath][$index]; + } else { + return false; + } + } + + /** + * + */ + public function dir_rewinddir () + { + $loopPath = self::$dirContentLoopPath[count(self::$dirContentLoopPath)-1]; + self::$dirContentIndex[$loopPath] = 0; + } + + /** + * @param $link + * @param $serverPath + * @param string $target + * @param bool $retry + * @return array + */ + protected function rawList($link, $serverPath, $target = 'd', $retry = true) + { + if ($target == 'f') { + $parentDir = PathUtils::forwardSlashDirname($serverPath); + $fileName = PathUtils::forwardSlashBasename($serverPath); + ftp_chdir($link, $parentDir); + $rl_dirlist = @ftp_rawlist($link, "-a ."); + //AJXP_Logger::debug(__CLASS__,__FUNCTION__,"FILE RAWLIST FROM ".$parentDir); + if (is_array($rl_dirlist)) { + $escaped = preg_quote($fileName); + foreach ($rl_dirlist as $rl_index => $rl_entry) { + if (preg_match("/ $escaped$/" , $rl_entry)) { + $contents = array($rl_dirlist[$rl_index]); + } + } + } + } else { + ftp_chdir($link, $serverPath); + $contents = ftp_rawlist($link, "-a ."); + //AJXP_Logger::debug(__CLASS__,__FUNCTION__,"RAW LIST RESULT ".print_r($contents, true)); + } + + if (!is_array($contents) && !$this->ftpActive) { + if ($retry == false) { + return array(); + } + // We might have timed out, so let's go passive if not done yet + global $_SESSION; + if ($_SESSION["ftpPasv"] == "true") { + return array(); + } + @ftp_pasv($link, TRUE); + $_SESSION["ftpPasv"]="true"; + // RETRY! + return $this->rawList($link, $serverPath, $target, FALSE); + } + if (!is_array($contents)) { + return array(); + } + return $contents; + } + + /** + * @param $entry + * @param bool $filterStatPerms + * @return array + */ + protected function rawListEntryToStat($entry, $filterStatPerms = false) + { + $info = array(); + $monthes = array_flip( $this->monthes ); + $vinfo = preg_split("/[\s]+/", $entry); + Logger::debug(__CLASS__,__FUNCTION__,"RAW LIST", $entry); + $statValue = array(); + $fileperms = ''; + if ($vinfo[0] !== "total") { + $fileperms = $vinfo[0]; + $info['num'] = $vinfo[1]; + $info['owner'] = $vinfo[2]; + $info['groups'] = array(); + $i = 3; + while (true) { + $info['groups'][] = $vinfo[$i]; + $i++; + // Detect "Size" and "Month" + if(is_numeric($vinfo[$i]) && !is_numeric($vinfo[$i+1])) break; + } + $info['group'] = implode(" ", $info["groups"]); + $info['size'] = $vinfo[$i]; $i++; + $info['month'] = $vinfo[$i]; $i++; + $info['day'] = $vinfo[$i]; $i++; + $info['timeOrYear'] = $vinfo[$i]; $i++; + //$info['name'] = $vinfo[$i]; $i++; + } + $resplit = preg_split("/[\s]+/", $entry, 8 + count($info["groups"])); + $file = trim(array_pop($resplit)); + $statValue[7] = $statValue["size"] = trim($info['size']); + if (strstr($info["timeOrYear"], ":")) { + $info["time"] = $info["timeOrYear"]; + $monthKey = $monthes[$info['month']] + 1; + if (intval(date('m')) < $monthKey) { + $info['year'] = date("Y") -1; + } else { + $info["year"] = date("Y"); + } + } else { + $info["time"] = '09:00'; + $info["year"] = $info["timeOrYear"]; + } + $statValue[4] = $statValue["uid"] = $info["owner"]; + $statValue[5] = $statValue["gid"] = $info["group"]; + $filedate = trim($info['day'])." ".trim($info['month'])." ".trim($info['year'])." ".trim($info['time']); + $statValue[9] = $statValue["mtime"] = strtotime($filedate); + + $isDir = false; + if (strpos($fileperms,"d")!==FALSE || strpos($fileperms,"l")!==FALSE) { + if (strpos($fileperms,"l")!==FALSE) { + $test=explode(" ->", $file); + $file=$test[0]; + } + $isDir = true; + } + $boolIsDir = $isDir; + $statValue[2] = $statValue["mode"] = $this->convertingChmod($fileperms); + $statValue["ftp_perms"] = $fileperms; + return array("name"=>$file, "stat"=>$statValue, "dir"=>$isDir); + } + + /** + * @param $url + * @param bool $forceLogin + * @return array + * @throws PydioException + * @throws \Exception + */ + protected function parseUrl($url, $forceLogin = false) + { + // URL MAY BE ajxp.ftp://username:password@host/path + $urlParts = UrlUtils::safeParseUrl($url); + $node = new AJXP_Node($url); + $this->repositoryId = $node->getRepositoryId(); + $repository = $node->getRepository(); + if ($repository == null) { + throw new \Exception("Cannot find repository for dynamic ftp authentication."); + } + $ctx = $node->getContext(); + $credentials = MemorySafe::tryLoadingCredentialsFromSources($node->getContext()); + $this->user = $credentials["user"]; + $this->password = $credentials["password"]; + if ($this->user=="") { + throw new PydioException("Cannot find user/pass for FTP access!"); + } + if ($repository->getContextOption($node->getContext(), "DYNAMIC_FTP") == "TRUE" && isSet($_SESSION["AJXP_DYNAMIC_FTP_DATA"])) { + $data = $_SESSION["AJXP_DYNAMIC_FTP_DATA"]; + $this->host = $data["FTP_HOST"]; + $this->path = $data["PATH"]; + $this->secure = ($data["FTP_SECURE"] == "TRUE"?true:false); + $this->port = ($data["FTP_PORT"]!=""?intval($data["FTP_PORT"]):($this->secure?22:21)); + $this->ftpActive = ($data["FTP_DIRECT"] == "TRUE"?true:false); + $this->repoCharset = $data["CHARSET"]; + } else { + $this->host = $ctx->getRepository()->getContextOption($ctx, "FTP_HOST"); + $this->path = $ctx->getRepository()->getContextOption($ctx, "PATH"); + $this->secure = ($ctx->getRepository()->getContextOption($ctx, "FTP_SECURE") == "TRUE"?true:false); + $this->port = ($ctx->getRepository()->getContextOption($ctx, "FTP_PORT")!=null?intval($ctx->getRepository()->getContextOption($ctx, "FTP_PORT")):($this->secure?22:21)); + $this->ftpActive = ($ctx->getRepository()->getContextOption($ctx, "FTP_DIRECT") == "TRUE"?true:false); + $this->repoCharset = $ctx->getRepository()->getContextOption($ctx, "CHARSET") OR ""; + } + + // Test Connexion and server features + global $_SESSION; + $cacheKey = $repository->getId()."_ftpCharset"; + if (!isset($_SESSION[$cacheKey]) || !strlen($_SESSION[$cacheKey]) || $forceLogin) { + $features = $this->getServerFeatures($node->getContext()); + $ctxCharset = SessionService::getContextCharset($node->getRepositoryId()); + if(empty($ctxCharset)) { + SessionService::setContextCharset($node->getRepositoryId(), $features["charset"]); + $_SESSION[$cacheKey] = $features["charset"]; + }else{ + $_SESSION[$cacheKey] = $ctxCharset; + } + } + return $urlParts; + } + + /** + * @param AJXP_Node $node + * @return array + */ + public static function getResolvedOptionsForNode($node){ + return ["TYPE" => "php"]; + } + + + + /** + * @param AJXP_Node $node + * @return array Array(UID, GID) to be used to compute permission + */ + public function getRemoteUserId($node) + { + $repoUid = $node->getRepository()->getContextOption($node->getContext(), "UID"); + if (!empty($repoUid)) { + return array($repoUid, "-1"); + } + return array($this->user, "-1"); + } + + /** + * @param $url + * @return string + * @throws PydioException + * @throws \Exception + */ + protected function buildRealUrl($url) + { + if (!isSet($this->user)) { + $parts = $this->parseUrl($url); + } else { + // parseUrl already called before (rename case). + $parts = UrlUtils::safeParseUrl($url); + } + $serverPath = InputFilter::securePath("/$this->path/" . $parts["path"]); + if($this->secure){ + $protocol = 'ftps'; + } + else{ + $protocol = 'ftp'; + } + $url = $protocol.'://'.urlencode($this->user).':'.urlencode($this->password).'@'.$this->host.':'.$this->port.$serverPath; + return $url; + } + + /** + * This method retrieves the FTP server features as described in RFC2389 + * A decent FTP server support MLST command to list file using UTF-8 encoding + * @param ContextInterface $ctx + * @return array of features (see code) + * @throws PydioException + * @throws \Exception + */ + protected function getServerFeatures(ContextInterface $ctx) + { + $link = $this->createFTPLink(); + + if ($ctx->getRepository()->getContextOption($ctx, "CREATE") == true) { + // Test if root exists and create it otherwise + $serverPath = InputFilter::securePath($this->path . "/"); + $testCd = @ftp_chdir($link, $serverPath); + if ($testCd !== true) { + $res = @ftp_mkdir($link, $serverPath); + if(!$res) throw new \Exception("Cannot create path on remote server!"); + } + } + + $features = @ftp_raw($link, "FEAT"); + // Check the answer code + if (isSet($features[0]) && $features[0][0] != "2") { + //ftp_close($link); + return array("list"=>"LIST", "charset"=>$this->repoCharset); + } + $retArray = array("list"=>"LIST", "charset"=>$this->repoCharset); + // Ok, find out the encoding used + foreach ($features as $feature) { + if (strstr($feature, "UTF8") !== FALSE) { // See http://wiki.filezilla-project.org/Character_Set for an explaination + @ftp_raw($link, "OPTS UTF-8 ON"); + $retArray['charset'] = "UTF-8"; + //ftp_close($link); + return $retArray; + } + } + // In the future version, we should also use MLST as it standardize the listing format + return $retArray; + } + + /** + * @return bool|resource + * @throws PydioException + */ + protected function createFTPLink() + { + // If connexion exist and is still connected + if(is_array($_SESSION["FTP_CONNEXIONS"]) + && array_key_exists($this->repositoryId, $_SESSION["FTP_CONNEXIONS"]) + && @ftp_systype($_SESSION["FTP_CONNEXIONS"][$this->repositoryId])){ + Logger::debug(__CLASS__,__FUNCTION__,"Using stored FTP Session"); + return $_SESSION["FTP_CONNEXIONS"][$this->repositoryId]; + } + Logger::debug(__CLASS__,__FUNCTION__,"Creating new FTP Session"); + $link = FALSE; + //Connects to the FTP. + if ($this->secure) { + $link = @ftp_ssl_connect($this->host, $this->port); + } else { + $link = @ftp_connect($this->host, $this->port); + } + if (!$link) { + throw new PydioException("Cannot connect to FTP server ($this->host, $this->port)"); + } + //register_shutdown_function('ftp_close', $link); + @ftp_set_option($link, FTP_TIMEOUT_SEC, 10); + if (!@ftp_login($link,$this->user,$this->password)) { + throw new PydioException("Cannot login to FTP server with user $this->user"); + } + if (!$this->ftpActive) { + @ftp_pasv($link, true); + global $_SESSION; + $_SESSION["ftpPasv"]="true"; + } + if (!is_array($_SESSION["FTP_CONNEXIONS"])) { + $_SESSION["FTP_CONNEXIONS"] = array(); + } + $_SESSION["FTP_CONNEXIONS"][$this->repositoryId] = $link; + return $link; + } + + /** + * @param $permissions + * @param bool $filterForStat + * @return int + */ + protected function convertingChmod($permissions, $filterForStat = false) + { + $mode = 0; + + if ($permissions[1] == 'r') $mode += 0400; + if ($permissions[2] == 'w') $mode += 0200; + if ($permissions[3] == 'x') $mode += 0100; + else if ($permissions[3] == 's') $mode += 04100; + else if ($permissions[3] == 'S') $mode += 04000; + + if ($permissions[4] == 'r') $mode += 040; + if ($permissions[5] == 'w' || ($filterForStat && $permissions[2] == 'w')) $mode += 020; + if ($permissions[6] == 'x' || ($filterForStat && $permissions[3] == 'x')) $mode += 010; + else if ($permissions[6] == 's') $mode += 02010; + else if ($permissions[6] == 'S') $mode += 02000; + + if ($permissions[7] == 'r') $mode += 04; + if ($permissions[8] == 'w' || ($filterForStat && $permissions[2] == 'w')) $mode += 02; + if ($permissions[9] == 'x' || ($filterForStat && $permissions[3] == 'x')) $mode += 01; + else if ($permissions[9] == 't') $mode += 01001; + else if ($permissions[9] == 'T') $mode += 01000; + + if ($permissions[0] != "d") { + $mode += 0100000; + } else { + $mode += 0040000; + } + + $mode = (string) ("0".$mode); + return $mode; + } + +} diff --git a/core/src/plugins/access.ftp/class.ftpAccessDriver.php b/core/src/plugins/access.ftp/class.ftpAccessDriver.php deleted file mode 100644 index a1c24fc004..0000000000 --- a/core/src/plugins/access.ftp/class.ftpAccessDriver.php +++ /dev/null @@ -1,328 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - * - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * AJXP_Plugin to access a remote server using the File Transfer Protocol - * @package AjaXplorer_Plugins - * @subpackage Access - */ -class ftpAccessDriver extends fsAccessDriver -{ - public function loadManifest() - { - parent::loadManifest(); - // BACKWARD COMPATIBILITY! - $res = $this->getXPath()->query('//param[@name="USER"] | //param[@name="PASS"] | //user_param[@name="USER"] | //user_param[@name="PASS"]'); - foreach ($res as $node) { - if ($node->getAttribute("name") == "USER") { - $node->setAttribute("name", "FTP_USER"); - } else if ($node->getAttribute("name") == "PASS") { - $node->setAttribute("name", "FTP_PASS"); - } - } - $this->reloadXPath(); - } - - /** - * Parse - * @param DOMNode $contribNode - */ - protected function parseSpecificContributions(&$contribNode) - { - parent::parseSpecificContributions($contribNode); - if($contribNode->nodeName != "actions") return ; - $this->disableArchiveBrowsingContributions($contribNode); - $this->redirectActionsToMethod($contribNode, array("upload", "next_to_remote", "trigger_remote_copy"), "uploadActions"); - } - - public function initRepository() - { - if (is_array($this->pluginConf)) { - $this->driverConf = $this->pluginConf; - } else { - $this->driverConf = array(); - } - $this->detectStreamWrapper(true); - $this->urlBase = "pydio://".$this->repository->getId(); - $recycle = $this->repository->getOption("RECYCLE_BIN"); - if ($recycle != "") { - RecycleBinManager::init($this->urlBase, "/".$recycle); - } - //AJXP_PromptException::testOrPromptForCredentials("ftp_ws_credentials", $this->repository->getId()); - } - - public function uploadActions($action, $httpVars, $filesVars) - { - switch ($action) { - case "trigger_remote_copy": - if(!$this->hasFilesToCopy()) break; - $toCopy = $this->getFileNameToCopy(); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::triggerBgAction("next_to_remote", array(), "Copying file ".$toCopy." to ftp server"); - AJXP_XMLWriter::close(); - exit(1); - break; - case "next_to_remote": - if(!$this->hasFilesToCopy()) break; - $fData = $this->getNextFileToCopy(); - $nextFile = ''; - if ($this->hasFilesToCopy()) { - $nextFile = $this->getFileNameToCopy(); - } - $this->logDebug("Base64 : ", array("from"=>$fData["destination"], "to"=>base64_decode($fData['destination']))); - $destPath = $this->urlBase.base64_decode($fData['destination'])."/".$fData['name']; - //$destPath = AJXP_Utils::decodeSecureMagic($destPath); - // DO NOT "SANITIZE", THE URL IS ALREADY IN THE FORM ajxp.ftp://repoId/filename - $destPath = SystemTextEncoding::fromPostedFileName($destPath); - $node = new AJXP_Node($destPath); - $this->logDebug("Copying file to server", array("from"=>$fData["tmp_name"], "to"=>$destPath, "name"=>$fData["name"])); - try { - AJXP_Controller::applyHook("node.before_create", array(&$node)); - $fp = fopen($destPath, "w"); - $fSource = fopen($fData["tmp_name"], "r"); - while (!feof($fSource)) { - fwrite($fp, fread($fSource, 4096)); - } - fclose($fSource); - $this->logDebug("Closing target : begin ftp copy"); - // Make sur the script does not time out! - @set_time_limit(240); - fclose($fp); - $this->logDebug("FTP Upload : end of ftp copy"); - @unlink($fData["tmp_name"]); - AJXP_Controller::applyHook("node.change", array(null, &$node)); - - } catch (Exception $e) { - $this->logDebug("Error during ftp copy", array($e->getMessage(), $e->getTrace())); - } - $this->logDebug("FTP Upload : shoud trigger next or reload nextFile=$nextFile"); - AJXP_XMLWriter::header(); - if ($nextFile!='') { - AJXP_XMLWriter::triggerBgAction("next_to_remote", array(), "Copying file ".SystemTextEncoding::toUTF8($nextFile)." to remote server"); - } else { - AJXP_XMLWriter::triggerBgAction("reload_node", array(), "Upload done, reloading client."); - } - AJXP_XMLWriter::close(); - exit(1); - break; - case "upload": - $rep_source = AJXP_Utils::securePath("/".$httpVars['dir']); - $this->logDebug("Upload : rep_source ", array($rep_source)); - $logMessage = ""; - foreach ($filesVars as $boxName => $boxData) { - if(substr($boxName, 0, 9) != "userfile_") continue; - $this->logDebug("Upload : rep_source ", array($rep_source)); - $err = AJXP_Utils::parseFileDataErrors($boxData); - if ($err != null) { - $errorCode = $err[0]; - $errorMessage = $err[1]; - break; - } - if (isSet($httpVars["auto_rename"])) { - $destination = $this->urlBase.$rep_source; - $boxData["name"] = fsAccessDriver::autoRenameForDest($destination, $boxData["name"]); - } - $boxData["destination"] = base64_encode($rep_source); - $destCopy = AJXP_XMLWriter::replaceAjxpXmlKeywords($this->repository->getOption("TMP_UPLOAD")); - $this->logDebug("Upload : tmp upload folder", array($destCopy)); - if (!is_dir($destCopy)) { - if (! @mkdir($destCopy)) { - $this->logDebug("Upload error : cannot create temporary folder", array($destCopy)); - $errorCode = 413; - $errorMessage = "Warning, cannot create folder for temporary copy."; - break; - } - } - if (!$this->isWriteable($destCopy)) { - $this->logDebug("Upload error: cannot write into temporary folder"); - $errorCode = 414; - $errorMessage = "Warning, cannot write into temporary folder."; - break; - } - $this->logDebug("Upload : tmp upload folder", array($destCopy)); - if (isSet($boxData["input_upload"])) { - try { - $destName = tempnam($destCopy, ""); - $this->logDebug("Begining reading INPUT stream"); - $input = fopen("php://input", "r"); - $output = fopen($destName, "w"); - $sizeRead = 0; - while ($sizeRead < intval($boxData["size"])) { - $chunk = fread($input, 4096); - $sizeRead += strlen($chunk); - fwrite($output, $chunk, strlen($chunk)); - } - fclose($input); - fclose($output); - $boxData["tmp_name"] = $destName; - $this->storeFileToCopy($boxData); - $this->logDebug("End reading INPUT stream"); - } catch (Exception $e) { - $errorCode=411; - $errorMessage = $e->getMessage(); - break; - } - } else { - $destName = $destCopy."/".basename($boxData["tmp_name"]); - if ($destName == $boxData["tmp_name"]) $destName .= "1"; - if (move_uploaded_file($boxData["tmp_name"], $destName)) { - $boxData["tmp_name"] = $destName; - $this->storeFileToCopy($boxData); - } else { - $mess = ConfService::getMessages(); - $errorCode = 411; - $errorMessage="$mess[33] ".$boxData["name"]; - break; - } - } - } - if (isSet($errorMessage)) { - $this->logDebug("Return error $errorCode $errorMessage"); - return array("ERROR" => array("CODE" => $errorCode, "MESSAGE" => $errorMessage)); - } else { - $this->logDebug("Return success"); - return array("SUCCESS" => true, "PREVENT_NOTIF" => true); - } - - break; - default: - break; - } - session_write_close(); - exit; - - } - - public function isWriteable($path, $type="dir") - { - $parts = parse_url($path); - $dir = $parts["path"]; - if ($type == "dir" && ($dir == "" || $dir == "/" || $dir == "\\")) { // ROOT, WE ARE NOT SURE TO BE ABLE TO READ THE PARENT - return true; - } else { - return is_writable($path); - } - - } - - public function deldir($location, $repoData) - { - if (is_dir($location)) { - $dirsToRecurse = array(); - $all=opendir($location); - while ($file=readdir($all)) { - if (is_dir("$location/$file") && $file !=".." && $file!=".") { - $dirsToRecurse[] = "$location/$file"; - } elseif (!is_dir("$location/$file")) { - if (file_exists("$location/$file")) { - unlink("$location/$file"); - } - unset($file); - } - } - closedir($all); - foreach ($dirsToRecurse as $recurse) { - $this->deldir($recurse, $repoData); - } - rmdir($location); - } else { - if (file_exists("$location")) { - $test = @unlink("$location"); - if(!$test) throw new Exception("Cannot delete file ".$location); - } - } - if (isSet($repoData["recycle"]) && basename(dirname($location)) == $repoData["recycle"]) { - // DELETING FROM RECYCLE - RecycleBinManager::deleteFromRecycle($location); - } - } - - - public function storeFileToCopy($fileData) - { - $user = AuthService::getLoggedUser(); - $files = $user->getTemporaryData("tmp_upload"); - $this->logDebug("Saving user temporary data", array($fileData)); - $files[] = $fileData; - $user->saveTemporaryData("tmp_upload", $files); - if(AJXP_Utils::userAgentIsNativePydioApp()){ - $this->logInfo("Up from",$_SERVER["HTTP_USER_AGENT"]." - direct triger of next to remote"); - $this->uploadActions("next_to_remote", array(), array()); - } - } - - public function getFileNameToCopy() - { - $user = AuthService::getLoggedUser(); - $files = $user->getTemporaryData("tmp_upload"); - return $files[0]["name"]; - } - - public function getNextFileToCopy() - { - if(!$this->hasFilesToCopy()) return ""; - $user = AuthService::getLoggedUser(); - $files = $user->getTemporaryData("tmp_upload"); - $fData = $files[0]; - array_shift($files); - $user->saveTemporaryData("tmp_upload", $files); - return $fData; - } - - public function hasFilesToCopy() - { - $user = AuthService::getLoggedUser(); - $files = $user->getTemporaryData("tmp_upload"); - return (count($files)?true:false); - } - - public function testParameters($params) - { - if (empty($params["FTP_USER"])) { - throw new AJXP_Exception("Even if you intend to use the credentials stored in the session, temporarily set a user and password to perform the connexion test."); - } - if ($params["FTP_SECURE"]) { - $link = @ftp_ssl_connect($params["FTP_HOST"], $params["FTP_PORT"]); - } else { - $link = @ftp_connect($params["FTP_HOST"], $params["FTP_PORT"]); - } - if (!$link) { - throw new AJXP_Exception("Cannot connect to FTP server (".$params["FTP_HOST"].",". $params["FTP_PORT"].")"); - } - @ftp_set_option($link, FTP_TIMEOUT_SEC, 10); - if (!@ftp_login($link,$params["FTP_USER"],$params["FTP_PASS"])) { - ftp_close($link); - throw new AJXP_Exception("Cannot login to FTP server with user ".$params["FTP_USER"]); - } - if (!$params["FTP_DIRECT"]) { - @ftp_pasv($link, true); - global $_SESSION; - $_SESSION["ftpPasv"]="true"; - } - ftp_close($link); - - return "SUCCESS: Could succesfully connect to the FTP server with user '".$params["FTP_USER"]."'."; - } - - -} diff --git a/core/src/plugins/access.ftp/class.ftpAccessWrapper.php b/core/src/plugins/access.ftp/class.ftpAccessWrapper.php deleted file mode 100644 index b5ac79f211..0000000000 --- a/core/src/plugins/access.ftp/class.ftpAccessWrapper.php +++ /dev/null @@ -1,606 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - * - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - - -require_once(AJXP_BIN_FOLDER."/interface.AjxpWrapper.php"); -/** - * Wrapper for encapsulation FTP accesses - * @package AjaXplorer_Plugins - * @subpackage Access - */ -class ftpAccessWrapper implements AjxpWrapper -{ - // Instance vars $this-> - protected $host; - protected $port; - protected $secure; - protected $path; - protected $user; - protected $password; - protected $ftpActive; - protected $repoCharset; - protected $repositoryId; - protected $fp; - - protected $crtMode; - protected $crtLink; - protected $crtTarget; - - // Shared vars self:: - private static $dirContentLoopPath = array(); - private static $dirContent = array(); - private static $dirContentKeys = array(); - private static $dirContentIndex = array(); - - private $monthes = array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Juil", "Aug", "Sep", "Oct", "Nov", "Dec"); - - public static function getRealFSReference($path, $persistent = false) - { - $tmpFile = AJXP_Utils::getAjxpTmpDir()."/".md5(time()); - $tmpHandle = fopen($tmpFile, "wb"); - self::copyFileInStream($path, $tmpHandle); - fclose($tmpHandle); - if (!$persistent) { - register_shutdown_function(array("AJXP_Utils", "silentUnlink"), $tmpFile); - } - return $tmpFile; - } - - public static function isRemote() - { - return true; - } - - public static function isSeekable($url) - { - return true; - } - - public static function copyFileInStream($path, $stream) - { - $fake = new ftpAccessWrapper(); - $parts = $fake->parseUrl($path); - $link = $fake->createFTPLink(); - $serverPath = AJXP_Utils::securePath($fake->path."/".$parts["path"]); - AJXP_Logger::debug($serverPath); - ftp_fget($link, $stream, $serverPath, FTP_BINARY); - } - - public static function changeMode($path, $chmodValue) - { - $fake = new ftpAccessWrapper(); - $parts = $fake->parseUrl($path); - $link = $fake->createFTPLink(); - $serverPath = AJXP_Utils::securePath($fake->path."/".$parts["path"]); - ftp_chmod($link, $chmodValue, $serverPath); - } - - public function stream_open($url, $mode, $options, &$context) - { - if (stripos($mode, "w") !== false) { - $this->crtMode = 'write'; - $parts = $this->parseUrl($url); - $this->crtTarget = AJXP_Utils::securePath($this->path."/".$parts["path"]); - $this->crtLink = $this->createFTPLink(); - $this->fp = tmpfile(); - } else { - $this->crtMode = 'read'; - $this->fp = tmpfile(); - $this->copyFileInStream($url, $this->fp); - rewind($this->fp); - } - /* - if ($context) { - $this->fp = @fopen($this->buildRealUrl($url), $mode, $options, $context); - } else { - $this->fp = @fopen($this->buildRealUrl($url), $mode); - } - */ - return ($this->fp !== false); - } - - public function stream_stat() - { - return fstat($this->fp); - } - - public function stream_seek($offset , $whence = SEEK_SET) - { - fseek($this->fp, $offset, SEEK_SET); - } - - public function stream_tell() - { - return ftell($this->fp); - } - - public function stream_read($count) - { - return fread($this->fp, $count); - } - - public function stream_write($data) - { - fwrite($this->fp, $data, strlen($data)); - return strlen($data); - } - - public function stream_eof() - { - return feof($this->fp); - } - - public function stream_close() - { - if (isSet($this->fp) && $this->fp!=-1 && $this->fp!==false) { - fclose($this->fp); - } - } - - // PHP bug #62035 - public static function fput_quota_hack($errno, $errstr, $errfile, $errline, $errcontext) - { - if (strpos($errstr, "Opening BINARY mode data connection") !== false) - $errstr = "Transfer failed. Please check available disk space (quota)"; - AJXP_XMLWriter::catchError($errno, $errstr, $errfile, $errline, $errcontext); - } - - public function stream_flush() - { - if (isSet($this->fp) && $this->fp!=-1 && $this->fp!==false) { - if ($this->crtMode == 'write') { - rewind($this->fp); - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"Ftp_fput", array("target"=>$this->crtTarget)); - set_error_handler(array("ftpAccessWrapper", "fput_quota_hack"), E_ALL & ~E_NOTICE ); - ftp_fput($this->crtLink, $this->crtTarget, $this->fp, FTP_BINARY); - restore_error_handler(); - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"Ftp_fput end", array("target"=>$this->crtTarget)); - } else { - fflush($this->fp); - } - } - } - - public function unlink($url) - { - return unlink($this->buildRealUrl($url)); - } - - public function rmdir($url, $options) - { - return rmdir($this->buildRealUrl($url)); - } - - public function mkdir($url, $mode, $options) - { - return mkdir($this->buildRealUrl($url), $mode); - } - - public function rename($from, $to) - { - return rename($this->buildRealUrl($from), $this->buildRealUrl($to)); - } - - public function url_stat($path, $flags) - { - // We are in an opendir loop - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"URL_STAT", $path); - $testLoopPath = AJXP_Utils::safeDirname($path); - if (is_array(self::$dirContent[$testLoopPath])) { - $search = AJXP_Utils::safeBasename($path); - //if($search == "") $search = "."; - if (array_key_exists($search, self::$dirContent[$testLoopPath])) { - return self::$dirContent[$testLoopPath][$search]; - } - } - $parts = $this->parseUrl($path); - $link = $this->createFTPLink(); - $serverPath = AJXP_Utils::securePath($this->path."/".$parts["path"]); - if(empty($parts["path"])) $parts["path"] = "/"; - if ($parts["path"] == "/") { - $basename = "."; - } else { - $basename = AJXP_Utils::safeBasename($serverPath); - } - - $serverParent = AJXP_Utils::safeDirname($parts["path"]); - $serverParent = AJXP_Utils::securePath($this->path."/".$serverParent); - - $testCd = @ftp_chdir($link, $serverPath); - if ($testCd === true) { - // DIR - $contents = $this->rawList($link, $serverParent, 'd'); - foreach ($contents as $entry) { - $res = $this->rawListEntryToStat($entry); - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"RAWLISTENTRY ".$res["name"]. " (searching ".$basename.")", $res["stat"]); - if ($res["name"] == $basename) { - AbstractAccessDriver::fixPermissions($res["stat"], ConfService::getRepositoryById($this->repositoryId), array($this, "getRemoteUserId")); - $statValue = $res["stat"]; - return $statValue; - } - } - // Not found : is it the "." - if($basename == "."){ - // Make at least a readable fake stat - $fakeStat = stat(AJXP_DATA_PATH); - return $fakeStat; - } - } else { - // FILE - $contents = $this->rawList($link, $serverPath, 'f'); - if (count($contents) == 1) { - $res = $this->rawListEntryToStat($contents[0]); - AbstractAccessDriver::fixPermissions($res["stat"], ConfService::getRepositoryById($this->repositoryId), array($this, "getRemoteUserId")); - $statValue = $res["stat"]; - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"STAT FILE $serverPath", $statValue); - return $statValue; - } - } - return null; - } - - public function dir_opendir ($url , $options ) - { - if(isSet(self::$dirContent[$url])){ - array_push(self::$dirContentLoopPath, $url); - return true; - } - $parts = $this->parseUrl($url); - $link = $this->createFTPLink(); - $serverPath = AJXP_Utils::securePath($this->path."/".$parts["path"]); - $contents = $this->rawList($link, $serverPath); - $folders = $files = array(); - foreach ($contents as $entry) { - $result = $this->rawListEntryToStat($entry); - AbstractAccessDriver::fixPermissions($result["stat"], ConfService::getRepositoryById($this->repositoryId), array($this, "getRemoteUserId")); - $isDir = $result["dir"]; - $statValue = $result["stat"]; - $file = $result["name"]; - if ($isDir) { - $folders[$file] = $statValue; - } else { - $files[$file] = $statValue; - } - } - // Append all files keys to folders. Do not use array_merge. - foreach ($files as $key => $value) { - $folders[$key] = $value; - } - AJXP_Logger::debug(__CLASS__, __FUNCTION__, "OPENDIR ", $folders); - array_push(self::$dirContentLoopPath, $url); - self::$dirContent[$url] = $folders; - self::$dirContentKeys[$url] = array_keys($folders); - self::$dirContentIndex[$url] = 0; - return true; - } - - public function dir_closedir () - { - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"CLOSEDIR"); - $loopPath = array_pop(self::$dirContentLoopPath); - self::$dirContentIndex[$loopPath] = 0; - //Make a simple rewind, keep in cache - //self::$dirContent[$loopPath] = null; - //self::$dirContentKeys[$loopPath] = null; - } - - public function dir_readdir () - { - $loopPath = self::$dirContentLoopPath[count(self::$dirContentLoopPath)-1]; - $index = self::$dirContentIndex[$loopPath]; - self::$dirContentIndex[$loopPath] ++; - if (isSet(self::$dirContentKeys[$loopPath][$index])) { - return self::$dirContentKeys[$loopPath][$index]; - } else { - return false; - } - } - - public function dir_rewinddir () - { - $loopPath = self::$dirContentLoopPath[count(self::$dirContentLoopPath)-1]; - self::$dirContentIndex[$loopPath] = 0; - } - - protected function rawList($link, $serverPath, $target = 'd', $retry = true) - { - if ($target == 'f') { - $parentDir = AJXP_Utils::safeDirname($serverPath); - $fileName = AJXP_Utils::safeBasename($serverPath); - ftp_chdir($link, $parentDir); - $rl_dirlist = @ftp_rawlist($link, "-a ."); - //AJXP_Logger::debug(__CLASS__,__FUNCTION__,"FILE RAWLIST FROM ".$parentDir); - if (is_array($rl_dirlist)) { - $escaped = preg_quote($fileName); - foreach ($rl_dirlist as $rl_index => $rl_entry) { - if (preg_match("/ $escaped$/" , $rl_entry)) { - $contents = array($rl_dirlist[$rl_index]); - } - } - } - } else { - ftp_chdir($link, $serverPath); - $contents = ftp_rawlist($link, "-a ."); - //AJXP_Logger::debug(__CLASS__,__FUNCTION__,"RAW LIST RESULT ".print_r($contents, true)); - } - - if (!is_array($contents) && !$this->ftpActive) { - if ($retry == false) { - return array(); - } - // We might have timed out, so let's go passive if not done yet - global $_SESSION; - if ($_SESSION["ftpPasv"] == "true") { - return array(); - } - @ftp_pasv($link, TRUE); - $_SESSION["ftpPasv"]="true"; - // RETRY! - return $this->rawList($link, $serverPath, $target, FALSE); - } - if (!is_array($contents)) { - return array(); - } - return $contents; - } - - protected function rawListEntryToStat($entry, $filterStatPerms = false) - { - $info = array(); - $monthes = array_flip( $this->monthes ); - $vinfo = preg_split("/[\s]+/", $entry); - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"RAW LIST", $entry); - $statValue = array(); - if ($vinfo[0] !== "total") { - $fileperms = $vinfo[0]; - $info['num'] = $vinfo[1]; - $info['owner'] = $vinfo[2]; - $info['groups'] = array(); - $i = 3; - while (true) { - $info['groups'][] = $vinfo[$i]; - $i++; - // Detect "Size" and "Month" - if(is_numeric($vinfo[$i]) && !is_numeric($vinfo[$i+1])) break; - } - $info['group'] = implode(" ", $info["groups"]); - $info['size'] = $vinfo[$i]; $i++; - $info['month'] = $vinfo[$i]; $i++; - $info['day'] = $vinfo[$i]; $i++; - $info['timeOrYear'] = $vinfo[$i]; $i++; - //$info['name'] = $vinfo[$i]; $i++; - } - $resplit = preg_split("/[\s]+/", $entry, 8 + count($info["groups"])); - $file = trim(array_pop($resplit)); - $statValue[7] = $statValue["size"] = trim($info['size']); - if (strstr($info["timeOrYear"], ":")) { - $info["time"] = $info["timeOrYear"]; - $monthKey = $monthes[$info['month']] + 1; - if (intval(date('m')) < $monthKey) { - $info['year'] = date("Y") -1; - } else { - $info["year"] = date("Y"); - } - } else { - $info["time"] = '09:00'; - $info["year"] = $info["timeOrYear"]; - } - $statValue[4] = $statValue["uid"] = $info["owner"]; - $statValue[5] = $statValue["gid"] = $info["group"]; - $filedate = trim($info['day'])." ".trim($info['month'])." ".trim($info['year'])." ".trim($info['time']); - $statValue[9] = $statValue["mtime"] = strtotime($filedate); - - $isDir = false; - if (strpos($fileperms,"d")!==FALSE || strpos($fileperms,"l")!==FALSE) { - if (strpos($fileperms,"l")!==FALSE) { - $test=explode(" ->", $file); - $file=$test[0]; - } - $isDir = true; - } - $boolIsDir = $isDir; - $statValue[2] = $statValue["mode"] = $this->convertingChmod($fileperms); - $statValue["ftp_perms"] = $fileperms; - return array("name"=>$file, "stat"=>$statValue, "dir"=>$isDir); - } - - protected function parseUrl($url, $forceLogin = false) - { - // URL MAY BE ajxp.ftp://username:password@host/path - $urlParts = AJXP_Utils::safeParseUrl($url); - $this->repositoryId = $urlParts["host"]; - $repository = ConfService::getRepositoryById($this->repositoryId); - if ($repository == null) { - throw new Exception("Cannot find repository for dynamic ftp authentication."); - } - $credentials = AJXP_Safe::tryLoadingCredentialsFromSources($urlParts, $repository); - $this->user = $credentials["user"]; - $this->password = $credentials["password"]; - if ($this->user=="") { - throw new AJXP_Exception("Cannot find user/pass for FTP access!"); - } - if ($repository->getOption("DYNAMIC_FTP") == "TRUE" && isSet($_SESSION["AJXP_DYNAMIC_FTP_DATA"])) { - $data = $_SESSION["AJXP_DYNAMIC_FTP_DATA"]; - $this->host = $data["FTP_HOST"]; - $this->path = $data["PATH"]; - $this->secure = ($data["FTP_SECURE"] == "TRUE"?true:false); - $this->port = ($data["FTP_PORT"]!=""?intval($data["FTP_PORT"]):($this->secure?22:21)); - $this->ftpActive = ($data["FTP_DIRECT"] == "TRUE"?true:false); - $this->repoCharset = $data["CHARSET"]; - } else { - $this->host = $repository->getOption("FTP_HOST"); - $this->path = $repository->getOption("PATH"); - $this->secure = ($repository->getOption("FTP_SECURE") == "TRUE"?true:false); - $this->port = ($repository->getOption("FTP_PORT")!=""?intval($repository->getOption("FTP_PORT")):($this->secure?22:21)); - $this->ftpActive = ($repository->getOption("FTP_DIRECT") == "TRUE"?true:false); - $this->repoCharset = $repository->getOption("CHARSET"); - } - - // Test Connexion and server features - global $_SESSION; - $cacheKey = $repository->getId()."_ftpCharset"; - if (!isset($_SESSION[$cacheKey]) || !strlen($_SESSION[$cacheKey]) || $forceLogin) { - $features = $this->getServerFeatures(); - $ctxCharset = ConfService::getContextCharset(); - if(empty($ctxCharset)) { - ConfService::setContextCharset($features["charset"]); - $_SESSION[$cacheKey] = $features["charset"]; - }else{ - $_SESSION[$cacheKey] = $ctxCharset; - } - } - return $urlParts; - } - - /** - * @return array Array(UID, GID) to be used to compute permission - */ - public function getRemoteUserId() - { - $repoUid = ConfService::getRepository()->getOption("UID"); - if (!empty($repoUid)) { - return array($repoUid, "-1"); - } - return array($this->user, "-1"); - } - - protected function buildRealUrl($url) - { - if (!isSet($this->user)) { - $parts = $this->parseUrl($url); - } else { - // parseUrl already called before (rename case). - $parts = AJXP_Utils::safeParseUrl($url); - } - $serverPath = AJXP_Utils::securePath("/$this->path/".$parts["path"]); - return "ftp".($this->secure?"s":"")."://$this->user:$this->password@$this->host:$this->port".$serverPath; - } - - /** This method retrieves the FTP server features as described in RFC2389 - * A decent FTP server support MLST command to list file using UTF-8 encoding - * @return an array of features (see code) - */ - protected function getServerFeatures() - { - $link = $this->createFTPLink(); - - if (ConfService::getRepositoryById($this->repositoryId)->getOption("CREATE") == true) { - // Test if root exists and create it otherwise - $serverPath = AJXP_Utils::securePath($this->path."/"); - $testCd = @ftp_chdir($link, $serverPath); - if ($testCd !== true) { - $res = @ftp_mkdir($link, $serverPath); - if(!$res) throw new Exception("Cannot create path on remote server!"); - } - } - - $features = @ftp_raw($link, "FEAT"); - // Check the answer code - if (isSet($features[0]) && $features[0][0] != "2") { - //ftp_close($link); - return array("list"=>"LIST", "charset"=>$this->repoCharset); - } - $retArray = array("list"=>"LIST", "charset"=>$this->repoCharset); - // Ok, find out the encoding used - foreach ($features as $feature) { - if (strstr($feature, "UTF8") !== FALSE) { // See http://wiki.filezilla-project.org/Character_Set for an explaination - @ftp_raw($link, "OPTS UTF-8 ON"); - $retArray['charset'] = "UTF-8"; - //ftp_close($link); - return $retArray; - } - } - // In the future version, we should also use MLST as it standardize the listing format - return $retArray; - } - - protected function createFTPLink() - { - // If connexion exist and is still connected - if(is_array($_SESSION["FTP_CONNEXIONS"]) - && array_key_exists($this->repositoryId, $_SESSION["FTP_CONNEXIONS"]) - && @ftp_systype($_SESSION["FTP_CONNEXIONS"][$this->repositoryId])){ - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"Using stored FTP Session"); - return $_SESSION["FTP_CONNEXIONS"][$this->repositoryId]; - } - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"Creating new FTP Session"); - $link = FALSE; - //Connects to the FTP. - if ($this->secure) { - $link = @ftp_ssl_connect($this->host, $this->port); - } else { - $link = @ftp_connect($this->host, $this->port); - } - if (!$link) { - throw new AJXP_Exception("Cannot connect to FTP server ($this->host, $this->port)"); - } - //register_shutdown_function('ftp_close', $link); - @ftp_set_option($link, FTP_TIMEOUT_SEC, 10); - if (!@ftp_login($link,$this->user,$this->password)) { - throw new AJXP_Exception("Cannot login to FTP server with user $this->user"); - } - if (!$this->ftpActive) { - @ftp_pasv($link, true); - global $_SESSION; - $_SESSION["ftpPasv"]="true"; - } - if (!is_array($_SESSION["FTP_CONNEXIONS"])) { - $_SESSION["FTP_CONNEXIONS"] = array(); - } - $_SESSION["FTP_CONNEXIONS"][$this->repositoryId] = $link; - return $link; - } - - protected function convertingChmod($permissions, $filterForStat = false) - { - $mode = 0; - - if ($permissions[1] == 'r') $mode += 0400; - if ($permissions[2] == 'w') $mode += 0200; - if ($permissions[3] == 'x') $mode += 0100; - else if ($permissions[3] == 's') $mode += 04100; - else if ($permissions[3] == 'S') $mode += 04000; - - if ($permissions[4] == 'r') $mode += 040; - if ($permissions[5] == 'w' || ($filterForStat && $permissions[2] == 'w')) $mode += 020; - if ($permissions[6] == 'x' || ($filterForStat && $permissions[3] == 'x')) $mode += 010; - else if ($permissions[6] == 's') $mode += 02010; - else if ($permissions[6] == 'S') $mode += 02000; - - if ($permissions[7] == 'r') $mode += 04; - if ($permissions[8] == 'w' || ($filterForStat && $permissions[2] == 'w')) $mode += 02; - if ($permissions[9] == 'x' || ($filterForStat && $permissions[3] == 'x')) $mode += 01; - else if ($permissions[9] == 't') $mode += 01001; - else if ($permissions[9] == 'T') $mode += 01000; - - if ($permissions[0] != "d") { - $mode += 0100000; - } else { - $mode += 0040000; - } - - $mode = (string) ("0".$mode); - return $mode; - } - -} diff --git a/core/src/plugins/access.ftp/i18n/conf/de.php b/core/src/plugins/access.ftp/i18n/conf/de.php index f64901cdcd..9796d80adf 100644 --- a/core/src/plugins/access.ftp/i18n/conf/de.php +++ b/core/src/plugins/access.ftp/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "FTP Server" => "FTP-Server", diff --git a/core/src/plugins/access.ftp/i18n/conf/en.php b/core/src/plugins/access.ftp/i18n/conf/en.php index bbaf7d48d8..2b26c9cc49 100644 --- a/core/src/plugins/access.ftp/i18n/conf/en.php +++ b/core/src/plugins/access.ftp/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "FTP Server" => "FTP Server", diff --git a/core/src/plugins/access.ftp/i18n/conf/fr.php b/core/src/plugins/access.ftp/i18n/conf/fr.php index 8348f622fb..32ca77f14e 100644 --- a/core/src/plugins/access.ftp/i18n/conf/fr.php +++ b/core/src/plugins/access.ftp/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "FTP Server" => "Serveur FTP", diff --git a/core/src/plugins/access.ftp/i18n/conf/it.php b/core/src/plugins/access.ftp/i18n/conf/it.php index 7874d4df89..ab2e657411 100644 --- a/core/src/plugins/access.ftp/i18n/conf/it.php +++ b/core/src/plugins/access.ftp/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "FTP Server" => "Server FTP", diff --git a/core/src/plugins/access.ftp/i18n/conf/pt.php b/core/src/plugins/access.ftp/i18n/conf/pt.php index 9984778654..008c58017b 100644 --- a/core/src/plugins/access.ftp/i18n/conf/pt.php +++ b/core/src/plugins/access.ftp/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "FTP Server" => "Servidor FTP", diff --git a/core/src/plugins/access.ftp/manifest.xml b/core/src/plugins/access.ftp/manifest.xml index 65e80518e9..fccff3d335 100644 --- a/core/src/plugins/access.ftp/manifest.xml +++ b/core/src/plugins/access.ftp/manifest.xml @@ -22,8 +22,6 @@ - - @@ -40,13 +38,16 @@ - + + + + + - + - - + diff --git a/core/src/plugins/access.ftp/test.ftpAccess.php b/core/src/plugins/access.ftp/test.ftpAccess.php index acc5105736..f0b442c0ed 100644 --- a/core/src/plugins/access.ftp/test.ftpAccess.php +++ b/core/src/plugins/access.ftp/test.ftpAccess.php @@ -16,11 +16,11 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ -defined('AJXP_EXEC') or die( 'Access not allowed'); +namespace Pydio\Tests; -require_once(AJXP_BIN_FOLDER . '/class.AbstractTest.php'); +defined('AJXP_EXEC') or die( 'Access not allowed'); /** * @package AjaXplorer_Plugins @@ -28,17 +28,24 @@ */ class ftpAccessTest extends AbstractTest { + /** + * ftpAccessTest constructor. + */ public function __construct() { parent::__construct("Remote FTP Filesystem Plugin", ""); } + /** + * @param \Pydio\Access\Core\Model\Repository $repo + * @return bool|int + */ public function doRepositoryTest($repo) { if($repo->accessType != "ftp") return -1; $basePath = AJXP_INSTALL_PATH."/".AJXP_PLUGINS_FOLDER."/access.ftp/" ; // Check file exists - if (!file_exists($basePath."class.ftpAccessDriver.php") + if (!file_exists($basePath."FtpAccessDriver.php") || !file_exists($basePath."manifest.xml")) - { $this->failedInfo .= "Missing at least one of the plugin files (class.ftpAccessDriver.php, manifest.xml, ftpActions.xml).\nPlease reinstall from lastest release."; return FALSE; } + { $this->failedInfo .= "Missing at least one of the plugin files (FtpAccessDriver.php, manifest.xml, ftpActions.xml).\nPlease reinstall from lastest release."; return FALSE; } return TRUE; } diff --git a/core/src/plugins/access.imap/ImapAccessDriver.php b/core/src/plugins/access.imap/ImapAccessDriver.php new file mode 100644 index 0000000000..e978280e94 --- /dev/null +++ b/core/src/plugins/access.imap/ImapAccessDriver.php @@ -0,0 +1,212 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Driver\StreamProvider\Imap; + +use DOMNode; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Driver\StreamProvider\FS\FsAccessDriver; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Utils\Vars\StatHelper; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Plugin to browse a mailbox content (IMAP OR POP) + * @package AjaXplorer_Plugins + * @subpackage Access + */ +class ImapAccessDriver extends FsAccessDriver +{ + /** + * @var \Pydio\Access\Core\Model\Repository + */ + public $repository; + public $driverConf; + protected $wrapperClassName; + protected $urlBase; + + /** + * @param ContextInterface $contextInterface + * @throws PydioException + * @throws \Exception + */ + protected function initRepository(ContextInterface $contextInterface) + { + if (is_array($this->pluginConf)) { + $this->driverConf = $this->pluginConf; + } else { + $this->driverConf = array(); + } + + $this->urlBase = $contextInterface->getUrlBase(); + + } + + public function performChecks() + { + if (!function_exists("imap_createmailbox")) { + throw new \Exception("PHP Imap extension must be loaded to use this driver!"); + } + } + + public static function inverseSort($st1, $st2) + { + return strnatcasecmp($st2, $st1); + } + + public static function sortInboxFirst($st1, $st2) + { + if($st1 == "INBOX") return -1; + if($st2 == "INBOX") return 1; + return strcmp($st1, $st2); + } + + public function switchAction(ServerRequestInterface &$request, ResponseInterface &$response) + { + if ($request->getAttribute("action") == "ls") { + $dir = $request->getParsedBody()["dir"]; + if ($dir == "/" || empty($dir)) { + // MAILBOXES CASE + $this->repository->addOption("PAGINATION_THRESHOLD", 500); + $this->driverConf["SCANDIR_RESULT_SORTFONC"] = array("Pydio\\Access\\Driver\\StreamProvider\\Imap\\imapAccessDriver", "sortInboxFirst"); + } else { + // MAILS LISTING CASE + $this->driverConf["SCANDIR_RESULT_SORTFONC"] = array("Pydio\\Access\\Driver\\StreamProvider\\Imap\\imapAccessDriver", "inverseSort"); + } + } + parent::switchAction($request, $response); + } + + /** + * + * @param \Pydio\Access\Core\Model\AJXP_Node $ajxpNode + */ + public function enrichMetadata(&$ajxpNode)//, &$metadata, $wrapperClassName, &$realFile) + { + $currentNode = $ajxpNode->getUrl(); + $baseUrl = $ajxpNode->getContext()->getUrlBase(); + $metadata = $ajxpNode->metadata; + $parsed = parse_url($currentNode); + if ( isSet($parsed["fragment"]) && strpos($parsed["fragment"], "attachments") === 0) { + list(, $attachmentId) = explode("/", $parsed["fragment"]); + $meta = ImapAccessWrapper::getCurrentAttachmentsMetadata(); + if ($meta != null) { + foreach ($meta as $attach) { + if ($attach["x-attachment-id"] == $attachmentId) { + $metadata["text"] = $attach["filename"]; + $fakeNode = new AJXP_Node($baseUrl."/".ltrim($attach["filename"], "/")); + $mimeData = StatHelper::getMimeInfo($fakeNode, false); + $metadata["mimestring_id"] = $mimeData[0]; + $metadata["icon"] = $mimeData[1]; + } + } + } + } + + if (!$metadata["is_file"] && $currentNode != "") { + $metadata["icon"] = "imap_images/ICON_SIZE/mail_folder_sent.png"; + } + if (basename($currentNode) == "INBOX") { + $metadata["text"] = "Incoming Mails"; + } + if (strstr($currentNode, "__delim__")!==false) { + $parts = explode("/", $currentNode); + $metadata["text"] = str_replace("__delim__", "/", array_pop($parts)); + } + $ajxpNode->metadata = $metadata; + } + + /** + * @param AJXP_Node $currentNode + * @param string $localName + * @param string $wrapperClassName + */ + public function attachmentDLName($currentNode, &$localName, $wrapperClassName) + { + $parsed = parse_url($currentNode->getUrl()); + if ( isSet($parsed["fragment"]) && strpos($parsed["fragment"], "attachments") === 0) { + list(, $attachmentId) = explode("/", $parsed["fragment"]); + $meta = ImapAccessWrapper::getCurrentAttachmentsMetadata(); + if ($meta == null) { + stat($currentNode); + $meta = ImapAccessWrapper::getCurrentAttachmentsMetadata(); + } + if ($meta != null) { + foreach ($meta as $attach) { + if ($attach["x-attachment-id"] == $attachmentId) { + $localName = $attach["filename"]; + } + } + } + } else { + $localName = basename($currentNode).".eml"; + } + } + + /** + * Parse + * @inheritdoc + */ + protected function parseSpecificContributions(ContextInterface $ctx, \DOMNode &$contribNode) + { + parent::parseSpecificContributions($ctx, $contribNode); + if($contribNode->nodeName != "actions") return ; + $this->disableArchiveBrowsingContributions($contribNode); + } + + /** + * @param ContextInterface $contextInterface + * @param $nodePath + * @param $nodeName + * @param $isLeaf + * @param $lsOptions + * @return bool + */ + public function filterNodeName(ContextInterface $contextInterface, $nodePath, $nodeName, &$isLeaf, $lsOptions) + { + return true; + } + + /** + * @param AJXP_Node $dirNode + * @param bool $foldersOnly + * @param bool $nonEmptyCheckOnly + * @param null $dirHANDLE + * @return int + * @throws \Exception + */ + public function countChildren(AJXP_Node $dirNode, $foldersOnly = false, $nonEmptyCheckOnly = false, $dirHANDLE = null) + { + if($foldersOnly) return 0; + $count = 0; + if($tmpHandle = opendir($dirNode->getUrl())){ + // WILL USE IMAP FUNCTIONS TO COUNT; + $this->logDebug("COUNT : ".ImapAccessWrapper::getCurrentDirCount()); + $count = ImapAccessWrapper::getCurrentDirCount(); + closedir($tmpHandle); + } + return $count; + } + +} diff --git a/core/src/plugins/access.imap/ImapAccessWrapper.php b/core/src/plugins/access.imap/ImapAccessWrapper.php new file mode 100644 index 0000000000..c3c5243978 --- /dev/null +++ b/core/src/plugins/access.imap/ImapAccessWrapper.php @@ -0,0 +1,555 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Driver\StreamProvider\Imap; + +use EmlParser; +use Pydio\Access\Core\IAjxpWrapper; +use Pydio\Access\Core\Model\AJXP_Node; + +use Pydio\Core\Utils\Vars\UrlUtils; +use Pydio\Core\Utils\TextEncoder; +use Pydio\Log\Core\Logger; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +function rejectEmpty($element){return !empty($element);} + +/** + * Plugin to browse a mailbox content (IMAP OR POP) + * @package AjaXplorer_Plugins + * @subpackage Access + */ +class ImapAccessWrapper implements IAjxpWrapper +{ + public $ih; + public $host; + public $port; + public $username; + public $password; + public $path; + public $pop3; + // stuff for dir reading + public $dir; + public $pos; + // stuff for file reading + public $data; + public $gotbody; + public $size; + public $time; + + public $fragment; + public $mailbox; + + public $mailboxes; + public $currentAttachmentData; + + protected $repositoryId; + + private static $currentStream; + private static $currentRef; + private static $currentCount; + private static $delimiter; + private static $attachmentsMetadata; + + public static function closeStreamFunc() + { + if (self::$currentStream) { + Logger::debug("Closing stream now!"); + imap_close(self::$currentStream); + } + } + + public static function getCurrentDirCount() + { + return self::$currentCount; + } + + public static function getCurrentAttachmentsMetadata() + { + return self::$attachmentsMetadata; + } + + public function stream_open($path, $mode, $options, &$opened_path) + { + // parse URL + $node = new AJXP_Node($path); + $parts = UrlUtils::safeParseUrl($path); + $this->repositoryId = $node->getRepositoryId(); + + $mainCacheDir = (defined('AJXP_SHARED_CACHE_DIR')?AJXP_SHARED_CACHE_DIR:AJXP_CACHE_DIR); + if (!isset(self::$delimiter) && file_exists($mainCacheDir."/access.imap/mailbox_delim_".$this->repositoryId)) { + self::$delimiter = file_get_contents($mainCacheDir."/access.imap/mailbox_delim_".$this->repositoryId); + } + + $this->path = substr($parts["path"], 1); + //$this->mailbox = "INBOX"; + $pathParts = explode("/", $this->path); + $pathParts = array_filter($pathParts, "rejectEmpty"); + if (count($pathParts) > 1) { + $this->path = array_pop($pathParts); + $this->mailbox = implode("/", $pathParts); + } else if (count($pathParts) == 1) { + $this->mailbox = implode("/", $pathParts); + $this->path = ""; + } else { + $this->mailbox = ""; + $this->path = ""; + } + $this->fragment = $parts["fragment"]; + if (empty ( $this->path ) && $mode !== 'np') { + return false; + } + if (!empty($this->mailbox)) { + $this->mailbox = mb_convert_encoding($this->mailbox, "UTF7-IMAP", TextEncoder::getEncoding()); + $this->mailbox = str_replace("__delim__", (isSet(self::$delimiter)?self::$delimiter:"/"), $this->mailbox); + } + if (!empty($this->fragment) && strpos($this->fragment, "attachments") === 0 && strpos($this->fragment, "/")!== false) { + // remove fragment + $ar = explode("#", $path); + $mailPath = array_shift($ar); + $ar = explode("/", $this->fragment); + $attachmentId = array_pop($ar); + $this->currentAttachmentData = array("realPath" => $mailPath, "attachmentId" => $attachmentId); + // EXTRACT ATTACHMENT AND RETURN + require_once AJXP_INSTALL_PATH."/plugins/editor.eml/class.EmlParser.php"; + $emlParser = new EmlParser("", ""); + $attachMeta = array(); + $this->data = $emlParser->getAttachmentBody( + $this->currentAttachmentData["realPath"], + $this->currentAttachmentData["attachmentId"], + true, + $attachMeta + ); + if (self::$attachmentsMetadata == null) { + self::$attachmentsMetadata = array($attachMeta); + } + + $this->currentAttachmentData["size"] = strlen($this->data); + $this->pos = 0; + $this->size = strlen($this->data); + return true; + } + + // open IMAP connection + if (self::$currentStream != null) { + Logger::debug(__CLASS__,__FUNCTION__,"Using currently opened stream! ".print_r(self::$currentStream, true)); + $this->ih = self::$currentStream; + // Rewind everything + $this->dir_rewinddir(); + $this->stream_seek(0); + } else { + $ctx = $node->getContext(); + $ssl = $ctx->getRepository()->getContextOption($ctx, "SSL") == "true" ? true: false ; + $this->pop3 = $ctx->getRepository()->getContextOption($ctx, "BOX_TYPE") == "pop3" ? true : false; + $this->host = $ctx->getRepository()->getContextOption($ctx, "HOST"); + $this->port = $ctx->getRepository()->getContextOption($ctx, "PORT"); + $this->username = $ctx->getRepository()->getContextOption($ctx, "USER"); + $this->password = $ctx->getRepository()->getContextOption($ctx, "PASS"); + $server = "{". $this->host . ":" . $this->port . "/".($this->pop3?"pop3/":"").($ssl?"ssl/novalidate-cert":"novalidate-cert")."}"; + self::$currentRef = $server; + Logger::debug(__CLASS__,__FUNCTION__,"Opening a new stream ".$server." with mailbox '".$this->mailbox."'"); + try { + $this->ih = imap_open ( $server.$this->mailbox , $this->username, $this->password, (!$this->pop3 && empty($this->mailbox)?OP_HALFOPEN:NULL), 1); + } catch (\Exception $e) { + throw new \Exception($e->getMessage()." - imap errors : ".print_r(imap_errors(), true), $e->getCode()); + } + self::$currentStream = $this->ih; + register_shutdown_function(function(){ + ImapAccessWrapper::closeStreamFunc(); + }); + } + if ($this->ih) { + if (! empty ( $this->path )) { + list ( $stats, ) = imap_fetch_overview ( $this->ih, $this->path ); + $this->size = $stats->size; + $this->time = strtotime ( $stats->date ); + } + return true; + } else { + return false; + } + } + + public function stream_close() + { + if (empty($this->mailbox)) { + //self::$currentStream = null; + //imap_close ( $this->ih ); + } + if (!empty($this->currentAttachmentData)) { + $this->currentAttachmentBody = null; + } + } + + /* Smart reader, at first it only downloads the header to memory, but if a read request is made + beyond the header, we download the rest of the body */ + public function stream_read($count) + { + //AJXP_Logger::debug(__CLASS__,__FUNCTION__,"READING $count FROM $this->path", $this->currentAttachmentData); + if (!empty($this->currentAttachmentData)) { + if (empty($this->data)) { + Logger::debug(__CLASS__,__FUNCTION__,"Attachement", $this->currentAttachmentData); + // EXTRACT ATTACHMENT AND RETURN + require_once AJXP_INSTALL_PATH."/plugins/editor.eml/class.EmlParser.php"; + $emlParser = new EmlParser("", ""); + $attachMeta = array(); + $this->data = $emlParser->getAttachmentBody( + $this->currentAttachmentData["realPath"], + $this->currentAttachmentData["attachmentId"], + true, + $attachMeta + ); + $this->pos = 0; + $this->size = strlen($this->data); + } + } else { + // smart... only download the header WHEN data is requested + if (empty ( $this->data )) { + $this->pos = 0; + $this->gotbody = false; + $this->data = imap_fetchheader ( $this->ih, $this->path ); + } + // only download the body once we read past the header + if ($this->gotbody == false && ($this->pos + $count > strlen ( $this->data )) && $this->fragment != "header") { + $this->gotbody = true; + $this->data .= imap_body ( $this->ih, $this->path ); + $this->size = strlen ( $this->data ); + } + } + if ($this->pos >= $this->size) { + return false; + } else { + $d = substr ( $this->data, $this->pos, $count ); + if ($this->pos + $count > strlen ( $this->data )) { + $this->pos = strlen ( $this->data ); + } else { + $this->pos = $this->pos + $count; + } + return $d; + } + } + + /* Can't write to POP3 */ + public function stream_write($data) + { + return false; + } + + public function stream_eof() + { + if ($this->pos >= $this->size) { + return true; + } else { + return false; + } + } + + public function stream_tell() + { + return $this->pos; + } + + public function stream_seek($offset , $whence = SEEK_SET) + { + switch ($whence) { + case SEEK_SET : + $this->pos = $offset; + break; + case SEEK_CUR : + $this->pos = $this->pos + $offset; + break; + case SEEK_END : + $this->pos = $this->size + $offset; + break; + } + } + + public function stream_stat() + { + $keys = array('dev' => 0, 'ino' => 0, 'mode' => 33216, 'nlink' => 0, 'uid' => 0, 'gid' => 0, 'rdev' => 0, 'size' => $this->size, 'atime' => $this->time, 'mtime' => $this->time, 'ctime' => $this->time, 'blksize' => 0, 'blocks' => 0 ); + return $keys; + } + + public function dir_opendir($path, $options) + { + // Reset + self::$attachmentsMetadata = null; + + $st = ''; + $stream = $this->stream_open ( $path, 'np', $options, $st ); + if (!$stream) { + return false; + } + if (empty($this->mailbox)) { + // We are browsing root, we want the list of mailboxes + $this->mailboxes = imap_getmailboxes($this->ih, self::$currentRef, "*"); + $this->dir = count($this->mailboxes); + self::$currentCount = count($this->mailboxes); + $this->pos = $this->dir - 1; + } else if ($this->fragment == "attachments") { + require_once AJXP_INSTALL_PATH.'/plugins/editor.eml/class.EmlParser.php'; + $parser = new EmlParser("", ""); + $ar = explode("#", $path); + $path = array_shift($ar);// remove fragment + self::$attachmentsMetadata = array(); + $parser->listAttachments($path, true, self::$attachmentsMetadata); + $this->dir = count(self::$attachmentsMetadata); + $this->pos = $this->dir - 1; + self::$currentCount = $this->dir; + + } else { + // We are in a mailbox, we want the messages number + $this->dir = imap_num_msg ( $this->ih ); + self::$currentCount = $this->dir; + $this->pos = $this->dir; + } + $this->stream_close (); + return true; + } + + public function dir_closedir() + { + // do nothing. + // $this->stream_close(); + $this->mailboxes = null; + } + + public function dir_readdir() + { + if ($this->mailboxes) { + if($this->pos < 0) return false; + else{ + $obj = $this->mailboxes[$this->pos]; + $this->pos --; + $x = $obj->name; + $x = mb_convert_encoding( $x, "UTF-8", "UTF7-IMAP" ); + $x = str_replace(self::$currentRef, "", $x); + $mainCacheDir = (defined('AJXP_SHARED_CACHE_DIR')?AJXP_SHARED_CACHE_DIR:AJXP_CACHE_DIR); + if (!isSet(self::$delimiter) && !file_exists($mainCacheDir."/access.imap/mailbox_delim_".$this->repositoryId)) { + if(!is_dir($mainCacheDir ."/access.imap")) mkdir($mainCacheDir."/access.imap"); + file_put_contents($mainCacheDir."/access.imap/mailbox_delim_".$this->repositoryId, $obj->delimiter); + self::$delimiter = $obj->delimiter; + } + $x = str_replace($obj->delimiter, "__delim__", $x); + } + } else if (self::$attachmentsMetadata != null) { + if($this->pos < 0) return false; + $x = self::$attachmentsMetadata[$this->pos]["x-attachment-id"]; + $this->pos--; + } else { + if ($this->pos < 1) { + return false; + } else { + $x = $this->pos; + $this->pos --; + //$x .= "#header"; + } + } + return $x; + } + + public function dir_rewinddir() + { + if (empty($this->mailbox)) { + $this->pos = $this->dir; + } else { + $this->pos = count($this->mailboxes) - 1; + } + } + + public function url_stat($path, $flags) + { + $emptyString = ''; + if ($this->stream_open ( $path, 'np', $flags, $emptyString)) { + if (!empty($this->path) && empty($this->currentAttachmentData)) { + // Mail + //$stats = array(); + if (empty($this->size) && empty($this->time)) { + list ( $stats, ) = imap_fetch_overview ( $this->ih, $this->path ); + $this->size = $stats->size; + $this->time = strtotime ( $stats->date ); + } + $keys = array( + 'dev' => 0, + 'ino' => 0, + 'mode' => 33279, + 'nlink' => 0, + 'uid' => 0, + 'gid' => 0, + 'rdev' => 0, + 'size' => $this->size, + 'atime' => $this->time, + 'mtime' => $this->time, + 'ctime' => $this->time, + 'blksize' => 0, + 'blocks' => 0 ); + } else { + // BOX + if (empty($this->currentAttachmentData) && !empty($this->mailbox) && !$this->pop3) { + // GET LAST MESSAGE + imap_reopen($this->ih, self::$currentRef.$this->mailbox); + $last = imap_num_msg($this->ih); + //AJXP_Logger::debug(__CLASS__,__FUNCTION__,"Should get mailbox data ".self::$currentRef.$this->mailbox . $last); + list ( $stats, ) = imap_fetch_overview ( $this->ih, $last ); + $this->size = $stats->size; + $this->time = strtotime ( $stats->date ); + } + $keys = array( + 'dev' => 0, + 'ino' => 0, + 'mode' => (empty($this->currentAttachmentData)?(33279 | 0040000):33216), + 'nlink' => 0, + 'uid' => 0, + 'gid' => 0, + 'rdev' => 0, + 'size' => (!empty($this->currentAttachmentData)?$this->currentAttachmentData["size"]:0), + 'atime' => (!empty($this->time)?$this->time:0), + 'mtime' => (!empty($this->time)?$this->time:0), + 'ctime' => (!empty($this->time)?$this->time:0), + 'blksize' => 0, + 'blocks' => 0 + ); + } + $this->stream_close (); + return $keys; + } else { + return false; + } + } + + /* Delete an email from the mailbox */ + public function unlink($path) + { + $st=''; + if ($this->stream_open ( $path, '', '', $st )) { + imap_delete ( $this->ih, $this->path ); + $this->stream_close (); + return true; + } else { + return false; + } + } + + + public static function getRealFSReference($path, $persistent = false) + { + return $path; + } + + /** + * Read a file (by chunks) and copy the data directly inside the given stream. + * + * @param string $path + * @param resource $stream + */ + public static function copyFileInStream($path, $stream) + { + //return $path; + $fp = fopen($path, 'r'); + $bufferSize = 4096 * 8; + if ($fp) { + while (!feof($fp)) { + $data = fread($fp, $bufferSize); + fwrite($stream, $data, strlen($data)); + } + fclose($fp); + } + } + + public static function isRemote() + { + return true; + } + + /** + * Chmod implementation for this type of access. + * + * @param string $path + * @param mixed $chmodValue + */ + public static function changeMode($path, $chmodValue) + { + } + + /** + * Enter description here... + * + * @param string $path + * @param int $mode + * @param int $options + * @return bool + */ + public function mkdir($path , $mode , $options) + { + } + + /** + * Enter description here... + * + * @param string $path_from + * @param string $path_to + * @return bool + */ + public function rename($path_from , $path_to) + { + } + + /** + * Enter description here... + * + * @param string $path + * @param int $options + * @return bool + */ + public function rmdir($path , $options) + { + } + + + /** + * Enter description here... + * + * @return bool + */ + public function stream_flush() + { + } + + + /** + * Describe whether the current wrapper can rewind a stream or not. + * @static + * @return boolean + */ + public static function isSeekable($url) + { + return false; + } + + /** + * @param AJXP_Node $node + * @return array + */ + public static function getResolvedOptionsForNode($node) + { + return ["TYPE" => "php"]; + } +} diff --git a/core/src/plugins/access.imap/class.imapAccessDriver.php b/core/src/plugins/access.imap/class.imapAccessDriver.php deleted file mode 100644 index 6352aca7ba..0000000000 --- a/core/src/plugins/access.imap/class.imapAccessDriver.php +++ /dev/null @@ -1,177 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * AJXP_Plugin to browse a mailbox content (IMAP OR POP) - * @package AjaXplorer_Plugins - * @subpackage Access - */ -class imapAccessDriver extends fsAccessDriver -{ - /** - * @var Repository - */ - public $repository; - public $driverConf; - protected $wrapperClassName; - protected $urlBase; - - public function initRepository() - { - if (is_array($this->pluginConf)) { - $this->driverConf = $this->pluginConf; - } else { - $this->driverConf = array(); - } - - $this->detectStreamWrapper(true); - $this->urlBase = "pydio://".$this->repository->getId(); - if ($this->repository->getOption("MAILBOX") != "") { - //$this->urlBase .= "/INBOX"; - } - /* - if (!file_exists($this->urlBase)) { - throw new AJXP_Exception("Cannot find base path for your repository! Please check the configuration!"); - } - */ - } - - public function performChecks() - { - if (!function_exists("imap_createmailbox")) { - throw new Exception("PHP Imap extension must be loaded to use this driver!"); - } - } - - public static function inverseSort($st1, $st2) - { - return strnatcasecmp($st2, $st1); - } - - public static function sortInboxFirst($st1, $st2) - { - if($st1 == "INBOX") return -1; - if($st2 == "INBOX") return 1; - return strcmp($st1, $st2); - } - - public function switchAction($action, $httpVars, $fileVars) - { - if ($action == "ls") { - $dir = $httpVars["dir"]; - if ($dir == "/" || empty($dir)) { - // MAILBOXES CASE - $this->repository->addOption("PAGINATION_THRESHOLD", 500); - $this->driverConf["SCANDIR_RESULT_SORTFONC"] = array("imapAccessDriver", "sortInboxFirst"); - } else { - // MAILS LISTING CASE - //$httpVars["dir"] = mb_convert_encoding($httpVars["dir"], "UTF7-IMAP", SystemTextEncoding::getEncoding()); - $this->driverConf["SCANDIR_RESULT_SORTFONC"] = array("imapAccessDriver", "inverseSort"); - } - } - parent::switchAction($action, $httpVars, $fileVars); - } - - /** - * - * @param AJXP_Node $ajxpNode - */ - public function enrichMetadata(&$ajxpNode)//, &$metadata, $wrapperClassName, &$realFile) - { - $currentNode = $ajxpNode->getUrl(); - $metadata = $ajxpNode->metadata; - $parsed = parse_url($currentNode); - if ( isSet($parsed["fragment"]) && strpos($parsed["fragment"], "attachments") === 0) { - list(, $attachmentId) = explode("/", $parsed["fragment"]); - $meta = imapAccessWrapper::getCurrentAttachmentsMetadata(); - if ($meta != null) { - foreach ($meta as $attach) { - if ($attach["x-attachment-id"] == $attachmentId) { - $metadata["text"] = $attach["filename"]; - $metadata["icon"] = AJXP_Utils::mimetype($attach["filename"], "image", false); - $metadata["mimestring"] = AJXP_Utils::mimetype($attach["filename"], "text", false); - } - } - } - } - - if (!$metadata["is_file"] && $currentNode != "") { - $metadata["icon"] = "imap_images/ICON_SIZE/mail_folder_sent.png"; - } - if (basename($currentNode) == "INBOX") { - $metadata["text"] = "Incoming Mails"; - } - if (strstr($currentNode, "__delim__")!==false) { - $parts = explode("/", $currentNode); - $metadata["text"] = str_replace("__delim__", "/", array_pop($parts)); - } - $ajxpNode->metadata = $metadata; - } - - public function attachmentDLName($currentNode, &$localName, $wrapperClassName) - { - $parsed = parse_url($currentNode); - if ( isSet($parsed["fragment"]) && strpos($parsed["fragment"], "attachments") === 0) { - list(, $attachmentId) = explode("/", $parsed["fragment"]); - $meta = imapAccessWrapper::getCurrentAttachmentsMetadata(); - if ($meta == null) { - stat($currentNode); - $meta = imapAccessWrapper::getCurrentAttachmentsMetadata(); - } - if ($meta != null) { - foreach ($meta as $attach) { - if ($attach["x-attachment-id"] == $attachmentId) { - $localName = $attach["filename"]; - } - } - } - } else { - $localName = basename($currentNode).".eml"; - } - } - - /** - * Parse - * @param DOMNode $contribNode - */ - protected function parseSpecificContributions(&$contribNode) - { - parent::parseSpecificContributions($contribNode); - if($contribNode->nodeName != "actions") return ; - $this->disableArchiveBrowsingContributions($contribNode); - } - - public function filterNodeName($nodePath, $nodeName, &$isLeaf, $lsOptions) - { - return true; - } - - public function countFiles($dirName, $foldersOnly = false, $nonEmptyCheckOnly = false, $dirHandle = null) - { - if($foldersOnly) return 0; - // WILL USE IMAP FUNCTIONS TO COUNT; - $tmpHandle = opendir($dirName); - $this->logDebug("COUNT : ".imapAccessWrapper::getCurrentDirCount()); - return imapAccessWrapper::getCurrentDirCount(); - } - -} diff --git a/core/src/plugins/access.imap/class.imapAccessWrapper.php b/core/src/plugins/access.imap/class.imapAccessWrapper.php deleted file mode 100755 index e2b9281353..0000000000 --- a/core/src/plugins/access.imap/class.imapAccessWrapper.php +++ /dev/null @@ -1,534 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -function rejectEmpty($element){return !empty($element);} - -/** - * AJXP_Plugin to browse a mailbox content (IMAP OR POP) - * @package AjaXplorer_Plugins - * @subpackage Access - */ -class imapAccessWrapper implements AjxpWrapper -{ - public $ih; - public $host; - public $port; - public $username; - public $password; - public $path; - public $pop3; - // stuff for dir reading - public $dir; - public $pos; - // stuff for file reading - public $data; - public $gotbody; - public $size; - public $time; - - public $fragment; - public $mailbox; - - public $mailboxes; - public $currentAttachmentData; - - - private static $currentStream; - private static $currentRef; - private static $currentCount; - private static $delimiter; - private static $attachmentsMetadata; - - public static function closeStreamFunc() - { - if (self::$currentStream) { - AJXP_Logger::debug("Closing stream now!"); - imap_close(self::$currentStream); - } - } - - public static function getCurrentDirCount() - { - return self::$currentCount; - } - - public static function getCurrentAttachmentsMetadata() - { - return self::$attachmentsMetadata; - } - - public function stream_open($path, $mode, $options, &$opened_path) - { - // parse URL - $parts = AJXP_Utils::safeParseUrl($path); - $this->repositoryId = $parts["host"]; - $mainCacheDir = (defined('AJXP_SHARED_CACHE_DIR')?AJXP_SHARED_CACHE_DIR:AJXP_CACHE_DIR); - if (!isset(self::$delimiter) && file_exists($mainCacheDir."/access.imap/mailbox_delim_".$this->repositoryId)) { - self::$delimiter = file_get_contents($mainCacheDir."/access.imap/mailbox_delim_".$this->repositoryId); - } - - $this->path = substr($parts["path"], 1); - //$this->mailbox = "INBOX"; - $pathParts = explode("/", $this->path); - $pathParts = array_filter($pathParts, "rejectEmpty"); - if (count($pathParts) > 1) { - $this->path = array_pop($pathParts); - $this->mailbox = implode("/", $pathParts); - } else if (count($pathParts) == 1) { - $this->mailbox = implode("/", $pathParts); - $this->path = ""; - } else { - $this->mailbox = ""; - $this->path = ""; - } - $this->fragment = $parts["fragment"]; - if (empty ( $this->path ) && $mode !== 'np') { - return false; - } - if (!empty($this->mailbox)) { - $this->mailbox = mb_convert_encoding($this->mailbox, "UTF7-IMAP", SystemTextEncoding::getEncoding()); - $this->mailbox = str_replace("__delim__", (isSet(self::$delimiter)?self::$delimiter:"/"), $this->mailbox); - } - if (!empty($this->fragment) && strpos($this->fragment, "attachments") === 0 && strpos($this->fragment, "/")!== false) { - // remove fragment - $ar = explode("#", $path); - $mailPath = array_shift($ar); - $ar = explode("/", $this->fragment); - $attachmentId = array_pop($ar); - $this->currentAttachmentData = array("realPath" => $mailPath, "attachmentId" => $attachmentId); - // EXTRACT ATTACHMENT AND RETURN - require_once AJXP_INSTALL_PATH."/plugins/editor.eml/class.EmlParser.php"; - $emlParser = new EmlParser("", ""); - $attachMeta = array(); - $this->data = $emlParser->getAttachmentBody( - $this->currentAttachmentData["realPath"], - $this->currentAttachmentData["attachmentId"], - true, - $attachMeta - ); - if (self::$attachmentsMetadata == null) { - self::$attachmentsMetadata = array($attachMeta); - } - - $this->currentAttachmentData["size"] = strlen($this->data); - $this->pos = 0; - $this->size = strlen($this->data); - return true; - } - - // open IMAP connection - if (self::$currentStream != null) { - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"Using currently opened stream! ".print_r(self::$currentStream, true)); - $this->ih = self::$currentStream; - // Rewind everything - $this->dir_rewinddir(); - $this->stream_seek(0); - } else { - $repository = ConfService::getRepositoryById($this->repositoryId); - $ssl = $repository->getOption("SSL") == "true" ? true: false ; - $this->pop3 = $repository->getOption("BOX_TYPE") == "pop3" ? true : false; - $this->host = $repository->getOption("HOST"); - $this->port = $repository->getOption("PORT"); - $this->username = $repository->getOption("USER"); - $this->password = $repository->getOption("PASS"); - $server = "{". $this->host . ":" . $this->port . "/".($this->pop3?"pop3/":"").($ssl?"ssl/novalidate-cert":"novalidate-cert")."}"; - self::$currentRef = $server; - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"Opening a new stream ".$server." with mailbox '".$this->mailbox."'"); - try { - $this->ih = imap_open ( $server.$this->mailbox , $this->username, $this->password, (!$this->pop3 && empty($this->mailbox)?OP_HALFOPEN:NULL), 1); - } catch (Exception $e) { - throw new Exception($e->getMessage()." - imap errors : ".print_r(imap_errors(), true), $e->getCode()); - } - self::$currentStream = $this->ih; - register_shutdown_function(array("imapAccessWrapper", "closeStreamFunc")); - } - if ($this->ih) { - if (! empty ( $this->path )) { - list ( $stats, ) = imap_fetch_overview ( $this->ih, $this->path ); - $this->size = $stats->size; - $this->time = strtotime ( $stats->date ); - } - return true; - } else { - return false; - } - } - - public function stream_close() - { - if (empty($this->mailbox)) { - //self::$currentStream = null; - //imap_close ( $this->ih ); - } - if (!empty($this->currentAttachmentData)) { - $this->currentAttachmentBody = null; - } - } - - /* Smart reader, at first it only downloads the header to memory, but if a read request is made - beyond the header, we download the rest of the body */ - public function stream_read($count) - { - //AJXP_Logger::debug(__CLASS__,__FUNCTION__,"READING $count FROM $this->path", $this->currentAttachmentData); - if (!empty($this->currentAttachmentData)) { - if (empty($this->data)) { - AJXP_Logger::debug(__CLASS__,__FUNCTION__,"Attachement", $this->currentAttachmentData); - // EXTRACT ATTACHMENT AND RETURN - require_once AJXP_INSTALL_PATH."/plugins/editor.eml/class.EmlParser.php"; - $emlParser = new EmlParser("", ""); - $attachMeta = array(); - $this->data = $emlParser->getAttachmentBody( - $this->currentAttachmentData["realPath"], - $this->currentAttachmentData["attachmentId"], - true, - $attachMeta - ); - $this->pos = 0; - $this->size = strlen($this->data); - } - } else { - // smart... only download the header WHEN data is requested - if (empty ( $this->data )) { - $this->pos = 0; - $this->gotbody = false; - $this->data = imap_fetchheader ( $this->ih, $this->path ); - } - // only download the body once we read past the header - if ($this->gotbody == false && ($this->pos + $count > strlen ( $this->data )) && $this->fragment != "header") { - $this->gotbody = true; - $this->data .= imap_body ( $this->ih, $this->path ); - $this->size = strlen ( $this->data ); - } - } - if ($this->pos >= $this->size) { - return false; - } else { - $d = substr ( $this->data, $this->pos, $count ); - if ($this->pos + $count > strlen ( $this->data )) { - $this->pos = strlen ( $this->data ); - } else { - $this->pos = $this->pos + $count; - } - return $d; - } - } - - /* Can't write to POP3 */ - public function stream_write($data) - { - return false; - } - - public function stream_eof() - { - if ($this->pos >= $this->size) { - return true; - } else { - return false; - } - } - - public function stream_tell() - { - return $this->pos; - } - - public function stream_seek($offset , $whence = SEEK_SET) - { - switch ($whence) { - case SEEK_SET : - $this->pos = $offset; - break; - case SEEK_CUR : - $this->pos = $this->pos + $offset; - break; - case SEEK_END : - $this->pos = $this->size + $offset; - break; - } - } - - public function stream_stat() - { - $keys = array('dev' => 0, 'ino' => 0, 'mode' => 33216, 'nlink' => 0, 'uid' => 0, 'gid' => 0, 'rdev' => 0, 'size' => $this->size, 'atime' => $this->time, 'mtime' => $this->time, 'ctime' => $this->time, 'blksize' => 0, 'blocks' => 0 ); - return $keys; - } - - public function dir_opendir($path, $options) - { - // Reset - self::$attachmentsMetadata = null; - - $st = ''; - $stream = $this->stream_open ( $path, 'np', $options, $st ); - if (!$stream) { - return false; - } - if (empty($this->mailbox)) { - // We are browsing root, we want the list of mailboxes - $this->mailboxes = imap_getmailboxes($this->ih, self::$currentRef, "*"); - $this->dir = count($this->mailboxes); - self::$currentCount = count($this->mailboxes); - $this->pos = $this->dir - 1; - } else if ($this->fragment == "attachments") { - require_once AJXP_INSTALL_PATH.'/plugins/editor.eml/class.EmlParser.php'; - $parser = new EmlParser("", ""); - $ar = explode("#", $path); - $path = array_shift($ar);// remove fragment - self::$attachmentsMetadata = array(); - $parser->listAttachments($path, true, self::$attachmentsMetadata); - $this->dir = count(self::$attachmentsMetadata); - $this->pos = $this->dir - 1; - self::$currentCount = $this->dir; - - } else { - // We are in a mailbox, we want the messages number - $this->dir = imap_num_msg ( $this->ih ); - self::$currentCount = $this->dir; - $this->pos = $this->dir; - } - $this->stream_close (); - return true; - } - - public function dir_closedir() - { - // do nothing. - // $this->stream_close(); - $this->mailboxes = null; - } - - public function dir_readdir() - { - if ($this->mailboxes) { - if($this->pos < 0) return false; - else{ - $obj = $this->mailboxes[$this->pos]; - $this->pos --; - $x = $obj->name; - $x = mb_convert_encoding( $x, "UTF-8", "UTF7-IMAP" ); - $x = str_replace(self::$currentRef, "", $x); - $mainCacheDir = (defined('AJXP_SHARED_CACHE_DIR')?AJXP_SHARED_CACHE_DIR:AJXP_CACHE_DIR); - if (!isSet(self::$delimiter) && !file_exists($mainCacheDir."/access.imap/mailbox_delim_".$this->repositoryId)) { - if(!is_dir($mainCacheDir ."/access.imap")) mkdir($mainCacheDir."/access.imap"); - file_put_contents($mainCacheDir."/access.imap/mailbox_delim_".$this->repositoryId, $obj->delimiter); - self::$delimiter = $obj->delimiter; - } - $x = str_replace($obj->delimiter, "__delim__", $x); - } - } else if (self::$attachmentsMetadata != null) { - if($this->pos < 0) return false; - $x = self::$attachmentsMetadata[$this->pos]["x-attachment-id"]; - $this->pos--; - } else { - if ($this->pos < 1) { - return false; - } else { - $x = $this->pos; - $this->pos --; - //$x .= "#header"; - } - } - return $x; - } - - public function dir_rewinddir() - { - if (empty($this->mailbox)) { - $this->pos = $this->dir; - } else { - $this->pos = count($this->mailboxes) - 1; - } - } - - public function url_stat($path, $flags) - { - $emptyString = ''; - if ($this->stream_open ( $path, 'np', $flags, $emptyString)) { - if (!empty($this->path) && empty($this->currentAttachmentData)) { - // Mail - //$stats = array(); - if (empty($this->size) && empty($this->time)) { - list ( $stats, ) = imap_fetch_overview ( $this->ih, $this->path ); - $this->size = $stats->size; - $this->time = strtotime ( $stats->date ); - } - $keys = array( - 'dev' => 0, - 'ino' => 0, - 'mode' => 33279, - 'nlink' => 0, - 'uid' => 0, - 'gid' => 0, - 'rdev' => 0, - 'size' => $this->size, - 'atime' => $this->time, - 'mtime' => $this->time, - 'ctime' => $this->time, - 'blksize' => 0, - 'blocks' => 0 ); - } else { - // BOX - if (empty($this->currentAttachmentData) && !empty($this->mailbox) && !$this->pop3) { - // GET LAST MESSAGE - imap_reopen($this->ih, self::$currentRef.$this->mailbox); - $last = imap_num_msg($this->ih); - //AJXP_Logger::debug(__CLASS__,__FUNCTION__,"Should get mailbox data ".self::$currentRef.$this->mailbox . $last); - list ( $stats, ) = imap_fetch_overview ( $this->ih, $last ); - $this->size = $stats->size; - $this->time = strtotime ( $stats->date ); - } - $keys = array( - 'dev' => 0, - 'ino' => 0, - 'mode' => (empty($this->currentAttachmentData)?(33279 | 0040000):33216), - 'nlink' => 0, - 'uid' => 0, - 'gid' => 0, - 'rdev' => 0, - 'size' => (!empty($this->currentAttachmentData)?$this->currentAttachmentData["size"]:0), - 'atime' => (!empty($this->time)?$this->time:0), - 'mtime' => (!empty($this->time)?$this->time:0), - 'ctime' => (!empty($this->time)?$this->time:0), - 'blksize' => 0, - 'blocks' => 0 - ); - } - $this->stream_close (); - return $keys; - } else { - return false; - } - } - - /* Delete an email from the mailbox */ - public function unlink($path) - { - $st=''; - if ($this->stream_open ( $path, '', '', $st )) { - imap_delete ( $this->ih, $this->path ); - $this->stream_close (); - return true; - } else { - return false; - } - } - - - public static function getRealFSReference($path, $persistent = false) - { - return $path; - } - - /** - * Read a file (by chunks) and copy the data directly inside the given stream. - * - * @param unknown_type $path - * @param unknown_type $stream - */ - public static function copyFileInStream($path, $stream) - { - //return $path; - $fp = fopen($path, 'r'); - $bufferSize = 4096 * 8; - if ($fp) { - $i = 0; - while (!feof($fp)) { - $data = fread($fp, $bufferSize); - fwrite($stream, $data, strlen($data)); - //if($i > 10) break; - //$i++; - } - fclose($fp); - } - } - - public static function isRemote() - { - return true; - } - - /** - * Chmod implementation for this type of access. - * - * @param unknown_type $path - * @param unknown_type $chmodValue - */ - public static function changeMode($path, $chmodValue) - { - } - - /** - * Enter description here... - * - * @param string $path - * @param int $mode - * @param int $options - * @return bool - */ - public function mkdir($path , $mode , $options) - { - } - - /** - * Enter description here... - * - * @param string $path_from - * @param string $path_to - * @return bool - */ - public function rename($path_from , $path_to) - { - } - - /** - * Enter description here... - * - * @param string $path - * @param int $options - * @return bool - */ - public function rmdir($path , $options) - { - } - - - /** - * Enter description here... - * - * @return bool - */ - public function stream_flush() - { - } - - - /** - * Describe whether the current wrapper can rewind a stream or not. - * @static - * @return boolean - */ - public static function isSeekable($url) - { - return false; - } -} diff --git a/core/src/plugins/access.imap/manifest.xml b/core/src/plugins/access.imap/manifest.xml index 316531fd5c..2e3b7015e2 100755 --- a/core/src/plugins/access.imap/manifest.xml +++ b/core/src/plugins/access.imap/manifest.xml @@ -19,7 +19,7 @@ - + @@ -27,12 +27,12 @@ - - + + - + diff --git a/core/src/plugins/access.imap/resources/i18n/conf/cs.php b/core/src/plugins/access.imap/resources/i18n/conf/cs.php index 598b321ba9..6749082328 100644 --- a/core/src/plugins/access.imap/resources/i18n/conf/cs.php +++ b/core/src/plugins/access.imap/resources/i18n/conf/cs.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Mailbox (Imap or POP)" => "Mailbox (IMAP nebo POP)", diff --git a/core/src/plugins/access.imap/resources/i18n/conf/de.php b/core/src/plugins/access.imap/resources/i18n/conf/de.php index 69a2b4aaae..16ff533ded 100644 --- a/core/src/plugins/access.imap/resources/i18n/conf/de.php +++ b/core/src/plugins/access.imap/resources/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Mailbox (Imap or POP)" => "Mailbox (Imap oder POP)", diff --git a/core/src/plugins/access.imap/resources/i18n/conf/en.php b/core/src/plugins/access.imap/resources/i18n/conf/en.php index e25c14c7ec..9f5059e919 100644 --- a/core/src/plugins/access.imap/resources/i18n/conf/en.php +++ b/core/src/plugins/access.imap/resources/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Mailbox (Imap or POP)" => "Mailbox (Imap or POP)", diff --git a/core/src/plugins/access.imap/resources/i18n/conf/fr.php b/core/src/plugins/access.imap/resources/i18n/conf/fr.php index af939b63cc..8faa4590e4 100644 --- a/core/src/plugins/access.imap/resources/i18n/conf/fr.php +++ b/core/src/plugins/access.imap/resources/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Mailbox (Imap or POP)" => "Boîte courriel (Imap ou POP)", diff --git a/core/src/plugins/access.imap/resources/i18n/conf/it.php b/core/src/plugins/access.imap/resources/i18n/conf/it.php index a1874cc201..0ca7ca3174 100644 --- a/core/src/plugins/access.imap/resources/i18n/conf/it.php +++ b/core/src/plugins/access.imap/resources/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Mailbox (Imap or POP)" => "Mailbox (IMAP o POP)", diff --git a/core/src/plugins/access.imap/resources/i18n/conf/pt.php b/core/src/plugins/access.imap/resources/i18n/conf/pt.php index 2b36a0a872..be80b73d9a 100644 --- a/core/src/plugins/access.imap/resources/i18n/conf/pt.php +++ b/core/src/plugins/access.imap/resources/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Mailbox (Imap or POP)" => "Caixa de Correio (Imap ou POP)", diff --git a/core/src/plugins/access.inbox/InboxAccessDriver.php b/core/src/plugins/access.inbox/InboxAccessDriver.php new file mode 100644 index 0000000000..523d3f05ba --- /dev/null +++ b/core/src/plugins/access.inbox/InboxAccessDriver.php @@ -0,0 +1,284 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Driver\StreamProvider\Inbox; + +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Filter\ContentFilter; +use Pydio\Access\Driver\StreamProvider\FS\FsAccessDriver; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Model\ContextInterface; + +use Pydio\Core\Controller\Controller; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Services\UsersService; +use Pydio\Core\Services\ApplicationState; +use Pydio\Core\Utils\Vars\StatHelper; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Driver for inbox repository + * Class inboxAccessDriver + * @package Pydio\Access\Driver\StreamProvider\Inbox + */ +class InboxAccessDriver extends FsAccessDriver +{ + private static $output; + + /** + * @param ContextInterface $contextInterface + * @throws PydioException + * @throws \Exception + */ + protected function initRepository(ContextInterface $contextInterface) + { + $this->urlBase = $contextInterface->getUrlBase(); + } + + /** + * Hook to node.info event + * @param AJXP_Node $ajxpNode + * @param bool $parentNode + * @param bool $details + */ + public function loadNodeInfo(&$ajxpNode, $parentNode = false, $details = false) + { + parent::loadNodeInfo($ajxpNode, $parentNode, $details); + if(!$ajxpNode->isRoot()){ + + // Retrieving stored details + $originalNode = self::$output[$ajxpNode->getLabel()]; + if(isSet($originalNode["meta"])){ + $meta = $originalNode["meta"]; + }else{ + $meta = []; + } + $label = $originalNode["label"]; + + if(!$ajxpNode->isLeaf()){ + $meta["icon"] = "mime_empty.png"; + } + + // Overriding display name with repository name + $ajxpNode->setLabel($label); + $ajxpNode->mergeMetadata($meta); + } + } + + /** + * Hook for load_repository_info event + * @param ContextInterface $ctx + * @param $data + */ + public function loadRepositoryInfo(ContextInterface $ctx, &$data){ + $allNodes = self::getNodes($ctx, false, false); + $data['access.inbox'] = [ + 'files' => count($allNodes) + ]; + } + + /** + * @param string $nodePath Url of a node + * @return array|mixed + */ + public static function getNodeData($nodePath){ + $nodeObject = new AJXP_Node($nodePath); + $basename = $nodeObject->getLabel(); + if($nodeObject->isRoot()){ + return ['stat' => stat(ApplicationState::getAjxpTmpDir())]; + } + $allNodes = self::getNodes($nodeObject->getContext(), false); + $nodeData = $allNodes[$basename]; + if(!isSet($nodeData["stat"])){ + if(in_array(pathinfo($basename, PATHINFO_EXTENSION), ["error", "invitation"])){ + $stat = stat(ApplicationState::getAjxpTmpDir()); + }else{ + $url = $nodeData["url"]; + $node = new AJXP_Node($url); + try{ + $node->getDriver()->detectStreamWrapper(true); + if($node->getRepository()->hasContentFilter()){ + $node->setLeaf(true); + } + Controller::applyHook("node.read", [&$node]); + $stat = stat($url); + }catch (\Exception $e){ + $stat = stat(ApplicationState::getAjxpTmpDir()); + } + if(is_array($stat) && $nodeObject->getContext()->hasUser()){ + $acl = $nodeObject->getContext()->getUser()->getMergedRole()->getAcl($nodeData["meta"]["shared_repository_id"]); + if($acl == "r"){ + self::disableWriteInStat($stat); + } + } + self::$output[$basename]["stat"] = $stat; + } + $nodeData["stat"] = $stat; + } + return $nodeData; + } + + /** + * @param ContextInterface $parentContext + * @param bool $checkStats + * @param bool $touch + * @return array + */ + public static function getNodes(ContextInterface $parentContext, $checkStats = false, $touch = true){ + + if(isSet(self::$output)){ + return self::$output; + } + + $mess = LocaleService::getMessages(); + $repos = UsersService::getRepositoriesForUser($parentContext->getUser()); + $userId = $parentContext->getUser()->getId(); + + $output = []; + $touchReposIds = []; + foreach($repos as $repo) { + if (!$repo->hasOwner() || !$repo->hasContentFilter()) { + continue; + } + $repoId = $repo->getId(); + +// DISABLE REMOTE SHARE FOR TESTING + /*if(strpos($repoId, "ocs_remote_share_") === 0){ + continue; + }*/ + + if(strpos($repoId, "ocs_remote_share_") !== 0){ + $touchReposIds[] = $repoId; + } + + $url = "pydio://".$userId . "@" . $repoId . "/"; + $meta = [ + "shared_repository_id" => $repoId, + "ajxp_description" => "File shared by ".$repo->getOwner(). " ". StatHelper::relativeDate($repo->getSafeOption("CREATION_TIME"), $mess), + "share_meta_type" => 1 + ]; + + $cFilter = $repo->getContentFilter(); + $filter = ($cFilter instanceof ContentFilter) ? array_keys($cFilter->filters)[0] : $cFilter; + if (!is_array($filter)) { + $label = basename($filter); + }else{ + $label = $repo->getDisplay(); + } + if(strpos($repoId, "ocs_remote_share") !== 0){ + // FOR REMOTE SHARES, DO NOT APPEND THE DOCUMENTNAME, WE STAT THE ROOT DIRECTLY + $url .= $label; + } + + $status = null; + $remoteShare = null; + $name = pathinfo($label, PATHINFO_FILENAME); + $ext = pathinfo($label, PATHINFO_EXTENSION); + + $node = new AJXP_Node($url); + $node->setLabel($label); + + if($checkStats){ + + try{ + $node->getDriver()->detectStreamWrapper(true); + }catch (\Exception $e){ + $ext = "error"; + $meta["ajxp_mime"] = "error"; + } + + $stat = @stat($url); + if($stat === false){ + $ext = "error"; + $meta["ajxp_mime"] = "error"; + $meta["share_meta_type"] = 2; + }else if(strpos($repoId, "ocs_remote_share_") === 0){ + // Check Status + $linkId = str_replace("ocs_remote_share_", "", $repoId); + $ocsStore = new \Pydio\OCS\Model\SQLStore(); + $remoteShare = $ocsStore->remoteShareById($linkId); + $status = $remoteShare->getStatus(); + if($status == OCS_INVITATION_STATUS_PENDING){ + $stat = stat(ApplicationState::getAjxpTmpDir()); + $ext = "invitation"; + $meta["ajxp_mime"] = "invitation"; + $meta["share_meta_type"] = 0; + } else { + $meta["remote_share_accepted"] = "true"; + } + $meta["remote_share_id"] = $remoteShare->getId(); + } + if($ext == "invitation"){ + $label .= " (".$mess["inbox_driver.4"].")"; + }else if($ext == "error"){ + $label .= " (".$mess["inbox_driver.5"].")"; + } + if(is_array($stat) && $parentContext->hasUser()){ + $acl = $parentContext->getUser()->getMergedRole()->getAcl($repoId); + if($acl == "r"){ + self::disableWriteInStat($stat); + } + + } + + } + + $index = 0;$suffix = ""; + while(isSet($output[$name.$suffix.".".$ext])){ + $index ++; + $suffix = " ($index)"; + } + $output[$name.$suffix.".".$ext] = [ + "label" => $label, + "url" => $url, + "remote_share" => $remoteShare, + "meta" => $meta + ]; + if(isset($stat)){ + $output[$name.$suffix.".".$ext]['stat'] = $stat; + } + } + self::$output = $output; + + if ($touch) { + if (count($touchReposIds) && $parentContext->hasUser()) { + $uPref = $parentContext->getUser()->getPref("repository_last_connected"); + if (empty($uPref)) $uPref = []; + foreach ($touchReposIds as $rId) { + $uPref[$rId] = time(); + } + $parentContext->getUser()->setPref("repository_last_connected", $uPref); + } + } + return $output; + } + + /** + * @param array $stat + */ + protected static function disableWriteInStat(&$stat){ + $octRights = decoct($stat["mode"]); + $last = (strlen($octRights)) - 1; + $octRights[$last] = $octRights[$last-1] = $octRights[$last-2] = 5; + $stat["mode"] = $stat[2] = octdec($octRights); + } + +} diff --git a/core/src/plugins/access.inbox/InboxAccessWrapper.php b/core/src/plugins/access.inbox/InboxAccessWrapper.php new file mode 100644 index 0000000000..213fa29b7d --- /dev/null +++ b/core/src/plugins/access.inbox/InboxAccessWrapper.php @@ -0,0 +1,438 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Driver\StreamProvider\Inbox; + +use ArrayIterator; +use Pydio\Access\Core\MetaStreamWrapper; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\IAjxpWrapper; + +use Pydio\Core\Services\ApplicationState; + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Class inboxAccessWrapper + * Stream wrapper for Inbox repository + * @package Pydio\Access\Driver\StreamProvider\Inbox + */ +class InboxAccessWrapper implements IAjxpWrapper +{ + /** + * @var ArrayIterator + */ + private $nodesIterator; + + /** + * @var Resource + */ + private $fp; + /** + * @var AJXP_Node + */ + private static $linkNode; + + + /** + * Support for opendir(). + * + * @param string $path The path to the directory + * @param string $options Whether or not to enforce safe_mode (0x04). Unused. + * + * @return bool true on success + * @see http://www.php.net/manual/en/function.opendir.php + */ + public function dir_opendir($path, $options) + { + $node = new AJXP_Node($path); + if($node->isRoot()){ + $this->nodesIterator = new ArrayIterator(InboxAccessDriver::getNodes($node->getContext(), true)); + }else{ + $this->nodesIterator = new ArrayIterator([]); + } + + + return true; + } + + /** + * Close the directory listing handles + * + * @return bool true on success + */ + public function dir_closedir() + { + $this->nodesIterator = null; + + return true; + } + + /** + * This method is called in response to rewinddir() + * + * @return boolean true on success + */ + public function dir_rewinddir() + { + $this->nodesIterator->rewind(); + + return true; + } + + /** + * This method is called in response to readdir() + * + * @return string Should return a string representing the next filename, or false if there is no next file. + * + * @link http://www.php.net/manual/en/function.readdir.php + */ + public function dir_readdir() + { + // Skip empty result keys + if (!$this->nodesIterator->valid()) { + return false; + } + + $key = $this->nodesIterator->key(); + $current = $this->nodesIterator->current(); + + $this->nodesIterator->next(); + + return $key; + } + + /** + * @param $path + * @return mixed|null|string + * @throws \Exception + */ + public static function translateURL($path){ + + $pydioScheme = false; + self::$linkNode = null; + $initialNode = new AJXP_Node($path); + + $nodes = InboxAccessDriver::getNodes($initialNode->getContext(), false); + $nodePath = basename($initialNode->getPath()); + $node = $nodes[ltrim($nodePath, '/')]; + + if (empty($node) || ! isset($node['url'])) { + return ApplicationState::getAjxpTmpDir(); + } + + $url = $node['url']; + $label = $node['label']; + + $scheme = parse_url($url, PHP_URL_SCHEME); + if($scheme == "pydio"){ + self::$linkNode = new AJXP_Node($path); + $pydioScheme = true; + } + + if(empty($nodePath)){ + return ApplicationState::getAjxpTmpDir(); + } + + if($pydioScheme){ + $node = new AJXP_Node($url); + $node->setLabel($label); + $node->getRepository()->driverInstance = null; + try{ + $node->getDriver(); + }catch (\Exception $e){ + + } + MetaStreamWrapper::detectWrapperForNode($node, true); + } + return $url; + } + + /** + * Get a "usable" reference to a file : the real file or a tmp copy. + * + * @param string $path + * @param bool $persistent + * @return string + */ + public static function getRealFSReference($path, $persistent = false) + { + $url = self::translateURL($path); + if(self::$linkNode !== null){ + $isRemote = MetaStreamWrapper::wrapperIsRemote($url); + $realFilePointer = MetaStreamWrapper::getRealFSReference($url, true); + if(!$isRemote){ + $ext = pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION); + $tmpname = tempnam(ApplicationState::getAjxpTmpDir(), "real-file-inbox-pointer").".".$ext; + copy($realFilePointer, $tmpname); + $realFilePointer = $tmpname; + } + self::$linkNode->getDriver(); + return $realFilePointer; + }else{ + $tmpname = tempnam(ApplicationState::getAjxpTmpDir(), "real-file-inbox-pointer"); + $source = fopen($url, "r"); + $dest = fopen($tmpname, "w"); + stream_copy_to_stream($source, $dest); + return $tmpname; + } + + } + + /** + * Read a file (by chunks) and copy the data directly inside the given stream. + * + * @param string $path + * @param resource $stream + */ + public static function copyFileInStream($path, $stream) + { + $url = self::translateURL($path); + MetaStreamWrapper::copyFileInStream($url, $stream); + if(self::$linkNode !== null){ + self::$linkNode->getDriver(); + } + } + + /** + * Chmod implementation for this type of access. + * + * @param string $path + * @param number $chmodValue + */ + public static function changeMode($path, $chmodValue){ + + } + + /** + * Describe whether the current wrapper operates on a remote server or not. + * @static + * @return boolean + */ + public static function isRemote() + { + return true; + } + + /** + * Describe whether the current wrapper can rewind a stream or not. + * @param String $url Url of the resource + * @static + * @return boolean + */ + public static function isSeekable($url) + { + return true; + } + + /** + * Enter description here... + * + * @param string $path + * @param int $mode + * @param int $options + * @return bool + */ + public function mkdir($path, $mode, $options) + { + return false; + } + + /** + * Enter description here... + * + * @param string $path_from + * @param string $path_to + * @return bool + */ + public function rename($path_from, $path_to) + { + return false; + } + + /** + * Enter description here... + * + * @param string $path + * @param int $options + * @return bool + */ + public function rmdir($path, $options) + { + return false; + } + + /** + * Enter description here... + * + */ + public function stream_close() + { + if($this->fp !== null){ + fclose($this->fp); + if(self::$linkNode !== null){ + self::$linkNode->getDriver(); + } + } + } + + /** + * Enter description here... + * + * @return bool + */ + public function stream_eof() + { + if(is_resource($this->fp)){ + return feof($this->fp); + } + return false; + } + + /** + * Enter description here... + * + * @return bool + */ + public function stream_flush() + { + if($this->fp !== null){ + return fflush($this->fp); + } + return null; + } + + /** + * Enter description here... + * + * @param string $path + * @param string $mode + * @param int $options + * @param string &$opened_path + * @return bool + */ + public function stream_open($path, $mode, $options, &$opened_path) + { + $this->fp = fopen(self::translateURL($path), $mode, $options); + return true; + } + + /** + * Enter description here... + * + * @param int $count + * @return string + */ + public function stream_read($count) + { + if($this->fp !== null){ + return fread($this->fp, $count); + } + return null; + } + + /** + * Enter description here... + * + * @param int $offset + * @param int $whence = SEEK_SET + * @return bool + */ + public function stream_seek($offset, $whence = SEEK_SET) + { + if($this->fp !== null){ + return fseek($this->fp, $offset, $whence); + } + return null; + } + + /** + * Enter description here... + * + * @return array + */ + public function stream_stat() + { + if($this->fp !== null){ + return fstat($this->fp); + } + return false; + } + + /** + * Enter description here... + * + * @return int + */ + public function stream_tell() + { + if($this->fp !== null){ + return ftell($this->fp); + } + return null; + } + + /** + * Enter description here... + * + * @param string $data + * @return int + */ + public function stream_write($data) + { + if($this->fp !== null){ + return fwrite($this->fp, $data); + } + return 0; + } + + /** + * Enter description here... + * + * @param string $path + * @return bool + */ + public function unlink($path) + { + // Do nothing + } + + /** + * Enter description here... + * + * @param string $path + * @param int $flags + * @return array + */ + public function url_stat($path, $flags) + { + $nodeData = InboxAccessDriver::getNodeData($path); + return $nodeData["stat"]; + } + + /** + * @param AJXP_Node $node + * @return array + */ + public static function getResolvedOptionsForNode($node){ + return ["TYPE" => "php"]; + } +} \ No newline at end of file diff --git a/core/src/plugins/access.inbox/class.inboxAccessDriver.php b/core/src/plugins/access.inbox/class.inboxAccessDriver.php deleted file mode 100644 index 99de9295d1..0000000000 --- a/core/src/plugins/access.inbox/class.inboxAccessDriver.php +++ /dev/null @@ -1,240 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die('Access not allowed'); - -class inboxAccessDriver extends fsAccessDriver -{ - private static $output; - private static $stats; - - public function initRepository() - { - $this->detectStreamWrapper(true); - $this->urlBase = "pydio://".$this->repository->getId(); - } - - public function loadNodeInfo(&$ajxpNode, $parentNode = false, $details = false) - { - parent::loadNodeInfo($ajxpNode, $parentNode, $details); - if(!$ajxpNode->isRoot()){ - - // Retrieving stored details - $originalNode = self::$output[$ajxpNode->getLabel()]; - if(isSet($originalNode["meta"])){ - $meta = $originalNode["meta"]; - }else{ - $meta = array(); - } - $label = $originalNode["label"]; - - if(!$ajxpNode->isLeaf()){ - $meta["icon"] = "mime_empty.png"; - } - - // Overriding display name with repository name - $ajxpNode->setLabel($label); - $ajxpNode->mergeMetadata($meta); - } - } - - public function loadRepositoryInfo(&$data){ - $allNodes = self::getNodes(false, false); - $data['access.inbox'] = array( - 'files' => count($allNodes) - ); - } - - public static function getNodeData($nodePath){ - $basename = basename(parse_url($nodePath, PHP_URL_PATH)); - if(empty($basename)){ - return ['stat' => stat(AJXP_Utils::getAjxpTmpDir())]; - } - $allNodes = self::getNodes(false); - $nodeData = $allNodes[$basename]; - if(!isSet($nodeData["stat"])){ - if(in_array(pathinfo($basename, PATHINFO_EXTENSION), array("error", "invitation"))){ - $stat = stat(AJXP_Utils::getAjxpTmpDir()); - }else{ - $url = $nodeData["url"]; - $node = new AJXP_Node($nodeData["url"]); - $node->getRepository()->driverInstance = null; - try{ - ConfService::loadDriverForRepository($node->getRepository()); - $node->getRepository()->detectStreamWrapper(true); - if($node->getRepository()->hasContentFilter()){ - $node->setLeaf(true); - } - AJXP_Controller::applyHook("node.read", array(&$node)); - $stat = stat($url); - }catch (Exception $e){ - $stat = stat(AJXP_Utils::getAjxpTmpDir()); - } - if(is_array($stat) && AuthService::getLoggedUser() != null){ - $acl = AuthService::getLoggedUser()->mergedRole->getAcl($nodeData["meta"]["shared_repository_id"]); - if($acl == "r"){ - self::disableWriteInStat($stat); - } - } - self::$output[$basename]["stat"] = $stat; - } - $nodeData["stat"] = $stat; - } - return $nodeData; - } - - public static function getNodes($checkStats = false, $touch = true){ - - if(isSet(self::$output)){ - return self::$output; - } - - $mess = ConfService::getMessages(); - $repos = ConfService::getAccessibleRepositories(); - - $output = array(); - $touchReposIds = array(); - foreach($repos as $repo) { - if (!$repo->hasOwner() || !$repo->hasContentFilter()) { - continue; - } - $repoId = $repo->getId(); - - if(strpos("ocs_remote_share_", $repoId) !== 0){ - $touchReposIds[] = $repoId; - } - - $url = "pydio://" . $repoId . "/"; - $meta = array( - "shared_repository_id" => $repoId, - "ajxp_description" => "File shared by ".$repo->getOwner(). " ". AJXP_Utils::relativeDate($repo->getOption("CREATION_TIME"), $mess), - "share_meta_type" => 1 - ); - - $cFilter = $repo->getContentFilter(); - $filter = ($cFilter instanceof ContentFilter) ? array_keys($cFilter->filters)[0] : $cFilter; - if (!is_array($filter)) { - $label = basename($filter); - }else{ - $label = $repo->getDisplay(); - } - if(strpos($repoId, "ocs_remote_share") !== 0){ - // FOR REMOTE SHARES, DO NOT APPEND THE DOCUMENTNAME, WE STAT THE ROOT DIRECTLY - $url .= $label; - } - - $status = null; - $remoteShare = null; - $name = pathinfo($label, PATHINFO_FILENAME); - $ext = pathinfo($label, PATHINFO_EXTENSION); - - $node = new AJXP_Node($url); - $node->setLabel($label); - - if($checkStats){ - - $node->getRepository()->driverInstance = null; - try{ - ConfService::loadDriverForRepository($node->getRepository()); - }catch (Exception $e){ - $ext = "error"; - $meta["ajxp_mime"] = "error"; - } - $node->getRepository()->detectStreamWrapper(true); - - $stat = @stat($url); - if($stat === false){ - $ext = "error"; - $meta["ajxp_mime"] = "error"; - $meta["share_meta_type"] = 2; - }else if(strpos($repoId, "ocs_remote_share_") === 0){ - // Check Status - $linkId = str_replace("ocs_remote_share_", "", $repoId); - $ocsStore = new \Pydio\OCS\Model\SQLStore(); - $remoteShare = $ocsStore->remoteShareById($linkId); - $status = $remoteShare->getStatus(); - if($status == OCS_INVITATION_STATUS_PENDING){ - $stat = stat(AJXP_Utils::getAjxpTmpDir()); - $ext = "invitation"; - $meta["ajxp_mime"] = "invitation"; - $meta["share_meta_type"] = 0; - } else { - $meta["remote_share_accepted"] = "true"; - } - $meta["remote_share_id"] = $remoteShare->getId(); - } - if($ext == "invitation"){ - $label .= " (".$mess["inbox_driver.4"].")"; - }else if($ext == "error"){ - $label .= " (".$mess["inbox_driver.5"].")"; - } - if(is_array($stat) && AuthService::getLoggedUser() != null){ - $acl = AuthService::getLoggedUser()->mergedRole->getAcl($repoId); - if($acl == "r"){ - self::disableWriteInStat($stat); - } - - } - - } - - $index = 0;$suffix = ""; - while(isSet($output[$name.$suffix.".".$ext])){ - $index ++; - $suffix = " ($index)"; - } - $output[$name.$suffix.".".$ext] = [ - "label" => $label, - "url" => $url, - "remote_share" => $remoteShare, - "meta" => $meta - ]; - if(isset($stat)){ - $output[$name.$suffix.".".$ext]['stat'] = $stat; - } - } - ConfService::loadDriverForRepository(ConfService::getRepository()); - self::$output = $output; - - if ($touch) { - if (count($touchReposIds) && AuthService::getLoggedUser() != null) { - $uPref = AuthService::getLoggedUser()->getPref("repository_last_connected"); - if (empty($uPref)) $uPref = array(); - foreach ($touchReposIds as $rId) { - $uPref[$rId] = time(); - } - AuthService::getLoggedUser()->setPref("repository_last_connected", $uPref); - } - } - return $output; - } - - /** - * @param array $stat - */ - protected static function disableWriteInStat(&$stat){ - $octRights = decoct($stat["mode"]); - $last = (strlen($octRights)) - 1; - $octRights[$last] = $octRights[$last-1] = $octRights[$last-2] = 5; - $stat["mode"] = $stat[2] = octdec($octRights); - } - -} diff --git a/core/src/plugins/access.inbox/class.inboxAccessWrapper.php b/core/src/plugins/access.inbox/class.inboxAccessWrapper.php deleted file mode 100644 index 11db543ccf..0000000000 --- a/core/src/plugins/access.inbox/class.inboxAccessWrapper.php +++ /dev/null @@ -1,413 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die('Access not allowed'); - - -class inboxAccessWrapper implements AjxpWrapper -{ - private $nodesIterator; - - /** - * @var Resource - */ - private $fp; - /** - * @var AJXP_Node - */ - private static $linkNode; - - - /** - * Support for opendir(). - * - * @param string $path The path to the directory - * @param string $options Whether or not to enforce safe_mode (0x04). Unused. - * - * @return bool true on success - * @see http://www.php.net/manual/en/function.opendir.php - */ - public function dir_opendir($path, $options) - { - if(trim(parse_url($path, PHP_URL_PATH), "/") == ""){ - $this->nodesIterator = new ArrayIterator(inboxAccessDriver::getNodes(true)); - }else{ - $this->nodesIterator = new ArrayIterator([]); - } - - - return true; - } - - /** - * Close the directory listing handles - * - * @return bool true on success - */ - public function dir_closedir() - { - $this->nodesIterator = null; - - return true; - } - - /** - * This method is called in response to rewinddir() - * - * @return boolean true on success - */ - public function dir_rewinddir() - { - $this->nodesIterator->rewind(); - - return true; - } - - /** - * This method is called in response to readdir() - * - * @return string Should return a string representing the next filename, or false if there is no next file. - * - * @link http://www.php.net/manual/en/function.readdir.php - */ - public function dir_readdir() - { - // Skip empty result keys - if (!$this->nodesIterator->valid()) { - return false; - } - - $key = $this->nodesIterator->key(); - $current = $this->nodesIterator->current(); - - $this->nodesIterator->next(); - - return $key; - } - - public static function translateURL($path){ - - $pydioScheme = false; - self::$linkNode = null; - - $nodes = inboxAccessDriver::getNodes(false); - $nodePath = basename(parse_url($path, PHP_URL_PATH)); - $node = $nodes[ltrim($nodePath, '/')]; - - if (empty($node) || ! isset($node['url'])) { - return AJXP_Utils::getAjxpTmpDir(); - } - - $url = $node['url']; - $label = $node['label']; - - $scheme = parse_url($url, PHP_URL_SCHEME); - if($scheme == "pydio"){ - self::$linkNode = new AJXP_Node($path); - $pydioScheme = true; - } - - if(empty($nodePath)){ - return AJXP_Utils::getAjxpTmpDir(); - } - - if($pydioScheme){ - $node = new AJXP_Node($url); - $node->setLabel($label); - $node->getRepository()->driverInstance = null; - try{ - ConfService::loadDriverForRepository($node->getRepository()); - }catch (Exception $e){ - - } - $node->getRepository()->detectStreamWrapper(true); - } - return $url; - } - - /** - * Get a "usable" reference to a file : the real file or a tmp copy. - * - * @param string $path - * @param bool $persistent - * @return string - */ - public static function getRealFSReference($path, $persistent = false) - { - $url = self::translateURL($path); - if(self::$linkNode !== null){ - $isRemote = AJXP_MetaStreamWrapper::wrapperIsRemote($url); - $realFilePointer = AJXP_MetaStreamWrapper::getRealFSReference($url, true); - if(!$isRemote){ - $ext = pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION); - $tmpname = tempnam(AJXP_Utils::getAjxpTmpDir(), "real-file-inbox-pointer").".".$ext; - copy($realFilePointer, $tmpname); - $realFilePointer = $tmpname; - } - ConfService::loadDriverForRepository(self::$linkNode->getRepository()); - return $realFilePointer; - }else{ - $tmpname = tempnam(AJXP_Utils::getAjxpTmpDir(), "real-file-inbox-pointer"); - $source = fopen($url, "r"); - $dest = fopen($tmpname, "w"); - stream_copy_to_stream($source, $dest); - return $tmpname; - } - - } - - /** - * Read a file (by chunks) and copy the data directly inside the given stream. - * - * @param string $path - * @param resource $stream - */ - public static function copyFileInStream($path, $stream) - { - $url = self::translateURL($path); - AJXP_MetaStreamWrapper::copyFileInStream($url, $stream); - /* - $wrapperClass = AJXP_MetaStreamWrapper::actualRepositoryWrapperClass(parse_url($url, PHP_URL_HOST)); - call_user_func(array($wrapperClass, "copyFileInStream"), $url, $stream); - */ - if(self::$linkNode !== null){ - ConfService::loadDriverForRepository(self::$linkNode->getRepository()); - } - } - - /** - * Chmod implementation for this type of access. - * - * @param string $path - * @param number $chmodValue - */ - public static function changeMode($path, $chmodValue) - { - // TODO: Implement changeMode() method. - } - - /** - * Describe whether the current wrapper operates on a remote server or not. - * @static - * @return boolean - */ - public static function isRemote() - { - return true; - } - - /** - * Describe whether the current wrapper can rewind a stream or not. - * @param String $url Url of the resource - * @static - * @return boolean - */ - public static function isSeekable($url) - { - return true; - } - - /** - * Enter description here... - * - * @param string $path - * @param int $mode - * @param int $options - * @return bool - */ - public function mkdir($path, $mode, $options) - { - return false; - } - - /** - * Enter description here... - * - * @param string $path_from - * @param string $path_to - * @return bool - */ - public function rename($path_from, $path_to) - { - return false; - } - - /** - * Enter description here... - * - * @param string $path - * @param int $options - * @return bool - */ - public function rmdir($path, $options) - { - return false; - } - - /** - * Enter description here... - * - */ - public function stream_close() - { - if($this->fp !== null){ - fclose($this->fp); - if(self::$linkNode !== null){ - ConfService::loadDriverForRepository(self::$linkNode->getRepository()); - } - } - } - - /** - * Enter description here... - * - * @return bool - */ - public function stream_eof() - { - if(is_resource($this->fp)){ - return feof($this->fp); - } - return false; - } - - /** - * Enter description here... - * - * @return bool - */ - public function stream_flush() - { - if($this->fp !== null){ - return fflush($this->fp); - } - return null; - } - - /** - * Enter description here... - * - * @param string $path - * @param string $mode - * @param int $options - * @param string &$opened_path - * @return bool - */ - public function stream_open($path, $mode, $options, &$opened_path) - { - $this->fp = fopen(self::translateURL($path), $mode, $options); - return true; - } - - /** - * Enter description here... - * - * @param int $count - * @return string - */ - public function stream_read($count) - { - if($this->fp !== null){ - return fread($this->fp, $count); - } - return null; - } - - /** - * Enter description here... - * - * @param int $offset - * @param int $whence = SEEK_SET - * @return bool - */ - public function stream_seek($offset, $whence = SEEK_SET) - { - if($this->fp !== null){ - return fseek($this->fp, $offset, $whence); - } - return null; - } - - /** - * Enter description here... - * - * @return array - */ - public function stream_stat() - { - if($this->fp !== null){ - return fstat($this->fp); - } - return false; - } - - /** - * Enter description here... - * - * @return int - */ - public function stream_tell() - { - if($this->fp !== null){ - return ftell($this->fp); - } - return null; - } - - /** - * Enter description here... - * - * @param string $data - * @return int - */ - public function stream_write($data) - { - if($this->fp !== null){ - return fwrite($this->fp, $data); - } - return 0; - } - - /** - * Enter description here... - * - * @param string $path - * @return bool - */ - public function unlink($path) - { - // TODO: Implement unlink() method. - } - - /** - * Enter description here... - * - * @param string $path - * @param int $flags - * @return array - */ - public function url_stat($path, $flags) - { - $nodeData = inboxAccessDriver::getNodeData($path); - return $nodeData["stat"]; - } - -} \ No newline at end of file diff --git a/core/src/plugins/access.inbox/manifest.xml b/core/src/plugins/access.inbox/manifest.xml index 976274c7ce..9780775fef 100644 --- a/core/src/plugins/access.inbox/manifest.xml +++ b/core/src/plugins/access.inbox/manifest.xml @@ -1,6 +1,6 @@ - + @@ -223,5 +223,5 @@ - + diff --git a/core/src/plugins/access.inbox/package.json b/core/src/plugins/access.inbox/package.json index 4471b0274c..8f88997329 100644 --- a/core/src/plugins/access.inbox/package.json +++ b/core/src/plugins/access.inbox/package.json @@ -1,6 +1,6 @@ { - "name": "action.share", - "version": "6.2.3", + "name": "access.inbox", + "version": "6.5.1", "description": "", "source_path":"res/react", "main": "index.js", diff --git a/core/src/plugins/access.jsapi/JsapiAccessDriver.php b/core/src/plugins/access.jsapi/JsapiAccessDriver.php new file mode 100644 index 0000000000..65cee1e5d4 --- /dev/null +++ b/core/src/plugins/access.jsapi/JsapiAccessDriver.php @@ -0,0 +1,94 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Driver\DataProvider; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Utils\Vars\InputFilter; + +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Plugin to send a javascript source to the browser + */ +class JsapiAccessDriver extends AbstractAccessDriver +{ + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + */ + public function switchAction(ServerRequestInterface &$request, ResponseInterface &$response) + { + if($request->getAttribute("action") !== "get_js_source"){ + return; + } + + $jsName = InputFilter::decodeSecureMagic($request->getParsedBody()["object_name"]); + $jsType = $request->getParsedBody()["object_type"]; // class or interface? + $fName = "class.".strtolower($jsName).".js"; + if ($jsName == "Splitter") { + $fName = "splitter.js"; + } + if (!defined("CLIENT_RESOURCES_FOLDER")) { + define("CLIENT_RESOURCES_FOLDER", AJXP_PLUGINS_FOLDER."/gui.ajax/res"); + } + $searchLocations = []; + // Locate the file class.ClassName.js + if ($jsType == "class") { + $searchLocations = array( + CLIENT_RESOURCES_FOLDER."/js/ajaxplorer", + CLIENT_RESOURCES_FOLDER."/js/lib", + AJXP_INSTALL_PATH."/plugins/" + ); + } else if ($jsType == "interface") { + $searchLocations = array( + CLIENT_RESOURCES_FOLDER."/js/ajaxplorer/interfaces", + ); + } + foreach ($searchLocations as $location) { + $dir_iterator = new RecursiveDirectoryIterator($location); + $iterator = new RecursiveIteratorIterator($dir_iterator, RecursiveIteratorIterator::SELF_FIRST); + // could use CHILD_FIRST if you so wish + $break = false; + foreach ($iterator as $file) { + if (strtolower(basename($file->getPathname())) == $fName) { + $response = $response->withHeader("Content-Type", "text/plain"); + $response->getBody()->write(file_get_contents($file->getPathname())); + $break = true; + break; + } + } + if($break) break; + } + + } + + /** + * @param ContextInterface $ctx + */ + protected function initRepository(ContextInterface $ctx) { + } +} diff --git a/core/src/plugins/access.jsapi/class.JsSourceViewer.js b/core/src/plugins/access.jsapi/class.JsSourceViewer.js index 67957a97c5..a9ddbdb3cf 100644 --- a/core/src/plugins/access.jsapi/class.JsSourceViewer.js +++ b/core/src/plugins/access.jsapi/class.JsSourceViewer.js @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . * Description : A dynamic panel displaying details on the current selection. Works with Templates. */ Class.create("JsSourceViewer", AjxpPane, { diff --git a/core/src/plugins/access.jsapi/class.LocalAPINodeProvider.js b/core/src/plugins/access.jsapi/class.LocalAPINodeProvider.js index df60ecc989..43d47b6f36 100644 --- a/core/src/plugins/access.jsapi/class.LocalAPINodeProvider.js +++ b/core/src/plugins/access.jsapi/class.LocalAPINodeProvider.js @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ /** * A local implementation that explore currently defined diff --git a/core/src/plugins/access.jsapi/class.jsapiAccessDriver.php b/core/src/plugins/access.jsapi/class.jsapiAccessDriver.php deleted file mode 100644 index f9e808dde7..0000000000 --- a/core/src/plugins/access.jsapi/class.jsapiAccessDriver.php +++ /dev/null @@ -1,75 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * AJXP_Plugin to send a javascript source to the browser - * @package AjaXplorer_Plugins - * @subpackage Access - */ -class jsapiAccessDriver extends AbstractAccessDriver -{ - public function switchAction($action, $httpVars, $fileVars) - { - switch ($action) { - case "get_js_source" : - $jsName = AJXP_Utils::decodeSecureMagic($httpVars["object_name"]); - $jsType = $httpVars["object_type"]; // class or interface? - $fName = "class.".strtolower($jsName).".js"; - if ($jsName == "Splitter") { - $fName = "splitter.js"; - } - if (!defined("CLIENT_RESOURCES_FOLDER")) { - define("CLIENT_RESOURCES_FOLDER", AJXP_PLUGINS_FOLDER."/gui.ajax/res"); - } - // Locate the file class.ClassName.js - if ($jsType == "class") { - $searchLocations = array( - CLIENT_RESOURCES_FOLDER."/js/ajaxplorer", - CLIENT_RESOURCES_FOLDER."/js/lib", - AJXP_INSTALL_PATH."/plugins/" - ); - } else if ($jsType == "interface") { - $searchLocations = array( - CLIENT_RESOURCES_FOLDER."/js/ajaxplorer/interfaces", - ); - } - foreach ($searchLocations as $location) { - $dir_iterator = new RecursiveDirectoryIterator($location); - $iterator = new RecursiveIteratorIterator($dir_iterator, RecursiveIteratorIterator::SELF_FIRST); - // could use CHILD_FIRST if you so wish - $break = false; - foreach ($iterator as $file) { - if (strtolower(basename($file->getPathname())) == $fName) { - HTMLWriter::charsetHeader("text/plain", "utf-8"); - echo(file_get_contents($file->getPathname())); - $break = true; - break; - } - } - if($break) break; - } - break; - } - - } - -} diff --git a/core/src/plugins/access.jsapi/i18n/conf/cs.php b/core/src/plugins/access.jsapi/i18n/conf/cs.php index 802050e514..923f0c3198 100644 --- a/core/src/plugins/access.jsapi/i18n/conf/cs.php +++ b/core/src/plugins/access.jsapi/i18n/conf/cs.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Javascript Api Browser" => "Javascript API prohlížeč", diff --git a/core/src/plugins/access.jsapi/i18n/conf/de.php b/core/src/plugins/access.jsapi/i18n/conf/de.php index 0830899d72..bdc3827d6d 100644 --- a/core/src/plugins/access.jsapi/i18n/conf/de.php +++ b/core/src/plugins/access.jsapi/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Javascript Api Browser" => "Javascript-Api Browser", diff --git a/core/src/plugins/access.jsapi/i18n/conf/en.php b/core/src/plugins/access.jsapi/i18n/conf/en.php index 93af1e9895..fab3d95c92 100644 --- a/core/src/plugins/access.jsapi/i18n/conf/en.php +++ b/core/src/plugins/access.jsapi/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Javascript Api Browser" => "Javascript Api Browser", diff --git a/core/src/plugins/access.jsapi/i18n/conf/fr.php b/core/src/plugins/access.jsapi/i18n/conf/fr.php index eb2dfa80ed..394159d908 100644 --- a/core/src/plugins/access.jsapi/i18n/conf/fr.php +++ b/core/src/plugins/access.jsapi/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Javascript Api Browser" => "Navigateur d'Api Javascript", diff --git a/core/src/plugins/access.jsapi/i18n/conf/it.php b/core/src/plugins/access.jsapi/i18n/conf/it.php index 74240175d0..fbdf235d9a 100644 --- a/core/src/plugins/access.jsapi/i18n/conf/it.php +++ b/core/src/plugins/access.jsapi/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Javascript Api Browser" => "Javascript Api Browser", diff --git a/core/src/plugins/access.jsapi/i18n/fr.php b/core/src/plugins/access.jsapi/i18n/fr.php index 93ff396f06..a417e3cee6 100644 --- a/core/src/plugins/access.jsapi/i18n/fr.php +++ b/core/src/plugins/access.jsapi/i18n/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "1" => "Classes et Interfaces", diff --git a/core/src/plugins/access.jsapi/i18n/si.php b/core/src/plugins/access.jsapi/i18n/si.php index ffdb86064f..1e3abc85c4 100644 --- a/core/src/plugins/access.jsapi/i18n/si.php +++ b/core/src/plugins/access.jsapi/i18n/si.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ // Slovenian translation: April 21 2011 by Vladimir Bohinc (vladimir.bohinc@gmail.com) $mess = array( diff --git a/core/src/plugins/access.jsapi/jsapi_styles.css b/core/src/plugins/access.jsapi/jsapi_styles.css index 6d2c008a02..119dfa2878 100644 --- a/core/src/plugins/access.jsapi/jsapi_styles.css +++ b/core/src/plugins/access.jsapi/jsapi_styles.css @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ span.jsapi_member{ font-weight: bold; diff --git a/core/src/plugins/access.jsapi/manifest.xml b/core/src/plugins/access.jsapi/manifest.xml index dbf3ee81fb..c0aa84baad 100644 --- a/core/src/plugins/access.jsapi/manifest.xml +++ b/core/src/plugins/access.jsapi/manifest.xml @@ -16,7 +16,7 @@ - + @@ -58,7 +58,7 @@ - - - - diff --git a/core/src/plugins/gui.mobile/MobileGuiPlugin.php b/core/src/plugins/gui.mobile/MobileGuiPlugin.php new file mode 100644 index 0000000000..40623ea411 --- /dev/null +++ b/core/src/plugins/gui.mobile/MobileGuiPlugin.php @@ -0,0 +1,91 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ + +namespace Pydio\Gui; + +use DOMXPath; +use Exception; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Utils\Http\UserAgent; + +use Pydio\Core\PluginFramework\Plugin; +use Pydio\Core\PluginFramework\PluginsService; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Test user agent + * @package AjaXplorer_Plugins + * @subpackage Editor + */ +class MobileGuiPlugin extends Plugin +{ + public function performChecks() + { + if (!UserAgent::userAgentIsMobile()) throw new Exception("no"); + } + + /** + * Dynamically modify some registry contributions nodes. Can be easily derivated to enable/disable + * some features dynamically during plugin initialization. + * @param ContextInterface $ctx + * @param \DOMNode $contribNode + * @return void + */ + public function parseSpecificContributions(ContextInterface $ctx, \DOMNode &$contribNode) + { + + if ($contribNode->nodeName == "client_configs" && !$this->orbitExtensionActive($ctx)) { + // remove template_part for orbit_content + $xPath = new DOMXPath($contribNode->ownerDocument); + $tplNodeList = $xPath->query('template_part[@ajxpId="orbit_content"]', $contribNode); + if (!$tplNodeList->length) return; + $contribNode->removeChild($tplNodeList->item(0)); + } + + } + + /** + * @param ContextInterface $ctx + * @return bool + */ + private function orbitExtensionActive(ContextInterface $ctx) + { + $confs = ConfService::getConfStorageImpl()->loadPluginConfig("gui", "ajax"); + if (!isset($confs) || !isSet($confs["GUI_THEME"])) $confs["GUI_THEME"] = "orbit"; + if ($confs["GUI_THEME"] == "orbit") { + $pServ = PluginsService::getInstance($ctx); + $activePlugs = $pServ->getActivePlugins(); + $streamWrappers = $pServ->getStreamWrapperPlugins(); + $streamActive = false; + foreach ($streamWrappers as $sW) { + if ((array_key_exists($sW, $activePlugs) && $activePlugs[$sW] === true)) { + $streamActive = true; + break; + } + } + return $streamActive; + } + return false; + } + +} diff --git a/core/src/plugins/gui.mobile/ajxp-mobile.css b/core/src/plugins/gui.mobile/ajxp-mobile.css index 0d6e380f79..311540376c 100644 --- a/core/src/plugins/gui.mobile/ajxp-mobile.css +++ b/core/src/plugins/gui.mobile/ajxp-mobile.css @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ .hsplitbar { height: 10px !important; @@ -55,14 +55,6 @@ a.m-2 { width: 100%; position: absolute; } -.umbra div#search_panel { - margin: 5px; -} -.ajxp_theme_vision a.m-2 { - margin: 3px 1px; - padding: 3px 5px; - background-color: #4dc5c3 !important; -} #uploadBrowseButton { display: none !important; } @@ -75,9 +67,6 @@ a.m-2 { font-size: 12px; height: 40px; } -div#userdashboard_main_tab h3.dashboard_panel_title { - display: none; -} /* UPDATE MINISITE TEMPlATES */ #ajxp_shared_folder div#breadcrumb, #ajxp_embed_template div#breadcrumb { diff --git a/core/src/plugins/gui.mobile/ajxp-mobile.js b/core/src/plugins/gui.mobile/ajxp-mobile.js index 766f145aae..02a79652df 100644 --- a/core/src/plugins/gui.mobile/ajxp-mobile.js +++ b/core/src/plugins/gui.mobile/ajxp-mobile.js @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ function getAjxpMobileActions(){ var mobileActions = $('mobile_actions_copy'); diff --git a/core/src/plugins/gui.mobile/ajxp-mobile.less b/core/src/plugins/gui.mobile/ajxp-mobile.less index 61350ff638..2183d2a4cc 100644 --- a/core/src/plugins/gui.mobile/ajxp-mobile.less +++ b/core/src/plugins/gui.mobile/ajxp-mobile.less @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ @reactBoxShadowDepth1:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23); @@ -63,16 +63,6 @@ a.m-2{ } } -.umbra div#search_panel{ - margin: 5px; -} - -.ajxp_theme_vision a.m-2{ - margin: 3px 1px; - padding: 3px 5px; - background-color:rgb(77, 197, 195) !important; -} - #uploadBrowseButton{ display: none !important; } @@ -89,10 +79,6 @@ a.m-2{ height: 40px; } -div#userdashboard_main_tab h3.dashboard_panel_title{ - display: none; -} - /* UPDATE MINISITE TEMPlATES */ #ajxp_shared_folder div#breadcrumb, #ajxp_embed_template div#breadcrumb diff --git a/core/src/plugins/gui.mobile/class.MobileGuiPlugin.php b/core/src/plugins/gui.mobile/class.MobileGuiPlugin.php deleted file mode 100644 index 827eb61c72..0000000000 --- a/core/src/plugins/gui.mobile/class.MobileGuiPlugin.php +++ /dev/null @@ -1,67 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Test user agent - * @package AjaXplorer_Plugins - * @subpackage Editor - */ -class MobileGuiPlugin extends AJXP_Plugin -{ - public function performChecks() - { - if(!AJXP_Utils::userAgentIsMobile()) throw new Exception("no"); - } - - public function parseSpecificContributions(&$contribNode){ - - if($contribNode->nodeName == "client_configs" && !$this->orbitExtensionActive()){ - // remove template_part for orbit_content - $xPath=new DOMXPath($contribNode->ownerDocument); - $tplNodeList = $xPath->query('template_part[@ajxpId="orbit_content"]', $contribNode); - if(!$tplNodeList->length) return ; - $contribNode->removeChild($tplNodeList->item(0)); - } - - } - - private function orbitExtensionActive(){ - $confs = ConfService::getConfStorageImpl()->loadPluginConfig("gui", "ajax"); - if(!isset($confs) || !isSet($confs["GUI_THEME"])) $confs["GUI_THEME"] = "orbit"; - if($confs["GUI_THEME"] == "orbit"){ - $pServ = AJXP_PluginsService::getInstance(); - $activePlugs = $pServ->getActivePlugins(); - $streamWrappers = $pServ->getStreamWrapperPlugins(); - $streamActive = false; - foreach($streamWrappers as $sW){ - if((array_key_exists($sW, $activePlugs) && $activePlugs[$sW] === true)){ - $streamActive = true; - break; - } - } - return $streamActive; - } - return false; - } - -} diff --git a/core/src/plugins/gui.mobile/i18n/conf/de.php b/core/src/plugins/gui.mobile/i18n/conf/de.php index 6a0a44d8ed..12498a0dd0 100644 --- a/core/src/plugins/gui.mobile/i18n/conf/de.php +++ b/core/src/plugins/gui.mobile/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ /******************************************************************************* diff --git a/core/src/plugins/gui.mobile/i18n/conf/en.php b/core/src/plugins/gui.mobile/i18n/conf/en.php index 181d90a560..c00dafc2b7 100644 --- a/core/src/plugins/gui.mobile/i18n/conf/en.php +++ b/core/src/plugins/gui.mobile/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Mobile" => "Mobile", diff --git a/core/src/plugins/gui.mobile/i18n/conf/fr.php b/core/src/plugins/gui.mobile/i18n/conf/fr.php index 66a491f75c..ec08019802 100644 --- a/core/src/plugins/gui.mobile/i18n/conf/fr.php +++ b/core/src/plugins/gui.mobile/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Mobile" => "Mobile", diff --git a/core/src/plugins/gui.mobile/i18n/conf/it.php b/core/src/plugins/gui.mobile/i18n/conf/it.php index f01e57cc56..3be4e39395 100644 --- a/core/src/plugins/gui.mobile/i18n/conf/it.php +++ b/core/src/plugins/gui.mobile/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Mobile" => "Mobile", diff --git a/core/src/plugins/gui.mobile/i18n/conf/pt.php b/core/src/plugins/gui.mobile/i18n/conf/pt.php index 4b4b77d5bc..f18c2cdb46 100644 --- a/core/src/plugins/gui.mobile/i18n/conf/pt.php +++ b/core/src/plugins/gui.mobile/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Mobile" => "Móvel", diff --git a/core/src/plugins/gui.mobile/manifest.xml b/core/src/plugins/gui.mobile/manifest.xml index a9b6986bc7..9beaffcf8f 100644 --- a/core/src/plugins/gui.mobile/manifest.xml +++ b/core/src/plugins/gui.mobile/manifest.xml @@ -1,6 +1,6 @@ - + @@ -44,93 +44,6 @@ window.ajxpMobile = true; ]]> - - -
      @@ -150,7 +63,7 @@
      -
      +
      diff --git a/core/src/plugins/gui.user/UserGuiController.php b/core/src/plugins/gui.user/UserGuiController.php new file mode 100644 index 0000000000..20c42bf1bc --- /dev/null +++ b/core/src/plugins/gui.user/UserGuiController.php @@ -0,0 +1,177 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Gui; + +use Exception; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Core\Services\AuthService; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Controller\Controller; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Services\UsersService; +use Pydio\Core\Services\ApplicationState; +use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Core\Utils\Vars\StringHelper; + +use Pydio\Core\PluginFramework\Plugin; +use Pydio\Core\PluginFramework\PluginsService; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * @package AjaXplorer_Plugins + * @subpackage Gui + * @class Pydio\Gui\UserGuiController + * Handle the specific /user access point + */ +class UserGuiController extends Plugin +{ + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @throws Exception + * @throws \Pydio\Core\Exception\ActionNotFoundException + * @throws \Pydio\Core\Exception\AuthRequiredException + * @throws \Pydio\Core\Exception\UserNotFoundException + */ + public function processUserAccessPoint(ServerRequestInterface &$requestInterface, ResponseInterface &$responseInterface) + { + $action = $requestInterface->getAttribute("action"); + $httpVars = $requestInterface->getParsedBody(); + $context = $requestInterface->getAttribute("ctx"); + + switch ($action) { + + case "user_access_point": + + $action = "reset-password"; + $key = InputFilter::sanitize($httpVars["key"], InputFilter::SANITIZE_ALPHANUM); + try { + + $keyData = ConfService::getConfStorageImpl()->loadTemporaryKey("password-reset", $key); + if ($keyData === null || $keyData["user_id"] === false) { + throw new Exception("Invalid password reset key! Did you make sure to copy the correct link?"); + } + + $_SESSION['OVERRIDE_GUI_START_PARAMETERS'] = array( + "REBASE" => "../../", + "USER_GUI_ACTION" => $action, + "USER_ACTION_KEY" => $key + ); + } catch (Exception $e) { + $_SESSION['OVERRIDE_GUI_START_PARAMETERS'] = array( + "ALERT" => $e->getMessage() + ); + } + $req = $requestInterface->withAttribute("action", "get_boot_gui"); + $responseInterface = Controller::run($req); + unset($_SESSION['OVERRIDE_GUI_START_PARAMETERS']); + + break; + + case "reset-password-ask": + + // This is a reset password request, generate a token and store it. + // Find user by id + if (UsersService::userExists($httpVars["email"])) { + // Send email + $mailUId = InputFilter::sanitize($httpVars["email"], InputFilter::SANITIZE_EMAILCHARS); + $userObject = UsersService::getUserById($mailUId); + $email = $userObject->getPersonalRole()->filterParameterValue("core.conf", "email", AJXP_REPO_SCOPE_ALL, ""); + if (!empty($email)) { + $uuid = StringHelper::generateRandomString(48); + ConfService::getConfStorageImpl()->saveTemporaryKey("password-reset", $uuid, InputFilter::decodeSecureMagic($httpVars["email"]), array()); + $mailer = PluginsService::getInstance($context)->getUniqueActivePluginForType("mailer"); + if ($mailer !== false) { + $mess = LocaleService::getMessages(); + $link = rtrim(ApplicationState::detectServerURL(true), "/") . "/user/reset-password/" . $uuid; + $mailer->sendMail($context, array($email), $mess["gui.user.1"], $mess["gui.user.7"] . "$link"); + } else { + echo 'ERROR: There is no mailer configured, please contact your administrator'; + } + } + + } + // Prune existing expired tokens + ConfService::getConfStorageImpl()->pruneTemporaryKeys("password-reset", 20); + echo "SUCCESS"; + + break; + + case "reset-password": + + ConfService::getConfStorageImpl()->pruneTemporaryKeys("password-reset", 20); + // This is a reset password + if (isSet($httpVars["key"]) && isSet($httpVars["user_id"])) { + $keyString = InputFilter::sanitize($httpVars["key"], InputFilter::SANITIZE_ALPHANUM); + $key = ConfService::getConfStorageImpl()->loadTemporaryKey("password-reset", $keyString); + ConfService::getConfStorageImpl()->deleteTemporaryKey("password-reset", $keyString); + $uId = $httpVars["user_id"]; + if (UsersService::ignoreUserCase()) { + $uId = strtolower($uId); + } + if ($key != null && strtolower($key["user_id"]) == $uId && UsersService::userExists($uId)) { + UsersService::updatePassword($key["user_id"], $httpVars["new_pass"]); + } else { + echo 'PASS_ERROR'; + break; + } + AuthService::disconnect(); + echo 'SUCCESS'; + }else{ + AuthService::disconnect(); + echo 'ERROR'; + } + + break; + default: + break; + } + } + + /** + * @param $actionName + * @param $args + * @throws Exception + */ + protected function processSubAction($actionName, $args) + { + switch ($actionName) { + case "reset-password-ask": + break; + case "reset-password": + if (count($args)) { + $token = $args[0]; + $key = ConfService::getConfStorageImpl()->loadTemporaryKey("password-reset", $token); + if ($key == null || $key["user_id"] === false) { + throw new Exception("Invalid password reset key! Did you make sure to copy the correct link?"); + } + } + break; + default: + + break; + } + } + +} diff --git a/core/src/plugins/gui.user/class.UserGuiController.js b/core/src/plugins/gui.user/class.UserGuiController.js index 2ad40af07e..fa1ee49e77 100644 --- a/core/src/plugins/gui.user/class.UserGuiController.js +++ b/core/src/plugins/gui.user/class.UserGuiController.js @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ Class.create("UserGuiController", AjxpPane, { diff --git a/core/src/plugins/gui.user/class.UserGuiController.php b/core/src/plugins/gui.user/class.UserGuiController.php deleted file mode 100644 index eec6ac8fdb..0000000000 --- a/core/src/plugins/gui.user/class.UserGuiController.php +++ /dev/null @@ -1,163 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * @package AjaXplorer_Plugins - * @subpackage Gui - * @class UserGuiController - * Handle the specific /user access point - */ -class UserGuiController extends AJXP_Plugin -{ - - /** - * Parse - * @param DOMNode $contribNode - */ - protected function parseSpecificContributions(&$contribNode) - { - parent::parseSpecificContributions($contribNode); - if (substr($_SERVER["REQUEST_URI"], 0, strlen('/user')) != '/user') { - if ($contribNode->nodeName == "client_configs") { - $children = $contribNode->childNodes; - foreach ($children as $child) { - if($child->nodeType == XML_ELEMENT_NODE) $contribNode->removeChild($child); - } - } else if ($contribNode->nodeName == "actions") { - $children = $contribNode->childNodes; - foreach ($children as $child) { - if ($child->nodeType == XML_ELEMENT_NODE && $child->nodeName == "action" && $child->getAttribute("name") == "login") { - $contribNode->removeChild($child); - } - } - - } - } - - } - - - public function processUserAccessPoint($action, $httpVars, $fileVars) - { - switch ($action) { - case "user_access_point": - $setUrl = ConfService::getCoreConf("SERVER_URL"); - $realUri = "/"; - if(!empty($setUrl)){ - $realUri = parse_url(ConfService::getCoreConf("SERVER_URL"), PHP_URL_PATH); - } - $requestURI = str_replace("//", "/", $_SERVER["REQUEST_URI"]); - $uri = trim(str_replace(rtrim($realUri, "/")."/user", "", $requestURI), "/"); - $uriParts = explode("/", $uri); - $action = array_shift($uriParts); - $key = ($action == "reset-password" && count($uriParts)) ? array_shift($uriParts) : ""; - try{ - $this->processSubAction($action, $uriParts); - $_SESSION['OVERRIDE_GUI_START_PARAMETERS'] = array( - "REBASE"=>"../../", - "USER_GUI_ACTION" => $action, - "USER_ACTION_KEY" => $key - ); - }catch(Exception $e){ - $_SESSION['OVERRIDE_GUI_START_PARAMETERS'] = array( - "ALERT" => $e->getMessage() - ); - } - AJXP_Controller::findActionAndApply("get_boot_gui", array(), array()); - unset($_SESSION['OVERRIDE_GUI_START_PARAMETERS']); - - break; - case "reset-password-ask": - - // This is a reset password request, generate a token and store it. - // Find user by id - if (AuthService::userExists($httpVars["email"])) { - // Send email - $userObject = ConfService::getConfStorageImpl()->createUserObject($httpVars["email"]); - $email = $userObject->personalRole->filterParameterValue("core.conf", "email", AJXP_REPO_SCOPE_ALL, ""); - if (!empty($email)) { - $uuid = AJXP_Utils::generateRandomString(48); - ConfService::getConfStorageImpl()->saveTemporaryKey("password-reset", $uuid, AJXP_Utils::decodeSecureMagic($httpVars["email"]), array()); - $mailer = AJXP_PluginsService::getInstance()->getUniqueActivePluginForType("mailer"); - if ($mailer !== false) { - $mess = ConfService::getMessages(); - $link = AJXP_Utils::detectServerURL()."/user/reset-password/".$uuid; - $mailer->sendMail(array($email), $mess["gui.user.1"], $mess["gui.user.7"]."$link"); - } else { - echo 'ERROR: There is no mailer configured, please contact your administrator'; - } - } - - } - // Prune existing expired tokens - ConfService::getConfStorageImpl()->pruneTemporaryKeys("password-reset", 20); - echo "SUCCESS"; - - break; - case "reset-password": - - ConfService::getConfStorageImpl()->pruneTemporaryKeys("password-reset", 20); - // This is a reset password - if (isSet($httpVars["key"]) && isSet($httpVars["user_id"])) { - $key = ConfService::getConfStorageImpl()->loadTemporaryKey("password-reset", $httpVars["key"]); - ConfService::getConfStorageImpl()->deleteTemporaryKey("password-reset", $httpVars["key"]); - $uId = $httpVars["user_id"]; - if(AuthService::ignoreUserCase()){ - $uId = strtolower($uId); - } - if ($key != null && strtolower($key["user_id"]) == $uId && AuthService::userExists($uId)) { - AuthService::updatePassword($key["user_id"], $httpVars["new_pass"]); - }else{ - echo 'PASS_ERROR'; - break; - } - } - AuthService::disconnect(); - echo 'SUCCESS'; - - break; - default: - break; - } - } - - protected function processSubAction($actionName, $args) - { - switch ($actionName) { - case "reset-password-ask": - break; - case "reset-password": - if (count($args)) { - $token = $args[0]; - $key = ConfService::getConfStorageImpl()->loadTemporaryKey("password-reset", $token); - if ($key == null || $key["user_id"] === false) { - throw new Exception("Invalid password reset key! Did you make sure to copy the correct link?"); - } - } - break; - default: - - break; - } - } - -} diff --git a/core/src/plugins/gui.user/i18n/conf/cs.php b/core/src/plugins/gui.user/i18n/conf/cs.php index 375c0ff4e5..d0146a500d 100644 --- a/core/src/plugins/gui.user/i18n/conf/cs.php +++ b/core/src/plugins/gui.user/i18n/conf/cs.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "User access point" => "Uživatelský přístupový bod", diff --git a/core/src/plugins/gui.user/i18n/conf/de.php b/core/src/plugins/gui.user/i18n/conf/de.php index 6374e6a305..6d1ffd7b86 100644 --- a/core/src/plugins/gui.user/i18n/conf/de.php +++ b/core/src/plugins/gui.user/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ /******************************************************************************* * German translation: diff --git a/core/src/plugins/gui.user/i18n/conf/en.php b/core/src/plugins/gui.user/i18n/conf/en.php index bf3df90ba9..c5bbd9b93c 100644 --- a/core/src/plugins/gui.user/i18n/conf/en.php +++ b/core/src/plugins/gui.user/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "User access point" => "User access point", diff --git a/core/src/plugins/gui.user/i18n/cs.php b/core/src/plugins/gui.user/i18n/cs.php index 76c01de2aa..d153e851ea 100644 --- a/core/src/plugins/gui.user/i18n/cs.php +++ b/core/src/plugins/gui.user/i18n/cs.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "1" => "Reset hesla", @@ -26,4 +26,5 @@ "5" => "Požadavek na změnu hesla byl zaslán na emailovou adresu registrovanou s vaším uživatelským účtem. Klikněte na odkaz v emailu pro provedení změny hesla.", "6" => "Děkujeme, nyní se můžete přihlásit!", "7" => "Byla vyžádána změna hesla pro účet vedený pod touto emailovou adresou. Pokud jste tento požadavek nevyžádali vy, můžete tento email ignorovat. Jinak klikněte na následující odkaz pro reset svého hesla:", + "8" => "Use the form below to reset your password. Please enter your login and the new password twice.", ); diff --git a/core/src/plugins/gui.user/i18n/de.php b/core/src/plugins/gui.user/i18n/de.php index bc468283c4..49efe413ec 100644 --- a/core/src/plugins/gui.user/i18n/de.php +++ b/core/src/plugins/gui.user/i18n/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ /******************************************************************************* * German translation: @@ -31,4 +31,5 @@ "5" => "E-Mail erfolgreich versendet. Klicken Sie auf den Link in der Nachricht, um ein neues Passwort zu vergeben.", "6" => "Vielen Dank. Sie können sich jetzt anmelden.", "7" => "Für das Konto mit dieser E-Mail-Adresse wurde eine Anfrage zum Zurücksetzen des Passwortes angefordert. Wenn Sie dieses Anfrage nicht gemacht haben, können Sie die E-Mail ignorieren. Ansonsten klicken Sie auf folgenden Link, um Ihr Passwort zurückzusetzen:", /* No HTML entities, might be the body of the mail */ + "8" => "Use the form below to reset your password. Please enter your login and the new password twice.", ); diff --git a/core/src/plugins/gui.user/i18n/en.php b/core/src/plugins/gui.user/i18n/en.php index f743f48413..066ddcc32f 100644 --- a/core/src/plugins/gui.user/i18n/en.php +++ b/core/src/plugins/gui.user/i18n/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "1" => "Password Reset", @@ -26,4 +26,5 @@ "5" => "A reset password request has been sent to the email registered with your account. Click on the link in the email to recreate a password.", "6" => "Thank you, you can now log in!", "7" => "A password reset has been requested on the account associated to this email address. If you are not the author of this request, you can safely ignore this email. Otherwise click on the following link to reset your password:", + "8" => "Use the form below to reset your password. Please enter your login and the new password twice." ); diff --git a/core/src/plugins/gui.user/i18n/fr.php b/core/src/plugins/gui.user/i18n/fr.php index 6f6c060da1..ca776a8853 100644 --- a/core/src/plugins/gui.user/i18n/fr.php +++ b/core/src/plugins/gui.user/i18n/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "1" => "Réinitialisation du mot de passe", @@ -26,4 +26,5 @@ "5" => "Un courriel contenant une demande de réinitialisation du mot de passe a été envoyé à l'adresse courriel enregistrée dans votre compte. Cliquez sur le lien du courriel pour créer un nouveau mot de passe.", "6" => "Merci, vous pouvez à présent vous connecter avec ce nouveau mot de passe!", "7" => "Une demande de réinitialisation de mot de passe a été demandée pour le compte associé à ce courriel. Si vous n'en êtes pas l'auteur, vous pouvez ignorer ce courriel. Sinon cliquez sur le lien suivant :", + "8" => "Utilisez le formulaire pour remettre à zéro votre mot de passe. Entrez votre identifiant et votre nouveau mot de passe deux fois.", ); diff --git a/core/src/plugins/gui.user/i18n/it.php b/core/src/plugins/gui.user/i18n/it.php index a93aa9cac2..415bba1cef 100644 --- a/core/src/plugins/gui.user/i18n/it.php +++ b/core/src/plugins/gui.user/i18n/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "1" => "Reset Password", @@ -26,4 +26,5 @@ "5" => "Una richiesta di reset password è stata inviata all'indirizzo mail associato al tuo account. Clicca sul link nella mail per creare una nuova password.", "6" => "Grazie. Ora puoi autenticarti!", "7" => "Il reset della password è stato richiesto dall'account associato a questo indirizzo mail. Se non sei l'autore di questa richiesta, puoi semplicemente ignorare questa mail. Altrimenti, clicca sul link seguente per reimpostare la tua password:", + "8" => "Use the form below to reset your password. Please enter your login and the new password twice.", ); diff --git a/core/src/plugins/gui.user/i18n/ru.php b/core/src/plugins/gui.user/i18n/ru.php index ce3bfd3b8e..677a5cdb68 100644 --- a/core/src/plugins/gui.user/i18n/ru.php +++ b/core/src/plugins/gui.user/i18n/ru.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "1" => "Сброс пароля", @@ -26,4 +26,5 @@ "5" => "Запрос на сброс пароля будет послан на email, который указан в вашей учётной записи. Нажмите на ссылку в письме, чтобы пересоздать пароль.", "6" => "Спасибо, теперь Вы можете зайти в систему!", "7" => "Запрошен сброс пароля для учётной записи ассоциированной с этим email адресом. Если Вы не автор запроса просто проигнорируйте это письмо. Если Вы хотите сбросить пароль, то нажмите на ссылку:", + "8" => "Use the form below to reset your password. Please enter your login and the new password twice.", ); diff --git a/core/src/plugins/gui.user/manifest.xml b/core/src/plugins/gui.user/manifest.xml index af9887538d..55e65aa7ce 100644 --- a/core/src/plugins/gui.user/manifest.xml +++ b/core/src/plugins/gui.user/manifest.xml @@ -9,7 +9,7 @@ - + @@ -109,14 +109,15 @@ +
      AJXP_MESSAGE[gui.user.8]
      -
      AJXP_MESSAGE[gui.user.4] :
      +
      AJXP_MESSAGE[gui.user.4] :
      -
      AJXP_MESSAGE[198] :
      +
      AJXP_MESSAGE[198] :
      -
      AJXP_MESSAGE[199] :
      +
      AJXP_MESSAGE[199] :
      @@ -124,35 +125,11 @@ - - - - - - - + diff --git a/core/src/plugins/index.elasticsearch/ElasticSearchIndexer.php b/core/src/plugins/index.elasticsearch/ElasticSearchIndexer.php new file mode 100644 index 0000000000..2c04e8685e --- /dev/null +++ b/core/src/plugins/index.elasticsearch/ElasticSearchIndexer.php @@ -0,0 +1,682 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ + +namespace Pydio\Access\Indexer\Implementation; + +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Indexer\Core\AbstractSearchEngineIndexer; +use Pydio\Core\Http\Message\UserMessage; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Services\UsersService; +use Pydio\Core\Services\ApplicationState; +use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Core\Utils\Vars\VarsFilter; +use \Elastica; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +require_once (dirname(__FILE__)."/vendor/autoload.php"); + +/** + * Encapsultion of the Elastica component as a plugin + * @package AjaXplorer_Plugins + * @subpackage Index + * @property Elastica\Client $client + * @property Elastica\Index $currentIndex + * @property Elastica\Type $currentType + */ +class ElasticSearchIndexer extends AbstractSearchEngineIndexer +{ + private $client; + private $currentIndex; + private $currentType; + private $nextId; + private $lastIdPath; + + private $metaFields = []; + private $indexContent = false; + private $specificId = ""; + private $verboseIndexation = false; + + /** + * @param ContextInterface $ctx + * @param array $options + */ + public function init(ContextInterface $ctx, $options = []) + { + parent::init($ctx, $options); + $metaFields = $this->getContextualOption($ctx, "index_meta_fields"); + $specKey = $this->getContextualOption($ctx, "repository_specific_keywords"); + if (!empty($metaFields)) { + $this->metaFields = explode(",",$metaFields); + } + + if (!empty($specKey)) { + $this->specificId = "-".str_replace([",", "/"], ["-", "__"], VarsFilter::filter($specKey, $ctx)); + } + + /* Connexion to Elastica Client with the default parameters */ + $this->client = new Elastica\Client([ + "host" => $this->getContextualOption($ctx, "ELASTICSEARCH_HOST"), + "port" => $this->getContextualOption($ctx, "ELASTICSEARCH_PORT")] + ); + + $this->indexContent = ($this->getContextualOption($ctx, "index_content") == true); + } + + /** + * @param ContextInterface $ctx + * @param \Pydio\Access\Core\AbstractAccessDriver $accessDriver + */ + public function initMeta(ContextInterface $ctx, \Pydio\Access\Core\AbstractAccessDriver $accessDriver) + { + if (!empty($this->metaFields) || $this->indexContent) { + $metaFields = $this->metaFields; + /** @var \DOMElement $el */ + $el = $this->getXPath()->query("/indexer")->item(0); + if ($this->indexContent) { + if($this->indexContent) $metaFields[] = "ajxp_document_content"; + $data = ["indexed_meta_fields" => $metaFields, + "additionnal_meta_columns" => ["ajxp_document_content" => "Content"] + ]; + $el->setAttribute("indexed_meta_fields", json_encode($data)); + } else { + $el->setAttribute("indexed_meta_fields", json_encode($metaFields)); + } + } + parent::init($ctx, $this->options); + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $node + */ + public function indexationIndexNode($node){ + $this->updateNodeIndex(null, $node, false, false); + } + + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $parentNode + */ + public function indexationStarts($parentNode){ + $this->loadIndex($parentNode->getContext(), true); + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $parentNode + */ + public function indexationEnds($parentNode){ + if($this->currentIndex) { + $this->currentIndex->optimize(); + } + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $requestInterface + * @param \Psr\Http\Message\ResponseInterface $responseInterface + * @return null + * @throws \Exception + */ + public function applyAction(\Psr\Http\Message\ServerRequestInterface $requestInterface, \Psr\Http\Message\ResponseInterface &$responseInterface) + { + $actionName = $requestInterface->getAttribute("action"); + $httpVars = $requestInterface->getParsedBody(); + /** @var ContextInterface $ctx */ + $ctx = $requestInterface->getAttribute("ctx"); + $ctxUser = $ctx->getUser(); + + $messages = LocaleService::getMessages(); + + $x = new \Pydio\Core\Http\Response\SerializableResponseStream(); + $nodesList = new \Pydio\Access\Core\Model\NodesList(); + $responseInterface = $responseInterface->withBody($x); + $x->addChunk($nodesList); + + if ($actionName == "search") { + // TMP + if (strpos($httpVars["query"], "keyword:") === 0) { + $parts = explode(":", $httpVars["query"]); + $requestInterface = $requestInterface->withAttribute("action", "search_by_keyword")->withParsedBody(["field" => $parts[1]]); + $this->applyAction($requestInterface, $responseInterface); + return null; + } + + try { + $this->loadIndex($ctx, false); + } catch (\Exception $ex) { + if (ConfService::backgroundActionsSupported() && !ApplicationState::sapiIsCli() && !isSet($httpVars["skip_unindexed"])) { + $task = \Pydio\Tasks\TaskService::actionAsTask($ctx, "index", []); + $task->setLabel($messages["index.lucene.7"]); + $responseInterface = \Pydio\Tasks\TaskService::getInstance()->enqueueTask($task, $requestInterface, $responseInterface); + $x->addChunk(new UserMessage($messages["index.lucene.7"])); + }else{ + $x->addChunk(new UserMessage($messages["index.lucene.12"])); + } + } + + $textQuery = $httpVars["query"]; + if($this->getContextualOption($ctx, "AUTO_WILDCARD") === true && strlen($textQuery) > 0 && ctype_alnum($textQuery)){ + if($textQuery[0] == '"' && $textQuery[strlen($textQuery)-1] == '"'){ + $textQuery = substr($textQuery, 1, -1); + }else if($textQuery[strlen($textQuery)-1] != "*" ){ + $textQuery.="*"; + } + } + + + $this->currentIndex->open(); + $fieldQuery = new Elastica\Query\QueryString(); + $fieldQuery->setAllowLeadingWildcard(true); + $fieldQuery->setFuzzyMinSim(0.8); + + if($textQuery == "*"){ + + $fields = ["ajxp_node"]; + $fieldQuery->setQuery("yes"); + $fieldQuery->setFields($fields); + + }else if(strpos($textQuery, ":") !== false){ + + // USE LUCENE DSL DIRECTLY (key1:value1 AND key2:value2...) + $textQuery = str_replace("ajxp_meta_ajxp_document_content:","body:", $textQuery); + $textQuery = $this->filterSearchRangesKeywords($textQuery); + $fieldQuery->setQuery($textQuery); + + } else{ + + $fields = ["basename","ajxp_meta_*", "body"]; + $fieldQuery->setQuery($textQuery); + $fieldQuery->setFields($fields); + + } + + /* + TODO : READAPT QUERY WITH EACH FIELD + if ((isSet($this->metaFields) || $this->indexContent) && isSet($httpVars["fields"])) { + $sParts = array(); + foreach (explode(",",$httpVars["fields"]) as $searchField) { + if ($searchField == "filename") { + $sParts[] = "basename:".$httpVars["query"]; + } else if (in_array($searchField, $this->metaFields)) { + $sParts[] = "ajxp_meta_".$searchField.":".$httpVars["query"]; + } else if ($searchField == "ajxp_document_content") { + $sParts[] = "title:".$httpVars["query"]; + $sParts[] = "body:".$httpVars["query"]; + $sParts[] = "keywords:".$httpVars["query"]; + } + } + $query = implode(" OR ", $sParts); + $query = "ajxp_scope:shared AND ($query)"; + $this->logDebug("Query : $query"); + } else { + */ + + //} + /* + We create this object search because it'll allow us to fetch the number of results we want at once. + We just have to set some parameters, the query type and the size of the result set. + */ + $search = new Elastica\Search($this->client); + $search->addIndex($this->currentIndex)->addType($this->currentType); + + $maxResults = $this->getContextualOption($ctx, "MAX_RESULTS"); + if(isSet($httpVars['limit'])){ + $maxResults = intval($httpVars['limit']); + } + $searchOptions = [ + \Elastica\Search::OPTION_SEARCH_TYPE => \Elastica\Search::OPTION_SEARCH_TYPE_QUERY_THEN_FETCH, + \Elastica\Search::OPTION_SIZE => $maxResults]; + + $this->logDebug(__FUNCTION__,"Executing query: ", $textQuery); + $fullQuery = new Elastica\Query(); + $fullQuery->setQuery($fieldQuery); + + $qb = new Elastica\QueryBuilder(); + $fullQuery = new Elastica\Query(); + $filter = $qb->query()->match("ajxp_scope", "shared"); + $fullQuery->setQuery( + $qb->query()->bool()->addMust($fieldQuery)->addFilter($filter) + ); + $result = $search->search($fullQuery, $searchOptions); + $this->logDebug(__FUNCTION__,"Search finished. "); + $hits = $result->getResults(); + + foreach ($hits as $hit) { + $source = $hit->getSource(); + + if ($source["serialized_metadata"] != null) { + $meta = unserialize(base64_decode($source["serialized_metadata"])); + $tmpNode = new AJXP_Node($source["node_url"], $meta); + } else { + $tmpNode = new AJXP_Node($source["node_url"], []); + $tmpNode->loadNodeInfo(); + } + + if (!file_exists($tmpNode->getUrl())) { + $this->currentType->deleteById($hit->getId()); + continue; + } + + $tmpNode->search_score = sprintf("%0.2f", $hit->getScore()); + $nodesList->addBranch($tmpNode); + } + + } else if ($actionName == "search_by_keyword") { + + try { + $this->loadIndex($ctx, false); + } catch (\Exception $ex) { + throw new \Exception($messages["index.lucene.7"]); + } + + $searchField = InputFilter::sanitize($httpVars["field"], InputFilter::SANITIZE_ALPHANUM); + + $fieldQuery = new Elastica\Query\QueryString(); + $fields = [$searchField]; + $fieldQuery->setQuery($searchField == "ajxp_node"?"yes":"true"); + + $fieldQuery->setFields($fields); + $fieldQuery->setAllowLeadingWildcard(false); + $fieldQuery->setFuzzyMinSim(0.8); + + + $search = new Elastica\Search($this->client); + $search->addIndex($this->currentIndex)->addType($this->currentType); + + $maxResults = $this->getContextualOption($ctx, "MAX_RESULTS"); + if(isSet($httpVars['limit'])){ + $maxResults = intval($httpVars['limit']); + } + $searchOptions = [ + \Elastica\Search::OPTION_SEARCH_TYPE => \Elastica\Search::OPTION_SEARCH_TYPE_QUERY_THEN_FETCH, + \Elastica\Search::OPTION_SIZE => $maxResults]; + + $qb = new Elastica\QueryBuilder(); + $fullQuery = new Elastica\Query(); + $fullQuery->setQuery( + $qb->query()->bool() + ->addMust($fieldQuery) + ->addMust($qb->query()->match("ajxp_scope", "user")) + ->addMust($qb->query()->match("user", $ctxUser->getId())) + ); + + $result = $search->search($fullQuery, $searchOptions); + $this->logDebug(__FUNCTION__,"Search finished. "); + $hits = $result->getResults(); + + foreach ($hits as $hit) { + + if ($hit->serialized_metadata!=null) { + $meta = unserialize(base64_decode($hit->serialized_metadata)); + $tmpNode = new AJXP_Node($hit->node_url, $meta); + } else { + $tmpNode = new AJXP_Node($hit->node_url, []); + $tmpNode->loadNodeInfo(); + } + if (!file_exists($tmpNode->getUrl())) { + $this->currentType->deleteById($hit->getId()); + continue; + } + $tmpNode->search_score = sprintf("%0.2f", $hit->score); + $nodesList->addBranch($tmpNode); + + } + } + + } + + /** + * @param $url + */ + public function recursiveIndexation($url) + { + //print("Indexing $url \n"); + $this->logDebug("Indexing content of folder ".$url); + if (ApplicationState::sapiIsCli() && $this->verboseIndexation) { + print("Indexing content of ".$url."\n"); + } + @set_time_limit(60); + $handle = opendir($url); + + if ($handle !== false) { + while ( ($child = readdir($handle)) != false) { + if($child[0] == ".") continue; + $newUrl = $url."/".$child; + $this->logDebug("Indexing Node ".$newUrl); + try { + $this->updateNodeIndex(null, new AJXP_Node($newUrl)); + } catch (\Exception $e) { + $this->logDebug("Error Indexing Node ".$newUrl." (".$e->getMessage().")"); + } + } + closedir($handle); + } else { + $this->logDebug("Cannot open $url!!"); + } + } + + /** + * + * Hooked to node.meta_change, this will update the index + * + * @param \Pydio\Access\Core\Model\AJXP_Node $node + */ + public function updateNodeIndexMeta($node) + { + $this->loadIndex($node->getContext(), true); + if (UsersService::usersEnabled() && $node->getContext()->hasUser()) { + + $query = new Elastica\Query\Term(); + $query->setTerm("node_url", $node->getUrl()); + $results = $this->currentType->search($query); + $hits = $results->getResults(); + foreach ($hits as $hit) { + $source = $hit->getSource(); + if ($source['ajxp_scope'] == 'shared' || ($source['ajxp_scope'] == 'user' && $source['ajxp_user'] == $node->getContext()->getUser()->getId())) { + $this->currentType->deleteById($hit->getId()); + } + } + } else { + $id = $this->getIndexedDocumentId($node); + if($id != null) $this->currentType->deleteById($id); + } + $this->createIndexedDocument($node); + + } + + /** + * + * Hooked to node.change, this will update the index + * if $oldNode = null => create node $newNode + * if $newNode = null => delete node $oldNode + * Else copy or move oldNode to newNode. + * + * @param \Pydio\Access\Core\Model\AJXP_Node $oldNode + * @param \Pydio\Access\Core\Model\AJXP_Node $newNode + * @param Boolean $copy + * @param bool $recursive + */ + public function updateNodeIndex($oldNode, $newNode = null, $copy = false, $recursive = false) + { + if($oldNode == null){ + $this->loadIndex($newNode->getContext(), true); + }else{ + $this->loadIndex($oldNode->getContext(), true); + } + + if ($oldNode != null && $copy == false) { + $oldDocId = $this->getIndexedDocumentId($oldNode); + if ($oldDocId != null) { + $this->currentType->deleteById($oldDocId); + $childrenHits = $this->getIndexedChildrenDocuments($oldNode); + + if ($childrenHits != null) { + $childrenHits = $childrenHits->getResults(); + + foreach ($childrenHits as $hit) { + $this->currentType->deleteById($hit->getId()); + } + } + } + } + + if ($newNode != null) { + // Make sure it does not already exists anyway + $newDocId = $this->getIndexedDocumentId($newNode); + if ($newDocId != null) { + try{ + $this->currentType->deleteById($newDocId); + }catch (Elastica\Exception\NotFoundException $eEx){ + $this->logError(__FUNCTION__, "Trying to delete a non existing document"); + } + $childrenHits = $this->getIndexedChildrenDocuments($newNode); + if ($childrenHits != null) { + $childrenHits = $childrenHits->getResults(); + + foreach ($childrenHits as $hit) { + try{ + $this->currentType->deleteById($hit->getId()); + }catch (Elastica\Exception\NotFoundException $eEx){ + $this->logError(__FUNCTION__, "Trying to delete a non existing document"); + } + } + } + } + + $this->createIndexedDocument($newNode); + + if ($recursive && $oldNode == null && is_dir($newNode->getUrl())) { + $this->recursiveIndexation($newNode->getUrl()); + } + } + + + if ($oldNode != null && $newNode != null && is_dir($newNode->getUrl())) { // Copy / Move / Rename + // Get old node children docs, and update them manually, no need to scan real directory + $childrenHits = $this->getIndexedChildrenDocuments($oldNode); + if ($childrenHits != null) { + $childrenHits = $childrenHits->getResults(); + + foreach ($childrenHits as $hit) { + $oldChildURL = $this->currentType->getDocument($hit->getId())->get("node_url"); + if ($copy == false) { + $this->currentType->deleteById($hit->getId()); + } + $newChildURL = str_replace($oldNode->getUrl(), + $newNode->getUrl(), + $oldChildURL); + $this->createIndexedDocument(new AJXP_Node($newChildURL)); + } + } + } + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $ajxpNode + * @throws \Exception + */ + public function createIndexedDocument($ajxpNode) + { + $ajxpNode->loadNodeInfo(); + + $parseContent = $this->indexContent; + if ($parseContent && $ajxpNode->bytesize > $this->getContextualOption($ajxpNode->getContext(), "PARSE_CONTENT_MAX_SIZE")) { + $parseContent = false; + } + + $data = []; + $data["node_url"] = $ajxpNode->getUrl(); + $data["node_path"] = str_replace("/", "AJXPFAKESEP", $ajxpNode->getPath()); + $data["basename"] = basename($ajxpNode->getPath()); + $data["ajxp_node"] = "yes"; + $data["ajxp_scope"] = "shared"; + $data["serialized_metadata"] = base64_encode(serialize($ajxpNode->metadata)); + $data["ajxp_modiftime"] = date("Ymd", $ajxpNode->ajxp_modiftime); + $data["ajxp_bytesize"] = $ajxpNode->bytesize; + $ajxpMime = $ajxpNode->ajxp_mime; + if (empty($ajxpMime)) { + $data["ajxp_mime"] = pathinfo($ajxpNode->getLabel(), PATHINFO_EXTENSION); + } else { + $data["ajxp_mime"] = $ajxpNode->ajxp_mime; + } + + if (isSet($ajxpNode->indexableMetaKeys["shared"])) { + foreach ($ajxpNode->indexableMetaKeys["shared"] as $sharedField) { + if ($ajxpNode->$sharedField) { + $data[$sharedField] = $ajxpNode->$sharedField; + } + } + } + foreach ($this->metaFields as $field) { + if ($ajxpNode->$field != null) { + $data["ajxp_meta_$field"] = $ajxpNode->$field; + } + } + if($parseContent){ + $body = $this->extractIndexableContent($ajxpNode); + if(!empty($body)){ + $data["body"] = $body; + } + } + + $mapping = new Elastica\Type\Mapping(); + $mapping->setType($this->currentType); + $mapping->setProperties($this->dataToMappingProperties($data)); + $mapping->send(); + $doc = new Elastica\Document($this->nextId, $data); + $this->currentType->addDocument($doc); + $this->nextId++; + + if (isSet($ajxpNode->indexableMetaKeys["user"]) && count($ajxpNode->indexableMetaKeys["user"]) && UsersService::usersEnabled() && $ajxpNode->getContext()->hasUser()) { + + $userData = [ + "ajxp_scope" => "user", + "user" => $ajxpNode->getUser()->getId(), + "serialized_metadata" => $data["serialized_metadata"], + "node_url" => $data["node_url"], + "node_path" => $data["node_path"] + ]; + $userData["ajxp_user"] = $ajxpNode->getContext()->getUser()->getId(); + foreach ($ajxpNode->indexableMetaKeys["user"] as $userField) { + if ($ajxpNode->$userField) { + $userData[$userField] = $ajxpNode->$userField; + } + } + $mapping = new Elastica\Type\Mapping(); + $mapping->setType($this->currentType); + $mapping->setProperties($this->dataToMappingProperties($userData)); + $mapping->send(); + $doc = new Elastica\Document($this->nextId, $userData); + $this->currentType->addDocument($doc); + $this->nextId++; + } + + /* we update the last id in the file */ + $file = fopen($this->lastIdPath, "w"); + fputs($file, $this->nextId-1); + fclose($file); + + } + + /** + * Transform not data to ready to store mapping + * @param $data + * @return array + */ + protected function dataToMappingProperties($data){ + + $mapping_properties = []; + foreach ($data as $key => $value) { + if ($key == "node_url" || $key == "node_path") { + $mapping_properties[$key] = ["type" => "string", "index" => "not_analyzed"]; + } else if($key == "serialized_metadata"){ + $mapping_properties[$key] = ["type" => "string" /*, "index" => "no" */]; + } else if ($key == "ajxp_bytesize"){ + $mapping_properties[$key] = ["type" => "long"]; + } else { + $type = gettype($value); + if ($type != "integer" && $type != "boolean" && $type != "double") { + $type = "string"; + } + $mapping_properties[$key] = ["type" => $type]; + } + } + return $mapping_properties; + + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $ajxpNode + * @return Number + */ + public function getIndexedDocumentId($ajxpNode) + { + /* + we used a term query that will check for the exact term (here is the path to a file) + that can't be duplicate.Thus it will get the right result. + */ + $query = new Elastica\Query\Term(); + $query->setTerm("node_url", $ajxpNode->getUrl()); + + /* returns a result set from the query */ + $results = $this->currentIndex->search($query); + + if($results->getTotalHits() == 0) return null; + + return $results->current()->getId(); + } + + /** + * Find all existing lucene documents based on the parent url + * @param AJXP_Node $ajxpNode + * @return Elastica\ResultSet + */ + public function getIndexedChildrenDocuments($ajxpNode) + { + $testQ = str_replace("/", "AJXPFAKESEP", $ajxpNode->getPath()."/"); + + /* we use a wildcard query to fetch all children documents relatively to their paths */ + $query = new Elastica\Query\Wildcard("node_path", $testQ."*"); + + /* returns a result set from the query */ + $results = $this->currentIndex->search($query); + + if($results->getTotalHits() == 0) return null; + + return $results; + } + + /** + * + * load the index into the class parameter currentIndex + * @param ContextInterface $ctx + * @param bool $create + */ + protected function loadIndex(ContextInterface $ctx, $create = true) + { + $specificId = $this->buildSpecificId($ctx); + + $this->currentIndex = $this->client->getIndex($specificId); + + /* if the cache directory for the repository index is not created we do create it */ + $iPath = (defined('AJXP_SHARED_CACHE_DIR')?AJXP_SHARED_CACHE_DIR:AJXP_CACHE_DIR)."/indexes/".$specificId; + if(!is_dir($iPath)) mkdir($iPath,0755, true); + + if ($create && !$this->currentIndex->exists()) { + $this->currentIndex->create(); + } + + $this->currentType = new Elastica\Type($this->currentIndex, "type_".$specificId); + + /* we fetch the last id we used to create a document and set the variable nextId */ + $this->lastIdPath = (defined('AJXP_SHARED_CACHE_DIR')?AJXP_SHARED_CACHE_DIR:AJXP_CACHE_DIR)."/indexes/".$specificId."/last_id"; + if (file_exists($this->lastIdPath)) { + $file = fopen($this->lastIdPath, "r"); + $this->nextId = floatval(fgets($file)) + 1; + fclose($file); + } else { + touch($this->lastIdPath); + $this->nextId = 1; + } + } +} \ No newline at end of file diff --git a/core/src/plugins/index.elasticsearch/class.AjxpElasticSearch.php b/core/src/plugins/index.elasticsearch/class.AjxpElasticSearch.php deleted file mode 100644 index 029a2bf702..0000000000 --- a/core/src/plugins/index.elasticsearch/class.AjxpElasticSearch.php +++ /dev/null @@ -1,677 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die( 'Access not allowed'); - -require_once (dirname(__FILE__)."/vendor/autoload.php"); - -/** - * Encapsultion of the Elastica component as a plugin - * @package AjaXplorer_Plugins - * @subpackage Index - * @property Elastica\Client $client - * @property Elastica\Index $currentIndex - * @property Elastica\Type $currentType - */ -class AjxpElasticSearch extends AbstractSearchEngineIndexer -{ - private $client; - private $currentIndex; - private $currentType; - private $nextId; - private $lastIdPath; - - private $metaFields = []; - private $indexContent = false; - private $specificId = ""; - private $verboseIndexation = false; - - public function init($options) - { - parent::init($options); - $metaFields = $this->getFilteredOption("index_meta_fields"); - $specKey = $this->getFilteredOption("repository_specific_keywords"); - if (!empty($metaFields)) { - $this->metaFields = explode(",",$metaFields); - } - - if (!empty($specKey)) { - $this->specificId = "-".str_replace(array(",", "/"), array("-", "__"), AJXP_VarsFilter::filter($specKey)); - } - - /* Connexion to Elastica Client with the default parameters */ - $this->client = new Elastica\Client(array( - "host" => $this->getFilteredOption("ELASTICSEARCH_HOST"), - "port" => $this->getFilteredOption("ELASTICSEARCH_PORT")) - ); - - $this->indexContent = ($this->getFilteredOption("index_content") == true); - } - - public function initMeta($accessDriver) - { - $this->accessDriver = $accessDriver; - if (!empty($this->metaFields) || $this->indexContent) { - $metaFields = $this->metaFields; - $el = $this->getXPath()->query("/indexer")->item(0); - if ($this->indexContent) { - if($this->indexContent) $metaFields[] = "ajxp_document_content"; - $data = ["indexed_meta_fields" => $metaFields, - "additionnal_meta_columns" => ["ajxp_document_content" => "Content"] - ]; - $el->setAttribute("indexed_meta_fields", json_encode($data)); - } else { - $el->setAttribute("indexed_meta_fields", json_encode($metaFields)); - } - } - parent::init($this->options); - } - - /** - * @param AJXP_Node $node - */ - public function indexationIndexNode($node){ - $this->updateNodeIndex(null, $node, false, false); - } - - - /** - * @param AJXP_Node $parentNode - */ - public function indexationStarts($parentNode){ - $this->loadIndex($parentNode->getRepositoryId(), true, $parentNode->getUser()); - } - - /** - * @param AJXP_Node $parentNode - */ - public function indexationEnds($parentNode){ - if($this->currentIndex) { - $this->currentIndex->optimize(); - } - } - - public function applyAction($actionName, $httpVars, $fileVars) - { - $messages = ConfService::getMessages(); - $repoId = $this->accessDriver->repository->getId(); - - if ($actionName == "search") { - // TMP - if (strpos($httpVars["query"], "keyword:") === 0) { - $parts = explode(":", $httpVars["query"]); - $this->applyAction("search_by_keyword", array("field" => $parts[1]), array()); - return; - } - - try { - $this->loadIndex($repoId, false); - } catch (Exception $ex) { - $this->applyAction("index", array(), array()); - throw new Exception($messages["index.lucene.7"]); - } - - $textQuery = $httpVars["query"]; - if($this->getFilteredOption("AUTO_WILDCARD") === true && strlen($textQuery) > 0 && ctype_alnum($textQuery)){ - if($textQuery[0] == '"' && $textQuery[strlen($textQuery)-1] == '"'){ - $textQuery = substr($textQuery, 1, -1); - }else if($textQuery[strlen($textQuery)-1] != "*" ){ - $textQuery.="*"; - } - } - - $this->currentIndex->open(); - $fieldQuery = new Elastica\Query\QueryString(); - $fieldQuery->setAllowLeadingWildcard(true); - $fieldQuery->setFuzzyMinSim(0.8); - - if($textQuery == "*"){ - - $fields = ["ajxp_node"]; - $fieldQuery->setQuery("yes"); - $fieldQuery->setFields($fields); - - }else if(strpos($textQuery, ":") !== false){ - - // USE LUCENE DSL DIRECTLY (key1:value1 AND key2:value2...) - $textQuery = str_replace("ajxp_meta_ajxp_document_content:","body:", $textQuery); - $textQuery = $this->filterSearchRangesKeywords($textQuery); - $fieldQuery->setQuery($textQuery); - - } else{ - - $fields = ["basename","ajxp_meta_*", "body"]; - $fieldQuery->setQuery($textQuery); - $fieldQuery->setFields($fields); - - } - - /* - TODO : READAPT QUERY WITH EACH FIELD - if ((isSet($this->metaFields) || $this->indexContent) && isSet($httpVars["fields"])) { - $sParts = array(); - foreach (explode(",",$httpVars["fields"]) as $searchField) { - if ($searchField == "filename") { - $sParts[] = "basename:".$httpVars["query"]; - } else if (in_array($searchField, $this->metaFields)) { - $sParts[] = "ajxp_meta_".$searchField.":".$httpVars["query"]; - } else if ($searchField == "ajxp_document_content") { - $sParts[] = "title:".$httpVars["query"]; - $sParts[] = "body:".$httpVars["query"]; - $sParts[] = "keywords:".$httpVars["query"]; - } - } - $query = implode(" OR ", $sParts); - $query = "ajxp_scope:shared AND ($query)"; - $this->logDebug("Query : $query"); - } else { - */ - - //} - /* - We create this object search because it'll allow us to fetch the number of results we want at once. - We just have to set some parameters, the query type and the size of the result set. - */ - $search = new Elastica\Search($this->client); - $search->addIndex($this->currentIndex)->addType($this->currentType); - - $maxResults = $this->getFilteredOption("MAX_RESULTS"); - if(isSet($httpVars['limit'])){ - $maxResults = intval($httpVars['limit']); - } - $searchOptions = [ - \Elastica\Search::OPTION_SEARCH_TYPE => \Elastica\Search::OPTION_SEARCH_TYPE_QUERY_THEN_FETCH, - \Elastica\Search::OPTION_SIZE => $maxResults]; - - $this->logDebug(__FUNCTION__,"Executing query: ", $textQuery); - $fullQuery = new Elastica\Query(); - $fullQuery->setQuery($fieldQuery); - - $qb = new Elastica\QueryBuilder(); - $fullQuery = new Elastica\Query(); - $filter = $qb->query()->match("ajxp_scope", "shared"); - $fullQuery->setQuery( - $qb->query()->bool()->addMust($fieldQuery)->addFilter($filter) - ); - - $result = $search->search($fullQuery, $searchOptions); - $this->logDebug(__FUNCTION__,"Search finished. "); - $hits = $result->getResults(); - - AJXP_XMLWriter::header(); - foreach ($hits as $hit) { - $source = $hit->getSource(); - - if ($source["serialized_metadata"] != null) { - $meta = unserialize(base64_decode($source["serialized_metadata"])); - $tmpNode = new AJXP_Node(SystemTextEncoding::fromUTF8($source["node_url"]), $meta); - } else { - $tmpNode = new AJXP_Node(SystemTextEncoding::fromUTF8($source["node_url"]), array()); - $tmpNode->loadNodeInfo(); - } - - if (!file_exists($tmpNode->getUrl())) { - $this->currentType->deleteById($hit->getId()); - continue; - } - - $tmpNode->search_score = sprintf("%0.2f", $hit->getScore()); - AJXP_XMLWriter::renderAjxpNode($tmpNode); - } - - AJXP_XMLWriter::close(); - - } else if ($actionName == "search_by_keyword") { - - $scope = "user"; - try { - $this->loadIndex($repoId, false); - } catch (Exception $ex) { - throw new Exception($messages["index.lucene.7"]); - } - - /* - $sParts = array(); - $searchField = $httpVars["field"]; - - - if ($scope == "user") { - if (AuthService::usersEnabled() && AuthService::getLoggedUser() == null) { - throw new Exception("Cannot find current user"); - } - $sParts[] = "ajxp_scope:user"; - $sParts[] = "ajxp_user:".AuthService::getLoggedUser()->getId(); - } else { - $sParts[] = "ajxp_scope:shared"; - } - $query = implode(" AND ", $sParts); - $this->logDebug("Query : $query");*/ - - $searchField = AJXP_Utils::sanitize($httpVars["field"], AJXP_SANITIZE_ALPHANUM); - - $fieldQuery = new Elastica\Query\QueryString(); - $fields = array($searchField); - $fieldQuery->setQuery($searchField == "ajxp_node"?"yes":"true"); - - $fieldQuery->setFields($fields); - $fieldQuery->setAllowLeadingWildcard(false); - $fieldQuery->setFuzzyMinSim(0.8); - - - $search = new Elastica\Search($this->client); - $search->addIndex($this->currentIndex)->addType($this->currentType); - - $maxResults = $this->getFilteredOption("MAX_RESULTS"); - if(isSet($httpVars['limit'])){ - $maxResults = intval($httpVars['limit']); - } - $searchOptions = array( - \Elastica\Search::OPTION_SEARCH_TYPE => \Elastica\Search::OPTION_SEARCH_TYPE_QUERY_THEN_FETCH, - \Elastica\Search::OPTION_SIZE => $maxResults); - - /* ADD SCOPE FILTER - $term = new Elastica\Filter\Term(); - $term->setTerm("ajxp_scope", "user");*/ - - $qb = new Elastica\QueryBuilder(); - $fullQuery = new Elastica\Query(); - $fullQuery->setQuery( - $qb->query()->bool() - ->addMust($fieldQuery) - ->addMust($qb->query()->match("ajxp_scope", "user")) - ->addMust($qb->query()->match("user", AuthService::getLoggedUser()->getId()) - ) - ); - - $result = $search->search($fullQuery, $searchOptions); - $this->logDebug(__FUNCTION__,"Search finished. "); - $hits = $result->getResults(); - - AJXP_XMLWriter::header(); - - $leafNodes = []; - foreach ($hits as $hit) { - if ($hit->serialized_metadata!=null) { - $meta = unserialize(base64_decode($hit->serialized_metadata)); - $tmpNode = new AJXP_Node(SystemTextEncoding::fromUTF8($hit->node_url), $meta); - } else { - $tmpNode = new AJXP_Node(SystemTextEncoding::fromUTF8($hit->node_url), array()); - $tmpNode->loadNodeInfo(); - } - if (!file_exists($tmpNode->getUrl())) { - $this->currentType->deleteById($hit->id); - continue; - } - $tmpNode->search_score = sprintf("%0.2f", $hit->score); - - if($tmpNode->isLeaf()){ - $leafNodes[]= $tmpNode; - } else { - AJXP_XMLWriter::renderAjxpNode($tmpNode); - } - - } - foreach ($leafNodes as $leaf) { - AJXP_XMLWriter::renderAjxpNode($leaf); - } - - AJXP_XMLWriter::close(); - } - - } - - /** - * @param $url - */ - public function recursiveIndexation($url) - { - //print("Indexing $url \n"); - $this->logDebug("Indexing content of folder ".$url); - if (ConfService::currentContextIsCommandLine() && $this->verboseIndexation) { - print("Indexing content of ".$url."\n"); - } - @set_time_limit(60); - $handle = opendir($url); - - if ($handle !== false) { - while ( ($child = readdir($handle)) != false) { - if($child[0] == ".") continue; - $newUrl = $url."/".$child; - $this->logDebug("Indexing Node ".$newUrl); - try { - $this->updateNodeIndex(null, new AJXP_Node($newUrl)); - } catch (Exception $e) { - $this->logDebug("Error Indexing Node ".$newUrl." (".$e->getMessage().")"); - } - } - closedir($handle); - } else { - $this->logDebug("Cannot open $url!!"); - } - } - - /** - * - * Hooked to node.meta_change, this will update the index - * - * @param AJXP_Node $node - */ - public function updateNodeIndexMeta($node) - { - $this->loadIndex($node->getRepositoryId(), true, $node->getUser()); - if (AuthService::usersEnabled() && AuthService::getLoggedUser()!=null) { - - $query = new Elastica\Query\Term(); - $query->setTerm("node_url", $node->getUrl()); - $results = $this->currentType->search($query); - $hits = $results->getResults(); - foreach ($hits as $hit) { - $source = $hit->getSource(); - if ($source['ajxp_scope'] == 'shared' || ($source['ajxp_scope'] == 'user' && $source['ajxp_user'] == AuthService::getLoggedUser()->getId())) { - $this->currentType->deleteById($hit->getId()); - } - } - } else { - $id = $this->getIndexedDocumentId($node); - if($id != null) $this->currentType->deleteById($id); - } - $this->createIndexedDocument($node); - - } - - /** - * - * Hooked to node.change, this will update the index - * if $oldNode = null => create node $newNode - * if $newNode = null => delete node $oldNode - * Else copy or move oldNode to newNode. - * - * @param AJXP_Node $oldNode - * @param AJXP_Node $newNode - * @param Boolean $copy - * @param bool $recursive - */ - public function updateNodeIndex($oldNode, $newNode = null, $copy = false, $recursive = false) - { - if($oldNode == null){ - $this->loadIndex($newNode->getRepositoryId(), true, $newNode->getUser()); - }else{ - $this->loadIndex($oldNode->getRepositoryId(), true, $oldNode->getUser()); - } - - if ($oldNode != null && $copy == false) { - $oldDocId = $this->getIndexedDocumentId($oldNode); - if ($oldDocId != null) { - $this->currentType->deleteById($oldDocId); - $childrenHits = $this->getIndexedChildrenDocuments($newNode); - - if ($childrenHits != null) { - $childrenHits = $childrenHits->getResults(); - - foreach ($childrenHits as $hit) { - $this->currentType->deleteById($hit->getId()); - } - } - } - } - - if ($newNode != null) { - // Make sure it does not already exists anyway - $newDocId = $this->getIndexedDocumentId($newNode); - if ($newDocId != null) { - try{ - $this->currentType->deleteById($newDocId); - }catch (Elastica\Exception\NotFoundException $eEx){ - $this->logError(__FUNCTION__, "Trying to delete a non existing document"); - } - $childrenHits = $this->getIndexedChildrenDocuments($newNode); - if ($childrenHits != null) { - $childrenHits = $childrenHits->getResults(); - - foreach ($childrenHits as $hit) { - try{ - $this->currentType->deleteById($hit->getId()); - }catch (Elastica\Exception\NotFoundException $eEx){ - $this->logError(__FUNCTION__, "Trying to delete a non existing document"); - } - } - } - } - - $this->createIndexedDocument($newNode); - - if ($recursive && $oldNode == null && is_dir($newNode->getUrl())) { - $this->recursiveIndexation($newNode->getUrl()); - } - } - - - if ($oldNode != null && $newNode != null && is_dir($newNode->getUrl())) { // Copy / Move / Rename - // Get old node children docs, and update them manually, no need to scan real directory - $childrenHits = $this->getIndexedChildrenDocuments($oldNode); - if ($childrenHits != null) { - $childrenHits = $childrenHits->getResults(); - - foreach ($childrenHits as $hit) { - $oldChildURL = $this->currentType->getDocument($hit->getId())->get("node_url"); - if ($copy == false) { - $this->currentType->deleteById($hit->getId()); - } - $newChildURL = str_replace(SystemTextEncoding::toUTF8($oldNode->getUrl()), - SystemTextEncoding::toUTF8($newNode->getUrl()), - $oldChildURL); - $newChildURL = SystemTextEncoding::fromUTF8($newChildURL); - $this->createIndexedDocument(new AJXP_Node($newChildURL)); - } - } - } - } - - /** - * @param AJXP_Node $ajxpNode - * @throws Exception - */ - public function createIndexedDocument($ajxpNode) - { - $ajxpNode->loadNodeInfo(); - - $parseContent = $this->indexContent; - if ($parseContent && $ajxpNode->bytesize > $this->getFilteredOption("PARSE_CONTENT_MAX_SIZE")) { - $parseContent = false; - } - - $data = []; - $data["node_url"] = $ajxpNode->getUrl(); - $data["node_path"] = str_replace("/", "AJXPFAKESEP", $ajxpNode->getPath()); - $data["basename"] = basename($ajxpNode->getPath()); - $data["ajxp_node"] = "yes"; - $data["ajxp_scope"] = "shared"; - $data["serialized_metadata"] = base64_encode(serialize($ajxpNode->metadata)); - $data["ajxp_modiftime"] = date("Ymd", $ajxpNode->ajxp_modiftime); - $data["ajxp_bytesize"] = $ajxpNode->bytesize; - $ajxpMime = $ajxpNode->ajxp_mime; - if (empty($ajxpMime)) { - $data["ajxp_mime"] = pathinfo($ajxpNode->getLabel(), PATHINFO_EXTENSION); - } else { - $data["ajxp_mime"] = $ajxpNode->ajxp_mime; - } - - if (isSet($ajxpNode->indexableMetaKeys["shared"])) { - foreach ($ajxpNode->indexableMetaKeys["shared"] as $sharedField) { - if ($ajxpNode->$sharedField) { - $data[$sharedField] = $ajxpNode->$sharedField; - } - } - } - foreach ($this->metaFields as $field) { - if ($ajxpNode->$field != null) { - $data["ajxp_meta_$field"] = $ajxpNode->$field; - } - } - if($parseContent){ - $body = $this->extractIndexableContent($ajxpNode); - if(!empty($body)){ - $data["body"] = $body; - } - } - - $mapping = new Elastica\Type\Mapping(); - $mapping->setType($this->currentType); - $mapping->setProperties($this->dataToMappingProperties($data)); - $mapping->send(); - $doc = new Elastica\Document($this->nextId, $data); - $this->currentType->addDocument($doc); - $this->nextId++; - - if (isSet($ajxpNode->indexableMetaKeys["user"]) && count($ajxpNode->indexableMetaKeys["user"]) && AuthService::usersEnabled() && AuthService::getLoggedUser() != null) { - - $userData = array( - "ajxp_scope" => "user", - "user" => AuthService::getLoggedUser()->getId(), - "serialized_metadata" => $data["serialized_metadata"], - "node_url" => $data["node_url"], - "node_path" => $data["node_path"] - ); - $userData["ajxp_user"] = AuthService::getLoggedUser()->getId(); - foreach ($ajxpNode->indexableMetaKeys["user"] as $userField) { - if ($ajxpNode->$userField) { - $userData[$userField] = $ajxpNode->$userField; - } - } - $mapping = new Elastica\Type\Mapping(); - $mapping->setType($this->currentType); - $mapping->setProperties($this->dataToMappingProperties($userData)); - $mapping->send(); - $doc = new Elastica\Document($this->nextId, $userData); - $this->currentType->addDocument($doc); - $this->nextId++; - } - - /* we update the last id in the file */ - $file = fopen($this->lastIdPath, "w"); - fputs($file, $this->nextId-1); - fclose($file); - - } - - /** - * Transform not data to ready to store mapping - * @param $data - * @return array - */ - protected function dataToMappingProperties($data){ - - $mapping_properties = []; - foreach ($data as $key => $value) { - if ($key == "node_url" || $key == "node_path") { - $mapping_properties[$key] = ["type" => "string", "index" => "not_analyzed"]; - } else if($key == "serialized_metadata"){ - $mapping_properties[$key] = ["type" => "string" /*, "index" => "no" */]; - } else if ($key == "ajxp_bytesize"){ - $mapping_properties[$key] = ["type" => "long"]; - } else { - $type = gettype($value); - if ($type != "integer" && $type != "boolean" && $type != "double") { - $type = "string"; - } - $mapping_properties[$key] = ["type" => $type]; - } - } - return $mapping_properties; - - } - - /** - * @param AJXP_Node $ajxpNode - * @return Number - */ - public function getIndexedDocumentId($ajxpNode) - { - /* - we used a term query that will check for the exact term (here is the path to a file) - that can't be duplicate.Thus it will get the right result. - */ - $query = new Elastica\Query\Term(); - $query->setTerm("node_url", $ajxpNode->getUrl()); - - /* returns a result set from the query */ - $results = $this->currentIndex->search($query); - - if($results->getTotalHits() == 0) return null; - - return $results->current()->getId(); - } - - /** - * Find all existing lucene documents based on the parent url - * @param AJXP_Node $ajxpNode - * @return Elastica\ResultSet - */ - public function getIndexedChildrenDocuments($ajxpNode) - { - $testQ = str_replace("/", "AJXPFAKESEP", SystemTextEncoding::toUTF8($ajxpNode->getPath()."/")); - - /* we use a wildcard query to fetch all children documents relatively to their paths */ - $query = new Elastica\Query\Wildcard("node_path", $testQ."*"); - - /* returns a result set from the query */ - $results = $this->currentIndex->search($query); - - if($results->getTotalHits() == 0) return null; - - return $results; - } - - /** - * - * load the index into the class parameter currentIndex - * @param Integer $repositoryId - * @param bool $create - * @param null $resolveUserId - */ - protected function loadIndex($repositoryId, $create = true, $resolveUserId = null) - { - $specificId = $this->buildSpecificId($repositoryId, $resolveUserId); - - $this->currentIndex = $this->client->getIndex($specificId); - - /* if the cache directory for the repository index is not created we do create it */ - $iPath = (defined('AJXP_SHARED_CACHE_DIR')?AJXP_SHARED_CACHE_DIR:AJXP_CACHE_DIR)."/indexes/".$specificId; - if(!is_dir($iPath)) mkdir($iPath,0755, true); - - if ($create && !$this->currentIndex->exists()) { - $this->currentIndex->create(); - } - - $this->currentType = new Elastica\Type($this->currentIndex, "type_".$specificId); - - /* we fetch the last id we used to create a document and set the variable nextId */ - $this->lastIdPath = (defined('AJXP_SHARED_CACHE_DIR')?AJXP_SHARED_CACHE_DIR:AJXP_CACHE_DIR)."/indexes/".$specificId."/last_id"; - if (file_exists($this->lastIdPath)) { - $file = fopen($this->lastIdPath, "r"); - $this->nextId = floatval(fgets($file)) + 1; - fclose($file); - } else { - touch($this->lastIdPath); - $this->nextId = 1; - } - } -} \ No newline at end of file diff --git a/core/src/plugins/index.elasticsearch/manifest.xml b/core/src/plugins/index.elasticsearch/manifest.xml index d5b12522cc..1d465515e8 100644 --- a/core/src/plugins/index.elasticsearch/manifest.xml +++ b/core/src/plugins/index.elasticsearch/manifest.xml @@ -4,7 +4,7 @@ - + @@ -40,7 +40,7 @@ write="false" adminOnly="false"> - + @@ -48,23 +48,24 @@ write="false" adminOnly="false"> - + -
      -
      -
      - ]]>
      - - -

      AJXP_MESSAGE[share_center.98] AJXP_MESSAGE[513]

      - -
      - ]]> + + + + + diff --git a/core/src/plugins/index.elasticsearch/plugin_doc.html b/core/src/plugins/index.elasticsearch/plugin_doc.html index 4e861b0dc3..e32776e04a 100644 --- a/core/src/plugins/index.elasticsearch/plugin_doc.html +++ b/core/src/plugins/index.elasticsearch/plugin_doc.html @@ -17,7 +17,7 @@ - Create an index called "test_index":
      $ curl -XPUT 'localhost:9200/test_index'

      - Then add a document with the type "test_type" with the following command:
      -
      $ curl -XPUT 'localhost:9200/test_index/test_type/1 -d '{"user":"test", "message":"this is a test"}'
      +
      $ curl -XPUT 'localhost:9200/test_index/test_type/1' -d '{"user":"test", "message":"this is a test"}'
      This will add a document with id equals to 1 containing two fields "user" and "message" that respective values are "test" and "this is a test".
      The document will be added in the index "test_index" under the type "test_type".

      - You can retrieve the documents by their id: diff --git a/core/src/plugins/index.elasticsearch/resources/i18n/conf/de.php b/core/src/plugins/index.elasticsearch/resources/i18n/conf/de.php index 788c1c3be0..b9da61b22b 100644 --- a/core/src/plugins/index.elasticsearch/resources/i18n/conf/de.php +++ b/core/src/plugins/index.elasticsearch/resources/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "ElasticSearch Search Engine" => "ElasticSearch", @@ -49,4 +49,4 @@ "ElasticSearch Server host (without http)" => "ElasticSearch Server host (without http)", "ElasticSearch Port" => "ElasticSearch Port", "ElasticSearch Server port (default 9200)" => "ElasticSearch Server port (default 9200)", -); \ No newline at end of file +); diff --git a/core/src/plugins/index.elasticsearch/resources/i18n/conf/en.php b/core/src/plugins/index.elasticsearch/resources/i18n/conf/en.php index 57ae52097b..630db8e668 100644 --- a/core/src/plugins/index.elasticsearch/resources/i18n/conf/en.php +++ b/core/src/plugins/index.elasticsearch/resources/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "ElasticSearch Search Engine" => "ElasticSearch Search Engine", diff --git a/core/src/plugins/index.elasticsearch/resources/i18n/conf/fr.php b/core/src/plugins/index.elasticsearch/resources/i18n/conf/fr.php index 3cd8bc6acf..47a0b9f112 100644 --- a/core/src/plugins/index.elasticsearch/resources/i18n/conf/fr.php +++ b/core/src/plugins/index.elasticsearch/resources/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Lucene Search Engine" => "Moteur de recherche Lucene", diff --git a/core/src/plugins/index.elasticsearch/resources/i18n/conf/it.php b/core/src/plugins/index.elasticsearch/resources/i18n/conf/it.php index cf1d6bb84d..340ad0c006 100644 --- a/core/src/plugins/index.elasticsearch/resources/i18n/conf/it.php +++ b/core/src/plugins/index.elasticsearch/resources/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Lucene Search Engine" => "Motore Rierca Lucene", @@ -51,4 +51,4 @@ "ElasticSearch Server host (without http)" => "ElasticSearch Server host (without http)", "ElasticSearch Port" => "ElasticSearch Port", "ElasticSearch Server port (default 9200)" => "ElasticSearch Server port (default 9200)", -); \ No newline at end of file +); diff --git a/core/src/plugins/index.elasticsearch/resources/i18n/conf/pt.php b/core/src/plugins/index.elasticsearch/resources/i18n/conf/pt.php index 98557eb217..c4f566386c 100644 --- a/core/src/plugins/index.elasticsearch/resources/i18n/conf/pt.php +++ b/core/src/plugins/index.elasticsearch/resources/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Lucene Search Engine" => "Motor de pesquisa Lucene", diff --git a/core/src/plugins/index.elasticsearch/resources/i18n/de.php b/core/src/plugins/index.elasticsearch/resources/i18n/de.php index 04c73f60f9..6ced872ed0 100644 --- a/core/src/plugins/index.elasticsearch/resources/i18n/de.php +++ b/core/src/plugins/index.elasticsearch/resources/i18n/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess = array( diff --git a/core/src/plugins/index.elasticsearch/resources/i18n/en.php b/core/src/plugins/index.elasticsearch/resources/i18n/en.php index af26a2cae2..ba8a212a52 100644 --- a/core/src/plugins/index.elasticsearch/resources/i18n/en.php +++ b/core/src/plugins/index.elasticsearch/resources/i18n/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess = array( diff --git a/core/src/plugins/index.elasticsearch/resources/i18n/es.php b/core/src/plugins/index.elasticsearch/resources/i18n/es.php index eee650e980..fedfef20c0 100644 --- a/core/src/plugins/index.elasticsearch/resources/i18n/es.php +++ b/core/src/plugins/index.elasticsearch/resources/i18n/es.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess = array( diff --git a/core/src/plugins/index.elasticsearch/resources/i18n/fr.php b/core/src/plugins/index.elasticsearch/resources/i18n/fr.php index b3687bb234..21ed65d02c 100644 --- a/core/src/plugins/index.elasticsearch/resources/i18n/fr.php +++ b/core/src/plugins/index.elasticsearch/resources/i18n/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess = array( diff --git a/core/src/plugins/index.elasticsearch/resources/i18n/it.php b/core/src/plugins/index.elasticsearch/resources/i18n/it.php index c42f721907..0124e9426b 100644 --- a/core/src/plugins/index.elasticsearch/resources/i18n/it.php +++ b/core/src/plugins/index.elasticsearch/resources/i18n/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess = array( diff --git a/core/src/plugins/index.elasticsearch/resources/i18n/pt.php b/core/src/plugins/index.elasticsearch/resources/i18n/pt.php index 564b6a3ccf..78f7d0d8bc 100644 --- a/core/src/plugins/index.elasticsearch/resources/i18n/pt.php +++ b/core/src/plugins/index.elasticsearch/resources/i18n/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess = array( diff --git a/core/src/plugins/index.lucene/LuceneIndexer.php b/core/src/plugins/index.lucene/LuceneIndexer.php new file mode 100644 index 0000000000..823d02da2f --- /dev/null +++ b/core/src/plugins/index.lucene/LuceneIndexer.php @@ -0,0 +1,835 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Indexer\Implementation; + +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Model\NodesList; +use Pydio\Access\Indexer\Core\AbstractSearchEngineIndexer; +use Pydio\Core\Http\Message\UserMessage; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Controller\Controller; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Services\UsersService; +use Pydio\Core\Services\ApplicationState; +use Pydio\Core\Utils\Vars\StatHelper; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Encapsultion of the Zend_Search_Lucene component as a plugin + * @package AjaXplorer_Plugins + * @subpackage Index + */ +class LuceneIndexer extends AbstractSearchEngineIndexer +{ + /** + * @var \Zend_Search_Lucene_Interface + */ + private $currentIndex; + private $metaFields = []; + private $indexContent = false; + private $verboseIndexation = false; + + /** + * @param ContextInterface $ctx + * @param array $options + */ + public function init(ContextInterface $ctx, $options = []) + { + parent::init($ctx, $options); + set_include_path(get_include_path().PATH_SEPARATOR.AJXP_INSTALL_PATH."/plugins/index.lucene"); + $metaFields = $this->getContextualOption($ctx, "index_meta_fields"); + if (!empty($metaFields)) { + $this->metaFields = explode(",",$metaFields); + } + $this->indexContent = ($this->getContextualOption($ctx, "index_content") == true); + } + + /** + * @param ContextInterface $contextInterface + * @param AbstractAccessDriver $accessDriver + */ + public function initMeta(ContextInterface $contextInterface, AbstractAccessDriver $accessDriver) + { + parent::initMeta($contextInterface, $accessDriver); + if (!empty($this->metaFields) || $this->indexContent) { + $metaFields = $this->metaFields; + $el = $this->getXPath()->query("/indexer")->item(0); + if ($this->indexContent) { + if($this->indexContent) $metaFields[] = "ajxp_document_content"; + $data = ["indexed_meta_fields" => $metaFields, + "additionnal_meta_columns" => ["ajxp_document_content" => "Content"] + ]; + $el->setAttribute("indexed_meta_fields", json_encode($data)); + } else { + $el->setAttribute("indexed_meta_fields", json_encode($metaFields)); + } + } + parent::init($contextInterface, $this->options); + } + + + /** + * @param string $queryAnalyzer + * @param int $wildcardLimitation + */ + protected function setDefaultAnalyzer($queryAnalyzer, $wildcardLimitation) + { + switch ($queryAnalyzer) { + case "utf8num_insensitive": + \Zend_Search_Lucene_Analysis_Analyzer::setDefault(new \Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive()); + break; + case "utf8num_sensitive": + \Zend_Search_Lucene_Analysis_Analyzer::setDefault(new \Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num()); + break; + case "utf8_insensitive": + \Zend_Search_Lucene_Analysis_Analyzer::setDefault(new \Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8_CaseInsensitive()); + break; + case "utf8_sensitive": + \Zend_Search_Lucene_Analysis_Analyzer::setDefault(new \Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8()); + break; + case "textnum_insensitive": + \Zend_Search_Lucene_Analysis_Analyzer::setDefault(new \Zend_Search_Lucene_Analysis_Analyzer_Common_TextNum_CaseInsensitive()); + break; + case "textnum_sensitive": + \Zend_Search_Lucene_Analysis_Analyzer::setDefault(new \Zend_Search_Lucene_Analysis_Analyzer_Common_TextNum()); + break; + case "text_insensitive": + \Zend_Search_Lucene_Analysis_Analyzer::setDefault(new \Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive()); + break; + case "text_sensitive": + \Zend_Search_Lucene_Analysis_Analyzer::setDefault(new \Zend_Search_Lucene_Analysis_Analyzer_Common_Text()); + break; + default: + \Zend_Search_Lucene_Analysis_Analyzer::setDefault(new \Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive()); + break; + } + \Zend_Search_Lucene_Search_Query_Wildcard::setMinPrefixLength($wildcardLimitation); + + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $requestInterface + * @param \Psr\Http\Message\ResponseInterface $responseInterface + * @return array|null + * @throws \Exception + */ + public function applyAction(\Psr\Http\Message\ServerRequestInterface &$requestInterface, \Psr\Http\Message\ResponseInterface &$responseInterface) + { + $messages = LocaleService::getMessages(); + $actionName = $requestInterface->getAttribute("action"); + $httpVars = $requestInterface->getParsedBody(); + /** @var ContextInterface $ctx */ + $ctx = $requestInterface->getAttribute("ctx"); + $ctxUser = $ctx->getUser(); + $repoId = $ctx->getRepositoryId(); + + $x = new \Pydio\Core\Http\Response\SerializableResponseStream(); + $responseInterface = $responseInterface->withBody($x); + + if ($actionName == "search") { + + // TMP + if (strpos($httpVars["query"], "keyword:") === 0) { + $parts = explode(":", $httpVars["query"]); + $requestInterface = $requestInterface->withAttribute("action", "search_by_keyword")->withParsedBody(["field" => $parts[1]]); + $this->applyAction($requestInterface, $responseInterface); + return null; + } + + require_once("Zend/Search/Lucene.php"); + try { + $index = $this->loadIndex($ctx, false); + } catch (\Exception $ex) { + if ($this->seemsCurrentlyIndexing($ctx, 3)){ + $x->addChunk(new UserMessage($messages["index.lucene.11"])); + }else if (ConfService::backgroundActionsSupported() && !ApplicationState::sapiIsCli() && !isSet($httpVars["skip_unindexed"])) { + $task = \Pydio\Tasks\TaskService::actionAsTask($ctx, "index", []); + $task->setLabel($messages["index.lucene.7"]); + $responseInterface = \Pydio\Tasks\TaskService::getInstance()->enqueueTask($task, $requestInterface, $responseInterface); + $x->addChunk(new UserMessage($messages["index.lucene.7"])); + }else{ + $x->addChunk(new UserMessage($messages["index.lucene.12"])); + } + return null; + } + $textQuery = $httpVars["query"]; + if($this->getContextualOption($ctx, "AUTO_WILDCARD") === true && strlen($textQuery) > 0 && ctype_alnum($textQuery)){ + if($textQuery[0] == '"' && $textQuery[strlen($textQuery)-1] == '"'){ + $textQuery = substr($textQuery, 1, -1); + }else if($textQuery[strlen($textQuery)-1] != "*" ){ + $textQuery.="*"; + } + } + if(strpos($textQuery, ":") !== false){ + $textQuery = str_replace("ajxp_meta_ajxp_document_content:","body:", $textQuery); + $textQuery = $this->filterSearchRangesKeywords($textQuery); + $query = "ajxp_scope:shared AND ($textQuery)"; + } + else if ((isSet($this->metaFields) || $this->indexContent) && isSet($httpVars["fields"])) { + $sParts = []; + foreach (explode(",",$httpVars["fields"]) as $searchField) { + if ($searchField == "filename") { + $sParts[] = "basename:".$textQuery; + } else if ($searchField == "ajxp_document_content"){ + $sParts[] = $textQuery; + } else if (in_array($searchField, $this->metaFields)) { + $sParts[] = "ajxp_meta_".$searchField.":".$textQuery; + } else if ($searchField == "ajxp_document_content") { + $sParts[] = "title:".$textQuery; + $sParts[] = "body:".$textQuery; + $sParts[] = "keywords:".$textQuery; + } + } + $query = implode(" OR ", $sParts); + $query = "ajxp_scope:shared AND ($query)"; + $this->logDebug("Query : $query"); + } else { + $index->setDefaultSearchField("basename"); + $query = $this->filterSearchRangesKeywords($textQuery); + } + $this->setDefaultAnalyzer( + $this->getContextualOption($ctx, "QUERY_ANALYSER"), + intval($this->getContextualOption($ctx, "WILDCARD_LIMITATION")) + ); + if ($query == "*") { + $index->setDefaultSearchField("ajxp_node"); + $query = "yes"; + $hits = $index->find($query); + } else { + if(isSet($httpVars["limit"])){ + $limit = intval($httpVars["limit"]); + // Ask one more to detect if there is "more" results or not. + $index->setResultSetLimit( $limit + 50 ); + } + $hits = $index->find($query); + } + $commitIndex = false; + + $nodesList = new NodesList(); + $x->addChunk($nodesList); + + $cursor = 0; + if(!empty($limit) && count($hits) > $limit){ + $nodesList->setPaginationData(count($hits), 1, 1); + } + foreach ($hits as $hit) { + // Backward compatibility + $hit->node_url = preg_replace("#ajxp\.[a-z_]+://#", "pydio://", $hit->node_url); + if ($hit->serialized_metadata!=null) { + $meta = unserialize(base64_decode($hit->serialized_metadata)); + if(isSet($meta["ajxp_modiftime"])){ + $meta["ajxp_relativetime"] = $meta["ajxp_description"] = $messages[4]." ". StatHelper::relativeDate($meta["ajxp_modiftime"], $messages); + } + $tmpNode = new AJXP_Node($hit->node_url, $meta); + if(!$tmpNode->hasUser()){ + if($hit->ajxp_scope === "user" && $hit->ajxp_user) $tmpNode->setUserId($hit->ajxp_user); + else $tmpNode->setUserId($ctx->getUser()->getId()); + } + } else { + $tmpNode = new AJXP_Node($hit->node_url, []); + if(!$tmpNode->hasUser()){ + if($hit->ajxp_scope === "user" && $hit->ajxp_user) $tmpNode->setUserId($hit->ajxp_user); + else $tmpNode->setUserId($ctx->getUser()->getId()); + } + $tmpNode->loadNodeInfo(); + } + if($tmpNode->getRepositoryId() != $repoId){ + $this->logDebug(__CLASS__, "Strange case, search retrieves a node from wrong repository!"); + $index->delete($hit->id); + $commitIndex = true; + continue; + } + if (!file_exists($tmpNode->getUrl())) { + $index->delete($hit->id); + $commitIndex = true; + continue; + } + if (!is_readable($tmpNode->getUrl())){ + continue; + } + $basename = basename($tmpNode->getPath()); + $isLeaf = $tmpNode->isLeaf(); + if (!$ctx->getRepository()->getDriverInstance($ctx)->filterNodeName($ctx, $tmpNode->getPath(), $basename, $isLeaf, ["d" => true, "f" => true, "z" => true])){ + continue; + } + $tmpNode->search_score = sprintf("%0.2f", $hit->score); + $nodesList->addBranch($tmpNode); + $cursor++; + if(isSet($limit) && $cursor >= $limit) break; + } + + if ($commitIndex) { + $index->commit(); + } + } else if ($actionName == "search_by_keyword") { + require_once("Zend/Search/Lucene.php"); + $scope = "user"; + + try { + $index = $this->loadIndex($ctx, false); + } catch (\Exception $ex) { + if (ConfService::backgroundActionsSupported() && !ApplicationState::sapiIsCli()) { + $task = \Pydio\Tasks\TaskService::actionAsTask($ctx, "index", []); + $task->setLabel($messages["index.lucene.7"]); + $responseInterface = \Pydio\Tasks\TaskService::getInstance()->enqueueTask($task, $requestInterface, $responseInterface); + $x->addChunk(new UserMessage($messages["index.lucene.7"])); + } + return null; + } + $sParts = []; + $searchField = $httpVars["field"]; + if ($searchField == "ajxp_node") { + $sParts[] = "$searchField:yes"; + } else { + $sParts[] = "$searchField:true"; + } + if ($scope == "user" && UsersService::usersEnabled()) { + if ($ctxUser == null) { + throw new \Exception("Cannot find current user"); + } + $sParts[] = "ajxp_scope:user"; + $sParts[] = "ajxp_user:".$ctxUser->getId(); + } else { + $sParts[] = "ajxp_scope:shared"; + } + $query = implode(" AND ", $sParts); + $this->logDebug("Query : $query"); + $index->setResultSetLimit(isSet($httpVars["limit"]) ? intval($httpVars["limit"]) : 50); + $hits = $index->find($query); + + $commitIndex = false; + + $nodesList = new NodesList(); + $x->addChunk($nodesList); + $leafNodes = []; + foreach ($hits as $hit) { + // Backward compat with old protocols + $hit->node_url = preg_replace("#ajxp\.[a-z_]+://#", "pydio://", $hit->node_url); + if ($hit->serialized_metadata!=null) { + $meta = unserialize(base64_decode($hit->serialized_metadata)); + $tmpNode = new AJXP_Node($hit->node_url, $meta); + if(!$tmpNode->hasUser()){ + if($hit->ajxp_user) $tmpNode->setUserId($hit->ajxp_user); + else $tmpNode->setUserId($ctx->getUser()->getId()); + } + } else { + $tmpNode = new AJXP_Node($hit->node_url, []); + if(!$tmpNode->hasUser()){ + if($hit->ajxp_user) $tmpNode->setUserId($hit->ajxp_user); + else $tmpNode->setUserId($ctx->getUser()->getId()); + } + $tmpNode->loadNodeInfo(); + } + if (!file_exists($tmpNode->getUrl())) { + $index->delete($hit->id); + $commitIndex = true; + continue; + } + if (!is_readable($tmpNode->getUrl())){ + continue; + } + $basename = basename($tmpNode->getPath()); + $isLeaf = $tmpNode->isLeaf(); + if (!$ctx->getRepository()->getDriverInstance($ctx)->filterNodeName($ctx, $tmpNode->getPath(), $basename, $isLeaf, ["d"=>true, "f"=>true])){ + continue; + } + $tmpNode->search_score = sprintf("%0.2f", $hit->score); + if($isLeaf){ + $leafNodes[]= $tmpNode; + }else{ + $nodesList->addBranch($tmpNode); + } + } + foreach ($leafNodes as $leaf){ + $nodesList->addBranch($leaf); + } + if ($commitIndex) { + $index->commit(); + } + } + if(isSet($returnNodes)) return $returnNodes; + else return null; + } + + /** + * @param AJXP_Node $parentNode + */ + public function indexationStarts($parentNode){ + $this->currentIndex = $this->loadTemporaryIndex($parentNode->getContext()); + } + + /** + * @param AJXP_Node $parentNode + */ + public function indexationEnds($parentNode){ + $this->logDebug('INDEX.END', 'Optimizing Index'); + $this->currentIndex->optimize(); + $this->logDebug('INDEX.END', 'Commiting Index'); + $this->currentIndex->commit(); + unset($this->currentIndex); + $this->logDebug('INDEX.END', 'Merging Temporary in main'); + $this->mergeTemporaryIndexToMain($parentNode->getContext()); + $this->logDebug('INDEX.END', 'Done'); + } + + /** + * @param AJXP_Node $node + */ + public function indexationIndexNode($node){ + $this->updateNodeIndex(null, $node, false, false); + } + + /** + * @param $url + */ + public function recursiveIndexation($url) + { + //print("Indexing $url \n"); + $this->logDebug("Indexing content of folder ".$url); + if (ApplicationState::sapiIsCli() && $this->verboseIndexation) { + print("Indexing content of ".$url."\n"); + } + if(!ApplicationState::sapiIsCli()) @set_time_limit(60); + $handle = opendir($url); + if ($handle !== false) { + while ( ($child = readdir($handle)) != false) { + if($child[0] == ".") continue; + $newUrl = $url."/".$child; + if (ApplicationState::sapiIsCli() && $this->verboseIndexation) { + print("Indexing node ".$newUrl."\n"); + } + $this->logDebug("Indexing Node ".$newUrl); + try { + $newNode = new AJXP_Node($newUrl); + $this->updateNodeIndex(null, $newNode, false, true); + Controller::applyHook("node.index.add", [$newNode]); + } catch (\Exception $e) { + if (ApplicationState::sapiIsCli() && $this->verboseIndexation) { + print("Error indexing node ".$newUrl." (".$e->getMessage().") \n"); + } + $this->logDebug("Error Indexing Node ".$newUrl." (".$e->getMessage().")"); + } + } + closedir($handle); + } else { + $this->logDebug("Cannot open $url!!"); + } + } + + /** + * Passes the array of META_SOURCES options before creating the shared workspace. + * Used here to disable the repository_specific_keywords, as path is in fact already fully resolved. + * @param ContextInterface $context + * @param $metaOptions array + */ + public function updateSharedChildOptions($context, &$metaOptions){ + if(isSet($metaOptions["index.lucene"]) && isSet($metaOptions["index.lucene"]["repository_specific_keywords"])){ + unset($metaOptions["index.lucene"]["repository_specific_keywords"]); + } + } + + /** + * Called on workspace.after_delete event, clear the index! + * @param $repoId + */ + public function clearWorkspaceIndexes($repoId){ + $iPath = $this->getIndexPath($repoId); + $this->clearIndexIfExists($iPath); + $this->clearIndexIfExists($iPath."-PYDIO_TMP"); + } + + /** + * + * Hooked to node.meta_change, this will update the index + * + * @param AJXP_Node $node + */ + public function updateNodeIndexMeta($node) + { + require_once("Zend/Search/Lucene.php"); + try{ + + if (isSet($this->currentIndex)) { + $index = $this->currentIndex; + } else { + $index = $this->loadIndex($node->getContext(), true); + } + \Zend_Search_Lucene_Analysis_Analyzer::setDefault( new \Zend_Search_Lucene_Analysis_Analyzer_Common_TextNum_CaseInsensitive()); + + if (UsersService::usersEnabled() && $node->getContext()->hasUser()) { + $term = new \Zend_Search_Lucene_Index_Term($node->getUrl(), "node_url"); + $hits = $index->termDocs($term); + foreach ($hits as $hitId) { + $hit = $index->getDocument($hitId); + if ($hit->ajxp_scope == 'shared' || ($hit->ajxp_scope == 'user' && $hit->ajxp_user == $node->getContext()->getUser()->getId())) { + $index->delete($hitId); + } + } + } else { + $id = $this->getIndexedDocumentId($index, $node); + if($id != null) $index->delete($id); + } + if(file_exists($node->getUrl())){ + $this->createIndexedDocument($node, $index); + } + $this->logDebug(__FILE__, "Indexation passed ".$node->getUrl()); + } catch (\Exception $e){ + $this->logError(__FILE__, "Lucene indexation failed for ".$node->getUrl()." (".$e->getMessage().")"); + } + } + + /** + * + * Hooked to node.change, this will update the index + * if $oldNode = null => create node $newNode + * if $newNode = null => delete node $oldNode + * Else copy or move oldNode to newNode. + * + * @param \Pydio\Access\Core\Model\AJXP_Node $oldNode + * @param \Pydio\Access\Core\Model\AJXP_Node $newNode + * @param Boolean $copy + * @param bool $recursive + */ + public function updateNodeIndex($oldNode, $newNode = null, $copy = false, $recursive = false) + { + require_once("Zend/Search/Lucene.php"); + if (isSet($this->currentIndex)) { + $oldIndex = $newIndex = $this->currentIndex; + } else { + if($oldNode == null){ + $newIndex = $oldIndex = $this->loadIndex($newNode->getContext(), true); + }else if($newNode == null){ + $oldIndex = $newIndex = $this->loadIndex($oldNode->getContext(), true); + }else{ + $newId = $newNode->getRepositoryId(); + $oldId = $oldNode->getRepositoryId(); + if($newId == $oldId){ + $newIndex = $oldIndex = $this->loadIndex($newNode->getContext(), true); + }else{ + $newIndex = $this->loadIndex($newNode->getContext(), true); + $oldIndex = $this->loadIndex($oldNode->getContext(), true); + } + } + } + $refNode = ($oldNode != null ? $oldNode : $newNode); + $this->setDefaultAnalyzer( + $this->getContextualOption($refNode->getContext(), "QUERY_ANALYSER"), + intval($this->getContextualOption($refNode->getContext(), "WILDCARD_LIMITATION")) + ); + if ($oldNode != null && $copy == false) { + $oldDocId = $this->getIndexedDocumentId($oldIndex, $oldNode); + if ($oldDocId != null) { + $oldIndex->delete($oldDocId); + if ($newNode == null) { // DELETION + $childrenHits = $this->getIndexedChildrenDocuments($oldIndex, $oldNode); + foreach ($childrenHits as $hit) { + $oldIndex->delete($hit->id); + } + } + } + } + + if ($newNode != null) { + // Make sure it does not already exists anyway + $newDocId = $this->getIndexedDocumentId($newIndex, $newNode); + if ($newDocId != null) { + $newIndex->delete($newDocId); + $childrenHits = $this->getIndexedChildrenDocuments($newIndex, $newNode); + foreach ($childrenHits as $hit) { + $newIndex->delete($hit->id); + } + } + $this->createIndexedDocument($newNode, $newIndex); + if ( $recursive && $oldNode == null && is_dir($newNode->getUrl())) { + $this->recursiveIndexation($newNode->getUrl()); + } + } + + if ($oldNode != null && $newNode != null && is_dir($newNode->getUrl()) && ($newIndex == $oldIndex)) { // Copy / Move / Rename + // Get old node children docs, and update them manually, no need to scan real directory + $childrenHits = $this->getIndexedChildrenDocuments($oldIndex, $oldNode); + foreach ($childrenHits as $hit) { + $oldChildURL = $oldIndex->getDocument($hit->id)->node_url; + if ($copy == false) { + $oldIndex->delete($hit->id); + } + $newChildURL = str_replace($oldNode->getUrl(),$newNode->getUrl(),$oldChildURL); + $this->createIndexedDocument(new AJXP_Node($newChildURL), $oldIndex); + } + } + + if (!isSet($this->currentIndex)) { + $oldIndex->commit(); + if($newIndex != $oldIndex){ + $newIndex->commit(); + } + } + } + + /** + * @param AJXP_Node $ajxpNode + * @param \Zend_Search_Lucene_Interface $index + * @throws \Exception + * @return \Zend_Search_Lucene_Document + */ + public function createIndexedDocument($ajxpNode, &$index) + { + if(!empty($this->metaFields)){ + $ajxpNode->loadNodeInfo(false, false, "all"); + }else{ + $ajxpNode->loadNodeInfo(); + } + $ext = strtolower(pathinfo($ajxpNode->getLabel(), PATHINFO_EXTENSION)); + $parseContent = $this->indexContent; + if ($parseContent && $ajxpNode->bytesize > $this->getContextualOption($ajxpNode->getContext(), "PARSE_CONTENT_MAX_SIZE")) { + $parseContent = false; + } + if ($parseContent && in_array($ext, explode(",", $this->getContextualOption($ajxpNode->getContext(), "PARSE_CONTENT_HTML")))) { + $doc = @\Zend_Search_Lucene_Document_Html::loadHTMLFile($ajxpNode->getUrl()); + } elseif ($parseContent && $ext == "docx" && class_exists("\Zend_Search_Lucene_Document_Docx")) { + $realFile = $ajxpNode->getRealFile(); + $doc = @\Zend_Search_Lucene_Document_Docx::loadDocxFile($realFile); + } elseif ($parseContent && $ext == "docx" && class_exists("\Zend_Search_Lucene_Document_Pptx")) { + $realFile = $ajxpNode->getRealFile(); + $doc = @\Zend_Search_Lucene_Document_Pptx::loadPptxFile($realFile); + } elseif ($parseContent && $ext == "xlsx" && class_exists("\Zend_Search_Lucene_Document_Xlsx")) { + $realFile = $ajxpNode->getRealFile(); + $doc = @\Zend_Search_Lucene_Document_Xlsx::loadXlsxFile($realFile); + } else { + $doc = new \Zend_Search_Lucene_Document(); + } + if($doc == null) throw new \Exception("Could not load document"); + + $doc->addField(\Zend_Search_Lucene_Field::keyword("node_url", $ajxpNode->getUrl(), "utf8")); + $doc->addField(\Zend_Search_Lucene_Field::keyword("node_path", str_replace("/", "AJXPFAKESEP", $ajxpNode->getPath()), "utf8")); + $doc->addField(\Zend_Search_Lucene_Field::text("basename", basename($ajxpNode->getPath()), "utf8")); + $doc->addField(\Zend_Search_Lucene_Field::keyword("ajxp_node", "yes")); + $doc->addField(\Zend_Search_Lucene_Field::keyword("ajxp_scope", "shared")); + $doc->addField(\Zend_Search_Lucene_Field::keyword("ajxp_modiftime", date("Ymd", $ajxpNode->ajxp_modiftime))); + $doc->addField(\Zend_Search_Lucene_Field::keyword("ajxp_bytesize", $ajxpNode->bytesize)); + $ajxpMime = $ajxpNode->ajxp_mime; + if (empty($ajxpMime)) { + $doc->addField(\Zend_Search_Lucene_Field::keyword("ajxp_mime", pathinfo($ajxpNode->getLabel(), PATHINFO_EXTENSION))); + } else { + $doc->addField(\Zend_Search_Lucene_Field::keyword("ajxp_mime", $ajxpNode->ajxp_mime)); + } + + + // Store a cached copy of the metadata + $serializedMeta = base64_encode(serialize($ajxpNode->metadata)); + $doc->addField(\Zend_Search_Lucene_Field::binary("serialized_metadata", $serializedMeta)); + if (isSet($ajxpNode->indexableMetaKeys["shared"])) { + foreach ($ajxpNode->indexableMetaKeys["shared"] as $sharedField) { + if($ajxpNode->$sharedField) $doc->addField(\Zend_Search_Lucene_Field::keyword($sharedField, $ajxpNode->$sharedField)); + } + } + foreach ($this->metaFields as $field) { + if ($ajxpNode->$field != null) { + $doc->addField(\Zend_Search_Lucene_Field::text("ajxp_meta_$field", $ajxpNode->$field, "utf8")); + } + } + if (isSet($ajxpNode->indexableMetaKeys["user"]) && count($ajxpNode->indexableMetaKeys["user"]) && UsersService::usersEnabled() && $ajxpNode->getContext()->hasUser() ) { + $privateDoc = new \Zend_Search_Lucene_Document(); + $privateDoc->addField(\Zend_Search_Lucene_Field::keyword("node_url", $ajxpNode->getUrl(), "utf8")); + $privateDoc->addField(\Zend_Search_Lucene_Field::keyword("node_path", str_replace("/", "AJXPFAKESEP", $ajxpNode->getPath()), "utf8")); + + $privateDoc->addField(\Zend_Search_Lucene_Field::keyword("ajxp_scope", "user")); + $privateDoc->addField(\Zend_Search_Lucene_Field::keyword("ajxp_user", $ajxpNode->getContext()->getUser()->getId())); + foreach ($ajxpNode->indexableMetaKeys["user"] as $userField) { + if ($ajxpNode->$userField) { + $privateDoc->addField(\Zend_Search_Lucene_Field::keyword($userField, $ajxpNode->$userField)); + } + } + $privateDoc->addField(\Zend_Search_Lucene_Field::binary("serialized_metadata", $serializedMeta)); + + $index->addDocument($privateDoc); + } + + if($parseContent){ + $body = $this->extractIndexableContent($ajxpNode); + if(!empty($body)) $doc->addField(\Zend_Search_Lucene_Field::unStored("body", $body)); + } + $index->addDocument($doc); + return $doc; + } + + /** + * @param \Zend_Search_Lucene_Interface $index + * @param AJXP_Node $ajxpNode + * @return Number + */ + public function getIndexedDocumentId($index, $ajxpNode) + { + $term = new \Zend_Search_Lucene_Index_Term($ajxpNode->getUrl(), "node_url"); + $docIds = $index->termDocs($term); + if(!count($docIds)) return null; + return $docIds[0]; + } + + /** + * Find all existing lucene documents based on the parent url + * @param \Zend_Search_Lucene_Interface $index + * @param AJXP_Node $ajxpNode + * @return \Zend_Search_Lucene_Search_QueryHit + */ + public function getIndexedChildrenDocuments($index, $ajxpNode) + { + // Try getting doc by url + $testQ = str_replace("/", "AJXPFAKESEP", $ajxpNode->getPath()); + $pattern = new \Zend_Search_Lucene_Index_Term($testQ .'*', 'node_path'); + $query = new \Zend_Search_Lucene_Search_Query_Wildcard($pattern); + $hits = $index->find($query); + return $hits; + } + + /** + * @param ContextInterface $ctx + * @return string + */ + protected function getIndexPath(ContextInterface $ctx){ + $mainCacheDir = (defined('AJXP_SHARED_CACHE_DIR')?AJXP_SHARED_CACHE_DIR:AJXP_CACHE_DIR); + if(!is_dir($mainCacheDir."/indexes")) mkdir($mainCacheDir."/indexes",0755,true); + $iPath = $mainCacheDir."/indexes/index-".$this->buildSpecificId($ctx); + return $iPath; + } + + /** + * @param ContextInterface $ctx + * @return \Zend_Search_Lucene_Interface + */ + protected function loadTemporaryIndex(ContextInterface $ctx){ + $indexPath = $this->getIndexPath($ctx); + $tmpIndexPath = $indexPath."-PYDIO_TMP"; + $this->clearIndexIfExists($tmpIndexPath); + $this->copyIndex($indexPath, $tmpIndexPath); + return $this->loadIndex($ctx, true, $tmpIndexPath); + } + + /** + * @param ContextInterface $ctx + * @param int $checkInterval + * @return bool + */ + protected function seemsCurrentlyIndexing(ContextInterface $ctx, $checkInterval){ + $tmpIndexPath = $this->getIndexPath($ctx)."-PYDIO_TMP"; + if(is_dir($tmpIndexPath)){ + $mtime = filemtime($tmpIndexPath); + if(time() - $mtime <= 60 * $checkInterval){ + return true; + } + } + return false; + } + + /** + * @param ContextInterface $ctx + */ + protected function mergeTemporaryIndexToMain(ContextInterface $ctx){ + $indexPath = $this->getIndexPath($ctx); + $tmpIndexPath = $indexPath."-PYDIO_TMP"; + $this->clearIndexIfExists($indexPath); + $this->moveIndex($tmpIndexPath, $indexPath); + $this->clearIndexIfExists($tmpIndexPath); + } + + /** + * @param $folder + */ + private function clearIndexIfExists($folder){ + if(!is_dir($folder))return; + $content = scandir($folder); + foreach($content as $file){ + if($file == "." || $file == "..") continue; + unlink($folder.DIRECTORY_SEPARATOR.$file); + } + rmdir($folder); + } + + /** + * @param $folder1 + * @param $folder2 + */ + private function copyIndex($folder1, $folder2){ + if(!is_dir($folder1))return; + if(!is_dir($folder2)) mkdir($folder2, 0755); + $content = scandir($folder1); + foreach($content as $file){ + if($file == "." || $file == "..") continue; + copy($folder1.DIRECTORY_SEPARATOR.$file, $folder2.DIRECTORY_SEPARATOR.$file); + } + } + + + /** + * @param $folder1 + * @param $folder2 + */ + private function moveIndex($folder1, $folder2){ + if(!is_dir($folder1))return; + if(!is_dir($folder2)) mkdir($folder2, 0755); + $content = scandir($folder1); + foreach($content as $file){ + if($file == "." || $file == "..") continue; + rename($folder1.DIRECTORY_SEPARATOR.$file, $folder2.DIRECTORY_SEPARATOR.$file); + } + } + + + /** + * + * Load index for context + * @param ContextInterface $ctx + * @param bool $create + * @param null $iPath + * @throws \Exception + * @return \Zend_Search_Lucene_Interface the index + */ + protected function loadIndex(ContextInterface $ctx, $create = true, $iPath = null) + { + require_once("Zend/Search/Lucene.php"); + if($iPath == null){ + $iPath = $this->getIndexPath($ctx); + } + if (is_dir($iPath)) { + try{ + $index = \Zend_Search_Lucene::open($iPath); + }catch (\Zend_Search_Lucene_Exception $se){ + $this->logError(__FUNCTION__, "Error while trying to load lucene index at path ".$iPath."! Maybe a permission issue?"); + throw $se; + } + } else { + if (!$create) { + $messages = LocaleService::getMessages(); + throw new \Exception($messages["index.lucene.9"]); + } + try{ + $index = \Zend_Search_Lucene::create($iPath); + }catch (\Zend_Search_Lucene_Exception $se){ + $this->logError(__FUNCTION__, "Error while trying to create lucene index at path ".$iPath."! Maybe a permission issue?"); + throw $se; + } + } + return $index; + } +} diff --git a/core/src/plugins/index.lucene/class.AjxpLuceneIndexer.php b/core/src/plugins/index.lucene/class.AjxpLuceneIndexer.php deleted file mode 100644 index 1032610b51..0000000000 --- a/core/src/plugins/index.lucene/class.AjxpLuceneIndexer.php +++ /dev/null @@ -1,778 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Encapsultion of the Zend_Search_Lucene component as a plugin - * @package AjaXplorer_Plugins - * @subpackage Index - */ -class AjxpLuceneIndexer extends AbstractSearchEngineIndexer -{ - /** - * @var Zend_Search_Lucene_Interface - */ - private $currentIndex; - private $metaFields = array(); - private $indexContent = false; - private $verboseIndexation = false; - - public function init($options) - { - parent::init($options); - set_include_path(get_include_path().PATH_SEPARATOR.AJXP_INSTALL_PATH."/plugins/index.lucene"); - $metaFields = $this->getFilteredOption("index_meta_fields"); - if (!empty($metaFields)) { - $this->metaFields = explode(",",$metaFields); - } - $this->indexContent = ($this->getFilteredOption("index_content") == true); - } - - public function initMeta($accessDriver) - { - parent::initMeta($accessDriver); - if (!empty($this->metaFields) || $this->indexContent) { - $metaFields = $this->metaFields; - $el = $this->getXPath()->query("/indexer")->item(0); - if ($this->indexContent) { - if($this->indexContent) $metaFields[] = "ajxp_document_content"; - $data = array("indexed_meta_fields" => $metaFields, - "additionnal_meta_columns" => array("ajxp_document_content" => "Content") - ); - $el->setAttribute("indexed_meta_fields", json_encode($data)); - } else { - $el->setAttribute("indexed_meta_fields", json_encode($metaFields)); - } - } - parent::init($this->options); - } - - - protected function setDefaultAnalyzer() - { - switch ($this->getFilteredOption("QUERY_ANALYSER")) { - case "utf8num_insensitive": - Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive()); - break; - case "utf8num_sensitive": - Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num()); - break; - case "utf8_insensitive": - Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8_CaseInsensitive()); - break; - case "utf8_sensitive": - Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8()); - break; - case "textnum_insensitive": - Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_TextNum_CaseInsensitive()); - break; - case "textnum_sensitive": - Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_textNum()); - break; - case "text_insensitive": - Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive()); - break; - case "text_sensitive": - Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Text()); - break; - default: - Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive()); - break; - } - Zend_Search_Lucene_Search_Query_Wildcard::setMinPrefixLength(intval($this->getFilteredOption("WILDCARD_LIMITATION"))); - - } - - public function applyAction($actionName, $httpVars, $fileVars) - { - $messages = ConfService::getMessages(); - $repoId = $this->accessDriver->repository->getId(); - - if ($actionName == "search") { - - // TMP - if (strpos($httpVars["query"], "keyword:") === 0) { - $parts = explode(":", $httpVars["query"]); - $this->applyAction("search_by_keyword", array("field" => $parts[1]), array()); - return null; - } - - require_once("Zend/Search/Lucene.php"); - try { - $index = $this->loadIndex($repoId, false); - } catch (Exception $ex) { - AJXP_XMLWriter::header(); - if ($this->seemsCurrentlyIndexing($repoId, 3)){ - AJXP_XMLWriter::sendMessage($messages["index.lucene.11"], null); - }else if (ConfService::backgroundActionsSupported() && !ConfService::currentContextIsCommandLine()) { - AJXP_Controller::applyActionInBackground($repoId, "index", array()); - sleep(2); - AJXP_XMLWriter::triggerBgAction("check_index_status", array("repository_id" => $repoId), sprintf($messages["index.lucene.8"], "/"), true, 5); - AJXP_XMLWriter::sendMessage($messages["index.lucene.7"], null); - }else{ - AJXP_XMLWriter::sendMessage($messages["index.lucene.12"], null); - } - AJXP_XMLWriter::close(); - return null; - } - $textQuery = $httpVars["query"]; - if($this->getFilteredOption("AUTO_WILDCARD") === true && strlen($textQuery) > 0 && ctype_alnum($textQuery)){ - if($textQuery[0] == '"' && $textQuery[strlen($textQuery)-1] == '"'){ - $textQuery = substr($textQuery, 1, -1); - }else if($textQuery[strlen($textQuery)-1] != "*" ){ - $textQuery.="*"; - } - } - if(strpos($textQuery, ":") !== false){ - $textQuery = str_replace("ajxp_meta_ajxp_document_content:","body:", $textQuery); - $textQuery = $this->filterSearchRangesKeywords($textQuery); - $query = "ajxp_scope:shared AND ($textQuery)"; - } - else if ((isSet($this->metaFields) || $this->indexContent) && isSet($httpVars["fields"])) { - $sParts = array(); - foreach (explode(",",$httpVars["fields"]) as $searchField) { - if ($searchField == "filename") { - $sParts[] = "basename:".$textQuery; - } else if ($searchField == "ajxp_document_content"){ - $sParts[] = $textQuery; - } else if (in_array($searchField, $this->metaFields)) { - $sParts[] = "ajxp_meta_".$searchField.":".$textQuery; - } else if ($searchField == "ajxp_document_content") { - $sParts[] = "title:".$textQuery; - $sParts[] = "body:".$textQuery; - $sParts[] = "keywords:".$textQuery; - } - } - $query = implode(" OR ", $sParts); - $query = "ajxp_scope:shared AND ($query)"; - $this->logDebug("Query : $query"); - } else { - $index->setDefaultSearchField("basename"); - $query = $this->filterSearchRangesKeywords($textQuery); - } - $this->setDefaultAnalyzer(); - if ($query == "*") { - $index->setDefaultSearchField("ajxp_node"); - $query = "yes"; - $hits = $index->find($query, "node_url", SORT_STRING); - } else { - $hits = $index->find($query); - } - $commitIndex = false; - - if (isSet($httpVars['return_selection'])) { - $returnNodes = array(); - } else { - AJXP_XMLWriter::header(); - } - $cursor = 0; - if(isSet($httpVars['limit'])){ - $limit = intval($httpVars['limit']); - } - if(!isSet($returnNodes) && !empty($limit) && count($hits) > $limit){ - AJXP_XMLWriter::renderPaginationData(count($hits), 1, 1); - } - foreach ($hits as $hit) { - // Backward compatibility - $hit->node_url = preg_replace("#ajxp\.[a-z_]+://#", "pydio://", $hit->node_url); - if ($hit->serialized_metadata!=null) { - $meta = unserialize(base64_decode($hit->serialized_metadata)); - if(isSet($meta["ajxp_modiftime"])){ - $meta["ajxp_relativetime"] = $meta["ajxp_description"] = $messages[4]." ".AJXP_Utils::relativeDate($meta["ajxp_modiftime"], $messages); - } - $tmpNode = new AJXP_Node(SystemTextEncoding::fromUTF8($hit->node_url), $meta); - } else { - $tmpNode = new AJXP_Node(SystemTextEncoding::fromUTF8($hit->node_url), array()); - $tmpNode->loadNodeInfo(); - } - if($tmpNode->getRepositoryId() != $repoId){ - $this->logDebug(__CLASS__, "Strange case, search retrieves a node from wrong repository!"); - $index->delete($hit->id); - $commitIndex = true; - continue; - } - if (!file_exists($tmpNode->getUrl())) { - $index->delete($hit->id); - $commitIndex = true; - continue; - } - if (!is_readable($tmpNode->getUrl())){ - continue; - } - $basename = basename($tmpNode->getPath()); - $isLeaf = $tmpNode->isLeaf(); - if (!$this->accessDriver->filterNodeName($tmpNode->getPath(), $basename, $isLeaf, array("d" => true, "f" => true, "z" => true))){ - continue; - } - $tmpNode->search_score = sprintf("%0.2f", $hit->score); - if (isSet($returnNodes)) { - $returnNodes[] = $tmpNode; - } else { - AJXP_XMLWriter::renderAjxpNode($tmpNode); - } - $cursor++; - if(isSet($limit) && $cursor >= $limit) break; - } - if(!isSet($returnNodes)) AJXP_XMLWriter::close(); - if ($commitIndex) { - $index->commit(); - } - } else if ($actionName == "search_by_keyword") { - require_once("Zend/Search/Lucene.php"); - $scope = "user"; - - try { - $index = $this->loadIndex($repoId, false); - } catch (Exception $ex) { - AJXP_XMLWriter::header(); - if (ConfService::backgroundActionsSupported() && !ConfService::currentContextIsCommandLine()) { - AJXP_Controller::applyActionInBackground($repoId, "index", array()); - AJXP_XMLWriter::triggerBgAction("check_index_status", array("repository_id" => $repoId), sprintf($messages["index.lucene.8"], "/"), true, 2); - } - AJXP_XMLWriter::sendMessage($messages["index.lucene.7"], null); - AJXP_XMLWriter::close(); - return null; - } - $sParts = array(); - $searchField = $httpVars["field"]; - if ($searchField == "ajxp_node") { - $sParts[] = "$searchField:yes"; - } else { - $sParts[] = "$searchField:true"; - } - if ($scope == "user" && AuthService::usersEnabled()) { - if (AuthService::getLoggedUser() == null) { - throw new Exception("Cannot find current user"); - } - $sParts[] = "ajxp_scope:user"; - $sParts[] = "ajxp_user:".AuthService::getLoggedUser()->getId(); - } else { - $sParts[] = "ajxp_scope:shared"; - } - $query = implode(" AND ", $sParts); - $this->logDebug("Query : $query"); - $hits = $index->find($query); - - $commitIndex = false; - - if (isSet($httpVars['return_selection'])) { - $returnNodes = array(); - } else { - AJXP_XMLWriter::header(); - } - foreach ($hits as $hit) { - // Backward compat with old protocols - $hit->node_url = preg_replace("#ajxp\.[a-z_]+://#", "pydio://", $hit->node_url); - if ($hit->serialized_metadata!=null) { - $meta = unserialize(base64_decode($hit->serialized_metadata)); - $tmpNode = new AJXP_Node(SystemTextEncoding::fromUTF8($hit->node_url), $meta); - } else { - $tmpNode = new AJXP_Node(SystemTextEncoding::fromUTF8($hit->node_url), array()); - $tmpNode->loadNodeInfo(); - } - if (!file_exists($tmpNode->getUrl())) { - $index->delete($hit->id); - $commitIndex = true; - continue; - } - if (!is_readable($tmpNode->getUrl())){ - continue; - } - $basename = basename($tmpNode->getPath()); - $isLeaf = $tmpNode->isLeaf(); - if (!$this->accessDriver->filterNodeName($tmpNode->getPath(), $basename, $isLeaf, array("d"=>true, "f"=>true))){ - continue; - } - $tmpNode->search_score = sprintf("%0.2f", $hit->score); - if (isSet($returnNodes)) { - $returnNodes[] = $tmpNode; - } else { - AJXP_XMLWriter::renderAjxpNode($tmpNode); - } - } - if(!isSet($returnNodes)) AJXP_XMLWriter::close(); - if ($commitIndex) { - $index->commit(); - } - } - if(isSet($returnNodes)) return $returnNodes; - else return null; - } - - /** - * @param AJXP_Node $parentNode - */ - public function indexationStarts($parentNode){ - $this->currentIndex = $this->loadTemporaryIndex($parentNode->getRepositoryId()); - } - - /** - * @param AJXP_Node $parentNode - */ - public function indexationEnds($parentNode){ - $this->logDebug('INDEX.END', 'Optimizing Index'); - $this->currentIndex->optimize(); - $this->logDebug('INDEX.END', 'Commiting Index'); - $this->currentIndex->commit(); - unset($this->currentIndex); - $this->logDebug('INDEX.END', 'Merging Temporary in main'); - $this->mergeTemporaryIndexToMain($parentNode->getRepositoryId()); - $this->logDebug('INDEX.END', 'Done'); - } - - /** - * @param AJXP_Node $node - */ - public function indexationIndexNode($node){ - $this->updateNodeIndex(null, $node, false, false); - } - - public function recursiveIndexation($url) - { - //print("Indexing $url \n"); - $this->logDebug("Indexing content of folder ".$url); - if (ConfService::currentContextIsCommandLine() && $this->verboseIndexation) { - print("Indexing content of ".$url."\n"); - } - if(!ConfService::currentContextIsCommandLine()) @set_time_limit(60); - $handle = opendir($url); - if ($handle !== false) { - while ( ($child = readdir($handle)) != false) { - if($child[0] == ".") continue; - $newUrl = $url."/".$child; - if (ConfService::currentContextIsCommandLine() && $this->verboseIndexation) { - print("Indexing node ".$newUrl."\n"); - } - $this->logDebug("Indexing Node ".$newUrl); - try { - $newNode = new AJXP_Node($newUrl); - $this->updateNodeIndex(null, $newNode, false, true); - AJXP_Controller::applyHook("node.index.add", array($newNode)); - } catch (Exception $e) { - if (ConfService::currentContextIsCommandLine() && $this->verboseIndexation) { - print("Error indexing node ".$newUrl." (".$e->getMessage().") \n"); - } - $this->logDebug("Error Indexing Node ".$newUrl." (".$e->getMessage().")"); - } - } - closedir($handle); - } else { - $this->logDebug("Cannot open $url!!"); - } - } - - /** - * Passes the array of META_SOURCES options before creating the shared workspace. - * Used here to disable the repository_specific_keywords, as path is in fact already fully resolved. - * @param $metaOptions array - */ - public function updateSharedChildOptions(&$metaOptions){ - if(isSet($metaOptions["index.lucene"]) && isSet($metaOptions["index.lucene"]["repository_specific_keywords"])){ - unset($metaOptions["index.lucene"]["repository_specific_keywords"]); - } - } - - /** - * Called on workspace.after_delete event, clear the index! - * @param $repoId - */ - public function clearWorkspaceIndexes($repoId){ - $iPath = $this->getIndexPath($repoId); - $this->clearIndexIfExists($iPath); - $this->clearIndexIfExists($iPath."-PYDIO_TMP"); - } - - /** - * - * Hooked to node.meta_change, this will update the index - * - * @param AJXP_Node $node - */ - public function updateNodeIndexMeta($node) - { - require_once("Zend/Search/Lucene.php"); - try{ - - if (isSet($this->currentIndex)) { - $index = $this->currentIndex; - } else { - $index = $this->loadIndex($node->getRepositoryId(), true, $node->getUser()); - } - Zend_Search_Lucene_Analysis_Analyzer::setDefault( new Zend_Search_Lucene_Analysis_Analyzer_Common_TextNum_CaseInsensitive()); - - if (AuthService::usersEnabled() && AuthService::getLoggedUser()!=null) { - $term = new Zend_Search_Lucene_Index_Term(SystemTextEncoding::toUTF8($node->getUrl()), "node_url"); - $hits = $index->termDocs($term); - foreach ($hits as $hitId) { - $hit = $index->getDocument($hitId); - if ($hit->ajxp_scope == 'shared' || ($hit->ajxp_scope == 'user' && $hit->ajxp_user == AuthService::getLoggedUser()->getId())) { - $index->delete($hitId); - } - } - } else { - $id = $this->getIndexedDocumentId($index, $node); - if($id != null) $index->delete($id); - } - if(file_exists($node->getUrl())){ - $this->createIndexedDocument($node, $index); - } - $this->logDebug(__FILE__, "Indexation passed ".$node->getUrl()); - } catch (Exception $e){ - $this->logError(__FILE__, "Lucene indexation failed for ".$node->getUrl()." (".$e->getMessage().")"); - } - } - - /** - * - * Hooked to node.change, this will update the index - * if $oldNode = null => create node $newNode - * if $newNode = null => delete node $oldNode - * Else copy or move oldNode to newNode. - * - * @param AJXP_Node $oldNode - * @param AJXP_Node $newNode - * @param Boolean $copy - * @param bool $recursive - */ - public function updateNodeIndex($oldNode, $newNode = null, $copy = false, $recursive = false) - { - require_once("Zend/Search/Lucene.php"); - if (isSet($this->currentIndex)) { - $oldIndex = $newIndex = $this->currentIndex; - } else { - if($oldNode == null){ - $newIndex = $oldIndex = $this->loadIndex($newNode->getRepositoryId(), true, $newNode->getUser()); - }else if($newNode == null){ - $oldIndex = $newIndex = $this->loadIndex($oldNode->getRepositoryId(), true, $oldNode->getUser()); - }else{ - $newId = $newNode->getRepositoryId(); - $oldId = $oldNode->getRepositoryId(); - if($newId == $oldId){ - $newIndex = $oldIndex = $this->loadIndex($newNode->getRepositoryId(), true, $newNode->getUser()); - }else{ - $newIndex = $this->loadIndex($newNode->getRepositoryId(), true, $newNode->getUser()); - $oldIndex = $this->loadIndex($oldNode->getRepositoryId(), true, $oldNode->getUser()); - } - } - } - $this->setDefaultAnalyzer(); - if ($oldNode != null && $copy == false) { - $oldDocId = $this->getIndexedDocumentId($oldIndex, $oldNode); - if ($oldDocId != null) { - $oldIndex->delete($oldDocId); - if ($newNode == null) { // DELETION - $childrenHits = $this->getIndexedChildrenDocuments($oldIndex, $oldNode); - foreach ($childrenHits as $hit) { - $oldIndex->delete($hit->id); - } - } - } - } - - if ($newNode != null) { - // Make sure it does not already exists anyway - $newDocId = $this->getIndexedDocumentId($newIndex, $newNode); - if ($newDocId != null) { - $newIndex->delete($newDocId); - $childrenHits = $this->getIndexedChildrenDocuments($newIndex, $newNode); - foreach ($childrenHits as $hit) { - $newIndex->delete($hit->id); - } - } - $this->createIndexedDocument($newNode, $newIndex); - if ( $recursive && $oldNode == null && is_dir($newNode->getUrl())) { - $this->recursiveIndexation($newNode->getUrl()); - } - } - - if ($oldNode != null && $newNode != null && is_dir($newNode->getUrl()) && ($newIndex == $oldIndex)) { // Copy / Move / Rename - // Get old node children docs, and update them manually, no need to scan real directory - $childrenHits = $this->getIndexedChildrenDocuments($oldIndex, $oldNode); - foreach ($childrenHits as $hit) { - $oldChildURL = $oldIndex->getDocument($hit->id)->node_url; - if ($copy == false) { - $oldIndex->delete($hit->id); - } - $newChildURL = str_replace(SystemTextEncoding::toUTF8($oldNode->getUrl()), - SystemTextEncoding::toUTF8($newNode->getUrl()), - $oldChildURL); - $newChildURL = SystemTextEncoding::fromUTF8($newChildURL); - $this->createIndexedDocument(new AJXP_Node($newChildURL), $oldIndex); - } - } - - if (!isSet($this->currentIndex)) { - $oldIndex->commit(); - if($newIndex != $oldIndex){ - $newIndex->commit(); - } - } - } - - /** - * @param AJXP_Node $ajxpNode - * @param Zend_Search_Lucene_Interface $index - * @throws Exception - * @return Zend_Search_Lucene_Document - */ - public function createIndexedDocument($ajxpNode, &$index) - { - if(!empty($this->metaFields)){ - $ajxpNode->loadNodeInfo(false, false, "all"); - }else{ - $ajxpNode->loadNodeInfo(); - } - $ext = strtolower(pathinfo($ajxpNode->getLabel(), PATHINFO_EXTENSION)); - $parseContent = $this->indexContent; - if ($parseContent && $ajxpNode->bytesize > $this->getFilteredOption("PARSE_CONTENT_MAX_SIZE")) { - $parseContent = false; - } - if ($parseContent && in_array($ext, explode(",",$this->getFilteredOption("PARSE_CONTENT_HTML")))) { - $doc = @Zend_Search_Lucene_Document_Html::loadHTMLFile($ajxpNode->getUrl()); - } elseif ($parseContent && $ext == "docx" && class_exists("Zend_Search_Lucene_Document_Docx")) { - $realFile = call_user_func(array($ajxpNode->wrapperClassName, "getRealFSReference"), $ajxpNode->getUrl()); - $doc = @Zend_Search_Lucene_Document_Docx::loadDocxFile($realFile); - } elseif ($parseContent && $ext == "docx" && class_exists("Zend_Search_Lucene_Document_Pptx")) { - $realFile = call_user_func(array($ajxpNode->wrapperClassName, "getRealFSReference"), $ajxpNode->getUrl()); - $doc = @Zend_Search_Lucene_Document_Pptx::loadPptxFile($realFile); - } elseif ($parseContent && $ext == "xlsx" && class_exists("Zend_Search_Lucene_Document_Xlsx")) { - $realFile = call_user_func(array($ajxpNode->wrapperClassName, "getRealFSReference"), $ajxpNode->getUrl()); - $doc = @Zend_Search_Lucene_Document_Xlsx::loadXlsxFile($realFile); - } else { - $doc = new Zend_Search_Lucene_Document(); - } - if($doc == null) throw new Exception("Could not load document"); - - $doc->addField(Zend_Search_Lucene_Field::Keyword("node_url", $ajxpNode->getUrl()), SystemTextEncoding::getEncoding()); - $doc->addField(Zend_Search_Lucene_Field::Keyword("node_path", str_replace("/", "AJXPFAKESEP", $ajxpNode->getPath())), SystemTextEncoding::getEncoding()); - $doc->addField(Zend_Search_Lucene_Field::Text("basename", basename($ajxpNode->getPath())), SystemTextEncoding::getEncoding()); - $doc->addField(Zend_Search_Lucene_Field::Keyword("ajxp_node", "yes"), SystemTextEncoding::getEncoding()); - $doc->addField(Zend_Search_Lucene_Field::Keyword("ajxp_scope", "shared")); - $doc->addField(Zend_Search_Lucene_Field::Keyword("ajxp_modiftime", date("Ymd", $ajxpNode->ajxp_modiftime))); - $doc->addField(Zend_Search_Lucene_Field::Keyword("ajxp_bytesize", $ajxpNode->bytesize)); - $ajxpMime = $ajxpNode->ajxp_mime; - if (empty($ajxpMime)) { - $doc->addField(Zend_Search_Lucene_Field::Keyword("ajxp_mime", pathinfo($ajxpNode->getLabel(), PATHINFO_EXTENSION))); - } else { - $doc->addField(Zend_Search_Lucene_Field::Keyword("ajxp_mime", $ajxpNode->ajxp_mime)); - } - - - // Store a cached copy of the metadata - $serializedMeta = base64_encode(serialize($ajxpNode->metadata)); - $doc->addField(Zend_Search_Lucene_Field::Binary("serialized_metadata", $serializedMeta)); - if (isSet($ajxpNode->indexableMetaKeys["shared"])) { - foreach ($ajxpNode->indexableMetaKeys["shared"] as $sharedField) { - if($ajxpNode->$sharedField) $doc->addField(Zend_search_Lucene_Field::keyword($sharedField, $ajxpNode->$sharedField)); - } - } - foreach ($this->metaFields as $field) { - if ($ajxpNode->$field != null) { - $doc->addField(Zend_Search_Lucene_Field::Text("ajxp_meta_$field", $ajxpNode->$field), SystemTextEncoding::getEncoding()); - } - } - if (isSet($ajxpNode->indexableMetaKeys["user"]) && count($ajxpNode->indexableMetaKeys["user"]) && AuthService::usersEnabled() && AuthService::getLoggedUser() != null) { - $privateDoc = new Zend_Search_Lucene_Document(); - $privateDoc->addField(Zend_Search_Lucene_Field::Keyword("node_url", $ajxpNode->getUrl(), SystemTextEncoding::getEncoding())); - $privateDoc->addField(Zend_Search_Lucene_Field::Keyword("node_path", str_replace("/", "AJXPFAKESEP", $ajxpNode->getPath()), SystemTextEncoding::getEncoding())); - - $privateDoc->addField(Zend_Search_Lucene_Field::Keyword("ajxp_scope", "user")); - $privateDoc->addField(Zend_Search_Lucene_Field::Keyword("ajxp_user", AuthService::getLoggedUser()->getId())); - foreach ($ajxpNode->indexableMetaKeys["user"] as $userField) { - if ($ajxpNode->$userField) { - $privateDoc->addField(Zend_search_Lucene_Field::keyword($userField, $ajxpNode->$userField)); - } - } - $privateDoc->addField(Zend_Search_Lucene_Field::Binary("serialized_metadata", $serializedMeta)); - - $index->addDocument($privateDoc); - } - - if($parseContent){ - $body = $this->extractIndexableContent($ajxpNode); - if(!empty($body)) $doc->addField(Zend_Search_Lucene_Field::unStored("body", $body)); - } - $index->addDocument($doc); - return $doc; - } - - /** - * @param Zend_Search_Lucene_Interface $index - * @param AJXP_Node $ajxpNode - * @return Number - */ - public function getIndexedDocumentId($index, $ajxpNode) - { - $term = new Zend_Search_Lucene_Index_Term(SystemTextEncoding::toUTF8($ajxpNode->getUrl()), "node_url"); - $docIds = $index->termDocs($term); - if(!count($docIds)) return null; - return $docIds[0]; - } - - /** - * Find all existing lucene documents based on the parent url - * @param Zend_Search_Lucene_Interface $index - * @param AJXP_Node $ajxpNode - * @return Zend_Search_Lucene_Search_QueryHit - */ - public function getIndexedChildrenDocuments($index, $ajxpNode) - { - // Try getting doc by url - $testQ = str_replace("/", "AJXPFAKESEP", SystemTextEncoding::toUTF8($ajxpNode->getPath())); - $pattern = new Zend_Search_Lucene_Index_Term($testQ .'*', 'node_path'); - $query = new Zend_Search_Lucene_Search_Query_Wildcard($pattern); - $hits = $index->find($query); - return $hits; - } - - /** - * @param $repositoryId - * @param null $resolveUserId - * @return string - */ - protected function getIndexPath($repositoryId, $resolveUserId = null){ - $mainCacheDir = (defined('AJXP_SHARED_CACHE_DIR')?AJXP_SHARED_CACHE_DIR:AJXP_CACHE_DIR); - if(!is_dir($mainCacheDir."/indexes")) mkdir($mainCacheDir."/indexes",0755,true); - $iPath = $mainCacheDir."/indexes/index-".$this->buildSpecificId($repositoryId, $resolveUserId); - return $iPath; - } - - /** - * @param $repositoryId - * @return Zend_Search_Lucene_Interface - */ - protected function loadTemporaryIndex($repositoryId){ - $indexPath = $this->getIndexPath($repositoryId); - $tmpIndexPath = $indexPath."-PYDIO_TMP"; - $this->clearIndexIfExists($tmpIndexPath); - $this->copyIndex($indexPath, $tmpIndexPath); - return $this->loadIndex($repositoryId, true, null, $tmpIndexPath); - } - - /** - * @param String $repositoryId - * @param int $checkInterval - * @return bool - */ - protected function seemsCurrentlyIndexing($repositoryId, $checkInterval){ - $tmpIndexPath = $this->getIndexPath($repositoryId)."-PYDIO_TMP"; - if(is_dir($tmpIndexPath)){ - $mtime = filemtime($tmpIndexPath); - if(time() - $mtime <= 60 * $checkInterval){ - return true; - } - } - return false; - } - - /** - * @param $repositoryId - */ - protected function mergeTemporaryIndexToMain($repositoryId){ - $indexPath = $this->getIndexPath($repositoryId); - $tmpIndexPath = $indexPath."-PYDIO_TMP"; - $this->clearIndexIfExists($indexPath); - $this->moveIndex($tmpIndexPath, $indexPath); - $this->clearIndexIfExists($tmpIndexPath); - } - - /** - * @param $folder - */ - private function clearIndexIfExists($folder){ - if(!is_dir($folder))return; - $content = scandir($folder); - foreach($content as $file){ - if($file == "." || $file == "..") continue; - unlink($folder.DIRECTORY_SEPARATOR.$file); - } - rmdir($folder); - } - - /** - * @param $folder1 - * @param $folder2 - */ - private function copyIndex($folder1, $folder2){ - if(!is_dir($folder1))return; - if(!is_dir($folder2)) mkdir($folder2, 0755); - $content = scandir($folder1); - foreach($content as $file){ - if($file == "." || $file == "..") continue; - copy($folder1.DIRECTORY_SEPARATOR.$file, $folder2.DIRECTORY_SEPARATOR.$file); - } - } - - - /** - * @param $folder1 - * @param $folder2 - */ - private function moveIndex($folder1, $folder2){ - if(!is_dir($folder1))return; - if(!is_dir($folder2)) mkdir($folder2, 0755); - $content = scandir($folder1); - foreach($content as $file){ - if($file == "." || $file == "..") continue; - rename($folder1.DIRECTORY_SEPARATOR.$file, $folder2.DIRECTORY_SEPARATOR.$file); - } - } - - - /** - * - * Enter description here ... - * @param Integer $repositoryId - * @param bool $create - * @param null $resolveUserId - * @param null $iPath - * @throws Exception - * @return Zend_Search_Lucene_Interface the index - */ - protected function loadIndex($repositoryId, $create = true, $resolveUserId = null, $iPath = null) - { - require_once("Zend/Search/Lucene.php"); - if($iPath == null){ - $iPath = $this->getIndexPath($repositoryId, $resolveUserId); - } - if (is_dir($iPath)) { - try{ - $index = Zend_Search_Lucene::open($iPath); - }catch (Zend_Search_Lucene_Exception $se){ - $this->logError(__FUNCTION__, "Error while trying to load lucene index at path ".$iPath."! Maybe a permission issue?"); - throw $se; - } - } else { - if (!$create) { - $messages = ConfService::getMessages(); - throw new Exception($messages["index.lucene.9"]); - } - try{ - $index = Zend_Search_Lucene::create($iPath); - }catch (Zend_Search_Lucene_Exception $se){ - $this->logError(__FUNCTION__, "Error while trying to create lucene index at path ".$iPath."! Maybe a permission issue?"); - throw $se; - } - } - return $index; - } -} diff --git a/core/src/plugins/index.lucene/manifest.xml b/core/src/plugins/index.lucene/manifest.xml index 4120af04c3..0c6964703c 100644 --- a/core/src/plugins/index.lucene/manifest.xml +++ b/core/src/plugins/index.lucene/manifest.xml @@ -1,6 +1,6 @@ - + @@ -54,11 +54,6 @@ -
      -
      -
      - ]]> . * - * The latest code can be found at . + * The latest code can be found at . */ // catalan translation: Salva Gómez , 2015 $mess = array( diff --git a/core/src/plugins/index.lucene/resources/i18n/conf/de.php b/core/src/plugins/index.lucene/resources/i18n/conf/de.php index 459e810969..4511d0286b 100644 --- a/core/src/plugins/index.lucene/resources/i18n/conf/de.php +++ b/core/src/plugins/index.lucene/resources/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Lucene Search Engine" => "Lucene", @@ -45,4 +45,4 @@ "Automatically append a * after the user query to make the search broader" => "Den Suchbegriff des Benutzers immer mit einem * beenden.", "Hide 'My Shares'" => "Hide 'My Shares'", "Hide My Shares section in the Orbit theme GUI." => "Hide My Shares section in the Orbit theme GUI.", -); \ No newline at end of file +); diff --git a/core/src/plugins/index.lucene/resources/i18n/conf/en.php b/core/src/plugins/index.lucene/resources/i18n/conf/en.php index 99c4b61280..baea48178e 100644 --- a/core/src/plugins/index.lucene/resources/i18n/conf/en.php +++ b/core/src/plugins/index.lucene/resources/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Lucene Search Engine" => "Lucene Search Engine", diff --git a/core/src/plugins/index.lucene/resources/i18n/conf/fr.php b/core/src/plugins/index.lucene/resources/i18n/conf/fr.php index b1f7c66905..bc849a222a 100644 --- a/core/src/plugins/index.lucene/resources/i18n/conf/fr.php +++ b/core/src/plugins/index.lucene/resources/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Lucene Search Engine" => "Moteur de recherche Lucene", diff --git a/core/src/plugins/index.lucene/resources/i18n/conf/it.php b/core/src/plugins/index.lucene/resources/i18n/conf/it.php index a8e9073462..3c8466a150 100644 --- a/core/src/plugins/index.lucene/resources/i18n/conf/it.php +++ b/core/src/plugins/index.lucene/resources/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Lucene Search Engine" => "Motore Rierca Lucene", @@ -45,4 +45,4 @@ "Automatically append a * after the user query to make the search broader" => "Automatically append a * after the user query to make the search broader", "Hide 'My Shares'" => "Hide 'My Shares'", "Hide My Shares section in the Orbit theme GUI." => "Hide My Shares section in the Orbit theme GUI.", -); \ No newline at end of file +); diff --git a/core/src/plugins/index.lucene/resources/i18n/conf/pt.php b/core/src/plugins/index.lucene/resources/i18n/conf/pt.php index 7ec7b4edc6..ac672d4ecf 100644 --- a/core/src/plugins/index.lucene/resources/i18n/conf/pt.php +++ b/core/src/plugins/index.lucene/resources/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Lucene Search Engine" => "Motor de pesquisa Lucene", diff --git a/core/src/plugins/index.lucene/resources/i18n/de.php b/core/src/plugins/index.lucene/resources/i18n/de.php index 62e0baa62f..de7a1fff0d 100644 --- a/core/src/plugins/index.lucene/resources/i18n/de.php +++ b/core/src/plugins/index.lucene/resources/i18n/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess = array( diff --git a/core/src/plugins/index.lucene/resources/i18n/en.php b/core/src/plugins/index.lucene/resources/i18n/en.php index 396c2d7e7e..d15114e69e 100644 --- a/core/src/plugins/index.lucene/resources/i18n/en.php +++ b/core/src/plugins/index.lucene/resources/i18n/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess = array( diff --git a/core/src/plugins/index.lucene/resources/i18n/es.php b/core/src/plugins/index.lucene/resources/i18n/es.php index eeb4f4b902..4fa09de69f 100644 --- a/core/src/plugins/index.lucene/resources/i18n/es.php +++ b/core/src/plugins/index.lucene/resources/i18n/es.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ // spanish translation: Salva Gómez , 2015 $mess = array( diff --git a/core/src/plugins/index.lucene/resources/i18n/fr.php b/core/src/plugins/index.lucene/resources/i18n/fr.php index 62ea7d87a1..7e8d779f5f 100644 --- a/core/src/plugins/index.lucene/resources/i18n/fr.php +++ b/core/src/plugins/index.lucene/resources/i18n/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess = array( diff --git a/core/src/plugins/index.lucene/resources/i18n/it.php b/core/src/plugins/index.lucene/resources/i18n/it.php index e46eb21486..0a022a502e 100644 --- a/core/src/plugins/index.lucene/resources/i18n/it.php +++ b/core/src/plugins/index.lucene/resources/i18n/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess = array( diff --git a/core/src/plugins/index.lucene/resources/i18n/pt.php b/core/src/plugins/index.lucene/resources/i18n/pt.php index 9b22e6094e..66a6abcc26 100644 --- a/core/src/plugins/index.lucene/resources/i18n/pt.php +++ b/core/src/plugins/index.lucene/resources/i18n/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess = array( diff --git a/core/src/plugins/index.lucene/resources/i18n/ru.php b/core/src/plugins/index.lucene/resources/i18n/ru.php index 741651ee25..fed22fab18 100644 --- a/core/src/plugins/index.lucene/resources/i18n/ru.php +++ b/core/src/plugins/index.lucene/resources/i18n/ru.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess = array( diff --git a/core/src/plugins/log.sql/SqlLogDriver.php b/core/src/plugins/log.sql/SqlLogDriver.php new file mode 100644 index 0000000000..a0075d83ff --- /dev/null +++ b/core/src/plugins/log.sql/SqlLogDriver.php @@ -0,0 +1,775 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ + +namespace Pydio\Log\Implementation; + +use DateTime; +use dibi; +use DibiDateTime; +use DibiException; +use Exception; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Utils\DBHelper; +use Pydio\Core\Utils\FileHelper; +use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Core\Utils\Vars\OptionsHelper; +use Pydio\Core\Utils\Vars\PathUtils; +use Pydio\Core\PluginFramework\SqlTableProvider; +use Pydio\Log\Core\AbstractLogDriver; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * SQL Logging Plugin + * Requires php bcmath (for inet_dtop/inet_ptod) enabled and php version 5.1 (for DateTime class) minimum + * @package AjaXplorer_Plugins + * @subpackage Log + */ +class SqlLogDriver extends AbstractLogDriver implements SqlTableProvider +{ + /** + * @var array + */ + private $sqlDriver; + private $queries; + + /** + * Initialize the driver. + * + * Gives the driver a chance to set up it's connection / file resource etc.. + * + * @param ContextInterface $ctx + * @param array $options + * @throws \Pydio\Core\Exception\DBConnectionException + */ + public function init(ContextInterface $ctx, $options = []) + { + parent::init($ctx, $options); + $this->sqlDriver = OptionsHelper::cleanDibiDriverParameters($options["SQL_DRIVER"]); + try { + if (!dibi::isConnected()) { + dibi::connect($this->sqlDriver); + } + } catch (DibiException $e) { + throw new \Pydio\Core\Exception\DBConnectionException(); + } + $this->queries = FileHelper::loadSerialFile($this->getBaseDir() . "/queries.json", false, "json"); + } + + public function performChecks() + { + if (!isSet($this->options)) return; + $test = OptionsHelper::cleanDibiDriverParameters($this->options["SQL_DRIVER"]); + if (!count($test)) { + throw new Exception("Please define an SQL connexion in the core configuration"); + } + } + + /** + * Find users last connections from log table + * @param array $userIds + * @return array + */ + public function usersLastConnection($userIds) + { + $res = dibi::query("SELECT [user], MAX([logdate]) AS date_max FROM [ajxp_log] WHERE [user] IN (%s) GROUP BY [user]", $userIds); + $all = $res->fetchPairs("user", "date_max"); + return $all; + } + + /** + * @param $actionName + * @param $httpVars + * @param $fileVars + */ + public function exposeQueries($actionName, &$httpVars, &$fileVars) + { + + header('Content-type: application/json'); + echo json_encode($this->queries); + + } + + /** + * @param $queryName + * @return bool + */ + private function getQuery($queryName) + { + foreach ($this->queries as $q) { + if (isset($q["NAME"]) && $q["NAME"] == $queryName) return $q; + } + return false; + } + + /** + * @param $queryName + * @param $start + * @param $count + * @param string $frequency + * @param array $additionalFilters + * @return array|\DibiRow[] + * @throws Exception + */ + protected function processOneQuery($queryName, $start, $count, $frequency = "auto", $additionalFilters = array()) + { + + $query = $this->getQuery($queryName); + if ($query === false) { + throw new Exception("Cannot find query " . $queryName); + } + $pg = ($this->sqlDriver["driver"] == "postgre"); + + $mess = LocaleService::getMessages(); + + $format = 'Y-m-d 00:00:00'; + $endFormat = 'Y-m-d 23:59:59'; + $dKeyFormat = $mess["date_relative_date_format"]; + $ref = time(); + $last = $start + $count; + $startDate = date($format, strtotime("-$last day", $ref)); + $endDate = date($endFormat, strtotime("-$start day", $ref)); + $dateCursor = "logdate > '$startDate' AND logdate <= '$endDate'"; + foreach ($additionalFilters as $filterField => $filterValue) { + $comparator = (strpos($filterValue, "%") !== false ? "LIKE" : "="); + $dateCursor .= " AND [" . InputFilter::sanitize($filterField, InputFilter::SANITIZE_ALPHANUM) . "] $comparator '" . InputFilter::sanitize($filterValue, InputFilter::SANITIZE_EMAILCHARS) . "'"; + } + + $q = $query["SQL"]; + $q = str_replace("AJXP_CURSOR_DATE", $dateCursor, $q); + if ($pg) { + $q = str_replace("ORDER BY logdate DESC", "ORDER BY DATE(logdate) DESC", $q); + } + + //$q .= " LIMIT $start, $count"; + $res = dibi::query($q); + $all = $res->fetchAll(); + $allDates = array(); + + if (isSet($query["AXIS"]) && $query["AXIS"]["x"] == "Date") { + if ($frequency == "auto") { + if ($count > 70) $frequency = "month"; + else if ($count > 21) $frequency = "week"; + } + $groupedSums = array(); + if ($frequency == "week") { + $groupByFormat = "W"; + } else if ($frequency == "month") { + $groupByFormat = "n"; + } + } + + foreach ($all as $row => &$data) { + // PG: Recapitalize keys + if ($pg) { + $newData = array(); + foreach ($data as $k => $v) { + $newData[ucfirst($k)] = $v; + } + $data = $newData; + } + if (isSet($data["File"])) { + $data["File"] = PathUtils::forwardSlashBasename($data["File"]); + } + if (isSet($data["Date"])) { + if ($data["Date"] instanceof DibiDateTime) { + $tStamp = $data["Date"]->getTimestamp(); + } else { + $tStamp = strtotime($data["Date"]); + } + $key = date($dKeyFormat, $tStamp); + $data["Date_sortable"] = $tStamp; + $data["Date"] = $key; + $allDates[$key] = true; + if (isSet($groupByFormat)) { + $newKey = date($groupByFormat, $tStamp); + if (!isSet($groupedSums[$newKey])) { + $groupedSums[$newKey] = $data; + } else { + $valueKey = $query["AXIS"]["y"]; + $groupedSums[$newKey][$valueKey] += $data[$valueKey]; + } + } + } + } + + if (isSet($query["AXIS"]) && $query["AXIS"]["x"] == "Date") { + for ($i = 0; $i < $count; $i++) { + $dateCurs = $start + $i; + $timeDate = strtotime("-$dateCurs day", $ref); + $dateK = date($dKeyFormat, $timeDate); + if (isSet($groupByFormat)) { + $newKey = date($groupByFormat, $timeDate); + if (!isSet($groupedSums[$newKey])) { + array_push($all, array("Date" => $dateK, "Date_sortable" => $timeDate)); + } + } else { + if (!isSet($allDates[$dateK])) { + array_push($all, array("Date" => $dateK, "Date_sortable" => $timeDate)); + } + } + } + } + + if (isSet($query["FIGURE"]) && isSet($all[0][$query["FIGURE"]])) { + $f = $all[0][$query["FIGURE"]]; + if ($f > 1000) $f = number_format($f / 1000, 1, ".", " ") . 'K'; + $all[0] = array($query["FIGURE"] => $f); + } + + if (isSet($groupByFormat) && isSet($groupedSums)) { + return array_values($groupedSums); + } else { + return $all; + } + + } + + /** + * @param $actionName + * @param $httpVars + * @param $fileVars + * @throws Exception + */ + public function processQuery($actionName, &$httpVars, &$fileVars) + { + + session_write_close(); + $query_name = $httpVars["query_name"]; + $start = 0; + $count = 30; + $frequency = (isSet($httpVars["frequency"]) ? InputFilter::sanitize($httpVars["frequency"], InputFilter::SANITIZE_ALPHANUM) : "auto"); + if (isSet($httpVars["start"])) $start = intval($httpVars["start"]); + if (isSet($httpVars["count"])) $count = intval($httpVars["count"]); + $additionalFilters = array(); + if (isSet($httpVars["user"])) $additionalFilters["user"] = InputFilter::sanitize($httpVars["user"], InputFilter::SANITIZE_EMAILCHARS); + if (isSet($httpVars["ws_id"])) $additionalFilters["repository_id"] = InputFilter::sanitize($httpVars["ws_id"], InputFilter::SANITIZE_ALPHANUM); + if (isSet($httpVars["filename_filter"])) { + $additionalFilters["basename"] = str_replace("*", "%", InputFilter::sanitize($httpVars["filename_filter"], InputFilter::SANITIZE_FILENAME)); + } + if (isSet($httpVars["dirname_filter"])) { + $additionalFilters["dirname"] = str_replace("*", "%", InputFilter::sanitize($httpVars["dirname_filter"], InputFilter::SANITIZE_DIRNAME)); + } + + + $queries = explode(",", $query_name); + $meta = array(); + if (count($queries) == 1) { + $qName = InputFilter::sanitize($query_name, InputFilter::SANITIZE_ALPHANUM); + $all = $this->processOneQuery($qName, $start, $count, $frequency, $additionalFilters); + $qDef = $this->getQuery($qName); + if ($qDef !== false && isSet($qDef['AXIS'])) { + unset($qDef["SQL"]); + $meta[$qName] = $qDef; + } + } else { + $all = array(); + foreach ($queries as $qName) { + $qName = InputFilter::sanitize($qName, InputFilter::SANITIZE_ALPHANUM); + $all[$qName] = $this->processOneQuery($qName, $start, $count, $frequency, $additionalFilters); + $qDef = $this->getQuery($qName); + if ($qDef !== false && isSet($qDef['AXIS'])) { + unset($qDef["SQL"]); + $meta[$qName] = $qDef; + } + } + } + + //$qry = "SELECT FOUND_ROWS() AS NbRows"; + //$res = dibi::query($qry); + $total_count = 1000; //$res->fetchSingle(); + + header('Content-type: application/json'); + $links = array(); + + if ($start > $count) { + $links[] = array('rel' => 'first', 'cursor' => 0, 'count' => $count); + } + if ($start > 0) { + $prev = max(0, $start - $count); + $links[] = array('rel' => 'previous', 'cursor' => $prev, 'count' => $count); + } + if ($start < $total_count) { + $next = $start + $count; + $links[] = array('rel' => 'next', 'cursor' => $next, 'count' => $count); + } + if ($start < $total_count - $count) { + $last = $total_count - ($total_count % $count); + //$links[] = array('rel' => 'last', 'cursor' => $last, 'count' => $count); + } + $hLinks = array(); + foreach ($links as $link) { + $hLinks[] = '; rel="' . $link["rel"] . '"'; + } + header('Link: ' . implode(",", $hLinks)); + + $envelope = array("links" => $links, "data" => $all, "meta" => $meta); + echo json_encode($envelope); + + } + + + /** + * Format a table row into an xml list of nodes for the log treeview + * + * @param String $node Name of the xml node + * @param String $icon Icon to use for the list item + * @param String $dateattrib + * @param String $display + * @param String $text Text displayed in the listview but not the treeview. + * @param String $filename + * @param Integer $is_file 0|1 to indicate whether this list item is a file or not. + * + * @return String Formatted XML node for insertion into the treeview. + */ + public function formatXmlLogList($node, $icon, $dateattrib, $display, $text, $filename, $is_file = 0) + { + return "<$node icon=\"{$icon}\" date=\"{$dateattrib}\" display=\"{$display}\" text=\"{$text}\" is_file=\"{$is_file}\" filename=\"{$filename}\"/>"; + } + + /** + * Format a table row into an xml list of nodes for the log reader + * + * @param String $node Name of the xml node + * @param String $icon Icon to use for the list item + * @param String $dateattrib + * @param String $filename Source of the list, usually a filename + * @param String $remote_ip Client IP that was logged + * @param String $log_level Log level of the item + * @param String $user User who was logged in + * @param $source + * @param String $action The action the user performed. + * @param String $params Parameters to the action + * @param string $rootPath + * @param null $rowId + * @return String Formatted XML node for insertion into the log reader + * + */ + public function formatXmlLogItem($node, $icon, $dateattrib, $filename, $remote_ip, $log_level, $user, $source, $action, $params, $rootPath = "/logs", $rowId = null) + { + $remote_ip = $this->inet_dtop($remote_ip); + $log_unixtime = strtotime($dateattrib); + $log_datetime = date("m-d-y", $log_unixtime) . " " . date("G:i:s", $log_unixtime); + $log_year = date('Y', $log_unixtime); + $log_month = date('m', $log_unixtime); + $log_date = date("m-d-y", $log_unixtime); + + $meta = [ + "icon" => $icon, + "date" => $log_datetime, + "ajxp_modiftime" => $log_unixtime, + "is_file" => true, + "filename" => "{$rootPath}/{$log_year}/{$log_month}/{$log_date}/{$log_datetime}", + "ajxp_mime" => "log", + "ip" => $remote_ip, + "level" => $log_level, + "user" => $user, + "action" => $action, + "source" => $source, + "params" => $params + ]; + if($rowId !== null){ + $meta["cursor"] = $rowId; + } + + return $meta; + + } + + /** + * Write an entry to the log. + * + * @param String $level Log severity: one of LOG_LEVEL_* (DEBUG,INFO,NOTICE,WARNING,ERROR) + * @param String $ip The client ip + * @param String $user The user login + * @param String $repositoryId current repository ID + * @param String $source The source of the message + * @param String $prefix The prefix of the message + * @param String $message The message to log + * @param array $nodesPathes + */ + public function write2($level, $ip, $user, $repositoryId, $source, $prefix, $message, $nodesPathes = array()) + { + if ($prefix == "Log In" && $message == "context=API") { + // Limit the number of logs + $test = dibi::query('SELECT [logdate] FROM [ajxp_log] WHERE [user]=%s AND [message]=%s AND [params]=%s ORDER BY [logdate] DESC %lmt %ofs', $user, $prefix, $message, 1, 0); + $lastInsert = $test->fetchSingle(); + $now = new DateTime('NOW'); + if ($lastInsert instanceof DibiDateTime) { + $lastTimestamp = $lastInsert->getTimestamp(); + } else { + $lastTimestamp = strtotime($lastInsert); + } + if ($lastInsert !== false && $now->getTimestamp() - $lastTimestamp < 60 * 60) { + // IGNORING, LIMIT API LOGINS TO ONE PER HOUR, OR IT WILL FILL THE LOGS + return; + } + } + $files = array(array("dirname" => "", "basename" => "")); + if (InputFilter::detectXSS($message)) { + $message = "XSS Detected in Message!"; + } else if (count($nodesPathes)) { + $files = array(); + foreach ($nodesPathes as $path) { + $parts = pathinfo($path); + $files[] = array("dirname" => $parts["dirname"], "basename" => $parts["basename"]); + } + } + foreach ($files as $fileDef) { + $log_row = Array( + 'logdate' => new DateTime('NOW'), + 'remote_ip' => $this->inet_ptod($ip), + 'severity' => strtoupper((string)$level), + 'user' => $user, + 'source' => $source, + 'message' => $prefix, + 'params' => $message, + 'repository_id' => $repositoryId, + 'device' => $_SERVER['HTTP_USER_AGENT'], + 'dirname' => $fileDef["dirname"], + 'basename' => $fileDef["basename"] + ); + //we already handle exception for write2 in core.log + dibi::query('INSERT INTO [ajxp_log]', $log_row); + } + } + + /** + * List available log files in XML + * + * @param string $nodeName + * @param null $year + * @param null $month + * @param string $rootPath + * @return array|\String[] + * @throws DibiException + * @throws PydioException + * @internal param bool $print + */ + public function listLogFiles($nodeName = "file", $year = null, $month = null, $rootPath = "/logs") + { + $data = array(); + + switch ($this->sqlDriver["driver"]) { + case "sqlite": + case "sqlite3": + $yFunc = "strftime('%Y', [logdate])"; + $mFunc = "strftime('%m', [logdate])"; + $dFunc = "date([logdate])"; + break; + case "mysqli": + case "mysql": + $yFunc = "YEAR([logdate])"; + $mFunc = "MONTH([logdate])"; + $dFunc = "DATE([logdate])"; + break; + case "postgre": + $yFunc = "EXTRACT(YEAR FROM [logdate])"; + $mFunc = "EXTRACT(MONTH FROM [logdate])"; + $dFunc = "DATE([logdate])"; + break; + default: + throw new PydioException("ERROR!, DB driver " . $this->sqlDriver["driver"] . " not supported yet in __FUNCTION__"); + } + + try { + if ($month != null) { // Get days + + //cal_days_in_month(CAL_GREGORIAN, $month, $year) + $start_time = mktime(0, 0, 0, $month, 1, $year); + $end_time = mktime(0, 0, 0, $month + 1, 1, $year); + + $q = 'SELECT + DISTINCT ' . $dFunc . ' AS logdate + FROM [ajxp_log] + WHERE [logdate] >= %t AND [logdate] < %t'; + $result = dibi::query($q, $start_time, $end_time); + + foreach ($result as $r) { + $log_time = strtotime($r['logdate']); + + $fullYear = date('Y', $log_time); + $logM = date('m', $log_time); + $date = $r['logdate']; + if ($date instanceof DibiDateTime) { + $date = $date->format("Y-m-d"); + } + $key = "$rootPath/$fullYear/$logM/$date"; + $data[$key] = [ + "icon" => "toggle_log.png", + "date" => $date, + "is_file" => false, + "ajxp_mime" => "datagrid", + "grid_datasource" => "get_action=ls&dir=" . urlencode($key), + "grid_header_title" => "Application Logs for $date", + "grid_actions" => "refresh,filter,copy_as_text" + ]; + } + + } else if ($year != null) { // Get months + $year_start_time = mktime(0, 0, 0, 1, 1, $year); + $year_end_time = mktime(0, 0, 0, 1, 1, $year + 1); + + $q = 'SELECT + DISTINCT ' . $yFunc . ' AS year, + ' . $mFunc . ' AS month + FROM [ajxp_log] + WHERE [logdate] >= %t AND [logdate] < %t'; + $result = dibi::query($q, $year_start_time, $year_end_time); + + foreach ($result as $r) { + /* We always recreate a unix timestamp while looping because it provides us with a uniform way to format the date. + * The month returned by the database will not be zero-padded and causes problems down the track when DateTime zero pads things */ + $month_time = mktime(0, 0, 0, $r['month'], 1, $r['year']); + + $fullYear = date('Y', $month_time); + $logMDisplay = date('F', $month_time); + $logM = date('m', $month_time); + + //"<$node icon=\"{$icon}\" date=\"{$dateattrib}\" display=\"{$display}\" text=\"{$text}\" is_file=\"{$is_file}\" filename=\"{$filename}\"/>"; + //$data[$r['month']] = $this->formatXmlLogList($nodeName, 'x-office-calendar.png', $logM, $logMDisplay, $logMDisplay, "$rootPath/$fullYear/$logM"); + $key = "$rootPath/$fullYear/$logM"; + $data[$key] = [ + "icon" => "x-office-calendar.png", + "text" => $logMDisplay, + "date" => $logM, + "display" => $logMDisplay, + "is_file" => false + ]; + + } + + } else { + + // Append Analytics Node + $data[$rootPath . "/0000_all_analytics"] = [ + "icon" => "graphs_viewer/ICON_SIZE/analytics.png", + "ajxp_mime" => "ajxp_graphs", + "is_file" => true, + "text" => "Analytics Dashboard" + ]; + + // Get years + $q = 'SELECT + DISTINCT ' . $yFunc . ' AS year + FROM [ajxp_log]'; + $result = dibi::query($q); + + foreach ($result as $r) { + $fullYear = $r['year']; + $nodeKey = "$rootPath/$fullYear"; + $data[$nodeKey] = [ + "icon" => "x-office-calendar.png", + "text" => $fullYear, + "date" => $fullYear, + "display" => $fullYear, + "is_file" => false + ]; + + } + } + } catch (DibiException $e) { + throw $e; + } + + return $data; + } + + /** + * List log contents in XML + * + * @param $parentDir + * @param String $date Assumed to be m-d-y format. + * @param string $nodeName + * @param string $rootPath + * @param int $cursor + * @return \array[] + */ + public function listLogs($parentDir, $date, $nodeName = "log", $rootPath = "/logs", $cursor = -1) + { + $start_time = strtotime($date); + $end_time = mktime(0, 0, 0, date('m', $start_time), date('d', $start_time) + 1, date('Y', $start_time)); + $logs = []; + + try { + if ($cursor != -1) { + $q = 'SELECT * FROM [ajxp_log] WHERE [id] > %i ORDER BY [logdate] DESC'; + $result = dibi::query($q, $cursor); + } else { + $q = 'SELECT * FROM [ajxp_log] WHERE [logdate] BETWEEN %t AND %t ORDER BY [logdate] DESC'; + $result = dibi::query($q, $start_time, $end_time); + } + $currentCount = 1; + foreach ($result as $r) { + + if (isSet($buffer) && $buffer["user"] == $r["user"] && $buffer["message"] == $r["message"] && $buffer["params"] == $r["params"]) { + $currentCount++; + continue; + } + if (isSet($buffer)) { + $meta = $this->formatXmlLogItem( + $nodeName, + 'toggle_log.png', + $buffer['logdate'], + $date, + $buffer['remote_ip'], + $buffer['severity'], + $buffer['user'], + $buffer['source'], + $buffer['message'] . ($currentCount > 1 ? " (" . $currentCount . ")" : ""), + $buffer['params'], + $rootPath, + $buffer['id'] + ); + $logs[$meta["filename"]] = $meta; + } + $meta = $this->formatXmlLogItem( + $nodeName, + 'toggle_log.png', + $r['logdate'], + $date, + $r['remote_ip'], + $r['severity'], + $r['user'], + $r['source'], + $r['message'], + $r['params'], + $rootPath, + $r['id'] + ); + $logs[$meta["filename"]] = $meta; + + $currentCount = 1; + $buffer = $r; + + } + + } catch (DibiException $e) { + //echo get_class($e), ': ', $e->getMessage(), "\n"; + } + return $logs; + } + + // IPV4/6 <--> DEC Lovingly lifted from stackoverflow, credit to Sander Marechal + // Requires bcmath + + /** + * Convert an IP address from presentation to decimal(39,0) format suitable for storage in MySQL + * + * @param string $ip_address An IP address in IPv4, IPv6 or decimal notation + * @return string The IP address in decimal notation + */ + public function inet_ptod($ip_address) + { + return $ip_address; + /* + // IPv4 address + if (strpos($ip_address, ':') === false && strpos($ip_address, '.') !== false) { + $ip_address = '::' . $ip_address; + } + + // IPv6 address + if (strpos($ip_address, ':') !== false) { + $network = inet_pton($ip_address); + $parts = unpack('N*', $network); + + foreach ($parts as &$part) { + if ($part < 0) { + $part = bcadd((string) $part, '4294967296'); + } + + if (!is_string($part)) { + $part = (string) $part; + } + } + + $decimal = $parts[4]; + $decimal = bcadd($decimal, bcmul($parts[3], '4294967296')); + $decimal = bcadd($decimal, bcmul($parts[2], '18446744073709551616')); + $decimal = bcadd($decimal, bcmul($parts[1], '79228162514264337593543950336')); + + return $decimal; + } + + // Decimal address + return $ip_address; + */ + } + + /** + * Convert an IP address from decimal format to presentation format + * + * @param string $decimal An IP address in IPv4, IPv6 or decimal notation + * @return string The IP address in presentation format + */ + public function inet_dtop($decimal) + { + return $decimal; + /* + // IPv4 or IPv6 format + if (strpos($decimal, ':') !== false || strpos($decimal, '.') !== false) { + return $decimal; + } + + // Decimal format + $parts = array(); + $parts[1] = bcdiv($decimal, '79228162514264337593543950336', 0); + $decimal = bcsub($decimal, bcmul($parts[1], '79228162514264337593543950336')); + $parts[2] = bcdiv($decimal, '18446744073709551616', 0); + $decimal = bcsub($decimal, bcmul($parts[2], '18446744073709551616')); + $parts[3] = bcdiv($decimal, '4294967296', 0); + $decimal = bcsub($decimal, bcmul($parts[3], '4294967296')); + $parts[4] = $decimal; + + foreach ($parts as &$part) { + if (bccomp($part, '2147483647') == 1) { + $part = bcsub($part, '4294967296'); + } + + $part = (int) $part; + } + + $network = pack('N4', $parts[1], $parts[2], $parts[3], $parts[4]); + $ip_address = inet_ntop($network); + + // Turn IPv6 to IPv4 if it's IPv4 + if (preg_match('/^::\d+.\d+.\d+.\d+$/', $ip_address)) { + return substr($ip_address, 2); + } + + return $ip_address; + */ + } + + /** + * @param array $param + * @return string + * @throws Exception + */ + public function installSQLTables($param) + { + $p = OptionsHelper::cleanDibiDriverParameters($param["SQL_DRIVER"]); + return DBHelper::runCreateTablesQuery($p, $this->getBaseDir() . "/create.sql"); + } + +} diff --git a/core/src/plugins/log.sql/class.sqlLogDriver.php b/core/src/plugins/log.sql/class.sqlLogDriver.php deleted file mode 100644 index 3a6e3e08b8..0000000000 --- a/core/src/plugins/log.sql/class.sqlLogDriver.php +++ /dev/null @@ -1,697 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * SQL Logging Plugin - * Requires php bcmath (for inet_dtop/inet_ptod) enabled and php version 5.1 (for DateTime class) minimum - * @package AjaXplorer_Plugins - * @subpackage Log - */ -class sqlLogDriver extends AbstractLogDriver implements SqlTableProvider -{ - /** - * @var Array - */ - private $sqlDriver; - private $queries; - - /** - * Initialize the driver. - * - * Gives the driver a chance to set up it's connection / file resource etc.. - * - * @param Array $options array of options specific to the logger driver. - * @access public - */ - public function init($options) - { - parent::init($options); - $this->sqlDriver = AJXP_Utils::cleanDibiDriverParameters($options["SQL_DRIVER"]); - try { - if(!dibi::isConnected()) { - dibi::connect($this->sqlDriver); - } - } catch (DibiException $e) { - echo get_class($e), ': ', $e->getMessage(), "\n"; - exit(1); - } - $this->queries = AJXP_Utils::loadSerialFile($this->getBaseDir()."/queries.json", false, "json"); - } - - public function performChecks() - { - if(!isSet($this->options)) return; - $test = AJXP_Utils::cleanDibiDriverParameters($this->options["SQL_DRIVER"]); - if (!count($test)) { - throw new Exception("Please define an SQL connexion in the core configuration"); - } - } - - public function usersLastConnection($userIds){ - $res = dibi::query("SELECT [user], MAX([logdate]) AS date_max FROM [ajxp_log] WHERE [user] IN (%s) GROUP BY [user]", $userIds); - $all = $res->fetchPairs("user", "date_max"); - return $all; - } - - public function exposeQueries($actionName, &$httpVars, &$fileVars){ - - header('Content-type: application/json'); - echo json_encode($this->queries); - - } - - private function getQuery($queryName){ - foreach($this->queries as $q){ - if(isset($q["NAME"]) && $q["NAME"] == $queryName) return $q; - } - return false; - } - - protected function processOneQuery($queryName, $start, $count, $frequency="auto", $additionalFilters=array()){ - - $query = $this->getQuery($queryName); - if($query === false){ - throw new Exception("Cannot find query ".$queryName); - } - $pg = ($this->sqlDriver["driver"] == "postgre"); - - $mess = ConfService::getMessages(); - - $format = 'Y-m-d 00:00:00'; - $endFormat = 'Y-m-d 23:59:59'; - $dKeyFormat = $mess["date_relative_date_format"]; - $ref = time(); - $last = $start + $count; - $startDate = date($format, strtotime("-$last day", $ref)); - $endDate = date($endFormat, strtotime("-$start day", $ref)); - $dateCursor = "logdate > '$startDate' AND logdate <= '$endDate'"; - foreach($additionalFilters as $filterField => $filterValue){ - $comparator = (strpos($filterValue, "%") !== false ? "LIKE" : "="); - $dateCursor .= " AND [".AJXP_Utils::sanitize($filterField, AJXP_SANITIZE_ALPHANUM)."] $comparator '".AJXP_Utils::sanitize($filterValue, AJXP_SANITIZE_EMAILCHARS)."'"; - } - - $q = $query["SQL"]; - $q = str_replace("AJXP_CURSOR_DATE", $dateCursor, $q); - if($pg){ - $q = str_replace("ORDER BY logdate DESC", "ORDER BY DATE(logdate) DESC",$q); - } - - //$q .= " LIMIT $start, $count"; - $res = dibi::query($q); - $all = $res->fetchAll(); - $allDates = array(); - - if(isSet($query["AXIS"]) && $query["AXIS"]["x"] == "Date"){ - if($frequency == "auto"){ - if($count > 70) $frequency = "month"; - else if($count > 21) $frequency = "week"; - } - $groupedSums = array(); - if($frequency == "week"){ - $groupByFormat = "W"; - } else if ($frequency == "month"){ - $groupByFormat = "n"; - } - } - - foreach($all as $row => &$data){ - // PG: Recapitalize keys - if($pg){ - $newData = array(); - foreach($data as $k => $v){ - $newData[ucfirst($k)] = $v; - } - $data = $newData; - } - if(isSet($data["File"])){ - $data["File"] = AJXP_Utils::safeBasename($data["File"]); - } - if(isSet($data["Date"])){ - if(is_a($data["Date"], "DibiDateTime")){ - $tStamp = $data["Date"]->getTimestamp(); - }else { - $tStamp = strtotime($data["Date"]); - } - $key = date($dKeyFormat, $tStamp); - $data["Date_sortable"] = $tStamp; - $data["Date"] = $key; - $allDates[$key] = true; - if(isSet($groupByFormat)){ - $newKey = date($groupByFormat, $tStamp); - if(!isSet($groupedSums[$newKey])){ - $groupedSums[$newKey] = $data; - }else{ - $valueKey = $query["AXIS"]["y"]; - $groupedSums[$newKey][$valueKey] += $data[$valueKey]; - } - } - } - } - - if(isSet($query["AXIS"]) && $query["AXIS"]["x"] == "Date"){ - for($i = 0;$i<$count;$i++){ - $dateCurs = $start + $i; - $timeDate = strtotime("-$dateCurs day", $ref); - $dateK = date($dKeyFormat, $timeDate); - if(isSet($groupByFormat)){ - $newKey = date($groupByFormat, $timeDate); - if(!isSet($groupedSums[$newKey])){ - array_push($all, array("Date" => $dateK, "Date_sortable" => $timeDate)); - } - }else{ - if(!isSet($allDates[$dateK])){ - array_push($all, array("Date" => $dateK, "Date_sortable" => $timeDate)); - } - } - } - } - - if(isSet($query["FIGURE"]) && isSet($all[0][$query["FIGURE"]])){ - $f = $all[0][$query["FIGURE"]]; - if($f > 1000) $f = number_format($f / 1000, 1, ".", " ") . 'K'; - $all[0] = array($query["FIGURE"] => $f); - } - - if(isSet($groupByFormat) && isSet($groupedSums)){ - return array_values($groupedSums); - }else{ - return $all; - } - - } - - public function processQuery($actionName, &$httpVars, &$fileVars){ - - session_write_close(); - $query_name = $httpVars["query_name"]; - $start = 0; - $count = 30; - $frequency = (isSet($httpVars["frequency"])?AJXP_Utils::sanitize($httpVars["frequency"], AJXP_SANITIZE_ALPHANUM):"auto"); - if(isSet($httpVars["start"])) $start = intval($httpVars["start"]); - if(isSet($httpVars["count"])) $count = intval($httpVars["count"]); - $additionalFilters = array(); - if(isSet($httpVars["user"])) $additionalFilters["user"] = AJXP_Utils::sanitize($httpVars["user"], AJXP_SANITIZE_EMAILCHARS); - if(isSet($httpVars["ws_id"])) $additionalFilters["repository_id"] = AJXP_Utils::sanitize($httpVars["ws_id"], AJXP_SANITIZE_ALPHANUM); - if(isSet($httpVars["filename_filter"])){ - $additionalFilters["basename"] = str_replace("*", "%", AJXP_Utils::sanitize($httpVars["filename_filter"], AJXP_SANITIZE_FILENAME)); - } - if(isSet($httpVars["dirname_filter"])){ - $additionalFilters["dirname"] = str_replace("*", "%", AJXP_Utils::sanitize($httpVars["dirname_filter"], AJXP_SANITIZE_DIRNAME)); - } - - - $queries = explode(",", $query_name); - $meta = array(); - if(count($queries) == 1){ - $qName = AJXP_Utils::sanitize($query_name, AJXP_SANITIZE_ALPHANUM); - $all = $this->processOneQuery($qName, $start, $count, $frequency, $additionalFilters); - $qDef = $this->getQuery($qName); - if($qDef !== false && isSet($qDef['AXIS'])) { - unset($qDef["SQL"]); - $meta[$qName] = $qDef; - } - }else{ - $all = array(); - foreach($queries as $qName){ - $qName = AJXP_Utils::sanitize($qName, AJXP_SANITIZE_ALPHANUM); - $all[$qName] = $this->processOneQuery($qName, $start, $count, $frequency, $additionalFilters); - $qDef = $this->getQuery($qName); - if($qDef !== false && isSet($qDef['AXIS'])) { - unset($qDef["SQL"]); - $meta[$qName] = $qDef; - } - } - } - - //$qry = "SELECT FOUND_ROWS() AS NbRows"; - //$res = dibi::query($qry); - $total_count = 1000; //$res->fetchSingle(); - - header('Content-type: application/json'); - $links = array(); - - if($start > $count){ - $links[] = array('rel' => 'first', 'cursor' => 0, 'count' => $count); - } - if($start > 0){ - $prev = max(0, $start - $count); - $links[] = array('rel' => 'previous', 'cursor' => $prev, 'count' => $count); - } - if($start < $total_count){ - $next = $start + $count; - $links[] = array('rel' => 'next', 'cursor' => $next, 'count' => $count); - } - if($start < $total_count - $count){ - $last = $total_count - ($total_count % $count); - //$links[] = array('rel' => 'last', 'cursor' => $last, 'count' => $count); - } - $hLinks = array(); - foreach($links as $link){ - $hLinks[] = '; rel="'.$link["rel"].'"'; - } - header('Link: '.implode(",", $hLinks)); - - $envelope = array("links" => $links, "data" => $all, "meta" => $meta); - echo json_encode($envelope); - - } - - - /** - * Format a table row into an xml list of nodes for the log treeview - * - * @param String $node Name of the xml node - * @param String $icon Icon to use for the list item - * @param String $dateattrib - * @param String $display - * @param String $text Text displayed in the listview but not the treeview. - * @param String $filename - * @param Integer $is_file 0|1 to indicate whether this list item is a file or not. - * - * @return String Formatted XML node for insertion into the treeview. - */ - public function formatXmlLogList($node, $icon, $dateattrib, $display, $text, $filename, $is_file = 0) - { - return "<$node icon=\"{$icon}\" date=\"{$dateattrib}\" display=\"{$display}\" text=\"{$text}\" is_file=\"{$is_file}\" filename=\"{$filename}\"/>"; - } - - /** - * Format a table row into an xml list of nodes for the log reader - * - * @param String $node Name of the xml node - * @param String $icon Icon to use for the list item - * @param String $dateattrib - * @param String $filename Source of the list, usually a filename - * @param String $remote_ip Client IP that was logged - * @param String $log_level Log level of the item - * @param String $user User who was logged in - * @param $source - * @param String $action The action the user performed. - * @param String $params Parameters to the action - * @param string $rootPath - * @param null $rowId - * @return String Formatted XML node for insertion into the log reader - * - */ - public function formatXmlLogItem($node, $icon, $dateattrib, $filename, $remote_ip, $log_level, $user, $source, $action, $params, $rootPath = "/logs", $rowId=null) - { - $remote_ip = $this->inet_dtop($remote_ip); - $log_unixtime = strtotime($dateattrib); - $log_datetime = date("m-d-y", $log_unixtime) . " " . date("G:i:s", $log_unixtime); - $log_year = date('Y', $log_unixtime); - $log_month = date('m', $log_unixtime); - $log_date = date("m-d-y", $log_unixtime); - - // Some actions or parameters can contain characters that need to be encoded, especially when a piece of code raises a notification or error. - $action = AJXP_Utils::xmlEntities($action); - $params = AJXP_Utils::xmlEntities($params); - $source = AJXP_Utils::xmlEntities($source); - $rowIdString = ""; - if($rowId !== null){ - $rowIdString = "cursor=\"{$rowId}\""; - } - - return "<$node $rowIdString icon=\"{$icon}\" date=\"{$log_datetime}\" ajxp_modiftime=\"{$log_unixtime}\" is_file=\"true\" filename=\"{$rootPath}/{$log_year}/{$log_month}/{$log_date}/{$log_datetime}\" ajxp_mime=\"log\" ip=\"{$remote_ip}\" level=\"{$log_level}\" user=\"{$user}\" action=\"{$action}\" source=\"{$source}\" params=\"{$params}\"/>"; - } - - /** - * Write an entry to the log. - * - * @param String $level Log severity: one of LOG_LEVEL_* (DEBUG,INFO,NOTICE,WARNING,ERROR) - * @param String $ip The client ip - * @param String $user The user login - * @param String $source The source of the message - * @param String $prefix The prefix of the message - * @param String $message The message to log - * @param array $nodesPathes - */ - public function write2($level, $ip, $user, $source, $prefix, $message, $nodesPathes = array()) - { - if($prefix == "Log In" && $message=="context=API"){ - // Limit the number of logs - $test = dibi::query('SELECT [logdate] FROM [ajxp_log] WHERE [user]=%s AND [message]=%s AND [params]=%s ORDER BY [logdate] DESC %lmt %ofs', $user, $prefix, $message, 1, 0); - $lastInsert = $test->fetchSingle(); - $now = new DateTime('NOW'); - if(is_a($lastInsert, "DibiDateTime")){ - $lastTimestamp = $lastInsert->getTimestamp(); - }else{ - $lastTimestamp = strtotime($lastInsert); - } - if($lastInsert !== false && $now->getTimestamp() - $lastTimestamp < 60 * 60){ - // IGNORING, LIMIT API LOGINS TO ONE PER HOUR, OR IT WILL FILL THE LOGS - return; - } - } - $files = array(array("dirname"=>"", "basename"=>"")); - if(AJXP_Utils::detectXSS($message)){ - $message = "XSS Detected in Message!"; - }else if(count($nodesPathes)){ - $files = array(); - foreach($nodesPathes as $path){ - $parts = pathinfo($path); - $files[] = array("dirname"=>$parts["dirname"], "basename"=>$parts["basename"]); - } - } - foreach($files as $fileDef){ - $log_row = Array( - 'logdate' => new DateTime('NOW'), - 'remote_ip' => $this->inet_ptod($ip), - 'severity' => strtoupper((string) $level), - 'user' => $user, - 'source' => $source, - 'message' => $prefix, - 'params' => $message, - 'repository_id' => ConfService::getInstance()->getContextRepositoryId(), - 'device' => $_SERVER['HTTP_USER_AGENT'], - 'dirname' => $fileDef["dirname"], - 'basename' => $fileDef["basename"] - ); - //we already handle exception for write2 in core.log - dibi::query('INSERT INTO [ajxp_log]', $log_row); - } - } - - /** - * List available log files in XML - * - * @param String [optional] $nodeName - * @param String [optional] $year - * @param String [optional] $month - */ - public function xmlListLogFiles($nodeName="file", $year=null, $month=null, $rootPath = "/logs", $print = true) - { - $xml_strings = array(); - - switch ($this->sqlDriver["driver"]) { - case "sqlite": - case "sqlite3": - $yFunc = "strftime('%Y', [logdate])"; - $mFunc = "strftime('%m', [logdate])"; - $dFunc = "date([logdate])"; - break; - case "mysqli": - case "mysql": - $yFunc = "YEAR([logdate])"; - $mFunc = "MONTH([logdate])"; - $dFunc = "DATE([logdate])"; - break; - case "postgre": - $yFunc = "EXTRACT(YEAR FROM [logdate])"; - $mFunc = "EXTRACT(MONTH FROM [logdate])"; - $dFunc = "DATE([logdate])"; - break; - default: - echo "ERROR!, DB driver "+ $this->sqlDriver["driver"] +" not supported yet in __FUNCTION__"; - exit(1); - } - - try { - if ($month != null) { // Get days - - //cal_days_in_month(CAL_GREGORIAN, $month, $year) - $start_time = mktime(0,0,0,$month,1,$year); - $end_time = mktime(0,0,0,$month+1,1,$year); - - $q = 'SELECT - DISTINCT '.$dFunc.' AS logdate - FROM [ajxp_log] - WHERE [logdate] >= %t AND [logdate] < %t'; - $result = dibi::query($q, $start_time, $end_time); - - foreach ($result as $r) { - $log_time = strtotime($r['logdate']); - - $fullYear = date('Y', $log_time); - $fullMonth = date('F', $log_time); - $logM = date('m', $log_time); - $date = $r['logdate']; - if (is_a($date, "DibiDateTime")) { - $date = $date->format("Y-m-d"); - } - $path = "$rootPath/$fullYear/$logM/$date"; - $metadata = array( - "icon" => "toggle_log.png", - "date"=> $date, - "ajxp_mime" => "datagrid", - "grid_datasource" => "get_action=ls&dir=".urlencode($path), - "grid_header_title" => "Application Logs for $date", - "grid_actions" => "refresh,filter,copy_as_text" - ); - $xml_strings[$date] = AJXP_XMLWriter::renderNode($path, $date, true, $metadata, true, false); - } - - } else if ($year != null) { // Get months - $year_start_time = mktime(0,0,0,1,1,$year); - $year_end_time = mktime(0,0,0,1,1,$year+1); - - $q = 'SELECT - DISTINCT '.$yFunc.' AS year, - '.$mFunc.' AS month - FROM [ajxp_log] - WHERE [logdate] >= %t AND [logdate] < %t'; - $result = dibi::query($q, $year_start_time, $year_end_time); - - foreach ($result as $r) { - /* We always recreate a unix timestamp while looping because it provides us with a uniform way to format the date. - * The month returned by the database will not be zero-padded and causes problems down the track when DateTime zero pads things */ - $month_time = mktime(0,0,0,$r['month'],1,$r['year']); - - $fullYear = date('Y', $month_time); - $fullMonth = date('F', $month_time); - $logMDisplay = date('F', $month_time); - $logM = date('m', $month_time); - - $xml_strings[$r['month']] = $this->formatXmlLogList($nodeName, 'x-office-calendar.png', $logM, $logMDisplay, $logMDisplay, "$rootPath/$fullYear/$logM"); - //"<$nodeName icon=\"x-office-calendar.png\" date=\"$fullMonth\" display=\"$logM\" text=\"$fullMonth\" is_file=\"0\" filename=\"/logs/$fullYear/$fullMonth\"/>"; - } - - } else { - - // Append Analytics Node - $xml_strings['0000'] = AJXP_XMLWriter::renderNode($rootPath."/all_analytics", - "Analytics Dashboard", - true, - array( - "icon" => "graphs_viewer/ICON_SIZE/analytics.png", - "ajxp_mime" => "ajxp_graphs", - ), - true, - false - ); - - - // Get years - $q = 'SELECT - DISTINCT '.$yFunc.' AS year - FROM [ajxp_log]'; - $result = dibi::query($q); - - foreach ($result as $r) { - $year_time = mktime(0,0,0,1,1,$r['year']); - $fullYear = $r['year']; - - $xml_strings[$r['year']] = $this->formatXmlLogList($nodeName, 'x-office-calendar.png', $fullYear, $fullYear, $fullYear, "$rootPath/$fullYear"); - //"<$nodeName icon=\"x-office-calendar.png\" date=\"$fullYear\" display=\"$fullYear\" text=\"$fullYear\" is_file=\"0\" filename=\"/logs/$fullYear\"/>"; - } - } - } catch (DibiException $e) { - echo get_class($e), ': ', $e->getMessage(), "\n"; - exit(1); - } - - if ($print) { - foreach ($xml_strings as $s) { - print($s); - } - } - - return $xml_strings ; - } - - /** - * List log contents in XML - * - * @param String $date Assumed to be m-d-y format. - * @param String [optional] $nodeName - */ - public function xmlLogs($parentDir, $date, $nodeName = "log", $rootPath = "/logs", $cursor = -1) - { - $start_time = strtotime($date); - $end_time = mktime(0,0,0,date('m', $start_time), date('d', $start_time) + 1, date('Y', $start_time)); - - try { - if($cursor != -1){ - $q = 'SELECT * FROM [ajxp_log] WHERE [id] > %i ORDER BY [logdate] DESC'; - $result = dibi::query($q, $cursor); - }else{ - $q = 'SELECT * FROM [ajxp_log] WHERE [logdate] BETWEEN %t AND %t ORDER BY [logdate] DESC'; - $result = dibi::query($q, $start_time, $end_time); - } - $log_items = ""; - $currentCount = 1; - foreach ($result as $r) { - - if(isSet($buffer) && $buffer["user"] == $r["user"] && $buffer["message"] == $r["message"] && $buffer["params"] == $r["params"]){ - $currentCount ++; - continue; - } - if(isSet($buffer)){ - $log_items .= SystemTextEncoding::toUTF8($this->formatXmlLogItem( - $nodeName, - 'toggle_log.png', - $buffer['logdate'], - $date, - $buffer['remote_ip'], - $buffer['severity'], - $buffer['user'], - $buffer['source'], - $buffer['message'].($currentCount > 1?" (".$currentCount.")":""), - $buffer['params'], - $rootPath, - $buffer['id'] - ) - ); - } - $log_items .= SystemTextEncoding::toUTF8($this->formatXmlLogItem( - $nodeName, - 'toggle_log.png', - $r['logdate'], - $date, - $r['remote_ip'], - $r['severity'], - $r['user'], - $r['source'], - $r['message'], - $r['params'], - $rootPath, - $r['id']) - ); - - $currentCount = 1; - - $buffer = $r; - - } - - print($log_items); - - } catch (DibiException $e) { - echo get_class($e), ': ', $e->getMessage(), "\n"; - exit(1); - } - } - - // IPV4/6 <--> DEC Lovingly lifted from stackoverflow, credit to Sander Marechal - // Requires bcmath - - /** - * Convert an IP address from presentation to decimal(39,0) format suitable for storage in MySQL - * - * @param string $ip_address An IP address in IPv4, IPv6 or decimal notation - * @return string The IP address in decimal notation - */ - public function inet_ptod($ip_address) - { - return $ip_address; - // IPv4 address - if (strpos($ip_address, ':') === false && strpos($ip_address, '.') !== false) { - $ip_address = '::' . $ip_address; - } - - // IPv6 address - if (strpos($ip_address, ':') !== false) { - $network = inet_pton($ip_address); - $parts = unpack('N*', $network); - - foreach ($parts as &$part) { - if ($part < 0) { - $part = bcadd((string) $part, '4294967296'); - } - - if (!is_string($part)) { - $part = (string) $part; - } - } - - $decimal = $parts[4]; - $decimal = bcadd($decimal, bcmul($parts[3], '4294967296')); - $decimal = bcadd($decimal, bcmul($parts[2], '18446744073709551616')); - $decimal = bcadd($decimal, bcmul($parts[1], '79228162514264337593543950336')); - - return $decimal; - } - - // Decimal address - return $ip_address; - } - - /** - * Convert an IP address from decimal format to presentation format - * - * @param string $decimal An IP address in IPv4, IPv6 or decimal notation - * @return string The IP address in presentation format - */ - public function inet_dtop($decimal) - { - return $decimal; - // IPv4 or IPv6 format - if (strpos($decimal, ':') !== false || strpos($decimal, '.') !== false) { - return $decimal; - } - - // Decimal format - $parts = array(); - $parts[1] = bcdiv($decimal, '79228162514264337593543950336', 0); - $decimal = bcsub($decimal, bcmul($parts[1], '79228162514264337593543950336')); - $parts[2] = bcdiv($decimal, '18446744073709551616', 0); - $decimal = bcsub($decimal, bcmul($parts[2], '18446744073709551616')); - $parts[3] = bcdiv($decimal, '4294967296', 0); - $decimal = bcsub($decimal, bcmul($parts[3], '4294967296')); - $parts[4] = $decimal; - - foreach ($parts as &$part) { - if (bccomp($part, '2147483647') == 1) { - $part = bcsub($part, '4294967296'); - } - - $part = (int) $part; - } - - $network = pack('N4', $parts[1], $parts[2], $parts[3], $parts[4]); - $ip_address = inet_ntop($network); - - // Turn IPv6 to IPv4 if it's IPv4 - if (preg_match('/^::\d+.\d+.\d+.\d+$/', $ip_address)) { - return substr($ip_address, 2); - } - - return $ip_address; - } - - public function installSQLTables($param) - { - $p = AJXP_Utils::cleanDibiDriverParameters($param["SQL_DRIVER"]); - return AJXP_Utils::runCreateTablesQuery($p, $this->getBaseDir()."/create.sql"); - } - -} diff --git a/core/src/plugins/log.sql/i18n/conf/de.php b/core/src/plugins/log.sql/i18n/conf/de.php index 0cc7cd496b..e9c07dcc3b 100644 --- a/core/src/plugins/log.sql/i18n/conf/de.php +++ b/core/src/plugins/log.sql/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "DB Storage" => "Datenbank", diff --git a/core/src/plugins/log.sql/i18n/conf/en.php b/core/src/plugins/log.sql/i18n/conf/en.php index 4b2c566636..d6e24a8ddf 100644 --- a/core/src/plugins/log.sql/i18n/conf/en.php +++ b/core/src/plugins/log.sql/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "DB Storage" => "DB Storage", diff --git a/core/src/plugins/log.sql/i18n/conf/fr.php b/core/src/plugins/log.sql/i18n/conf/fr.php index dd95a1d94b..d6a9d1c5b3 100644 --- a/core/src/plugins/log.sql/i18n/conf/fr.php +++ b/core/src/plugins/log.sql/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "DB Storage" => "Logs en BDD", diff --git a/core/src/plugins/log.sql/i18n/conf/it.php b/core/src/plugins/log.sql/i18n/conf/it.php index c47416003c..5a867e0177 100644 --- a/core/src/plugins/log.sql/i18n/conf/it.php +++ b/core/src/plugins/log.sql/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "DB Storage" => "Memoria DB", diff --git a/core/src/plugins/log.sql/i18n/conf/pt.php b/core/src/plugins/log.sql/i18n/conf/pt.php index 38a97f16d9..2ff0939adf 100644 --- a/core/src/plugins/log.sql/i18n/conf/pt.php +++ b/core/src/plugins/log.sql/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "DB Storage" => "Armazenamento da BD", diff --git a/core/src/plugins/log.sql/manifest.xml b/core/src/plugins/log.sql/manifest.xml index 93b3e71c44..c20222466f 100644 --- a/core/src/plugins/log.sql/manifest.xml +++ b/core/src/plugins/log.sql/manifest.xml @@ -29,5 +29,5 @@ - + diff --git a/core/src/plugins/log.sql/queries.json b/core/src/plugins/log.sql/queries.json index 152022ee31..4410698ad2 100644 --- a/core/src/plugins/log.sql/queries.json +++ b/core/src/plugins/log.sql/queries.json @@ -52,7 +52,7 @@ "order":"Date_sortable" }, "DIAGRAM":"bar", - "SQL":"SELECT DATE( logdate )AS Date, COUNT( DISTINCT ajxp_log.user ) AS Connections FROM ajxp_log WHERE ajxp_log.user IN ( SELECT DISTINCT login FROM ajxp_user_rights WHERE severity = \"INFO\" AND login NOT IN ( SELECT DISTINCT login FROM ajxp_user_rights WHERE repo_uuid = \"ajxp.parent_user\" ) ) AND AJXP_CURSOR_DATE GROUP BY DATE( logdate ) ORDER BY logdate DESC " + "SQL":"SELECT DATE( logdate )AS Date, COUNT( DISTINCT ajxp_log.user ) AS Connections FROM ajxp_log WHERE ajxp_log.user IN ( SELECT DISTINCT login FROM ajxp_user_rights WHERE severity = \"INFO\" AND login NOT IN ( SELECT DISTINCT login FROM ajxp_user_rights WHERE repo_uuid = \"ajxp.parent_user\" ) ) AND AJXP_CURSOR_DATE GROUP BY DATE( logdate ) ORDER BY DATE(logdate) DESC " }, { "NAME":"connections_per_day", @@ -63,7 +63,7 @@ "order":"Date_sortable" }, "DIAGRAM":"bar", - "SQL":"SELECT DATE( logdate )AS Date, COUNT( DISTINCT id ) AS Connections FROM ajxp_log WHERE ajxp_log.user IN ( SELECT DISTINCT login FROM ajxp_user_rights WHERE severity = \"INFO\" AND login NOT IN (SELECT DISTINCT login FROM ajxp_user_rights WHERE repo_uuid = \"ajxp.parent_user\" ) ) AND AJXP_CURSOR_DATE AND (params LIKE \"Log In%\" OR message LIKE \"Log In%\" ) AND ( params LIKE \"%WebUI%\" ) GROUP BY DATE( logdate ) ORDER BY logdate DESC" + "SQL":"SELECT DATE( logdate )AS Date, COUNT( DISTINCT id ) AS Connections FROM ajxp_log WHERE ajxp_log.user IN ( SELECT DISTINCT login FROM ajxp_user_rights WHERE severity = \"INFO\" AND login NOT IN (SELECT DISTINCT login FROM ajxp_user_rights WHERE repo_uuid = \"ajxp.parent_user\" ) ) AND AJXP_CURSOR_DATE AND (params LIKE \"Log In%\" OR message LIKE \"Log In%\" ) AND ( params LIKE \"%WebUI%\" ) GROUP BY DATE( logdate ) ORDER BY DATE(logdate) DESC" }, { "SEPARATOR":true, @@ -78,7 +78,7 @@ "order":"Date_sortable" }, "DIAGRAM":"bar", - "SQL":"SELECT DATE(logdate) AS Date, COUNT(distinct id) AS Downloads FROM ajxp_log WHERE severity = \"INFO\" AND (params like \"Download%\" OR message like \"Download%\" ) AND AJXP_CURSOR_DATE GROUP BY DATE(logdate) ORDER BY logdate DESC" + "SQL":"SELECT DATE(logdate) AS Date, COUNT(distinct id) AS Downloads FROM ajxp_log WHERE severity = \"INFO\" AND (params like \"Download%\" OR message like \"Download%\" ) AND AJXP_CURSOR_DATE GROUP BY DATE(logdate) ORDER BY DATE(logdate) DESC" }, { "NAME":"uploads_per_day", @@ -89,7 +89,7 @@ "order":"Date_sortable" }, "DIAGRAM":"bar", - "SQL":"SELECT DATE(logdate) AS Date, COUNT(distinct id) AS Uploads FROM ajxp_log WHERE severity = \"INFO\" AND (message like \"Upload%\" OR message like \"Upload%\") AND AJXP_CURSOR_DATE GROUP BY DATE(logdate) ORDER BY logdate DESC" + "SQL":"SELECT DATE(logdate) AS Date, COUNT(distinct id) AS Uploads FROM ajxp_log WHERE severity = \"INFO\" AND (message like \"Upload%\" OR message like \"Upload%\") AND AJXP_CURSOR_DATE GROUP BY DATE(logdate) ORDER BY DATE(logdate) DESC" }, { "NAME":"sharedfiles_per_day", @@ -100,7 +100,7 @@ "order":"Date_sortable" }, "DIAGRAM":"bar", - "SQL":"SELECT DATE( logdate )AS Date, COUNT( DISTINCT id ) AS Shares FROM ajxp_log WHERE severity = \"INFO\" AND AJXP_CURSOR_DATE AND ajxp_log.user IN ( SELECT DISTINCT login FROM ajxp_user_rights WHERE login NOT IN ( SELECT DISTINCT login FROM ajxp_user_rights WHERE repo_uuid = \"ajxp.parent_user\" ) ) AND (params LIKE \"New Share%\" OR message LIKE \"New Share%\" ) GROUP BY DATE( logdate ) ORDER BY logdate DESC" + "SQL":"SELECT DATE( logdate )AS Date, COUNT( DISTINCT id ) AS Shares FROM ajxp_log WHERE severity = \"INFO\" AND AJXP_CURSOR_DATE AND ajxp_log.user IN ( SELECT DISTINCT login FROM ajxp_user_rights WHERE login NOT IN ( SELECT DISTINCT login FROM ajxp_user_rights WHERE repo_uuid = \"ajxp.parent_user\" ) ) AND (params LIKE \"New Share%\" OR message LIKE \"New Share%\" ) GROUP BY DATE( logdate ) ORDER BY DATE(logdate) DESC" }, { "NAME":"most_downloaded", @@ -112,4 +112,4 @@ "DIAGRAM": "pie", "SQL":"SELECT SUBSTR(params,7) AS File, COUNT( 1 ) AS Downloaded FROM ajxp_log WHERE severity = \"INFO\" AND (params LIKE \"Download%\" OR message LIKE \"Download%\" ) AND AJXP_CURSOR_DATE GROUP BY params ORDER BY COUNT( 1 ) DESC LIMIT 20" } -] \ No newline at end of file +] diff --git a/core/src/plugins/log.syslog/SysLogDriver.php b/core/src/plugins/log.syslog/SysLogDriver.php new file mode 100644 index 0000000000..2ef57913c0 --- /dev/null +++ b/core/src/plugins/log.syslog/SysLogDriver.php @@ -0,0 +1,220 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ + +namespace Pydio\Log\Implementation; + +use Pydio\Core\Model\ContextInterface; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Standard logger. Writes logs into text files + * @package AjaXplorer_Plugins + * @subpackage Log + */ +class SysLogDriver extends TextLogDriver +{ + /** + * @var Integer File handle to currently open log file. + */ + public $fileHandle = false; + + /** + * @var array stack of log messages to be written when file becomes available. + */ + public $stack; + + /** + * @var String identifer + */ + public $signature; + + /** + * Close file handle on objects destructor. + */ + public function __destruct() + { + if ($this->fileHandle !== false) { + $this->close(); + } + parent::__destruct(); + } + + /** + * If the plugin is cloned, make sure to renew the $fileHandle + */ + public function __clone() + { + $this->close(); + $this->open(); + parent::__clone(); + } + + /** + * Initialise storage: check and/or make log folder and file. + */ + public function initStorage() + { + $this->open(); + } + + /** + * Open log file for append, and flush out buffered messages to the file. + */ + public function open() + { + $this->fileHandle = openlog($this->signature, LOG_ODELAY | LOG_PID, LOG_LOCAL0); + if ($this->fileHandle !== false && count($this->stack)) { + $this->stackFlush(); + } + } + + /** + * Initialise the text log driver. + * + * Sets the user defined options. + * Makes sure that the folder and file exist, and makes them if they don't. + * + * @param ContextInterface $ctx + * @param array $options + */ + public function init(ContextInterface $ctx, $options = []) + { + parent::init($ctx, $options); + $this->stack = array(); + $this->fileHandle = false; + + $this->signature = $this->options["IDENTIFIER"]; + + $this->initStorage(); + + } + + /** + * Write text to the log file. + * + * If write is not allowed because the file is not yet open, the message is buffered until + * file becomes available. + * + * @param String $level Log severity: one of LOG_LEVEL_* (DEBUG,INFO,NOTICE,WARNING,ERROR) + * @param String $ip The client ip + * @param String $user The user login + * @param String $repositoryId current repository ID + * @param String $source The source of the message + * @param String $prefix The prefix of the message + * @param String $message The message to log + * @param array $nodePathes + */ + public function write2($level, $ip, $user, $repositoryId, $source, $prefix, $message, $nodePathes = array()) + { + //syslog already take care of timestamp and log severity + $textMessage = "$ip\t$user\t$source\t$prefix\t$message"; + + switch ($level) { + case LOG_LEVEL_DEBUG: + $sysLevel = LOG_DEBUG; + break; + case LOG_LEVEL_INFO: + $sysLevel = LOG_INFO; + break; + case LOG_LEVEL_NOTICE: + $sysLevel = LOG_NOTICE; + break; + case LOG_LEVEL_WARNING: + $sysLevel = LOG_WARNING; + break; + case LOG_LEVEL_ERROR: + $sysLevel = LOG_ERR; + break; + default: + $sysLevel = LOG_LEVEL_INFO; + break; + } + + + if ($this->fileHandle !== false) { + + if (count($this->stack)) $this->stackFlush(); + syslog($sysLevel, $textMessage); + + } else { + $this->stack[] = array($sysLevel, $textMessage); + } + + } + + /** + * Flush the stack/buffer of messages that couldn't be written earlier. + * + */ + public function stackFlush() + { + // Flush stack for messages that could have been written before the file opening. + foreach ($this->stack as $message) { + syslog($message[0], $message[1]); + } + $this->stack = array(); + } + + /** + * closes the handle to the log file + * + * @access public + */ + public function close() + { + if ($this->fileHandle) closelog(); + } + + /** + * List available logs in XML format. + * + * This method prints the response. + * + * @param String $nodeName Name of the XML node to use as response. + * @param Integer $year The year to list. + * @param Integer $month The month to list. + * @param string $rootPath + * @return null + * @internal param bool $print + */ + public function listLogFiles($nodeName = "file", $year = null, $month = null, $rootPath = "/logs") + { + return ["$rootPath/see" => [ + "date" => "", "icon" => "", "display" => "Logs are not readable via this GUI, they are sent directly to your system logger daemon.", + "text" => "Logs are not readable via this GUI, they are sent directly to your system logger daemon.", "is_file" => 1, "filename" => "$rootPath/see" + ]]; + } + + /** + * Get a log in XML format. + * + * @param $parentDir + * @param String $date Date in m-d-y format. + * @param String $nodeName The name of the node to use for each log item. + * @param string $rootPath + * @param int $cursor + * @return null + */ + public function listLogs($parentDir, $date, $nodeName = "log", $rootPath = "/logs", $cursor = -1) + { + } +} diff --git a/core/src/plugins/log.syslog/class.sysLogDriver.php b/core/src/plugins/log.syslog/class.sysLogDriver.php deleted file mode 100644 index 7667707647..0000000000 --- a/core/src/plugins/log.syslog/class.sysLogDriver.php +++ /dev/null @@ -1,213 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Standard logger. Writes logs into text files - * @package AjaXplorer_Plugins - * @subpackage Log - */ -class sysLogDriver extends textLogDriver -{ - /** - * @var Integer File handle to currently open log file. - */ - public $fileHandle = false; - - /** - * @var Array stack of log messages to be written when file becomes available. - */ - public $stack; - - /** - * @var String identifer - */ - public $signature; - - /** - * Close file handle on objects destructor. - */ - public function __destruct() - { - if ($this->fileHandle !== false) { - $this->close(); - } - } - - /** - * If the plugin is cloned, make sure to renew the $fileHandle - */ - public function __clone() { - $this->close(); - $this->open(); - } - - /** - * Initialise storage: check and/or make log folder and file. - */ - public function initStorage() - { - $this->open(); - } - - /** - * Open log file for append, and flush out buffered messages to the file. - */ - public function open() - { - $this->fileHandle = openlog($this->signature, LOG_ODELAY | LOG_PID, LOG_LOCAL0 ); - if ($this->fileHandle !== false && count($this->stack)) { - $this->stackFlush(); - } - } - - /** - * Initialise the text log driver. - * - * Sets the user defined options. - * Makes sure that the folder and file exist, and makes them if they don't. - * - * @param Array $options array of options specific to the logger driver. - * @access public - * @return null - */ - public function init($options) - { - parent::init($options); - $this->severityDescription = 0; - $this->stack = array(); - $this->fileHandle = false; - - $this->signature = $this->options["IDENTIFIER"]; - - $this->initStorage(); - - } - - /** - * Write text to the log file. - * - * If write is not allowed because the file is not yet open, the message is buffered until - * file becomes available. - * - * @param String $level Log severity: one of LOG_LEVEL_* (DEBUG,INFO,NOTICE,WARNING,ERROR) - * @param String $ip The client ip - * @param String $user The user login - * @param String $source The source of the message - * @param String $prefix The prefix of the message - * @param String $message The message to log - * - */ - public function write2($level, $ip, $user, $source, $prefix, $message, $nodePathes = array()) - { - //syslog already take care of timestamp and log severity - $textMessage = "$ip\t$user\t$source\t$prefix\t$message"; - - switch ($level) { - case LOG_LEVEL_DEBUG: - $sysLevel = LOG_DEBUG; - break; - case LOG_LEVEL_INFO: - $sysLevel = LOG_INFO; - break; - case LOG_LEVEL_NOTICE: - $sysLevel = LOG_NOTICE; - break; - case LOG_LEVEL_WARNING: - $sysLevel = LOG_WARNING; - break; - case LOG_LEVEL_ERROR: - $sysLevel = LOG_ERR; - break; - } - - - if ($this->fileHandle !== false) { - - if(count($this->stack)) $this->stackFlush(); - syslog($sysLevel, $textMessage); - - } else { - $this->stack[] = array($sysLevel, $textMessage); - } - - } - - /** - * Flush the stack/buffer of messages that couldn't be written earlier. - * - */ - public function stackFlush() - { - // Flush stack for messages that could have been written before the file opening. - foreach ($this->stack as $message) { - syslog($message[0], $message[1]); - } - $this->stack = array(); - } - - /** - * closes the handle to the log file - * - * @access public - */ - public function close() - { - if($this->fileHandle) closelog(); - } - - /** - * List available logs in XML format. - * - * This method prints the response. - * - * @param String $nodeName Name of the XML node to use as response. - * @param Integer $year The year to list. - * @param Integer $month The month to list. - * @param string $rootPath - * @param bool $print - * @return null - */ - public function xmlListLogFiles($nodeName="file", $year=null, $month=null, $rootPath = "/logs", $print = true) - { - $xml = "<$nodeName icon=\"toggle_log.png\" date=\"\" - display=\"Logs are not readable via this GUI, they are sent directly to your system logger daemon.\" - text=\"Logs are not readable via this GUI, they are sent directly to your system logger daemon.\" - is_file=\"1\" - filename=\"$rootPath/see\"/>"; - if($print) print $xml; - return array($xml); - } - - /** - * Get a log in XML format. - * - * @param $parentDir - * @param String $date Date in m-d-y format. - * @param String $nodeName The name of the node to use for each log item. - * @param string $rootPath - * @return null - */ - public function xmlLogs($parentDir, $date, $nodeName = "log", $rootPath = "/logs") - { - } -} diff --git a/core/src/plugins/log.syslog/i18n/conf/cs.php b/core/src/plugins/log.syslog/i18n/conf/cs.php index 976906f256..3c9f84ecf4 100644 --- a/core/src/plugins/log.syslog/i18n/conf/cs.php +++ b/core/src/plugins/log.syslog/i18n/conf/cs.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Syslog logger" => "Syslog logger", diff --git a/core/src/plugins/log.syslog/i18n/conf/de.php b/core/src/plugins/log.syslog/i18n/conf/de.php index 3e3fbc3935..e5e4896325 100644 --- a/core/src/plugins/log.syslog/i18n/conf/de.php +++ b/core/src/plugins/log.syslog/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Syslog logger" => "Syslog", diff --git a/core/src/plugins/log.syslog/i18n/conf/en.php b/core/src/plugins/log.syslog/i18n/conf/en.php index 42400c91c8..b6d63de0c2 100644 --- a/core/src/plugins/log.syslog/i18n/conf/en.php +++ b/core/src/plugins/log.syslog/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Syslog logger" => "Syslog logger", diff --git a/core/src/plugins/log.syslog/i18n/conf/fr.php b/core/src/plugins/log.syslog/i18n/conf/fr.php index 15b9308088..051afd28ca 100644 --- a/core/src/plugins/log.syslog/i18n/conf/fr.php +++ b/core/src/plugins/log.syslog/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Syslog logger" => "Journaux système", diff --git a/core/src/plugins/log.syslog/i18n/conf/it.php b/core/src/plugins/log.syslog/i18n/conf/it.php index 1f6fd943ab..76c43bd583 100644 --- a/core/src/plugins/log.syslog/i18n/conf/it.php +++ b/core/src/plugins/log.syslog/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Syslog logger" => "Logger Syslog", diff --git a/core/src/plugins/log.syslog/i18n/conf/pt.php b/core/src/plugins/log.syslog/i18n/conf/pt.php index 245bbdd1ef..7855d24933 100644 --- a/core/src/plugins/log.syslog/i18n/conf/pt.php +++ b/core/src/plugins/log.syslog/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Syslog logger" => "Registos Syslog", diff --git a/core/src/plugins/log.syslog/manifest.xml b/core/src/plugins/log.syslog/manifest.xml index 6973c911df..9075c97e9a 100644 --- a/core/src/plugins/log.syslog/manifest.xml +++ b/core/src/plugins/log.syslog/manifest.xml @@ -4,7 +4,7 @@ Charles du Jeu - + diff --git a/core/src/plugins/log.text/TextLogDriver.php b/core/src/plugins/log.text/TextLogDriver.php new file mode 100644 index 0000000000..fabed6bf4e --- /dev/null +++ b/core/src/plugins/log.text/TextLogDriver.php @@ -0,0 +1,345 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ + +namespace Pydio\Log\Implementation; + +use Exception; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Core\Utils\Vars\StringHelper; + +use Pydio\Core\Utils\Vars\VarsFilter; +use Pydio\Log\Core\AbstractLogDriver; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Standard logger. Writes logs into text files + * @package AjaXplorer_Plugins + * @subpackage Log + */ +class TextLogDriver extends AbstractLogDriver +{ + /** + * @var Integer Default permissions, in chmod format. + */ + public $USER_GROUP_RIGHTS = 0770; + + /** + * @var resource File handle to currently open log file. + */ + public $fileHandle; + + /** + * @var array stack of log messages to be written when file becomes available. + */ + public $stack; + + /** + * @var String full path to the directory where logs will be kept, with trailing slash. + */ + public $storageDir = ""; + + /** + * @var String name of the log file to write. + */ + public $logFileName = ""; + + + /** + * Close file handle on objects destructor. + */ + public function __destruct() + { + if ($this->fileHandle !== false) $this->close(); + } + + /** + * If the plugin is cloned, make sure to renew the $fileHandle + */ + public function __clone() + { + $this->close(); + $this->open(); + } + + /** + * Initialise storage: check and/or make log folder and file. + */ + public function initStorage() + { + $storageDir = $this->storageDir; + if (!file_exists($storageDir)) { + @mkdir($storageDir); + } + $this->open(); + } + + /** + * Open log file for append, and flush out buffered messages to the file. + */ + public function open() + { + if ($this->storageDir != "") { + $create = false; + if (!file_exists($this->storageDir . $this->logFileName)) { + // file creation + $create = true; + } + $this->fileHandle = @fopen($this->storageDir . $this->logFileName, "at+"); + if ($this->fileHandle === false) { + error_log("Cannot open log file " . $this->storageDir . $this->logFileName); + } + if ($this->fileHandle !== false && count($this->stack)) { + $this->stackFlush(); + } + if ($create && $this->fileHandle !== false) { + $mainLink = $this->storageDir . "ajxp_access.log"; + if (file_exists($mainLink)) { + @unlink($mainLink); + } + @symlink($this->storageDir . $this->logFileName, $mainLink); + } + } + } + + /** + * Initialise the text log driver. + * + * Sets the user defined options. + * Makes sure that the folder and file exist, and makes them if they don't. + * + * @param ContextInterface $ctx + * @param array $options + */ + public function init(ContextInterface $ctx, $options = []) + { + parent::init($ctx, $options); + + $this->stack = array(); + $this->fileHandle = false; + + + $this->storageDir = isset($this->options['LOG_PATH']) ? $this->options['LOG_PATH'] : ""; + $this->storageDir = VarsFilter::filter($this->storageDir, $ctx); + $this->storageDir = (rtrim($this->storageDir)) . "/"; + $this->logFileName = isset($this->options['LOG_FILE_NAME']) ? $this->options['LOG_FILE_NAME'] : 'log_' . date('m-d-y') . '.txt'; + $this->USER_GROUP_RIGHTS = isset($this->options['LOG_CHMOD']) ? $this->options['LOG_CHMOD'] : 0770; + + if (preg_match("/(.*)date\('(.*)'\)(.*)/i", $this->logFileName, $matches)) { + $this->logFileName = $matches[1] . date($matches[2]) . $matches[3]; + } + + $this->initStorage(); + } + + /** + * Write text to the log file. + * + * If write is not allowed because the file is not yet open, the message is buffered until + * file becomes available. + * + * @param String $level Log severity: one of LOG_LEVEL_* (DEBUG,INFO,NOTICE,WARNING,ERROR) + * @param String $ip The client ip + * @param String $user The user login + * @param String $repositoryId current repository ID + * @param String $source The source of the message + * @param String $prefix The prefix of the message + * @param String $message The message to log + * @param array $nodePathes + * @throws Exception + */ + public function write2($level, $ip, $user, $repositoryId, $source, $prefix, $message, $nodePathes = array()) + { + if (InputFilter::detectXSS($message)) $message = "XSS Detected in message!"; + $textMessage = date("m-d-y") . " " . date("H:i:s") . "\t"; + $textMessage .= "$ip\t" . strtoupper((string)$level) . "\t$user\t$source\t$prefix\t$message\n"; + + if ($this->fileHandle !== false) { + if (count($this->stack)) $this->stackFlush(); + if (fwrite($this->fileHandle, $textMessage) === false) { + throw new Exception("There was an error writing to log file ($this->logFileName)"); + } + } else { + $this->stack[] = $textMessage; + } + } + + /** + * Flush the stack/buffer of messages that couldn't be written earlier. + * + */ + public function stackFlush() + { + // Flush stack for messages that could have been written before the file opening. + foreach ($this->stack as $message) { + @fwrite($this->fileHandle, $message); + } + $this->stack = array(); + } + + /** + * closes the handle to the log file + * + * @access public + */ + public function close() + { + if (is_resource($this->fileHandle)) { + fclose($this->fileHandle); + $this->fileHandle = FALSE; + } + } + + /** + * List available logs in XML format. + * + * This method prints the response. + * + * @param String $nodeName Name of the XML node to use as response. + * @param Integer $year The year to list. + * @param Integer $month The month to list. + * @param string $rootPath + * @return null + */ + public function listLogFiles($nodeName = "file", $year = null, $month = null, $rootPath = "/logs") + { + $logs = array(); + if (!is_dir($this->storageDir)) { + return $logs; + } + $years = array(); + $months = array(); + if (($handle = opendir($this->storageDir)) !== false) { + while ($file = readdir($handle)) { + if ($file == "index.html" || $file == "ajxp_access.log") continue; + $split = explode(".", $file); + if (!count($split) || $split[0] == "") continue; + $split2 = explode("_", $split[0]); + $date = $split2[1]; + $dSplit = explode("-", $date); + $time = mktime(0, 0, 1, intval($dSplit[0]), intval($dSplit[1]), intval($dSplit[2])); + $display = date("l d", $time); + $fullYear = date("Y", $time); + $fullMonth = date("F", $time); + $logM = $fullMonth; + if ($year != null && $fullYear != $year) continue; + if ($month != null && $fullMonth != $month) continue; + $key = "$rootPath/$fullYear/$fullMonth/$date"; + $logs[$key] = [ + "icon" => "toggle_log.png", + "date" => $display, + "text" => $date, + "is_file" => false, + "filename" => $key, + "ajxp_mime" => "datagrid", + "grid_datasource" => "get_action=ls&dir=" . urlencode($key), + "grid_header_title" => "Application Logs for $date", + "grid_actions" => "refresh,filter,copy_as_text" + ]; + $years["$rootPath/$fullYear"] = [ + "icon" => "x-office-calendar.png", + "date" => $fullYear, + "display" => $fullYear, + "text" => $fullYear, + "is_file" => false, + "filename" => "$rootPath/$fullYear" + ]; + $months["$rootPath/$fullYear/$fullMonth"] = [ + "icon" => "x-office-calendar.png", + "date" => $fullMonth, + "display" => $logM, + "text" => $fullMonth, + "is_file" => false, + "filename" => "$rootPath/$fullYear/$fullMonth" + ]; + } + closedir($handle); + } + $result = $years; + if ($year != null) { + $result = $months; + if ($month != null) { + $result = $logs; + } + } + krsort($result, SORT_STRING); + return $result; + } + + /** + * Get a log in XML format. + * + * @param $parentDir + * @param String $date Date in m-d-y format. + * @param String $nodeName The name of the node to use for each log item. + * @param string $rootPath + * @param int $cursor + * @return null + */ + public function listLogs($parentDir, $date, $nodeName = "log", $rootPath = "/logs", $cursor = -1) + { + $logs = []; + $fName = $this->storageDir . "log_" . $date . ".txt"; + if (!is_file($fName) || !is_readable($fName)) { + return $logs; + } + + $lines = file($fName); + foreach ($lines as $line) { + $line = StringHelper::xmlEntities($line); + $matches = explode("\t", $line, 7); + if (count($matches) == 6) { + $matches[6] = $matches[5]; + $matches[5] = $matches[4]; + $matches[4] = $matches[3]; + $matches[3] = ""; + } + if (count($matches) == 7) { + $fileName = $parentDir . "/" . $matches[0]; + foreach ($matches as $key => $match) { + $match = str_replace("\"", "'", $match); + $matches[$key] = $match; + } + if (count($matches) < 3) continue; + // rebuild timestamp + $date = $matches[0]; + list($m, $d, $Y, $h, $i, $s) = sscanf($date, "%i-%i-%i %i:%i:%i"); + $tStamp = mktime($h, $i, $s, $m, $d, $Y); + $logs[$fileName] = [ + "is_file" => true, + "ajxp_modiftime" => $tStamp, + "filename" => $fileName, + "ajxp_mime" => "log", + "date" => $matches[0], + "ip" => $matches[1], + "level" => $matches[2], + "user" => $matches[3], + "source"=> $matches[4], + "action" => $matches[5], + "params" => $matches[6], + "icon" => "toggle_log.png" + ]; + } + } + return $logs; + } +} diff --git a/core/src/plugins/log.text/class.textLogDriver.php b/core/src/plugins/log.text/class.textLogDriver.php deleted file mode 100644 index df486475f1..0000000000 --- a/core/src/plugins/log.text/class.textLogDriver.php +++ /dev/null @@ -1,294 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Standard logger. Writes logs into text files - * @package AjaXplorer_Plugins - * @subpackage Log - */ -class textLogDriver extends AbstractLogDriver -{ - /** - * @var Integer Default permissions, in chmod format. - */ - public $USER_GROUP_RIGHTS = 0770; - - /** - * @var Integer File handle to currently open log file. - */ - public $fileHandle; - - /** - * @var Array stack of log messages to be written when file becomes available. - */ - public $stack; - - /** - * @var String full path to the directory where logs will be kept, with trailing slash. - */ - public $storageDir = ""; - - /** - * @var String name of the log file to write. - */ - public $logFileName = ""; - - - /** - * Close file handle on objects destructor. - */ - public function __destruct() - { - if($this->fileHandle !== false) $this->close(); - } - - /** - * If the plugin is cloned, make sure to renew the $fileHandle - */ - public function __clone() { - $this->close(); - $this->open(); - } - - /** - * Initialise storage: check and/or make log folder and file. - */ - public function initStorage() - { - $storageDir = $this->storageDir; - if (!file_exists($storageDir)) { - @mkdir($storageDir); - } - $this->open(); - } - - /** - * Open log file for append, and flush out buffered messages to the file. - */ - public function open() - { - if ($this->storageDir!="") { - $create = false; - if (!file_exists($this->storageDir . $this->logFileName)) { - // file creation - $create = true; - } - $this->fileHandle = @fopen($this->storageDir . $this->logFileName, "at+"); - if ($this->fileHandle === false) { - error_log("Cannot open log file ".$this->storageDir . $this->logFileName); - } - if ($this->fileHandle !== false && count($this->stack)) { - $this->stackFlush(); - } - if ($create && $this->fileHandle !== false) { - $mainLink = $this->storageDir."ajxp_access.log"; - if (file_exists($mainLink)) { - @unlink($mainLink); - } - @symlink($this->storageDir.$this->logFileName, $mainLink); - } - } - } - - /** - * Initialise the text log driver. - * - * Sets the user defined options. - * Makes sure that the folder and file exist, and makes them if they don't. - * - * @param Array $options array of options specific to the logger driver. - * @access public - * @return null - */ - public function init($options) - { - parent::init($options); - - $this->severityDescription = 0; - $this->stack = array(); - $this->fileHandle = false; - - - $this->storageDir = isset($this->options['LOG_PATH']) ? $this->options['LOG_PATH'] : ""; - $this->storageDir = AJXP_VarsFilter::filter($this->storageDir); - $this->storageDir = (rtrim($this->storageDir))."/"; - $this->logFileName = isset($this->options['LOG_FILE_NAME']) ? $this->options['LOG_FILE_NAME'] : 'log_' . date('m-d-y') . '.txt'; - $this->USER_GROUP_RIGHTS = isset($this->options['LOG_CHMOD']) ? $this->options['LOG_CHMOD'] : 0770; - - if (preg_match("/(.*)date\('(.*)'\)(.*)/i", $this->logFileName, $matches)) { - $this->logFileName = $matches[1].date($matches[2]).$matches[3]; - } - - $this->initStorage(); - } - - /** - * Write text to the log file. - * - * If write is not allowed because the file is not yet open, the message is buffered until - * file becomes available. - * - * @param String $level Log severity: one of LOG_LEVEL_* (DEBUG,INFO,NOTICE,WARNING,ERROR) - * @param String $ip The client ip - * @param String $user The user login - * @param String $source The source of the message - * @param String $prefix The prefix of the message - * @param String $message The message to log - * @throws Exception - * @return void - */ - public function write2($level, $ip, $user, $source, $prefix, $message, $nodePathes = array()) - { - if(AJXP_Utils::detectXSS($message)) $message = "XSS Detected in message!"; - $textMessage = date("m-d-y") . " " . date("H:i:s") . "\t"; - $textMessage .= "$ip\t".strtoupper((string) $level)."\t$user\t$source\t$prefix\t$message\n"; - - if ($this->fileHandle !== false) { - if(count($this->stack)) $this->stackFlush(); - if (fwrite($this->fileHandle, $textMessage) === false) { - throw new Exception("There was an error writing to log file ($this->logFileName)"); - } - } else { - $this->stack[] = $textMessage; - } - } - - /** - * Flush the stack/buffer of messages that couldn't be written earlier. - * - */ - public function stackFlush() - { - // Flush stack for messages that could have been written before the file opening. - foreach ($this->stack as $message) { - @fwrite($this->fileHandle, $message); - } - $this->stack = array(); - } - - /** - * closes the handle to the log file - * - * @access public - */ - public function close() - { - if(is_resource($this->fileHandle)){ - fclose($this->fileHandle); - $this->fileHandle = FALSE; - } - } - - /** - * List available logs in XML format. - * - * This method prints the response. - * - * @param String $nodeName Name of the XML node to use as response. - * @param Integer $year The year to list. - * @param Integer $month The month to list. - * @return null - */ - public function xmlListLogFiles($nodeName="file", $year=null, $month=null, $rootPath = "/logs", $print = true) - { - $dir = $this->storageDir; - if(!is_dir($this->storageDir)) return ; - $logs = array(); - $years = array(); - $months = array(); - if (($handle = opendir($this->storageDir))!==false) { - while ($file = readdir($handle)) { - if($file == "index.html" || $file == "ajxp_access.log") continue; - $split = explode(".", $file); - if(!count($split) || $split[0] == "") continue; - $split2 = explode("_", $split[0]); - $date = $split2[1]; - $dSplit = explode("-", $date); - $time = mktime(0,0,1,intval($dSplit[0]), intval($dSplit[1]), intval($dSplit[2])); - $display = date("l d", $time); - $fullYear = date("Y", $time); - $fullMonth = date("F", $time); - $logY = $fullYear; - $logM = $fullMonth; - if($year != null && $fullYear != $year) continue; - if($month != null && $fullMonth != $month) continue; - $logs[$time] = "<$nodeName icon=\"toggle_log.png\" date=\"$display\" display=\"$display\" text=\"$date\" is_file=\"0\" filename=\"$rootPath/$fullYear/$fullMonth/$date\"/>"; - $years[$logY] = "<$nodeName icon=\"x-office-calendar.png\" date=\"$fullYear\" display=\"$fullYear\" text=\"$fullYear\" is_file=\"0\" filename=\"$rootPath/$fullYear\"/>"; - $months[$logM] = "<$nodeName icon=\"x-office-calendar.png\" date=\"$fullMonth\" display=\"$logM\" text=\"$fullMonth\" is_file=\"0\" filename=\"$rootPath/$fullYear/$fullMonth\"/>"; - } - closedir($handle); - } - $result = $years; - if ($year != null) { - $result = $months; - if ($month != null) { - $result = $logs; - } - } - krsort($result, SORT_STRING); - if($print) foreach($result as $log) print($log); - return $result; - } - - /** - * Get a log in XML format. - * - * @param String $date Date in m-d-y format. - * @param String $nodeName The name of the node to use for each log item. - * @return null - */ - public function xmlLogs($parentDir, $date, $nodeName = "log", $rootPath = "/logs") - { - $fName = $this->storageDir."log_".$date.".txt"; - - if(!is_file($fName) || !is_readable($fName)) return; - - $res = ""; - $lines = file($fName); - foreach ($lines as $line) { - $line = AJXP_Utils::xmlEntities($line); - $matches = explode("\t",$line,7); - if (count($matches) == 6){ - $matches[6] = $matches[5]; - $matches[5] = $matches[4]; - $matches[4] = $matches[3]; - $matches[3] = ""; - } - if (count($matches) == 7) { - $fileName = $parentDir."/".$matches[0]; - foreach ($matches as $key => $match) { - $match = AJXP_Utils::xmlEntities($match); - $match = str_replace("\"", "'", $match); - $matches[$key] = $match; - } - if(count($matches) < 3) continue; - // rebuild timestamp - $date = $matches[0]; - list($m,$d,$Y,$h,$i,$s) = sscanf($date, "%i-%i-%i %i:%i:%i"); - $tStamp = mktime($h,$i,$s,$m,$d,$Y); - print(SystemTextEncoding::toUTF8("<$nodeName is_file=\"1\" ajxp_modiftime=\"$tStamp\" filename=\"$fileName\" ajxp_mime=\"log\" date=\"$matches[0]\" ip=\"$matches[1]\" level=\"$matches[2]\" user=\"$matches[3]\" source=\"$matches[4]\" action=\"$matches[5]\" params=\"$matches[6]\" icon=\"toggle_log.png\" />", false)); - } - } - return ; - } -} diff --git a/core/src/plugins/log.text/i18n/conf/cs.php b/core/src/plugins/log.text/i18n/conf/cs.php index 3d9e3018d7..719df9d0f4 100644 --- a/core/src/plugins/log.text/i18n/conf/cs.php +++ b/core/src/plugins/log.text/i18n/conf/cs.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Text logger" => "Textové logy", @@ -40,4 +40,4 @@ "The port to access Redis server" => "The port to access Redis server", "Cache Prefix" => "Cache Prefix", "" => "", -); \ No newline at end of file +); diff --git a/core/src/plugins/log.text/i18n/conf/de.php b/core/src/plugins/log.text/i18n/conf/de.php index cdbd756231..d820113d42 100644 --- a/core/src/plugins/log.text/i18n/conf/de.php +++ b/core/src/plugins/log.text/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Text logger" => "Textdatei", diff --git a/core/src/plugins/log.text/i18n/conf/en.php b/core/src/plugins/log.text/i18n/conf/en.php index f089cb5076..e19a88f70b 100644 --- a/core/src/plugins/log.text/i18n/conf/en.php +++ b/core/src/plugins/log.text/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Text logger" => "Text logger", diff --git a/core/src/plugins/log.text/i18n/conf/fr.php b/core/src/plugins/log.text/i18n/conf/fr.php index 01b7648c07..69370d5e1e 100644 --- a/core/src/plugins/log.text/i18n/conf/fr.php +++ b/core/src/plugins/log.text/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Text logger" => "Journaux texte", diff --git a/core/src/plugins/log.text/i18n/conf/it.php b/core/src/plugins/log.text/i18n/conf/it.php index e7583ff7af..9ac187da6b 100644 --- a/core/src/plugins/log.text/i18n/conf/it.php +++ b/core/src/plugins/log.text/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Text logger" => "Logger Testuale", @@ -42,4 +42,4 @@ "The hostname to access Redis server" => "The hostname to access Redis server", "The port to access Redis server" => "The port to access Redis server", "Cache Prefix" => "Cache Prefix", -); \ No newline at end of file +); diff --git a/core/src/plugins/log.text/i18n/conf/pt.php b/core/src/plugins/log.text/i18n/conf/pt.php index 5cc580a7fb..04b429944c 100644 --- a/core/src/plugins/log.text/i18n/conf/pt.php +++ b/core/src/plugins/log.text/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Text logger" => "Registos em Texto", diff --git a/core/src/plugins/log.text/manifest.xml b/core/src/plugins/log.text/manifest.xml index 1aac5ae25b..9d897dc565 100644 --- a/core/src/plugins/log.text/manifest.xml +++ b/core/src/plugins/log.text/manifest.xml @@ -1,6 +1,6 @@ - + diff --git a/core/src/plugins/mailer.phpmailer-lite/PhpMailLite.php b/core/src/plugins/mailer.phpmailer-lite/PhpMailLite.php new file mode 100644 index 0000000000..eb16a68c96 --- /dev/null +++ b/core/src/plugins/mailer.phpmailer-lite/PhpMailLite.php @@ -0,0 +1,108 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ + +namespace Pydio\Mailer\Implementation; + +use Exception; +use phpmailerException; +use PHPMailerLite; +use Pydio\Core\Model\Context; +use Pydio\Core\Model\ContextInterface; +use Pydio\Mailer\Core\Mailer; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Send notifications to user on some predefined actions + * @package AjaXplorer_Plugins + * @subpackage Mailer + */ +class PhpMailLite extends Mailer +{ + /** + * @param ContextInterface $ctx + * @param $recipients + * @param $subject + * @param $body + * @param null $from + * @param array $images + * @param bool $useHtml + * @throws Exception + * @throws phpmailerException + */ + protected function sendMailImpl(ContextInterface $ctx, $recipients, $subject, $body, $from = null, $images = array(), $useHtml = true) + { + require_once("lib/class.phpmailer-lite.php"); + $realRecipients = $this->resolveAdresses($ctx, $recipients); + if(!count($realRecipients)){ + return; + } + // NOW IF THERE ARE RECIPIENTS FOR ANY REASON, GO + $mail = new PHPMailerLite(true); + $mail->Mailer = $this->getContextualOption(Context::emptyContext(), "MAILER"); + $mail->Sendmail = $this->getContextualOption(Context::emptyContext(), "SENDMAIL_PATH"); + $from = $this->resolveFrom($ctx, $from); + if (!is_array($from) || empty($from["adress"])) { + throw new Exception("Cannot send email without a FROM address. Please check your core.mailer configuration."); + } + if (!empty($from)) { + if ($from["adress"] != $from["name"]) { + $mail->SetFrom($from["adress"], $from["name"]); + } else { + $mail->SetFrom($from["adress"]); + } + } + foreach ($realRecipients as $address) { + if ($address["adress"] == $address["name"]) { + $mail->AddAddress(trim($address["adress"])); + } else { + $mail->AddAddress(trim($address["adress"]), trim($address["name"])); + } + } + $mail->WordWrap = 50; // set word wrap to 50 characters + $mail->IsHTML($useHtml); // set email format to HTML + $mail->CharSet = "utf-8"; + $mail->Encoding = $this->getContextualOption(Context::emptyContext(), "MAIL_ENCODING"); + foreach ($images as $image) { + $mail->AddEmbeddedImage($image["path"], $image["cid"], '', 'base64', 'image/png'); + } + + $mail->Subject = $subject; + if ($useHtml) { + if (strpos($body, "Body = $body; + } else { + $mail->Body = "" . nl2br($body) . ""; + } + $mail->AltBody = Mailer::simpleHtml2Text($mail->Body); + } else { + $mail->Body = Mailer::simpleHtml2Text($body); + } + + if (!$mail->Send()) { + $message = "Message could not be sent\n"; + $message .= "Mailer Error: " . $mail->ErrorInfo; + throw new Exception($message); + } + + } + +} diff --git a/core/src/plugins/mailer.phpmailer-lite/class.PhpMailLiteMailer.php b/core/src/plugins/mailer.phpmailer-lite/class.PhpMailLiteMailer.php deleted file mode 100644 index 6981423da6..0000000000 --- a/core/src/plugins/mailer.phpmailer-lite/class.PhpMailLiteMailer.php +++ /dev/null @@ -1,86 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Send notifications to user on some predefined actions - * @package AjaXplorer_Plugins - * @subpackage Mailer - */ -class PhpMailLiteMailer extends AjxpMailer -{ - protected function sendMailImpl($recipients, $subject, $body, $from = null, $images = array(), $useHtml = true) - { - require_once("lib/class.phpmailer-lite.php"); - $realRecipients = $this->resolveAdresses($recipients); - - // NOW IF THERE ARE RECIPIENTS FOR ANY REASON, GO - $mail = new PHPMailerLite(true); - $mail->Mailer = $this->getFilteredOption("MAILER"); - $mail->Sendmail = $this->getFilteredOption("SENDMAIL_PATH"); - $from = $this->resolveFrom($from); - if (!is_array($from) || empty($from["adress"])) { - throw new Exception("Cannot send email without a FROM address. Please check your core.mailer configuration."); - } - if (!empty($from)) { - if ($from["adress"] != $from["name"]) { - $mail->SetFrom($from["adress"], $from["name"]); - } else { - $mail->setFrom($from["adress"]); - } - } - foreach ($realRecipients as $address) { - if ($address["adress"] == $address["name"]) { - $mail->AddAddress(trim($address["adress"])); - } else { - $mail->AddAddress(trim($address["adress"]), trim($address["name"])); - } - } - $mail->WordWrap = 50; // set word wrap to 50 characters - $mail->IsHTML($useHtml); // set email format to HTML - $mail->CharSet = "utf-8"; - $mail->Encoding = $this->getFilteredOption("MAIL_ENCODING"); - foreach ($images as $image) { - $mail->AddEmbeddedImage($image["path"], $image["cid"], '', 'base64', 'image/png'); - } - - $mail->Subject = $subject; - if($useHtml){ - if (strpos($body, "Body = $body; - } else { - $mail->Body = "".nl2br($body).""; - } - $mail->AltBody = AjxpMailer::simpleHtml2Text($mail->Body); - }else{ - $mail->Body = AjxpMailer::simpleHtml2Text($body); - } - - if (!$mail->Send()) { - $message = "Message could not be sent\n"; - $message .= "Mailer Error: " . $mail->ErrorInfo; - throw new Exception($message); - } - - } - -} diff --git a/core/src/plugins/mailer.phpmailer-lite/i18n/conf/cs.php b/core/src/plugins/mailer.phpmailer-lite/i18n/conf/cs.php index 61e4f6995a..0eca3de83e 100644 --- a/core/src/plugins/mailer.phpmailer-lite/i18n/conf/cs.php +++ b/core/src/plugins/mailer.phpmailer-lite/i18n/conf/cs.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "PHPMailer-lite" => "PHPMailer-lite", @@ -27,4 +27,4 @@ "Path to sendmail if not the default one" => "Cesta k sendmail, pokud mimo obvyklé umístění", "Mails Encoding" => "Mails Encoding", "Content encoding of the email. Default value should be ok for most configurations. Some specific Windows Exchange + Outlook combination may require switching to 7bit." => "Content encoding of the email. Default value should be ok for most configurations. Some specific Windows Exchange + Outlook combination may require switching to 7bit.", -); \ No newline at end of file +); diff --git a/core/src/plugins/mailer.phpmailer-lite/i18n/conf/de.php b/core/src/plugins/mailer.phpmailer-lite/i18n/conf/de.php index cb6b511b41..51a48bbdb6 100644 --- a/core/src/plugins/mailer.phpmailer-lite/i18n/conf/de.php +++ b/core/src/plugins/mailer.phpmailer-lite/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "PHPMailer-lite" => "PHPMailer-lite", @@ -27,4 +27,4 @@ "Path to sendmail if not the default one" => "Pfad zur ausführbaren Datei 'sendmail'", "Mails Encoding" => "Mails Encoding", "Content encoding of the email. Default value should be ok for most configurations. Some specific Windows Exchange + Outlook combination may require switching to 7bit." => "Content encoding of the email. Default value should be ok for most configurations. Some specific Windows Exchange + Outlook combination may require switching to 7bit.", -); \ No newline at end of file +); diff --git a/core/src/plugins/mailer.phpmailer-lite/i18n/conf/en.php b/core/src/plugins/mailer.phpmailer-lite/i18n/conf/en.php index 289c742715..f40c2f19b0 100644 --- a/core/src/plugins/mailer.phpmailer-lite/i18n/conf/en.php +++ b/core/src/plugins/mailer.phpmailer-lite/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "PHPMailer-lite" => "PHPMailer-lite", @@ -27,4 +27,4 @@ "Path to sendmail if not the default one" => "Path to sendmail if not the default one", "Mails Encoding" => "Mails Encoding", "Content encoding of the email. Default value should be ok for most configurations. Some specific Windows Exchange + Outlook combination may require switching to 7bit." => "Content encoding of the email. Default value should be ok for most configurations. Some specific Windows Exchange + Outlook combination may require switching to 7bit.", -); \ No newline at end of file +); diff --git a/core/src/plugins/mailer.phpmailer-lite/lib/class.phpmailer-lite.php b/core/src/plugins/mailer.phpmailer-lite/lib/class.phpmailer-lite.php index 95f8a8f937..fc142c61d5 100644 --- a/core/src/plugins/mailer.phpmailer-lite/lib/class.phpmailer-lite.php +++ b/core/src/plugins/mailer.phpmailer-lite/lib/class.phpmailer-lite.php @@ -582,7 +582,7 @@ protected function MailSend($header, $body) } $to = implode(', ', $toArr); - $params = sprintf("-oi -f %s", $this->Sender); + $params = sprintf("-oi -f%s", $this->Sender); if ($this->Sender != '' && strlen(ini_get('safe_mode'))< 1) { $old_from = ini_get('sendmail_from'); ini_set('sendmail_from', $this->Sender); diff --git a/core/src/plugins/mailer.phpmailer-lite/manifest.xml b/core/src/plugins/mailer.phpmailer-lite/manifest.xml index 7b5c7b9bb2..3b39f53b16 100644 --- a/core/src/plugins/mailer.phpmailer-lite/manifest.xml +++ b/core/src/plugins/mailer.phpmailer-lite/manifest.xml @@ -6,7 +6,7 @@ - + diff --git a/core/src/plugins/meta.comments/CommentsMetaManager.php b/core/src/plugins/meta.comments/CommentsMetaManager.php new file mode 100644 index 0000000000..eac1181473 --- /dev/null +++ b/core/src/plugins/meta.comments/CommentsMetaManager.php @@ -0,0 +1,256 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Meta\UserGenerated; + +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Access\Core\Model\UserSelection; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Core\Utils\Vars\StatHelper; + +use Pydio\Core\Controller\HTMLWriter; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Access\Meta\Core\AbstractMetaSource; +use Pydio\Access\Metastore\Core\IMetaStoreProvider; +use Pydio\Notification\Core\IFeedStore; + +define("AJXP_META_SPACE_COMMENTS", "AJXP_META_SPACE_COMMENTS"); + +/** + * Class CommentsMetaManager + * Manage use defined comments + * @package Pydio\Meta\Comment + */ +class CommentsMetaManager extends AbstractMetaSource +{ + /** + * @var IMetaStoreProvider + */ + private $metaStore; + /** + * @var IFeedStore + */ + private $feedStore; + + private $storageMode; + + /** + * @param ContextInterface $ctx + * @param AbstractAccessDriver $accessDriver + * @throws \Exception + */ + public function initMeta(ContextInterface $ctx, AbstractAccessDriver $accessDriver) + { + parent::initMeta($ctx, $accessDriver); + $pService = PluginsService::getInstance($ctx); + $feed = $pService->getUniqueActivePluginForType("feed"); + if ($feed) { + $this->storageMode = "FEED"; + $this->feedStore = $feed; + } else { + $store = $pService->getUniqueActivePluginForType("metastore"); + if ($store === false) { + throw new \Exception("The 'meta.comments' plugin requires at least one active 'metastore' plugin"); + } + $this->metaStore = $store; + $this->storageMode = "METASTORE"; + } + } + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $ajxpNode + */ + public function mergeMeta($ajxpNode) + { + $ajxpNode->mergeMetadata(array("ajxp_has_comments_feed" => "true")); + if ($ajxpNode->retrieveMetadata(AJXP_META_SPACE_COMMENTS, false) != null) { + } + + } + + /** + * + * @param \Pydio\Access\Core\Model\AJXP_Node $oldFile + * @param \Pydio\Access\Core\Model\AJXP_Node $newFile + * @param Boolean $copy + */ + public function moveMeta($oldFile, $newFile = null, $copy = false) + { + if($oldFile == null) return; + $feedStore = PluginsService::getInstance($oldFile->getContext())->getUniqueActivePluginForType("feed"); + if ($feedStore !== false) { + $feedStore->updateMetaObject($oldFile->getRepositoryId(), $oldFile->getPath(), ($newFile!=null?$newFile->getPath():null), $copy); + return; + } + + if(!$copy && $this->metaStore->inherentMetaMove()) return; + + $oldMeta = $this->metaStore->retrieveMetadata($oldFile, AJXP_META_SPACE_COMMENTS); + if (!count($oldMeta)) { + return; + } + // If it's a move or a delete, delete old data + if (!$copy) { + $this->metaStore->removeMetadata($oldFile, AJXP_META_SPACE_COMMENTS); + } + // If copy or move, copy data. + if ($newFile != null) { + $this->metaStore->setMetadata($newFile, AJXP_META_SPACE_COMMENTS, $oldMeta); + } + } + + + /** + * @param String $actionName + * @param array $httpVars + * @param array $fileVars + * @param ContextInterface $ctx + */ + public function switchActions($actionName, $httpVars, $fileVars, ContextInterface $ctx) + { + $userSelection = UserSelection::fromContext($ctx, $httpVars); + $uniqNode = $userSelection->getUniqueNode(); + /** @var IFeedStore $feedStore */ + $feedStore = PluginsService::getInstance($ctx)->getUniqueActivePluginForType("feed"); + $existingFeed = $uniqNode->retrieveMetadata(AJXP_META_SPACE_COMMENTS, false); + if ($existingFeed == null) { + $existingFeed = array(); + } + $mess = LocaleService::getMessages(); + switch ($actionName) { + + case "post_comment": + + $uId = $ctx->getUser()->getId(); + $limit = $this->getContextualOption($ctx, "COMMENT_SIZE_LIMIT"); + if (!empty($limit)) { + $content = substr(InputFilter::decodeSecureMagic($httpVars["content"]), 0, $limit); + } else { + $content = InputFilter::decodeSecureMagic($httpVars["content"]); + } + $com = array( + "date" => time(), + "author" => $uId, + "content" => $content + ); + $existingFeed[] = $com; + if ($feedStore!== false) { + $feedStore->persistMetaObject( + $uniqNode->getPath(), + base64_encode($content), + $uniqNode->getRepositoryId(), + $uniqNode->getRepository()->securityScope(), + $uniqNode->getRepository()->getOwner(), + $ctx->getUser()->getId(), + $ctx->getUser()->getGroupPath()); + } else { + $uniqNode->removeMetadata(AJXP_META_SPACE_COMMENTS, false); + $uniqNode->setMetadata(AJXP_META_SPACE_COMMENTS, $existingFeed, false); + } + HTMLWriter::charsetHeader("application/json"); + $com["hdate"] = StatHelper::relativeDate($com["date"], $mess); + $com["path"] = $uniqNode->getPath(); + echo json_encode($com); + + break; + + case "load_comments_feed": + + HTMLWriter::charsetHeader("application/json"); + if ($feedStore !== false) { + $sortBy = isSet($httpVars["sort_by"])? InputFilter::decodeSecureMagic($httpVars["sort_by"]) :"date"; + $sortDir = isSet($httpVars["sort_dir"])? InputFilter::decodeSecureMagic($httpVars["sort_dir"]) :"asc"; + $offset = isSet($httpVars["offset"]) ? intval($httpVars["offset"]) : 0; + $limit = isSet($httpVars["limit"]) ? intval($httpVars["limit"]) : 100; + $uniqNode->loadNodeInfo(); + $data = $feedStore->findMetaObjectsByIndexPath( + $ctx->getRepositoryId(), + $uniqNode->getPath(), + $ctx->getUser()->getId(), + $ctx->getUser()->getGroupPath(), + $offset, + $limit, + $sortBy, + $sortDir, + !$uniqNode->isLeaf() + ); + $theFeed = array(); + foreach ($data as $stdObject) { + $rPath = substr($stdObject->path, strlen($uniqNode->getPath())); + if($rPath == false && $stdObject->path == $uniqNode->getPath()) $rPath = ""; + $rPath = ltrim($rPath, "/"); + $newItem = array( + "date" =>$stdObject->date, + "hdate" => StatHelper::relativeDate($stdObject->date, $mess), + "author" => $stdObject->author, + "content" => base64_decode($stdObject->content), + "path" => $stdObject->path, + "rpath" => $rPath, + "uuid" => $stdObject->uuid + ); + if (isSet($previous) && $previous["author"] == $newItem["author"] && $previous["path"] == $newItem["path"] && $previous["hdate"] == $newItem["hdate"] ) { + $theFeed[count($theFeed) - 1]["content"].= '
      '.$newItem["content"]; + + } else { + $theFeed[] = $newItem; + } + $previous = $newItem; + } + echo json_encode($theFeed); + } else { + foreach ($existingFeed as &$item) { + $item["hdate"] = StatHelper::relativeDate($item["date"], $mess); + } + echo json_encode($existingFeed); + } + + break; + + case "delete_comment": + + $data = json_decode($httpVars["comment_data"], true); + if ($feedStore === false) { + $reFeed = array(); + if($data["author"] != $ctx->getUser()->getId()) break; + foreach ($existingFeed as $fElement) { + if ($fElement["date"] == $data["date"] && $fElement["author"] == $data["author"] && $fElement["content"] == $data["content"]) { + continue; + } + $fElement["hdate"] = StatHelper::relativeDate($fElement["date"], $mess); + $reFeed[] = $fElement; + } + $uniqNode->removeMetadata(AJXP_META_SPACE_COMMENTS, false); + $uniqNode->setMetadata(AJXP_META_SPACE_COMMENTS, $reFeed, false); + HTMLWriter::charsetHeader("application/json"); + echo json_encode($reFeed); + } else { + $feedStore->dismissAlertById($ctx, $data["uuid"], 1); + } + + break; + + default: + break; + } + + } + +} diff --git a/core/src/plugins/meta.comments/class.CommentsMetaManager.php b/core/src/plugins/meta.comments/class.CommentsMetaManager.php deleted file mode 100644 index 1499514c4f..0000000000 --- a/core/src/plugins/meta.comments/class.CommentsMetaManager.php +++ /dev/null @@ -1,228 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -define("AJXP_META_SPACE_COMMENTS", "AJXP_META_SPACE_COMMENTS"); - -class CommentsMetaManager extends AJXP_AbstractMetaSource -{ - /** - * @var MetaStoreProvider - */ - private $metaStore; - /** - * @var AJXP_FeedStore - */ - private $feedStore; - - private $storageMode; - - public function initMeta($accessDriver) - { - parent::initMeta($accessDriver); - $feed = AJXP_PluginsService::getInstance()->getUniqueActivePluginForType("feed"); - if ($feed) { - $this->storageMode = "FEED"; - $this->feedStore = $feed; - } else { - $store = AJXP_PluginsService::getInstance()->getUniqueActivePluginForType("metastore"); - if ($store === false) { - throw new Exception("The 'meta.comments' plugin requires at least one active 'metastore' plugin"); - } - $this->metaStore = $store; - $this->storageMode = "METASTORE"; - } - } - /** - * @param AJXP_Node $ajxpNode - */ - public function mergeMeta($ajxpNode) - { - $ajxpNode->mergeMetadata(array("ajxp_has_comments_feed" => "true")); - if ($ajxpNode->retrieveMetadata(AJXP_META_SPACE_COMMENTS, false) != null) { - } - - } - - /** - * - * @param AJXP_Node $oldFile - * @param AJXP_Node $newFile - * @param Boolean $copy - */ - public function moveMeta($oldFile, $newFile = null, $copy = false) - { - if($oldFile == null) return; - $feedStore = AJXP_PluginsService::getInstance()->getUniqueActivePluginForType("feed"); - if ($feedStore !== false) { - $feedStore->updateMetaObject($oldFile->getRepositoryId(), $oldFile->getPath(), ($newFile!=null?$newFile->getPath():null), $copy); - return; - } - - if(!$copy && $this->metaStore->inherentMetaMove()) return; - - $oldMeta = $this->metaStore->retrieveMetadata($oldFile, AJXP_META_SPACE_COMMENTS); - if (!count($oldMeta)) { - return; - } - // If it's a move or a delete, delete old data - if (!$copy) { - $this->metaStore->removeMetadata($oldFile, AJXP_META_SPACE_COMMENTS); - } - // If copy or move, copy data. - if ($newFile != null) { - $this->metaStore->setMetadata($newFile, AJXP_META_SPACE_COMMENTS, $oldMeta); - } - } - - - /** - * @param String $actionName - * @param Array $httpVars - * @param Array $fileVars - */ - public function switchActions($actionName, $httpVars, $fileVars) - { - $userSelection = new UserSelection($this->accessDriver->repository, $httpVars); - $uniqNode = $userSelection->getUniqueNode(); - $feedStore = AJXP_PluginsService::getInstance()->getUniqueActivePluginForType("feed"); - $existingFeed = $uniqNode->retrieveMetadata(AJXP_META_SPACE_COMMENTS, false); - if ($existingFeed == null) { - $existingFeed = array(); - } - $mess = ConfService::getMessages(); - switch ($actionName) { - - case "post_comment": - - $uId = AuthService::getLoggedUser()->getId(); - $limit = $this->getFilteredOption("COMMENT_SIZE_LIMIT"); - if (!empty($limit)) { - $content = substr(AJXP_Utils::decodeSecureMagic($httpVars["content"]), 0, $limit); - } else { - $content = AJXP_Utils::decodeSecureMagic($httpVars["content"]); - } - $com = array( - "date" => time(), - "author" => $uId, - "content" => $content - ); - $existingFeed[] = $com; - if ($feedStore!== false) { - $feedStore->persistMetaObject( - $uniqNode->getPath(), - base64_encode($content), - $uniqNode->getRepositoryId(), - $uniqNode->getRepository()->securityScope(), - $uniqNode->getRepository()->getOwner(), - AuthService::getLoggedUser()->getId(), - AuthService::getLoggedUser()->getGroupPath()); - } else { - $uniqNode->removeMetadata(AJXP_META_SPACE_COMMENTS, false); - $uniqNode->setMetadata(AJXP_META_SPACE_COMMENTS, $existingFeed, false); - } - HTMLWriter::charsetHeader("application/json"); - $com["hdate"] = AJXP_Utils::relativeDate($com["date"], $mess); - $com["path"] = $uniqNode->getPath(); - echo json_encode($com); - - break; - - case "load_comments_feed": - - HTMLWriter::charsetHeader("application/json"); - if ($feedStore !== false) { - $sortBy = isSet($httpVars["sort_by"])?AJXP_Utils::decodeSecureMagic($httpVars["sort_by"]):"date"; - $sortDir = isSet($httpVars["sort_dir"])?AJXP_Utils::decodeSecureMagic($httpVars["sort_dir"]):"asc"; - $offset = isSet($httpVars["offset"]) ? intval($httpVars["offset"]) : 0; - $limit = isSet($httpVars["limit"]) ? intval($httpVars["limit"]) : 100; - $uniqNode->loadNodeInfo(); - $data = $feedStore->findMetaObjectsByIndexPath( - $this->accessDriver->repository->getId(), - $uniqNode->getPath(), - AuthService::getLoggedUser()->getId(), - AuthService::getLoggedUser()->getGroupPath(), - $offset, - $limit, - $sortBy, - $sortDir, - !$uniqNode->isLeaf() - ); - $theFeed = array(); - foreach ($data as $stdObject) { - $rPath = substr($stdObject->path, strlen($uniqNode->getPath())); - if($rPath == false && $stdObject->path == $uniqNode->getPath()) $rPath = ""; - $rPath = ltrim($rPath, "/"); - $newItem = array( - "date" =>$stdObject->date, - "hdate" => AJXP_Utils::relativeDate($stdObject->date, $mess), - "author" => $stdObject->author, - "content" => base64_decode($stdObject->content), - "path" => $stdObject->path, - "rpath" => $rPath, - "uuid" => $stdObject->uuid - ); - if (isSet($previous) && $previous["author"] == $newItem["author"] && $previous["path"] == $newItem["path"] && $previous["hdate"] == $newItem["hdate"] ) { - $theFeed[count($theFeed) - 1]["content"].= '
      '.$newItem["content"]; - - } else { - $theFeed[] = $newItem; - } - $previous = $newItem; - } - echo json_encode($theFeed); - } else { - foreach ($existingFeed as &$item) { - $item["hdate"] = AJXP_Utils::relativeDate($item["date"], $mess); - } - echo json_encode($existingFeed); - } - - break; - - case "delete_comment": - - $data = json_decode($httpVars["comment_data"], true); - if ($feedStore === false) { - $reFeed = array(); - if($data["author"] != AuthService::getLoggedUser()->getId()) break; - foreach ($existingFeed as $fElement) { - if ($fElement["date"] == $data["date"] && $fElement["author"] == $data["author"] && $fElement["content"] == $data["content"]) { - continue; - } - $fElement["hdate"] = AJXP_Utils::relativeDate($fElement["date"], $mess); - $reFeed[] = $fElement; - } - $uniqNode->removeMetadata(AJXP_META_SPACE_COMMENTS, false); - $uniqNode->setMetadata(AJXP_META_SPACE_COMMENTS, $reFeed, false); - HTMLWriter::charsetHeader("application/json"); - echo json_encode($reFeed); - } else { - $feedStore->dismissAlertById($data["uuid"], 1); - } - - break; - - default: - break; - } - - } - -} diff --git a/core/src/plugins/meta.comments/class.CommentsPanel.js b/core/src/plugins/meta.comments/class.CommentsPanel.js index 854d95e698..2470a5d2d5 100644 --- a/core/src/plugins/meta.comments/class.CommentsPanel.js +++ b/core/src/plugins/meta.comments/class.CommentsPanel.js @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ diff --git a/core/src/plugins/meta.comments/i18n/conf/cs.php b/core/src/plugins/meta.comments/i18n/conf/cs.php index 3ea4b23337..bfb012b734 100644 --- a/core/src/plugins/meta.comments/i18n/conf/cs.php +++ b/core/src/plugins/meta.comments/i18n/conf/cs.php @@ -16,11 +16,11 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Comments Feed" => "Kanál komentářů", "Display a chat-like feed on all nodes" => "Zobrazit kanál podobný chatu na všech uzlech", "Comment limit" => "Comment limit", "Maximum number of characters for each comment" => "Maximum number of characters for each comment", -); \ No newline at end of file +); diff --git a/core/src/plugins/meta.comments/i18n/conf/de.php b/core/src/plugins/meta.comments/i18n/conf/de.php index 27d472d6fd..4dbed67f1a 100644 --- a/core/src/plugins/meta.comments/i18n/conf/de.php +++ b/core/src/plugins/meta.comments/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Comments Feed" => "Kommentare", diff --git a/core/src/plugins/meta.comments/i18n/conf/en.php b/core/src/plugins/meta.comments/i18n/conf/en.php index 92bca4efb0..03dda3ff3e 100644 --- a/core/src/plugins/meta.comments/i18n/conf/en.php +++ b/core/src/plugins/meta.comments/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Comments Feed" => "Comments Feed", diff --git a/core/src/plugins/meta.comments/i18n/conf/fr.php b/core/src/plugins/meta.comments/i18n/conf/fr.php index c7db4b6b10..0cf3ffd284 100644 --- a/core/src/plugins/meta.comments/i18n/conf/fr.php +++ b/core/src/plugins/meta.comments/i18n/conf/fr.php @@ -16,11 +16,11 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Comments Feed" => "Flux de commentaires", "Display a chat-like feed on all nodes" => "Afficher un flux type chat sur tous les noeuds", "Comment limit" => "Limite des commentaires", "Maximum number of characters for each comment" => "Nombre de commentaires maximum pour chaque fil.", -); \ No newline at end of file +); diff --git a/core/src/plugins/meta.comments/i18n/conf/it.php b/core/src/plugins/meta.comments/i18n/conf/it.php index de311630f3..47ce2b80b6 100644 --- a/core/src/plugins/meta.comments/i18n/conf/it.php +++ b/core/src/plugins/meta.comments/i18n/conf/it.php @@ -16,11 +16,11 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Comments Feed" => "Feed Commenti", "Display a chat-like feed on all nodes" => "Visualizza un feed simile alle chat per tutti i nodi", "Comment limit" => "Comment limit", "Maximum number of characters for each comment" => "Maximum number of characters for each comment", -); \ No newline at end of file +); diff --git a/core/src/plugins/meta.comments/manifest.xml b/core/src/plugins/meta.comments/manifest.xml index 5016f27a38..ad005a2983 100644 --- a/core/src/plugins/meta.comments/manifest.xml +++ b/core/src/plugins/meta.comments/manifest.xml @@ -1,6 +1,6 @@ - + @@ -66,6 +66,6 @@ - + diff --git a/core/src/plugins/meta.exif/ExifMetaManager.php b/core/src/plugins/meta.exif/ExifMetaManager.php new file mode 100644 index 0000000000..a2c49a921a --- /dev/null +++ b/core/src/plugins/meta.exif/ExifMetaManager.php @@ -0,0 +1,312 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ + +namespace Pydio\Access\Meta\Exif; + +use Pydio\Access\Core\AbstractAccessDriver; + +use Pydio\Access\Core\Model\UserSelection; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\ApplicationState; + + + +use Pydio\Core\Utils\TextEncoder; +use Pydio\Access\Meta\Core\AbstractMetaSource; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Extract Exif data from JPEG IMAGES + * @package AjaXplorer_Plugins + * @subpackage Meta + */ +class ExifMetaManager extends AbstractMetaSource +{ + protected $metaDefinitions; + + /** + * @param ContextInterface $contextInterface + * @param array $options + */ + public function init(ContextInterface $contextInterface, $options = []) + { + $this->options = $options; + } + + public function performChecks() + { + if (!function_exists("exif_imagetype")) { + throw new \Exception("Exif PHP extension does not seem to be installed!"); + } + } + + /** + * @param ContextInterface $ctx + * @param AbstractAccessDriver $accessDriver + */ + public function initMeta(ContextInterface $ctx, AbstractAccessDriver $accessDriver) + { + parent::initMeta($ctx, $accessDriver); + + if(!function_exists("exif_read_data")) return ; + + $def = $this->getMetaDefinition(); + if (!count($def)) { + return ; + } + $cdataHead = '
      +
      AJXP_MESSAGE[meta.exif.2]AJXP_MESSAGE[meta.exif.3]
      +
      '; + $cdataFoot = '
      '; + $cdataParts = ""; + $even = false; + foreach ($def as $key=>$label) { + $trClass = ($even?" class=\"even\"":""); + $even = !$even; + $cdataParts .= '
      '.$label.'
      #{'.$key.'}
      '; + } + + $selection = $this->getXPath()->query('registry_contributions/client_configs/component_config[@className="InfoPanel"]/infoPanelExtension'); + /** @var \DOMElement $contrib */ + $contrib = $selection->item(0); + $contrib->setAttribute("attributes", implode(",", array_keys($def))); + $contrib->setAttribute("modifier", "ExifCellRenderer.prototype.infoPanelModifier"); + $htmlSel = $this->getXPath()->query('html', $contrib); + $html = $htmlSel->item(0); + $cdata = $this->manifestDoc->createCDATASection($cdataHead . $cdataParts . $cdataFoot); + $html->appendChild($cdata); + + parent::init($ctx, $this->options); + + } + + /** + * @return array + */ + protected function getMetaDefinition() + { + if (isSet($this->metaDefinitions)) { + return $this->metaDefinitions; + } + $fields = $this->options["meta_fields"]; + $arrF = explode(",", $fields); + $labels = $this->options["meta_labels"]; + $arrL = explode(",", $labels); + $result = array(); + foreach ($arrF as $index => $value) { + $value = str_replace(".", "-", $value); + if (isSet($arrL[$index])) { + $result[$value] = $arrL[$index]; + } else { + $result[$value] = $value; + } + } + $this->metaDefinitions = $result; + return $result; + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $requestInterface + * @param \Psr\Http\Message\ResponseInterface $responseInterface + */ + public function extractExif(\Psr\Http\Message\ServerRequestInterface $requestInterface, \Psr\Http\Message\ResponseInterface &$responseInterface) + { + $httpVars = $requestInterface->getParsedBody(); + $ctx = $requestInterface->getAttribute("ctx"); + $userSelection = UserSelection::fromContext($ctx, $httpVars); + $selectedNode = $userSelection->getUniqueNode(); + $realFile = $selectedNode->getRealFile(); + + ApplicationState::safeIniSet('exif.encode_unicode', 'UTF-8'); + $exifData = @exif_read_data($realFile, 0, TRUE); + if($exifData === false || !is_array($exifData)) return; + if ($exifData !== false && isSet($exifData["GPS"])) { + $exifData["COMPUTED_GPS"] = $this->convertGPSData($exifData); + } + $iptc = $this->extractIPTC($realFile); + if(count($iptc)){ + $exifData["IPTC"] = $iptc; + } + $excludeTags = array();// array("componentsconfiguration", "filesource", "scenetype", "makernote", "datadump"); + + $filteredData = array(); + foreach ($exifData as $section => $data) { + $filteredData[$section] = array(); + foreach ($data as $key => $value) { + if (is_array($value)) { + $value = implode(",", $value); + } + if(in_array(strtolower($key), $excludeTags)) continue; + if(strpos($key, "UndefinedTag:") === 0) continue; + $value = preg_replace( '/[^[:print:]]/', '',$value); + $filteredData[$section][$key] = TextEncoder::toUTF8($value); + } + } + + require_once "ExifXmlMessage.php"; + $x = new \Pydio\Core\Http\Response\SerializableResponseStream(); + $x->addChunk(new ExifXmlMessage($filteredData)); + $responseInterface = $responseInterface->withBody($x); + + } + + /** + * @param $str + * @return string + */ + public function string_format($str) + { + $tmpStr = ""; + + for ($i=0;$igetUrl(); + if(!$ajxpNode->isLeaf() || preg_match("/\.zip\//",$currentFile)) return ; + if(!preg_match("/\.jpg$|\.jpeg$|\.tif$|\.tiff$/i",$currentFile)) return ; + $definitions = $this->getMetaDefinition(); + if(!count($definitions)) return ; + if(!function_exists("exif_read_data")) return ; + //if(!exif_imagetype($currentFile)) return ; + $realFile = $ajxpNode->getRealFile(); + $exif = @exif_read_data($realFile, 0, TRUE); + $iptc = $this->extractIPTC($realFile); + + if($exif === false) return ; + $additionalMeta = array(); + foreach ($definitions as $def => $label) { + list($exifSection, $exifName) = explode("-", $def); + if ($exifSection == "COMPUTED_GPS" && !isSet($exif["COMPUTED_GPS"])) { + $exif['COMPUTED_GPS'] = $this->convertGPSData($exif); + } + if (isSet($exif[$exifSection]) && isSet($exif[$exifSection][$exifName])) { + $additionalMeta[$def] = $exif[$exifSection][$exifName]; + } + if ($exifSection == "IPTC" && isSet($iptc[$exifName])){ + $additionalMeta[$def] = $iptc[$exifName]; + } + } + $ajxpNode->mergeMetadata($additionalMeta); + } + + /** + * @param $realFile + * @return array + */ + private function extractIPTC($realFile){ + $output = array(); + if(!function_exists("iptcparse")) { + return $output; + } + getimagesize($realFile,$info); + if(!isset($info['APP13'])) { + return $output; + } + $iptcHeaderArray = array + ( + '2#005'=>'DocumentTitle', + '2#010'=>'Urgency', + '2#015'=>'Category', + '2#020'=>'Subcategories', + '2#025'=>'Keywords', + '2#040'=>'SpecialInstructions', + '2#055'=>'CreationDate', + '2#080'=>'AuthorByline', + '2#085'=>'AuthorTitle', + '2#090'=>'City', + '2#095'=>'State', + '2#101'=>'Country', + '2#103'=>'OTR', + '2#105'=>'Headline', + '2#110'=>'Source', + '2#115'=>'PhotoSource', + '2#116'=>'Copyright', + '2#120'=>'Caption', + '2#122'=>'CaptionWriter' + ); + $iptc =iptcparse($info['APP13']); + if (!is_array($iptc)) { + return $output; + } + foreach (array_keys($iptc) as $key) { + if (isSet($iptcHeaderArray[$key])) { + $cnt = count($iptc[$key]); + $val = ""; + for ($i=0; $i < $cnt; $i++) $val .= $iptc[$key][$i] . " "; + $output[$iptcHeaderArray[$key]] = preg_replace( '/[^[:print:]]/', '',$val); + } + } + return $output; + } + + /** + * @param $exif + * @return array + */ + private function convertGPSData($exif) + { + if(!isSet($exif["GPS"])) return array(); + require_once(AJXP_INSTALL_PATH . "/plugins/meta.exif/GeoConversion.php"); + $converter = new GeoConversion(); + $latDeg=@$this->parseGPSValue($exif["GPS"]["GPSLatitude"][0]); + $latMin=@$this->parseGPSValue($exif["GPS"]["GPSLatitude"][1]); + $latSec=@$this->parseGPSValue($exif["GPS"]["GPSLatitude"][2]); + $latHem=$exif["GPS"]["GPSLatitudeRef"]; + $longDeg=@$this->parseGPSValue($exif["GPS"]["GPSLongitude"][0]); + $longMin=@$this->parseGPSValue($exif["GPS"]["GPSLongitude"][1]); + $longSec=@$this->parseGPSValue($exif["GPS"]["GPSLongitude"][2]); + $longRef=$exif["GPS"]["GPSLongitudeRef"]; + $latSign = ($latHem == "S" ? "-":""); + $longSign = ($longRef == "W" ? "-":""); + $gpsData = array( + "GPS_Latitude"=>"$latDeg deg $latMin' $latSec $latHem--".$converter->DMS2Dd($latSign.$latDeg."o$latMin'$latSec"), + "GPS_Longitude"=>"$longDeg deg $longMin' $longSec $longRef--".$converter->DMS2Dd($longSign.$longDeg."o$longMin'$longSec"), + "GPS_Altitude"=> $exif["GPS"]["GPSAltitude"][0] + ); + return $gpsData; + } + + /** + * @param $value + * @return float + */ + private function parseGPSValue($value) + { + if (strstr($value, "/") === false) { + return floatval($value); + } else { + $exp = explode("/", $value); + return round(intval($exp[0])/intval($exp[1]), 4); + } + } + +} diff --git a/core/src/plugins/meta.exif/ExifXmlMessage.php b/core/src/plugins/meta.exif/ExifXmlMessage.php new file mode 100644 index 0000000000..e12bc9d096 --- /dev/null +++ b/core/src/plugins/meta.exif/ExifXmlMessage.php @@ -0,0 +1,79 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Meta\Exif; + +use Pydio\Core\Utils\Vars\StringHelper; + + +defined('AJXP_EXEC') or die('Access not allowed'); + + +/** + * Class ExifXmlMessage + * @package Pydio\Access\Meta\Exif + */ +class ExifXmlMessage implements \Pydio\Core\Http\Response\XMLSerializableResponseChunk, \Pydio\Core\Http\Response\JSONSerializableResponseChunk +{ + protected $data; + + /** + * ExifXmlMessage constructor. + * @param $filteredData + */ + public function __construct($filteredData) + { + $this->data = $filteredData; + } + + /** + * @return string + */ + public function toXML() + { + $buffer = ""; + foreach ($this->data as $section => $data) { + $buffer .= ""; + foreach ($data as $key => $value) { + $buffer .= "". StringHelper::xmlEntities($value) .""; + } + $buffer .= ""; + } + return $buffer; + + } + + /** + * @return string + */ + public function jsonSerializableKey() + { + return "exif"; + } + + /** + * @return mixed + */ + public function jsonSerializableData() + { + return $this->data; + } + +} \ No newline at end of file diff --git a/core/src/plugins/meta.exif/GeoConversion.php b/core/src/plugins/meta.exif/GeoConversion.php new file mode 100644 index 0000000000..0506af44a0 --- /dev/null +++ b/core/src/plugins/meta.exif/GeoConversion.php @@ -0,0 +1,221 @@ +DMS2Dd('45�22\'38"') -> 45.3772 // +// // +// // +// // +// Considerations: // +// D = Degrees // +// M = Minutes // +// S = Seconds // +// .m = Decimal Minutes // +// .s = Decimal Seconds // +// // +// DM.m (DMm) = Degrees, Minutes, Decimal Minutes (eg. 45o22.6333) // +// D.d (Dd) = Degrees, Decimal Degrees (eg. 45.3772o) // +// DMS (DMS) = Degrees, Minutes, Seconds (eg. 45o22'38") // +//***********************************************************************// + +//**************************Descrio em Portugus*********************// +// Classe para converso de coordenadas de Latitude e Longitude // +// Desenvolvida por: Diego Garrido de Almeida // +// Localizado: Conselheiro Lafaiete - Minas Gerais / Brasil // +// Licenca: Nenhuma, podendo ser alterada, sem necessidade de creditos // +// Utilizatio Recomendada: Conversco das Coordenadas do Google Earth // +// para a API do Google Maps para WEB, atraves // +// do Metodo GeoConversao::DMS2Dd // +// ex: $GeoConversao->DMS2Dd('45a22\'38"') -> 45.3772 // +// // +// Considerades: // +// D = Degrees (Degrais) // +// M = Minutes (Minutos) // +// S = Seconds (Segundos) // +// .m = Decimal Minutes (Decimos de Minuto) // +// .s = Decimal Seconds (Decimos de Segundo) // +// // +// DM.m (DMm) = Degrees, Minutes, Decimal Minutes (ex. 45o22.6333) // +// D.d (Dd) = Degrees, Decimal Degrees (ex. 45.3772o) // +// DMS (DMS) = Degrees, Minutes, Seconds (ex. 45o22'38") // +//*********************************************************************// +namespace Pydio\Access\Meta\Exif; + +/** + * Class GeoConversion + * @package Pydio\Access\Meta\Exif + */ +Class GeoConversion{ + + public $negative = FALSE; + public $real = FALSE; + public $negative_path = ''; + + /** + * @param $string + */ + private function is_negative(&$string) + { + if ($string[0] == '-') { + $this->negative = TRUE; + $string = str_replace('-','',$string); + $this->negative_path = '-'; + } + $real = TRUE; + } + + /** + * @param $string + * @param $decimal + */ + private function replace_special_chars(&$string, $decimal) + { + for ($I = 0 ; $I < strlen($string) ; $I++) { + $not_decimal = $decimal == FALSE ? ($string[$I] != '.') : TRUE; + if (!is_numeric($string[$I]) && $not_decimal && $string[$I] != ' ') { + $string[$I] = ';'; + } else if ($string[$I] == ' ') { + $string[$I] = ''; + } + } + } + + /** + * @param $DMS + * @return array + */ + private function SepDMS($DMS) + { + $this->replace_special_chars($DMS,FALSE); + $dados = explode(';',$DMS); + return array('D' => $dados[0],'M' => $dados[1],'S' => $dados[2]); + } + + /** + * @param $DMm + * @return array + */ + private function SepDMm($DMm) + { + $this->replace_special_chars($DMm,TRUE); + $dados = explode(';',$DMm); + return array('D' => $dados[0],'M' => $dados[1],'m' => $dados[2]); + } + + /** + * @param $Dd + * @return array + */ + private function SepDd($Dd) + { + $this->replace_special_chars($Dd,TRUE); + $dados = explode(';',$Dd); + return array('D' => $dados[0],'d' => $dados[1]); + } + + /** + * @param $DMS + * @return string + */ + public function DMS2DMm($DMS) + { + $this->is_negative($DMS); + + $array_DMm = array('D' => '','M' => '','m' => ''); + $array_DMS = $this->SepDMS($DMS); + + $array_DMm['m'] = $array_DMS['S']/60; + $array_DMm['M'] = $array_DMS['M']; + $array_DMm['D'] = $array_DMS['D']; + + return $this->negative_path.$array_DMm['D'].'�'.($array_DMm['M'] + $array_DMm['m']); + } + + /** + * @param $DMm + * @return string + */ + public function DMm2Dd($DMm) + { + $this->is_negative($DMm); + + $array_Dd = array('D' => '','d' => ''); + $array_DMm = $this->SepDMm($DMm); + + $array_Dd['d'] = ($array_DMm['M'].'.'.$array_DMm['m'])/60; + $array_Dd['D'] = $array_DMm['D']; + + return $this->negative_path.($array_Dd['D'] + $array_Dd['d']); + } + + /** + * @param $DMS + * @return string + */ + public function DMS2Dd($DMS) + { + $this->is_negative($DMS); + + $DMm = $this->DMS2DMm($DMS); + return $this->DMm2Dd($DMm); + } + + /** + * @param $DMm + * @return string + */ + public function DMm2DMS($DMm) + { + $this->is_negative($DMm); + + $array_DMS = array('D' => '', 'M' => '', 'S' => ''); + $array_DMm = $this->SepDMm($DMm); + + $str_S = ((0).".".$array_DMm['m']) * 60; + + $array_DMS['S'] = $str_S; + $array_DMS['M'] = $array_DMm['M']; + $array_DMS['D'] = $array_DMm['D']; + + return $array_DMS['D'].'�'.$array_DMS['M'].'\''.$array_DMS['S'].'"'; + } + + /** + * @param $Dd + * @return string + */ + public function Dd2DMm($Dd) + { + $this->is_negative($Dd); + + $array_DMm = array('D' => '','M' => '','m' => ''); + $array_Dd = $this->SepDd($Dd); + + $str_Mm = ((0).".".$array_Dd['d']) * 60; + + $dados_Mm = explode(".",$str_Mm); + + $array_DMm['m'] = $dados_Mm[1]; + $array_DMm['M'] = $dados_Mm[0]; + $array_DMm['D'] = $array_Dd['D']; + + return $this->negative_path.$array_DMm['D']."� ".$array_DMm['M'].".".$array_DMm['m']; + } + + /** + * @param $Dd + * @return string + */ + public function Dd2DMS($Dd) + { + $this->is_negative($Dd); + + $DMm = $this->Dd2DMm($Dd); + return $this->DMm2DMS($DMm); + } +} diff --git a/core/src/plugins/meta.exif/class.ExifCellRenderer.js b/core/src/plugins/meta.exif/class.ExifCellRenderer.js index 7826a8eaf6..09baac5573 100644 --- a/core/src/plugins/meta.exif/class.ExifCellRenderer.js +++ b/core/src/plugins/meta.exif/class.ExifCellRenderer.js @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ Class.create("ExifCellRenderer", { initialize: function(){ diff --git a/core/src/plugins/meta.exif/class.ExifMetaManager.php b/core/src/plugins/meta.exif/class.ExifMetaManager.php deleted file mode 100644 index 7d30b885dd..0000000000 --- a/core/src/plugins/meta.exif/class.ExifMetaManager.php +++ /dev/null @@ -1,284 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Extract Exif data from JPEG IMAGES - * @package AjaXplorer_Plugins - * @subpackage Meta - */ -class ExifMetaManager extends AJXP_AbstractMetaSource -{ - protected $metaDefinitions; - - public function init($options) - { - $this->options = $options; - } - - public function performChecks() - { - if (!function_exists("exif_imagetype")) { - throw new Exception("Exif PHP extension does not seem to be installed!"); - } - } - - public function initMeta($accessDriver) - { - parent::initMeta($accessDriver); - if(!function_exists("exif_read_data")) return ; - //$messages = ConfService::getMessages(); - $def = $this->getMetaDefinition(); - if (!count($def)) { - return ; - } - $cdataHead = '
      -
      AJXP_MESSAGE[meta.exif.2]AJXP_MESSAGE[meta.exif.3]
      -
      '; - $cdataFoot = '
      '; - $cdataParts = ""; - $even = false; - foreach ($def as $key=>$label) { - $trClass = ($even?" class=\"even\"":""); - $even = !$even; - $cdataParts .= '
      '.$label.'
      #{'.$key.'}
      '; - } - - $selection = $this->getXPath()->query('registry_contributions/client_configs/component_config[@className="InfoPanel"]/infoPanelExtension'); - $contrib = $selection->item(0); - $contrib->setAttribute("attributes", implode(",", array_keys($def))); - $contrib->setAttribute("modifier", "ExifCellRenderer.prototype.infoPanelModifier"); - $htmlSel = $this->getXPath()->query('html', $contrib); - $html = $htmlSel->item(0); - $cdata = $this->manifestDoc->createCDATASection($cdataHead . $cdataParts . $cdataFoot); - $html->appendChild($cdata); - - parent::init($this->options); - - } - - protected function getMetaDefinition() - { - if (isSet($this->metaDefinitions)) { - return $this->metaDefinitions; - } - $fields = $this->options["meta_fields"]; - $arrF = explode(",", $fields); - $labels = $this->options["meta_labels"]; - $arrL = explode(",", $labels); - $result = array(); - foreach ($arrF as $index => $value) { - $value = str_replace(".", "-", $value); - if (isSet($arrL[$index])) { - $result[$value] = $arrL[$index]; - } else { - $result[$value] = $value; - } - } - $this->metaDefinitions = $result; - return $result; - } - - public function extractExif($actionName, $httpVars, $fileVars) - { - $repo = $this->accessDriver->repository; - $userSelection = new UserSelection($this->accessDriver->repository, $httpVars); - $repo->detectStreamWrapper(true); - - $selectedNode = $userSelection->getUniqueNode(); - $realFile = AJXP_MetaStreamWrapper::getRealFSReference($selectedNode->getUrl()); - - AJXP_Utils::safeIniSet('exif.encode_unicode', 'UTF-8'); - $exifData = @exif_read_data($realFile, 0, TRUE); - if($exifData === false || !is_array($exifData)) return; - if ($exifData !== false && isSet($exifData["GPS"])) { - $exifData["COMPUTED_GPS"] = $this->convertGPSData($exifData); - } - $iptc = $this->extractIPTC($realFile); - if(count($iptc)){ - $exifData["IPTC"] = $iptc; - } - $excludeTags = array();// array("componentsconfiguration", "filesource", "scenetype", "makernote", "datadump"); - $format = "xml"; - if(isSet($httpVars["format"]) && $httpVars["format"] == "json"){ - $format = "json"; - } - $filteredData = array(); - foreach ($exifData as $section => $data) { - $filteredData[$section] = array(); - foreach ($data as $key => $value) { - if (is_array($value)) { - $value = implode(",", $value); - } - if(in_array(strtolower($key), $excludeTags)) continue; - if(strpos($key, "UndefinedTag:") === 0) continue; - $value = preg_replace( '/[^[:print:]]/', '',$value); - $filteredData[$section][$key] = SystemTextEncoding::toUTF8($value); - } - } - - if($format == "xml"){ - - AJXP_XMLWriter::header("metadata", array("file" => $selectedNode->getPath(), "type" => "EXIF")); - foreach ($filteredData as $section => $data) { - print(""); - foreach ($data as $key => $value) { - print("". AJXP_Utils::xmlEntities($value).""); - } - print(""); - } - AJXP_XMLWriter::close("metadata"); - - }else{ - - HTMLWriter::charsetHeader("application/json"); - echo json_encode($filteredData); - - } - - } - - public function string_format($str) - { - $tmpStr = ""; - - for ($i=0;$igetUrl(); - if(!$ajxpNode->isLeaf() || preg_match("/\.zip\//",$currentFile)) return ; - if(!preg_match("/\.jpg$|\.jpeg$|\.tif$|\.tiff$/i",$currentFile)) return ; - $definitions = $this->getMetaDefinition(); - if(!count($definitions)) return ; - if(!function_exists("exif_read_data")) return ; - //if(!exif_imagetype($currentFile)) return ; - $realFile = $ajxpNode->getRealFile(); - $exif = @exif_read_data($realFile, 0, TRUE); - $iptc = $this->extractIPTC($realFile); - - if($exif === false) return ; - $additionalMeta = array(); - foreach ($definitions as $def => $label) { - list($exifSection, $exifName) = explode("-", $def); - if ($exifSection == "COMPUTED_GPS" && !isSet($exif["COMPUTED_GPS"])) { - $exif['COMPUTED_GPS'] = $this->convertGPSData($exif); - } - if (isSet($exif[$exifSection]) && isSet($exif[$exifSection][$exifName])) { - $additionalMeta[$def] = $exif[$exifSection][$exifName]; - } - if ($exifSection == "IPTC" && isSet($iptc[$exifName])){ - $additionalMeta[$def] = $iptc[$exifName]; - } - } - $ajxpNode->mergeMetadata($additionalMeta); - } - - private function extractIPTC($realFile){ - $output = array(); - if(!function_exists("iptcparse")) { - return $output; - } - getimagesize($realFile,$info); - if(!isset($info['APP13'])) { - return $output; - } - $iptcHeaderArray = array - ( - '2#005'=>'DocumentTitle', - '2#010'=>'Urgency', - '2#015'=>'Category', - '2#020'=>'Subcategories', - '2#025'=>'Keywords', - '2#040'=>'SpecialInstructions', - '2#055'=>'CreationDate', - '2#080'=>'AuthorByline', - '2#085'=>'AuthorTitle', - '2#090'=>'City', - '2#095'=>'State', - '2#101'=>'Country', - '2#103'=>'OTR', - '2#105'=>'Headline', - '2#110'=>'Source', - '2#115'=>'PhotoSource', - '2#116'=>'Copyright', - '2#120'=>'Caption', - '2#122'=>'CaptionWriter' - ); - $iptc =iptcparse($info['APP13']); - if (!is_array($iptc)) { - return $output; - } - foreach (array_keys($iptc) as $key) { - if (isSet($iptcHeaderArray[$key])) { - $cnt = count($iptc[$key]); - $val = ""; - for ($i=0; $i < $cnt; $i++) $val .= $iptc[$key][$i] . " "; - $output[$iptcHeaderArray[$key]] = preg_replace( '/[^[:print:]]/', '',$val); - } - } - return $output; - } - - private function convertGPSData($exif) - { - if(!isSet($exif["GPS"])) return array(); - require_once(AJXP_INSTALL_PATH."/plugins/meta.exif/class.GeoConversion.php"); - $converter = new GeoConversion(); - $latDeg=@$this->parseGPSValue($exif["GPS"]["GPSLatitude"][0]); - $latMin=@$this->parseGPSValue($exif["GPS"]["GPSLatitude"][1]); - $latSec=@$this->parseGPSValue($exif["GPS"]["GPSLatitude"][2]); - $latHem=$exif["GPS"]["GPSLatitudeRef"]; - $longDeg=@$this->parseGPSValue($exif["GPS"]["GPSLongitude"][0]); - $longMin=@$this->parseGPSValue($exif["GPS"]["GPSLongitude"][1]); - $longSec=@$this->parseGPSValue($exif["GPS"]["GPSLongitude"][2]); - $longRef=$exif["GPS"]["GPSLongitudeRef"]; - $latSign = ($latHem == "S" ? "-":""); - $longSign = ($longRef == "W" ? "-":""); - $gpsData = array( - "GPS_Latitude"=>"$latDeg deg $latMin' $latSec $latHem--".$converter->DMS2Dd($latSign.$latDeg."o$latMin'$latSec"), - "GPS_Longitude"=>"$longDeg deg $longMin' $longSec $longRef--".$converter->DMS2Dd($longSign.$longDeg."o$longMin'$longSec"), - "GPS_Altitude"=> $exif["GPS"]["GPSAltitude"][0] - ); - return $gpsData; - } - - private function parseGPSValue($value) - { - if (strstr($value, "/") === false) { - return floatval($value); - } else { - $exp = explode("/", $value); - return round(intval($exp[0])/intval($exp[1]), 4); - } - } - -} diff --git a/core/src/plugins/meta.exif/class.GeoConversion.php b/core/src/plugins/meta.exif/class.GeoConversion.php deleted file mode 100644 index c1196739c0..0000000000 --- a/core/src/plugins/meta.exif/class.GeoConversion.php +++ /dev/null @@ -1,174 +0,0 @@ -DMS2Dd('4522\'38"') -> 45.3772 // -// // -// // -// // -// Considerations: // -// D = Degrees // -// M = Minutes // -// S = Seconds // -// .m = Decimal Minutes // -// .s = Decimal Seconds // -// // -// DM.m (DMm) = Degrees, Minutes, Decimal Minutes (eg. 45o22.6333) // -// D.d (Dd) = Degrees, Decimal Degrees (eg. 45.3772o) // -// DMS (DMS) = Degrees, Minutes, Seconds (eg. 45o22'38") // -//***********************************************************************// - -//**************************Descrio em Portugus*********************// -// Classe para converso de coordenadas de Latitude e Longitude // -// Desenvolvida por: Digo Garrido de Almeida // -// Localizao: Conselheiro Lafaiete - Minas Gerais / Brasil // -// Licena: Nenhuma, podendo ser alterada, sem necessidade de crditos // -// Utilizao Recomendada: Converso das Coordenadas do Google Earth // -// para a API do Google Maps para WEB, atravs // -// do Mtodo GeoConversao::DMS2Dd // -// ex: $GeoConversao->DMS2Dd('4522\'38"') -> 45.3772 // -// // -// Consideraes: // -// D = Degrees (Degrais) // -// M = Minutes (Minutos) // -// S = Seconds (Segundos) // -// .m = Decimal Minutes (Dcimos de Minuto) // -// .s = Decimal Seconds (Dcimos de Segundo) // -// // -// DM.m (DMm) = Degrees, Minutes, Decimal Minutes (ex. 45o22.6333) // -// D.d (Dd) = Degrees, Decimal Degrees (ex. 45.3772o) // -// DMS (DMS) = Degrees, Minutes, Seconds (ex. 45o22'38") // -//*********************************************************************// - - -Class GeoConversion{ - - public $negative = FALSE; - public $real = FALSE; - public $negative_path = ''; - - private function is_negative(&$string) - { - if ($string[0] == '-') { - $this->negative = TRUE; - $string = str_replace('-','',$string); - $this->negative_path = '-'; - } - $real = TRUE; - } - - private function replace_special_chars(&$string,$decimal) - { - for ($I = 0 ; $I < strlen($string) ; $I++) { - $not_decimal = $decimal == FALSE ? ($string[$I] != '.') : TRUE; - if (!is_numeric($string[$I]) && $not_decimal && $string[$I] != ' ') { - $string[$I] = ';'; - } else if ($string[$I] == ' ') { - $string[$I] = ''; - } - } - } - - private function SepDMS($DMS) - { - $this->replace_special_chars($DMS,FALSE); - $dados = explode(';',$DMS); - return array('D' => $dados[0],'M' => $dados[1],'S' => $dados[2]); - } - - private function SepDMm($DMm) - { - $this->replace_special_chars($DMm,TRUE); - $dados = explode(';',$DMm); - return array('D' => $dados[0],'M' => $dados[1],'m' => $dados[2]); - } - - private function SepDd($Dd) - { - $this->replace_special_chars($Dd,TRUE); - $dados = explode(';',$Dd); - return array('D' => $dados[0],'d' => $dados[1]); - } - - public function DMS2DMm($DMS) - { - $this->is_negative($DMS); - - $array_DMm = array('D' => '','M' => '','m' => ''); - $array_DMS = $this->SepDMS($DMS); - - $array_DMm['m'] = $array_DMS['S']/60; - $array_DMm['M'] = $array_DMS['M']; - $array_DMm['D'] = $array_DMS['D']; - - return $this->negative_path.$array_DMm['D'].''.($array_DMm['M'] + $array_DMm['m']); - } - - public function DMm2Dd($DMm) - { - $this->is_negative($DMm); - - $array_Dd = array('D' => '','d' => ''); - $array_DMm = $this->SepDMm($DMm); - - $array_Dd['d'] = ($array_DMm['M'].'.'.$array_DMm['m'])/60; - $array_Dd['D'] = $array_DMm['D']; - - return $this->negative_path.($array_Dd['D'] + $array_Dd['d']); - } - - public function DMS2Dd($DMS) - { - $this->is_negative($DMS); - - $DMm = $this->DMS2DMm($DMS); - return $this->DMm2Dd($DMm); - } - - public function DMm2DMS($DMm) - { - $this->is_negative($DMm); - - $array_DMS = array('D' => '', 'M' => '', 'S' => ''); - $array_DMm = $this->SepDMm($DMm); - - $str_S = ((0).".".$array_DMm['m']) * 60; - - $array_DMS['S'] = $str_S; - $array_DMS['M'] = $array_DMm['M']; - $array_DMS['D'] = $array_DMm['D']; - - return $array_DMS['D'].''.$array_DMS['M'].'\''.$array_DMS['S'].'"'; - } - - public function Dd2DMm($Dd) - { - $this->is_negative($Dd); - - $array_DMm = array('D' => '','M' => '','m' => ''); - $array_Dd = $this->SepDd($Dd); - - $str_Mm = ((0).".".$array_Dd['d']) * 60; - - $dados_Mm = explode(".",$str_Mm); - - $array_DMm['m'] = $dados_Mm[1]; - $array_DMm['M'] = $dados_Mm[0]; - $array_DMm['D'] = $array_Dd['D']; - - return $this->negative_path.$array_DMm['D']." ".$array_DMm['M'].".".$array_DMm['m']; - } - - public function Dd2DMS($Dd) - { - $this->is_negative($Dd); - - $DMm = $this->Dd2DMm($Dd); - return $this->DMm2DMS($DMm); - } -} diff --git a/core/src/plugins/meta.exif/i18n/conf/de.php b/core/src/plugins/meta.exif/i18n/conf/de.php index cefca2dd96..1c448496e7 100644 --- a/core/src/plugins/meta.exif/i18n/conf/de.php +++ b/core/src/plugins/meta.exif/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Exif Metadata" => "Exif-Metadaten", diff --git a/core/src/plugins/meta.exif/i18n/conf/en.php b/core/src/plugins/meta.exif/i18n/conf/en.php index 68b140e9f7..68fc9f8a3a 100644 --- a/core/src/plugins/meta.exif/i18n/conf/en.php +++ b/core/src/plugins/meta.exif/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Exif Metadata" => "Exif Metadata", diff --git a/core/src/plugins/meta.exif/i18n/conf/fr.php b/core/src/plugins/meta.exif/i18n/conf/fr.php index 41cadd3877..158ab79f63 100644 --- a/core/src/plugins/meta.exif/i18n/conf/fr.php +++ b/core/src/plugins/meta.exif/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Exif Metadata" => "Métadonnées EXIF", diff --git a/core/src/plugins/meta.exif/i18n/conf/it.php b/core/src/plugins/meta.exif/i18n/conf/it.php index 437069e2fa..ace0596af3 100644 --- a/core/src/plugins/meta.exif/i18n/conf/it.php +++ b/core/src/plugins/meta.exif/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Exif Metadata" => "Metadati Exif", diff --git a/core/src/plugins/meta.exif/i18n/conf/pt.php b/core/src/plugins/meta.exif/i18n/conf/pt.php index af4fb5e2b0..5e4c1f6105 100644 --- a/core/src/plugins/meta.exif/i18n/conf/pt.php +++ b/core/src/plugins/meta.exif/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Exif Metadata" => "Exif Metadata", diff --git a/core/src/plugins/meta.exif/i18n/fr.php b/core/src/plugins/meta.exif/i18n/fr.php index 740919fc1c..51dbc56ad1 100644 --- a/core/src/plugins/meta.exif/i18n/fr.php +++ b/core/src/plugins/meta.exif/i18n/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "1" => "Géolocalisation", diff --git a/core/src/plugins/meta.exif/i18n/si.php b/core/src/plugins/meta.exif/i18n/si.php index c03bf2b846..da5ec3c1b0 100644 --- a/core/src/plugins/meta.exif/i18n/si.php +++ b/core/src/plugins/meta.exif/i18n/si.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ // Slovenian translation: April 21 2011 by Vladimir Bohinc (vladimir.bohinc@gmail.com) // diff --git a/core/src/plugins/meta.exif/manifest.xml b/core/src/plugins/meta.exif/manifest.xml index 8bcd537746..c8df599dcf 100644 --- a/core/src/plugins/meta.exif/manifest.xml +++ b/core/src/plugins/meta.exif/manifest.xml @@ -1,6 +1,6 @@ - + @@ -36,6 +36,6 @@
      - + diff --git a/core/src/plugins/meta.filehasher/FileHasher.php b/core/src/plugins/meta.filehasher/FileHasher.php new file mode 100644 index 0000000000..e1ab61edfd --- /dev/null +++ b/core/src/plugins/meta.filehasher/FileHasher.php @@ -0,0 +1,340 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Meta\Hash; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Access\Core\MetaStreamWrapper; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Model\UserSelection; +use Pydio\Access\Meta\Core\IFileHasher; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\LocalCache; +use Pydio\Core\Controller\Controller; +use Pydio\Core\Services\ApplicationState; + +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Access\Meta\Core\AbstractMetaSource; +use Pydio\Access\Metastore\Core\IMetaStoreProvider; +use Zend\Diactoros\Response\JsonResponse; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Generates and caches and md5 hash of each file + * @package Pydio\Access\Meta\Hash + */ +class FileHasher extends AbstractMetaSource implements IFileHasher +{ + const METADATA_HASH_NAMESPACE = "file_hahser"; + /** + * @var IMetaStoreProvider + */ + protected $metaStore; + + /** + * @return bool + */ + public static function rsyncEnabled() + { + return function_exists("rsync_generate_signature"); + } + + /** + * @return array + */ + public function getConfigs() + { + $data = parent::getConfigs(); + $this->filterData($data); + return $data; + } + + /** + * @param array $data + */ + public function loadConfigs($data) + { + $this->filterData($data); + parent::loadConfigs($data); + + } + + /** + * @param $data + */ + private function filterData(&$data) + { + $data["RSYNC_SUPPORTED"] = self::rsyncEnabled(); + } + + + /** + * @param ContextInterface $ctx + * @param \DOMNode $contribNode + */ + public function parseSpecificContributions(ContextInterface $ctx, \DOMNode &$contribNode) + { + parent::parseSpecificContributions($ctx, $contribNode); + if (!self::rsyncEnabled() && $contribNode->nodeName == "actions") { + // REMOVE rsync actions, this will advertise the fact that + // rsync is not enabled. + $xp = new \DOMXPath($contribNode->ownerDocument); + $children = $xp->query("action[contains(@name, 'filehasher')]", $contribNode); + foreach ($children as $child) { + $contribNode->removeChild($child); + } + } + if ($this->getContextualOption($ctx, "CACHE_XML_TREE") !== true && $contribNode->nodeName == "actions") { + // REMOVE pre and post process on LS action + $xp = new \DOMXPath($contribNode->ownerDocument); + $children = $xp->query("action[@name='ls']", $contribNode); + foreach ($children as $child) { + $contribNode->removeChild($child); + } + } + } + + /** + * @param ContextInterface $ctx + * @param AbstractAccessDriver $accessDriver + * @throws PydioException + */ + public function initMeta(ContextInterface $ctx, AbstractAccessDriver $accessDriver) + { + parent::initMeta($ctx, $accessDriver); + $store = PluginsService::getInstance($ctx)->getUniqueActivePluginForType("metastore"); + if ($store === false) { + throw new PydioException("The 'meta.simple_lock' plugin requires at least one active 'metastore' plugin"); + } + $this->metaStore = $store; + $this->metaStore->initMeta($ctx, $accessDriver); + } + + /** + * Handle stat_hash action + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + */ + public function statAction(ServerRequestInterface $requestInterface, ResponseInterface &$responseInterface){ + + $selection = UserSelection::fromContext($requestInterface->getAttribute("ctx"), $requestInterface->getParsedBody()); + + clearstatcache(); + if ($selection->isUnique()) { + $node = $selection->getUniqueNode(); + $stat = @stat($node->getUrl()); + if (!$stat || !is_readable($node->getUrl())) { + $responseData = new \stdClass(); + } else { + if(is_file($node->getUrl())) { + $serverParams = $requestInterface->getServerParams(); + if(isSet($serverParams["HTTP_RANGE"])){ + $fullSize = floatval($stat['size']); + $ranges = explode('=', $serverParams["HTTP_RANGE"]); + $offsets = explode('-', $ranges[1]); + $offset = floatval($offsets[0]); + $length = floatval($offsets[1]) - $offset; + if (!$length) $length = $fullSize - $offset; + if ($length + $offset > $fullSize || $length < 0) $length = $fullSize - $offset; + $hash = $this->getPartialHash($node, $offset, $length); + }else{ + $selection->getUniqueNode()->loadNodeInfo(true); + $hash = $this->getFileHash($selection->getUniqueNode()); + } + } + else $hash = 'directory'; + $stat[13] = $stat["hash"] = $hash; + $responseData = $stat; + } + } else { + $files = $selection->getFiles(); + $responseData = []; + foreach ($files as $index => $path) { + $node = new AJXP_Node($selection->currentBaseUrl().$path); + $stat = @stat($selection->currentBaseUrl().$path); + if(!$stat || !is_readable($node->getUrl())) { + $stat = new \stdClass(); + } else { + if(!is_dir($node->getUrl())) { + $node->loadNodeInfo(true); + $hash = $this->getFileHash($node); + } else { + $hash = 'directory'; + } + $stat[13] = $stat["hash"] = $hash; + } + $responseData[$path] = $stat; + } + + } + + $responseInterface = new JsonResponse($responseData); + + } + + /** + * @param $actionName + * @param $httpVars + * @param $fileVars + * @param ContextInterface $ctx + * @throws \Exception + */ + public function switchActions($actionName, $httpVars, $fileVars, ContextInterface $ctx) + { + $selection = UserSelection::fromContext($ctx, $httpVars); + + switch ($actionName) { + + case "filehasher_signature": + $file = $selection->getUniqueNode(); + if(!file_exists($file->getUrl())) break; + $cacheItem = LocalCache::getItem("signatures", $file->getUrl(), array($this, "generateSignature")); + $data = $cacheItem->getData(); + header("Content-Type:application/octet-stream"); + header("Content-Length", strlen($data)); + echo($data); + break; + + case "filehasher_delta": + case "filehasher_patch": + // HANDLE UPLOAD DATA + $this->logDebug("Received signature file, should compute delta now"); + if (!isSet($fileVars) && !is_array($fileVars["userfile_0"])) { + throw new \Exception("These action should find uploaded data"); + } + $signature_delta_file = $fileVars["userfile_0"]["tmp_name"]; + $fileUrl = $selection->getUniqueNode()->getUrl(); + $file = MetaStreamWrapper::getRealFSReference($fileUrl, true); + if ($actionName == "filehasher_delta") { + $deltaFile = tempnam(ApplicationState::getAjxpTmpDir(), $actionName."-delta"); + $this->logDebug("Received signature file, should compute delta now"); + \rsync_generate_delta($signature_delta_file, $file, $deltaFile); + $this->logDebug("Computed delta file, size is ".filesize($deltaFile)); + header("Content-Type:application/octet-stream"); + header("Content-Length:".filesize($deltaFile)); + readfile($deltaFile); + unlink($deltaFile); + } else { + $patched = $file.".rdiff_patched"; + \rsync_patch_file($file, $signature_delta_file, $patched); + rename($patched, $file); + $node = $selection->getUniqueNode(); + Controller::applyHook("node.change", array($node, $node, false)); + header("Content-Type:text/plain"); + echo md5_file($file); + } + break; + + } + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $node + * @return String md5 + */ + public function getFileHash(AJXP_Node $node) + { + // Make sure that node is really there + if ($node->isLeaf()) { + $md5 = null; + if ($this->metaStore != false) { + + $hashMeta = $this->metaStore->retrieveMetadata( + $node, + FileHasher::METADATA_HASH_NAMESPACE, + false, + AJXP_METADATA_SCOPE_GLOBAL); + $mtime = filemtime($node->getUrl()); + if(is_array($hashMeta) + && array_key_exists("md5", $hashMeta) + && array_key_exists("md5_mtime", $hashMeta) + && $hashMeta["md5_mtime"] >= $mtime){ + $md5 = $hashMeta["md5"]; + } + if ($md5 == null) { + $md5 = md5_file($node->getUrl()); + $hashMeta = array( + "md5" => $md5, + "md5_mtime" => $mtime + ); + $this->metaStore->setMetadata($node, FileHasher::METADATA_HASH_NAMESPACE, $hashMeta, false, AJXP_METADATA_SCOPE_GLOBAL); + } + + } else { + + $md5 = md5_file($node->getUrl()); + + } + $node->mergeMetadata(array("md5" => $md5)); + return $md5; + }else{ + return 'directory'; + } + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $node + * @param float $offset + * @param float $length + * @return String md5 + */ + public function getPartialHash($node, $offset, $length){ + + $this->logDebug('Getting partial hash from ' . $offset . ' to ' . $length ); + $fp = fopen($node->getUrl(), "r"); + $ctx = hash_init('md5'); + if($offset > 0){ + fseek($fp, $offset); + } + hash_update_stream($ctx, $fp, $length); + $hash = hash_final($ctx); + $this->logDebug('Partial hash is ' . $hash ); + fclose($fp); + return $hash; + + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $oldNode + * @param \Pydio\Access\Core\Model\AJXP_Node $newNode + * @param bool $copy + */ + public function invalidateHash($oldNode = null, $newNode = null, $copy = false) + { + if($this->metaStore == false) return; + if ($oldNode != null) { + $this->metaStore->removeMetadata($oldNode, FileHasher::METADATA_HASH_NAMESPACE, false, AJXP_METADATA_SCOPE_GLOBAL); + } + } + + + /** + * @param $masterFile + * @param $targetFile + */ + public function generateSignature($masterFile, $targetFile) + { + \rsync_generate_signature($masterFile, $targetFile); + } +} diff --git a/core/src/plugins/meta.filehasher/class.FileHasher.php b/core/src/plugins/meta.filehasher/class.FileHasher.php deleted file mode 100755 index a10b9842f1..0000000000 --- a/core/src/plugins/meta.filehasher/class.FileHasher.php +++ /dev/null @@ -1,318 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die('Access not allowed'); - -/** - * Generates and caches and md5 hash of each file - * @package AjaXplorer_Plugins - * @subpackage Meta - */ -class FileHasher extends AJXP_AbstractMetaSource -{ - const METADATA_HASH_NAMESPACE = "file_hahser"; - /** - * @var MetaStoreProvider - */ - protected $metaStore; - - public static function rsyncEnabled() - { - return function_exists("rsync_generate_signature"); - } - - public function getConfigs() - { - $data = parent::getConfigs(); - $this->filterData($data); - return $data; - } - public function loadConfigs($data) - { - $this->filterData($data); - parent::loadConfigs($data); - - } - - private function filterData(&$data) - { - $data["RSYNC_SUPPORTED"] = self::rsyncEnabled(); - } - - - public function parseSpecificContributions(&$contribNode) - { - parent::parseSpecificContributions($contribNode); - if (!self::rsyncEnabled() && $contribNode->nodeName == "actions") { - // REMOVE rsync actions, this will advertise the fact that - // rsync is not enabled. - $xp = new DOMXPath($contribNode->ownerDocument); - $children = $xp->query("action[contains(@name, 'filehasher')]", $contribNode); - foreach ($children as $child) { - $contribNode->removeChild($child); - } - } - if ($this->getFilteredOption("CACHE_XML_TREE") !== true && $contribNode->nodeName == "actions") { - // REMOVE pre and post process on LS action - $xp = new DOMXPath($contribNode->ownerDocument); - $children = $xp->query("action[@name='ls']", $contribNode); - foreach ($children as $child) { - $contribNode->removeChild($child); - } - } - } - - public function initMeta($accessDriver) - { - parent::initMeta($accessDriver); - $store = AJXP_PluginsService::getInstance()->getUniqueActivePluginForType("metastore"); - if ($store === false) { - throw new Exception("The 'meta.simple_lock' plugin requires at least one active 'metastore' plugin"); - } - $this->metaStore = $store; - $this->metaStore->initMeta($accessDriver); - } - - private function getTreeName() - { - $repo = $this->accessDriver->repository; - $base = AJXP_SHARED_CACHE_DIR."/trees/tree-".$repo->getId(); - $secuScope = $repo->securityScope(); - if ($secuScope == "USER") { - $base .= "-".AuthService::getLoggedUser()->getId(); - } else if ($secuScope == "GROUP") { - $base .= "-".str_replace("/", "_", AuthService::getLoggedUser()->getGroupPath()); - } - return $base . "-full.xml"; - } - - public function checkFullTreeCache($actionName, &$httpVars, &$fileVars) - { - $cName = $this->getTreeName(); - if (is_file($cName)) { - header('Content-Type: text/xml; charset=UTF-8'); - header('Cache-Control: no-cache'); - if ( strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') ) { - header('Content-Encoding:deflate'); - readfile($cName.".gz"); - } else { - readfile($cName); - } - exit(); - } - } - - public function cacheFullTree($actionName, $httpVars, $postProcessData) - { - $cName = $this->getTreeName(); - if(!is_dir(dirname($cName))) mkdir(dirname($cName)); - $xmlString = $postProcessData["ob_output"]; - file_put_contents($cName, $xmlString); - file_put_contents($cName.".gz", gzdeflate($xmlString, 9)); - print($xmlString); - } - - public function switchActions($actionName, $httpVars, $fileVars) - { - //$urlBase = $this->accessDriver - $repository = $this->accessDriver->repository; - if (!$repository->detectStreamWrapper(true)) { - return false; - } - $selection = new UserSelection($repository, $httpVars); - switch ($actionName) { - case "filehasher_signature": - $file = $selection->getUniqueNode(); - if(!file_exists($file->getUrl())) break; - $cacheItem = AJXP_Cache::getItem("signatures", $file->getUrl(), array($this, "generateSignature")); - $data = $cacheItem->getData(); - header("Content-Type:application/octet-stream"); - header("Content-Length", strlen($data)); - echo($data); - break; - case "filehasher_delta": - case "filehasher_patch": - // HANDLE UPLOAD DATA - $this->logDebug("Received signature file, should compute delta now"); - if (!isSet($fileVars) && !is_array($fileVars["userfile_0"])) { - throw new Exception("These action should find uploaded data"); - } - $signature_delta_file = $fileVars["userfile_0"]["tmp_name"]; - $fileUrl = $selection->getUniqueNode()->getUrl(); - $file = AJXP_MetaStreamWrapper::getRealFSReference($fileUrl, true); - if ($actionName == "filehasher_delta") { - $deltaFile = tempnam(AJXP_Utils::getAjxpTmpDir(), $actionName."-delta"); - $this->logDebug("Received signature file, should compute delta now"); - rsync_generate_delta($signature_delta_file, $file, $deltaFile); - $this->logDebug("Computed delta file, size is ".filesize($deltaFile)); - header("Content-Type:application/octet-stream"); - header("Content-Length:".filesize($deltaFile)); - readfile($deltaFile); - unlink($deltaFile); - } else { - $patched = $file.".rdiff_patched"; - rsync_patch_file($file, $signature_delta_file, $patched); - rename($patched, $file); - $node = $selection->getUniqueNode(); - AJXP_Controller::applyHook("node.change", array($node, $node, false)); - header("Content-Type:text/plain"); - echo md5_file($file); - } - break; - case "stat_hash" : - clearstatcache(); - header("Content-type:application/json"); - if ($selection->isUnique()) { - $node = $selection->getUniqueNode(); - $stat = @stat($node->getUrl()); - if (!$stat || !is_readable($node->getUrl())) { - print '{}'; - } else { - if(is_file($node->getUrl())) { - if(isSet($_SERVER["HTTP_RANGE"])){ - $fullSize = floatval($stat['size']); - $ranges = explode('=', $_SERVER["HTTP_RANGE"]); - $offsets = explode('-', $ranges[1]); - $offset = floatval($offsets[0]); - $length = floatval($offsets[1]) - $offset; - if (!$length) $length = $fullSize - $offset; - if ($length + $offset > $fullSize || $length < 0) $length = $fullSize - $offset; - $hash = $this->getPartialHash($node, $offset, $length); - }else{ - $hash = $this->getFileHash($selection->getUniqueNode()); - } - } - else $hash = 'directory'; - $stat[13] = $stat["hash"] = $hash; - print json_encode($stat); - } - } else { - $files = $selection->getFiles(); - print '{'; - foreach ($files as $index => $path) { - $node = new AJXP_Node($selection->currentBaseUrl().$path); - $stat = @stat($selection->currentBaseUrl().$path); - if(!$stat || !is_readable($node->getUrl())) $stat = '{}'; - else { - if(!is_dir($node->getUrl())) $hash = $this->getFileHash($node); - else $hash = 'directory'; - $stat[13] = $stat["hash"] = $hash; - $stat = json_encode($stat); - } - print json_encode(SystemTextEncoding::toUTF8($path)).':'.$stat . (($index < count($files) -1) ? "," : ""); - } - print '}'; - } - - break; - - - break; - } - } - - /** - * @param AJXP_Node $node - * @return String md5 - */ - public function getFileHash($node) - { - if ($node->isLeaf()) { - $md5 = null; - if ($this->metaStore != false) { - - $hashMeta = $this->metaStore->retrieveMetadata( - $node, - FileHasher::METADATA_HASH_NAMESPACE, - false, - AJXP_METADATA_SCOPE_GLOBAL); - $mtime = filemtime($node->getUrl()); - if(is_array($hashMeta) - && array_key_exists("md5", $hashMeta) - && array_key_exists("md5_mtime", $hashMeta) - && $hashMeta["md5_mtime"] >= $mtime){ - $md5 = $hashMeta["md5"]; - } - if ($md5 == null) { - $md5 = md5_file($node->getUrl()); - $hashMeta = array( - "md5" => $md5, - "md5_mtime" => $mtime - ); - $this->metaStore->setMetadata($node, FileHasher::METADATA_HASH_NAMESPACE, $hashMeta, false, AJXP_METADATA_SCOPE_GLOBAL); - } - - } else { - - $md5 = md5_file($node->getUrl()); - - } - $node->mergeMetadata(array("md5" => $md5)); - return $md5; - }else{ - return 'directory'; - } - } - - /** - * @param AJXP_Node $node - * @param float $offset - * @param float $length - * @return String md5 - */ - public function getPartialHash($node, $offset, $length){ - - $this->logDebug('Getting partial hash from ' . $offset . ' to ' . $length ); - $fp = fopen($node->getUrl(), "r"); - $ctx = hash_init('md5'); - if($offset > 0){ - fseek($fp, $offset); - } - hash_update_stream($ctx, $fp, $length); - $hash = hash_final($ctx); - $this->logDebug('Partial hash is ' . $hash ); - fclose($fp); - return $hash; - - } - - /** - * @param AJXP_Node $oldNode - * @param AJXP_Node $newNode - * @param bool $copy - */ - public function invalidateHash($oldNode = null, $newNode = null, $copy = false) - { - if($this->metaStore == false) return; - if ($oldNode != null) { - $this->metaStore->removeMetadata($oldNode, FileHasher::METADATA_HASH_NAMESPACE, false, AJXP_METADATA_SCOPE_GLOBAL); - } - if ($this->getFilteredOption("CACHE_XML_TREE") === true && is_file($this->getTreeName())) { - @unlink($this->getTreeName()); - } - } - - - public function generateSignature($masterFile, $targetFile) - { - rsync_generate_signature($masterFile, $targetFile); - } -} diff --git a/core/src/plugins/meta.filehasher/i18n/conf/de.php b/core/src/plugins/meta.filehasher/i18n/conf/de.php index 349c4031fe..2c5852ffb8 100644 --- a/core/src/plugins/meta.filehasher/i18n/conf/de.php +++ b/core/src/plugins/meta.filehasher/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "File Hasher" => "Datei-Hashes", diff --git a/core/src/plugins/meta.filehasher/i18n/conf/en.php b/core/src/plugins/meta.filehasher/i18n/conf/en.php index 8ce90ab297..a8eca1ce77 100644 --- a/core/src/plugins/meta.filehasher/i18n/conf/en.php +++ b/core/src/plugins/meta.filehasher/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "File Hasher" => "File Hasher", diff --git a/core/src/plugins/meta.filehasher/i18n/conf/fr.php b/core/src/plugins/meta.filehasher/i18n/conf/fr.php index 8a13e99f0b..065f5335dd 100644 --- a/core/src/plugins/meta.filehasher/i18n/conf/fr.php +++ b/core/src/plugins/meta.filehasher/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "File Hasher" => "Empreinte de fichier", @@ -25,4 +25,4 @@ "Cache full tree" => "Cacher l'arbre", "Cache XML tree and invalidate on node change" => "Cacher l'arbre XML et l'invalider lors d'un changement de noeud.", "Dont edit, it will be detected by the server" => "Ne pas éditer, sera détecter par le serveur.", -); \ No newline at end of file +); diff --git a/core/src/plugins/meta.filehasher/i18n/conf/it.php b/core/src/plugins/meta.filehasher/i18n/conf/it.php index fd3e822142..0e6af6c3c4 100644 --- a/core/src/plugins/meta.filehasher/i18n/conf/it.php +++ b/core/src/plugins/meta.filehasher/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "File Hasher" => "Hasher File", @@ -25,4 +25,4 @@ "Cache full tree" => "Cache full tree", "Cache XML tree and invalidate on node change" => "Cache XML tree and invalidate on node change", "Dont edit, it will be detected by the server" => "Don't edit, it will be detected by the server", -); \ No newline at end of file +); diff --git a/core/src/plugins/meta.filehasher/i18n/conf/pt.php b/core/src/plugins/meta.filehasher/i18n/conf/pt.php index 0effa8835e..95f464fe53 100644 --- a/core/src/plugins/meta.filehasher/i18n/conf/pt.php +++ b/core/src/plugins/meta.filehasher/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "File Hasher" => "Obter Hash de Ficheiro", @@ -25,4 +25,4 @@ "Cache full tree" => "Cache full tree", "Cache XML tree and invalidate on node change" => "Cache XML tree and invalidate on node change", "Dont edit, it will be detected by the server" => "Don't edit, it will be detected by the server", -); \ No newline at end of file +); diff --git a/core/src/plugins/meta.filehasher/manifest.xml b/core/src/plugins/meta.filehasher/manifest.xml index a921c392ea..7f96ce4d18 100644 --- a/core/src/plugins/meta.filehasher/manifest.xml +++ b/core/src/plugins/meta.filehasher/manifest.xml @@ -1,9 +1,8 @@ - + - @@ -40,20 +39,12 @@ - + - - - - - - - - diff --git a/core/src/plugins/meta.git/GitManager.php b/core/src/plugins/meta.git/GitManager.php new file mode 100644 index 0000000000..a6991ee894 --- /dev/null +++ b/core/src/plugins/meta.git/GitManager.php @@ -0,0 +1,361 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Meta\Version; + +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Services\ApplicationState; +use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Core\Utils\Vars\StatHelper; + +use Pydio\Core\Controller\HTMLWriter; +use Pydio\Access\Meta\Core\AbstractMetaSource; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Manage versioning using Git + * @package Pydio\Access\Meta\Version + */ +class GitManager extends AbstractMetaSource +{ + + private $repoBase; + + public function performChecks() + { + $ex = ApplicationState::searchIncludePath("VersionControl/Git.php"); + if (!$ex) { + throw new \Exception("Cannot find PEAR library VersionControl/Git"); + } + } + + /** + * @param ContextInterface $ctx + * @param AbstractAccessDriver $accessDriver + * @throws \Exception + */ + public function initMeta(ContextInterface $ctx, AbstractAccessDriver $accessDriver) + { + parent::initMeta($ctx, $accessDriver); + require_once("VersionControl/Git.php"); + $repo = $ctx->getRepository(); + $this->repoBase = $repo->getContextOption($ctx, "PATH"); + if(empty($this->repoBase)){ + throw new \Exception("Meta.git: cannot find PATH option in repository! Are you sure it's an FS-based workspace?"); + } + if (!is_dir($this->repoBase.DIRECTORY_SEPARATOR.".git")) { + $git = new \VersionControl_Git($this->repoBase); + $git->initRepository(); + } + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $requestInterface + * @param \Psr\Http\Message\ResponseInterface $responseInterface + */ + public function applyActions(\Psr\Http\Message\ServerRequestInterface $requestInterface, \Psr\Http\Message\ResponseInterface &$responseInterface) + { + + $actionName = $requestInterface->getAttribute("action"); + $ctx = $requestInterface->getAttribute("ctx"); + $httpVars = $requestInterface->getParsedBody(); + $x = new \Pydio\Core\Http\Response\SerializableResponseStream(); + $responseInterface = $responseInterface->withBody($x); + $userSelection = \Pydio\Access\Core\Model\UserSelection::fromContext($ctx, $httpVars); + + $git = new \VersionControl_Git($this->repoBase); + switch ($actionName) { + case "git_history": + $nodesList = new \Pydio\Access\Core\Model\NodesList(); + $selectedNode = $userSelection->getUniqueNode(); + $file = ltrim($selectedNode->getPath(), "/"); + $res = $this->gitHistory($git, $file); + $ic = StatHelper::getMimeInfo($selectedNode, false)[1]; + $index = count($res); + $mess = LocaleService::getMessages(); + foreach ($res as &$commit) { + unset($commit["DETAILS"]); + $commit["icon"] = $ic; + $commit["index"] = $index; + $commit["EVENT"] = $mess["meta.git.".$commit["EVENT"]]; + $commit["text"] = basename($commit["FILE"]); + $index --; + $n = new AJXP_Node("/".$commit["ID"], $commit); + $n->setLeaf(true); + $nodesList->addBranch($n); + } + $x->addChunk($nodesList); + break; + break; + + case "git_revertfile": + + $originalFile = InputFilter::decodeSecureMagic($httpVars["original_file"]); + $file = InputFilter::decodeSecureMagic($httpVars["file"]); + $commitId = $httpVars["commit_id"]; + + $command = $git->getCommand("cat-file"); + $command->setOption("s", true); + $command->addArgument($commitId.":".$file); + $command->execute(); + + $command = $git->getCommand("show"); + $command->addArgument($commitId.":".$file); + $commandLine = $command->createCommandString(); + $outputStream = fopen($this->repoBase.$originalFile, "w"); + $this->executeCommandInStreams($git, $commandLine, $outputStream); + fclose($outputStream); + /** @var ContextInterface $ctx */ + $ctx = $requestInterface->getAttribute("ctx"); + $this->commitChanges($ctx); + $diff = new \Pydio\Access\Core\Model\NodesDiff(); + $diff->update(new AJXP_Node($ctx->getUrlBase().$file)); + $x->addChunk($diff); + + + break; + + case "git_getfile": + + $file = InputFilter::decodeSecureMagic($httpVars["file"]); + $commitId = $httpVars["commit_id"]; + $attach = $httpVars["attach"]; + + $command = $git->getCommand("cat-file"); + $command->setOption("s", true); + $command->addArgument($commitId.":".$file); + $size = $command->execute(); + + $command = $git->getCommand("show"); + $command->addArgument($commitId.":".$file); + $commandLine = $command->createCommandString(); + + if ($attach == "inline") { + $fileExt = substr(strrchr(basename($file), '.'), 1); + if (empty($fileExt)) { + $fileMime = "application/octet-stream"; + } else { + $regex = "/^([\w\+\-\.\/]+)\s+(\w+\s)*($fileExt\s)/i"; + $lines = file( AJXP_INSTALL_PATH."/".AJXP_PLUGINS_FOLDER."/editor.browser/resources/other/mime.types"); + foreach ($lines as $line) { + if(substr($line, 0, 1) == '#') + continue; // skip comments + $line = rtrim($line) . " "; + if(!preg_match($regex, $line, $matches)) + continue; // no match to the extension + $fileMime = $matches[1]; + } + } + if(empty($fileMime)) $fileMime = "application/octet-stream"; + $responseInterface = HTMLWriter::responseWithInlineHeaders($responseInterface, basename($file), $size, $fileMime); + } else { + $responseInterface = HTMLWriter::responseWithAttachmentsHeaders($responseInterface, basename($file), $size, false, false); + } + + + $reader = function() use ($git, $commandLine){ + $outputStream = fopen("php://output", "a"); + $this->executeCommandInStreams($git, $commandLine, $outputStream); + fclose($outputStream); + if(intval(ini_get("output_buffering")) > 0){ + ob_end_flush(); + } + }; + + $async = new \Pydio\Core\Http\Response\AsyncResponseStream($reader); + $responseInterface = $responseInterface->withBody($async); + + break; + + break; + + default: + break; + } + + + } + + /** + * @param \VersionControl_Git $git + * @param $commandLine + * @param $outputStream + * @param null $errorStream + * @return string + */ + protected function executeCommandInStreams($git, $commandLine, $outputStream, $errorStream = null) + { + $descriptorspec = array( + 1 => array('pipe', 'w'), + 2 => array('pipe', 'w'), + ); + $pipes = array(); + $resource = proc_open($commandLine, $descriptorspec, $pipes, realpath($git->getDirectory())); + + //$stdout = stream_get_contents($pipes[1]); + //$stderr = stream_get_contents($pipes[2]); + $bufLength = 4096; + while ( ($read = fread($pipes[1], $bufLength)) != false ) { + fputs($outputStream, $read, strlen($read)); + } + //stream_copy_to_stream($pipes[1], $outputStream); + if ($errorStream != null) { + stream_copy_to_stream($pipes[2], $errorStream); + } else { + $stderr = stream_get_contents($pipes[2]); + } + foreach ($pipes as $pipe) { + fclose($pipe); + } + + $status = trim(proc_close($resource)); + return $status; + + } + + /** + * @param \VersionControl_Git $git + * @param string $file + * @return array + */ + protected function gitHistory($git, $file) + { + $command = $git->getCommand("log"); + if(strpos($file, " ") === false){ + // We currently cannot use follow if file/folder has a space + $command->setOption("follow", true); + } + $command->setOption("p", true); + $command->addArgument($file); + //var_dump($command->createCommandString()); + $res = $command->execute(); + $lines = explode(PHP_EOL, $res); + $allCommits = array(); + $grabOtherLines = $grabMessageLines = false; + while (count($lines)) { + $line = array_shift($lines); + if (preg_match("/^commit /i", $line)) { + if (isSet($currentCommit)) { + if (isSet($currentCommit["DETAILS"])) { + $currentCommit["DETAILS"] = implode(PHP_EOL, $currentCommit["DETAILS"]); + } + $allCommits[] = $currentCommit; + } + $currentCommit = array(); + $currentCommit["ID"] = substr($line, strlen("commit ")); + $grabMessageLines = false; + $grabOtherLines = false; + } else if (preg_match("/^diff --git a\/(.*) b\/(.*)/i", $line, $matches)) { + $origA = $matches[1]; + $origB = $matches[2]; + $currentCommit["FILE"] = $origB; + if ($origB != $origA) { + if (basename($origB) != basename($origA)) { + $currentCommit["EVENT"] = "RENAME"; + } else if (dirname($origA) != dirname($origB)) { + $currentCommit["EVENT"] = "MOVE"; + } + } else { + $currentCommit["EVENT"] = "MODIFICATION"; + $currentCommit["DETAILS"] = array(); + $grabOtherLines = true; + } + } else if (preg_match("/^Date: /", $line)) { + $currentCommit["DATE"] = trim(substr($line, strlen("Date: "))); + $currentCommit["ajxp_modiftime"] = strtotime(substr($line, strlen("Date: "))); + } else if ($grabOtherLines) { + if(isSet($currentCommit) && count($currentCommit["DETAILS"]) >= 10) continue; + $currentCommit["DETAILS"][] = $line; + } else if (trim($line) == "") { + $grabMessageLines = !$grabMessageLines; + } else if ($grabMessageLines) { + if(!isSet($currentCommit["MESSAGE"])) $currentCommit["MESSAGE"] = ""; + $currentCommit["MESSAGE"] .= trim($line); + } + } + // $currentCommit + if(isSet($currentCommit)){ + if (count($currentCommit["DETAILS"]) && substr($currentCommit["DETAILS"][0], 0, strlen("new file")) == "new file") { + $currentCommit["EVENT"] = "CREATION"; + unset($currentCommit["DETAILS"]); + } + $allCommits[] = $currentCommit; + } + return $allCommits; + } + + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $fromNode + * @param \Pydio\Access\Core\Model\AJXP_Node$toNode + * @param boolean $copy + */ + public function changesHook($fromNode=null, $toNode=null, $copy=false) + { + $refNode = ($fromNode !== null ? $fromNode : $toNode); + $this->commitChanges($refNode->getContext()); + return; + } + + /** + * @param ContextInterface $ctx + * @param string $path + */ + private function commitChanges(ContextInterface $ctx, $path = null) + { + $git = new \VersionControl_Git($this->repoBase); + $command = $git->getCommand("add"); + $command->addArgument("."); + try { + $cmd = $command->createCommandString(); + $this->logDebug("Git command ".$cmd); + $res = $command->execute(); + $this->logDebug("GIT RESULT ADD : ".$res); + } catch (\Exception $e) { + $this->logDebug("Error in GIT Command ".$e->getMessage()); + } + + $command = $git->getCommand("commit"); + $command->setOption("a", true); + $userId = "no user"; + $mail = "mail@mail.com"; + if ($ctx->hasUser()) { + $userId = $ctx->getUser()->getId(); + $mail = $ctx->getUser()->getPersonalRole()->filterParameterValue("core.conf", "email", AJXP_REPO_SCOPE_ALL, "mail@mail.com"); + } + $command->setOption("m", $userId); + $command->setOption("author", "$userId <$mail>"); + //$command->addArgument($path); + + try { + $cmd = $command->createCommandString(); + $this->logDebug("Git command ".$cmd); + $res = $command->execute(); + $this->logDebug("GIT RESULT COMMIT : ".$res); + } catch (\Exception $e) { + $this->logDebug("Error ".$e->getMessage()); + } + } + +} diff --git a/core/src/plugins/meta.git/class.GitManager.php b/core/src/plugins/meta.git/class.GitManager.php deleted file mode 100644 index 5d82da7541..0000000000 --- a/core/src/plugins/meta.git/class.GitManager.php +++ /dev/null @@ -1,311 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die('Access not allowed'); - -/** - * Manage versioning using Git - * @package AjaXplorer_Plugins - * @subpackage Meta - */ -class GitManager extends AJXP_AbstractMetaSource -{ - - private $repoBase; - - public function performChecks() - { - $ex = AJXP_Utils::searchIncludePath("VersionControl/Git.php"); - if (!$ex) { - throw new Exception("Cannot find PEAR library VersionControl/Git"); - } - } - - /** - * @param AbstractAccessDriver $accessDriver - * @throws Exception - */ - public function initMeta($accessDriver) - { - parent::initMeta($accessDriver); - require_once("VersionControl/Git.php"); - $repo = $accessDriver->repository; - $this->repoBase = $repo->getOption("PATH"); - if(empty($this->repoBase)){ - throw new Exception("Meta.git: cannot find PATH option in repository! Are you sure it's an FS-based workspace?"); - } - if (!is_dir($this->repoBase.DIRECTORY_SEPARATOR.".git")) { - $git = new VersionControl_Git($this->repoBase); - $git->initRepository(); - } - } - - public function applyActions($actionName, $httpVars, $fileVars) - { - $git = new VersionControl_Git($this->repoBase); - switch ($actionName) { - case "git_history": - $file = AJXP_Utils::decodeSecureMagic($httpVars["file"]); - $file = ltrim($file, "/"); - $res = $this->gitHistory($git, $file); - AJXP_XMLWriter::header(); - $ic = AJXP_Utils::mimetype($file, "image", false); - $index = count($res); - $mess = ConfService::getMessages(); - foreach ($res as &$commit) { - unset($commit["DETAILS"]); - $commit["icon"] = $ic; - $commit["index"] = $index; - $commit["EVENT"] = $mess["meta.git.".$commit["EVENT"]]; - $index --; - AJXP_XMLWriter::renderNode("/".$commit["ID"], basename($commit["FILE"]), true, $commit); - } - AJXP_XMLWriter::close(); - break; - break; - - case "git_revertfile": - - $originalFile = AJXP_Utils::decodeSecureMagic($httpVars["original_file"]); - $file = AJXP_Utils::decodeSecureMagic($httpVars["file"]); - $commitId = $httpVars["commit_id"]; - - $command = $git->getCommand("cat-file"); - $command->setOption("s", true); - $command->addArgument($commitId.":".$file); - $size = $command->execute(); - - $command = $git->getCommand("show"); - $command->addArgument($commitId.":".$file); - $commandLine = $command->createCommandString(); - $outputStream = fopen($this->repoBase.$originalFile, "w"); - $this->executeCommandInStreams($git, $commandLine, $outputStream); - fclose($outputStream); - $this->commitChanges(); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::reloadDataNode(); - AJXP_XMLWriter::close(); - - - break; - - case "git_getfile": - - $file = AJXP_Utils::decodeSecureMagic($httpVars["file"]); - $commitId = $httpVars["commit_id"]; - $attach = $httpVars["attach"]; - - $command = $git->getCommand("cat-file"); - $command->setOption("s", true); - $command->addArgument($commitId.":".$file); - $size = $command->execute(); - - $command = $git->getCommand("show"); - $command->addArgument($commitId.":".$file); - $commandLine = $command->createCommandString(); - - if ($attach == "inline") { - $fileExt = substr(strrchr(basename($file), '.'), 1); - if (empty($fileExt)) { - $fileMime = "application/octet-stream"; - } else { - $regex = "/^([\w\+\-\.\/]+)\s+(\w+\s)*($fileExt\s)/i"; - $lines = file( AJXP_INSTALL_PATH."/".AJXP_PLUGINS_FOLDER."/editor.browser/resources/other/mime.types"); - foreach ($lines as $line) { - if(substr($line, 0, 1) == '#') - continue; // skip comments - $line = rtrim($line) . " "; - if(!preg_match($regex, $line, $matches)) - continue; // no match to the extension - $fileMime = $matches[1]; - } - } - if(empty($fileMime)) $fileMime = "application/octet-stream"; - HTMLWriter::generateInlineHeaders(basename($file), $size, $fileMime); - } else { - HTMLWriter::generateAttachmentsHeader(basename($file), $size, false, false); - } - $outputStream = fopen("php://output", "a"); - $this->executeCommandInStreams($git, $commandLine, $outputStream); - fclose($outputStream); - if(intval(ini_get("output_buffering")) > 0){ - ob_end_flush(); - } - break; - - break; - - default: - break; - } - - - } - - protected function executeCommandInStreams($git, $commandLine, $outputStream, $errorStream = null) - { - $descriptorspec = array( - 1 => array('pipe', 'w'), - 2 => array('pipe', 'w'), - ); - $pipes = array(); - $resource = proc_open($commandLine, $descriptorspec, $pipes, realpath($git->getDirectory())); - - //$stdout = stream_get_contents($pipes[1]); - //$stderr = stream_get_contents($pipes[2]); - $bufLength = 4096; - while ( ($read = fread($pipes[1], $bufLength)) != false ) { - fputs($outputStream, $read, strlen($read)); - } - //stream_copy_to_stream($pipes[1], $outputStream); - if ($errorStream != null) { - stream_copy_to_stream($pipes[2], $errorStream); - } else { - $stderr = stream_get_contents($pipes[2]); - } - foreach ($pipes as $pipe) { - fclose($pipe); - } - - $status = trim(proc_close($resource)); - return $status; - - } - - protected function gitHistory($git, $file) - { - $command = $git->getCommand("log"); - if(strpos($file, " ") === false){ - // We currently cannot use follow if file/folder has a space - $command->setOption("follow", true); - } - $command->setOption("p", true); - $command->addArgument($file); - //var_dump($command->createCommandString()); - $res = $command->execute(); - $lines = explode(PHP_EOL, $res); - $allCommits = array(); - while (count($lines)) { - $line = array_shift($lines); - if (preg_match("/^commit /i", $line)) { - if (isSet($currentCommit)) { - if (isSet($currentCommit["DETAILS"])) { - $currentCommit["DETAILS"] = implode(PHP_EOL, $currentCommit["DETAILS"]); - } - $allCommits[] = $currentCommit; - } - $currentCommit = array(); - $currentCommit["ID"] = substr($line, strlen("commit ")); - $grabMessageLines = false; - $grabOtherLines = false; - } else if (preg_match("/^diff --git a\/(.*) b\/(.*)/i", $line, $matches)) { - $origA = $matches[1]; - $origB = $matches[2]; - $currentCommit["FILE"] = $origB; - if ($origB != $origA) { - if (basename($origB) != basename($origA)) { - $currentCommit["EVENT"] = "RENAME"; - } else if (dirname($origA) != dirname($origB)) { - $currentCommit["EVENT"] = "MOVE"; - } - } else { - $currentCommit["EVENT"] = "MODIFICATION"; - $currentCommit["DETAILS"] = array(); - $grabOtherLines = true; - } - } else if (preg_match("/^Date: /", $line)) { - $currentCommit["DATE"] = trim(substr($line, strlen("Date: "))); - $currentCommit["ajxp_modiftime"] = strtotime(substr($line, strlen("Date: "))); - } else if ($grabOtherLines) { - if(count($currentCommit["DETAILS"]) >= 10) continue; - $currentCommit["DETAILS"][] = $line; - } else if (trim($line) == "") { - $grabMessageLines = !$grabMessageLines; - } else if ($grabMessageLines) { - if(!isSet($currentCommit["MESSAGE"])) $currentCommit["MESSAGE"] = ""; - $currentCommit["MESSAGE"] .= trim($line); - } - } - // $currentCommit - if (count($currentCommit["DETAILS"]) && substr($currentCommit["DETAILS"][0], 0, strlen("new file")) == "new file") { - $currentCommit["EVENT"] = "CREATION"; - unset($currentCommit["DETAILS"]); - } - $allCommits[] = $currentCommit; - return $allCommits; - } - - - /** - * @param AJXP_Node $fromNode - * @param AJXP_Node$toNode - * @param boolean $copy - */ - public function changesHook($fromNode=null, $toNode=null, $copy=false) - { - $this->commitChanges(); - return; - /* - $refNode = $fromNode; - if ($fromNode == null && $toNode != null) { - $refNode = $toNode; - } - $this->commitChanges(dirname($refNode->getPath())); - */ - } - - private function commitChanges($path = null) - { - $git = new VersionControl_Git($this->repoBase); - $command = $git->getCommand("add"); - $command->addArgument("."); - try { - $cmd = $command->createCommandString(); - $this->logDebug("Git command ".$cmd); - $res = $command->execute(); - } catch (Exception $e) { - $this->logDebug("Error ".$e->getMessage()); - } - $this->logDebug("GIT RESULT ADD : ".$res); - - $command = $git->getCommand("commit"); - $command->setOption("a", true); - $userId = "no user"; - $mail = "mail@mail.com"; - if (AuthService::getLoggedUser()!=null) { - $userId = AuthService::getLoggedUser()->getId(); - $mail = AuthService::getLoggedUser()->personalRole->filterParameterValue("core.conf", "email", AJXP_REPO_SCOPE_ALL, "mail@mail.com"); - } - $command->setOption("m", $userId); - $command->setOption("author", "$userId <$mail>"); - //$command->addArgument($path); - - try { - $cmd = $command->createCommandString(); - $this->logDebug("Git command ".$cmd); - $res = $command->execute(); - } catch (Exception $e) { - $this->logDebug("Error ".$e->getMessage()); - } - $this->logDebug("GIT RESULT COMMIT : ".$res); - } - -} diff --git a/core/src/plugins/meta.git/class.HistoryBrowser.js b/core/src/plugins/meta.git/class.HistoryBrowser.js index 3d8dbe3489..b5c9a011d9 100644 --- a/core/src/plugins/meta.git/class.HistoryBrowser.js +++ b/core/src/plugins/meta.git/class.HistoryBrowser.js @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . * Description : Simple display of SVN logs. */ Class.create("HistoryBrowser", { @@ -66,7 +66,6 @@ Class.create("HistoryBrowser", { this.toolbarObject = new ActionsToolbar(this.toolbar, { buttonRenderer : 'this', - skipBubbling: true, toolbarsList : $A(['history']), dataModelElementId: this.element.id }); diff --git a/core/src/plugins/meta.git/i18n/conf/cs.php b/core/src/plugins/meta.git/i18n/conf/cs.php index 795dfd5f12..936a4d1101 100644 --- a/core/src/plugins/meta.git/i18n/conf/cs.php +++ b/core/src/plugins/meta.git/i18n/conf/cs.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ defined('AJXP_EXEC') or die('Access not allowed'); diff --git a/core/src/plugins/meta.git/i18n/conf/de.php b/core/src/plugins/meta.git/i18n/conf/de.php index 06b3e65d41..10823ecbc4 100644 --- a/core/src/plugins/meta.git/i18n/conf/de.php +++ b/core/src/plugins/meta.git/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ defined('AJXP_EXEC') or die('Access not allowed'); diff --git a/core/src/plugins/meta.git/i18n/conf/en.php b/core/src/plugins/meta.git/i18n/conf/en.php index 8f5e434752..171a0ef885 100644 --- a/core/src/plugins/meta.git/i18n/conf/en.php +++ b/core/src/plugins/meta.git/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ defined('AJXP_EXEC') or die('Access not allowed'); diff --git a/core/src/plugins/meta.git/i18n/conf/fr.php b/core/src/plugins/meta.git/i18n/conf/fr.php index 90342fb78d..69c900a48e 100644 --- a/core/src/plugins/meta.git/i18n/conf/fr.php +++ b/core/src/plugins/meta.git/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ defined('AJXP_EXEC') or die('Access not allowed'); diff --git a/core/src/plugins/meta.git/i18n/conf/it.php b/core/src/plugins/meta.git/i18n/conf/it.php index cd80895563..5854a19d92 100644 --- a/core/src/plugins/meta.git/i18n/conf/it.php +++ b/core/src/plugins/meta.git/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ defined('AJXP_EXEC') or die('Accesso non consentito'); diff --git a/core/src/plugins/meta.git/i18n/conf/pt.php b/core/src/plugins/meta.git/i18n/conf/pt.php index 747743677e..356b4441ec 100644 --- a/core/src/plugins/meta.git/i18n/conf/pt.php +++ b/core/src/plugins/meta.git/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ defined('AJXP_EXEC') or die('Access not allowed'); diff --git a/core/src/plugins/meta.git/i18n/cs.php b/core/src/plugins/meta.git/i18n/cs.php index 6fc0ace85b..f29604fbd3 100644 --- a/core/src/plugins/meta.git/i18n/cs.php +++ b/core/src/plugins/meta.git/i18n/cs.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ defined('AJXP_EXEC') or die('Access not allowed'); diff --git a/core/src/plugins/meta.git/i18n/de.php b/core/src/plugins/meta.git/i18n/de.php index f011a60865..dd9023f821 100644 --- a/core/src/plugins/meta.git/i18n/de.php +++ b/core/src/plugins/meta.git/i18n/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ defined('AJXP_EXEC') or die('Access not allowed'); diff --git a/core/src/plugins/meta.git/i18n/en.php b/core/src/plugins/meta.git/i18n/en.php index b88b646926..3a0fdb6d0d 100644 --- a/core/src/plugins/meta.git/i18n/en.php +++ b/core/src/plugins/meta.git/i18n/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ defined('AJXP_EXEC') or die('Access not allowed'); diff --git a/core/src/plugins/meta.git/i18n/fr.php b/core/src/plugins/meta.git/i18n/fr.php index 4a9f88d952..2aa1a13414 100644 --- a/core/src/plugins/meta.git/i18n/fr.php +++ b/core/src/plugins/meta.git/i18n/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ defined('AJXP_EXEC') or die('Access not allowed'); diff --git a/core/src/plugins/meta.git/i18n/it.php b/core/src/plugins/meta.git/i18n/it.php index 9906cbae56..aaa406b9da 100644 --- a/core/src/plugins/meta.git/i18n/it.php +++ b/core/src/plugins/meta.git/i18n/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ defined('AJXP_EXEC') or die('Accesso non consentito'); diff --git a/core/src/plugins/meta.git/i18n/pt.php b/core/src/plugins/meta.git/i18n/pt.php index 7641c58487..a49df34b28 100644 --- a/core/src/plugins/meta.git/i18n/pt.php +++ b/core/src/plugins/meta.git/i18n/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ defined('AJXP_EXEC') or die('Access not allowed'); diff --git a/core/src/plugins/meta.git/manifest.xml b/core/src/plugins/meta.git/manifest.xml index fdabc24a73..0310e78137 100644 --- a/core/src/plugins/meta.git/manifest.xml +++ b/core/src/plugins/meta.git/manifest.xml @@ -5,7 +5,7 @@ Charles du Jeu - + diff --git a/core/src/plugins/meta.monitor_fs/FSMonitoringManager.php b/core/src/plugins/meta.monitor_fs/FSMonitoringManager.php new file mode 100644 index 0000000000..3709401803 --- /dev/null +++ b/core/src/plugins/meta.monitor_fs/FSMonitoringManager.php @@ -0,0 +1,83 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Meta\Monitor; + +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Core\Controller\CliRunner; + +use Pydio\Core\Model\ContextInterface; +use Pydio\Access\Meta\Core\AbstractMetaSource; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Monitor filesystem using Python + * @package AjaXplorer_Plugins + * @subpackage Meta + */ +class FSMonitoringManager extends AbstractMetaSource +{ + private $repoBase; + + /** + * @param ContextInterface $ctx + * @param AbstractAccessDriver $accessDriver + */ + public function initMeta(ContextInterface $ctx, AbstractAccessDriver $accessDriver) + { + parent::initMeta($ctx, $accessDriver); + $repo = $ctx->getRepository(); + $this->repoBase = $repo->getContextOption($ctx, "PATH"); + } + + /** + * @param AJXP_Node $node + */ + public function beforePathChange(AJXP_Node $node){ + $this->informWatcher("path_change", $node->getPath()); + } + + /** + * @param AJXP_Node $node + */ + public function beforeChange(AJXP_Node $node){ + $this->informWatcher("content_change", $node->getPath()); + } + + /** + * @param AJXP_Node $node + */ + public function beforeCreate(AJXP_Node $node){ + $this->informWatcher("create", $node->getPath()); + } + + /** + * @param $action + * @param $path + */ + protected function informWatcher($action, $path) + { + $cmd = "python ".$this->getBaseDir()."/framework_watch.py --action=$action --path=". escapeshellarg($path); + CliRunner::runCommandInBackground($cmd, $this->getBaseDir() . "/cmd.out"); + } + +} \ No newline at end of file diff --git a/core/src/plugins/meta.monitor_fs/class.FSMonitoringManager.php b/core/src/plugins/meta.monitor_fs/class.FSMonitoringManager.php deleted file mode 100644 index b0bca0fc8e..0000000000 --- a/core/src/plugins/meta.monitor_fs/class.FSMonitoringManager.php +++ /dev/null @@ -1,61 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die('Access not allowed'); - -/** - * Monitor filesystem using Python - * @package AjaXplorer_Plugins - * @subpackage Meta - */ -class FSMonitoringManager extends AJXP_AbstractMetaSource -{ - private $repoBase; - - /** - * @param AbstractAccessDriver $accessDriver - */ - public function initMeta($accessDriver) - { - parent::initMeta($accessDriver); - $repo = $accessDriver->repository; - $this->repoBase = $repo->getOption("PATH"); - } - - public function beforePathChange(AJXP_Node $node){ - $this->informWatcher("path_change", $node->getPath()); - } - - public function beforeChange(AJXP_Node $node){ - $this->informWatcher("content_change", $node->getPath()); - } - - public function beforeCreate(AJXP_Node $node){ - $this->informWatcher("create", $node->getPath()); - } - - protected function informWatcher($action, $path) - { - $cmd = "python ".$this->getBaseDir()."/framework_watch.py --action=$action --path=". escapeshellarg($path); - AJXP_Controller::runCommandInBackground($cmd, $this->getBaseDir()."/cmd.out"); - } - -} \ No newline at end of file diff --git a/core/src/plugins/meta.monitor_fs/i18n/conf/de.php b/core/src/plugins/meta.monitor_fs/i18n/conf/de.php index 543cea5af7..41fdb084d0 100644 --- a/core/src/plugins/meta.monitor_fs/i18n/conf/de.php +++ b/core/src/plugins/meta.monitor_fs/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "FileSystem Monitoring" => "Arbeitsumgebung überwachen", diff --git a/core/src/plugins/meta.monitor_fs/i18n/conf/en.php b/core/src/plugins/meta.monitor_fs/i18n/conf/en.php index 212f42eacf..c9a23cea30 100644 --- a/core/src/plugins/meta.monitor_fs/i18n/conf/en.php +++ b/core/src/plugins/meta.monitor_fs/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "FileSystem Monitoring" => "FileSystem Monitoring", diff --git a/core/src/plugins/meta.monitor_fs/i18n/conf/it.php b/core/src/plugins/meta.monitor_fs/i18n/conf/it.php index a37c02011f..bc2ab7a81e 100644 --- a/core/src/plugins/meta.monitor_fs/i18n/conf/it.php +++ b/core/src/plugins/meta.monitor_fs/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "FileSystem Monitoring" => "Monitoraggio FileSystem", diff --git a/core/src/plugins/meta.monitor_fs/manifest.xml b/core/src/plugins/meta.monitor_fs/manifest.xml index 7beb059d54..5f15266a25 100644 --- a/core/src/plugins/meta.monitor_fs/manifest.xml +++ b/core/src/plugins/meta.monitor_fs/manifest.xml @@ -6,8 +6,8 @@ Charles du Jeu - + diff --git a/core/src/plugins/meta.mount/FilesystemMounter.php b/core/src/plugins/meta.mount/FilesystemMounter.php new file mode 100644 index 0000000000..27e4233960 --- /dev/null +++ b/core/src/plugins/meta.mount/FilesystemMounter.php @@ -0,0 +1,234 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Meta\Mount; + +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Auth\Core\MemorySafe; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Utils\Vars\VarsFilter; +use Pydio\Access\Meta\Core\AbstractMetaSource; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Dynamically mount a remote folder when switching to the repository + * @package AjaXplorer_Plugins + * @subpackage Meta + * + */ +class FilesystemMounter extends AbstractMetaSource +{ + /** + * @param ContextInterface $ctx + * @param AbstractAccessDriver $accessDriver + */ + public function beforeInitMeta(ContextInterface $ctx, AbstractAccessDriver $accessDriver) + { + if($this->isAlreadyMounted($ctx)) { + return; + } + $this->mountFS($ctx); + } + + /** + * @param ContextInterface $ctx + * @param AbstractAccessDriver $accessDriver + */ + public function initMeta(ContextInterface $ctx, AbstractAccessDriver $accessDriver) + { + parent::initMeta($ctx, $accessDriver); + } + + /** + * @return array + * @throws \Exception + */ + protected function getCredentials() + { + // 1. Try from plugin config + $user = $this->options["USER"]; + $password = $this->options["PASS"]; + // 1BIS : encoded? + if ($user == "" && isSet($this->options["ENCODED_CREDENTIALS"])) { + list($user,$password) = MemorySafe::getCredentialsFromEncodedString($this->options["ENCODED_CREDENTIALS"]); + } + // 2. Try from session + if ($user=="" && isSet($this->options["USE_SESSION_CREDENTIALS"]) ) { + $safeCred = MemorySafe::loadCredentials(); + if ($safeCred !== false) { + $user = $safeCred["user"]; + $password = $safeCred["password"]; + } else { + throw new \Exception("Session credential are empty! Did you forget to check the Set Session Credential in the Authentication configuration panel?"); + } + } + return array($user, $password); + } + + /** + * @param ContextInterface $ctx + * @param $name + * @param string $user + * @param string $pass + * @param bool $escapePass + * @return mixed + * @throws \Pydio\Core\Exception\PydioException + */ + protected function getOption(ContextInterface $ctx, $name, $user="", $pass="", $escapePass = true) + { + $opt = $this->options[$name]; + $opt = str_replace("AJXP_USER", $user, $opt); + if($escapePass) $opt = str_replace("AJXP_PASS", "'$pass'", $opt); + else $opt = str_replace("AJXP_PASS", $pass, $opt); + $opt = str_replace("AJXP_SERVER_UID", posix_getuid(), $opt); + $opt = str_replace("AJXP_SERVER_GID", posix_getgid(), $opt); + if (stristr($opt, "AJXP_REPOSITORY_PATH") !== false) { + $repo = $ctx->getRepository(); + $path = $repo->getContextOption($ctx, "PATH"); + $opt = str_replace("AJXP_REPOSITORY_PATH", $path, $opt); + } + $opt = VarsFilter::filter($opt, $ctx); + return $opt; + } + + /** + * @param ContextInterface $contextInterface + * @return bool + * @throws \Exception + */ + protected function isAlreadyMounted(ContextInterface $contextInterface) + { + list($user, $password) = $this->getCredentials(); + $MOUNT_POINT = $this->getOption($contextInterface, "MOUNT_POINT", $user, $password); + if( is_dir($MOUNT_POINT) ){ + $statParent = stat(dirname($MOUNT_POINT)); + $statMount = stat($MOUNT_POINT); + // Compare device id's + if( $statParent[0] == $statMount[0] ){ + return false; + }else{ + return true; + } + }else{ + return false; + } + } + + /** + * @param ContextInterface $ctx + * @throws \Exception + * @throws \Pydio\Core\Exception\PydioException + */ + public function mountFS(ContextInterface $ctx) + { + list($user, $password) = $this->getCredentials(); + $this->logDebug("FSMounter::mountFS Should mount" . $user); + $repo = $ctx->getRepository(); + + if(isset($this->options["MOUNT_DEVIL"]) && !empty($this->options["MOUNT_DEVIL"]) && $this->options["MOUNT_DEVIL"]) { + $udevil = "udevil --quiet"; + }else{ + $udevil = ""; + } + + $MOUNT_TYPE = $this->options["FILESYSTEM_TYPE"]; + $MOUNT_POINT = $this->getOption($ctx, "MOUNT_POINT", $user, $password); + $MOUNT_POINT_ROOT = $this->getOption($ctx, "MOUNT_POINT", "", ""); + $create = $repo->getContextOption($ctx, "CREATE"); + if ( $MOUNT_POINT != $MOUNT_POINT_ROOT && !is_dir($MOUNT_POINT_ROOT) && $create) { + @mkdir($MOUNT_POINT_ROOT, 0755); + } + $recycle = false; + if (!is_dir($MOUNT_POINT) && $create) { + @mkdir($MOUNT_POINT, 0755); + } else { + if ($repo->getContextOption($ctx, "RECYCLE_BIN") != "") { + // Make sure the recycle bin was not mounted inside the mount point! + $recycle = $repo->getContextOption($ctx, "PATH")."/".$repo->getContextOption($ctx, "RECYCLE_BIN"); + if (@is_dir($recycle)) { + @rmdir($recycle); + } + } + } + $UNC_PATH = $this->getOption($ctx, "UNC_PATH", $user, $password, false); + $MOUNT_OPTIONS = $this->getOption($ctx, "MOUNT_OPTIONS", $user, $password, false); + + $cmd = $udevil."mount -t " .$MOUNT_TYPE. (empty( $MOUNT_OPTIONS )? " " : " -o " .escapeshellarg($MOUNT_OPTIONS). " " ) .escapeshellarg($UNC_PATH). " " .escapeshellarg($MOUNT_POINT); + $res = null; + if($this->getOption($ctx, "MOUNT_ENV_PASSWD") == true){ + putenv("PASSWD=$password"); + } + system($cmd, $res); + if($this->getOption($ctx, "MOUNT_ENV_PASSWD") == true){ + putenv("PASSWD="); + } + $resultsOptions = str_replace(" ", "", $this->getOption($ctx, "MOUNT_RESULT_SUCCESS")); + $acceptedResults = array(0); + if(!empty($resultsOptions)){ + $acceptedResults = array_merge($acceptedResults, array_map("intval", explode(",", $resultsOptions))); + } + + if($res === null){ + // Check it is correctly mounted now! + // Could not get the output return code + $cmd1 = "mount | grep ".escapeshellarg($MOUNT_POINT); + $output = shell_exec($cmd1); + $success = !empty($output); + }else{ + $success = (in_array($res, $acceptedResults)); + } + if (!$success) { + throw new \Exception("Error while mounting file system!"); + } else { + if ($recycle !== false && !is_dir($recycle)) { + @mkdir($recycle, 0755); + } + } + } + + /** + * @param ContextInterface $contextInterface + * @return bool + * @throws \Exception + */ + public function umountFS(ContextInterface $contextInterface) + { + $this->logDebug("FSMounter::unmountFS"); + list($user, $password) = $this->getCredentials(); + $MOUNT_POINT = $this->getOption($contextInterface, "MOUNT_POINT", $user, $password); + + if(isset($this->options["MOUNT_DEVIL"]) && !empty($this->options["MOUNT_DEVIL"]) && $this->options["MOUNT_DEVIL"]) { + $udevil = "udevil --quiet"; + }else{ + $udevil = ""; + } + system($udevil."umount ".escapeshellarg($MOUNT_POINT), $res); + if($this->getOption($contextInterface, "REMOVE_MOUNTPOINT_ON_UNMOUNT") == true && $res == 0 && !$this->isAlreadyMounted($contextInterface) ){ + // Remove mount point + $testRm = @rmdir($MOUNT_POINT); + if($testRm === false){ + $this->logError("[umount]", "Error while trying to delete mount point on unmount"); + } + } + return true; + } + +} diff --git a/core/src/plugins/meta.mount/class.FilesystemMounter.php b/core/src/plugins/meta.mount/class.FilesystemMounter.php deleted file mode 100755 index 1bc7d8af30..0000000000 --- a/core/src/plugins/meta.mount/class.FilesystemMounter.php +++ /dev/null @@ -1,208 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die('Access not allowed'); - -/** - * Dynamically mount a remote folder when switching to the repository - * @package AjaXplorer_Plugins - * @subpackage Meta - * - */ -class FilesystemMounter extends AJXP_AbstractMetaSource -{ - /** - * @var Repository - */ - protected $repository; - - /** - * @param AbstractAccessDriver $accessDriver - * @param Repository $repository - */ - public function beforeInitMeta($accessDriver, $repository) - { - $this->accessDriver = $accessDriver; - $this->repository = $repository; - if($this->isAlreadyMounted()) return; - $this->mountFS(); - } - - /** - * @param AbstractAccessDriver $accessDriver - */ - public function initMeta($accessDriver) - { - parent::initMeta($accessDriver); - $this->repository = $this->accessDriver->repository; - /* - if($this->isAlreadyMounted()) return; - $this->mountFS(); - */ - } - - protected function getCredentials() - { - // 1. Try from plugin config - $user = $this->options["USER"]; - $password = $this->options["PASS"]; - // 1BIS : encoded? - if ($user == "" && isSet($this->options["ENCODED_CREDENTIALS"])) { - list($user,$password) = AJXP_Safe::getCredentialsFromEncodedString($this->options["ENCODED_CREDENTIALS"]); - } - // 2. Try from session - if ($user=="" && isSet($this->options["USE_SESSION_CREDENTIALS"]) ) { - $safeCred = AJXP_Safe::loadCredentials(); - if ($safeCred !== false) { - $user = $safeCred["user"]; - $password = $safeCred["password"]; - } else { - throw new Exception("Session credential are empty! Did you forget to check the Set Session Credential in the Authentication configuration panel?"); - } - } - return array($user, $password); - } - - protected function getOption($name, $user="", $pass="", $escapePass = true) - { - $opt = $this->options[$name]; - $opt = str_replace("AJXP_USER", $user, $opt); - if($escapePass) $opt = str_replace("AJXP_PASS", "'$pass'", $opt); - else $opt = str_replace("AJXP_PASS", $pass, $opt); - $opt = str_replace("AJXP_SERVER_UID", posix_getuid(), $opt); - $opt = str_replace("AJXP_SERVER_GID", posix_getgid(), $opt); - if (stristr($opt, "AJXP_REPOSITORY_PATH") !== false) { - $repo = $this->repository; - $path = $repo->getOption("PATH"); - $opt = str_replace("AJXP_REPOSITORY_PATH", $path, $opt); - } - $opt = AJXP_VarsFilter::filter($opt); - return $opt; - } - - protected function isAlreadyMounted() - { - list($user, $password) = $this->getCredentials(); - $MOUNT_POINT = $this->getOption("MOUNT_POINT", $user, $password); - if( is_dir($MOUNT_POINT) ){ - $statParent = stat(dirname($MOUNT_POINT)); - $statMount = stat($MOUNT_POINT); - // Compare device id's - if( $statParent[0] == $statMount[0] ){ - return false; - }else{ - return true; - } - }else{ - return false; - } - } - - public function mountFS() - { - list($user, $password) = $this->getCredentials(); - $this->logDebug("FSMounter::mountFS Should mount" . $user); - $repo = $this->repository; - - if(isset($this->options["MOUNT_DEVIL"]) && !empty($this->options["MOUNT_DEVIL"]) && $this->options["MOUNT_DEVIL"]) { - $udevil = "udevil "; - }else{ - $udevil = ""; - } - - $MOUNT_TYPE = $this->options["FILESYSTEM_TYPE"]; - $MOUNT_POINT = $this->getOption("MOUNT_POINT", $user, $password); - $MOUNT_POINT_ROOT = $this->getOption("MOUNT_POINT", "", ""); - $create = $repo->getOption("CREATE"); - if ( $MOUNT_POINT != $MOUNT_POINT_ROOT && !is_dir($MOUNT_POINT_ROOT) && $create) { - @mkdir($MOUNT_POINT_ROOT, 0755); - } - $recycle = false; - if (!is_dir($MOUNT_POINT) && $create) { - @mkdir($MOUNT_POINT, 0755); - } else { - if ($repo->getOption("RECYCLE_BIN") != "") { - // Make sure the recycle bin was not mounted inside the mount point! - $recycle = $repo->getOption("PATH")."/".$repo->getOption("RECYCLE_BIN"); - if (@is_dir($recycle)) { - @rmdir($recycle); - } - } - } - $UNC_PATH = $this->getOption("UNC_PATH", $user, $password, false); - $MOUNT_OPTIONS = $this->getOption("MOUNT_OPTIONS", $user, $password, false); - - $cmd = $udevil."mount -t " .$MOUNT_TYPE. (empty( $MOUNT_OPTIONS )? " " : " -o " .escapeshellarg($MOUNT_OPTIONS). " " ) .escapeshellarg($UNC_PATH). " " .escapeshellarg($MOUNT_POINT); - $res = null; - if($this->getOption("MOUNT_ENV_PASSWD") == true){ - putenv("PASSWD=$password"); - } - system($cmd, $res); - if($this->getOption("MOUNT_ENV_PASSWD") == true){ - putenv("PASSWD="); - } - $resultsOptions = str_replace(" ", "", $this->getOption("MOUNT_RESULT_SUCCESS")); - $acceptedResults = array(0); - if(!empty($resultsOptions)){ - $acceptedResults = array_merge($acceptedResults, array_map("intval", explode(",", $resultsOptions))); - } - - if($res === null){ - // Check it is correctly mounted now! - // Could not get the output return code - $cmd1 = "mount | grep ".escapeshellarg($MOUNT_POINT); - $output = shell_exec($cmd1); - $success = !empty($output); - }else{ - $success = (in_array($res, $acceptedResults)); - } - if (!$success) { - throw new Exception("Error while mounting file system!"); - } else { - if ($recycle !== false && !is_dir($recycle)) { - @mkdir($recycle, 0755); - } - } - } - - public function umountFS() - { - $this->logDebug("FSMounter::unmountFS"); - list($user, $password) = $this->getCredentials(); - $MOUNT_POINT = $this->getOption("MOUNT_POINT", $user, $password); - - if(isset($this->options["MOUNT_DEVIL"]) && !empty($this->options["MOUNT_DEVIL"]) && $this->options["MOUNT_DEVIL"]) { - $udevil = "udevil "; - }else{ - $udevil = ""; - } - system($udevil."umount ".escapeshellarg($MOUNT_POINT), $res); - if($this->getOption("REMOVE_MOUNTPOINT_ON_UNMOUNT") == true && $res == 0 && !$this->isAlreadyMounted() ){ - // Remove mount point - $testRm = @rmdir($MOUNT_POINT); - if($testRm === false){ - $this->logError("[umount]", "Error while trying to delete mount point on unmount"); - } - } - return true; - } - -} diff --git a/core/src/plugins/meta.mount/i18n/conf/de.php b/core/src/plugins/meta.mount/i18n/conf/de.php index d43b9db6df..bd1ac68d63 100644 --- a/core/src/plugins/meta.mount/i18n/conf/de.php +++ b/core/src/plugins/meta.mount/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "FS Mount" => "Dateisystem einbinden", @@ -39,4 +39,4 @@ "On some setup result code 32 is often an already mounted code and we want to consider this as a success. Add comma-separated list of codes." => "On some setup result code 32 is often an already mounted code and we want to consider this as a success. Add comma-separated list of codes.", "Remove mount point on unmount" => "Remove mount point on unmount", "Delete mount folder on unmount. Can be required for security reasons." => "Delete mount folder on unmount. Can be required for security reasons.", -); \ No newline at end of file +); diff --git a/core/src/plugins/meta.mount/i18n/conf/en.php b/core/src/plugins/meta.mount/i18n/conf/en.php index f32f852fe5..0b35865564 100644 --- a/core/src/plugins/meta.mount/i18n/conf/en.php +++ b/core/src/plugins/meta.mount/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "FS Mount" => "FS Mount", diff --git a/core/src/plugins/meta.mount/i18n/conf/fr.php b/core/src/plugins/meta.mount/i18n/conf/fr.php index 876daeabd0..cfd7b97f69 100644 --- a/core/src/plugins/meta.mount/i18n/conf/fr.php +++ b/core/src/plugins/meta.mount/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "FS Mount" => "Montage de système de fichiers", diff --git a/core/src/plugins/meta.mount/i18n/conf/it.php b/core/src/plugins/meta.mount/i18n/conf/it.php index 232c76c9c2..e23f409ddf 100644 --- a/core/src/plugins/meta.mount/i18n/conf/it.php +++ b/core/src/plugins/meta.mount/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "FS Mount" => "Monta FS", @@ -39,4 +39,4 @@ "On some setup result code 32 is often an already mounted code and we want to consider this as a success. Add comma-separated list of codes." => "On some setup result code 32 is often an already mounted code and we want to consider this as a success. Add comma-separated list of codes.", "Remove mount point on unmount" => "Remove mount point on unmount", "Delete mount folder on unmount. Can be required for security reasons." => "Delete mount folder on unmount. Can be required for security reasons.", -); \ No newline at end of file +); diff --git a/core/src/plugins/meta.mount/i18n/conf/pt.php b/core/src/plugins/meta.mount/i18n/conf/pt.php index 777ad31715..94100879ac 100644 --- a/core/src/plugins/meta.mount/i18n/conf/pt.php +++ b/core/src/plugins/meta.mount/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "FS Mount" => "Partição FS", diff --git a/core/src/plugins/meta.mount/manifest.xml b/core/src/plugins/meta.mount/manifest.xml index 1f6e1f9a25..021b0e0ffb 100644 --- a/core/src/plugins/meta.mount/manifest.xml +++ b/core/src/plugins/meta.mount/manifest.xml @@ -1,7 +1,7 @@ - + diff --git a/core/src/plugins/meta.quota/QuotaComputer.php b/core/src/plugins/meta.quota/QuotaComputer.php new file mode 100644 index 0000000000..4e5cfbfb0b --- /dev/null +++ b/core/src/plugins/meta.quota/QuotaComputer.php @@ -0,0 +1,263 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Meta\Quota; + +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Controller\Controller; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Services\UsersService; +use Pydio\Core\Utils\Vars\StatHelper; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Access\Meta\Core\AbstractMetaSource; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Computes used storage for user + * @package AjaXplorer_Plugins + * @subpackage Meta + */ +class QuotaComputer extends AbstractMetaSource +{ + /** + * @var AbstractAccessDriver + */ + protected $accessDriver; + protected $currentQuota; + protected $computeLocal = true; + public static $loadedQuota; + public static $loadedSoftLimit; + /** + * @var \Pydio\Mailer\Core\Mailer + */ + protected $mailer; + + /** + * @param ContextInterface $ctx + * @return ContextInterface + */ + protected function getEffectiveContext(ContextInterface $ctx){ + $repository = $ctx->getRepository(); + if($repository->hasParent() && $repository->getOwner() !== null){ + $parentOwner = $repository->getOwner(); + return $ctx->withRepositoryId($repository->getParentId())->withUserId($parentOwner); + }else{ + return $ctx; + } + } + + /** + * @param ContextInterface $ctx + * @param $optionName + * @return mixed|null + */ + public function getContextualOption(ContextInterface $ctx, $optionName) + { + $repo = $ctx->getRepository(); + $user = $ctx->getUser(); + if ($repo->hasParent() && $repo->getOwner() != null && $repo->getOwner() != $user->getId()) { + // Pass parent user instead of currently logged + $userObject = UsersService::getUserById($repo->getOwner()); + $newCtx = \Pydio\Core\Model\Context::contextWithObjects($userObject, $repo); + return parent::getContextualOption($newCtx, $optionName); + }else{ + return parent::getContextualOption($ctx, $optionName); + } + + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $node + * @param int $newSize + * @return mixed + * @throws \Exception + */ + public function precheckQuotaUsage($node, $newSize = 0) + { + // POSITIVE DELTA ? + if ($newSize == 0) { + return null; + } + $delta = $newSize; + $quota = $this->getAuthorized($node->getContext()); + $soft = $this->getSoftLimit($node->getContext()); + $q = $this->getUsageForContext($node->getContext()); + $this->logDebug("QUOTA : Previous usage was $q"); + if ($q + $delta >= $quota) { + $mess = LocaleService::getMessages(); + throw new \Exception($mess["meta.quota.3"]." (". StatHelper::roundSize($quota) .")!"); + } else if ( $soft !== false && ($q + $delta) >= $soft && $q <= $soft) { + $this->sendSoftLimitAlert($node->getContext()); + } + } + + /** + * @param ContextInterface $ctx + */ + protected function sendSoftLimitAlert(ContextInterface $ctx) + { + $mailer = PluginsService::getInstance($ctx)->getActivePluginsForType("mailer", true); + if ($mailer !== false && $ctx->hasUser()) { + $percent = $this->getContextualOption($ctx, "SOFT_QUOTA"); + $quota = $this->getContextualOption($ctx, "DEFAULT_QUOTA"); + $mailer->sendMail( + $ctx, + array($ctx->getUser()->getId()), + "You are close to exceed your quota!", + "You are currently using more than $percent% of your authorized quota of $quota!"); + } + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $requestInterface + * @param \Psr\Http\Message\ResponseInterface $responseInterface + */ + public function getCurrentQuota(\Psr\Http\Message\ServerRequestInterface $requestInterface, \Psr\Http\Message\ResponseInterface &$responseInterface) + { + $ctx = $requestInterface->getAttribute("ctx"); + $u = $this->getUsageForContext($ctx); + $responseInterface = new \Zend\Diactoros\Response\JsonResponse(['USAGE' => $u, 'TOTAL' => $this->getAuthorized($ctx)]); + return; + } + + /** + * @param ContextInterface $ctx + * @param $data + */ + public function loadRepositoryInfo(ContextInterface $ctx, &$data){ + $data['meta.quota'] = array( + 'usage' => $u = $this->getUsageForContext($ctx), + 'total' => $this->getAuthorized($ctx) + ); + } + + /** + * @param AJXP_Node $oldNode + * @param AJXP_Node $newNode + * @param bool $copy + * @throws \Exception + */ + public function recomputeQuotaUsage($oldNode = null, $newNode = null, $copy = false) + { + //$repoOptions = $this->getWorkingRepositoryOptions(); + //$q = $this->accessDriver->directoryUsage("", $repoOptions); + + $refNode = ($oldNode !== null ? $oldNode : $newNode); + $q = $this->getUsageForContext($refNode->getContext()); + $this->storeUsage($refNode->getContext(), $q); + $t = $this->getAuthorized($refNode->getContext()); + + Controller::applyHook("msg.instant", array($refNode->getContext(), "")); + } + + /** + * @param ContextInterface $ctx + * @param $quota + */ + protected function storeUsage(ContextInterface $ctx, $quota) + { + $data = $this->getUserData($ctx); + $repo = $ctx->getRepositoryId(); + if(!isset($data["REPO_USAGES"])) $data["REPO_USAGES"] = array(); + $data["REPO_USAGES"][$repo] = $quota; + $this->saveUserData($ctx, $data); + } + + /** + * @param ContextInterface $ctx + * @return int + */ + protected function getAuthorized(ContextInterface $ctx) + { + if(self::$loadedQuota != null) return self::$loadedQuota; + $q = $this->getContextualOption($ctx, "DEFAULT_QUOTA"); + self::$loadedQuota = StatHelper::convertBytes($q); + return self::$loadedQuota; + } + + /** + * @param ContextInterface $ctx + * @return bool|float + */ + protected function getSoftLimit(ContextInterface $ctx) + { + if(self::$loadedSoftLimit != null) return self::$loadedSoftLimit; + $l = $this->getContextualOption($ctx, "SOFT_QUOTA"); + if (!empty($l)) { + self::$loadedSoftLimit = round($this->getAuthorized($ctx)*intval($l)/100); + } else { + self::$loadedSoftLimit = false; + } + return self::$loadedSoftLimit; + } + + /** + * @param ContextInterface $ctx + * @return integer + */ + private function getUsageForContext(ContextInterface $ctx){ + + $ctx = $this->getEffectiveContext($ctx); + $rootNode = new AJXP_Node($ctx->getUrlBase()."/"); + + if (!isSet($data["REPO_USAGES"][$ctx->getRepositoryId()]) || $this->options["CACHE_QUOTA"] === false) { + + $quota = $rootNode->getSizeRecursive(); + if(!isset($data["REPO_USAGES"])) $data["REPO_USAGES"] = array(); + $data["REPO_USAGES"][$ctx->getRepositoryId()] = $quota; + $this->saveUserData($ctx, $data); + + } + + if ($this->getContextualOption($ctx, "USAGE_SCOPE") == "local") { + return floatval($data["REPO_USAGES"][$ctx->getRepositoryId()]); + } else { + return array_sum(array_map("floatval", $data["REPO_USAGES"])); + } + + } + + /** + * @param ContextInterface $ctx + * @return array|mixed|string + */ + private function getUserData(ContextInterface $ctx) + { + $logged = $ctx->getUser(); + $data = $logged->getPref("meta.quota"); + if(is_array($data)) return $data; + else return array(); + } + + /** + * @param ContextInterface $ctx + * @param $data + */ + private function saveUserData(ContextInterface $ctx, $data) + { + $logged = $ctx->getUser(); + $logged->setPref("meta.quota", $data); + $logged->save("user"); + } + +} diff --git a/core/src/plugins/meta.quota/class.QuotaComputer.php b/core/src/plugins/meta.quota/class.QuotaComputer.php deleted file mode 100644 index 55e6ef4a63..0000000000 --- a/core/src/plugins/meta.quota/class.QuotaComputer.php +++ /dev/null @@ -1,250 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die('Access not allowed'); - -/** - * Computes used storage for user - * @package AjaXplorer_Plugins - * @subpackage Meta - */ -class QuotaComputer extends AJXP_AbstractMetaSource -{ - /** - * @var AbstractAccessDriver - */ - protected $accessDriver; - protected $currentQuota; - protected $computeLocal = true; - public static $loadedQuota; - public static $loadedSoftLimit; - /** - * @var AjxpMailer - */ - protected $mailer; - - protected function getWorkingPath() - { - $repo = $this->accessDriver->repository; - $clearParent = null; - // SPECIAL : QUOTA MUST BE COMPUTED ON PARENT REPOSITORY FOLDER - if ($repo->hasParent()) { - $parentOwner = $repo->getOwner(); - if ($parentOwner !== null) { - $repo = ConfService::getRepositoryById($repo->getParentId()); - $originalUser = AuthService::getLoggedUser(); - $loggedUser = AuthService::getLoggedUser(); - if (!$loggedUser->hasParent()) { - $loggedUser->setParent($parentOwner); - $clearParent = null; - } else { - $clearParent = $loggedUser->getParent(); - } - $loggedUser->setResolveAsParent(true); - AuthService::updateUser($loggedUser); - } - } - $path = $repo->getOption("PATH"); - if ( isSet($originalUser) ) { - $originalUser->setParent($clearParent); - $originalUser->setResolveAsParent(false); - AuthService::updateUser($originalUser); - } - - return $path; - } - - /** - * @return array - */ - protected function getWorkingRepositoryOptions() - { - $p = array(); - $repo = $this->accessDriver->repository; - $clearParent = null; - // SPECIAL : QUOTA MUST BE COMPUTED ON PARENT REPOSITORY FOLDER - if ($repo->hasParent()) { - $parentOwner = $repo->getOwner(); - if ($parentOwner !== null) { - $repo = ConfService::getRepositoryById($repo->getParentId()); - $originalUser = AuthService::getLoggedUser(); - $loggedUser = AuthService::getLoggedUser(); - if (!$loggedUser->hasParent()) { - $loggedUser->setParent($parentOwner); - $clearParent = null; - } else { - $clearParent = $loggedUser->getParent(); - } - $loggedUser->setResolveAsParent(true); - AuthService::updateUser($loggedUser); - } - } - $path = $repo->getOption("PATH"); - $p["PATH"] = $path; - if ( isSet($originalUser) ) { - $originalUser->setParent($clearParent); - $originalUser->setResolveAsParent(false); - AuthService::updateUser($originalUser); - } - return $p; - } - - public function getFilteredOption($optionName, $repoScope = AJXP_REPO_SCOPE_ALL, $userObject = null){ - $repo = $this->accessDriver->repository; - if ($repo->hasParent() && $repo->getOwner() != null && $repo->getOwner() != AuthService::getLoggedUser()->getId()) { - // Pass parent user instead of currently logged - $userObject = ConfService::getConfStorageImpl()->createUserObject($repo->getOwner()); - } - return parent::getFilteredOption($optionName, $repoScope, $userObject); - } - - /** - * @param AJXP_Node $node - * @param int $newSize - * @return mixed - * @throws Exception - */ - public function precheckQuotaUsage($node, $newSize = 0) - { - // POSITIVE DELTA ? - if ($newSize == 0) { - return null; - } - $delta = $newSize; - $quota = $this->getAuthorized(); - $soft = $this->getSoftLimit(); - $q = $this->getUsage(); - $this->logDebug("QUOTA : Previous usage was $q"); - if ($q + $delta >= $quota) { - $mess = ConfService::getMessages(); - throw new Exception($mess["meta.quota.3"]." (".AJXP_Utils::roundSize($quota) .")!"); - } else if ( $soft !== false && ($q + $delta) >= $soft && $q <= $soft) { - $this->sendSoftLimitAlert(); - } - } - - protected function sendSoftLimitAlert() - { - $mailer = AJXP_PluginsService::getInstance()->getActivePluginsForType("mailer", true); - if ($mailer !== false) { - $percent = $this->getFilteredOption("SOFT_QUOTA"); - $quota = $this->getFilteredOption("DEFAULT_QUOTA"); - $mailer->sendMail( - array(AuthService::getLoggedUser()->getId()), - "You are close to exceed your quota!", - "You are currently using more than $percent% of your authorized quota of $quota!"); - } - } - - public function getCurrentQuota($action, $httpVars, $fileVars) - { - $u = $this->getUsage(); - HTMLWriter::charsetHeader("application/json"); - print json_encode(array('USAGE' => $u, 'TOTAL' => $this->getAuthorized())); - return; - } - - public function loadRepositoryInfo(&$data){ - $data['meta.quota'] = array( - 'usage' => $u = $this->getUsage(), - 'total' => $this->getAuthorized() - ); - } - - public function recomputeQuotaUsage($oldNode = null, $newNode = null, $copy = false) - { - $repoOptions = $this->getWorkingRepositoryOptions(); - $q = $this->accessDriver->directoryUsage("", $repoOptions); - $this->storeUsage($q); - $t = $this->getAuthorized(); - AJXP_Controller::applyHook("msg.instant", array("", $this->accessDriver->repository->getId())); - } - - protected function storeUsage($quota) - { - $data = $this->getUserData(); - $repo = $this->accessDriver->repository->getId(); - if(!isset($data["REPO_USAGES"])) $data["REPO_USAGES"] = array(); - $data["REPO_USAGES"][$repo] = $quota; - $this->saveUserData($data); - } - - protected function getAuthorized() - { - if(self::$loadedQuota != null) return self::$loadedQuota; - $q = $this->getFilteredOption("DEFAULT_QUOTA"); - self::$loadedQuota = AJXP_Utils::convertBytes($q); - return self::$loadedQuota; - } - - protected function getSoftLimit() - { - if(self::$loadedSoftLimit != null) return self::$loadedSoftLimit; - $l = $this->getFilteredOption("SOFT_QUOTA"); - if (!empty($l)) { - self::$loadedSoftLimit = round($this->getAuthorized()*intval($l)/100); - } else { - self::$loadedSoftLimit = false; - } - return self::$loadedSoftLimit; - } - - /** - * @param String $dir - * @return bool|int - */ - private function getUsage() - { - $data = $this->getUserData(); - $repo = $this->accessDriver->repository->getId(); - $repoOptions = $this->getWorkingRepositoryOptions(); - if (!isSet($data["REPO_USAGES"][$repo]) || $this->options["CACHE_QUOTA"] === false) { - $quota = $this->accessDriver->directoryUsage("", $repoOptions); - if(!isset($data["REPO_USAGES"])) $data["REPO_USAGES"] = array(); - $data["REPO_USAGES"][$repo] = $quota; - $this->saveUserData($data); - } - - if ($this->getFilteredOption("USAGE_SCOPE", $repo) == "local") { - return floatval($data["REPO_USAGES"][$repo]); - } else { - return array_sum(array_map("floatval", $data["REPO_USAGES"])); - } - - } - - private function getUserData() - { - $logged = AuthService::getLoggedUser(); - $data = $logged->getPref("meta.quota"); - if(is_array($data)) return $data; - else return array(); - } - - private function saveUserData($data) - { - $logged = AuthService::getLoggedUser(); - $logged->setPref("meta.quota", $data); - $logged->save("user"); - AuthService::updateUser($logged); - } - -} diff --git a/core/src/plugins/meta.quota/i18n/ca.php b/core/src/plugins/meta.quota/i18n/ca.php index 2065005427..1fdf4d78b5 100644 --- a/core/src/plugins/meta.quota/i18n/ca.php +++ b/core/src/plugins/meta.quota/i18n/ca.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ // catalan translation: Salva Gómez , 2015 $mess=array( diff --git a/core/src/plugins/meta.quota/i18n/conf/de.php b/core/src/plugins/meta.quota/i18n/conf/de.php index 6b17796c02..8a0b403ca4 100644 --- a/core/src/plugins/meta.quota/i18n/conf/de.php +++ b/core/src/plugins/meta.quota/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "FS Mount" => "Dateisystem einbinden", diff --git a/core/src/plugins/meta.quota/i18n/conf/en.php b/core/src/plugins/meta.quota/i18n/conf/en.php index 7a2877dca9..79a592919e 100644 --- a/core/src/plugins/meta.quota/i18n/conf/en.php +++ b/core/src/plugins/meta.quota/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "FS Mount" => "FS Mount", diff --git a/core/src/plugins/meta.quota/i18n/conf/fr.php b/core/src/plugins/meta.quota/i18n/conf/fr.php index 5a0d59c600..354e465de8 100644 --- a/core/src/plugins/meta.quota/i18n/conf/fr.php +++ b/core/src/plugins/meta.quota/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "FS Mount" => "Montage de système de fichiers", diff --git a/core/src/plugins/meta.quota/i18n/conf/it.php b/core/src/plugins/meta.quota/i18n/conf/it.php index 8cb57ceee7..ad4a1b4d5f 100644 --- a/core/src/plugins/meta.quota/i18n/conf/it.php +++ b/core/src/plugins/meta.quota/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "FS Mount" => "Monta FS", diff --git a/core/src/plugins/meta.quota/i18n/conf/pt.php b/core/src/plugins/meta.quota/i18n/conf/pt.php index fcd5b0acd6..949a67a7d7 100644 --- a/core/src/plugins/meta.quota/i18n/conf/pt.php +++ b/core/src/plugins/meta.quota/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "FS Mount" => "Partição FS", diff --git a/core/src/plugins/meta.quota/i18n/conf/ru.php b/core/src/plugins/meta.quota/i18n/conf/ru.php index 31dcc2772a..d7b93d7603 100644 --- a/core/src/plugins/meta.quota/i18n/conf/ru.php +++ b/core/src/plugins/meta.quota/i18n/conf/ru.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "FS Mount" => "FS Mount", @@ -34,4 +34,4 @@ "Soft Limit (%)" => "Soft Limit (%)", "Custom Field (Deprecated)" => "Custom Field (Deprecated)", "If you want to define quotas for each user, define a custom field in the CUSTOM_DATA parameter of the conf plugin, and declare this field name here." => "If you want to define quotas for each user, define a custom field in the CUSTOM_DATA parameter of the conf plugin, and declare this field name here.", -); \ No newline at end of file +); diff --git a/core/src/plugins/meta.quota/i18n/de.php b/core/src/plugins/meta.quota/i18n/de.php index 81593089dd..9dffa88d70 100644 --- a/core/src/plugins/meta.quota/i18n/de.php +++ b/core/src/plugins/meta.quota/i18n/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "1" => "Speicherverbrauch (lädt...)", diff --git a/core/src/plugins/meta.quota/i18n/en.php b/core/src/plugins/meta.quota/i18n/en.php index eb7f543f53..e262a8e45c 100644 --- a/core/src/plugins/meta.quota/i18n/en.php +++ b/core/src/plugins/meta.quota/i18n/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "1" => "Quota usage (loading...)", diff --git a/core/src/plugins/meta.quota/i18n/es.php b/core/src/plugins/meta.quota/i18n/es.php index 1dc95f2560..4ed7fc1a85 100644 --- a/core/src/plugins/meta.quota/i18n/es.php +++ b/core/src/plugins/meta.quota/i18n/es.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ // spanish translation: Salva Gómez , 2015 $mess=array( diff --git a/core/src/plugins/meta.quota/i18n/fr.php b/core/src/plugins/meta.quota/i18n/fr.php index a4b98830f1..81700b4979 100644 --- a/core/src/plugins/meta.quota/i18n/fr.php +++ b/core/src/plugins/meta.quota/i18n/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "1" => "Utilisation du quota (en cours...)", diff --git a/core/src/plugins/meta.quota/i18n/it.php b/core/src/plugins/meta.quota/i18n/it.php index 00c6e2ad47..a9413e4d31 100644 --- a/core/src/plugins/meta.quota/i18n/it.php +++ b/core/src/plugins/meta.quota/i18n/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "1" => "Utilizzo (caricamento...)", diff --git a/core/src/plugins/meta.quota/i18n/pt.php b/core/src/plugins/meta.quota/i18n/pt.php index b752f81bdb..753c35210f 100644 --- a/core/src/plugins/meta.quota/i18n/pt.php +++ b/core/src/plugins/meta.quota/i18n/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "1" => "Uso da Quota...", diff --git a/core/src/plugins/meta.quota/i18n/ru.php b/core/src/plugins/meta.quota/i18n/ru.php index 8eabbc008b..9e58561ee6 100644 --- a/core/src/plugins/meta.quota/i18n/ru.php +++ b/core/src/plugins/meta.quota/i18n/ru.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "1" => "Объём файлов (загрузка...)", diff --git a/core/src/plugins/meta.quota/manifest.xml b/core/src/plugins/meta.quota/manifest.xml index 6a72b413a8..04447a06d5 100644 --- a/core/src/plugins/meta.quota/manifest.xml +++ b/core/src/plugins/meta.quota/manifest.xml @@ -1,7 +1,7 @@ - + diff --git a/core/src/plugins/meta.simple_lock/SimpleLockManager.php b/core/src/plugins/meta.simple_lock/SimpleLockManager.php new file mode 100644 index 0000000000..7b9f2e0095 --- /dev/null +++ b/core/src/plugins/meta.simple_lock/SimpleLockManager.php @@ -0,0 +1,156 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Meta\Lock; + +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Model\NodesDiff; +use Pydio\Access\Core\Model\UserSelection; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Http\Response\SerializableResponseStream; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Services\UsersService; +use Pydio\Access\Meta\Core\AbstractMetaSource; +use Pydio\Access\Metastore\Core\IMetaStoreProvider; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Locks a folder manually + * @package AjaXplorer_Plugins + * @subpackage Meta + */ +class SimpleLockManager extends AbstractMetaSource +{ + const METADATA_LOCK_NAMESPACE = "simple_lock"; + /** + * @var IMetaStoreProvider + */ + protected $metaStore; + + /** + * @param ContextInterface $ctx + * @param AbstractAccessDriver $accessDriver + * @throws PydioException + */ + public function initMeta(ContextInterface $ctx, AbstractAccessDriver $accessDriver) + { + parent::initMeta($ctx, $accessDriver); + $store = PluginsService::getInstance($ctx)->getUniqueActivePluginForType("metastore"); + if ($store === false) { + throw new PydioException("The 'meta.simple_lock' plugin requires at least one active 'metastore' plugin"); + } + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $requestInterface + * @param \Psr\Http\Message\ResponseInterface $responseInterface + * @throws PydioException + */ + public function applyChangeLock(\Psr\Http\Message\ServerRequestInterface $requestInterface, \Psr\Http\Message\ResponseInterface &$responseInterface) + { + $httpVars = $requestInterface->getParsedBody(); + /** @var ContextInterface $ctx */ + $ctx = $requestInterface->getAttribute("ctx"); + + if ($ctx->getRepository()->getDriverInstance($ctx) instanceof \Pydio\Access\Driver\StreamProvider\FS\DemoAccessDriver) { + throw new PydioException("Write actions are disabled in demo mode!"); + } + $repo = $ctx->getRepository(); + $user = $ctx->getUser(); + if (!UsersService::usersEnabled() && $user!=null && !$user->canWrite($repo->getId())) { + throw new PydioException("You have no right on this action."); + } + $selection = UserSelection::fromContext($ctx, $httpVars); + + $unlock = (isSet($httpVars["unlock"])?true:false); + $node = $selection->getUniqueNode(); + if ($unlock) { + $node->removeMetadata(self::METADATA_LOCK_NAMESPACE, false, AJXP_METADATA_SCOPE_GLOBAL); + } else { + $node->setMetadata( + SimpleLockManager::METADATA_LOCK_NAMESPACE, + array("lock_user" => $ctx->getUser()->getId()), + false, + AJXP_METADATA_SCOPE_GLOBAL + ); + } + $x = new SerializableResponseStream(); + $diff = new NodesDiff(); + $diff->update($selection->getUniqueNode()); + $x->addChunk($diff); + $responseInterface = $responseInterface->withBody($x); + } + + /** + * @param AJXP_Node $node + */ + public function processLockMeta($node) + { + // Transform meta into overlay_icon + // $this->logDebug("SHOULD PROCESS METADATA FOR ", $node->getLabel()); + $lock = $node->retrieveMetadata( + SimpleLockManager::METADATA_LOCK_NAMESPACE, + false, + AJXP_METADATA_SCOPE_GLOBAL); + if(is_array($lock) + && array_key_exists("lock_user", $lock)){ + if ($lock["lock_user"] != $node->getContext()->getUser()->getId()) { + $displayName = UsersService::getUserPersonalParameter("USER_DISPLAY_NAME", $lock["lock_user"], "core.conf", $lock["lock_user"]); + $node->setLabel($node->getLabel() . " (locked by ".$displayName.")"); + $node->mergeMetadata(array( + "sl_locked" => "true", + "overlay_icon" => "meta_simple_lock/ICON_SIZE/lock.png", + "overlay_class" => "icon-lock" + ), true); + } else { + $node->setLabel($node->getLabel() . " (locked by you)"); + $node->mergeMetadata(array( + "sl_locked" => "true", + "sl_mylock" => "true", + "overlay_icon" => "meta_simple_lock/ICON_SIZE/lock_my.png", + "overlay_class" => "icon-lock" + ), true); + } + } + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $node + * @throws PydioException + */ + public function checkFileLock($node) + { + $this->logDebug("SHOULD CHECK LOCK METADATA FOR ", $node->getLabel()); + $lock = $node->retrieveMetadata( + SimpleLockManager::METADATA_LOCK_NAMESPACE, + false, + AJXP_METADATA_SCOPE_GLOBAL); + if(is_array($lock) + && array_key_exists("lock_user", $lock) + && $lock["lock_user"] != $node->getUserId()){ + $mess = LocaleService::getMessages(); + throw new PydioException($mess["meta.simple_lock.5"]); + } + } +} diff --git a/core/src/plugins/meta.simple_lock/class.SimpleLockManager.php b/core/src/plugins/meta.simple_lock/class.SimpleLockManager.php deleted file mode 100644 index 0e103fee77..0000000000 --- a/core/src/plugins/meta.simple_lock/class.SimpleLockManager.php +++ /dev/null @@ -1,131 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die('Access not allowed'); - -/** - * Locks a folder manually - * @package AjaXplorer_Plugins - * @subpackage Meta - */ -class SimpleLockManager extends AJXP_AbstractMetaSource -{ - const METADATA_LOCK_NAMESPACE = "simple_lock"; - /** - * @var MetaStoreProvider - */ - protected $metaStore; - - public function initMeta($accessDriver) - { - parent::initMeta($accessDriver); - $store = AJXP_PluginsService::getInstance()->getUniqueActivePluginForType("metastore"); - if ($store === false) { - throw new Exception("The 'meta.simple_lock' plugin requires at least one active 'metastore' plugin"); - } - $this->metaStore = $store; - $this->metaStore->initMeta($accessDriver); - } - - /** - * @param string $action - * @param array $httpVars - * @param array $fileVars - */ - public function applyChangeLock($actionName, $httpVars, $fileVars) - { - if (is_a($this->accessDriver, "demoAccessDriver")) { - throw new Exception("Write actions are disabled in demo mode!"); - } - $repo = $this->accessDriver->repository; - $user = AuthService::getLoggedUser(); - if (!AuthService::usersEnabled() && $user!=null && !$user->canWrite($repo->getId())) { - throw new Exception("You have no right on this action."); - } - $selection = new UserSelection($repo, $httpVars); - - $unlock = (isSet($httpVars["unlock"])?true:false); - if ($unlock) { - $this->metaStore->removeMetadata($selection->getUniqueNode(), self::METADATA_LOCK_NAMESPACE, false, AJXP_METADATA_SCOPE_GLOBAL); - } else { - $this->metaStore->setMetadata( - $selection->getUniqueNode(), - SimpleLockManager::METADATA_LOCK_NAMESPACE, - array("lock_user" => AuthService::getLoggedUser()->getId()), - false, - AJXP_METADATA_SCOPE_GLOBAL - ); - } - AJXP_XMLWriter::header(); - AJXP_XMLWriter::reloadDataNode(); - AJXP_XMLWriter::close(); - } - - /** - * @param AJXP_Node $node - */ - public function processLockMeta($node) - { - // Transform meta into overlay_icon - // $this->logDebug("SHOULD PROCESS METADATA FOR ", $node->getLabel()); - $lock = $this->metaStore->retrieveMetadata( - $node, - SimpleLockManager::METADATA_LOCK_NAMESPACE, - false, - AJXP_METADATA_SCOPE_GLOBAL); - if(is_array($lock) - && array_key_exists("lock_user", $lock)){ - if ($lock["lock_user"] != AuthService::getLoggedUser()->getId()) { - $node->mergeMetadata(array( - "sl_locked" => "true", - "overlay_icon" => "meta_simple_lock/ICON_SIZE/lock.png", - "overlay_class" => "icon-lock" - ), true); - } else { - $node->mergeMetadata(array( - "sl_locked" => "true", - "sl_mylock" => "true", - "overlay_icon" => "meta_simple_lock/ICON_SIZE/lock_my.png", - "overlay_class" => "icon-lock" - ), true); - } - } - } - - /** - * @param AJXP_Node $node - */ - public function checkFileLock($node) - { - $this->logDebug("SHOULD CHECK LOCK METADATA FOR ", $node->getLabel()); - $lock = $this->metaStore->retrieveMetadata( - $node, - SimpleLockManager::METADATA_LOCK_NAMESPACE, - false, - AJXP_METADATA_SCOPE_GLOBAL); - if(is_array($lock) - && array_key_exists("lock_user", $lock) - && $lock["lock_user"] != AuthService::getLoggedUser()->getId()){ - $mess = ConfService::getMessages(); - throw new Exception($mess["meta.simple_lock.5"]); - } - } -} diff --git a/core/src/plugins/meta.simple_lock/manifest.xml b/core/src/plugins/meta.simple_lock/manifest.xml index 6c0c716d58..84ca991d00 100644 --- a/core/src/plugins/meta.simple_lock/manifest.xml +++ b/core/src/plugins/meta.simple_lock/manifest.xml @@ -1,7 +1,7 @@ - + @@ -10,9 +10,9 @@ - - - + + + diff --git a/core/src/plugins/meta.svn/SvnManager.php b/core/src/plugins/meta.svn/SvnManager.php new file mode 100644 index 0000000000..869a682769 --- /dev/null +++ b/core/src/plugins/meta.svn/SvnManager.php @@ -0,0 +1,494 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Meta\Version; + +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Access\Core\MetaStreamWrapper; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\RecycleBinManager; +use Pydio\Access\Core\Model\UserSelection; +use Pydio\Core\Http\Message\ReloadMessage; +use Pydio\Core\Http\Message\UserMessage; +use Pydio\Core\Http\Message\XMLMessage; +use Pydio\Core\Http\Response\SerializableResponseStream; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\AuthService; + +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Utils\Vars\InputFilter; + +use Pydio\Core\Utils\Vars\XMLFilter; +use Pydio\Core\Utils\TextEncoder; +use Pydio\Access\Meta\Core\AbstractMetaSource; + +defined('AJXP_EXEC') or die( 'Access not allowed'); +define('SVNLIB_PATH', ''); +if (SVNLIB_PATH != "") { + putenv("LD_LIBRARY_PATH=".SVNLIB_PATH); +} +/** + * Uses svn command lines to extract version infos. Autocommit on change. + * @package AjaXplorer_Plugins + * @subpackage Meta + */ +class SvnManager extends AbstractMetaSource +{ + private static $svnListDir; + private static $svnListCache; + private $commitMessageParams; + + /** + * @param \Pydio\Core\Model\ContextInterface $ctx + * @param array $options + */ + public function init(\Pydio\Core\Model\ContextInterface $ctx, $options = []) + { + $this->options = $options; + // Do nothing + } + + /** + * @param ContextInterface $ctx + * @param AbstractAccessDriver $accessDriver + */ + public function initMeta(ContextInterface $ctx, AbstractAccessDriver $accessDriver) + { + require_once("svn_lib.inc.php"); + parent::initMeta($ctx, $accessDriver); + parent::init($ctx, $this->options); + } + + /** + * @param ContextInterface $ctx + * @param $httpVars + * @param array $additionnalPathes + * @param bool $testRecycle + * @return array + * @throws \Exception + */ + protected function initDirAndSelection(ContextInterface $ctx, $httpVars, $additionnalPathes = array(), $testRecycle = false) + { + $userSelection = UserSelection::fromContext($ctx, $httpVars); + $repo = $userSelection->getContext()->getRepository(); + $urlBase = $userSelection->currentBaseUrl(); + $result = array(); + + if ($testRecycle) { + $recycle = $repo->getContextOption($ctx, "RECYCLE_BIN"); + if ($recycle != "") { + RecycleBinManager::init($urlBase, "/".$recycle); + RecycleBinManager::filterActions($httpVars["get_action"], $userSelection, $httpVars); + $result["RECYCLE"] = $recycle; + // if necessary, check recycle was checked. + // We could use a hook instead here? Maybe the full recycle system + // could be turned into a plugin + $sessionKey = "AJXP_SVN_".$repo->getId()."_RECYCLE_CHECKED"; + if (isSet($_SESSION[$sessionKey])) { + $file = RecycleBinManager::getRelativeRecycle()."/".RecycleBinManager::getCacheFileName(); + $realFile = MetaStreamWrapper::getRealFSReference($urlBase.$file); + $this->addIfNotVersionned($ctx, $file, $realFile); + $_SESSION[$sessionKey] = true; + } + } + } + + $result["DIR"] = MetaStreamWrapper::getRealFSReference($urlBase. InputFilter::decodeSecureMagic($httpVars["dir"])); + $result["ORIGINAL_SELECTION"] = $userSelection; + $result["SELECTION"] = array(); + if (!$userSelection->isEmpty()) { + $files = $userSelection->getFiles(); + foreach ($files as $selected) { + $result["SELECTION"][] = MetaStreamWrapper::getRealFSReference($urlBase.$selected); + } + } + foreach ($additionnalPathes as $parameter => $path) { + $result[$parameter] = MetaStreamWrapper::getRealFSReference($urlBase.$path); + } + return $result; + } + + /** + * @param $ctx ContextInterface + * @param $repoFile + * @param $realFile + * @throws \Exception + */ + protected function addIfNotVersionned(ContextInterface $ctx, $repoFile, $realFile) + { + $error = false; + $res = []; + try { + $res = ExecSvnCmd("svn status ", $realFile); + } catch (\Exception $e) { + $error = true; + } + if ($error || (count($res[IDX_STDOUT]) && substr($res[IDX_STDOUT][0],0,1) == "?")) { + ExecSvnCmd("svn add", "$realFile"); + $this->commitMessageParams = "Recycle cache file"; + $this->commitChanges("ADD", array("dir" => dirname($repoFile)), array(), $ctx); + } + } + + /** + * @param String $file URL of the file to commit (probably a metadata) + * @param \Pydio\Access\Core\Model\AJXP_Node $ajxpNode Optionnal node to commit along. + */ + public function commitFile($file, $ajxpNode) + { + $realFile = MetaStreamWrapper::getRealFSReference($file); + + $res = ExecSvnCmd("svn status ", $realFile); + if (count($res[IDX_STDOUT]) && substr($res[IDX_STDOUT][0],0,1) == "?") { + $res2 = ExecSvnCmd("svn add", "$realFile"); + } + $ctx = $ajxpNode->getContext(); + if ($ajxpNode != null) { + $nodeRealFile = MetaStreamWrapper::getRealFSReference($ajxpNode->getUrl()); + try { + ExecSvnCmd("svn propset metachange ".time(), $nodeRealFile); + } catch (\Exception $e) { + $this->commitChanges("COMMIT_META", $realFile, array(), $ctx); + return; + } + // WILL COMMIT BOTH AT ONCE + $command = "svn commit"; + $user = $ctx->getUser()->getId(); + $switches = "-m \"Pydio||$user||COMMIT_META||file:".escapeshellarg($file)."\""; + ExecSvnCmd($command, array($realFile, $nodeRealFile), $switches); + ExecSvnCmd('svn update', dirname($nodeRealFile), ''); + } else { + $this->commitChanges("COMMIT_META", $realFile, array(), $ctx); + } + } + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @throws \Exception + */ + public function switchAction(ServerRequestInterface $requestInterface, ResponseInterface &$responseInterface) + { + $actionName = $requestInterface->getAttribute("action"); + $httpVars = $requestInterface->getParsedBody(); + $ctx = $requestInterface->getAttribute("ctx"); + + $init = $this->initDirAndSelection($ctx, $httpVars); + if ($actionName == "svnlog") { + $res1 = ExecSvnCmd("svnversion", $init["DIR"]); + $test = trim(implode("", $res1[IDX_STDOUT])); + if (is_numeric($test)) { + $currentRev = $test; + } else if (strstr($test, ":")!==false && count(explode(":", $test))) { + $revRange = explode(":", $test); + } + $command = 'svn log'; + $switches = '--xml -rHEAD:0'; + $arg = $init["SELECTION"][0]; + $res = ExecSvnCmd($command, $arg, $switches); + $serialStream = new SerializableResponseStream(); + $lines = explode(PHP_EOL, $res[IDX_STDOUT]); + array_shift($lines); + if (isSet($currentRev)) { + $serialStream->addChunk(new XMLMessage("$currentRev")); + } else if (isSet($revRange)) { + $serialStream->addChunk(new XMLMessage("")); + } + $serialStream->addChunk(new XMLMessage(implode("", $lines))); + $responseInterface = $responseInterface->withBody($serialStream); + + } else if ($actionName == "svndownload") { + $revision = $httpVars["revision"]; + $realFile = $init["SELECTION"][0]; + $entries = $this->svnListNode($realFile, $revision); + $keys = array_keys($entries); + $localName = $keys[0]; + $contentSize = 0; + if (isSet($entries[$localName]["last_revision_size"])) { + $contentSize = intval($entries[$localName]["last_revision_size"]); + } + // output directly the file! + header("Content-Type: application/force-download; name=\"".$localName."\""); + header("Content-Transfer-Encoding: binary"); + if($contentSize > 0) header("Content-Length: ".$contentSize); + header("Content-Disposition: attachment; filename=\"".$localName."\""); + header("Expires: 0"); + header("Cache-Control: no-cache, must-revalidate"); + header("Pragma: no-cache"); + if (preg_match('/ MSIE /',$_SERVER['HTTP_USER_AGENT'])) { + header("Pragma: public"); + header("Expires: 0"); + header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); + header("Cache-Control: private",false); + } + $realFile = escapeshellarg($realFile); + $revision = escapeshellarg($revision); + system( (SVNLIB_PATH!=""?SVNLIB_PATH."/":"") ."svn cat -r$revision $realFile"); + return; + } else if ($actionName == "revert_file") { + + $revision = escapeshellarg($httpVars["revision"]); + $realFile = $init["SELECTION"][0]; + $compare = (isSet($httpVars["compare"]) && $httpVars["compare"] == "true"); + $escapedFile = escapeshellarg($realFile); + if ($compare) { + $ext = pathinfo($realFile, PATHINFO_EXTENSION); + $targetFile = preg_replace("/\.$ext$/", "-r$revision.$ext", $realFile); + system( (SVNLIB_PATH!=""?SVNLIB_PATH."/":"") ."svn cat -r$revision $escapedFile > ".escapeshellarg($targetFile)); + } else { + system( (SVNLIB_PATH!=""?SVNLIB_PATH."/":"") ."svn cat -r$revision $escapedFile > $escapedFile"); + $this->commitChanges($actionName, $realFile, array(), $ctx); + } + + } else if ($actionName == "svnswitch") { + $revision = escapeshellarg($httpVars["revision"]); + ExecSvnCmd("svn update -r$revision ".$init["DIR"]); + } + } + + /** + * @param $actionName + * @param $httpVars + * @param $filesVars + * @param ContextInterface $ctx + * @throws \Exception + */ + public function addSelection($actionName, $httpVars, $filesVars, ContextInterface $ctx) + { + switch ($actionName) { + case "mkdir": + $init = $this->initDirAndSelection($ctx, $httpVars, array("NEW_DIR" => InputFilter::decodeSecureMagic($httpVars["dir"] . "/" . $httpVars["dirname"]))); + $res = ExecSvnCmd("svn add", $init["NEW_DIR"]); + $this->commitMessageParams = $httpVars["dirname"]; + break; + case "mkfile": + $init = $this->initDirAndSelection($ctx, $httpVars, array("NEW_FILE" => InputFilter::decodeSecureMagic($httpVars["dir"] . "/" . $httpVars["filename"]))); + $res = ExecSvnCmd("svn add", $init["NEW_FILE"]); + $this->commitMessageParams = $httpVars["filename"]; + break; + case "upload": + if (isSet($filesVars) && isSet($filesVars["userfile_0"]) && isSet($filesVars["userfile_0"]["name"])) { + $init = $this->initDirAndSelection($ctx, $httpVars, array("NEW_FILE" => TextEncoder::fromUTF8($httpVars["dir"])."/".$filesVars["userfile_0"]["name"])); + $res = ExecSvnCmd("svn status ", $init["NEW_FILE"]); + if (count($res[IDX_STDOUT]) && substr($res[IDX_STDOUT][0],0,1) == "?") { + $res = ExecSvnCmd("svn add", $init["NEW_FILE"]); + } else { + $res = true; + } + //$res = ExecSvnCmd("svn add", $init["NEW_FILE"]); + $this->commitMessageParams = $filesVars["userfile_0"]["name"]; + } + break; + } + if (isSet($res)) { + $this->commitChanges($actionName, $httpVars, $filesVars, $ctx); + } + } + + /** + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + * @throws \Exception + */ + public function copyOrMoveSelection(ServerRequestInterface $requestInterface, ResponseInterface &$responseInterface) + { + $actionName = $requestInterface->getAttribute("action"); + $httpVars = $requestInterface->getParsedBody(); + $ctx = $requestInterface->getAttribute("ctx"); + + if ($actionName != "rename") { + $init = $this->initDirAndSelection($ctx, $httpVars, array("DEST_DIR" => InputFilter::decodeSecureMagic($httpVars["dest"]))); + $this->commitMessageParams = "To:".$httpVars["dest"].";items:"; + } else { + $init = $this->initDirAndSelection($ctx, $httpVars, array(), true); + } + $this->logDebug("Entering SVN MAnager for action $actionName", $init); + $action = 'copy'; + if ($actionName == "move" || $actionName == "rename") { + $action = 'move'; + } + foreach ($init["SELECTION"] as $selectedFile) { + if ($actionName == "rename") { + $destFile = dirname($selectedFile)."/". InputFilter::decodeSecureMagic($httpVars["filename_new"]); + $this->commitMessageParams = "To:".$httpVars["filename_new"].";item:".$httpVars["file"]; + } else { + $destFile = $init["DEST_DIR"]."/".basename($selectedFile); + } + $this->addIfNotVersionned($ctx, str_replace($init["DIR"], "", $selectedFile), $selectedFile); + ExecSvnCmd("svn $action", array($selectedFile,$destFile), ''); + } + if ($actionName != "rename") { + $this->commitMessageParams .= "[".implode(",",$init["SELECTION"])."]"; + } + $this->commitChanges($actionName, $httpVars, [], $ctx); + if ($actionName != "rename") { + $this->commitChanges($actionName, array("dir" => $httpVars["dest"]), [], $ctx); + } + $this->logInfo("CopyMove/Rename (svn delegate)", array("files"=>$init["SELECTION"])); + + $mess = LocaleService::getMessages(); + + $serialStream = new SerializableResponseStream([new UserMessage($mess["meta.svn.5"]), new ReloadMessage()]); + $responseInterface = $responseInterface->withBody($serialStream); + } + + /** + * @param $actionName + * @param $httpVars + * @param $filesVars + * @param ContextInterface $ctx + * @throws \Exception + */ + public function deleteSelection(ServerRequestInterface $requestInterface, ResponseInterface &$responseInterface) + { + $actionName = $requestInterface->getAttribute("action"); + $httpVars = $requestInterface->getParsedBody(); + $ctx = $requestInterface->getAttribute("ctx"); + + $init = $this->initDirAndSelection($ctx, $httpVars, array(), true); + if (isSet($init["RECYCLE"]) && isSet($init["RECYCLE"]["action"]) && $init["RECYCLE"]["action"] != "delete") { + $httpVars["dest"] = TextEncoder::fromUTF8($init["RECYCLE"]["dest"]); + $updatedRequest = $requestInterface->withAttribute("action", "move")->withParsedBody($httpVars); + $this->copyOrMoveSelection($updatedRequest, $responseInterface); + + $userSelection = $init["ORIGINAL_SELECTION"]; + $files = $userSelection->getFiles(); + if ($actionName == "delete") { + foreach ($files as $file) { + RecycleBinManager::fileToRecycle($file); + } + } else if ($actionName == "restore") { + foreach ($files as $file) { + RecycleBinManager::deleteFromRecycle($file); + } + } + $this->commitChanges($actionName, array("dir" => RecycleBinManager::getRelativeRecycle()), [], $ctx); + return ; + } + foreach ($init["SELECTION"] as $selectedFile) { + $res = ExecSvnCmd('svn delete', $selectedFile, '--force'); + } + $this->commitMessageParams = "[".implode(",",$init["SELECTION"])."]"; + $this->commitChanges($actionName, $httpVars, [], $ctx); + $this->logInfo("Delete (svn delegate)", array("files"=>$init["SELECTION"])); + + $mess = LocaleService::getMessages(); + $serialStream = new SerializableResponseStream([new UserMessage($mess["meta.svn.51"]), new ReloadMessage()]); + $responseInterface = $responseInterface->withBody($serialStream); + } + + /** + * @param $actionName + * @param $httpVars + * @param $filesVars + * @param ContextInterface $ctx + * @throws \Exception + */ + public function commitChanges($actionName, $httpVars, $filesVars, ContextInterface $ctx) + { + if (is_array($httpVars)) { + $init = $this->initDirAndSelection($ctx, $httpVars); + $args = $init["DIR"]; + } else { + $args = $httpVars; + } + $status = ExecSvnCmd('svn status', $args); + if (trim(implode("", $status[IDX_STDOUT])) == "") { + return; + } + $command = "svn commit"; + $user = $ctx->getUser()->getId(); + $switches = "-m \"Pydio||$user||$actionName".(isSet($this->commitMessageParams)?"||".$this->commitMessageParams:"")."\""; + $res = ExecSvnCmd($command, $args, $switches); + if (is_file($args)) { + $res2 = ExecSvnCmd('svn update', dirname($args), ''); + } else if (is_dir($args)) { + $res2 = ExecSvnCmd('svn update', $args, ''); + } + } + /** + * + * @param AJXP_Node $ajxpNode + */ + public function extractMeta(&$ajxpNode) + { + //if(isSet($_SESSION["SVN_COMMAND_RUNNING"]) && $_SESSION["SVN_COMMAND_RUNNING"] === true) return ; + $realDir = dirname($ajxpNode->getRealFile()); + if (SvnManager::$svnListDir == $realDir) { + $entries = SvnManager::$svnListCache; + } else { + try { + SvnManager::$svnListDir = $realDir; + $entries = $this->svnListNode($realDir); + SvnManager::$svnListCache = $entries; + } catch (\Exception $e) { + $this->logError("ExtractMeta", $e->getMessage()); + } + } + $fileId = TextEncoder::toUTF8(basename($ajxpNode->getUrl())); + if (isSet($entries[$fileId])) { + $ajxpNode->mergeMetadata($entries[$fileId]); + } + } + + /** + * @param $realPath + * @param null $revision + * @return array + * @throws \Exception + */ + protected function svnListNode($realPath, $revision = null) + { + $command = 'svn list'; + $switches = '--xml'; + if ($revision != null) { + $switches = '--xml -r'.$revision; + } + $_SESSION["SVN_COMMAND_RUNNING"] = true; + //if(substr(strtolower(PHP_OS), 0, 3) == "win") session_write_close(); + try { + $res = ExecSvnCmd($command, $realPath, $switches); + } catch (\Exception $e) { + return array(); + } + //if(substr(strtolower(PHP_OS), 0, 3) == "win") session_start(); + unset($_SESSION["SVN_COMMAND_RUNNING"]); + $domDoc = new \DOMDocument(); + $domDoc->loadXML($res[IDX_STDOUT]); + $xPath = new \DOMXPath($domDoc); + $entriesList = $xPath->query("list/entry"); + $entries = array(); + foreach ($entriesList as $entry) { + $logEntry = array(); + $name = $xPath->query("name", $entry)->item(0)->nodeValue; + $logEntry["last_revision"] = $xPath->query("commit/@revision", $entry)->item(0)->value; + $logEntry["last_revision_author"] = $xPath->query("commit/author", $entry)->item(0)->nodeValue; + $logEntry["last_revision_date"] = $xPath->query("commit/date", $entry)->item(0)->nodeValue; + $logEntry["last_revision_size"] = $xPath->query("size", $entry)->item(0)->nodeValue; + $entries[$name] = $logEntry; + } + return $entries; + } + + + +} diff --git a/core/src/plugins/meta.svn/class.SVNLogger.js b/core/src/plugins/meta.svn/class.SVNLogger.js index 76c1819174..b0faef434b 100644 --- a/core/src/plugins/meta.svn/class.SVNLogger.js +++ b/core/src/plugins/meta.svn/class.SVNLogger.js @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . * Description : Simple display of SVN logs. */ Class.create("SVNLogger", { diff --git a/core/src/plugins/meta.svn/class.SvnManager.php b/core/src/plugins/meta.svn/class.SvnManager.php deleted file mode 100644 index d446d47148..0000000000 --- a/core/src/plugins/meta.svn/class.SvnManager.php +++ /dev/null @@ -1,402 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die( 'Access not allowed'); -define('SVNLIB_PATH', ''); -if (SVNLIB_PATH != "") { - putenv("LD_LIBRARY_PATH=".SVNLIB_PATH); -} -/** - * Uses svn command lines to extract version infos. Autocommit on change. - * @package AjaXplorer_Plugins - * @subpackage Meta - */ -class SvnManager extends AJXP_AbstractMetaSource -{ - private static $svnListDir; - private static $svnListCache; - private $commitMessageParams; - - public function init($options) - { - $this->options = $options; - // Do nothing - } - - public function initMeta($accessDriver) - { - require_once("svn_lib.inc.php"); - parent::initMeta($accessDriver); - parent::init($this->options); - } - - protected function initDirAndSelection($httpVars, $additionnalPathes = array(), $testRecycle = false) - { - $repo = $this->accessDriver->repository; - $repo->detectStreamWrapper(true); - $userSelection = new UserSelection($repo, $httpVars); - $urlBase = $userSelection->currentBaseUrl(); - $result = array(); - - if ($testRecycle) { - $recycle = $repo->getOption("RECYCLE_BIN"); - if ($recycle != "") { - RecycleBinManager::init($urlBase, "/".$recycle); - $result["RECYCLE"] = RecycleBinManager::filterActions($httpVars["get_action"], $userSelection, $httpVars["dir"], $httpVars); - // if necessary, check recycle was checked. - // We could use a hook instead here? Maybe the full recycle system - // could be turned into a plugin - $sessionKey = "AJXP_SVN_".$repo->getId()."_RECYCLE_CHECKED"; - if (isSet($_SESSION[$sessionKey])) { - $file = RecycleBinManager::getRelativeRecycle()."/".RecycleBinManager::getCacheFileName(); - $realFile = AJXP_MetaStreamWrapper::getRealFSReference($urlBase.$file); - $this->addIfNotVersionned($file, $realFile); - $_SESSION[$sessionKey] = true; - } - } - } - - $result["DIR"] = AJXP_MetaStreamWrapper::getRealFSReference($urlBase.AJXP_Utils::decodeSecureMagic($httpVars["dir"])); - $result["ORIGINAL_SELECTION"] = $userSelection; - $result["SELECTION"] = array(); - if (!$userSelection->isEmpty()) { - $files = $userSelection->getFiles(); - foreach ($files as $selected) { - $result["SELECTION"][] = AJXP_MetaStreamWrapper::getRealFSReference($urlBase.$selected); - } - } - foreach ($additionnalPathes as $parameter => $path) { - $result[$parameter] = AJXP_MetaStreamWrapper::getRealFSReference($urlBase.$path); - } - return $result; - } - - protected function addIfNotVersionned($repoFile, $realFile) - { - $error = false; - try { - //$res = ExecSvnCmd("svnversion", $realFile, ""); - $res = ExecSvnCmd("svn status ", $realFile); - } catch (Exception $e) { - $error = true; - } - if ($error || (count($res[IDX_STDOUT]) && substr($res[IDX_STDOUT][0],0,1) == "?")) { - $res2 = ExecSvnCmd("svn add", "$realFile"); - $this->commitMessageParams = "Recycle cache file"; - $this->commitChanges("ADD", array("dir" => dirname($repoFile)), array()); - } - } - - /** - * @param String $file URL of the file to commit (probably a metadata) - * @param AJXP_Node $ajxpNode Optionnal node to commit along. - */ - public function commitFile($file, $ajxpNode = null) - { - $repo = $this->accessDriver->repository; - $repo->detectStreamWrapper(); - $realFile = AJXP_MetaStreamWrapper::getRealFSReference($file); - - $res = ExecSvnCmd("svn status ", $realFile); - if (count($res[IDX_STDOUT]) && substr($res[IDX_STDOUT][0],0,1) == "?") { - $res2 = ExecSvnCmd("svn add", "$realFile"); - } - if ($ajxpNode != null) { - $nodeRealFile = AJXP_MetaStreamWrapper::getRealFSReference($ajxpNode->getUrl()); - try { - ExecSvnCmd("svn propset metachange ".time(), $nodeRealFile); - } catch (Exception $e) { - $this->commitChanges("COMMIT_META", $realFile, array()); - return; - } - // WILL COMMIT BOTH AT ONCE - $command = "svn commit"; - $user = AuthService::getLoggedUser()->getId(); - $switches = "-m \"Pydio||$user||COMMIT_META||file:".escapeshellarg($file)."\""; - ExecSvnCmd($command, array($realFile, $nodeRealFile), $switches); - ExecSvnCmd('svn update', dirname($nodeRealFile), ''); - } else { - $this->commitChanges("COMMIT_META", $realFile, array()); - } - } - - public function switchAction($actionName, $httpVars, $filesVars) - { - $init = $this->initDirAndSelection($httpVars); - if ($actionName == "svnlog") { - $res1 = ExecSvnCmd("svnversion", $init["DIR"]); - $test = trim(implode("", $res1[IDX_STDOUT])); - if (is_numeric($test)) { - $currentRev = $test; - } else if (strstr($test, ":")!==false && count(explode(":", $test))) { - $revRange = explode(":", $test); - } - $command = 'svn log'; - $switches = '--xml -rHEAD:0'; - $arg = $init["SELECTION"][0]; - $res = ExecSvnCmd($command, $arg, $switches); - AJXP_XMLWriter::header(); - $lines = explode(PHP_EOL, $res[IDX_STDOUT]); - array_shift($lines); - if (isSet($currentRev)) { - print("$currentRev"); - } else if (isSet($revRange)) { - print(""); - } - print(SystemTextEncoding::toUTF8(implode("", $lines), false)); - AJXP_XMLWriter::close(); - } else if ($actionName == "svndownload") { - $revision = $httpVars["revision"]; - $realFile = $init["SELECTION"][0]; - $entries = $this->svnListNode($realFile, $revision); - $keys = array_keys($entries); - $localName = $keys[0]; - $contentSize = 0; - if (isSet($entries[$localName]["last_revision_size"])) { - $contentSize = intval($entries[$localName]["last_revision_size"]); - } - // output directly the file! - header("Content-Type: application/force-download; name=\"".$localName."\""); - header("Content-Transfer-Encoding: binary"); - if($contentSize > 0) header("Content-Length: ".$contentSize); - header("Content-Disposition: attachment; filename=\"".$localName."\""); - header("Expires: 0"); - header("Cache-Control: no-cache, must-revalidate"); - header("Pragma: no-cache"); - if (preg_match('/ MSIE /',$_SERVER['HTTP_USER_AGENT'])) { - header("Pragma: public"); - header("Expires: 0"); - header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); - header("Cache-Control: private",false); - } - $realFile = escapeshellarg($realFile); - $revision = escapeshellarg($revision); - system( (SVNLIB_PATH!=""?SVNLIB_PATH."/":"") ."svn cat -r$revision $realFile"); - exit(0); - } else if ($actionName == "revert_file") { - - $revision = escapeshellarg($httpVars["revision"]); - $realFile = $init["SELECTION"][0]; - $compare = (isSet($httpVars["compare"]) && $httpVars["compare"] == "true"); - $escapedFile = escapeshellarg($realFile); - if ($compare) { - $ext = pathinfo($realFile, PATHINFO_EXTENSION); - $targetFile = preg_replace("/\.$ext$/", "-r$revision.$ext", $realFile); - system( (SVNLIB_PATH!=""?SVNLIB_PATH."/":"") ."svn cat -r$revision $escapedFile > ".escapeshellarg($targetFile)); - } else { - system( (SVNLIB_PATH!=""?SVNLIB_PATH."/":"") ."svn cat -r$revision $escapedFile > $escapedFile"); - $this->commitChanges($actionName, $realFile, array()); - } - - } else if ($actionName == "svnswitch") { - $revision = escapeshellarg($httpVars["revision"]); - ExecSvnCmd("svn update -r$revision ".$init["DIR"]); - } - } - - public function addSelection($actionName, $httpVars, $filesVars) - { - switch ($actionName) { - case "mkdir": - $init = $this->initDirAndSelection($httpVars, array("NEW_DIR" => AJXP_Utils::decodeSecureMagic($httpVars["dir"]."/".$httpVars["dirname"]))); - $res = ExecSvnCmd("svn add", $init["NEW_DIR"]); - $this->commitMessageParams = $httpVars["dirname"]; - break; - case "mkfile": - $init = $this->initDirAndSelection($httpVars, array("NEW_FILE" => AJXP_Utils::decodeSecureMagic($httpVars["dir"]."/".$httpVars["filename"]))); - $res = ExecSvnCmd("svn add", $init["NEW_FILE"]); - $this->commitMessageParams = $httpVars["filename"]; - break; - case "upload": - if (isSet($filesVars) && isSet($filesVars["userfile_0"]) && isSet($filesVars["userfile_0"]["name"])) { - $init = $this->initDirAndSelection($httpVars, array("NEW_FILE" => SystemTextEncoding::fromUTF8($httpVars["dir"])."/".$filesVars["userfile_0"]["name"])); - $res = ExecSvnCmd("svn status ", $init["NEW_FILE"]); - if (count($res[IDX_STDOUT]) && substr($res[IDX_STDOUT][0],0,1) == "?") { - $res = ExecSvnCmd("svn add", $init["NEW_FILE"]); - } else { - $res = true; - } - //$res = ExecSvnCmd("svn add", $init["NEW_FILE"]); - $this->commitMessageParams = $filesVars["userfile_0"]["name"]; - } - break; - } - if (isSet($res)) { - $this->commitChanges($actionName, $httpVars, $filesVars); - } - } - - public function copyOrMoveSelection($actionName, &$httpVars, $filesVars) - { - if ($actionName != "rename") { - $init = $this->initDirAndSelection($httpVars, array("DEST_DIR" => AJXP_Utils::decodeSecureMagic($httpVars["dest"]))); - $this->commitMessageParams = "To:".$httpVars["dest"].";items:"; - } else { - $init = $this->initDirAndSelection($httpVars, array(), true); - } - $this->logDebug("Entering SVN MAnager for action $actionName", $init); - $action = 'copy'; - if ($actionName == "move" || $actionName == "rename") { - $action = 'move'; - } - foreach ($init["SELECTION"] as $selectedFile) { - if ($actionName == "rename") { - $destFile = dirname($selectedFile)."/".AJXP_Utils::decodeSecureMagic($httpVars["filename_new"]); - $this->commitMessageParams = "To:".$httpVars["filename_new"].";item:".$httpVars["file"]; - } else { - $destFile = $init["DEST_DIR"]."/".basename($selectedFile); - } - $this->addIfNotVersionned(str_replace($init["DIR"], "", $selectedFile), $selectedFile); - $res = ExecSvnCmd("svn $action", array($selectedFile,$destFile), ''); - } - if ($actionName != "rename") { - $this->commitMessageParams .= "[".implode(",",$init["SELECTION"])."]"; - } - $this->commitChanges($actionName, $httpVars, $filesVars); - if ($actionName != "rename") { - $this->commitChanges($actionName, array("dir" => $httpVars["dest"]), $filesVars); - } - $this->logInfo("CopyMove/Rename (svn delegate)", array("files"=>$init["SELECTION"])); - - $mess = ConfService::getMessages(); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage($mess["meta.svn.5"], null); - AJXP_XMLWriter::reloadDataNode(); - AJXP_XMLWriter::close(); - } - - public function deleteSelection($actionName, &$httpVars, $filesVars) - { - $init = $this->initDirAndSelection($httpVars, array(), true); - if (isSet($init["RECYCLE"]) && isSet($init["RECYCLE"]["action"]) && $init["RECYCLE"]["action"] != "delete") { - $httpVars["dest"] = SystemTextEncoding::fromUTF8($init["RECYCLE"]["dest"]); - $this->copyOrMoveSelection("move", $httpVars, $filesVars); - $userSelection = $init["ORIGINAL_SELECTION"]; - $files = $userSelection->getFiles(); - if ($actionName == "delete") { - foreach ($files as $file) { - RecycleBinManager::fileToRecycle($file); - } - } else if ($actionName == "restore") { - foreach ($files as $file) { - RecycleBinManager::deleteFromRecycle($file); - } - } - $this->commitChanges($actionName, array("dir" => RecycleBinManager::getRelativeRecycle()), $filesVars); - return ; - } - foreach ($init["SELECTION"] as $selectedFile) { - $res = ExecSvnCmd('svn delete', $selectedFile, '--force'); - } - $this->commitMessageParams = "[".implode(",",$init["SELECTION"])."]"; - $this->commitChanges($actionName, $httpVars, $filesVars); - $this->logInfo("Delete (svn delegate)", array("files"=>$init["SELECTION"])); - - $mess = ConfService::getMessages(); - AJXP_XMLWriter::header(); - AJXP_XMLWriter::sendMessage($mess["meta.svn.51"], null); - AJXP_XMLWriter::reloadDataNode(); - AJXP_XMLWriter::close(); - } - - public function commitChanges($actionName, $httpVars, $filesVars) - { - if (is_array($httpVars)) { - $init = $this->initDirAndSelection($httpVars); - $args = $init["DIR"]; - } else { - $args = $httpVars; - } - $status = ExecSvnCmd('svn status', $args); - if (trim(implode("", $status[IDX_STDOUT])) == "") { - return; - } - $command = "svn commit"; - $user = AuthService::getLoggedUser()->getId(); - $switches = "-m \"Pydio||$user||$actionName".(isSet($this->commitMessageParams)?"||".$this->commitMessageParams:"")."\""; - $res = ExecSvnCmd($command, $args, $switches); - if (is_file($args)) { - $res2 = ExecSvnCmd('svn update', dirname($args), ''); - } else if (is_dir($args)) { - $res2 = ExecSvnCmd('svn update', $args, ''); - } - } - /** - * - * @param AJXP_Node $ajxpNode - */ - public function extractMeta(&$ajxpNode) - { - //if(isSet($_SESSION["SVN_COMMAND_RUNNING"]) && $_SESSION["SVN_COMMAND_RUNNING"] === true) return ; - $realDir = dirname($ajxpNode->getRealFile()); - if (SvnManager::$svnListDir == $realDir) { - $entries = SvnManager::$svnListCache; - } else { - try { - SvnManager::$svnListDir = $realDir; - $entries = $this->svnListNode($realDir); - SvnManager::$svnListCache = $entries; - } catch (Exception $e) { - $this->logError("ExtractMeta", $e->getMessage()); - } - } - $fileId = SystemTextEncoding::toUTF8(basename($ajxpNode->getUrl())); - if (isSet($entries[$fileId])) { - $ajxpNode->mergeMetadata($entries[$fileId]); - } - } - - protected function svnListNode($realPath, $revision = null) - { - $command = 'svn list'; - $switches = '--xml'; - if ($revision != null) { - $switches = '--xml -r'.$revision; - } - $_SESSION["SVN_COMMAND_RUNNING"] = true; - //if(substr(strtolower(PHP_OS), 0, 3) == "win") session_write_close(); - try { - $res = ExecSvnCmd($command, $realPath, $switches); - } catch (Exception $e) { - return array(); - } - //if(substr(strtolower(PHP_OS), 0, 3) == "win") session_start(); - unset($_SESSION["SVN_COMMAND_RUNNING"]); - $domDoc = new DOMDocument(); - $domDoc->loadXML($res[IDX_STDOUT]); - $xPath = new DOMXPath($domDoc); - $entriesList = $xPath->query("list/entry"); - $entries = array(); - foreach ($entriesList as $entry) { - $logEntry = array(); - $name = $xPath->query("name", $entry)->item(0)->nodeValue; - $logEntry["last_revision"] = $xPath->query("commit/@revision", $entry)->item(0)->value; - $logEntry["last_revision_author"] = $xPath->query("commit/author", $entry)->item(0)->nodeValue; - $logEntry["last_revision_date"] = $xPath->query("commit/date", $entry)->item(0)->nodeValue; - $logEntry["last_revision_size"] = $xPath->query("size", $entry)->item(0)->nodeValue; - $entries[$name] = $logEntry; - } - return $entries; - } - - - -} diff --git a/core/src/plugins/meta.svn/i18n/conf/cs.php b/core/src/plugins/meta.svn/i18n/conf/cs.php index 2395bdd424..16745c2dd2 100644 --- a/core/src/plugins/meta.svn/i18n/conf/cs.php +++ b/core/src/plugins/meta.svn/i18n/conf/cs.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Subversion Repository" => "Subversion repozitář", diff --git a/core/src/plugins/meta.svn/i18n/conf/de.php b/core/src/plugins/meta.svn/i18n/conf/de.php index 8b0e8ccc03..f9067d8128 100644 --- a/core/src/plugins/meta.svn/i18n/conf/de.php +++ b/core/src/plugins/meta.svn/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Subversion Repository" => "Subversion-Repository", diff --git a/core/src/plugins/meta.svn/i18n/conf/en.php b/core/src/plugins/meta.svn/i18n/conf/en.php index 95863a0413..c8ed318ef2 100644 --- a/core/src/plugins/meta.svn/i18n/conf/en.php +++ b/core/src/plugins/meta.svn/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Subversion Repository" => "Subversion Repository", diff --git a/core/src/plugins/meta.svn/i18n/conf/fr.php b/core/src/plugins/meta.svn/i18n/conf/fr.php index 07117fc39e..cf1fb8cfdc 100644 --- a/core/src/plugins/meta.svn/i18n/conf/fr.php +++ b/core/src/plugins/meta.svn/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Subversion Repository" => "Dépôt Subversion", diff --git a/core/src/plugins/meta.svn/i18n/conf/it.php b/core/src/plugins/meta.svn/i18n/conf/it.php index 19c4a80d4c..39954f5c29 100644 --- a/core/src/plugins/meta.svn/i18n/conf/it.php +++ b/core/src/plugins/meta.svn/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Subversion Repository" => "Repository Subversion", diff --git a/core/src/plugins/meta.svn/i18n/conf/pt.php b/core/src/plugins/meta.svn/i18n/conf/pt.php index ef998ef59c..f1d1d68185 100644 --- a/core/src/plugins/meta.svn/i18n/conf/pt.php +++ b/core/src/plugins/meta.svn/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Subversion Repository" => "Repositório de Sub-versões", diff --git a/core/src/plugins/meta.svn/i18n/fr.php b/core/src/plugins/meta.svn/i18n/fr.php index 14df7b014f..26028c840d 100644 --- a/core/src/plugins/meta.svn/i18n/fr.php +++ b/core/src/plugins/meta.svn/i18n/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "1" => "Versions", @@ -34,4 +34,4 @@ "5" => "The selected files/folders have been copied/moved (by SVN)", "51" => "The selected files/folders have been deleted (by SVN)", "61" => "Actions", -); \ No newline at end of file +); diff --git a/core/src/plugins/meta.svn/i18n/si.php b/core/src/plugins/meta.svn/i18n/si.php index f2ed504cf9..a0ab850971 100644 --- a/core/src/plugins/meta.svn/i18n/si.php +++ b/core/src/plugins/meta.svn/i18n/si.php @@ -1,23 +1,23 @@ -* This file is part of Pydio. -* -* Pydio 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. -* -* Pydio 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 Pydio. If not, see . -* -* The latest code can be found at . -*/ + * Copyright 2007-2013 Charles du Jeu - Abstrium SAS + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ // Slovenian translation: April 21 2011 by Vladimir Bohinc (vladimir.bohinc@gmail.com) // //--------------------------------------------------------------------------------------------------- @@ -27,14 +27,4 @@ "2" => "SVN dnevnik", "21" => "SVN dnevnik izbrane datoteke/mape", "3" => "Preklopi na to različico", -"31" => "Compare", -"32" => "Revert", -"33" => "This will create a new version of the file with the content of this previous version. Are you sure?", -"4" => "Versioning", -"41" => "Last Revision", -"42" => "Revision Date", -"43" => "Revision Author", -"5" => "The selected files/folders have been copied/moved (by SVN)", -"51" => "The selected files/folders have been deleted (by SVN)", -"61" => "Actions", -); \ No newline at end of file +); diff --git a/core/src/plugins/meta.svn/manifest.xml b/core/src/plugins/meta.svn/manifest.xml index 43a229859e..26d30f8d0c 100644 --- a/core/src/plugins/meta.svn/manifest.xml +++ b/core/src/plugins/meta.svn/manifest.xml @@ -1,6 +1,6 @@ - + @@ -168,6 +168,6 @@ - + diff --git a/core/src/plugins/meta.svn/svn_lib.inc.php b/core/src/plugins/meta.svn/svn_lib.inc.php index 76cb35bb11..268d73fb8f 100644 --- a/core/src/plugins/meta.svn/svn_lib.inc.php +++ b/core/src/plugins/meta.svn/svn_lib.inc.php @@ -630,7 +630,7 @@ function &ExecSvnCmd($cmd, $arg = '', $switches = ''){ if (is_array($arg)) { $arg = implode(" ", array_map("escapeshellarg", $arg)); } else { - $arg = escapeshellarg(SystemTextEncoding::toUTF8($arg)); + $arg = escapeshellarg(\Pydio\Core\Utils\TextEncoder::toUTF8($arg)); } $cmdline = (SVNLIB_PATH!=""?SVNLIB_PATH."/":"").$cmd." ".$switches." ".$arg; diff --git a/core/src/plugins/meta.syncable/ChangesTracker.php b/core/src/plugins/meta.syncable/ChangesTracker.php new file mode 100755 index 0000000000..6832d32900 --- /dev/null +++ b/core/src/plugins/meta.syncable/ChangesTracker.php @@ -0,0 +1,713 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Meta\Sync; + +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Filter\AJXP_Permission; +use Pydio\Core\Controller\CliRunner; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Controller\Controller; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Services\RepositoryService; +use Pydio\Core\Services\ApplicationState; +use Pydio\Core\Utils\DBHelper; +use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Core\Utils\Vars\OptionsHelper; +use Pydio\Core\Controller\HTMLWriter; +use Pydio\Core\PluginFramework\SqlTableProvider; +use Pydio\Log\Core\Logger; +use Pydio\Access\Meta\Core\AbstractMetaSource; +use Pydio\Tasks\Schedule; +use Pydio\Tasks\TaskService; +use \dibi as dibi; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Generates and caches and md5 hash of each file + * @package AjaXplorer_Plugins + * @subpackage Meta + */ +class ChangesTracker extends AbstractMetaSource implements SqlTableProvider +{ + private $sqlDriver; + + /** + * @param ContextInterface $ctx + * @param array $options + */ + public function init(ContextInterface $ctx, $options = []) + { + $this->sqlDriver = OptionsHelper::cleanDibiDriverParameters(["group_switch_value" => "core"]); + parent::init($ctx, $options); + } + + /** + * @param ContextInterface $ctx + * @param string $path + * @return bool + */ + protected function excludeFromSync($ctx, $path){ + $excludedExtensions = ["dlpart"]; + $ext = pathinfo($path, PATHINFO_EXTENSION); + if(!empty($ext) && in_array($ext, $excludedExtensions)){ + return true; + } + try{ + $ctx->getRepository()->getDriverInstance($ctx)->filterUserSelectionToHidden($ctx, [$path]); + }catch(\Exception $e){ + return true; + } + return false; + } + + /** + * @param ContextInterface $ctx + */ + protected function indexIsSync(ContextInterface $ctx){ + // Grab all folders mtime and compare them + $repoIdentifier = $this->computeIdentifier($ctx); + $res = dibi::query("SELECT [node_path],[mtime] FROM [ajxp_index] WHERE [md5] = %s AND [repository_identifier] = %s", 'directory', $repoIdentifier); + $modified = []; + + // REGISTER ROOT ANYWAY: WE PROBABLY CAN'T GET A "FILEMTIME" ON IT. + $mod = [ + "url" => $ctx->getUrlBase(), + "path" => "/", + "children" => [] + ]; + $children = dibi::query("SELECT [node_path],[mtime] FROM [ajxp_index] WHERE [repository_identifier] = %s AND [node_path] LIKE %s AND [node_path] NOT LIKE %s", + $repoIdentifier, "/%", "/%/%"); + foreach($children as $cRow){ + $cp = substr($cRow->node_path, 1); + if(empty($cp)) continue; + if(PHP_OS == 'Darwin'){ + $cp = \Normalizer::normalize($cp, \Normalizer::FORM_D); + } + $mod["children"][$cp] = $cRow->mtime; + } + $modified[] = $mod; + + clearstatcache(); + // CHECK ALL FOLDERS + foreach($res as $row){ + $path = $row->node_path; + $mtime = intval($row->mtime); + $url = $ctx->getUrlBase().$path; + $currentTime = @filemtime($url); + if($currentTime === false && !file_exists($url)) { + // Deleted folder! + $this->logDebug(__FUNCTION__, "Folder deleted directly on storage: ".$url); + $node = new AJXP_Node($url); + Controller::applyHook("node.change", [&$node, null, false], true); + continue; + } + if($currentTime > $mtime){ + $mod = [ + "url" => $url, + "path" => $path, + "children" => [], + "current_time" => $currentTime + ]; + $children = dibi::query("SELECT [node_path],[mtime],[md5] FROM [ajxp_index] WHERE [repository_identifier] = %s AND [node_path] LIKE %s AND [node_path] NOT LIKE %s", + $repoIdentifier, "$path/%", "$path/%/%"); + foreach($children as $cRow){ + $cp = substr($cRow->node_path, strlen($path)+1); + if(empty($cp)) continue; + if(PHP_OS == 'Darwin'){ + $cp = \Normalizer::normalize($cp, \Normalizer::FORM_D); + } + $mod["children"][$cp] = $cRow->mtime; + } + $modified[] = $mod; + } + } + + // NOW COMPUTE DIFFS + foreach($modified as $mod_data){ + $url = $mod_data["url"]; + $this->logDebug("Current folder is ".$url); + $current_time = $mod_data["current_time"]; + $currentChildren = $mod_data["children"]; + $files = scandir($url); + foreach($files as $f){ + if($f[0] == ".") continue; + $nodeUrl = $url."/".$f; + $this->logDebug(__FUNCTION__, "Scanning ".$nodeUrl); + $node = new AJXP_Node($nodeUrl); + // Ignore dirs modified time + // if(is_dir($nodeUrl) && $mod_data["path"] != "/") continue; + if(!isSet($currentChildren[$f])){ + if($this->excludeFromSync($ctx, $nodeUrl)){ + $this->logDebug(__FUNCTION__, "Excluding item detected on storage: ".$nodeUrl); + continue; + } + // New items detected + $this->logDebug(__FUNCTION__, "New item detected on storage: ".$nodeUrl); + Controller::applyHook("node.change", [null, &$node, false, true], true); + continue; + }else { + if(is_dir($nodeUrl)) continue; // Make sure to not trigger a recursive indexation here. + if(filemtime($nodeUrl) > $currentChildren[$f]){ + if($this->excludeFromSync($ctx, $nodeUrl)){ + $this->logDebug(__FUNCTION__, "Excluding item changed on storage: ".$nodeUrl); + continue; + } + // Changed! + $this->logDebug(__FUNCTION__, "Item modified directly on storage: ".$nodeUrl); + Controller::applyHook("node.change", [&$node, &$node, false], true); + } + } + } + foreach($currentChildren as $cPath => $mtime){ + $this->logDebug(__FUNCTION__, "Existing children ".$cPath); + if(!in_array($cPath, $files)){ + if($this->excludeFromSync($ctx, $url."/".$cPath)){ + $this->logDebug(__FUNCTION__, "Excluding item deleted on storage: ".$url."/".$cPath); + continue; + } + // Deleted + $this->logDebug(__FUNCTION__, "File deleted directly on storage: ".$url."/".$cPath); + $node = new AJXP_Node($url."/".$cPath); + Controller::applyHook("node.change", [&$node, null, false], true); + } + } + // Now "touch" parent directory + if(isSet($current_time)){ + dibi::query("UPDATE [ajxp_index] SET ", ["mtime" => $current_time], " WHERE [repository_identifier] = %s AND [node_path] = %s", $repoIdentifier, $mod_data["path"]); + } + } + } + + /** + * @param ContextInterface $ctx + * @param bool $check + * @return string + * @throws \Exception + */ + protected function getResyncTimestampFile(\Pydio\Core\Model\ContextInterface $ctx, $check = false){ + $repo = $ctx->getRepository(); + $sScope = $repo->securityScope(); + $suffix = "-".$repo->getId(); + if(!empty($sScope)) $suffix = "-".$ctx->getUser()->getId(); + $file = $this->getPluginCacheDir(true, $check)."/storage_changes_time".$suffix; + return $file; + } + + /** + * @param $actionName + * @param $httpVars + * @param $fileVars + * @param ContextInterface $contextInterface + */ + public function resyncAction($actionName, $httpVars, $fileVars, \Pydio\Core\Model\ContextInterface $contextInterface) + { + if (ConfService::backgroundActionsSupported() && !ApplicationState::sapiIsCli()) { + CliRunner::applyActionInBackground($contextInterface, "resync_storage", $httpVars); + }else{ + $file = $this->getResyncTimestampFile($contextInterface, true); + file_put_contents($file, time()); + $this->indexIsSync($contextInterface); + } + } + + /** + * @param $actionName + * @param $httpVars + * @param $fileVars + * @param ContextInterface $contextInterface + * @return null + * @throws \Exception + * @throws \Pydio\Core\Exception\PydioException + */ + public function switchActions($actionName, $httpVars, $fileVars, \Pydio\Core\Model\ContextInterface $contextInterface) + { + if($actionName != "changes" || !isSet($httpVars["seq_id"])) return false; + if(!dibi::isConnected()) { + dibi::connect($this->sqlDriver); + } + $filter = null; + $masks = []; + $currentRepo = $contextInterface->getRepository(); + Controller::applyHook("role.masks", [$contextInterface, &$masks, AJXP_Permission::READ]); + if(count($masks) == 1 && $masks[0] == "/"){ + $masks = []; + } + $recycle = $currentRepo->getContextOption($contextInterface, "RECYCLE_BIN"); + $recycle = (!empty($recycle)?$recycle:false); + + if($this->options["OBSERVE_STORAGE_CHANGES"] === true){ + // Do it every XX minutes + $minutes = 5; + if(isSet($this->options["OBSERVE_STORAGE_EVERY"])){ + $minutes = intval($this->options["OBSERVE_STORAGE_EVERY"]); + } + $file = $this->getResyncTimestampFile($contextInterface); + $last = 0; + if(is_file($file)) $last = intval(file_get_contents($file)); + if(time() - $last > $minutes * 60){ + $this->resyncAction("resync_storage", [], [], $contextInterface); + } + } + if($this->options["REQUIRES_INDEXATION"]){ + $task = TaskService::actionAsTask($contextInterface, "index", []); + $task->setActionLabel(LocaleService::getMessages(), 'core.index.3'); + TaskService::getInstance()->enqueueTask($task); + // Unset the REQUIRES_INDEXATION FLAG + $meta = $currentRepo->getContextOption($contextInterface, "META_SOURCES"); + unset($meta["meta.syncable"]["REQUIRES_INDEXATION"]); + $currentRepo->addOption("META_SOURCES", $meta); + RepositoryService::replaceRepository($currentRepo->getId(), $currentRepo); + } + + HTMLWriter::charsetHeader('application/json', 'UTF-8'); + $stream = isSet($httpVars["stream"]); + $separator = $stream ? "\n" : ","; + + + $veryLastSeq = intval(dibi::query("SELECT MAX([seq]) FROM [ajxp_changes]")->fetchSingle()); + $seqId = intval(InputFilter::sanitize($httpVars["seq_id"], InputFilter::SANITIZE_ALPHANUM)); + if($veryLastSeq > 0 && $seqId > $veryLastSeq){ + // This is not normal! Send a signal reload all changes from start. + if(!$stream) echo json_encode(['changes'=> [], 'last_seq'=>1]); + else echo 'LAST_SEQ:1'; + return null; + } + + + $ands = []; + $ands[] = ["[ajxp_changes].[repository_identifier] = %s", $this->computeIdentifier($contextInterface)]; + $ands[]= ["[seq] > %i", $seqId]; + if(isSet($httpVars["filter"])) { + $filter = InputFilter::decodeSecureMagic($httpVars["filter"]); + $filterLike = rtrim($filter, "/") . "/"; + $ands[] = ["[source] LIKE %like~ OR [target] LIKE %like~", $filterLike, $filterLike]; + } + if(count($masks)){ + $ors = []; + foreach($masks as $mask){ + $trimmedMask = rtrim($mask, "/") ; + $filterLike = $trimmedMask . "/"; + $ors[] = ["[source] LIKE %like~ OR [target] LIKE %like~", $filterLike, $filterLike]; + $ors[] = ["[source] = %s OR [target] = %s", $trimmedMask, $trimmedMask]; + } + if(count($ors)){ + $ands[] = ["%or", $ors]; + } + } + $res = dibi::query("SELECT + [seq] , [ajxp_changes].[repository_identifier] , [ajxp_changes].[node_id] , [type] , [source] , [target] , [ajxp_index].[bytesize], [ajxp_index].[md5], [ajxp_index].[mtime], [ajxp_index].[node_path] + FROM [ajxp_changes] + LEFT JOIN [ajxp_index] + ON [ajxp_changes].[node_id] = [ajxp_index].[node_id] + WHERE %and + ORDER BY [ajxp_changes].[node_id], [seq] ASC", + $ands); + + if(!$stream) echo '{"changes":['; + $previousNodeId = -1; + $previousRow = null; + $order = ["path"=>0, "content"=>1, "create"=>2, "delete"=>3]; + $relocateAttrs = ["bytesize", "md5", "mtime", "node_path", "repository_identifier"]; + $valuesSent = false; + foreach ($res as $row) { + $row->node = []; + foreach ($relocateAttrs as $att) { + $row->node[$att] = $row->$att; + unset($row->$att); + } + if(!empty($recycle)) $this->cancelRecycleNodes($row, $recycle); + if($this->pathOutOfMask($row->node["node_path"], $masks)){ + $row->node["node_path"] = false; + } + if(!isSet($httpVars["flatten"]) || $httpVars["flatten"] == "false"){ + + if(!$this->filterMasks($row, $masks) && !$this->filterRow($row, $filter)){ + if ($valuesSent) { + echo $separator; + } + echo json_encode($row); + $valuesSent = true; + } + + }else{ + + if ($row->node_id == $previousNodeId) { + $previousRow->target = $row->target; + $previousRow->seq = $row->seq; + // Specific case, maybe linked to recycle bin management + // A create should make a new node ID. + if ($row->type === "create" && $previousRow->type === "delete"){ + $previousRow->type = "create"; + }else if ($order[$row->type] > $order[$previousRow->type]) { + $previousRow->type = $row->type; + } + } else { + if (isSet($previousRow) && ($previousRow->source != $previousRow->target || $previousRow->type == "content")) { + if($this->filterMasks($previousRow, $masks) || $this->filterRow($previousRow, $filter)){ + $previousRow = $row; + $previousNodeId = $row->node_id; + $lastSeq = $row->seq; + continue; + } + if($valuesSent) echo $separator; + echo json_encode($previousRow); + $valuesSent = true; + } + $previousRow = $row; + $previousNodeId = $row->node_id; + } + if(!isSet($lastSeq) || $row->seq > $lastSeq){ + $lastSeq = $row->seq; + } + flush(); + } + } + + // SEND LAST ROW IF THERE IS ONE + if (isSet($previousRow) && ($previousRow->source != $previousRow->target || $previousRow->type == "content") + && !$this->pathOutOfMask($previousRow->target, $masks) && !$this->filterMasks($previousRow, $masks) && !$this->filterRow($previousRow, $filter)) { + + if($valuesSent) echo $separator; + echo json_encode($previousRow); + if (!isSet($lastSeq) || $previousRow->seq > $lastSeq){ + $lastSeq = $previousRow->seq; + } + $valuesSent = true; + } + + if (isSet($lastSeq)) { + if($stream){ + echo("\nLAST_SEQ:".$lastSeq); + }else{ + echo '], "last_seq":'.$lastSeq.'}'; + } + } else { + $lastSeq = dibi::query("SELECT MAX([seq]) FROM [ajxp_changes]")->fetchSingle(); + if(empty($lastSeq)) $lastSeq = 1; + if($stream){ + echo("\nLAST_SEQ:".$lastSeq); + }else{ + echo '], "last_seq":'.$lastSeq.'}'; + } + } + return null; + } + + /** + * @param $row + * @param $recycle + */ + protected function cancelRecycleNodes(&$row, $recycle){ + if($row->type != 'path') return; + if(strpos($row->source, '/'.$recycle) === 0){ + $row->source = 'NULL'; + $row->type = 'create'; + }else if(strpos($row->target, '/'.$recycle) === 0){ + $row->target = 'NULL'; + $row->type = 'delete'; + } + } + + /** + * @param $previousRow + * @param null $filter + * @return bool + */ + protected function filterRow(&$previousRow, $filter = null){ + if($filter == null) return false; + $srcInFilter = strpos($previousRow->source, $filter."/") === 0; + $targetInFilter = strpos($previousRow->target, $filter."/") === 0; + if(!$srcInFilter && !$targetInFilter){ + return true; + } + if($previousRow->type == 'path'){ + if(!$srcInFilter){ + $previousRow->type = 'create'; + $previousRow->source = 'NULL'; + }else if(!$targetInFilter){ + $previousRow->type = 'delete'; + $previousRow->target = 'NULL'; + } + } + if($srcInFilter){ + $previousRow->source = substr($previousRow->source, strlen($filter)); + } + if($targetInFilter){ + $previousRow->target = substr($previousRow->target, strlen($filter)); + } + if($previousRow->type != 'delete'){ + $previousRow->node['node_path'] = substr($previousRow->node['node_path'], strlen($filter)); + }else if(strpos($previousRow->node['node_path'], $filter) !== 0){ + $previousRow->node['node_path'] = false; + } + return false; + } + + /** + * @param $testPath + * @param array $masks + * @return bool + */ + protected function pathOutOfMask($testPath, $masks = []){ + if(!count($masks)) return false; + $regexps = []; + foreach($masks as $path){ + $regexps[] = '^'.preg_quote($path.'/', '/'); + } + $regexp = '/'.implode("|", $regexps).'/'; + $inMask = ($testPath == 'NULL') || $testPath === false || in_array($testPath, $masks) || preg_match($regexp, $testPath); + return !$inMask; + } + + /** + * @param $previousRow + * @param array $masks + * @return bool + */ + protected function filterMasks(&$previousRow, $masks = []){ + if(!count($masks)) return false; + + $srcInFilter = !$this->pathOutOfMask($previousRow->source, $masks); + $targetInFilter = !$this->pathOutOfMask($previousRow->target, $masks); + + if(!$srcInFilter && !$targetInFilter){ + return true; + } + if($previousRow->type == 'path'){ + if(!$srcInFilter){ + $previousRow->type = 'create'; + $previousRow->source = 'NULL'; + }else if(!$targetInFilter){ + $previousRow->type = 'delete'; + $previousRow->target = 'NULL'; + } + } + return false; + } + + /** + * @param ContextInterface $ctx + * @return String + */ + protected function computeIdentifier(ContextInterface $ctx) + { + $parts = [$ctx->getRepositoryId()]; + $repository = $ctx->getRepository(); + if ($repository->securityScope() == 'USER') { + $parts[] = $ctx->getUser()->getId(); + } else if ($repository->securityScope() == 'GROUP') { + $parts[] = $ctx->getUser()->getGroupPath(); + } + return implode("-", $parts); + } + + /** + * Called on workspace.after_delete event. Remove all references to this WS in the DB. + * Find all repo identifier exactly equal to $repoId , or like $repoId-% + * @param $repoId + */ + public function clearIndexForWorkspaceId($repoId){ + if(!dibi::isConnected()) { + dibi::connect($this->sqlDriver); + } + dibi::query("DELETE FROM [ajxp_index] WHERE [repository_identifier] = %s OR [repository_identifier] LIKE %like~", $repoId, $repoId."-"); + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $oldNode + * @param AJXP_Node $newNode + * @param bool $copy + */ + public function updateNodesIndex($oldNode = null, $newNode = null, $copy = false) + { + if(!dibi::isConnected()) { + dibi::connect($this->sqlDriver); + } + $refNode = ($oldNode !== null ? $oldNode : $newNode); + try { + if ($newNode != null && $this->excludeNode($newNode)) { + // CREATE + if($oldNode == null) { + Logger::debug("Ignoring ".$newNode->getUrl()." for indexation"); + return; + }else{ + Logger::debug("Target node is excluded, see it as a deletion: ".$newNode->getUrl()); + $newNode = null; + } + } + if ($newNode == null) { + $repoId = $this->computeIdentifier($refNode->getContext()); + // DELETE + $this->logDebug('DELETE', $oldNode->getUrl()); + dibi::query("DELETE FROM [ajxp_index] WHERE [node_path] LIKE %like~ AND [repository_identifier] = %s", $oldNode->getPath(), $repoId); + } else if ($oldNode == null || $copy) { + // CREATE + $stat = stat($newNode->getUrl()); + $newNode->setLeaf(!($stat['mode'] & 040000)); + $this->logDebug('INSERT', $newNode->getUrl()); + dibi::query("INSERT INTO [ajxp_index]", [ + "node_path" => $newNode->getPath(), + "bytesize" => $stat["size"], + "mtime" => $stat["mtime"], + "md5" => $newNode->isLeaf()? md5_file($newNode->getUrl()):"directory", + "repository_identifier" => $repoId = $this->computeIdentifier($refNode->getContext()) + ]); + if($copy && !$newNode->isLeaf()){ + // Make sure to index the content of this folder + $this->logInfo("Core.index", "Should reindex folder ".$newNode->getPath()); + $task = TaskService::actionAsTask($newNode->getContext(), "index", ["file" => $newNode->getPath()]); + $task->setActionLabel(LocaleService::getMessages(), 'core.index.3'); + $task->setSchedule(new Schedule(Schedule::TYPE_ONCE_DEFER)); + TaskService::getInstance()->enqueueTask($task); + } + } else { + $repoId = $this->computeIdentifier($refNode->getContext()); + if ($oldNode->getPath() == $newNode->getPath()) { + // CONTENT CHANGE + clearstatcache(); + $stat = stat($newNode->getUrl()); + $this->logDebug("Content changed", "current stat size is : " . $stat["size"]); + $this->logDebug('UPDATE CONTENT', $newNode->getUrl()); + dibi::query("UPDATE [ajxp_index] SET ", [ + "bytesize" => $stat["size"], + "mtime" => $stat["mtime"], + "md5" => md5_file($newNode->getUrl()) + ], "WHERE [node_path] = %s AND [repository_identifier] = %s", $oldNode->getPath(), $repoId); + try{ + $rowCount = dibi::getAffectedRows(); + if($rowCount === 0){ + $this->logError(__FUNCTION__, "There was an update event on a non-indexed node (".$newNode->getPath()."), creating index entry!"); + $this->updateNodesIndex(null, $newNode, false); + } + }catch (\Exception $e){} + + } else { + // PATH CHANGE ONLY + $newNode->loadNodeInfo(); + if ($newNode->isLeaf()) { + $this->logDebug('UPDATE LEAF PATH', $newNode->getUrl()); + dibi::query("UPDATE [ajxp_index] SET ", [ + "node_path" => $newNode->getPath(), + ], "WHERE [node_path] = %s AND [repository_identifier] = %s", $oldNode->getPath(), $repoId); + try{ + $rowCount = dibi::getAffectedRows(); + if($rowCount === 0){ + $this->logError(__FUNCTION__, "There was an update event on a non-indexed node (".$newNode->getPath()."), creating index entry!"); + $this->updateNodesIndex(null, $newNode, false); + } + }catch (\Exception $e){} + } else { + $this->logDebug('UPDATE FOLDER PATH', $newNode->getUrl()); + dibi::query("UPDATE [ajxp_index] SET [node_path]=REPLACE( REPLACE(CONCAT('$$$',[node_path]), CONCAT('$$$', %s), CONCAT('$$$', %s)) , '$$$', '') ", + $oldNode->getPath(), + $newNode->getPath(), + "WHERE ([node_path] = %s OR [node_path] LIKE %like~) AND [repository_identifier] = %s", + $oldNode->getPath(), + rtrim($oldNode->getPath(), '/') . '/', + $repoId); + try{ + $rowCount = dibi::getAffectedRows(); + if($rowCount === 0){ + $this->logError(__FUNCTION__, "There was an update event on a non-indexed folder (".$newNode->getPath()."), relaunching a recursive indexation!"); + $task = TaskService::actionAsTask($newNode->getContext(), "index", ["file" => $newNode->getPath()]); + $task->setActionLabel(LocaleService::getMessages(), 'core.index.3'); + $task->setSchedule(new Schedule(Schedule::TYPE_ONCE_DEFER)); + TaskService::getInstance()->enqueueTask($task); + + } + }catch (\Exception $e){} + } + + } + } + } catch (\Exception $e) { + Logger::error("[meta.syncable]", "Exception", $e->getTraceAsString()); + Logger::error("[meta.syncable]", "Indexation", $e->getMessage()); + } + + } + + /** + * @param AJXP_Node $node + * @param integer $result + */ + public function computeSizeRecursive(&$node, &$result){ + + $id = $this->computeIdentifier($node->getContext()); + $res = dibi::query("SELECT SUM([bytesize]) FROM [ajxp_index] WHERE [repository_identifier] = %s AND ([node_path] = %s OR [node_path] LIKE %s)", $id, $node->getPath(), rtrim($node->getPath(), "/")."/%"); + $result = floatval($res->fetchSingle()); + + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $node + * @return bool + */ + protected function excludeNode($node){ + // DO NOT EXCLUDE RECYCLE INDEXATION, OTHERWISE RESTORED DATA IS NOT DETECTED! + //$repo = $node->getRepository(); + //$recycle = $repo->getOption("RECYCLE_BIN"); + //if(!empty($recycle) && strpos($node->getPath(), "/".trim($recycle, "/")) === 0) return true; + + // Other exclusions conditions here? + return false; + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $node + */ + public function indexNode($node){ + // Create + $this->updateNodesIndex(null, $node, false); + } + + /** + * @param AJXP_Node $node + */ + public function clearIndexForNode($node){ + // Delete + $this->updateNodesIndex($node, null, false); + } + + /** + * @param ContextInterface $context + * @param array $metaSourcesOptions + */ + public function setIndexationRequiredFlag($context, &$metaSourcesOptions){ + if(isSet($metaSourcesOptions["meta.syncable"]) && (!isSet($metaSourcesOptions["meta.syncable"]["REPO_SYNCABLE"]) || $metaSourcesOptions["meta.syncable"]["REPO_SYNCABLE"] === true )){ + $metaSourcesOptions["meta.syncable"]["REQUIRES_INDEXATION"] = true; + } + } + + /** + * @param array $param + * @return string + * @throws \Exception + */ + public function installSQLTables($param) + { + $p = OptionsHelper::cleanDibiDriverParameters(isSet($param) && isSet($param["SQL_DRIVER"]) ? $param["SQL_DRIVER"] : $this->sqlDriver); + return DBHelper::runCreateTablesQuery($p, $this->getBaseDir() . "/create.sql"); + } + +} diff --git a/core/src/plugins/meta.syncable/class.ChangesTracker.php b/core/src/plugins/meta.syncable/class.ChangesTracker.php deleted file mode 100755 index a721e419bb..0000000000 --- a/core/src/plugins/meta.syncable/class.ChangesTracker.php +++ /dev/null @@ -1,635 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die('Access not allowed'); - -/** - * Generates and caches and md5 hash of each file - * @package AjaXplorer_Plugins - * @subpackage Meta - */ -class ChangesTracker extends AJXP_AbstractMetaSource implements SqlTableProvider -{ - private $sqlDriver; - - public function init($options) - { - $this->sqlDriver = AJXP_Utils::cleanDibiDriverParameters(array("group_switch_value" => "core")); - parent::init($options); - } - - protected function excludeFromSync($path){ - $excludedExtensions = array("dlpart"); - $ext = pathinfo($path, PATHINFO_EXTENSION); - if(!empty($ext) && in_array($ext, $excludedExtensions)){ - return true; - } - try{ - $this->accessDriver->filterUserSelectionToHidden(array($path)); - }catch(Exception $e){ - return true; - } - return false; - } - - protected function indexIsSync(){ - // Grab all folders mtime and compare them - $repoIdentifier = $this->computeIdentifier($this->accessDriver->repository); - $res = dibi::query("SELECT [node_path],[mtime] FROM [ajxp_index] WHERE [md5] = %s AND [repository_identifier] = %s", 'directory', $repoIdentifier); - $modified = array(); - - // REGISTER ROOT ANYWAY: WE PROBABLY CAN'T GET A "FILEMTIME" ON IT. - $mod = array( - "url" => $this->accessDriver->getResourceUrl(""), - "path" => "/", - "children" => array() - ); - $children = dibi::query("SELECT [node_path],[mtime] FROM [ajxp_index] WHERE [repository_identifier] = %s AND [node_path] LIKE %s AND [node_path] NOT LIKE %s", - $repoIdentifier, "/%", "/%/%"); - foreach($children as $cRow){ - $cp = substr($cRow->node_path, 1); - if(empty($cp)) continue; - if(PHP_OS == 'Darwin'){ - $cp = Normalizer::normalize($cp, Normalizer::FORM_D); - } - $mod["children"][$cp] = $cRow->mtime; - } - $modified[] = $mod; - - clearstatcache(); - // CHECK ALL FOLDERS - foreach($res as $row){ - $path = $row->node_path; - $mtime = intval($row->mtime); - $url = $this->accessDriver->getResourceUrl($path); - $currentTime = @filemtime($url); - if($currentTime === false && !file_exists($url)) { - // Deleted folder! - $this->logDebug(__FUNCTION__, "Folder deleted directly on storage: ".$url); - $node = new AJXP_Node($url); - AJXP_Controller::applyHook("node.change", array(&$node, null, false), true); - continue; - } - if($currentTime > $mtime){ - $mod = array( - "url" => $url, - "path" => $path, - "children" => array(), - "current_time" => $currentTime - ); - $children = dibi::query("SELECT [node_path],[mtime],[md5] FROM [ajxp_index] WHERE [repository_identifier] = %s AND [node_path] LIKE %s AND [node_path] NOT LIKE %s", - $repoIdentifier, "$path/%", "$path/%/%"); - foreach($children as $cRow){ - $cp = substr($cRow->node_path, strlen($path)+1); - if(empty($cp)) continue; - if(PHP_OS == 'Darwin'){ - $cp = Normalizer::normalize($cp, Normalizer::FORM_D); - } - $mod["children"][$cp] = $cRow->mtime; - } - $modified[] = $mod; - } - } - - // NOW COMPUTE DIFFS - foreach($modified as $mod_data){ - $url = $mod_data["url"]; - $this->logDebug("Current folder is ".$url); - $current_time = $mod_data["current_time"]; - $currentChildren = $mod_data["children"]; - $files = scandir($url); - foreach($files as $f){ - if($f[0] == ".") continue; - $nodeUrl = $url."/".$f; - $this->logDebug(__FUNCTION__, "Scanning ".$nodeUrl); - $node = new AJXP_Node($nodeUrl); - // Ignore dirs modified time - // if(is_dir($nodeUrl) && $mod_data["path"] != "/") continue; - if(!isSet($currentChildren[$f])){ - if($this->excludeFromSync($nodeUrl)){ - $this->logDebug(__FUNCTION__, "Excluding item detected on storage: ".$nodeUrl); - continue; - } - // New items detected - $this->logDebug(__FUNCTION__, "New item detected on storage: ".$nodeUrl); - AJXP_Controller::applyHook("node.change", array(null, &$node, false, true), true); - continue; - }else { - if(is_dir($nodeUrl)) continue; // Make sure to not trigger a recursive indexation here. - if(filemtime($nodeUrl) > $currentChildren[$f]){ - if($this->excludeFromSync($nodeUrl)){ - $this->logDebug(__FUNCTION__, "Excluding item changed on storage: ".$nodeUrl); - continue; - } - // Changed! - $this->logDebug(__FUNCTION__, "Item modified directly on storage: ".$nodeUrl); - AJXP_Controller::applyHook("node.change", array(&$node, &$node, false), true); - } - } - } - foreach($currentChildren as $cPath => $mtime){ - $this->logDebug(__FUNCTION__, "Existing children ".$cPath); - if(!in_array($cPath, $files)){ - if($this->excludeFromSync($url."/".$cPath)){ - $this->logDebug(__FUNCTION__, "Excluding item deleted on storage: ".$url."/".$cPath); - continue; - } - // Deleted - $this->logDebug(__FUNCTION__, "File deleted directly on storage: ".$url."/".$cPath); - $node = new AJXP_Node($url."/".$cPath); - AJXP_Controller::applyHook("node.change", array(&$node, null, false), true); - } - } - // Now "touch" parent directory - if(isSet($current_time)){ - dibi::query("UPDATE [ajxp_index] SET ", array("mtime" => $current_time), " WHERE [repository_identifier] = %s AND [node_path] = %s", $repoIdentifier, $mod_data["path"]); - } - } - } - - protected function getResyncTimestampFile($check = false){ - $repo = ConfService::getRepository(); - $sScope = $repo->securityScope(); - $suffix = "-".$repo->getId(); - if(!empty($sScope)) $suffix = "-".AuthService::getLoggedUser()->getId(); - $file = $this->getPluginCacheDir(true, $check)."/storage_changes_time".$suffix; - return $file; - } - - public function resyncAction($actionName, $httpVars, $fileVars) - { - if (ConfService::backgroundActionsSupported() && !ConfService::currentContextIsCommandLine()) { - AJXP_Controller::applyActionInBackground(ConfService::getRepository()->getId(), "resync_storage", $httpVars); - }else{ - $file = $this->getResyncTimestampFile(true); - file_put_contents($file, time()); - $this->indexIsSync(); - } - } - - public function switchActions($actionName, $httpVars, $fileVars) - { - if($actionName != "changes" || !isSet($httpVars["seq_id"])) return false; - if(!dibi::isConnected()) { - dibi::connect($this->sqlDriver); - } - $filter = null; - $masks = array(); - $currentRepo = $this->accessDriver->repository; - AJXP_Controller::applyHook("role.masks", array($currentRepo->getId(), &$masks, AJXP_Permission::READ)); - if(count($masks) == 1 && $masks[0] == "/"){ - $masks = array(); - } - $recycle = $currentRepo->getOption("RECYCLE_BIN"); - $recycle = (!empty($recycle)?$recycle:false); - - if($this->options["OBSERVE_STORAGE_CHANGES"] === true){ - // Do it every XX minutes - $minutes = 5; - if(isSet($this->options["OBSERVE_STORAGE_EVERY"])){ - $minutes = intval($this->options["OBSERVE_STORAGE_EVERY"]); - } - $file = $this->getResyncTimestampFile(); - $last = 0; - if(is_file($file)) $last = intval(file_get_contents($file)); - if(time() - $last > $minutes * 60){ - $this->resyncAction("resync_storage", array(), array()); - } - } - if($this->options["REQUIRES_INDEXATION"]){ - if (ConfService::backgroundActionsSupported()){ - AJXP_Controller::applyActionInBackground(ConfService::getRepository()->getId(), "index", array()); - }else{ - AJXP_Controller::findActionAndApply("index", array(), array()); - } - // Unset the REQUIRES_INDEXATION FLAG - $meta = $currentRepo->getOption("META_SOURCES"); - unset($meta["meta.syncable"]["REQUIRES_INDEXATION"]); - $currentRepo->addOption("META_SOURCES", $meta); - ConfService::replaceRepository($currentRepo->getId(), $currentRepo); - } - - HTMLWriter::charsetHeader('application/json', 'UTF-8'); - $stream = isSet($httpVars["stream"]); - $separator = $stream ? "\n" : ","; - - - $veryLastSeq = intval(dibi::query("SELECT MAX([seq]) FROM [ajxp_changes]")->fetchSingle()); - $seqId = intval(AJXP_Utils::sanitize($httpVars["seq_id"], AJXP_SANITIZE_ALPHANUM)); - if($veryLastSeq > 0 && $seqId > $veryLastSeq){ - // This is not normal! Send a signal reload all changes from start. - if(!$stream) echo json_encode(array('changes'=>array(), 'last_seq'=>1)); - else echo 'LAST_SEQ:1'; - return null; - } - - - $ands = array(); - $ands[] = array("[ajxp_changes].[repository_identifier] = %s", $this->computeIdentifier($currentRepo)); - $ands[]= array("[seq] > %i", $seqId); - if(isSet($httpVars["filter"])) { - $filter = AJXP_Utils::decodeSecureMagic($httpVars["filter"]); - $filterLike = rtrim($filter, "/") . "/"; - $ands[] = array("[source] LIKE %like~ OR [target] LIKE %like~", $filterLike, $filterLike); - } - if(count($masks)){ - $ors = array(); - foreach($masks as $mask){ - $trimmedMask = rtrim($mask, "/") ; - $filterLike = $trimmedMask . "/"; - $ors[] = array("[source] LIKE %like~ OR [target] LIKE %like~", $filterLike, $filterLike); - $ors[] = array("[source] = %s OR [target] = %s", $trimmedMask, $trimmedMask); - } - if(count($ors)){ - $ands[] = array("%or", $ors); - } - } - $res = dibi::query("SELECT - [seq] , [ajxp_changes].[repository_identifier] , [ajxp_changes].[node_id] , [type] , [source] , [target] , [ajxp_index].[bytesize], [ajxp_index].[md5], [ajxp_index].[mtime], [ajxp_index].[node_path] - FROM [ajxp_changes] - LEFT JOIN [ajxp_index] - ON [ajxp_changes].[node_id] = [ajxp_index].[node_id] - WHERE %and - ORDER BY [ajxp_changes].[node_id], [seq] ASC", - $ands); - - if(!$stream) echo '{"changes":['; - $previousNodeId = -1; - $previousRow = null; - $order = array("path"=>0, "content"=>1, "create"=>2, "delete"=>3); - $relocateAttrs = array("bytesize", "md5", "mtime", "node_path", "repository_identifier"); - $valuesSent = false; - foreach ($res as $row) { - $row->node = array(); - foreach ($relocateAttrs as $att) { - $row->node[$att] = $row->$att; - unset($row->$att); - } - if(!empty($recycle)) $this->cancelRecycleNodes($row, $recycle); - if($this->pathOutOfMask($row->node["node_path"], $masks)){ - $row->node["node_path"] = false; - } - if(!isSet($httpVars["flatten"]) || $httpVars["flatten"] == "false"){ - - if(!$this->filterMasks($row, $masks) && !$this->filterRow($row, $filter)){ - if ($valuesSent) { - echo $separator; - } - echo json_encode($row); - $valuesSent = true; - } - - }else{ - - if ($row->node_id == $previousNodeId) { - $previousRow->target = $row->target; - $previousRow->seq = $row->seq; - // Specific case, maybe linked to recycle bin management - // A create should make a new node ID. - if ($row->type === "create" && $previousRow->type === "delete"){ - $previousRow->type = "create"; - }else if ($order[$row->type] > $order[$previousRow->type]) { - $previousRow->type = $row->type; - } - } else { - if (isSet($previousRow) && ($previousRow->source != $previousRow->target || $previousRow->type == "content")) { - if($this->filterMasks($previousRow, $masks) || $this->filterRow($previousRow, $filter)){ - $previousRow = $row; - $previousNodeId = $row->node_id; - $lastSeq = $row->seq; - continue; - } - if($valuesSent) echo $separator; - echo json_encode($previousRow); - $valuesSent = true; - } - $previousRow = $row; - $previousNodeId = $row->node_id; - } - if(!isSet($lastSeq) || $row->seq > $lastSeq){ - $lastSeq = $row->seq; - } - flush(); - } - } - - // SEND LAST ROW IF THERE IS ONE - if (isSet($previousRow) && ($previousRow->source != $previousRow->target || $previousRow->type == "content") - && !$this->pathOutOfMask($previousRow->target, $masks) && !$this->filterMasks($previousRow, $masks) && !$this->filterRow($previousRow, $filter)) { - - if($valuesSent) echo $separator; - echo json_encode($previousRow); - if (!isSet($lastSeq) || $previousRow->seq > $lastSeq){ - $lastSeq = $previousRow->seq; - } - $valuesSent = true; - } - - if (isSet($lastSeq)) { - if($stream){ - echo("\nLAST_SEQ:".$lastSeq); - }else{ - echo '], "last_seq":'.$lastSeq.'}'; - } - } else { - $lastSeq = dibi::query("SELECT MAX([seq]) FROM [ajxp_changes]")->fetchSingle(); - if(empty($lastSeq)) $lastSeq = 1; - if($stream){ - echo("\nLAST_SEQ:".$lastSeq); - }else{ - echo '], "last_seq":'.$lastSeq.'}'; - } - } - return null; - } - - protected function cancelRecycleNodes(&$row, $recycle){ - if($row->type != 'path') return; - if(strpos($row->source, '/'.$recycle) === 0){ - $row->source = 'NULL'; - $row->type = 'create'; - }else if(strpos($row->target, '/'.$recycle) === 0){ - $row->target = 'NULL'; - $row->type = 'delete'; - } - } - - protected function filterRow(&$previousRow, $filter = null){ - if($filter == null) return false; - $srcInFilter = strpos($previousRow->source, $filter."/") === 0; - $targetInFilter = strpos($previousRow->target, $filter."/") === 0; - if(!$srcInFilter && !$targetInFilter){ - return true; - } - if($previousRow->type == 'path'){ - if(!$srcInFilter){ - $previousRow->type = 'create'; - $previousRow->source = 'NULL'; - }else if(!$targetInFilter){ - $previousRow->type = 'delete'; - $previousRow->target = 'NULL'; - } - } - if($srcInFilter){ - $previousRow->source = substr($previousRow->source, strlen($filter)); - } - if($targetInFilter){ - $previousRow->target = substr($previousRow->target, strlen($filter)); - } - if($previousRow->type != 'delete'){ - $previousRow->node['node_path'] = substr($previousRow->node['node_path'], strlen($filter)); - }else if(strpos($previousRow->node['node_path'], $filter) !== 0){ - $previousRow->node['node_path'] = false; - } - return false; - } - - protected function pathOutOfMask($testPath, $masks = array()){ - if(!count($masks)) return false; - $regexps = array(); - foreach($masks as $path){ - $regexps[] = '^'.preg_quote($path.'/', '/'); - } - $regexp = '/'.implode("|", $regexps).'/'; - $inMask = ($testPath == 'NULL') || $testPath === false || in_array($testPath, $masks) || preg_match($regexp, $testPath); - return !$inMask; - } - - protected function filterMasks(&$previousRow, $masks = array()){ - if(!count($masks)) return false; - - $srcInFilter = !$this->pathOutOfMask($previousRow->source, $masks); - $targetInFilter = !$this->pathOutOfMask($previousRow->target, $masks); - - if(!$srcInFilter && !$targetInFilter){ - return true; - } - if($previousRow->type == 'path'){ - if(!$srcInFilter){ - $previousRow->type = 'create'; - $previousRow->source = 'NULL'; - }else if(!$targetInFilter){ - $previousRow->type = 'delete'; - $previousRow->target = 'NULL'; - } - } - return false; - } - - /** - * @param Repository $repository - * @param null $resolveUserId - * @return String - */ - protected function computeIdentifier($repository, $resolveUserId = null) - { - $parts = array($repository->getId()); - if ($repository->securityScope() == 'USER') { - if($resolveUserId != null) { - $parts[] = $resolveUserId; - } else { - $parts[] = AuthService::getLoggedUser()->getId(); - } - } else if ($repository->securityScope() == 'GROUP') { - if($resolveUserId != null) { - $userObject = ConfService::getConfStorageImpl()->createUserObject($resolveUserId); - if($userObject != null) $parts[] = $userObject->getGroupPath(); - }else{ - $parts[] = AuthService::getLoggedUser()->getGroupPath(); - } - } - return implode("-", $parts); - } - - /** - * @param Repository $repository - * @return float - */ - public function getRepositorySpaceUsage($repository){ - $id = $this->computeIdentifier($repository); - $res = dibi::query("SELECT SUM([bytesize]) FROM [ajxp_index] WHERE [repository_identifier] = %s", $id); - return floatval($res->fetchSingle()); - } - - /** - * Called on workspace.after_delete event. Remove all references to this WS in the DB. - * Find all repo identifier exactly equal to $repoId , or like $repoId-% - * @param $repoId - */ - public function clearIndexForWorkspaceId($repoId){ - if(!dibi::isConnected()) { - dibi::connect($this->sqlDriver); - } - dibi::query("DELETE FROM [ajxp_index] WHERE [repository_identifier] = %s OR [repository_identifier] LIKE %like~", $repoId, $repoId."-"); - } - - /** - * @param AJXP_Node $oldNode - * @param AJXP_Node $newNode - * @param bool $copy - */ - public function updateNodesIndex($oldNode = null, $newNode = null, $copy = false) - { - if(!dibi::isConnected()) { - dibi::connect($this->sqlDriver); - } - //$this->logInfo("Syncable index", array($oldNode == null?'null':$oldNode->getUrl(), $newNode == null?'null':$newNode->getUrl())); - try { - if ($newNode != null && $this->excludeNode($newNode)) { - // CREATE - if($oldNode == null) { - AJXP_Logger::debug("Ignoring ".$newNode->getUrl()." for indexation"); - return; - }else{ - AJXP_Logger::debug("Target node is excluded, see it as a deletion: ".$newNode->getUrl()); - $newNode = null; - } - } - if ($newNode == null) { - $repoId = $this->computeIdentifier($oldNode->getRepository(), $oldNode->getUser()); - // DELETE - $this->logDebug('DELETE', $oldNode->getUrl()); - dibi::query("DELETE FROM [ajxp_index] WHERE [node_path] LIKE %like~ AND [repository_identifier] = %s", SystemTextEncoding::toUTF8($oldNode->getPath()), $repoId); - } else if ($oldNode == null || $copy) { - // CREATE - $stat = stat($newNode->getUrl()); - $newNode->setLeaf(!($stat['mode'] & 040000)); - $this->logDebug('INSERT', $newNode->getUrl()); - dibi::query("INSERT INTO [ajxp_index]", array( - "node_path" => SystemTextEncoding::toUTF8($newNode->getPath()), - "bytesize" => $stat["size"], - "mtime" => $stat["mtime"], - "md5" => $newNode->isLeaf()? md5_file($newNode->getUrl()):"directory", - "repository_identifier" => $repoId = $this->computeIdentifier($newNode->getRepository(), $newNode->getUser()) - )); - if($copy && !$newNode->isLeaf()){ - // Make sure to index the content of this file - AJXP_Controller::findActionAndApply("index", array("file" => $newNode->getPath()), array()); - } - } else { - $repoId = $this->computeIdentifier($oldNode->getRepository(), $oldNode->getUser()); - if ($oldNode->getPath() == $newNode->getPath()) { - // CONTENT CHANGE - clearstatcache(); - $stat = stat($newNode->getUrl()); - $this->logDebug("Content changed", "current stat size is : " . $stat["size"]); - $this->logDebug('UPDATE CONTENT', $newNode->getUrl()); - dibi::query("UPDATE [ajxp_index] SET ", array( - "bytesize" => $stat["size"], - "mtime" => $stat["mtime"], - "md5" => md5_file($newNode->getUrl()) - ), "WHERE [node_path] = %s AND [repository_identifier] = %s", SystemTextEncoding::toUTF8($oldNode->getPath()), $repoId); - try{ - $rowCount = dibi::getAffectedRows(); - if($rowCount === 0){ - $this->logError(__FUNCTION__, "There was an update event on a non-indexed node (".$newNode->getPath()."), creating index entry!"); - $this->updateNodesIndex(null, $newNode, false); - } - }catch (Exception $e){} - - } else { - // PATH CHANGE ONLY - $newNode->loadNodeInfo(); - if ($newNode->isLeaf()) { - $this->logDebug('UPDATE LEAF PATH', $newNode->getUrl()); - dibi::query("UPDATE [ajxp_index] SET ", array( - "node_path" => SystemTextEncoding::toUTF8($newNode->getPath()), - ), "WHERE [node_path] = %s AND [repository_identifier] = %s", SystemTextEncoding::toUTF8($oldNode->getPath()), $repoId); - try{ - $rowCount = dibi::getAffectedRows(); - if($rowCount === 0){ - $this->logError(__FUNCTION__, "There was an update event on a non-indexed node (".$newNode->getPath()."), creating index entry!"); - $this->updateNodesIndex(null, $newNode, false); - } - }catch (Exception $e){} - } else { - $this->logDebug('UPDATE FOLDER PATH', $newNode->getUrl()); - dibi::query("UPDATE [ajxp_index] SET [node_path]=REPLACE( REPLACE(CONCAT('$$$',[node_path]), CONCAT('$$$', %s), CONCAT('$$$', %s)) , '$$$', '') ", - SystemTextEncoding::toUTF8($oldNode->getPath()), - SystemTextEncoding::toUTF8($newNode->getPath()), - "WHERE ([node_path] = %s OR [node_path] LIKE %like~) AND [repository_identifier] = %s", - SystemTextEncoding::toUTF8($oldNode->getPath()), - rtrim(SystemTextEncoding::toUTF8($oldNode->getPath()), '/') . '/', - $repoId); - try{ - $rowCount = dibi::getAffectedRows(); - if($rowCount === 0){ - $this->logError(__FUNCTION__, "There was an update event on a non-indexed folder (".$newNode->getPath()."), relaunching a recursive indexation!"); - AJXP_Controller::findActionAndApply("index", array("file" => $newNode->getPath()), array()); - } - }catch (Exception $e){} - } - - } - } - } catch (Exception $e) { - AJXP_Logger::error("[meta.syncable]", "Exception", $e->getTraceAsString()); - AJXP_Logger::error("[meta.syncable]", "Indexation", $e->getMessage()); - } - - } - - /** - * @param AJXP_Node $node - * @return bool - */ - protected function excludeNode($node){ - // DO NOT EXCLUDE RECYCLE INDEXATION, OTHERWISE RESTORED DATA IS NOT DETECTED! - //$repo = $node->getRepository(); - //$recycle = $repo->getOption("RECYCLE_BIN"); - //if(!empty($recycle) && strpos($node->getPath(), "/".trim($recycle, "/")) === 0) return true; - - // Other exclusions conditions here? - return false; - } - - /** - * @param AJXP_Node $node - */ - public function indexNode($node){ - // Create - $this->updateNodesIndex(null, $node, false); - } - - /** - * @param AJXP_Node $node - */ - public function clearIndexForNode($node){ - // Delete - $this->updateNodesIndex($node, null, false); - } - - /** - * @param array $metaSourcesOptions - */ - public function setIndexationRequiredFlag(&$metaSourcesOptions){ - if(isSet($metaSourcesOptions["meta.syncable"]) && (!isSet($metaSourcesOptions["meta.syncable"]["REPO_SYNCABLE"]) || $metaSourcesOptions["meta.syncable"]["REPO_SYNCABLE"] === true )){ - $metaSourcesOptions["meta.syncable"]["REQUIRES_INDEXATION"] = true; - } - } - - public function installSQLTables($param) - { - $p = AJXP_Utils::cleanDibiDriverParameters(isSet($param) && isSet($param["SQL_DRIVER"])?$param["SQL_DRIVER"]:$this->sqlDriver); - return AJXP_Utils::runCreateTablesQuery($p, $this->getBaseDir()."/create.sql"); - } - -} diff --git a/core/src/plugins/meta.syncable/create.mysql b/core/src/plugins/meta.syncable/create.mysql index 2e92a2cb5a..26ad0403a0 100644 --- a/core/src/plugins/meta.syncable/create.mysql +++ b/core/src/plugins/meta.syncable/create.mysql @@ -35,4 +35,4 @@ DROP TRIGGER IF EXISTS `LOG_UPDATE`; CREATE TRIGGER `LOG_UPDATE` AFTER UPDATE ON `ajxp_index` FOR EACH ROW INSERT INTO ajxp_changes (repository_identifier, node_id,source,target,type) - VALUES (new.repository_identifier, new.node_id, old.node_path, new.node_path, CASE old.node_path = new.node_path WHEN true THEN 'content' ELSE 'path' END); + VALUES (new.repository_identifier, new.node_id, old.node_path, new.node_path, CASE old.node_path COLLATE utf8_bin = new.node_path COLLATE utf8_bin WHEN true THEN 'content' ELSE 'path' END); diff --git a/core/src/plugins/meta.syncable/i18n/conf/de.php b/core/src/plugins/meta.syncable/i18n/conf/de.php index 5d08ce6d49..c75c156ae3 100644 --- a/core/src/plugins/meta.syncable/i18n/conf/de.php +++ b/core/src/plugins/meta.syncable/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Syncable Workspace" => "Synchronisierbare Arbeitsumgebung", diff --git a/core/src/plugins/meta.syncable/i18n/conf/en.php b/core/src/plugins/meta.syncable/i18n/conf/en.php index 39486c6348..7040291169 100644 --- a/core/src/plugins/meta.syncable/i18n/conf/en.php +++ b/core/src/plugins/meta.syncable/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Syncable Workspace" => "Syncable Workspace", diff --git a/core/src/plugins/meta.syncable/i18n/conf/it.php b/core/src/plugins/meta.syncable/i18n/conf/it.php index ecb83664db..15a06e95a0 100644 --- a/core/src/plugins/meta.syncable/i18n/conf/it.php +++ b/core/src/plugins/meta.syncable/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Syncable Workspace" => "Workspace Sincronizzabile", diff --git a/core/src/plugins/meta.syncable/manifest.xml b/core/src/plugins/meta.syncable/manifest.xml index f7e593a4f5..e116458447 100644 --- a/core/src/plugins/meta.syncable/manifest.xml +++ b/core/src/plugins/meta.syncable/manifest.xml @@ -1,7 +1,7 @@ - + @@ -35,6 +35,7 @@ + diff --git a/core/src/plugins/meta.user/UserMetaManager.php b/core/src/plugins/meta.user/UserMetaManager.php new file mode 100644 index 0000000000..1efbf3307f --- /dev/null +++ b/core/src/plugins/meta.user/UserMetaManager.php @@ -0,0 +1,413 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Meta\UserGenerated; + +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Model\UserSelection; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Controller\Controller; +use Pydio\Core\Services\UsersService; +use Pydio\Core\Utils\Vars\InputFilter; + +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Access\Meta\Core\AbstractMetaSource; +use Pydio\Access\Metastore\Core\IMetaStoreProvider; +use Pydio\Core\Utils\Vars\StringHelper; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Simple metadata implementation, stored in hidden files inside the + * folders + * @package AjaXplorer_Plugins + * @subpackage Meta + */ +class UserMetaManager extends AbstractMetaSource +{ + /** + * @var IMetaStoreProvider + */ + protected $metaStore; + protected $fieldsAdditionalData = array(); + private $metaOptionsParsed = false; + + /** + * @param ContextInterface $ctx + * @param array $options + */ + public function init(ContextInterface $ctx, $options = []) + { + $this->options = $options; + // Do not call parent + } + + /** + * @param ContextInterface $ctx + * @param AbstractAccessDriver $accessDriver + * @throws PydioException + */ + public function initMeta(ContextInterface $ctx, AbstractAccessDriver $accessDriver) + { + parent::initMeta($ctx, $accessDriver); + + $store = PluginsService::getInstance($ctx)->getUniqueActivePluginForType("metastore"); + if ($store === false) { + throw new PydioException("The 'meta.user' plugin requires at least one active 'metastore' plugin"); + } + $this->metaStore = $store; + $this->metaStore->initMeta($ctx, $accessDriver); + + $def = $this->getMetaDefinition(); + foreach($def as $k => &$d){ + if(isSet($this->fieldsAdditionalData[$k])) $d["data"] = $this->fieldsAdditionalData[$k]; + } + $this->exposeConfigInManifest("meta_definitions", json_encode($def)); + if(!isSet($this->options["meta_visibility"])) $visibilities = array("visible"); + else $visibilities = explode(",", $this->options["meta_visibility"]); + $editButton = ''; + $u = $ctx->getUser(); + if($u != null && $u->canWrite($ctx->getRepositoryId())){ + $editButton = ''; + } + $cdataHead = '
      +
      '.$editButton.'AJXP_MESSAGE[meta.user.1]
      + '; + $cdataFoot = '
      '; + $cdataParts = ""; + + $selection = $this->getXPath()->query('registry_contributions/client_configs/component_config[@className="FilesList"]/columns'); + $contrib = $selection->item(0); + $even = false; + $searchables = array(); + $searchablesRenderers = array(); + $index = 0; + + foreach ($def as $key=> $data) { + $label = $data["label"]; + $fieldType = $data["type"]; + + if (isSet($visibilities[$index])) { + $lastVisibility = $visibilities[$index]; + } + $index ++; + $col = $this->manifestDoc->createElement("additional_column"); + $col->setAttribute("messageString", $label); + $col->setAttribute("attributeName", $key); + $col->setAttribute("sortType", "String"); + if(isSet($lastVisibility)) $col->setAttribute("defaultVisibilty", $lastVisibility); + switch ($fieldType) { + case "stars_rate": + $col->setAttribute("modifier", "MetaCellRenderer.prototype.starsRateFilter"); + $col->setAttribute("sortType", "CellSorterValue"); + $searchables[$key] = $label; + $searchablesRenderers[$key] = "MetaCellRenderer.prototype.formPanelStars"; + break; + case "css_label": + $col->setAttribute("modifier", "MetaCellRenderer.prototype.cssLabelsFilter"); + $col->setAttribute("sortType", "CellSorterValue"); + $searchables[$key] = $label; + $searchablesRenderers[$key] = "MetaCellRenderer.prototype.formPanelCssLabels"; + break; + case "textarea": + $searchables[$key] = $label; + break; + case "string": + $searchables[$key] = $label; + break; + case "choice": + $searchables[$key] = $label; + $col->setAttribute("modifier", "MetaCellRenderer.prototype.selectorsFilter"); + $col->setAttribute("sortType", "CellSorterValue"); + $col->setAttribute("metaAdditional", $this->fieldsAdditionalData[$key]); + $searchablesRenderers[$key] = "MetaCellRenderer.prototype.formPanelSelectorFilter"; + break; + case "tags": + $searchables[$key] = $label; + $searchablesRenderers[$key] = "MetaCellRenderer.prototype.formPanelTags"; + break; + default: + break; + } + $contrib->appendChild($col); + $trClass = ($even?" class=\"even infoPanelRow\"":" class=\"infoPanelRow\""); + $even = !$even; + $cdataParts .= '
      '.$label.'
      #{'.$key.'}
      '; + } + + $selection = $this->getXPath()->query('registry_contributions/client_configs/component_config[@className="InfoPanel"]/infoPanelExtension'); + $contrib = $selection->item(0); + $contrib->setAttribute("attributes", implode(",", array_keys($def))); + if (!empty($this->fieldsAdditionalData)) { + $contrib->setAttribute("metaAdditional", json_encode($this->fieldsAdditionalData)); + } + $contrib->setAttribute("modifier", "MetaCellRenderer.prototype.infoPanelModifier"); + + $htmlSel = $this->getXPath()->query('html', $contrib); + $html = $htmlSel->item(0); + $cdata = $this->manifestDoc->createCDATASection($cdataHead . $cdataParts . $cdataFoot); + $html->appendChild($cdata); + + $selection = $this->getXPath()->query('registry_contributions/client_configs/template_part[@ajxpClass="SearchEngine"]'); + foreach ($selection as $tag) { + $v = $tag->attributes->getNamedItem("ajxpOptions")->nodeValue; + $metaV = count($searchables)? '"metaColumns":'.json_encode($searchables): ""; + if (count($searchablesRenderers)) { + $metaV .= ',"metaColumnsRenderers":'.json_encode($searchablesRenderers); + } + if (!empty($v) && trim($v) != "{}" && !empty($metaV)) { + $v = str_replace("}", ", ".$metaV."}", $v); + } else { + $v = "{".$metaV."}"; + } + $tag->setAttribute("ajxpOptions", $v); + } + + parent::init($ctx, $this->options); + + } + + /** + * @return array + */ + protected function getMetaDefinition() + { + if (!$this->metaOptionsParsed) { + if (!isSet($this->options["meta_types"]) && isSet($this->options["meta_fields"])) { + // Get type from name + $val = $this->options["meta_fields"]; + if($val == "stars_rate") $this->options["meta_types"].="stars_rate"; + else if($val == "css_label") $this->options["meta_types"].="css_label"; + else if(substr($val, 0,5) == "area_") $this->options["meta_types"].="textarea"; + else $this->options["meta_types"].="string"; + } + if(!empty($this->options["meta_additional"])){ + $this->fieldsAdditionalData[$this->options["meta_fields"]] = $this->options["meta_additional"]; + } + foreach ($this->options as $key => $val) { + $matches = array(); + if (preg_match('/^meta_fields_(.*)$/', $key, $matches) != 0) { + $repIndex = $matches[1]; + $this->options["meta_fields"].=",".$val; + $this->options["meta_labels"].=",".$this->options["meta_labels_".$repIndex]; + if (!empty($this->options["meta_additional_".$repIndex])) { + $this->fieldsAdditionalData[$val] = $this->options["meta_additional_".$repIndex]; + } + if (isSet($this->options["meta_types_".$repIndex])) { + $this->options["meta_types"].=",".$this->options["meta_types_".$repIndex]; + } else { + // Get type from name + if($val == "stars_rate") $this->options["meta_types"].=","."stars_rate"; + else if($val == "css_label") $this->options["meta_types"].=","."css_label"; + else if(substr($val,0,5) == "area_") $this->options["meta_types"].=","."textarea"; + else $this->options["meta_types"].=","."string"; + } + if (isSet($this->options["meta_visibility_".$repIndex]) && isSet($this->options["meta_visibility"])) { + $this->options["meta_visibility"].=",".$this->options["meta_visibility_".$repIndex]; + } + } + } + $this->metaOptionsParsed = true; + } + + $fields = $this->options["meta_fields"]; + $arrF = explode(",", $fields); + $labels = $this->options["meta_labels"]; + $arrL = explode(",", $labels); + $arrT = explode(",", $this->options["meta_types"]); + + $result = array(); + foreach ($arrF as $index => $value) { + //make sure value does not contain spaces or things like that + $value = StringHelper::slugify($value); + if (isSet($arrL[$index])) { + $result[$value] = array("label" => $arrL[$index], "type" => $arrT[$index]); + } else { + $result[$value] = array("label" => $value, "type" => $arrT[$index]); + } + } + return $result; + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $requestInterface + * @param \Psr\Http\Message\ResponseInterface $responseInterface + * @throws \Exception + */ + public function editMeta(\Psr\Http\Message\ServerRequestInterface &$requestInterface, \Psr\Http\Message\ResponseInterface &$responseInterface) + { + /** @var ContextInterface $ctx */ + $ctx = $requestInterface->getAttribute("ctx"); + $httpVars = $requestInterface->getParsedBody(); + if ($ctx->getRepository()->getDriverInstance($ctx) instanceof \Pydio\Access\Driver\StreamProvider\FS\DemoAccessDriver) { + throw new \Exception("Write actions are disabled in demo mode!"); + } + $user = $ctx->getUser(); + + if (!UsersService::usersEnabled() && $user!=null && !$user->canWrite($ctx->getRepositoryId())) { + throw new \Exception("You have no right on this action."); + } + $selection = UserSelection::fromContext($ctx, $httpVars); + + $nodes = $selection->buildNodes(); + $nodesDiffs = new \Pydio\Access\Core\Model\NodesDiff(); + $def = $this->getMetaDefinition(); + foreach($nodes as $ajxpNode){ + + $newValues = array(); + if(!is_writable($ajxpNode->getUrl())){ + throw new \Exception("You are not allowed to perform this action"); + } + Controller::applyHook("node.before_change", array(&$ajxpNode)); + foreach ($def as $key => $data) { + if (isSet($httpVars[$key])) { + $newValues[$key] = InputFilter::decodeSecureMagic($httpVars[$key]); + if($data["type"] == "tags"){ + $this->updateTags($ctx, InputFilter::decodeSecureMagic($httpVars[$key])); + } + } else { + if (!isset($original)) { + $original = $ajxpNode->retrieveMetadata("users_meta", false, AJXP_METADATA_SCOPE_GLOBAL); + } + if (isSet($original) && isset($original[$key])) { + $newValues[$key] = $original[$key]; + } + } + } + $ajxpNode->setMetadata("users_meta", $newValues, false, AJXP_METADATA_SCOPE_GLOBAL); + Controller::applyHook("node.meta_change", array($ajxpNode)); + $ajxpNode->loadNodeInfo(true, false, "all"); + $nodesDiffs->update($ajxpNode); + + } + $respStream = new \Pydio\Core\Http\Response\SerializableResponseStream(); + $responseInterface = $responseInterface->withBody($respStream); + $respStream->addChunk($nodesDiffs); + } + + /** + * + * @param AJXP_Node $ajxpNode + * @param bool $contextNode + * @param bool $details + * @return void + */ + public function extractMeta(&$ajxpNode, $contextNode = false, $details = false) + { + $metadata = $ajxpNode->retrieveMetadata("users_meta", false, AJXP_METADATA_SCOPE_GLOBAL); + if(empty($metadata)) $metadata = array(); + $ajxpNode->mergeMetadata($metadata); + + } + + /** + * + * @param \Pydio\Access\Core\Model\AJXP_Node $oldNode + * @param AJXP_Node $newNode + * @param Boolean $copy + */ + public function updateMetaLocation($oldNode, $newNode = null, $copy = false) + { + $defs = $this->getMetaDefinition(); + $updateField = $createField = null; + foreach($defs as $f => $data){ + if($data["type"] == "updater") $updateField = $f; + else if($data["type"] == "creator") $createField = $f; + } + $valuesUpdate = (isSet($updateField) || isSet($createField)); + $currentUser = null; + if($valuesUpdate){ + $refNode = ($oldNode !== null ? $oldNode : $newNode); + $currentUser = $refNode->getUserId(); + } + + if($oldNode == null && !$valuesUpdate) return; + if(!$copy && !$valuesUpdate && $this->metaStore->inherentMetaMove()) return; + + if($oldNode == null){ + $oldMeta = $this->metaStore->retrieveMetadata($newNode, "users_meta", false, AJXP_METADATA_SCOPE_GLOBAL); + }else{ + $oldMeta = $this->metaStore->retrieveMetadata($oldNode, "users_meta", false, AJXP_METADATA_SCOPE_GLOBAL); + } + if($valuesUpdate){ + if(isSet($updateField))$oldMeta[$updateField] = $currentUser; + if(isSet($createField) && $oldNode == null) $oldMeta[$createField] = $currentUser; + } + if (!count($oldMeta)) { + return; + } + // If it's a move or a delete, delete old data + if ($oldNode != null && !$copy) { + $this->metaStore->removeMetadata($oldNode, "users_meta", false, AJXP_METADATA_SCOPE_GLOBAL); + } + // If copy or move, copy data. + if ($newNode != null) { + $this->metaStore->setMetadata($newNode, "users_meta", $oldMeta, false, AJXP_METADATA_SCOPE_GLOBAL); + } + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $requestInterface + * @param \Psr\Http\Message\ResponseInterface $responseInterface + * @return \Psr\Http\Message\ResponseInterface|\Zend\Diactoros\Response\JsonResponse + */ + public function listTags(\Psr\Http\Message\ServerRequestInterface $requestInterface, \Psr\Http\Message\ResponseInterface &$responseInterface){ + + $tags = $this->loadTags($requestInterface->getAttribute("ctx")); + if(empty($tags)) $tags = array(); + $responseInterface = new \Zend\Diactoros\Response\JsonResponse($tags); + return $responseInterface; + + } + + /** + * @param ContextInterface $ctx + * @return array + */ + protected function loadTags(ContextInterface $ctx){ + + $store = ConfService::getConfStorageImpl(); + if(!($store instanceof \Pydio\Conf\Sql\SqlConfDriver)) return array(); + $data = array(); + $store->simpleStoreGet("meta_user_tags", $ctx->getRepositoryId(), "serial", $data); + return $data; + + } + + /** + * @param ContextInterface $ctx + * @param $tagString + * @throws \Exception + */ + protected function updateTags(ContextInterface $ctx, $tagString){ + + $store = ConfService::getConfStorageImpl(); + if(!($store instanceof \Pydio\Conf\Sql\SqlConfDriver)) return; + $tags = $this->loadTags($ctx); + $tags = array_merge($tags, array_map("trim", explode(",", $tagString))); + $tags = array_unique($tags); + $store->simpleStoreSet("meta_user_tags", $ctx->getRepositoryId(), array_values($tags), "serial"); + + } + +} diff --git a/core/src/plugins/meta.user/class.MetaCellRenderer.js b/core/src/plugins/meta.user/class.MetaCellRenderer.js index 30931ccf3c..311a8f8d4b 100644 --- a/core/src/plugins/meta.user/class.MetaCellRenderer.js +++ b/core/src/plugins/meta.user/class.MetaCellRenderer.js @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . * Description : Static class for renderers */ Class.create("MetaCellRenderer", { @@ -389,13 +389,14 @@ Class.create("MetaCellRenderer", { htmlElement.select('[data-metatype]').each(function(td){ var metaType = td.readAttribute("data-metatype"); var metaName = td.id.replace(/^ip_/, ''); + var value; switch(metaType){ case "stars_rate": - var value = parseInt(td.innerHTML); + value = parseInt(td.innerHTML); td.update(this.createStars(value, null, metaName)); break; case "css_label": - var value = td.innerHTML.strip(); + value = td.innerHTML.strip(); var rule = this.findCssRule(value); if(rule){ td.addClassName(rule.cssClass); @@ -406,14 +407,14 @@ Class.create("MetaCellRenderer", { if(MetaCellRenderer.staticMetadataCache && MetaCellRenderer.staticMetadataCache.get(metaName)){ var selectorValues = MetaCellRenderer.staticMetadataCache.get(metaName); if(!selectorValues) break; - var value = td.innerHTML.strip(); + value = td.innerHTML.strip(); if(selectorValues[value]){ td.update(selectorValues[value]); } } break; case "tags": - var value = td.innerHTML.strip(); + value = td.innerHTML.strip(); this.displayTagsAsBlocks(metaName, td, value, ajxpNode); break; case "text": @@ -438,7 +439,7 @@ Class.create("MetaCellRenderer", { }, linkEditableDiv : function(div){ - div.saver = new Element("img", {src:"plugins/gui.ajax/res/themes/umbra/images/actions/22/dialog_ok_apply.png"}).setStyle({ + div.saver = new Element("img", {src:"plugins/gui.ajax/res/themes/orbit/images/actions/22/dialog_ok_apply.png"}).setStyle({ float:"left", width: "22px", height:"22px", diff --git a/core/src/plugins/meta.user/class.UserMetaManager.php b/core/src/plugins/meta.user/class.UserMetaManager.php deleted file mode 100644 index 31b21ffd9b..0000000000 --- a/core/src/plugins/meta.user/class.UserMetaManager.php +++ /dev/null @@ -1,357 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Simple metadata implementation, stored in hidden files inside the - * folders - * @package AjaXplorer_Plugins - * @subpackage Meta - */ -class UserMetaManager extends AJXP_AbstractMetaSource -{ - /** - * @var MetaStoreProvider - */ - protected $metaStore; - protected $fieldsAdditionalData = array(); - private $metaOptionsParsed = false; - - public function init($options) - { - $this->options = $options; - // Do nothing - } - - public function initMeta($accessDriver) - { - parent::initMeta($accessDriver); - - $store = AJXP_PluginsService::getInstance()->getUniqueActivePluginForType("metastore"); - if ($store === false) { - throw new Exception("The 'meta.user' plugin requires at least one active 'metastore' plugin"); - } - $this->metaStore = $store; - $this->metaStore->initMeta($accessDriver); - - //$messages = ConfService::getMessages(); - $def = $this->getMetaDefinition(); - foreach($def as $k => &$d){ - if(isSet($this->fieldsAdditionalData[$k])) $d["data"] = $this->fieldsAdditionalData[$k]; - } - $this->exposeConfigInManifest("meta_definitions", json_encode($def)); - if(!isSet($this->options["meta_visibility"])) $visibilities = array("visible"); - else $visibilities = explode(",", $this->options["meta_visibility"]); - $editButton = ''; - $u = AuthService::getLoggedUser(); - if($u != null && $u->canWrite($this->accessDriver->repository->getId())){ - $editButton = ''; - } - $cdataHead = '
      -
      '.$editButton.'AJXP_MESSAGE[meta.user.1]
      - '; - $cdataFoot = '
      '; - $cdataParts = ""; - - $selection = $this->getXPath()->query('registry_contributions/client_configs/component_config[@className="FilesList"]/columns'); - $contrib = $selection->item(0); - $even = false; - $searchables = array(); - $searchablesRenderers = array(); - $index = 0; - $fieldType = "text"; - foreach ($def as $key=> $data) { - $label = $data["label"]; - $fieldType = $data["type"]; - - if (isSet($visibilities[$index])) { - $lastVisibility = $visibilities[$index]; - } - $index ++; - $col = $this->manifestDoc->createElement("additional_column"); - $col->setAttribute("messageString", $label); - $col->setAttribute("attributeName", $key); - $col->setAttribute("sortType", "String"); - if(isSet($lastVisibility)) $col->setAttribute("defaultVisibilty", $lastVisibility); - switch ($fieldType) { - case "stars_rate": - $col->setAttribute("modifier", "MetaCellRenderer.prototype.starsRateFilter"); - $col->setAttribute("sortType", "CellSorterValue"); - $searchables[$key] = $label; - $searchablesRenderers[$key] = "MetaCellRenderer.prototype.formPanelStars"; - break; - case "css_label": - $col->setAttribute("modifier", "MetaCellRenderer.prototype.cssLabelsFilter"); - $col->setAttribute("sortType", "CellSorterValue"); - $searchables[$key] = $label; - $searchablesRenderers[$key] = "MetaCellRenderer.prototype.formPanelCssLabels"; - break; - case "textarea": - $searchables[$key] = $label; - break; - case "string": - $searchables[$key] = $label; - break; - case "choice": - $searchables[$key] = $label; - $col->setAttribute("modifier", "MetaCellRenderer.prototype.selectorsFilter"); - $col->setAttribute("sortType", "CellSorterValue"); - $col->setAttribute("metaAdditional", $this->fieldsAdditionalData[$key]); - $searchablesRenderers[$key] = "MetaCellRenderer.prototype.formPanelSelectorFilter"; - break; - case "tags": - $searchables[$key] = $label; - $searchablesRenderers[$key] = "MetaCellRenderer.prototype.formPanelTags"; - break; - default: - break; - } - $contrib->appendChild($col); - $trClass = ($even?" class=\"even infoPanelRow\"":" class=\"infoPanelRow\""); - $even = !$even; - $cdataParts .= '
      '.$label.'
      #{'.$key.'}
      '; - } - - $selection = $this->getXPath()->query('registry_contributions/client_configs/component_config[@className="InfoPanel"]/infoPanelExtension'); - $contrib = $selection->item(0); - $contrib->setAttribute("attributes", implode(",", array_keys($def))); - if (!empty($this->fieldsAdditionalData)) { - $contrib->setAttribute("metaAdditional", json_encode($this->fieldsAdditionalData)); - } - $contrib->setAttribute("modifier", "MetaCellRenderer.prototype.infoPanelModifier"); - - $htmlSel = $this->getXPath()->query('html', $contrib); - $html = $htmlSel->item(0); - $cdata = $this->manifestDoc->createCDATASection($cdataHead . $cdataParts . $cdataFoot); - $html->appendChild($cdata); - - $selection = $this->getXPath()->query('registry_contributions/client_configs/template_part[@ajxpClass="SearchEngine"]'); - foreach ($selection as $tag) { - $v = $tag->attributes->getNamedItem("ajxpOptions")->nodeValue; - $metaV = count($searchables)? '"metaColumns":'.json_encode($searchables): ""; - if (count($searchablesRenderers)) { - $metaV .= ',"metaColumnsRenderers":'.json_encode($searchablesRenderers); - } - if (!empty($v) && trim($v) != "{}" && !empty($metaV)) { - $v = str_replace("}", ", ".$metaV."}", $v); - } else { - $v = "{".$metaV."}"; - } - $tag->setAttribute("ajxpOptions", $v); - } - - parent::init($this->options); - - } - - protected function getMetaDefinition() - { - if (!$this->metaOptionsParsed) { - if (!isSet($this->options["meta_types"]) && isSet($this->options["meta_fields"])) { - // Get type from name - $val = $this->options["meta_fields"]; - if($val == "stars_rate") $this->options["meta_types"].="stars_rate"; - else if($val == "css_label") $this->options["meta_types"].="css_label"; - else if(substr($val, 0,5) == "area_") $this->options["meta_types"].="textarea"; - else $this->options["meta_types"].="string"; - } - if(!empty($this->options["meta_additional"])){ - $this->fieldsAdditionalData[$this->options["meta_fields"]] = $this->options["meta_additional"]; - } - foreach ($this->options as $key => $val) { - $matches = array(); - if (preg_match('/^meta_fields_(.*)$/', $key, $matches) != 0) { - $repIndex = $matches[1]; - $this->options["meta_fields"].=",".$val; - $this->options["meta_labels"].=",".$this->options["meta_labels_".$repIndex]; - if (!empty($this->options["meta_additional_".$repIndex])) { - $this->fieldsAdditionalData[$val] = $this->options["meta_additional_".$repIndex]; - } - if (isSet($this->options["meta_types_".$repIndex])) { - $this->options["meta_types"].=",".$this->options["meta_types_".$repIndex]; - } else { - // Get type from name - if($val == "stars_rate") $this->options["meta_types"].=","."stars_rate"; - else if($val == "css_label") $this->options["meta_types"].=","."css_label"; - else if(substr($val,0,5) == "area_") $this->options["meta_types"].=","."textarea"; - else $this->options["meta_types"].=","."string"; - } - if (isSet($this->options["meta_visibility_".$repIndex]) && isSet($this->options["meta_visibility"])) { - $this->options["meta_visibility"].=",".$this->options["meta_visibility_".$repIndex]; - } - } - } - $this->metaOptionsParsed = true; - } - - $fields = $this->options["meta_fields"]; - $arrF = explode(",", $fields); - $labels = $this->options["meta_labels"]; - $arrL = explode(",", $labels); - $arrT = explode(",", $this->options["meta_types"]); - - $result = array(); - foreach ($arrF as $index => $value) { - if (isSet($arrL[$index])) { - $result[$value] = array("label" => $arrL[$index], "type" => $arrT[$index]); - } else { - $result[$value] = array("label" => $value, "type" => $arrT[$index]); - } - } - return $result; - } - - public function editMeta($actionName, $httpVars, $fileVars) - { - if (is_a($this->accessDriver, "demoAccessDriver")) { - throw new Exception("Write actions are disabled in demo mode!"); - } - $repo = $this->accessDriver->repository; - $user = AuthService::getLoggedUser(); - if (!AuthService::usersEnabled() && $user!=null && !$user->canWrite($repo->getId())) { - throw new Exception("You have no right on this action."); - } - $selection = new UserSelection($repo, $httpVars); - - $nodes = $selection->buildNodes(); - $nodesDiffs = array(); - $def = $this->getMetaDefinition(); - foreach($nodes as $ajxpNode){ - - $newValues = array(); - if(!is_writable($ajxpNode->getUrl())){ - throw new Exception("You are not allowed to perform this action"); - } - AJXP_Controller::applyHook("node.before_change", array(&$ajxpNode)); - foreach ($def as $key => $data) { - if (isSet($httpVars[$key])) { - $newValues[$key] = AJXP_Utils::decodeSecureMagic($httpVars[$key]); - if($data["type"] == "tags"){ - $this->updateTags(AJXP_Utils::decodeSecureMagic($httpVars[$key])); - } - } else { - if (!isset($original)) { - $original = $ajxpNode->retrieveMetadata("users_meta", false, AJXP_METADATA_SCOPE_GLOBAL); - } - if (isSet($original) && isset($original[$key])) { - $newValues[$key] = $original[$key]; - } - } - } - $ajxpNode->setMetadata("users_meta", $newValues, false, AJXP_METADATA_SCOPE_GLOBAL); - AJXP_Controller::applyHook("node.meta_change", array($ajxpNode)); - - $nodesDiffs[$ajxpNode->getPath()] = $ajxpNode; - - } - AJXP_XMLWriter::header(); - AJXP_XMLWriter::writeNodesDiff(array("UPDATE" => $nodesDiffs), true); - AJXP_XMLWriter::close(); - } - - /** - * - * @param AJXP_Node $ajxpNode - * @param bool $contextNode - * @param bool $details - * @return void - */ - public function extractMeta(&$ajxpNode, $contextNode = false, $details = false) - { - $metadata = $ajxpNode->retrieveMetadata("users_meta", false, AJXP_METADATA_SCOPE_GLOBAL); - if(empty($metadata)) $metadata = array(); - $ajxpNode->mergeMetadata($metadata); - - } - - /** - * - * @param AJXP_Node $oldNode - * @param AJXP_Node $newNode - * @param Boolean $copy - */ - public function updateMetaLocation($oldNode, $newNode = null, $copy = false) - { - $defs = $this->getMetaDefinition(); - $updateField = $createField = null; - foreach($defs as $f => $data){ - if($data["type"] == "updater") $updateField = $f; - else if($data["type"] == "creator") $createField = $f; - } - $valuesUpdate = (isSet($updateField) || isSet($createField)); - $currentUser = null; - if($valuesUpdate){ - $currentUser = AuthService::getLoggedUser()->getId(); - } - - if($oldNode == null && !$valuesUpdate) return; - if(!$copy && !$valuesUpdate && $this->metaStore->inherentMetaMove()) return; - - if($oldNode == null){ - $oldMeta = $this->metaStore->retrieveMetadata($newNode, "users_meta", false, AJXP_METADATA_SCOPE_GLOBAL); - }else{ - $oldMeta = $this->metaStore->retrieveMetadata($oldNode, "users_meta", false, AJXP_METADATA_SCOPE_GLOBAL); - } - if($valuesUpdate){ - if(isSet($updateField))$oldMeta[$updateField] = $currentUser; - if(isSet($createField) && $oldNode == null) $oldMeta[$createField] = $currentUser; - } - if (!count($oldMeta)) { - return; - } - // If it's a move or a delete, delete old data - if ($oldNode != null && !$copy) { - $this->metaStore->removeMetadata($oldNode, "users_meta", false, AJXP_METADATA_SCOPE_GLOBAL); - } - // If copy or move, copy data. - if ($newNode != null) { - $this->metaStore->setMetadata($newNode, "users_meta", $oldMeta, false, AJXP_METADATA_SCOPE_GLOBAL); - } - } - - public function listTags($actionName, &$httpVars, &$fileVars){ - - HTMLWriter::charsetHeader("application/json"); - $tags = $this->loadTags(); - if(empty($tags)) $tags = array(); - echo json_encode($tags); - - } - - protected function loadTags(){ - $store = ConfService::getConfStorageImpl(); - if(!is_a($store, "sqlConfDriver")) return array(); - $data = array(); - $store->simpleStoreGet("meta_user_tags", ConfService::getRepository()->getId(), "serial", $data); - return $data; - } - - protected function updateTags($tagString){ - $store = ConfService::getConfStorageImpl(); - if(!is_a($store, "sqlConfDriver")) return; - $tags = $this->loadTags(); - $tags = array_merge($tags, array_map("trim", explode(",", $tagString))); - $tags = array_unique($tags); - $store->simpleStoreSet("meta_user_tags", ConfService::getRepository()->getId(), array_values($tags), "serial"); - } - -} diff --git a/core/src/plugins/meta.user/i18n/conf/cs.php b/core/src/plugins/meta.user/i18n/conf/cs.php index 982e6a6841..76044a6df5 100644 --- a/core/src/plugins/meta.user/i18n/conf/cs.php +++ b/core/src/plugins/meta.user/i18n/conf/cs.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Text Metadata" => "Textová metadata", @@ -49,4 +49,4 @@ "Visible" => "Visible", "Hidden" => "Hidden", "Tags (extensible sets of values)" => "Tags (extensible sets of values)", -); \ No newline at end of file +); diff --git a/core/src/plugins/meta.user/i18n/conf/de.php b/core/src/plugins/meta.user/i18n/conf/de.php index 7e1a869161..c7244a670f 100644 --- a/core/src/plugins/meta.user/i18n/conf/de.php +++ b/core/src/plugins/meta.user/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Text Metadata" => "Zusätzliche Metadaten in Datei", @@ -43,4 +43,4 @@ "Additional info" => "Zusätzliche Info", "Depending on the field type. Currently used for selection only" => "Abhängig vom Typ des Feldes. Aktuell nur für Wertelisten verwendet.", "Tags (extensible sets of values)" => "Tags (extensible sets of values)", -); \ No newline at end of file +); diff --git a/core/src/plugins/meta.user/i18n/conf/en.php b/core/src/plugins/meta.user/i18n/conf/en.php index ca1e0d7f94..57a1e095d8 100644 --- a/core/src/plugins/meta.user/i18n/conf/en.php +++ b/core/src/plugins/meta.user/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Text Metadata" => "Text Metadata", @@ -43,4 +43,4 @@ "Additional info" => "Additional info", "Depending on the field type. Currently used for selection only" => "Depending on the field type. Currently used for selection only", "Tags (extensible sets of values)" => "Tags (extensible sets of values)", -); \ No newline at end of file +); diff --git a/core/src/plugins/meta.user/i18n/conf/fr.php b/core/src/plugins/meta.user/i18n/conf/fr.php index d288b4f837..0b1c17d5ec 100644 --- a/core/src/plugins/meta.user/i18n/conf/fr.php +++ b/core/src/plugins/meta.user/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Text Metadata" => "Métadonnées texte", diff --git a/core/src/plugins/meta.user/i18n/conf/it.php b/core/src/plugins/meta.user/i18n/conf/it.php index d77d2fc0cc..52e43b3f95 100644 --- a/core/src/plugins/meta.user/i18n/conf/it.php +++ b/core/src/plugins/meta.user/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Text Metadata" => "Testo Metadata", @@ -43,4 +43,4 @@ "Additional info" => "Info Addizionali", "Depending on the field type. Currently used for selection only" => "In base al tipo di campo. Attualmente utilizzato solo per la selezione.", "Tags (extensible sets of values)" => "Tags (extensible sets of values)", -); \ No newline at end of file +); diff --git a/core/src/plugins/meta.user/i18n/conf/pt.php b/core/src/plugins/meta.user/i18n/conf/pt.php index 62aec221d7..994e7a2413 100644 --- a/core/src/plugins/meta.user/i18n/conf/pt.php +++ b/core/src/plugins/meta.user/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Text Metadata" => "Texto de Metadata", diff --git a/core/src/plugins/meta.user/i18n/cs.php b/core/src/plugins/meta.user/i18n/cs.php index 8a49325952..a83961efed 100644 --- a/core/src/plugins/meta.user/i18n/cs.php +++ b/core/src/plugins/meta.user/i18n/cs.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "1" => "Meta data", diff --git a/core/src/plugins/meta.user/i18n/de.php b/core/src/plugins/meta.user/i18n/de.php index 9a4a47ceb8..10462e7850 100644 --- a/core/src/plugins/meta.user/i18n/de.php +++ b/core/src/plugins/meta.user/i18n/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ // german translation: Axel Otterstätter // diff --git a/core/src/plugins/meta.user/i18n/en.php b/core/src/plugins/meta.user/i18n/en.php index bc1c9e3d68..daa74566b7 100644 --- a/core/src/plugins/meta.user/i18n/en.php +++ b/core/src/plugins/meta.user/i18n/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "1" => "Meta Data", diff --git a/core/src/plugins/meta.user/i18n/es.php b/core/src/plugins/meta.user/i18n/es.php index d3d14ff3b9..4818ea6f15 100644 --- a/core/src/plugins/meta.user/i18n/es.php +++ b/core/src/plugins/meta.user/i18n/es.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ // spanish translation: Ion Rey Bakaikoa , 2010 //--------------------------------------------------------------------------------------------------- diff --git a/core/src/plugins/meta.user/i18n/fi.php b/core/src/plugins/meta.user/i18n/fi.php index fd891d9e50..9bd31ef19a 100644 --- a/core/src/plugins/meta.user/i18n/fi.php +++ b/core/src/plugins/meta.user/i18n/fi.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ // Finnish translation by Aleksi Postari // aleksi (at) postari.net diff --git a/core/src/plugins/meta.user/i18n/fr.php b/core/src/plugins/meta.user/i18n/fr.php index b2a0e213f2..f389235e48 100644 --- a/core/src/plugins/meta.user/i18n/fr.php +++ b/core/src/plugins/meta.user/i18n/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "1" => "Meta-données", diff --git a/core/src/plugins/meta.user/i18n/it.php b/core/src/plugins/meta.user/i18n/it.php index 5516454869..1e3ee0a02a 100644 --- a/core/src/plugins/meta.user/i18n/it.php +++ b/core/src/plugins/meta.user/i18n/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "1" => "Meta Data", diff --git a/core/src/plugins/meta.user/i18n/pt.php b/core/src/plugins/meta.user/i18n/pt.php index be2daf753f..eeb4d98f59 100644 --- a/core/src/plugins/meta.user/i18n/pt.php +++ b/core/src/plugins/meta.user/i18n/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "1" => "MetaData", diff --git a/core/src/plugins/meta.user/i18n/ru.php b/core/src/plugins/meta.user/i18n/ru.php index 493d10e760..2adfc30ba6 100644 --- a/core/src/plugins/meta.user/i18n/ru.php +++ b/core/src/plugins/meta.user/i18n/ru.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */ $mess=array( "1" => "Метаданные", diff --git a/core/src/plugins/meta.user/i18n/si.php b/core/src/plugins/meta.user/i18n/si.php index 6a9a72dfe9..0871d9873b 100644 --- a/core/src/plugins/meta.user/i18n/si.php +++ b/core/src/plugins/meta.user/i18n/si.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . */// Slovenian translation: April 21 2011 by Vladimir Bohinc (vladimir.bohinc@gmail.com) // //--------------------------------------------------------------------------------------------------- diff --git a/core/src/plugins/meta.user/manifest.xml b/core/src/plugins/meta.user/manifest.xml index b1992ab7f1..aca754fe12 100644 --- a/core/src/plugins/meta.user/manifest.xml +++ b/core/src/plugins/meta.user/manifest.xml @@ -1,6 +1,6 @@ - + @@ -22,8 +22,6 @@ - - @@ -120,6 +118,6 @@
      - + diff --git a/core/src/plugins/meta.watch/WatchRegister.php b/core/src/plugins/meta.watch/WatchRegister.php new file mode 100644 index 0000000000..64c2087135 --- /dev/null +++ b/core/src/plugins/meta.watch/WatchRegister.php @@ -0,0 +1,591 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Meta\Watch; + +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Model\UserSelection; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\PluginFramework\PluginsService; +use Pydio\Core\Services\UsersService; +use Pydio\Access\Meta\Core\AbstractMetaSource; +use Pydio\Access\Metastore\Core\IMetaStoreProvider; +use Pydio\Notification\Core\Notification; +use Pydio\Notification\Core\NotificationCenter; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Keep an eye on a folder to be alerted when something changes inside it + * @package AjaXplorer_Plugins + * @subpackage Meta + */ +class WatchRegister extends AbstractMetaSource +{ + public static $META_WATCH_CHANGE = "META_WATCH_CHANGE"; + public static $META_WATCH_READ = "META_WATCH_READ"; + public static $META_WATCH_BOTH = "META_WATCH_BOTH"; + public static $META_WATCH_NAMESPACE = "META_WATCH"; + + public static $META_WATCH_USERS_READ = "META_WATCH_USERS_READ"; + public static $META_WATCH_USERS_CHANGE = "META_WATCH_USERS_CHANGE"; + public static $META_WATCH_USERS_NAMESPACE = "META_WATCH_USERS"; + + /** + * @var IMetaStoreProvider + */ + protected $metaStore; + + /** + * @var NotificationCenter + */ + protected $notificationCenter; + + /** + * @param ContextInterface $ctx + * @param AbstractAccessDriver $accessDriver + * @throws PydioException + */ + public function initMeta(ContextInterface $ctx, AbstractAccessDriver $accessDriver) + { + parent::initMeta($ctx, $accessDriver); + $this->notificationCenter = PluginsService::getInstance($ctx)->getPluginById("core.notifications"); + $store = PluginsService::getInstance($ctx)->getUniqueActivePluginForType("metastore"); + if ($store === false) { + throw new PydioException("The 'meta.watch' plugin requires at least one active 'metastore' plugin"); + } + $this->metaStore = $store; + $this->metaStore->initMeta($ctx, $accessDriver); + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $node + * @param string $userId + * @param string $watchType + * @param array $targetUsers Optional list of specific users to watch + */ + public function setWatchOnFolder($node, $userId, $watchType, $targetUsers = array()) + { + if ( ($watchType == self::$META_WATCH_USERS_READ || $watchType == self::$META_WATCH_USERS_CHANGE ) && count($targetUsers)) { + $usersMeta = $this->metaStore->retrieveMetadata($node, self::$META_WATCH_USERS_NAMESPACE); + if (is_array($usersMeta) && is_array($usersMeta[$watchType]) && is_array($usersMeta[$watchType][$userId])) { + $usersMeta[$watchType][$userId] = array_merge($usersMeta[$watchType][$userId], $targetUsers); + } else { + if(!is_array($usersMeta)) $usersMeta = array(); + if(!is_array($usersMeta[$watchType])) $usersMeta[$watchType] = array(); + $usersMeta[$watchType][$userId] = $targetUsers; + } + $this->metaStore->setMetadata( + $node, + self::$META_WATCH_USERS_NAMESPACE, + $usersMeta, + false, + AJXP_METADATA_SCOPE_REPOSITORY + ); + } else { + $meta = $this->metaStore->retrieveMetadata( + $node, + self::$META_WATCH_NAMESPACE, + false, + AJXP_METADATA_SCOPE_REPOSITORY + ); + if (isSet($meta) && isSet($meta[$userId])) { + unset($meta[$userId]); + $this->metaStore->removeMetadata( + $node, + self::$META_WATCH_NAMESPACE, + false, + AJXP_METADATA_SCOPE_REPOSITORY + ); + } + $meta[$userId] = $watchType; + if (count($meta)) { + $this->metaStore->setMetadata( + $node, + self::$META_WATCH_NAMESPACE, + $meta, + false, + AJXP_METADATA_SCOPE_REPOSITORY + ); + } + } + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $node + * @param $userId + * @param bool $clearUsers + * @param bool $targetUserId + */ + public function removeWatchFromFolder($node, $userId, $clearUsers = false, $targetUserId = false) + { + if ($clearUsers) { + $usersMeta = $this->metaStore->retrieveMetadata( + $node, + self::$META_WATCH_USERS_NAMESPACE, + false, + AJXP_METADATA_SCOPE_REPOSITORY + ); + + // WEIRD / WILL IT REMOVE OTHER PEOPLE WATCHES?? + if (isSet($usersMeta) && (isSet($usersMeta[self::$META_WATCH_USERS_CHANGE][$userId]) || isSet($usersMeta[self::$META_WATCH_USERS_READ][$userId]))) { + + if ($targetUserId != false) { + if (isSet($usersMeta[self::$META_WATCH_USERS_CHANGE][$userId])) { + $c = $usersMeta[self::$META_WATCH_USERS_CHANGE][$userId]; + if(in_array($targetUserId, $c)) $c = array_diff($c, array($targetUserId)); + if(count($c)) $usersMeta[self::$META_WATCH_USERS_CHANGE][$userId] = $c; + else unset($usersMeta[self::$META_WATCH_USERS_CHANGE][$userId]); + } + if (isSet($usersMeta[self::$META_WATCH_USERS_READ][$userId])) { + $c = $usersMeta[self::$META_WATCH_USERS_READ][$userId]; + if(in_array($targetUserId, $c)) $c = array_diff($c, array($targetUserId)); + if(count($c)) $usersMeta[self::$META_WATCH_USERS_READ][$userId] = $c; + else unset($usersMeta[self::$META_WATCH_USERS_READ][$userId]); + } + $this->metaStore->setMetadata($node, self::$META_WATCH_USERS_NAMESPACE, $usersMeta, false, AJXP_METADATA_SCOPE_REPOSITORY); + + } else { + + $this->metaStore->removeMetadata( + $node, + self::$META_WATCH_USERS_NAMESPACE, + false, + AJXP_METADATA_SCOPE_REPOSITORY); + + } + + } + } else { + + $meta = $this->metaStore->retrieveMetadata( + $node, + self::$META_WATCH_NAMESPACE, + false, + AJXP_METADATA_SCOPE_REPOSITORY + ); + if (isSet($meta) && isSet($meta[$userId])) { + $this->metaStore->removeMetadata( + $node, + self::$META_WATCH_NAMESPACE, + false, + AJXP_METADATA_SCOPE_REPOSITORY + ); + } + + } + + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $node + * @param $userId + * @param string $ns Watch namespace + * @param array $result + * @return string|bool the type of watch + */ + public function hasWatchOnNode($node, $userId, $ns = "META_WATCH", &$result = array()) + { + $meta = $this->metaStore->retrieveMetadata( + $node, + $ns, + false, + AJXP_METADATA_SCOPE_REPOSITORY + ); + if ($ns == self::$META_WATCH_USERS_NAMESPACE) { + if (isSet($meta[self::$META_WATCH_USERS_READ]) && isSet($meta[self::$META_WATCH_USERS_READ][$userId])) { + $result = $meta[self::$META_WATCH_USERS_READ][$userId]; + return self::$META_WATCH_USERS_READ; + } + if (isSet($meta[self::$META_WATCH_USERS_CHANGE]) && isSet($meta[self::$META_WATCH_USERS_CHANGE][$userId])) { + $result = $meta[self::$META_WATCH_USERS_CHANGE][$userId]; + return self::$META_WATCH_USERS_CHANGE; + } + return false; + } else if (isSet($meta) && isSet($meta[$userId])) { + return $meta[$userId]; + } else { + return false; + } + + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $node + * @param String $watchType + * @return array + */ + public function collectWatches($node, $watchType) + { + $currentUserId = "shared"; + $ctx = $node->getContext(); + if ($ctx->hasUser()) { + $currentUserId = $ctx->getUser()->getId(); + } + $result = array(); + $result["node"] = $this->getWatchesOnNode($node, $watchType, $currentUserId); + + $ancestors = array(); + $node->collectMetadatasInParents( + array(self::$META_WATCH_NAMESPACE,self::$META_WATCH_USERS_NAMESPACE), + false, + AJXP_METADATA_SCOPE_REPOSITORY, + false, + $ancestors + ); + + $result["ancestors"] = array(); + foreach($ancestors as $nodeMeta){ + $source = $nodeMeta["SOURCE_NODE"]; + $watchMeta = isSet($nodeMeta[self::$META_WATCH_NAMESPACE]) ? $nodeMeta[self::$META_WATCH_NAMESPACE] : false; + $usersMeta = isSet($nodeMeta[self::$META_WATCH_USERS_NAMESPACE]) ? $nodeMeta[self::$META_WATCH_USERS_NAMESPACE] : false; + $ids = $this->loadWatchesFromMeta($watchType, $currentUserId, $source, $watchMeta, $usersMeta); + foreach($ids as $id){ + // Do not send notification to myself! + if($id !== $currentUserId){ + $result["ancestors"][] = array("node" => $source, "id" => $id); + } + } + } + + return $result; + } + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $node + * @param String $watchType + * @param String $userId + * @return array + */ + public function getWatchesOnNode($node, $watchType, $userId = null) + { + $ctx = $node->getContext(); + if($userId == null){ + $currentUserId = "shared"; + if ($ctx->hasUser()) { + $currentUserId = $ctx->getUser()->getId(); + } + }else{ + $currentUserId = $userId; + } + $meta = $node->retrieveMetadata( + self::$META_WATCH_NAMESPACE, + false, + AJXP_METADATA_SCOPE_REPOSITORY + ); + $usersMeta = false; + if ($currentUserId != 'shared') { + $usersMeta = $node->retrieveMetadata( + self::$META_WATCH_USERS_NAMESPACE, + false, + AJXP_METADATA_SCOPE_REPOSITORY + ); + } + return $this->loadWatchesFromMeta($watchType, $currentUserId, $node, $meta, $usersMeta); + } + + /** + * @param String $watchType + * @param String $currentUserId + * @param \Pydio\Access\Core\Model\AJXP_Node $node + * @param array|bool $watchMeta + * @param array|bool $usersMeta + * @return array + */ + private function loadWatchesFromMeta($watchType, $currentUserId, $node, $watchMeta = false, $usersMeta = false){ + $IDS = array(); + if($usersMeta !== false){ + if ($watchType == self::$META_WATCH_CHANGE && isSet($usersMeta[self::$META_WATCH_USERS_CHANGE])) { + $usersMeta = $usersMeta[self::$META_WATCH_USERS_CHANGE]; + } else if ($watchType == self::$META_WATCH_READ && isSet($usersMeta[self::$META_WATCH_USERS_READ])) { + $usersMeta = $usersMeta[self::$META_WATCH_USERS_READ]; + } else { + $usersMeta = null; + } + } + + if (!empty($watchMeta) && is_array($watchMeta)) { + foreach ($watchMeta as $id => $type) { + if ($type == $watchType || $type == self::$META_WATCH_BOTH) { + $IDS[] = $id; + } + } + } + if (!empty($usersMeta) && is_array($usersMeta)) { + foreach ($usersMeta as $id => $targetUsers) { + if (in_array($currentUserId, $targetUsers)) { + $IDS[] = $id; + } + } + } + if (count($IDS)) { + $changes = false; + foreach ($IDS as $index => $id) { + if ($currentUserId == $id && !AJXP_SERVER_DEBUG) { + // In non-debug mode, do not send notifications to watcher! + unset($IDS[$index]); + continue; + } + if (!UsersService::userExists($id)) { + unset($IDS[$index]); + if(is_array($watchMeta)){ + $changes = true; + $watchMeta[$id] = AJXP_VALUE_CLEAR; + } + }else{ + // Make sure the user is still authorized on this node, otherwise remove it. + $uObject = UsersService::getUserById($id, false); + $acl = $uObject->getMergedRole()->getAcl($node->getRepositoryId()); + $isOwner = ($node->getRepository()->getOwner() == $uObject->getId()); + if(!$isOwner && (empty($acl) || strpos($acl, "r") === FALSE)){ + unset($IDS[$index]); + if(is_array($watchMeta)){ + $changes = true; + $watchMeta[$id] = AJXP_VALUE_CLEAR; + } + } + } + } + if ($changes) { + $node->setMetadata( + self::$META_WATCH_NAMESPACE, + $watchMeta, + false, + AJXP_METADATA_SCOPE_REPOSITORY + ); + } + } + return $IDS; + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $requestInterface + * @param \Psr\Http\Message\ResponseInterface $responseInterface + */ + public function switchActions(\Psr\Http\Message\ServerRequestInterface $requestInterface, \Psr\Http\Message\ResponseInterface &$responseInterface) + { + $actionName = $requestInterface->getAttribute("action"); + $httpVars = $requestInterface->getParsedBody(); + if($actionName !== "toggle_watch") return; + /** @var ContextInterface $ctx */ + $ctx = $requestInterface->getAttribute("ctx"); + + $us = UserSelection::fromContext($ctx, $httpVars); + $node = $us->getUniqueNode(); + $node->loadNodeInfo(); + $cmd = $httpVars["watch_action"]; + + $meta = $this->metaStore->retrieveMetadata( + $node, + self::$META_WATCH_NAMESPACE, + false, + AJXP_METADATA_SCOPE_REPOSITORY + ); + $userId = $ctx->hasUser() ? $ctx->getUser()->getId() : "shared"; + + if($node->isRoot() && $node->getRepository()->hasParent() && $cmd == "watch_stop" && !(isSet($meta) && isSet($meta[$userId]))){ + $usersMeta = $this->metaStore->retrieveMetadata( + $node, + self::$META_WATCH_USERS_NAMESPACE, + false, + AJXP_METADATA_SCOPE_REPOSITORY); + if(is_array($usersMeta) && array_key_exists(self::$META_WATCH_USERS_CHANGE, $usersMeta) && array_key_exists($userId,$usersMeta[self::$META_WATCH_USERS_CHANGE])) { + unset($usersMeta[self::$META_WATCH_USERS_CHANGE][$userId]); + $this->metaStore->setMetadata( + $node, + self::$META_WATCH_USERS_NAMESPACE, + $usersMeta, + false, + AJXP_METADATA_SCOPE_REPOSITORY + ); + } + } else if ($cmd == "watch_stop" && isSet($meta) && isSet($meta[$userId])) { + unset($meta[$userId]); + $this->metaStore->removeMetadata( + $node, + self::$META_WATCH_NAMESPACE, + false, + AJXP_METADATA_SCOPE_REPOSITORY + ); + } else { + switch ($cmd) { + case "watch_change": $type = self::$META_WATCH_CHANGE;break; + case "watch_read": $type = self::$META_WATCH_READ; break; + case "watch_both": $type = self::$META_WATCH_BOTH; break; + default: $type = self::$META_WATCH_BOTH; break; + } + $meta[$userId] = $type; + $this->metaStore->setMetadata( + $node, + self::$META_WATCH_NAMESPACE, + $meta, + false, + AJXP_METADATA_SCOPE_REPOSITORY + ); + } + + $node->metadata = array(); + $node->loadNodeInfo(true, false, "all"); + $this->enrichNode($node); + $nodesDiff = new \Pydio\Access\Core\Model\NodesDiff(); + $nodesDiff->update($node); + $x = new \Pydio\Core\Http\Response\SerializableResponseStream(); + $x->addChunk($nodesDiff); + $responseInterface = $responseInterface->withBody($x); + + } + + + /** + * @param AJXP_Node|null $oldNode + * @param AJXP_Node|null $newNode + * @param bool $copy + */ + public function processChangeHook(AJXP_Node $oldNode=null, AJXP_Node $newNode=null, $copy = false) + { + $newNotif = $this->notificationCenter->generateNotificationFromChangeHook($oldNode, $newNode, $copy, "new"); + if ($newNotif !== false && $newNotif->getNode() !== false) { + $this->processActiveHook($newNotif, self::$META_WATCH_CHANGE, AJXP_NOTIF_NODE_CHANGE); + } + if($oldNode != null && $newNode != null && $oldNode->getUrl() == $newNode->getUrl()) return; + $oldNotif = $this->notificationCenter->generateNotificationFromChangeHook($oldNode, $newNode, $copy, "old"); + if ($oldNotif !== false && $oldNotif->getNode() !== false) { + $this->processActiveHook($oldNotif, self::$META_WATCH_CHANGE, AJXP_NOTIF_NODE_CHANGE); + } + + $this->updateMetaLocation($oldNode, $newNode, $copy); + + } + + /** + * @param AJXP_Node $node + */ + public function processReadHook(AJXP_Node $node) + { + $notification = new Notification(); + $notification->setAction(AJXP_NOTIF_NODE_VIEW); + $notification->setNode($node); + $this->processActiveHook($notification, self::$META_WATCH_READ); + + } + + /** + * @param Notification $notification + * @param $namespace + * @param null $parentActionType + */ + private function processActiveHook(Notification $notification, $namespace, $parentActionType = null){ + $origNode = $notification->getNode(); + $node = new AJXP_Node($origNode->getUrl()); + $all = $this->collectWatches($node, $namespace); + if($node->isRoot() && $node->getRepository() !== null && $node->getRepository()->hasContentFilter()){ + $node->setLeaf(true); + }else{ + $node->setLeaf(false); + } + foreach($all["node"] as $id) { + $this->notificationCenter->postNotification($notification, $id); + } + foreach($all["ancestors"] as $pair){ + $parentNotification = new Notification(); + /** + * @var \Pydio\Access\Core\Model\AJXP_Node $parentNode + */ + $parentNode = $pair["node"]; + if($parentNode->isRoot() && $parentNode->getRepository() !== null && $parentNode->getRepository()->hasContentFilter()){ + $parentNode->setLeaf(true); + }else{ + $parentNode->setLeaf(false); + } + $parentNotification->setNode($parentNode); + if($parentActionType == null){ + $parentNotification->setAction($notification->getAction()); + }else{ + $parentNotification->setAction($parentActionType); + } + $this->notificationCenter->prepareNotification($notification); + $parentNotification->addRelatedNotification($notification); + $this->notificationCenter->postNotification($parentNotification, $pair["id"]); + } + } + + /** + * @param AJXP_Node $node + */ + public function enrichNode(&$node) + { + if(!$node->getContext()->hasUser()) { + return; + } + $meta = $this->metaStore->retrieveMetadata( + $node, + self::$META_WATCH_NAMESPACE, + false, + AJXP_METADATA_SCOPE_REPOSITORY); + $userId = $node->getContext()->getUser()->getId(); + if(is_array($meta) && array_key_exists($userId, $meta)){ + $node->mergeMetadata(array( + "meta_watched" => $meta[$userId], + "overlay_icon" => "meta.watch/ICON_SIZE/watch.png", + "overlay_class" => "icon-eye-open" + ), true); + }else if($node->isRoot() && $node->getRepository()->hasParent()){ + $usersMeta = $this->metaStore->retrieveMetadata( + $node, + self::$META_WATCH_USERS_NAMESPACE, + false, + AJXP_METADATA_SCOPE_REPOSITORY); + if(is_array($usersMeta) && array_key_exists(self::$META_WATCH_USERS_CHANGE, $usersMeta) && array_key_exists($userId,$usersMeta[self::$META_WATCH_USERS_CHANGE])) { + $node->mergeMetadata(array( + "meta_watched" => self::$META_WATCH_CHANGE, + "overlay_icon" => "meta.watch/ICON_SIZE/watch.png", + "overlay_class" => "icon-eye-open" + ), true); + } + } + } + + /** + * + * @param \Pydio\Access\Core\Model\AJXP_Node $oldFile + * @param \Pydio\Access\Core\Model\AJXP_Node $newFile + * @param Boolean $copy + */ + public function updateMetaLocation($oldFile, $newFile = null, $copy = false) + { + if($oldFile == null) return; + if(!$copy && $this->metaStore->inherentMetaMove()) return; + + $oldMeta = $this->metaStore->retrieveMetadata($oldFile, self::$META_WATCH_NAMESPACE, false, AJXP_METADATA_SCOPE_REPOSITORY); + if (count($oldMeta)) { + // If it's a move or a delete, delete old data + if (!$copy) { + $this->metaStore->removeMetadata($oldFile, self::$META_WATCH_NAMESPACE, false, AJXP_METADATA_SCOPE_REPOSITORY); + } + // If copy or move, copy data. + if ($newFile != null) { + $this->metaStore->setMetadata($newFile, self::$META_WATCH_NAMESPACE, $oldMeta, false, AJXP_METADATA_SCOPE_REPOSITORY); + } + } + + } + +} diff --git a/core/src/plugins/meta.watch/class.MetaWatchRegister.php b/core/src/plugins/meta.watch/class.MetaWatchRegister.php deleted file mode 100755 index 935fdbcde3..0000000000 --- a/core/src/plugins/meta.watch/class.MetaWatchRegister.php +++ /dev/null @@ -1,525 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Keep an eye on a folder to be alerted when something changes inside it - * @package AjaXplorer_Plugins - * @subpackage Meta - */ -class MetaWatchRegister extends AJXP_AbstractMetaSource -{ - public static $META_WATCH_CHANGE = "META_WATCH_CHANGE"; - public static $META_WATCH_READ = "META_WATCH_READ"; - public static $META_WATCH_BOTH = "META_WATCH_BOTH"; - public static $META_WATCH_NAMESPACE = "META_WATCH"; - - public static $META_WATCH_USERS_READ = "META_WATCH_USERS_READ"; - public static $META_WATCH_USERS_CHANGE = "META_WATCH_USERS_CHANGE"; - public static $META_WATCH_USERS_NAMESPACE = "META_WATCH_USERS"; - - /** - * @var MetaStoreProvider - */ - protected $metaStore; - - /** - * @var AJXP_NotificationCenter - */ - protected $notificationCenter; - - public function initMeta($accessDriver) - { - parent::initMeta($accessDriver); - $this->notificationCenter = AJXP_PluginsService::findPluginById("core.notifications"); - $store = AJXP_PluginsService::getInstance()->getUniqueActivePluginForType("metastore"); - if ($store === false) { - throw new Exception("The 'meta.watch' plugin requires at least one active 'metastore' plugin"); - } - $this->metaStore = $store; - $this->metaStore->initMeta($accessDriver); - } - - /** - * @param AJXP_Node $node - * @param string $userId - * @param string $watchType - * @param array $targetUsers Optional list of specific users to watch - */ - public function setWatchOnFolder($node, $userId, $watchType, $targetUsers = array()) - { - if ( ($watchType == self::$META_WATCH_USERS_READ || $watchType == self::$META_WATCH_USERS_CHANGE ) && count($targetUsers)) { - $usersMeta = $this->metaStore->retrieveMetadata($node, self::$META_WATCH_USERS_NAMESPACE); - if (is_array($usersMeta) && is_array($usersMeta[$watchType]) && is_array($usersMeta[$watchType][$userId])) { - $usersMeta[$watchType][$userId] = array_merge($usersMeta[$watchType][$userId], $targetUsers); - } else { - if(!is_array($usersMeta)) $usersMeta = array(); - if(!is_array($usersMeta[$watchType])) $usersMeta[$watchType] = array(); - $usersMeta[$watchType][$userId] = $targetUsers; - } - $this->metaStore->setMetadata( - $node, - self::$META_WATCH_USERS_NAMESPACE, - $usersMeta, - false, - AJXP_METADATA_SCOPE_REPOSITORY - ); - } else { - $meta = $this->metaStore->retrieveMetadata( - $node, - self::$META_WATCH_NAMESPACE, - false, - AJXP_METADATA_SCOPE_REPOSITORY - ); - if (isSet($meta) && isSet($meta[$userId])) { - unset($meta[$userId]); - $this->metaStore->removeMetadata( - $node, - self::$META_WATCH_NAMESPACE, - false, - AJXP_METADATA_SCOPE_REPOSITORY - ); - } - $meta[$userId] = $watchType; - if (count($meta)) { - $this->metaStore->setMetadata( - $node, - self::$META_WATCH_NAMESPACE, - $meta, - false, - AJXP_METADATA_SCOPE_REPOSITORY - ); - } - } - } - - /** - * @param AJXP_Node $node - * @param $userId - * @param bool $clearUsers - * @param bool $targetUserId - */ - public function removeWatchFromFolder($node, $userId, $clearUsers = false, $targetUserId = false) - { - if ($clearUsers) { - $usersMeta = $this->metaStore->retrieveMetadata( - $node, - self::$META_WATCH_USERS_NAMESPACE, - false, - AJXP_METADATA_SCOPE_REPOSITORY - ); - - // WEIRD / WILL IT REMOVE OTHER PEOPLE WATCHES?? - if (isSet($usersMeta) && (isSet($usersMeta[self::$META_WATCH_USERS_CHANGE][$userId]) || isSet($usersMeta[self::$META_WATCH_USERS_READ][$userId]))) { - - if ($targetUserId != false) { - if (isSet($usersMeta[self::$META_WATCH_USERS_CHANGE][$userId])) { - $c = $usersMeta[self::$META_WATCH_USERS_CHANGE][$userId]; - if(in_array($targetUserId, $c)) $c = array_diff($c, array($targetUserId)); - if(count($c)) $usersMeta[self::$META_WATCH_USERS_CHANGE][$userId] = $c; - else unset($usersMeta[self::$META_WATCH_USERS_CHANGE][$userId]); - } - if (isSet($usersMeta[self::$META_WATCH_USERS_READ][$userId])) { - $c = $usersMeta[self::$META_WATCH_USERS_READ][$userId]; - if(in_array($targetUserId, $c)) $c = array_diff($c, array($targetUserId)); - if(count($c)) $usersMeta[self::$META_WATCH_USERS_READ][$userId] = $c; - else unset($usersMeta[self::$META_WATCH_USERS_READ][$userId]); - } - $this->metaStore->setMetadata($node, self::$META_WATCH_USERS_NAMESPACE, $usersMeta, false, AJXP_METADATA_SCOPE_REPOSITORY); - - } else { - - $this->metaStore->removeMetadata( - $node, - self::$META_WATCH_USERS_NAMESPACE, - false, - AJXP_METADATA_SCOPE_REPOSITORY); - - } - - } - } else { - - $meta = $this->metaStore->retrieveMetadata( - $node, - self::$META_WATCH_NAMESPACE, - false, - AJXP_METADATA_SCOPE_REPOSITORY - ); - if (isSet($meta) && isSet($meta[$userId])) { - $this->metaStore->removeMetadata( - $node, - self::$META_WATCH_NAMESPACE, - false, - AJXP_METADATA_SCOPE_REPOSITORY - ); - } - - } - - } - - /** - * @param AJXP_Node $node - * @param $userId - * @param string $ns Watch namespace - * @param array $result - * @return string|bool the type of watch - */ - public function hasWatchOnNode($node, $userId, $ns = "META_WATCH", &$result = array()) - { - $meta = $this->metaStore->retrieveMetadata( - $node, - $ns, - false, - AJXP_METADATA_SCOPE_REPOSITORY - ); - if ($ns == self::$META_WATCH_USERS_NAMESPACE) { - if (isSet($meta[self::$META_WATCH_USERS_READ]) && isSet($meta[self::$META_WATCH_USERS_READ][$userId])) { - $result = $meta[self::$META_WATCH_USERS_READ][$userId]; - return self::$META_WATCH_USERS_READ; - } - if (isSet($meta[self::$META_WATCH_USERS_CHANGE]) && isSet($meta[self::$META_WATCH_USERS_CHANGE][$userId])) { - $result = $meta[self::$META_WATCH_USERS_CHANGE][$userId]; - return self::$META_WATCH_USERS_CHANGE; - } - return false; - } else if (isSet($meta) && isSet($meta[$userId])) { - return $meta[$userId]; - } else { - return false; - } - - } - - /** - * @param AJXP_Node $node - * @param String $watchType - * @return array - */ - public function collectWatches($node, $watchType) - { - $currentUserId = "shared"; - if (AuthService::getLoggedUser() != null) { - $currentUserId = AuthService::getLoggedUser()->getId(); - } - $result = array(); - $result["node"] = $this->getWatchesOnNode($node, $watchType, $currentUserId); - - $ancestors = array(); - $node->collectMetadatasInParents( - array(self::$META_WATCH_NAMESPACE,self::$META_WATCH_USERS_NAMESPACE), - false, - AJXP_METADATA_SCOPE_REPOSITORY, - false, - $ancestors - ); - - $result["ancestors"] = array(); - foreach($ancestors as $nodeMeta){ - $source = $nodeMeta["SOURCE_NODE"]; - $watchMeta = isSet($nodeMeta[self::$META_WATCH_NAMESPACE]) ? $nodeMeta[self::$META_WATCH_NAMESPACE] : false; - $usersMeta = isSet($nodeMeta[self::$META_WATCH_USERS_NAMESPACE]) ? $nodeMeta[self::$META_WATCH_USERS_NAMESPACE] : false; - $ids = $this->loadWatchesFromMeta($watchType, $currentUserId, $source, $watchMeta, $usersMeta); - foreach($ids as $id){ - // Do not send notification to myself! - if($id !== $currentUserId){ - $result["ancestors"][] = array("node" => $source, "id" => $id); - } - } - } - - return $result; - } - /** - * @param AJXP_Node $node - * @param String $watchType - * @param String $userId - * @return array - */ - public function getWatchesOnNode($node, $watchType, $userId = null) - { - if($userId == null){ - $currentUserId = "shared"; - if (AuthService::getLoggedUser() != null) { - $currentUserId = AuthService::getLoggedUser()->getId(); - } - }else{ - $currentUserId = $userId; - } - $meta = $node->retrieveMetadata( - self::$META_WATCH_NAMESPACE, - false, - AJXP_METADATA_SCOPE_REPOSITORY - ); - $usersMeta = false; - if ($currentUserId != 'shared') { - $usersMeta = $node->retrieveMetadata( - self::$META_WATCH_USERS_NAMESPACE, - false, - AJXP_METADATA_SCOPE_REPOSITORY - ); - } - return $this->loadWatchesFromMeta($watchType, $currentUserId, $node, $meta, $usersMeta); - } - - /** - * @param String $watchType - * @param String $currentUserId - * @param AJXP_Node $node - * @param array|bool $watchMeta - * @param array|bool $usersMeta - * @return array - */ - private function loadWatchesFromMeta($watchType, $currentUserId, $node, $watchMeta = false, $usersMeta = false){ - $IDS = array(); - if($usersMeta !== false){ - if ($watchType == self::$META_WATCH_CHANGE && isSet($usersMeta[self::$META_WATCH_USERS_CHANGE])) { - $usersMeta = $usersMeta[self::$META_WATCH_USERS_CHANGE]; - } else if ($watchType == self::$META_WATCH_READ && isSet($usersMeta[self::$META_WATCH_USERS_READ])) { - $usersMeta = $usersMeta[self::$META_WATCH_USERS_READ]; - } else { - $usersMeta = null; - } - } - - if (!empty($watchMeta) && is_array($watchMeta)) { - foreach ($watchMeta as $id => $type) { - if ($type == $watchType || $type == self::$META_WATCH_BOTH) { - $IDS[] = $id; - } - } - } - if (!empty($usersMeta) && is_array($usersMeta)) { - foreach ($usersMeta as $id => $targetUsers) { - if (in_array($currentUserId, $targetUsers)) { - $IDS[] = $id; - } - } - } - if (count($IDS)) { - $changes = false; - foreach ($IDS as $index => $id) { - if ($currentUserId == $id && !AJXP_SERVER_DEBUG) { - // In non-debug mode, do not send notifications to watcher! - unset($IDS[$index]); - continue; - } - if (!AuthService::userExists($id)) { - unset($IDS[$index]); - if(is_array($watchMeta)){ - $changes = true; - $watchMeta[$id] = AJXP_VALUE_CLEAR; - } - }else{ - // Make sure the user is still authorized on this node, otherwise remove it. - $uObject = ConfService::getConfStorageImpl()->createUserObject($id); - $acl = $uObject->mergedRole->getAcl($node->getRepositoryId()); - $isOwner = ($node->getRepository()->getOwner() == $uObject->getId()); - if(!$isOwner && (empty($acl) || strpos($acl, "r") === FALSE)){ - unset($IDS[$index]); - if(is_array($watchMeta)){ - $changes = true; - $watchMeta[$id] = AJXP_VALUE_CLEAR; - } - } - } - } - if ($changes) { - $node->setMetadata( - self::$META_WATCH_NAMESPACE, - $watchMeta, - false, - AJXP_METADATA_SCOPE_REPOSITORY - ); - } - } - return $IDS; - } - - public function switchActions($actionName, $httpVars, $fileVars) - { - switch ($actionName) { - - case "toggle_watch": - - $us = new UserSelection($this->accessDriver->repository, $httpVars); - $node = $us->getUniqueNode(); - $node->loadNodeInfo(); - $cmd = $httpVars["watch_action"]; - - $meta = $this->metaStore->retrieveMetadata( - $node, - self::$META_WATCH_NAMESPACE, - false, - AJXP_METADATA_SCOPE_REPOSITORY - ); - $userId = AuthService::getLoggedUser()!= null ? AuthService::getLoggedUser()->getId() : "shared"; - - if ($cmd == "watch_stop" && isSet($meta) && isSet($meta[$userId])) { - unset($meta[$userId]); - $this->metaStore->removeMetadata( - $node, - self::$META_WATCH_NAMESPACE, - false, - AJXP_METADATA_SCOPE_REPOSITORY - ); - } else { - switch ($cmd) { - case "watch_change": $type = self::$META_WATCH_CHANGE;break; - case "watch_read": $type = self::$META_WATCH_READ; break; - case "watch_both": $type = self::$META_WATCH_BOTH; break; - default: break; - } - $meta[$userId] = $type; - $this->metaStore->setMetadata( - $node, - self::$META_WATCH_NAMESPACE, - $meta, - false, - AJXP_METADATA_SCOPE_REPOSITORY - ); - } - - AJXP_XMLWriter::header(); - $node->metadata = array(); - $node->loadNodeInfo(true, false, "all"); - $this->enrichNode($node); - AJXP_XMLWriter::writeNodesDiff(array("UPDATE" => array( $node->getPath() => $node )), true); - AJXP_XMLWriter::close(); - - break; - - default: - break; - - } - } - - - public function processChangeHook(AJXP_Node $oldNode=null, AJXP_Node $newNode=null, $copy = false) - { - $newNotif = $this->notificationCenter->generateNotificationFromChangeHook($oldNode, $newNode, $copy, "new"); - if ($newNotif !== false && $newNotif->getNode() !== false) { - $this->processActiveHook($newNotif, self::$META_WATCH_CHANGE, AJXP_NOTIF_NODE_CHANGE); - } - if($oldNode != null && $newNode != null && $oldNode->getUrl() == $newNode->getUrl()) return; - $oldNotif = $this->notificationCenter->generateNotificationFromChangeHook($oldNode, $newNode, $copy, "old"); - if ($oldNotif !== false && $oldNotif->getNode() !== false) { - $this->processActiveHook($oldNotif, self::$META_WATCH_CHANGE, AJXP_NOTIF_NODE_CHANGE); - } - - $this->updateMetaLocation($oldNode, $newNode, $copy); - - } - - public function processReadHook(AJXP_Node $node) - { - $notification = new AJXP_Notification(); - $notification->setAction(AJXP_NOTIF_NODE_VIEW); - $notification->setNode($node); - $this->processActiveHook($notification, self::$META_WATCH_READ); - - } - - private function processActiveHook(AJXP_Notification $notification, $namespace, $parentActionType = null){ - $origNode = $notification->getNode(); - $node = new AJXP_Node($origNode->getUrl()); - $all = $this->collectWatches($node, $namespace); - if($node->isRoot() && $node->getRepository() !== null && $node->getRepository()->hasContentFilter()){ - $node->setLeaf(true); - }else{ - $node->setLeaf(false); - } - foreach($all["node"] as $id) { - $this->notificationCenter->postNotification($notification, $id); - } - foreach($all["ancestors"] as $pair){ - $parentNotification = new AJXP_Notification(); - /** - * @var AJXP_Node $parentNode - */ - $parentNode = $pair["node"]; - if($parentNode->isRoot() && $parentNode->getRepository() !== null && $parentNode->getRepository()->hasContentFilter()){ - $parentNode->setLeaf(true); - }else{ - $parentNode->setLeaf(false); - } - $parentNotification->setNode($parentNode); - if($parentActionType == null){ - $parentNotification->setAction($notification->getAction()); - }else{ - $parentNotification->setAction($parentActionType); - } - $this->notificationCenter->prepareNotification($notification); - $parentNotification->addRelatedNotification($notification); - $this->notificationCenter->postNotification($parentNotification, $pair["id"]); - } - } - - /** - * @param AJXP_Node $node - */ - public function enrichNode($node) - { - if(AuthService::getLoggedUser() == null) return; - $meta = $this->metaStore->retrieveMetadata( - $node, - self::$META_WATCH_NAMESPACE, - false, - AJXP_METADATA_SCOPE_REPOSITORY); - if(is_array($meta) - && array_key_exists(AuthService::getLoggedUser()->getId(), $meta)){ - $node->mergeMetadata(array( - "meta_watched" => $meta[AuthService::getLoggedUser()->getId()], - "overlay_icon" => "meta.watch/ICON_SIZE/watch.png", - "overlay_class" => "icon-eye-open" - ), true); - } - } - - /** - * - * @param AJXP_Node $oldFile - * @param AJXP_Node $newFile - * @param Boolean $copy - */ - public function updateMetaLocation($oldFile, $newFile = null, $copy = false) - { - if($oldFile == null) return; - if(!$copy && $this->metaStore->inherentMetaMove()) return; - - $oldMeta = $this->metaStore->retrieveMetadata($oldFile, self::$META_WATCH_NAMESPACE, false, AJXP_METADATA_SCOPE_REPOSITORY); - if (count($oldMeta)) { - // If it's a move or a delete, delete old data - if (!$copy) { - $this->metaStore->removeMetadata($oldFile, self::$META_WATCH_NAMESPACE, false, AJXP_METADATA_SCOPE_REPOSITORY); - } - // If copy or move, copy data. - if ($newFile != null) { - $this->metaStore->setMetadata($newFile, self::$META_WATCH_NAMESPACE, $oldMeta, false, AJXP_METADATA_SCOPE_REPOSITORY); - } - } - - } - -} diff --git a/core/src/plugins/meta.watch/manifest.xml b/core/src/plugins/meta.watch/manifest.xml index d7953ac2e1..068bb5e10f 100755 --- a/core/src/plugins/meta.watch/manifest.xml +++ b/core/src/plugins/meta.watch/manifest.xml @@ -1,6 +1,6 @@ - + @@ -12,7 +12,7 @@ - + @@ -104,7 +104,7 @@ - + diff --git a/core/src/plugins/metastore.s3/S3MetaStore.php b/core/src/plugins/metastore.s3/S3MetaStore.php new file mode 100644 index 0000000000..8343e56400 --- /dev/null +++ b/core/src/plugins/metastore.s3/S3MetaStore.php @@ -0,0 +1,250 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Metastore\Implementation; + +use Pydio\Access\Core\AbstractAccessDriver; +use Pydio\Core\Model\ContextInterface; + +use Pydio\Access\Meta\Core\AbstractMetaSource; +use Pydio\Access\Metastore\Core\IMetaStoreProvider; + +defined('AJXP_EXEC') or die( 'Access not allowed'); +/** + * Simple metadata implementation, coupled with an S3 repository, stores + * the metadata in the s3 bucket + */ +class S3MetaStore extends AbstractMetaSource implements IMetaStoreProvider +{ + private static $metaCache; + protected $bucketName; + + /** + * @param ContextInterface $ctx + * @param AbstractAccessDriver $accessDriver + */ + public function initMeta(ContextInterface $ctx, AbstractAccessDriver $accessDriver) + { + parent::initMeta($ctx, $accessDriver); + $this->bucketName = $ctx->getRepository()->getContextOption($ctx, "CONTAINER"); + } + + /** + * @abstract + * @return bool + */ + public function inherentMetaMove() + { + return true; + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $node + * @return string + */ + protected function getUserId($node) + { + if($node->hasUser()) return $node->getUserId(); + return "shared"; + } + + /** + * @return \aws\S3\S3Client + */ + protected function getAwsService(ContextInterface $ctx) + { + if(method_exists($ctx->getRepository()->getDriverInstance($ctx), "getS3Service")){ + return $ctx->getRepository()->getDriverInstance($ctx)->getS3Service(); + } + return null; + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $ajxpNode + * @param boolean $create + * @return String + */ + private function updateNodeMetaPath($ajxpNode, $create = false){ + $trim = trim($ajxpNode->getPath(), "/"); + if($ajxpNode->is_file !== null){ + $folder = !$ajxpNode->isLeaf(); + }else{ + $folder = !is_file($ajxpNode->getUrl()); + } + if(!$folder) return $trim; + $meta = is_file(rtrim($ajxpNode->getUrl(), "/")."/.meta"); + if(!$meta){ + if($create) file_put_contents(rtrim($ajxpNode->getUrl(), "/")."/.meta", "meta"); + else return null; + } + return $trim."/.meta"; + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $ajxpNode + * @param String $nameSpace + * @param array $metaData + * @param bool $private + * @param int $scope + */ + public function setMetadata($ajxpNode, $nameSpace, $metaData, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY) + { + $aws = $this->getAwsService($ajxpNode->getContext()); + if($aws == null) return; + $user = ($private?$this->getUserId($ajxpNode):AJXP_METADATA_SHAREDUSER); + $pathName = $this->updateNodeMetaPath($ajxpNode, true); + $response = $aws->copyObject( + array( + 'Bucket' => $this->bucketName, + 'Key' => $pathName, + 'CopySource' => $this->bucketName."/".rawurlencode($pathName), + 'MetadataDirective' => 'REPLACE', + 'Metadata' => array($this->getMetaKey($nameSpace,$scope,$user) => base64_encode(serialize($metaData))) + ) + ); + $this->logDebug("UPDATE RESPONSE", $response); + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $ajxpNode + * @param String $nameSpace + * @param bool $private + * @param int $scope + */ + public function removeMetadata($ajxpNode, $nameSpace, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY) + { + $aws = $this->getAwsService($ajxpNode->getContext()); + if($aws == null) return; + $user = ($private?$this->getUserId($ajxpNode):AJXP_METADATA_SHAREDUSER); + $pathName = $this->updateNodeMetaPath($ajxpNode, false); + if($pathName != null){ + $aws->copyObject( + array( + 'Bucket' => $this->bucketName, + 'Key' => $pathName, + 'CopySource' => $this->bucketName."/".rawurlencode($pathName), + 'MetadataDirective' => 'REPLACE', + 'Metadata' => array($this->getMetaKey($nameSpace,$scope,$user) => "") + ) + ); + } + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $ajxpNode + * @param String $nameSpace + * @param bool $private + * @param int $scope + * @return array|mixed + */ + public function retrieveMetadata($ajxpNode, $nameSpace, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY) + { + $aws = $this->getAwsService($ajxpNode->getContext()); + if($aws == null) return array(); + + if (isSet(self::$metaCache[$ajxpNode->getPath()])) { + $data = self::$metaCache[$ajxpNode->getPath()]; + } else { + $pathName = $this->updateNodeMetaPath($ajxpNode, false); + if($pathName == null) return []; + try{ + $response = $aws->headObject(array("Bucket" => $this->bucketName, "Key" => $pathName)); + $metadata = $response["Metadata"]; + if($metadata == null){ + $metadata = array(); + } + }catch(Aws\S3\Exception\S3Exception $e){ + $metadata = array(); + } + self::$metaCache[$ajxpNode->getPath()] = $metadata; + $data = self::$metaCache[$ajxpNode->getPath()]; + } + if($private === AJXP_METADATA_ALLUSERS){ + $startKey = $this->getMetaKey($nameSpace, $scope, ""); + $arrMeta = array(); + foreach($data as $k => $mData){ + if(strpos($k, $startKey) === 0){ + $decData = unserialize(base64_decode($mData)); + if(is_array($decData)) $arrMeta = array_merge_recursive($arrMeta, $decData); + } + } + return $arrMeta; + }else{ + $user = ($private?$this->getUserId($ajxpNode):AJXP_METADATA_SHAREDUSER); + $mKey = $this->getMetaKey($nameSpace,$scope,$user); + if (isSet($data[$mKey])) { + $arrMeta = unserialize(base64_decode($data[$mKey])); + if(is_array($arrMeta)) return $arrMeta; + } + } + return array(); + } + + /** + * @param $namespace + * @param $scope + * @param $user + * @return string + */ + private function getMetaKey($namespace, $scope, $user) + { + return strtolower($namespace."-".$scope."-".$user); + } + + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $ajxpNode + * @return void + */ + public function enrichNode(&$ajxpNode) + { + // Try both + $aws = $this->getAwsService($ajxpNode->getContext()); + if($aws == null) return; + + if (isSet(self::$metaCache[$ajxpNode->getPath()])) { + $data = self::$metaCache[$ajxpNode->getPath()]; + } else { + $this->logDebug("Should retrieve metadata for ".$ajxpNode->getPath()); + $pathName = $this->updateNodeMetaPath($ajxpNode, false); + if($pathName == null) return; + try{ + $response = $aws->headObject(array("Bucket" => $this->bucketName, "Key" => $pathName)); + $metadata = $response["Metadata"]; + if($metadata == null){ + $metadata = array(); + } + }catch (Aws\S3\Exception\S3Exception $e){ + $metadata = array(); + } + self::$metaCache[$ajxpNode->getPath()] = $metadata; + $data = self::$metaCache[$ajxpNode->getPath()]; + } + $allMeta = array(); + foreach ($data as $amzKey => $value) { + $parts = explode("-", $amzKey); + $all[$parts[0]] = $value; + } + + $ajxpNode->mergeMetadata($allMeta); + } + + +} diff --git a/core/src/plugins/metastore.s3/class.s3MetaStore.php b/core/src/plugins/metastore.s3/class.s3MetaStore.php deleted file mode 100755 index 642c7df9a0..0000000000 --- a/core/src/plugins/metastore.s3/class.s3MetaStore.php +++ /dev/null @@ -1,228 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die( 'Access not allowed'); -/** - * Simple metadata implementation, coupled with an S3 repository, stores - * the metadata in the s3 bucket - * @package AjaXplorer_Plugins - * @subpackage Metastore - */ -class s3MetaStore extends AJXP_AbstractMetaSource implements MetaStoreProvider -{ - private static $currentMetaName; - private static $metaCache; - private static $fullMetaCache; - - protected $globalMetaFile; - protected $bucketName; - - - public function init($options) - { - $this->options = $options; - $this->loadRegistryContributions(); - $this->globalMetaFile = AJXP_DATA_PATH."/plugins/metastore.serial/ajxp_meta"; - } - - public function initMeta($accessDriver) - { - parent::initMeta($accessDriver); - $this->bucketName = $this->accessDriver->repository->getOption("CONTAINER"); - } - - /** - * @abstract - * @return bool - */ - public function inherentMetaMove() - { - return true; - } - - /** - * @param AJXP_Node $node - * @return string - */ - protected function getUserId($node) - { - if($node->hasUser()) return $node->getUser(); - if(AuthService::usersEnabled()) return AuthService::getLoggedUser()->getId(); - return "shared"; - } - - /** - * @return \aws\S3\S3Client - */ - protected function getAwsService() - { - if(method_exists($this->accessDriver, "getS3Service")){ - return $this->accessDriver->getS3Service(); - } - return null; - } - - /** - * @param AJXP_Node $ajxpNode - * @param boolean $create - * @return String - */ - private function updateNodeMetaPath($ajxpNode, $create = false){ - $trim = trim($ajxpNode->getPath(), "/"); - if($ajxpNode->is_file !== null){ - $folder = !$ajxpNode->isLeaf(); - }else{ - $folder = !is_file($ajxpNode->getUrl()); - } - if(!$folder) return $trim; - $meta = is_file(rtrim($ajxpNode->getUrl(), "/")."/.meta"); - if(!$meta){ - if($create) file_put_contents(rtrim($ajxpNode->getUrl(), "/")."/.meta", "meta"); - else return null; - } - return $trim."/.meta"; - } - - public function setMetadata($ajxpNode, $nameSpace, $metaData, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY) - { - $aws = $this->getAwsService(); - if($aws == null) return; - $user = ($private?$this->getUserId($ajxpNode):AJXP_METADATA_SHAREDUSER); - $pathName = $this->updateNodeMetaPath($ajxpNode, true); - $response = $aws->copyObject( - array( - 'Bucket' => $this->bucketName, - 'Key' => $pathName, - 'CopySource' => $this->bucketName."/".rawurlencode($pathName), - 'MetadataDirective' => 'REPLACE', - 'Metadata' => array($this->getMetaKey($nameSpace,$scope,$user) => base64_encode(serialize($metaData))) - ) - ); - $this->logDebug("UPDATE RESPONSE", $response); - } - - public function removeMetadata($ajxpNode, $nameSpace, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY) - { - $aws = $this->getAwsService(); - if($aws == null) return; - $user = ($private?$this->getUserId($ajxpNode):AJXP_METADATA_SHAREDUSER); - $pathName = $this->updateNodeMetaPath($ajxpNode, false); - if($pathName != null){ - $aws->copyObject( - array( - 'Bucket' => $this->bucketName, - 'Key' => $pathName, - 'CopySource' => $this->bucketName."/".rawurlencode($pathName), - 'MetadataDirective' => 'REPLACE', - 'Metadata' => array($this->getMetaKey($nameSpace,$scope,$user) => "") - ) - ); - } - } - - public function retrieveMetadata($ajxpNode, $nameSpace, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY) - { - $aws = $this->getAwsService(); - if($aws == null) return array(); - - if (isSet(self::$metaCache[$ajxpNode->getPath()])) { - $data = self::$metaCache[$ajxpNode->getPath()]; - } else { - $pathName = $this->updateNodeMetaPath($ajxpNode, false); - if($pathName == null) return; - try{ - $response = $aws->headObject(array("Bucket" => $this->bucketName, "Key" => $pathName)); - $metadata = $response["Metadata"]; - if($metadata == null){ - $metadata = array(); - } - }catch(Aws\S3\Exception\S3Exception $e){ - $metadata = array(); - } - self::$metaCache[$ajxpNode->getPath()] = $metadata; - $data = self::$metaCache[$ajxpNode->getPath()]; - } - if($private === AJXP_METADATA_ALLUSERS){ - $startKey = $this->getMetaKey($nameSpace, $scope, ""); - $arrMeta = array(); - foreach($data as $k => $mData){ - if(strpos($k, $startKey) === 0){ - $decData = unserialize(base64_decode($mData)); - if(is_array($decData)) $arrMeta = array_merge_recursive($arrMeta, $decData); - } - } - return $arrMeta; - }else{ - $user = ($private?$this->getUserId($ajxpNode):AJXP_METADATA_SHAREDUSER); - $mKey = $this->getMetaKey($nameSpace,$scope,$user); - if (isSet($data[$mKey])) { - $arrMeta = unserialize(base64_decode($data[$mKey])); - if(is_array($arrMeta)) return $arrMeta; - } - } - return array(); - } - - private function getMetaKey($namespace, $scope, $user) - { - return strtolower($namespace."-".$scope."-".$user); - } - - - /** - * @param AJXP_Node $ajxpNode - * @return void - */ - public function enrichNode(&$ajxpNode) - { - // Try both - $aws = $this->getAwsService(); - if($aws == null) return; - - if (isSet(self::$metaCache[$ajxpNode->getPath()])) { - $data = self::$metaCache[$ajxpNode->getPath()]; - } else { - $this->logDebug("Should retrieve metadata for ".$ajxpNode->getPath()); - $pathName = $this->updateNodeMetaPath($ajxpNode, false); - if($pathName == null) return; - try{ - $response = $aws->headObject(array("Bucket" => $this->bucketName, "Key" => $pathName)); - $metadata = $response["Metadata"]; - if($metadata == null){ - $metadata = array(); - } - }catch (Aws\S3\Exception\S3Exception $e){ - $metadata = array(); - } - self::$metaCache[$ajxpNode->getPath()] = $metadata; - $data = self::$metaCache[$ajxpNode->getPath()]; - } - $allMeta = array(); - foreach ($data as $amzKey => $value) { - $parts = explode("-", $amzKey); - $all[$parts[0]] = $value; - } - - $ajxpNode->mergeMetadata($allMeta); - } - - -} diff --git a/core/src/plugins/metastore.s3/i18n/conf/de.php b/core/src/plugins/metastore.s3/i18n/conf/de.php index d6e85ed3a6..a8d7227ce6 100755 --- a/core/src/plugins/metastore.s3/i18n/conf/de.php +++ b/core/src/plugins/metastore.s3/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "S3 MetaStore" => "Metadaten in S3", diff --git a/core/src/plugins/metastore.s3/i18n/conf/en.php b/core/src/plugins/metastore.s3/i18n/conf/en.php index d79fe3b169..7abaf32b24 100755 --- a/core/src/plugins/metastore.s3/i18n/conf/en.php +++ b/core/src/plugins/metastore.s3/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "S3 MetaStore" => "MetaStore S3", diff --git a/core/src/plugins/metastore.s3/i18n/conf/fr.php b/core/src/plugins/metastore.s3/i18n/conf/fr.php index ede5e20e1b..afb297546f 100755 --- a/core/src/plugins/metastore.s3/i18n/conf/fr.php +++ b/core/src/plugins/metastore.s3/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "S3 MetaStore" => "Stockage Meta S3", diff --git a/core/src/plugins/metastore.s3/i18n/conf/it.php b/core/src/plugins/metastore.s3/i18n/conf/it.php index 76fd214216..c938e8c5ac 100755 --- a/core/src/plugins/metastore.s3/i18n/conf/it.php +++ b/core/src/plugins/metastore.s3/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "S3 MetaStore" => "MetaStore S3", diff --git a/core/src/plugins/metastore.s3/i18n/conf/pt.php b/core/src/plugins/metastore.s3/i18n/conf/pt.php index ca09308138..1fc4ba62e8 100644 --- a/core/src/plugins/metastore.s3/i18n/conf/pt.php +++ b/core/src/plugins/metastore.s3/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "S3 MetaStore" => "MetaStore S3", diff --git a/core/src/plugins/metastore.s3/manifest.xml b/core/src/plugins/metastore.s3/manifest.xml index 5cf00fafbe..b095b43e5e 100755 --- a/core/src/plugins/metastore.s3/manifest.xml +++ b/core/src/plugins/metastore.s3/manifest.xml @@ -1,6 +1,6 @@ - + diff --git a/core/src/plugins/metastore.serial/SerialMetaStore.php b/core/src/plugins/metastore.serial/SerialMetaStore.php new file mode 100644 index 0000000000..071fba85f8 --- /dev/null +++ b/core/src/plugins/metastore.serial/SerialMetaStore.php @@ -0,0 +1,404 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Metastore\Implementation; + +use Pydio\Access\Core\MetaStreamWrapper; +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Core\Controller\Controller; +use Pydio\Access\Meta\Core\AbstractMetaSource; +use Pydio\Access\Metastore\Core\IMetaStoreProvider; +use Pydio\Core\Utils\TextEncoder; +use Pydio\Core\Utils\Vars\PathUtils; +use Pydio\Core\Utils\Vars\StatHelper; + +defined('AJXP_EXEC') or die( 'Access not allowed'); +/** + * Simple metadata implementation, stored in hidden files inside the + * folders + */ +class SerialMetaStore extends AbstractMetaSource implements IMetaStoreProvider +{ + private static $currentMetaName; + private static $metaCache; + private static $fullMetaCache; + + protected $globalMetaFile; + + /** + * @param \Pydio\Core\Model\ContextInterface $ctx + * @param array $options + */ + public function init(\Pydio\Core\Model\ContextInterface $ctx, $options = []) + { + $this->options = $options; + $this->globalMetaFile = AJXP_DATA_PATH."/plugins/metastore.serial/ajxp_meta"; + } + + /** + * @abstract + * @return bool + */ + public function inherentMetaMove() + { + return false; + } + + + /** + * @param AJXP_Node $node + * @return string + */ + protected function getUserId($node) + { + if($node->hasUser()) return $node->getUserId(); + return "shared"; + } + + /** + * @param AJXP_Node $ajxpNode + * @param String $nameSpace + * @param array $metaData + * @param bool $private + * @param int $scope + */ + public function setMetadata($ajxpNode, $nameSpace, $metaData, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY) + { + $this->loadMetaFileData( + $ajxpNode, + $scope, + ($private?$this->getUserId($ajxpNode):AJXP_METADATA_SHAREDUSER) + ); + if (!isSet(self::$metaCache[$nameSpace])) { + self::$metaCache[$nameSpace] = array(); + } + self::$metaCache[$nameSpace] = array_merge(self::$metaCache[$nameSpace], $metaData); + if(is_array(self::$metaCache[$nameSpace])){ + foreach(self::$metaCache[$nameSpace] as $k => $v){ + if($v == AJXP_VALUE_CLEAR) unset(self::$metaCache[$nameSpace][$k]); + } + } + $this->saveMetaFileData( + $ajxpNode, + $scope, + ($private?$this->getUserId($ajxpNode):AJXP_METADATA_SHAREDUSER) + ); + } + + /** + * @param AJXP_Node $ajxpNode + * @param String $nameSpace + * @param bool $private + * @param int $scope + * @return array|void + */ + public function removeMetadata($ajxpNode, $nameSpace, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY) + { + $this->loadMetaFileData( + $ajxpNode, + $scope, + ($private?$this->getUserId($ajxpNode):AJXP_METADATA_SHAREDUSER) + ); + if(!isSet(self::$metaCache[$nameSpace])) return; + unset(self::$metaCache[$nameSpace]); + $this->saveMetaFileData( + $ajxpNode, + $scope, + ($private?$this->getUserId($ajxpNode):AJXP_METADATA_SHAREDUSER) + ); + } + + /** + * @param AJXP_Node $ajxpNode + * @param String $nameSpace + * @param bool $private + * @param int $scope + * @return array + */ + public function retrieveMetadata($ajxpNode, $nameSpace, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY) + { + if($private === AJXP_METADATA_ALLUSERS){ + $userScope = AJXP_METADATA_ALLUSERS; + }else if($private === true){ + $userScope = $this->getUserId($ajxpNode); + }else{ + $userScope = AJXP_METADATA_SHAREDUSER; + } + $this->loadMetaFileData( + $ajxpNode, + $scope, + $userScope + ); + if(!isSet(self::$metaCache[$nameSpace])) return array(); + else return self::$metaCache[$nameSpace]; + } + + + + /** + * @param AJXP_Node $ajxpNode + * @return void + */ + public function enrichNode(&$ajxpNode) + { + // Try both + $all = array(); + $this->loadMetaFileData($ajxpNode, AJXP_METADATA_SCOPE_GLOBAL, AJXP_METADATA_SHAREDUSER); + $all[] = self::$metaCache; + $this->loadMetaFileData($ajxpNode, AJXP_METADATA_SCOPE_GLOBAL, $this->getUserId($ajxpNode)); + $all[] = self::$metaCache; + $this->loadMetaFileData($ajxpNode, AJXP_METADATA_SCOPE_REPOSITORY, AJXP_METADATA_SHAREDUSER); + $all[] = self::$metaCache; + $this->loadMetaFileData($ajxpNode, AJXP_METADATA_SCOPE_REPOSITORY, $this->getUserId($ajxpNode)); + $all[] = self::$metaCache; + $allMeta = array(); + foreach ($all as $metadata) { + foreach ($metadata as $namespace => $meta) { + foreach ($meta as $key => $value) { + $allMeta[$namespace."-".$key] = $value; + } + } + } + $ajxpNode->mergeMetadata($allMeta); + } + + /** + * @param $metaFile + * @param \Pydio\Core\Model\ContextInterface $ctx + * @return string + */ + protected function updateSecurityScope($metaFile, \Pydio\Core\Model\ContextInterface $ctx) + { + $repo = $ctx->getRepository(); + if (!is_object($repo)) { + return $metaFile; + } + $securityScope = $repo->securityScope(); + if($securityScope == false) return $metaFile; + if($ctx->hasUser()){ + if ($securityScope == "USER") { + $metaFile .= "_".$ctx->getUser()->getId(); + }else if($securityScope == "GROUP"){ + $uObj= $ctx->getUser(); + if($uObj != null){ + $u = str_replace("/", "__", $uObj->getGroupPath()); + $metaFile.= "_".$u; + } + } + } + return $metaFile; + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $ajxpNode + * @param String $scope + * @param String $userId + * @return void + */ + protected function loadMetaFileData($ajxpNode, $scope, $userId) + { + $currentFile = $ajxpNode->getUrl(); + $fileKey = $ajxpNode->getPath(); + if($fileKey == null) $fileKey = "/"; + if (isSet($this->options["METADATA_FILE_LOCATION"]) && $this->options["METADATA_FILE_LOCATION"] == "outside") { + // Force scope + $scope = AJXP_METADATA_SCOPE_REPOSITORY; + } + if ($scope == AJXP_METADATA_SCOPE_GLOBAL) { + $metaFile = dirname($currentFile)."/".$this->options["METADATA_FILE"]; + if (preg_match("/\.zip\//",$currentFile)) { + self::$fullMetaCache[$metaFile] = array(); + self::$metaCache = array(); + return ; + } + $fileKey = basename($fileKey); + } else { + $metaFile = $this->globalMetaFile."_".$ajxpNode->getRepositoryId(); + $metaFile = $this->updateSecurityScope($metaFile, $ajxpNode->getContext()); + } + self::$metaCache = array(); + if (!isSet(self::$fullMetaCache[$metaFile])) { + self::$currentMetaName = $metaFile; + $rawData = @file_get_contents($metaFile); + if ($rawData !== false) { + self::$fullMetaCache[$metaFile] = unserialize($rawData); + } + } + if (isSet(self::$fullMetaCache[$metaFile]) && is_array(self::$fullMetaCache[$metaFile])) { + if($userId == AJXP_METADATA_ALLUSERS && is_array(self::$fullMetaCache[$metaFile][$fileKey])){ + foreach(self::$fullMetaCache[$metaFile][$fileKey] as $uId => $data){ + self::$metaCache = array_merge_recursive(self::$metaCache, $data); + } + }else{ + if (isSet(self::$fullMetaCache[$metaFile][$fileKey][$userId])) { + self::$metaCache = self::$fullMetaCache[$metaFile][$fileKey][$userId]; + } else { + if ($this->options["UPGRADE_FROM_METASERIAL"] == true && count(self::$fullMetaCache[$metaFile]) && !isSet(self::$fullMetaCache[$metaFile]["AJXP_METASTORE_UPGRADED"])) { + self::$fullMetaCache[$metaFile] = $this->upgradeDataFromMetaSerial(self::$fullMetaCache[$metaFile]); + if (isSet(self::$fullMetaCache[$metaFile][$fileKey][$userId])) { + self::$metaCache = self::$fullMetaCache[$metaFile][$fileKey][$userId]; + } + // Save upgraded version + file_put_contents($metaFile, serialize(self::$fullMetaCache[$metaFile])); + } + } + } + } else { + self::$fullMetaCache[$metaFile] = array(); + self::$metaCache = array(); + } + } + + /** + * @param AJXP_Node $ajxpNode + * @param String $scope + * @param String $userId + */ + protected function saveMetaFileData($ajxpNode, $scope, $userId) + { + $currentFile = $ajxpNode->getUrl(); + $repositoryId = $ajxpNode->getRepositoryId(); + $fileKey = $ajxpNode->getPath(); + if(empty($fileKey)) $fileKey = "/"; + if (isSet($this->options["METADATA_FILE_LOCATION"]) && $this->options["METADATA_FILE_LOCATION"] == "outside") { + // Force scope + $scope = AJXP_METADATA_SCOPE_REPOSITORY; + } + if ($scope == AJXP_METADATA_SCOPE_GLOBAL) { + $metaFile = dirname($currentFile)."/".$this->options["METADATA_FILE"]; + $fileKey = basename($fileKey); + } else { + if (!is_dir(dirname($this->globalMetaFile))) { + mkdir(dirname($this->globalMetaFile), 0755, true); + } + $metaFile = $this->globalMetaFile."_".$repositoryId; + $metaFile = $this->updateSecurityScope($metaFile, $ajxpNode->getContext()); + } + $metFileNode = new AJXP_Node($metaFile); + $driver = $metFileNode->getDriver(); + if($scope==AJXP_METADATA_SCOPE_REPOSITORY + || (@is_file($metaFile) && call_user_func(array($driver, "isWriteable"), $metFileNode)) + || call_user_func(array($driver, "isWriteable"), $metFileNode->getParent()) ){ + if (is_array(self::$metaCache) && count(self::$metaCache)) { + if (!isset(self::$fullMetaCache[$metaFile])) { + self::$fullMetaCache[$metaFile] = array(); + } + if (!isset(self::$fullMetaCache[$metaFile][$fileKey])) { + self::$fullMetaCache[$metaFile][$fileKey] = array(); + } + if (!isset(self::$fullMetaCache[$metaFile][$fileKey][$userId])) { + self::$fullMetaCache[$metaFile][$fileKey][$userId] = array(); + } + self::$fullMetaCache[$metaFile][$fileKey][$userId] = self::$metaCache; + } else { + // CLEAN + if (isset(self::$fullMetaCache[$metaFile][$fileKey][$userId])) { + unset(self::$fullMetaCache[$metaFile][$fileKey][$userId]); + } + if(isset(self::$fullMetaCache[$metaFile][$fileKey]) + && !count(self::$fullMetaCache[$metaFile][$fileKey])){ + unset(self::$fullMetaCache[$metaFile][$fileKey]); + } + } + $writeMode = "w"; + $nodeIsWinLocal = false; + if($scope === AJXP_METADATA_SCOPE_GLOBAL && $this->nodeIsLocalWindowsFS($ajxpNode)) { + $nodeIsWinLocal = true; + if (file_exists($metaFile)) { + $writeMode = "rw+"; + $nodeIsWinLocal = false; + } + } + $fp = @fopen($metaFile, $writeMode); + if ($fp !== false) { + @fwrite($fp, serialize(self::$fullMetaCache[$metaFile]), strlen(serialize(self::$fullMetaCache[$metaFile]))); + @fclose($fp); + if($nodeIsWinLocal){ + $real_path_metafile = realpath(MetaStreamWrapper::getRealFSReference($metaFile)); + if (is_dir(dirname($real_path_metafile))) { + StatHelper::winSetHidden($real_path_metafile); + } + } + }else{ + $this->logError(__FUNCTION__, "Error while trying to open the meta file, maybe a permission problem?"); + } + if ($scope == AJXP_METADATA_SCOPE_GLOBAL) { + Controller::applyHook("version.commit_file", array($metaFile, $ajxpNode)); + } + } + } + + /** + * @param AJXP_Node $ajxpNode + * @param string $nameSpace + * @param string $userScope + * @return array + */ + public function collectChildrenWithRepositoryMeta($ajxpNode, $nameSpace, $userScope){ + $result = array(); + $repositoryId = $ajxpNode->getRepositoryId(); + $metaFile = $this->globalMetaFile."_".$repositoryId; + $metaFile = $this->updateSecurityScope($metaFile, $ajxpNode->getContext()); + if(!is_file($metaFile)) return $result; + $raw_data = file_get_contents($metaFile); + if($raw_data === false) return $result; + $metadata = unserialize($raw_data); + if($metadata === false || !is_array($metadata)) return $result; + $srcPath = $ajxpNode->getPath(); + if($srcPath == "/") $srcPath = ""; + foreach($metadata as $path => $data){ + preg_match("#^".preg_quote($srcPath, "#")."/#", $path, $matches); + if($path == $srcPath || count($matches)) { + $relativePath = substr($path, strlen($srcPath)); // REMOVE ORIGINAL NODE PATH + if($relativePath === false) $relativePath = "/"; + foreach($data as $userId => $meta){ + if(($userScope == $userId || $userScope == AJXP_METADATA_ALLUSERS) && isSet($meta[$nameSpace])){ + if(!isSet($result[$relativePath])) $result[$relativePath] = array(); + $result[$relativePath][$userId] = $meta[$nameSpace]; + } + } + } + } + return $result; + } + + /** + * @param AJXP_Node $ajxpNode + * @return bool + */ + private function nodeIsLocalWindowsFS($ajxpNode){ + return (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' && !MetaStreamWrapper::wrapperIsRemote($ajxpNode->getUrl())); + } + + /** + * @param $data + * @return array + */ + protected function upgradeDataFromMetaSerial($data) + { + $new = array(); + foreach ($data as $fileKey => $fileData) { + $new[$fileKey] = array(AJXP_METADATA_SHAREDUSER => array( "users_meta" => $fileData )); + $new["AJXP_METASTORE_UPGRADED"] = true; + } + return $new; + } + +} diff --git a/core/src/plugins/metastore.serial/class.SerialMetaStore.php b/core/src/plugins/metastore.serial/class.SerialMetaStore.php deleted file mode 100644 index f674fd4247..0000000000 --- a/core/src/plugins/metastore.serial/class.SerialMetaStore.php +++ /dev/null @@ -1,392 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die( 'Access not allowed'); -/** - * Simple metadata implementation, stored in hidden files inside the - * folders - * @package AjaXplorer_Plugins - * @subpackage Metastore - */ -class SerialMetaStore extends AJXP_AbstractMetaSource implements MetaStoreProvider -{ - private static $currentMetaName; - private static $metaCache; - private static $fullMetaCache; - - protected $globalMetaFile; - - - public function init($options) - { - $this->options = $options; - $this->loadRegistryContributions(); - $this->globalMetaFile = AJXP_DATA_PATH."/plugins/metastore.serial/ajxp_meta"; - } - - /** - * @abstract - * @return bool - */ - public function inherentMetaMove() - { - return false; - } - - - /** - * @param AJXP_Node $node - * @return string - */ - protected function getUserId($node) - { - if($node->hasUser()) return $node->getUser(); - if(AuthService::usersEnabled()) return AuthService::getLoggedUser()->getId(); - return "shared"; - } - - public function setMetadata($ajxpNode, $nameSpace, $metaData, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY) - { - $this->loadMetaFileData( - $ajxpNode, - $scope, - ($private?$this->getUserId($ajxpNode):AJXP_METADATA_SHAREDUSER) - ); - if (!isSet(self::$metaCache[$nameSpace])) { - self::$metaCache[$nameSpace] = array(); - } - self::$metaCache[$nameSpace] = array_merge(self::$metaCache[$nameSpace], $metaData); - if(is_array(self::$metaCache[$nameSpace])){ - foreach(self::$metaCache[$nameSpace] as $k => $v){ - if($v == AJXP_VALUE_CLEAR) unset(self::$metaCache[$nameSpace][$k]); - } - } - $this->saveMetaFileData( - $ajxpNode, - $scope, - ($private?$this->getUserId($ajxpNode):AJXP_METADATA_SHAREDUSER) - ); - } - - public function removeMetadata($ajxpNode, $nameSpace, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY) - { - $this->loadMetaFileData( - $ajxpNode, - $scope, - ($private?$this->getUserId($ajxpNode):AJXP_METADATA_SHAREDUSER) - ); - if(!isSet(self::$metaCache[$nameSpace])) return; - unset(self::$metaCache[$nameSpace]); - $this->saveMetaFileData( - $ajxpNode, - $scope, - ($private?$this->getUserId($ajxpNode):AJXP_METADATA_SHAREDUSER) - ); - } - - public function retrieveMetadata($ajxpNode, $nameSpace, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY) - { - if($private === AJXP_METADATA_ALLUSERS){ - $userScope = AJXP_METADATA_ALLUSERS; - }else if($private === true){ - $userScope = $this->getUserId($ajxpNode); - }else{ - $userScope = AJXP_METADATA_SHAREDUSER; - } - $this->loadMetaFileData( - $ajxpNode, - $scope, - $userScope - ); - if(!isSet(self::$metaCache[$nameSpace])) return array(); - else return self::$metaCache[$nameSpace]; - } - - - - /** - * @param AJXP_Node $ajxpNode - * @return void - */ - public function enrichNode(&$ajxpNode) - { - // Try both - $all = array(); - $this->loadMetaFileData($ajxpNode, AJXP_METADATA_SCOPE_GLOBAL, AJXP_METADATA_SHAREDUSER); - $all[] = self::$metaCache; - $this->loadMetaFileData($ajxpNode, AJXP_METADATA_SCOPE_GLOBAL, $this->getUserId($ajxpNode)); - $all[] = self::$metaCache; - $this->loadMetaFileData($ajxpNode, AJXP_METADATA_SCOPE_REPOSITORY, AJXP_METADATA_SHAREDUSER); - $all[] = self::$metaCache; - $this->loadMetaFileData($ajxpNode, AJXP_METADATA_SCOPE_REPOSITORY, $this->getUserId($ajxpNode)); - $all[] = self::$metaCache; - $allMeta = array(); - foreach ($all as $metadata) { - foreach ($metadata as $namespace => $meta) { - foreach ($meta as $key => $value) { - $allMeta[$namespace."-".$key] = $value; - } - } - } - $ajxpNode->mergeMetadata($allMeta); - } - - protected function updateSecurityScope($metaFile, $repositoryId, $resolveUserId = null) - { - $repo = ConfService::getRepositoryById($repositoryId); - if (!is_object($repo)) { - return $metaFile; - } - $securityScope = $repo->securityScope(); - if($securityScope == false) return $metaFile; - if($resolveUserId != null){ - if ($securityScope == "USER") { - $metaFile .= "_".$resolveUserId; - }else if($securityScope == "GROUP"){ - $uObj= ConfService::getConfStorageImpl()->createUserObject($resolveUserId); - if($uObj != null){ - $u = str_replace("/", "__", $uObj->getGroupPath()); - $metaFile.= "_".$u; - } - } - }else if (AuthService::getLoggedUser() != null) { - if ($securityScope == "USER") { - $u = AuthService::getLoggedUser(); - if($u->getResolveAsParent()) $id = $u->getParent(); - else $id = $u->getId(); - $metaFile .= "_".$id; - } else if ($securityScope == "GROUP") { - $u = AuthService::getLoggedUser()->getGroupPath(); - $u = str_replace("/", "__", $u); - $metaFile .= "_".$u; - } - } - return $metaFile; - } - - /** - * @param AJXP_Node $ajxpNode - * @param String $scope - * @param String $userId - * @return void - */ - protected function loadMetaFileData($ajxpNode, $scope, $userId) - { - $currentFile = $ajxpNode->getUrl(); - $fileKey = $ajxpNode->getPath(); - if($fileKey == null) $fileKey = "/"; - if (isSet($this->options["METADATA_FILE_LOCATION"]) && $this->options["METADATA_FILE_LOCATION"] == "outside") { - // Force scope - $scope = AJXP_METADATA_SCOPE_REPOSITORY; - } - if ($scope == AJXP_METADATA_SCOPE_GLOBAL) { - $metaFile = dirname($currentFile)."/".$this->options["METADATA_FILE"]; - if (preg_match("/\.zip\//",$currentFile)) { - self::$fullMetaCache[$metaFile] = array(); - self::$metaCache = array(); - return ; - } - $fileKey = basename($fileKey); - } else { - $metaFile = $this->globalMetaFile."_".$ajxpNode->getRepositoryId(); - $metaFile = $this->updateSecurityScope($metaFile, $ajxpNode->getRepositoryId(), $ajxpNode->getUser()); - } - self::$metaCache = array(); - if (!isSet(self::$fullMetaCache[$metaFile])) { - self::$currentMetaName = $metaFile; - $rawData = @file_get_contents($metaFile); - if ($rawData !== false) { - self::$fullMetaCache[$metaFile] = unserialize($rawData); - } - } - if (isSet(self::$fullMetaCache[$metaFile]) && is_array(self::$fullMetaCache[$metaFile])) { - if($userId == AJXP_METADATA_ALLUSERS && is_array(self::$fullMetaCache[$metaFile][$fileKey])){ - foreach(self::$fullMetaCache[$metaFile][$fileKey] as $uId => $data){ - self::$metaCache = array_merge_recursive(self::$metaCache, $data); - } - }else{ - if (isSet(self::$fullMetaCache[$metaFile][$fileKey][$userId])) { - self::$metaCache = self::$fullMetaCache[$metaFile][$fileKey][$userId]; - } else { - if ($this->options["UPGRADE_FROM_METASERIAL"] == true && count(self::$fullMetaCache[$metaFile]) && !isSet(self::$fullMetaCache[$metaFile]["AJXP_METASTORE_UPGRADED"])) { - self::$fullMetaCache[$metaFile] = $this->upgradeDataFromMetaSerial(self::$fullMetaCache[$metaFile]); - if (isSet(self::$fullMetaCache[$metaFile][$fileKey][$userId])) { - self::$metaCache = self::$fullMetaCache[$metaFile][$fileKey][$userId]; - } - // Save upgraded version - file_put_contents($metaFile, serialize(self::$fullMetaCache[$metaFile])); - } - } - } - } else { - self::$fullMetaCache[$metaFile] = array(); - self::$metaCache = array(); - } - } - - /** - * @param AJXP_Node $ajxpNode - * @param String $scope - * @param String $userId - */ - protected function saveMetaFileData($ajxpNode, $scope, $userId) - { - $currentFile = $ajxpNode->getUrl(); - $repositoryId = $ajxpNode->getRepositoryId(); - $fileKey = $ajxpNode->getPath(); - if(empty($fileKey)) $fileKey = "/"; - if (isSet($this->options["METADATA_FILE_LOCATION"]) && $this->options["METADATA_FILE_LOCATION"] == "outside") { - // Force scope - $scope = AJXP_METADATA_SCOPE_REPOSITORY; - } - if ($scope == AJXP_METADATA_SCOPE_GLOBAL) { - $metaFile = dirname($currentFile)."/".$this->options["METADATA_FILE"]; - $fileKey = basename($fileKey); - } else { - if (!is_dir(dirname($this->globalMetaFile))) { - mkdir(dirname($this->globalMetaFile), 0755, true); - } - $metaFile = $this->globalMetaFile."_".$repositoryId; - $metaFile = $this->updateSecurityScope($metaFile, $ajxpNode->getRepositoryId(), $ajxpNode->getUser()); - } - if($scope==AJXP_METADATA_SCOPE_REPOSITORY - || (@is_file($metaFile) && call_user_func(array($this->accessDriver, "isWriteable"), $metaFile)) - || call_user_func(array($this->accessDriver, "isWriteable"), dirname($metaFile)) ){ - if (is_array(self::$metaCache) && count(self::$metaCache)) { - if (!isset(self::$fullMetaCache[$metaFile])) { - self::$fullMetaCache[$metaFile] = array(); - } - if (!isset(self::$fullMetaCache[$metaFile][$fileKey])) { - self::$fullMetaCache[$metaFile][$fileKey] = array(); - } - if (!isset(self::$fullMetaCache[$metaFile][$fileKey][$userId])) { - self::$fullMetaCache[$metaFile][$fileKey][$userId] = array(); - } - self::$fullMetaCache[$metaFile][$fileKey][$userId] = self::$metaCache; - } else { - // CLEAN - if (isset(self::$fullMetaCache[$metaFile][$fileKey][$userId])) { - unset(self::$fullMetaCache[$metaFile][$fileKey][$userId]); - } - if(isset(self::$fullMetaCache[$metaFile][$fileKey]) - && !count(self::$fullMetaCache[$metaFile][$fileKey])){ - unset(self::$fullMetaCache[$metaFile][$fileKey]); - } - } - $fp = @fopen($metaFile, "w"); - if ($fp !== false) { - @fwrite($fp, serialize(self::$fullMetaCache[$metaFile]), strlen(serialize(self::$fullMetaCache[$metaFile]))); - @fclose($fp); - }else{ - $this->logError(__FUNCTION__, "Error while trying to open the meta file, maybe a permission problem?"); - } - if ($scope == AJXP_METADATA_SCOPE_GLOBAL) { - AJXP_Controller::applyHook("version.commit_file", array($metaFile, $ajxpNode)); - } - } - } - - /** - * @param AJXP_Node $ajxpNode - * @param string $nameSpace - * @param string $userScope - * @return array - */ - public function collectChildrenWithRepositoryMeta($ajxpNode, $nameSpace, $userScope){ - $result = array(); - $repositoryId = $ajxpNode->getRepositoryId(); - $metaFile = $this->globalMetaFile."_".$repositoryId; - $metaFile = $this->updateSecurityScope($metaFile, $ajxpNode->getRepositoryId(), $ajxpNode->getUser()); - if(!is_file($metaFile)) return $result; - $raw_data = file_get_contents($metaFile); - if($raw_data === false) return $result; - $metadata = unserialize($raw_data); - if($metadata === false || !is_array($metadata)) return $result; - $srcPath = $ajxpNode->getPath(); - if($srcPath == "/") $srcPath = ""; - foreach($metadata as $path => $data){ - preg_match("#^".preg_quote($srcPath, "#")."/#", $path, $matches); - if($path == $srcPath || count($matches)) { - $relativePath = substr($path, strlen($srcPath)); // REMOVE ORIGINAL NODE PATH - if($relativePath === false) $relativePath = "/"; - foreach($data as $userId => $meta){ - if(($userScope == $userId || $userScope == AJXP_METADATA_ALLUSERS) && isSet($meta[$nameSpace])){ - if(!isSet($result[$relativePath])) $result[$relativePath] = array(); - $result[$relativePath][$userId] = $meta[$nameSpace]; - } - } - } - } - return $result; - } - - /** - * @param AJXP_Node|null $srcNode - * @param AJXP_Node|null $destNode - * @param bool|false $copy - */ - public function nodeChangeHook($srcNode = null, $destNode = null, $copy = false){ - // This is not called, it's the responsibility of the meta provider/setter to - // handle the node.change event. - // For example, it would break the ShareCenter "shares" management. - /* - if($srcNode == null || $copy) return; - $operation = $destNode == null ? "delete" : "move"; - $repositoryId = $srcNode->getRepositoryId(); - $metaFile = $this->globalMetaFile."_".$repositoryId; - $metaFile = $this->updateSecurityScope($metaFile, $srcNode->getRepositoryId(), $srcNode->getUser()); - if(!is_file($metaFile)) return; - $raw_data = file_get_contents($metaFile); - if($raw_data === false) return; - $metadata = unserialize($raw_data); - if($metadata === false || !is_array($metadata)) return; - $changes = false; - $srcPath = $srcNode->getPath(); - foreach($metadata as $path => $data){ - preg_match("#^".preg_quote($srcPath, "#")."/#", $path, $matches); - if($path == $srcPath || count($matches)){ - if($operation == "move"){ - if($path == $srcPath) $newPath = $destNode->getPath(); - else $newPath = preg_replace("#^".preg_quote($srcPath, "#")."#", $destNode->getPath(), $path); - $metadata[$newPath] = $data; - } - unset($metadata[$path]); - $changes = true; - } - } - if($changes){ - // Should update $metadata now. - @file_put_contents($metaFile, serialize($metadata)); - } - */ - } - - protected function upgradeDataFromMetaSerial($data) - { - $new = array(); - foreach ($data as $fileKey => $fileData) { - $new[$fileKey] = array(AJXP_METADATA_SHAREDUSER => array( "users_meta" => $fileData )); - $new["AJXP_METASTORE_UPGRADED"] = true; - } - return $new; - } - -} diff --git a/core/src/plugins/metastore.serial/i18n/conf/de.php b/core/src/plugins/metastore.serial/i18n/conf/de.php index e0ad76aa3f..5e077852e3 100644 --- a/core/src/plugins/metastore.serial/i18n/conf/de.php +++ b/core/src/plugins/metastore.serial/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Files MetaStore" => "Metadaten in Datei", diff --git a/core/src/plugins/metastore.serial/i18n/conf/en.php b/core/src/plugins/metastore.serial/i18n/conf/en.php index a2ec42d6c6..bf4dba26a4 100644 --- a/core/src/plugins/metastore.serial/i18n/conf/en.php +++ b/core/src/plugins/metastore.serial/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Files MetaStore" => "Files MetaStore", diff --git a/core/src/plugins/metastore.serial/i18n/conf/fr.php b/core/src/plugins/metastore.serial/i18n/conf/fr.php index f10c8b28e3..7b965d73ad 100644 --- a/core/src/plugins/metastore.serial/i18n/conf/fr.php +++ b/core/src/plugins/metastore.serial/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Files MetaStore" => "MetaStore Fichiers", diff --git a/core/src/plugins/metastore.serial/i18n/conf/it.php b/core/src/plugins/metastore.serial/i18n/conf/it.php index d52b0a27d0..cdfa3d22f5 100644 --- a/core/src/plugins/metastore.serial/i18n/conf/it.php +++ b/core/src/plugins/metastore.serial/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Files MetaStore" => "File MetaStore", diff --git a/core/src/plugins/metastore.serial/i18n/conf/pt.php b/core/src/plugins/metastore.serial/i18n/conf/pt.php index c070bd6dab..cec46ca86f 100644 --- a/core/src/plugins/metastore.serial/i18n/conf/pt.php +++ b/core/src/plugins/metastore.serial/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Files MetaStore" => "Ficheiros MetaStore", diff --git a/core/src/plugins/metastore.serial/manifest.xml b/core/src/plugins/metastore.serial/manifest.xml index b03553a42a..80105743c7 100644 --- a/core/src/plugins/metastore.serial/manifest.xml +++ b/core/src/plugins/metastore.serial/manifest.xml @@ -1,6 +1,6 @@ - + @@ -10,11 +10,4 @@ - - - diff --git a/core/src/plugins/metastore.xattr/XAttrMetaStore.php b/core/src/plugins/metastore.xattr/XAttrMetaStore.php new file mode 100644 index 0000000000..40d394f469 --- /dev/null +++ b/core/src/plugins/metastore.xattr/XAttrMetaStore.php @@ -0,0 +1,163 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Access\Metastore\Implementation; + +use Pydio\Access\Core\Model\AJXP_Node; + +use Pydio\Access\Meta\Core\AbstractMetaSource; +use Pydio\Access\Metastore\Core\IMetaStoreProvider; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + + +/** + * Class XAttrMetaStore + * @package Pydio\Access\Metastore\Implementation + */ +class XAttrMetaStore extends AbstractMetaSource implements IMetaStoreProvider +{ + protected $rootPath; + + public function performChecks() + { + if (!function_exists("xattr_list")) { + throw new \Exception("The PHP Xattr Extension does not seem to be loaded"); + } + } + + /** + * @param $namespace + * @param $scope + * @param $user + * @return string + */ + private function getMetaKey($namespace, $scope, $user) + { + return strtolower($namespace."-".$scope."-".$user); + } + + /** + * @abstract + * @return bool + */ + public function inherentMetaMove() + { + return true; + } + + /** + * @param bool $private + * @param AJXP_Node $node + * @return string + */ + protected function getUserId($private, $node) + { + if(!$private) return AJXP_METADATA_SHAREDUSER; + if($node->hasUser()) return $node->getUserId(); + return "shared"; + } + + /** + * @abstract + * @param \Pydio\Access\Core\Model\AJXP_Node $ajxpNode + * @param String $nameSpace + * @param array $metaData + * @param bool $private + * @param int $scope + */ + public function setMetadata($ajxpNode, $nameSpace, $metaData, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY) + { + $path = $ajxpNode->getRealFile(); + if(!file_exists($path)) return; + $key = $this->getMetaKey($nameSpace, $scope, $this->getUserId($private, $ajxpNode)); + if (!\xattr_supported($path)) { + throw new \Exception("Filesystem does not support Extended Attributes!"); + } + $value = base64_encode(serialize($metaData)); + \xattr_set($path, $key, $value); + + } + + /** + * @abstract + * @param \Pydio\Access\Core\Model\AJXP_Node $ajxpNode + * @param String $nameSpace + * @param bool $private + * @param int $scope + * @return array|void + * @throws \Exception + */ + public function removeMetadata($ajxpNode, $nameSpace, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY) + { + $path = $ajxpNode->getRealFile(); + if(!file_exists($path)) return; + $key = $this->getMetaKey($nameSpace, $scope, $this->getUserId($private, $ajxpNode)); + if (!\xattr_supported($path)) { + throw new \Exception("Filesystem does not support Extended Attributes!"); + } + \xattr_remove($path, $key); + } + + /** + * @abstract + * @param AJXP_Node $ajxpNode + * @param String $nameSpace + * @param bool|String $private + * @param int $scope + * @return array() + */ + public function retrieveMetadata($ajxpNode, $nameSpace, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY) + { + $path = $ajxpNode->getRealFile(); + if(!file_exists($path)) return array(); + if (!\xattr_supported($path)) { + //throw new Exception("Filesystem does not support Extended Attributes!"); + return array(); + } + if($private === AJXP_METADATA_ALLUSERS){ + $startKey = $this->getMetaKey($nameSpace, $scope, ""); + $arrMeta = array(); + $keyList = \xattr_list($path); + foreach($keyList as $k){ + if(strpos($k, $startKey) === 0){ + $mData = \xattr_get($path, $k); + $decData = unserialize(base64_decode($mData)); + if(is_array($decData)) $arrMeta = array_merge_recursive($arrMeta, $decData); + } + } + return $arrMeta; + }else{ + $key = $this->getMetaKey($nameSpace, $scope, $this->getUserId($private, $ajxpNode)); + $data = \xattr_get($path, $key); + $data = unserialize(base64_decode($data)); + if( empty($data) || !is_array($data)) return array(); + return $data; + } + } + + /** + * @param \Pydio\Access\Core\Model\AJXP_Node $ajxpNode + * @return void + */ + public function enrichNode(&$ajxpNode) + { + } +} diff --git a/core/src/plugins/metastore.xattr/class.xAttrMetaStore.php b/core/src/plugins/metastore.xattr/class.xAttrMetaStore.php deleted file mode 100755 index d38c685f42..0000000000 --- a/core/src/plugins/metastore.xattr/class.xAttrMetaStore.php +++ /dev/null @@ -1,150 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - - -/** - * Use an xattr-enabled filesystem to store metadata - * @package AjaXplorer_Plugins - * @subpackage Metastore - */ -class xAttrMetaStore extends AJXP_AbstractMetaSource implements MetaStoreProvider -{ - protected $rootPath; - - public function performChecks() - { - if (!function_exists("xattr_list")) { - throw new Exception("The PHP Xattr Extension does not seem to be loaded"); - } - } - - private function getMetaKey($namespace, $scope, $user) - { - return strtolower($namespace."-".$scope."-".$user); - } - - /** - * @abstract - * @return bool - */ - public function inherentMetaMove() - { - return true; - } - - /** - * @param bool $private - * @param AJXP_Node $node - * @return string - */ - protected function getUserId($private, $node) - { - if(!$private) return AJXP_METADATA_SHAREDUSER; - if($node->hasUser()) return $node->getUser(); - if(AuthService::usersEnabled()) return AuthService::getLoggedUser()->getId(); - return "shared"; - } - - /** - * @abstract - * @param AJXP_Node $ajxpNode - * @param String $nameSpace - * @param array $metaData - * @param bool $private - * @param int $scope - */ - public function setMetadata($ajxpNode, $nameSpace, $metaData, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY) - { - $path = $ajxpNode->getRealFile(); - if(!file_exists($path)) return; - $key = $this->getMetaKey($nameSpace, $scope, $this->getUserId($private, $ajxpNode)); - if (!xattr_supported($path)) { - throw new Exception("Filesystem does not support Extended Attributes!"); - } - $value = base64_encode(serialize($metaData)); - xattr_set($path, $key, $value); - - } - - /** - * @abstract - * @param AJXP_Node $ajxpNode - * @param String $nameSpace - * @param bool $private - * @param int $scope - */ - public function removeMetadata($ajxpNode, $nameSpace, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY) - { - $path = $ajxpNode->getRealFile(); - if(!file_exists($path)) return; - $key = $this->getMetaKey($nameSpace, $scope, $this->getUserId($private, $ajxpNode)); - if (!xattr_supported($path)) { - throw new Exception("Filesystem does not support Extended Attributes!"); - } - xattr_remove($path, $key); - } - - /** - * @abstract - * @param AJXP_Node $ajxpNode - * @param String $nameSpace - * @param bool|String $private - * @param int $scope - * @return array() - */ - public function retrieveMetadata($ajxpNode, $nameSpace, $private = false, $scope=AJXP_METADATA_SCOPE_REPOSITORY) - { - $path = $ajxpNode->getRealFile(); - if(!file_exists($path)) return array(); - if (!xattr_supported($path)) { - //throw new Exception("Filesystem does not support Extended Attributes!"); - return array(); - } - if($private === AJXP_METADATA_ALLUSERS){ - $startKey = $this->getMetaKey($nameSpace, $scope, ""); - $arrMeta = array(); - $keyList = xattr_list($path); - foreach($keyList as $k){ - if(strpos($k, $startKey) === 0){ - $mData = xattr_get($path, $k); - $decData = unserialize(base64_decode($mData)); - if(is_array($decData)) $arrMeta = array_merge_recursive($arrMeta, $decData); - } - } - return $arrMeta; - }else{ - $key = $this->getMetaKey($nameSpace, $scope, $this->getUserId($private, $ajxpNode)); - $data = xattr_get($path, $key); - $data = unserialize(base64_decode($data)); - if( empty($data) || !is_array($data)) return array(); - return $data; - } - } - - /** - * @param AJXP_Node $ajxpNode - * @return void - */ - public function enrichNode(&$ajxpNode) - { - } -} diff --git a/core/src/plugins/metastore.xattr/i18n/conf/de.php b/core/src/plugins/metastore.xattr/i18n/conf/de.php index 367bb61c6a..89b4379f92 100644 --- a/core/src/plugins/metastore.xattr/i18n/conf/de.php +++ b/core/src/plugins/metastore.xattr/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Extended Attributes" => "Erweiterte Attribute", diff --git a/core/src/plugins/metastore.xattr/i18n/conf/en.php b/core/src/plugins/metastore.xattr/i18n/conf/en.php index 92bd84d325..51851bc257 100644 --- a/core/src/plugins/metastore.xattr/i18n/conf/en.php +++ b/core/src/plugins/metastore.xattr/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Extended Attributes" => "Extended Attributes", diff --git a/core/src/plugins/metastore.xattr/i18n/conf/it.php b/core/src/plugins/metastore.xattr/i18n/conf/it.php index ec85ad371e..cd080ad79d 100644 --- a/core/src/plugins/metastore.xattr/i18n/conf/it.php +++ b/core/src/plugins/metastore.xattr/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Extended Attributes" => "Attributi Estesi", diff --git a/core/src/plugins/metastore.xattr/manifest.xml b/core/src/plugins/metastore.xattr/manifest.xml index ee81b8ed1e..01a968db33 100644 --- a/core/src/plugins/metastore.xattr/manifest.xml +++ b/core/src/plugins/metastore.xattr/manifest.xml @@ -1,6 +1,6 @@ - + diff --git a/core/src/plugins/mq.serial/SerialMessageExchanger.php b/core/src/plugins/mq.serial/SerialMessageExchanger.php new file mode 100644 index 0000000000..d1e0accf54 --- /dev/null +++ b/core/src/plugins/mq.serial/SerialMessageExchanger.php @@ -0,0 +1,267 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Mq\Implementation; + +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\UsersService; +use Pydio\Core\Utils\FileHelper; + +use Pydio\Core\PluginFramework\Plugin; +use Pydio\Notification\Core\IMessageExchanger; + +defined('AJXP_EXEC') or die('Access not allowed'); +/** + * Serialized file plugin to manage messages queues + * @package AjaXplorer_Plugins + * @subpackage Mq + */ +class SerialMessageExchanger extends Plugin implements IMessageExchanger +{ + + /** + * @var array + */ + private $channels; + private $clientsGCTime = 10; + + /** + * @param $channelName + * @param bool $create + * @throws \Exception + */ + public function loadChannel($channelName, $create = false) + { + if (isSet($this->channels) && is_array($this->channels[$channelName])) { + return; + } + if (is_file($this->getPluginWorkDir()."/queues/channel-$channelName")) { + if(!isset($this->channels)) $this->channels = array(); + $data = FileHelper::loadSerialFile($this->getPluginWorkDir() . "/queues/channel-$channelName"); + if (is_array($data)) { + if(!is_array($data["MESSAGES"])) $data["MESSAGES"] = array(); + if(!is_array($data["CLIENTS"])) $data["CLIENTS"] = array(); + $this->channels[$channelName] = $data; + return; + } + } + if ($create) { + if(!isSet($this->channels)) $this->channels = array(); + $this->channels[$channelName] = array("CLIENTS" => array(), + "MESSAGES" => array()); + } + } + + public function __destruct() + { + if (isSet($this->channels) && is_array($this->channels)) { + foreach ($this->channels as $channelName => $data) { + if (is_array($data)) { + FileHelper::saveSerialFile($this->getPluginWorkDir() . "/queues/channel-$channelName", $data); + } + } + } + } + + + /** + * @param ContextInterface $ctx + * @param $channelName + * @param $clientId + * @throws \Exception + * @return mixed + */ + public function suscribeToChannel(ContextInterface $ctx, $channelName, $clientId) + { + $this->loadChannel($channelName, true); + if (UsersService::usersEnabled()) { + $user = $ctx->getUser(); + if ($user == null) { + throw new \Exception("You must be logged in"); + } + $GROUP_PATH = $user->getGroupPath(); + $USER_ID = $user->getId(); + } else { + $GROUP_PATH = '/'; + $USER_ID = 'shared'; + } + if($GROUP_PATH == null) $GROUP_PATH = false; + $this->channels[$channelName]["CLIENTS"][$clientId] = array( + "ALIVE" => time(), + "USER_ID" => $USER_ID, + "GROUP_PATH" => $GROUP_PATH + ); + foreach ($this->channels[$channelName]["MESSAGES"] as &$object) { + $object->messageRC[$clientId] = $clientId; + } + } + + /** + * @param ContextInterface $ctx + * @param $channelName + * @param $clientId + * @return mixed + */ + public function unsuscribeFromChannel(ContextInterface $ctx, $channelName, $clientId) + { + $this->loadChannel($channelName); + if(!isSet($this->channels) || !isSet($this->channels[$channelName])) return; + if(!array_key_exists($clientId, $this->channels[$channelName]["CLIENTS"])) return; + unset($this->channels[$channelName]["CLIENTS"][$clientId]); + foreach ($this->channels[$channelName]["MESSAGES"] as $index => &$object) { + unset($object->messageRC[$clientId]); + if (count($object->messageRC)== 0) { + unset($this->channels[$channelName]["MESSAGES"][$index]); + } + } + } + + /** + * @param ContextInterface $ctx + * @param $channelName + * @param $messageObject + */ + public function publishToChannel(ContextInterface $ctx, $channelName, $messageObject) + { + $this->loadChannel($channelName); + if(!isSet($this->channels) || !isSet($this->channels[$channelName])) return; + if(!count($this->channels[$channelName]["CLIENTS"])) return; + $clientIds = array_keys($this->channels[$channelName]["CLIENTS"]); + $messageObject->messageRC = array_combine($clientIds, $clientIds); + $messageObject->messageTS = microtime(); + $this->channels[$channelName]["MESSAGES"][] = $messageObject; + } + + /** + * @param ContextInterface $ctx + * @param $channelName + * @param $clientId + * @param $userId + * @param $userGroup + * @return mixed + */ + public function consumeInstantChannel(ContextInterface $ctx, $channelName, $clientId, $userId, $userGroup) + { + // Force refresh + //$this->channels = null; + $this->loadChannel($channelName); + if(!isSet($this->channels) || !isSet($this->channels[$channelName])) { + return null; + } + // Check dead clients + if (is_array($this->channels[$channelName]["CLIENTS"])) { + $toRemove = array(); + foreach ($this->channels[$channelName]["CLIENTS"] as $cId => $cData) { + $cAlive = $cData["ALIVE"]; + if( $cId != $clientId && time() - $cAlive > $this->clientsGCTime * 60) $toRemove[] = $cId; + } + if(count($toRemove)) { + foreach($toRemove as $c) { + $this->unsuscribeFromChannel($ctx, $channelName, $c); + } + } + } + if (!array_key_exists($clientId, $this->channels[$channelName]["CLIENTS"])) { + // Auto Suscribe + $this->suscribeToChannel($ctx, $channelName, $clientId); + } + $this->channels[$channelName]["CLIENTS"][$clientId]["ALIVE"] = time(); + + $result = array(); + foreach ($this->channels[$channelName]["MESSAGES"] as $index => $object) { + if (!isSet($object->messageRC[$clientId])) { + continue; + } + if (isSet($object->userId) && $object->userId != $userId) { + // Skipping, restricted to userId + continue; + } + if (isSet($object->groupPath) && $object->groupPath != $userGroup) { + // Skipping, restricted to groupPath + continue; + } + $result[] = $object; + unset($object->messageRC[$clientId]); + if (count($object->messageRC) <= 0) { + unset($this->channels[$channelName]["MESSAGES"][$index]); + } else { + $this->channels[$channelName]["MESSAGES"][$index] = $object; + } + } + return $result; + } + + /** + * @param ContextInterface $ctx + * @param $channelName + * @param $filter + * @return mixed + */ + public function consumeWorkerChannel(ContextInterface $ctx, $channelName, $filter = null) + { + $data = array(); + if (file_exists($this->getPluginWorkDir()."/worker/$channelName.ser")) { + $data = unserialize(file_get_contents($this->getPluginWorkDir()."/worker/$channelName.ser")); + file_put_contents($this->getPluginWorkDir()."/worker/$channelName.ser", array(), LOCK_EX); + } + return $data; + } + + /** + * @param ContextInterface $ctx + * @param string $channel Name of the persistant queue to create + * @param object $message Message to send + * @return mixed + */ + public function publishWorkerMessage(ContextInterface $ctx, $channel, $message) + { + $data = array(); + $fExists = false; + if (file_exists($this->getPluginWorkDir()."/worker/$channel.ser")) { + $fExists = true; + $data = unserialize(file_get_contents($this->getPluginWorkDir()."/worker/$channel.ser")); + } + $data[] = $message; + if (!$fExists) { + if (!is_dir($this->getPluginWorkDir()."/queues")) { + mkdir($this->getPluginWorkDir()."/queues", 0755, true); + } + } + $res = file_put_contents($this->getPluginWorkDir()."/worker/$channel.ser", serialize($data), LOCK_EX); + return $res; + } + + /** + * @param ContextInterface $ctx + * @param $channel + * @param $message + * @return Object + */ + public function publishInstantMessage(ContextInterface $ctx, $channel, $message) + { + $this->loadChannel($channel); + if(!isSet($this->channels) || !isSet($this->channels[$channel])) return; + if(!count($this->channels[$channel]["CLIENTS"])) return; + $clientIds = array_keys($this->channels[$channel]["CLIENTS"]); + $message->messageRC = array_combine($clientIds, $clientIds); + $message->messageTS = microtime(); + $this->channels[$channel]["MESSAGES"][] = $message; + } +} diff --git a/core/src/plugins/mq.serial/class.AJXP_SerialMessageExchanger.php b/core/src/plugins/mq.serial/class.AJXP_SerialMessageExchanger.php deleted file mode 100644 index 0bff769d89..0000000000 --- a/core/src/plugins/mq.serial/class.AJXP_SerialMessageExchanger.php +++ /dev/null @@ -1,248 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die('Access not allowed'); -/** - * Serialized file plugin to manage messages queues - * @package AjaXplorer_Plugins - * @subpackage Mq - */ -class AJXP_SerialMessageExchanger extends AJXP_Plugin implements AJXP_MessageExchanger -{ - - /** - * @var Array - */ - private $channels; - private $clientsGCTime = 10; - - public function loadChannel($channelName, $create = false) - { - if (isSet($this->channels) && is_array($this->channels[$channelName])) { - return; - } - if (is_file($this->getPluginWorkDir()."/queues/channel-$channelName")) { - if(!isset($this->channels)) $this->channels = array(); - $data = AJXP_Utils::loadSerialFile($this->getPluginWorkDir()."/queues/channel-$channelName"); - if (is_array($data)) { - if(!is_array($data["MESSAGES"])) $data["MESSAGES"] = array(); - if(!is_array($data["CLIENTS"])) $data["CLIENTS"] = array(); - $this->channels[$channelName] = $data; - return; - } - } - if ($create) { - if(!isSet($this->channels)) $this->channels = array(); - $this->channels[$channelName] = array("CLIENTS" => array(), - "MESSAGES" => array()); - } - } - - public function __destruct() - { - if (isSet($this->channels) && is_array($this->channels)) { - foreach ($this->channels as $channelName => $data) { - if (is_array($data)) { - AJXP_Utils::saveSerialFile($this->getPluginWorkDir()."/queues/channel-$channelName", $data); - } - } - } - } - - - /** - * @param $channelName - * @param $clientId - * @throws Exception - * @return mixed - */ - public function suscribeToChannel($channelName, $clientId) - { - $this->loadChannel($channelName, true); - if (AuthService::usersEnabled()) { - $user = AuthService::getLoggedUser(); - if ($user == null) { - throw new Exception("You must be logged in"); - } - $GROUP_PATH = $user->getGroupPath(); - $USER_ID = $user->getId(); - } else { - $GROUP_PATH = '/'; - $USER_ID = 'shared'; - } - if($GROUP_PATH == null) $GROUP_PATH = false; - $this->channels[$channelName]["CLIENTS"][$clientId] = array( - "ALIVE" => time(), - "USER_ID" => $USER_ID, - "GROUP_PATH" => $GROUP_PATH - ); - foreach ($this->channels[$channelName]["MESSAGES"] as &$object) { - $object->messageRC[$clientId] = $clientId; - } - } - - /** - * @param $channelName - * @param $clientId - * @return mixed - */ - public function unsuscribeFromChannel($channelName, $clientId) - { - $this->loadChannel($channelName); - if(!isSet($this->channels) || !isSet($this->channels[$channelName])) return; - if(!array_key_exists($clientId, $this->channels[$channelName]["CLIENTS"])) return; - unset($this->channels[$channelName]["CLIENTS"][$clientId]); - foreach ($this->channels[$channelName]["MESSAGES"] as $index => &$object) { - unset($object->messageRC[$clientId]); - if (count($object->messageRC)== 0) { - unset($this->channels[$channelName]["MESSAGES"][$index]); - } - } - } - - public function publishToChannel($channelName, $messageObject) - { - $this->loadChannel($channelName); - if(!isSet($this->channels) || !isSet($this->channels[$channelName])) return; - if(!count($this->channels[$channelName]["CLIENTS"])) return; - $clientIds = array_keys($this->channels[$channelName]["CLIENTS"]); - $messageObject->messageRC = array_combine($clientIds, $clientIds); - $messageObject->messageTS = microtime(); - $this->channels[$channelName]["MESSAGES"][] = $messageObject; - } - - /** - * @param $channelName - * @param $clientId - * @param $userId - * @param $userGroup - * @return mixed - */ - public function consumeInstantChannel($channelName, $clientId, $userId, $userGroup) - { - // Force refresh - //$this->channels = null; - $this->loadChannel($channelName); - if(!isSet($this->channels) || !isSet($this->channels[$channelName])) return; - // Check dead clients - if (is_array($this->channels[$channelName]["CLIENTS"])) { - $toRemove = array(); - foreach ($this->channels[$channelName]["CLIENTS"] as $cId => $cData) { - $cAlive = $cData["ALIVE"]; - if( $cId != $clientId && time() - $cAlive > $this->clientsGCTime * 60) $toRemove[] = $cId; - } - if(count($toRemove)) foreach($toRemove as $c) $this->unsuscribeFromChannel($channelName, $c); - } - if (!array_key_exists($clientId, $this->channels[$channelName]["CLIENTS"])) { - // Auto Suscribe - $this->suscribeToChannel($channelName, $clientId); - } - $this->channels[$channelName]["CLIENTS"][$clientId]["ALIVE"] = time(); - - //$user = AuthService::getLoggedUser(); - // if ($user == null) { - // throw new Exception("You must be logged in"); - // } - //$GROUP_PATH = $user->getGroupPath(); - // if($GROUP_PATH == null) $GROUP_PATH = false; - - $result = array(); - foreach ($this->channels[$channelName]["MESSAGES"] as $index => $object) { - if (!isSet($object->messageRC[$clientId])) { - continue; - } - if (isSet($object->userId) && $object->userId != $userId) { - // Skipping, restricted to userId - continue; - } - if (isSet($object->groupPath) && $object->groupPath != $userGroup) { - // Skipping, restricted to groupPath - continue; - } - $result[] = $object; - unset($object->messageRC[$clientId]); - if (count($object->messageRC) <= 0) { - unset($this->channels[$channelName]["MESSAGES"][$index]); - } else { - $this->channels[$channelName]["MESSAGES"][$index] = $object; - } - } - return $result; - } - - - - - - /** - * @param $channelName - * @param $filter - * @return mixed - */ - public function consumeWorkerChannel($channelName, $filter = null) - { - $data = array(); - if (file_exists($this->getPluginWorkDir()."/worker/$channelName.ser")) { - $data = unserialize(file_get_contents($this->getPluginWorkDir()."/worker/$channelName.ser")); - file_put_contents($this->getPluginWorkDir()."/worker/$channelName.ser", array(), LOCK_EX); - } - return $data; - } - - /** - * @param string $channel Name of the persistant queue to create - * @param object $message Message to send - * @return mixed - */ - public function publishWorkerMessage($channel, $message) - { - $data = array(); - $fExists = false; - if (file_exists($this->getPluginWorkDir()."/worker/$channel.ser")) { - $fExists = true; - $data = unserialize(file_get_contents($this->getPluginWorkDir()."/worker/$channel.ser")); - } - $data[] = $message; - if (!$fExists) { - if (!is_dir($this->getPluginWorkDir()."/queues")) { - mkdir($this->getPluginWorkDir()."/queues", 0755, true); - } - } - $res = file_put_contents($this->getPluginWorkDir()."/worker/$channel.ser", serialize($data), LOCK_EX); - return $res; - } - - /** - * @param $channel - * @param $message - * @return Object - */ - public function publishInstantMessage($channel, $message) - { - $this->loadChannel($channel); - if(!isSet($this->channels) || !isSet($this->channels[$channel])) return; - if(!count($this->channels[$channel]["CLIENTS"])) return; - $clientIds = array_keys($this->channels[$channel]["CLIENTS"]); - $message->messageRC = array_combine($clientIds, $clientIds); - $message->messageTS = microtime(); - $this->channels[$channel]["MESSAGES"][] = $message; - } -} diff --git a/core/src/plugins/mq.serial/i18n/conf/cs.php b/core/src/plugins/mq.serial/i18n/conf/cs.php index e2b85f3b37..67a9b8aa2d 100644 --- a/core/src/plugins/mq.serial/i18n/conf/cs.php +++ b/core/src/plugins/mq.serial/i18n/conf/cs.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "MQ Serial" => "MQ Serial", diff --git a/core/src/plugins/mq.serial/i18n/conf/de.php b/core/src/plugins/mq.serial/i18n/conf/de.php index 9cc77f8a02..44da30a105 100644 --- a/core/src/plugins/mq.serial/i18n/conf/de.php +++ b/core/src/plugins/mq.serial/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "MQ Serial" => "Datei-Warteschlange", diff --git a/core/src/plugins/mq.serial/i18n/conf/en.php b/core/src/plugins/mq.serial/i18n/conf/en.php index 1831502c82..d9dbda128a 100644 --- a/core/src/plugins/mq.serial/i18n/conf/en.php +++ b/core/src/plugins/mq.serial/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "MQ Serial" => "MQ Serial", diff --git a/core/src/plugins/mq.serial/i18n/conf/it.php b/core/src/plugins/mq.serial/i18n/conf/it.php index b9184602fe..9b68717707 100644 --- a/core/src/plugins/mq.serial/i18n/conf/it.php +++ b/core/src/plugins/mq.serial/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "MQ Serial" => "MQ Seriale", diff --git a/core/src/plugins/mq.serial/manifest.xml b/core/src/plugins/mq.serial/manifest.xml index 9c0f33d53d..93bc1d6a00 100644 --- a/core/src/plugins/mq.serial/manifest.xml +++ b/core/src/plugins/mq.serial/manifest.xml @@ -1,7 +1,7 @@ - + diff --git a/core/src/plugins/mq.sql/SqlMessageExchanger.php b/core/src/plugins/mq.sql/SqlMessageExchanger.php new file mode 100644 index 0000000000..821a8bec67 --- /dev/null +++ b/core/src/plugins/mq.sql/SqlMessageExchanger.php @@ -0,0 +1,308 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Mq\Implementation; + +use Pydio\Core\Model\ContextInterface; + +use Pydio\Core\Utils\FileHelper; +use Pydio\Core\Utils\Vars\OptionsHelper; + +use Pydio\Core\PluginFramework\Plugin; +use Pydio\Notification\Core\IMessageExchanger; +use \dibi as dibi; + +defined('AJXP_EXEC') or die('Access not allowed'); + +/** + * Sql-based plugin to manage messages queues + * + * @package AjaXplorer_Plugins + * @subpackage Mq + */ +class SqlMessageExchanger extends Plugin implements IMessageExchanger +{ + + /** + * @param ContextInterface $ctx + * @param array $options + */ + public function init(ContextInterface $ctx, $options = []) + { + parent::init($ctx, $options); + $this->sqlDriver = $this->sqlDriver = OptionsHelper::cleanDibiDriverParameters($options["SQL_DRIVER"]); + } + + public function performChecks() + { + if(!isSet($this->options)) return; + $test = OptionsHelper::cleanDibiDriverParameters($this->options["SQL_DRIVER"]); + if (!count($test)) { + throw new \Exception("Please define an SQL connexion in the core configuration"); + } + } + + + /** + * @var array + */ + private $channels; + private $clientsGCTime = 10; + private $sqlDriver; + + /** + * @param $channelName + * @param bool $create + * @throws \Exception + */ + public function loadChannel($channelName, $create = false) + { + if (isSet($this->channels) && is_array($this->channels[$channelName])) { + return; + } + if (is_file($this->getPluginWorkDir()."/queues/channel-$channelName")) { + if(!isset($this->channels)) $this->channels = array(); + $data = FileHelper::loadSerialFile($this->getPluginWorkDir() . "/queues/channel-$channelName"); + if (is_array($data)) { + if(!is_array($data["MESSAGES"])) $data["MESSAGES"] = array(); + if(!is_array($data["CLIENTS"])) $data["CLIENTS"] = array(); + $this->channels[$channelName] = $data; + return; + } + } + if ($create) { + if(!isSet($this->channels)) $this->channels = array(); + $this->channels[$channelName] = array("CLIENTS" => array(), + "MESSAGES" => array()); + } + } + + public function __destruct() + { + if (isSet($this->channels) && is_array($this->channels)) { + foreach ($this->channels as $channelName => $data) { + if (is_array($data)) { + FileHelper::saveSerialFile($this->getPluginWorkDir() . "/queues/channel-$channelName", $data); + } + } + } + } + + + /** + * @param ContextInterface $ctx + * @param $channelName + * @param $clientId + * @return mixed + * @throws \Exception + */ + public function suscribeToChannel(ContextInterface $ctx, $channelName, $clientId) + { + $this->loadChannel($channelName, true); + if ($ctx->hasUser()) { + $user = $ctx->getUser(); + if ($user == null) { + throw new \Exception("You must be logged in"); + } + $GROUP_PATH = $user->getGroupPath(); + $USER_ID = $user->getId(); + } else { + $GROUP_PATH = "/"; + $USER_ID = "shared"; + } + if($GROUP_PATH == null) $GROUP_PATH = false; + $this->channels[$channelName]["CLIENTS"][$clientId] = array( + "ALIVE" => time(), + "USER_ID" => $USER_ID, + "GROUP_PATH" => $GROUP_PATH + ); + /* + foreach ($this->channels[$channelName]["MESSAGES"] as &$object) { + $object->messageRC[$clientId] = $clientId; + } + */ + } + + /** + * @param ContextInterface $ctx + * @param $channelName + * @param $clientId + * @return mixed + */ + public function unsuscribeFromChannel(ContextInterface $ctx, $channelName, $clientId) + { + $this->loadChannel($channelName); + if(!isSet($this->channels) || !isSet($this->channels[$channelName])) return; + if(!array_key_exists($clientId, $this->channels[$channelName]["CLIENTS"])) return; + unset($this->channels[$channelName]["CLIENTS"][$clientId]); + foreach ($this->channels[$channelName]["MESSAGES"] as $index => &$object) { + unset($object->messageRC[$clientId]); + if (count($object->messageRC)== 0) { + unset($this->channels[$channelName]["MESSAGES"][$index]); + } + } + } + + /** + * @param ContextInterface $ctx + * @param $channelName + * @param $messageObject + */ + public function publishToChannel(ContextInterface $ctx, $channelName, $messageObject) + { + $this->loadChannel($channelName); + if(!isSet($this->channels) || !isSet($this->channels[$channelName])) return; + if(!count($this->channels[$channelName]["CLIENTS"])) return; + $clientIds = array_keys($this->channels[$channelName]["CLIENTS"]); + $messageObject->messageRC = array_combine($clientIds, $clientIds); + $messageObject->messageTS = microtime(); + $this->channels[$channelName]["MESSAGES"][] = $messageObject; + } + + /** + * @param ContextInterface $ctx + * @param $channelName + * @param $clientId + * @param $userId + * @param $userGroup + * @return mixed + * @throws \Exception + */ + public function consumeInstantChannel(ContextInterface $ctx, $channelName, $clientId, $userId, $userGroup) + { + $this->loadChannel($channelName); + if(!isSet($this->channels) || !isSet($this->channels[$channelName])) { + return null; + } + // Check dead clients + if (is_array($this->channels[$channelName]["CLIENTS"])) { + $toRemove = array(); + foreach ($this->channels[$channelName]["CLIENTS"] as $cId => $cData) { + $cAlive = $cData["ALIVE"]; + if( $cId != $clientId && time() - $cAlive > $this->clientsGCTime * 60) $toRemove[] = $cId; + } + if(count($toRemove)) { + foreach($toRemove as $c) { + $this->unsuscribeFromChannel($ctx, $channelName, $c); + } + } + } + if (!array_key_exists($clientId, $this->channels[$channelName]["CLIENTS"])) { + // Auto Suscribe + $this->suscribeToChannel($ctx, $channelName, $clientId); + } + $this->channels[$channelName]["CLIENTS"][$clientId]["ALIVE"] = time(); + + $result = array(); + foreach ($this->channels[$channelName]["MESSAGES"] as $index => $object) { + if (!isSet($object->messageRC[$clientId])) { + continue; + } + if (isSet($object->userId) && $object->userId != $userId) { + // Skipping, restricted to userId + continue; + } + if (isSet($object->groupPath) && $object->groupPath != $userGroup) { + // Skipping, restricted to groupPath + continue; + } + $result[] = $object; + unset($object->messageRC[$clientId]); + if (count($object->messageRC) <= 0) { + unset($this->channels[$channelName]["MESSAGES"][$index]); + } else { + $this->channels[$channelName]["MESSAGES"][$index] = $object; + } + } + return $result; + } + + /** + * @param ContextInterface $ctx + * @param $channelName + * @param $filter + * @return mixed + */ + public function consumeWorkerChannel(ContextInterface $ctx, $channelName, $filter = null) + { + if(!dibi::isConnected()) { + dibi::connect($this->sqlDriver); + } + $castType = "UNSIGNED"; + if($this->sqlDriver["driver"] == "postgre") $castType = "INTEGER"; + $results = dibi::query("SELECT * FROM [ajxp_simple_store] WHERE [store_id]=%s ORDER BY CAST([object_id] AS ".$castType.") ASC", "queues.$channelName"); + $rows = $results->fetchAll(); + $arr = array(); + $deleted = array(); + foreach ($rows as $row) { + $arr[] = unserialize($row["serialized_data"]); + $deleted[] = $row["object_id"]; + } + if (count($deleted)) { + // We use (%s) instead of %in to pass everyting as string ('1' instead of 1) + dibi::query("DELETE FROM [ajxp_simple_store] WHERE [store_id]=%s AND [object_id] IN (%s)", "queues.$channelName", $deleted); + } + return $arr; + } + + /** + * @param ContextInterface $ctx + * @param string $channel Name of the persistant queue to create + * @param object $message Message to send + * @return mixed + */ + public function publishWorkerMessage(ContextInterface $ctx, $channel, $message) + { + if(!dibi::isConnected()) { + dibi::connect($this->sqlDriver); + } + $castType = "UNSIGNED"; + if($this->sqlDriver["driver"] == "postgre") $castType = "INTEGER"; + $r = dibi::query("SELECT MAX( CAST( [object_id] AS ".$castType." ) ) FROM [ajxp_simple_store] WHERE [store_id]=%s", "queues.$channel"); + $index = $r->fetchSingle(); + if($index == null) $index = 1; + else $index = intval($index)+1; + $values = array( + "store_id" => "queues.$channel", + "object_id" => $index, + "serialized_data" => serialize($message) + ); + dibi::query("INSERT INTO [ajxp_simple_store] ([object_id],[store_id],[serialized_data],[binary_data],[related_object_id]) VALUES (%s,%s,%bin,%bin,%s)", + $values["object_id"], $values["store_id"], $values["serialized_data"], $values["binary_data"], $values["related_object_id"]); + } + + /** + * @param ContextInterface $ctx + * @param $channel + * @param $message + * @return Object + */ + public function publishInstantMessage(ContextInterface $ctx, $channel, $message) + { + $this->loadChannel($channel); + if(!isSet($this->channels) || !isSet($this->channels[$channel])) return; + if(!count($this->channels[$channel]["CLIENTS"])) return; + $clientIds = array_keys($this->channels[$channel]["CLIENTS"]); + $message->messageRC = array_combine($clientIds, $clientIds); + $message->messageTS = microtime(); + $this->channels[$channel]["MESSAGES"][] = $message; + } + +} diff --git a/core/src/plugins/mq.sql/class.AJXP_SqlMessageExchanger.php b/core/src/plugins/mq.sql/class.AJXP_SqlMessageExchanger.php deleted file mode 100644 index 375e72219d..0000000000 --- a/core/src/plugins/mq.sql/class.AJXP_SqlMessageExchanger.php +++ /dev/null @@ -1,282 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die('Access not allowed'); - -/** - * Sql-based plugin to manage messages queues - * - * @package AjaXplorer_Plugins - * @subpackage Mq - */ -class AJXP_SqlMessageExchanger extends AJXP_Plugin implements AJXP_MessageExchanger -{ - - public function init($options) - { - parent::init($options); - $this->sqlDriver = $this->sqlDriver = AJXP_Utils::cleanDibiDriverParameters($options["SQL_DRIVER"]); - } - - public function performChecks() - { - if(!isSet($this->options)) return; - $test = AJXP_Utils::cleanDibiDriverParameters($this->options["SQL_DRIVER"]); - if (!count($test)) { - throw new Exception("Please define an SQL connexion in the core configuration"); - } - } - - - /** - * @var Array - */ - private $channels; - private $clientsGCTime = 10; - private $sqlDriver; - - public function loadChannel($channelName, $create = false) - { - if (isSet($this->channels) && is_array($this->channels[$channelName])) { - return; - } - if (is_file($this->getPluginWorkDir()."/queues/channel-$channelName")) { - if(!isset($this->channels)) $this->channels = array(); - $data = AJXP_Utils::loadSerialFile($this->getPluginWorkDir()."/queues/channel-$channelName"); - if (is_array($data)) { - if(!is_array($data["MESSAGES"])) $data["MESSAGES"] = array(); - if(!is_array($data["CLIENTS"])) $data["CLIENTS"] = array(); - $this->channels[$channelName] = $data; - return; - } - } - if ($create) { - if(!isSet($this->channels)) $this->channels = array(); - $this->channels[$channelName] = array("CLIENTS" => array(), - "MESSAGES" => array()); - } - } - - public function __destruct() - { - if (isSet($this->channels) && is_array($this->channels)) { - foreach ($this->channels as $channelName => $data) { - if (is_array($data)) { - AJXP_Utils::saveSerialFile($this->getPluginWorkDir()."/queues/channel-$channelName", $data); - } - } - } - } - - - /** - * @param $channelName - * @param $clientId - * @return mixed - */ - public function suscribeToChannel($channelName, $clientId) - { - $this->loadChannel($channelName, true); - if (AuthService::usersEnabled()) { - $user = AuthService::getLoggedUser(); - if ($user == null) { - throw new Exception("You must be logged in"); - } - $GROUP_PATH = $user->getGroupPath(); - $USER_ID = $user->getId(); - } else { - $GROUP_PATH = "/"; - $USER_ID = "shared"; - } - if($GROUP_PATH == null) $GROUP_PATH = false; - $this->channels[$channelName]["CLIENTS"][$clientId] = array( - "ALIVE" => time(), - "USER_ID" => $USER_ID, - "GROUP_PATH" => $GROUP_PATH - ); - /* - foreach ($this->channels[$channelName]["MESSAGES"] as &$object) { - $object->messageRC[$clientId] = $clientId; - } - */ - } - - /** - * @param $channelName - * @param $clientId - * @return mixed - */ - public function unsuscribeFromChannel($channelName, $clientId) - { - $this->loadChannel($channelName); - if(!isSet($this->channels) || !isSet($this->channels[$channelName])) return; - if(!array_key_exists($clientId, $this->channels[$channelName]["CLIENTS"])) return; - unset($this->channels[$channelName]["CLIENTS"][$clientId]); - foreach ($this->channels[$channelName]["MESSAGES"] as $index => &$object) { - unset($object->messageRC[$clientId]); - if (count($object->messageRC)== 0) { - unset($this->channels[$channelName]["MESSAGES"][$index]); - } - } - } - - public function publishToChannel($channelName, $messageObject) - { - $this->loadChannel($channelName); - if(!isSet($this->channels) || !isSet($this->channels[$channelName])) return; - if(!count($this->channels[$channelName]["CLIENTS"])) return; - $clientIds = array_keys($this->channels[$channelName]["CLIENTS"]); - $messageObject->messageRC = array_combine($clientIds, $clientIds); - $messageObject->messageTS = microtime(); - $this->channels[$channelName]["MESSAGES"][] = $messageObject; - } - - /** - * @param $channelName - * @param $clientId - * @param $userId - * @param $userGroup - * @return mixed - */ - public function consumeInstantChannel($channelName, $clientId, $userId, $userGroup) - { - $this->loadChannel($channelName); - if(!isSet($this->channels) || !isSet($this->channels[$channelName])) return; - // Check dead clients - if (is_array($this->channels[$channelName]["CLIENTS"])) { - $toRemove = array(); - foreach ($this->channels[$channelName]["CLIENTS"] as $cId => $cData) { - $cAlive = $cData["ALIVE"]; - if( $cId != $clientId && time() - $cAlive > $this->clientsGCTime * 60) $toRemove[] = $cId; - } - if(count($toRemove)) foreach($toRemove as $c) $this->unsuscribeFromChannel($channelName, $c); - } - if (!array_key_exists($clientId, $this->channels[$channelName]["CLIENTS"])) { - // Auto Suscribe - $this->suscribeToChannel($channelName, $clientId); - } - $this->channels[$channelName]["CLIENTS"][$clientId]["ALIVE"] = time(); - - /* - $user = AuthService::getLoggedUser(); - if ($user == null) { - throw new Exception("You must be logged in"); - } - $GROUP_PATH = $user->getGroupPath(); - if($GROUP_PATH == null) $GROUP_PATH = false; - */ - $result = array(); - foreach ($this->channels[$channelName]["MESSAGES"] as $index => $object) { - if (!isSet($object->messageRC[$clientId])) { - continue; - } - if (isSet($object->userId) && $object->userId != $userId) { - // Skipping, restricted to userId - continue; - } - if (isSet($object->groupPath) && $object->groupPath != $userGroup) { - // Skipping, restricted to groupPath - continue; - } - $result[] = $object; - unset($object->messageRC[$clientId]); - if (count($object->messageRC) <= 0) { - unset($this->channels[$channelName]["MESSAGES"][$index]); - } else { - $this->channels[$channelName]["MESSAGES"][$index] = $object; - } - } - return $result; - } - - - - - - /** - * @param $channelName - * @param $filter - * @return mixed - */ - public function consumeWorkerChannel($channelName, $filter = null) - { - if(!dibi::isConnected()) { - dibi::connect($this->sqlDriver); - } - $castType = "UNSIGNED"; - if($this->sqlDriver["driver"] == "postgre") $castType = "INTEGER"; - $results = dibi::query("SELECT * FROM [ajxp_simple_store] WHERE [store_id]=%s ORDER BY CAST([object_id] AS ".$castType.") ASC", "queues.$channelName"); - $rows = $results->fetchAll(); - $arr = array(); - $deleted = array(); - foreach ($rows as $row) { - $arr[] = unserialize($row["serialized_data"]); - $deleted[] = $row["object_id"]; - } - if (count($deleted)) { - // We use (%s) instead of %in to pass everyting as string ('1' instead of 1) - dibi::query("DELETE FROM [ajxp_simple_store] WHERE [store_id]=%s AND [object_id] IN (%s)", "queues.$channelName", $deleted); - } - return $arr; - } - - /** - * @param string $channel Name of the persistant queue to create - * @param object $message Message to send - * @return mixed - */ - public function publishWorkerMessage($channel, $message) - { - if(!dibi::isConnected()) { - dibi::connect($this->sqlDriver); - } - $castType = "UNSIGNED"; - if($this->sqlDriver["driver"] == "postgre") $castType = "INTEGER"; - $r = dibi::query("SELECT MAX( CAST( [object_id] AS ".$castType." ) ) FROM [ajxp_simple_store] WHERE [store_id]=%s", "queues.$channel"); - $index = $r->fetchSingle(); - if($index == null) $index = 1; - else $index = intval($index)+1; - $values = array( - "store_id" => "queues.$channel", - "object_id" => $index, - "serialized_data" => serialize($message) - ); - dibi::query("INSERT INTO [ajxp_simple_store] ([object_id],[store_id],[serialized_data],[binary_data],[related_object_id]) VALUES (%s,%s,%bin,%bin,%s)", - $values["object_id"], $values["store_id"], $values["serialized_data"], $values["binary_data"], $values["related_object_id"]); - } - - /** - * @param $channel - * @param $message - * @return Object - */ - public function publishInstantMessage($channel, $message) - { - $this->loadChannel($channel); - if(!isSet($this->channels) || !isSet($this->channels[$channel])) return; - if(!count($this->channels[$channel]["CLIENTS"])) return; - $clientIds = array_keys($this->channels[$channel]["CLIENTS"]); - $message->messageRC = array_combine($clientIds, $clientIds); - $message->messageTS = microtime(); - $this->channels[$channel]["MESSAGES"][] = $message; - } - -} diff --git a/core/src/plugins/mq.sql/i18n/conf/cs.php b/core/src/plugins/mq.sql/i18n/conf/cs.php index ad1267f8bc..bc59ca209f 100644 --- a/core/src/plugins/mq.sql/i18n/conf/cs.php +++ b/core/src/plugins/mq.sql/i18n/conf/cs.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "MQ SQL" => "MQ SQL", diff --git a/core/src/plugins/mq.sql/i18n/conf/de.php b/core/src/plugins/mq.sql/i18n/conf/de.php index 10bc042f45..55e8e46a81 100644 --- a/core/src/plugins/mq.sql/i18n/conf/de.php +++ b/core/src/plugins/mq.sql/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "MQ SQL" => "SQL-Warteschlange", diff --git a/core/src/plugins/mq.sql/i18n/conf/en.php b/core/src/plugins/mq.sql/i18n/conf/en.php index d23c601ed3..ad8530211a 100644 --- a/core/src/plugins/mq.sql/i18n/conf/en.php +++ b/core/src/plugins/mq.sql/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "MQ SQL" => "MQ SQL", diff --git a/core/src/plugins/mq.sql/i18n/conf/it.php b/core/src/plugins/mq.sql/i18n/conf/it.php index 27373bfe88..588006043b 100644 --- a/core/src/plugins/mq.sql/i18n/conf/it.php +++ b/core/src/plugins/mq.sql/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "MQ SQL" => "MQ SQL", diff --git a/core/src/plugins/mq.sql/manifest.xml b/core/src/plugins/mq.sql/manifest.xml index e6da3b7959..d81a5acf4c 100644 --- a/core/src/plugins/mq.sql/manifest.xml +++ b/core/src/plugins/mq.sql/manifest.xml @@ -1,7 +1,7 @@ - + diff --git a/core/src/plugins/shorten.bitly/class.BitlyShortener.php b/core/src/plugins/shorten.bitly/class.BitlyShortener.php deleted file mode 100644 index 2a165821bd..0000000000 --- a/core/src/plugins/shorten.bitly/class.BitlyShortener.php +++ /dev/null @@ -1,98 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Use a developer Bit.ly account to shorten publiclet links. - * @package AjaXplorer_Plugins - * @subpackage Shorten - */ -class BitlyShortener extends AJXP_Plugin -{ - public function postProcess($action, $httpVars, $params) - { - $jsonData = json_decode($params["ob_output"], true); - if ($jsonData != false) { - $url = $jsonData["publiclet_link"] ; - $elementId = $jsonData["element_id"]; - } else { - $url = $params["ob_output"]; - $elementId = -1; - } - - $BITLY_USER = $this->getFilteredOption("BITLY_USER"); - $BITLY_APIKEY = $this->getFilteredOption("BITLY_APIKEY"); - - if (empty($BITLY_USER) || empty($BITLY_APIKEY)) { - print($url); - $this->logError("Config", "Bitly Shortener : you must drop the conf.shorten.bitly.inc file inside conf.php and set the login/api key!"); - return; - } - $bitly_login = $BITLY_USER; - $bitly_api = $BITLY_APIKEY; - $format = 'json'; - $version = '2.0.1'; - $bitly = 'http://api.bit.ly/shorten?version='.$version.'&longUrl='.urlencode($url).'&login='.$bitly_login.'&apiKey='.$bitly_api.'&format='.$format; - $response = AJXP_Utils::getRemoteContent($bitly); - $json = json_decode($response, true); - if (isSet($json['results'][$url]['shortUrl'])) { - print($json['results'][$url]['shortUrl']); - $this->updateMetaShort($httpVars["file"], $elementId, $json['results'][$url]['shortUrl']); - } else { - print($url); - } - } - - protected function updateMetaShort($file, $elementId, $shortUrl) - { - $context = new UserSelection(ConfService::getRepository()); - $baseUrl = $context->currentBaseUrl(); - $node = new AJXP_Node($baseUrl.$file); - if ($node->hasMetaStore()) { - $metadata = $node->retrieveMetadata( - "ajxp_shared", - true, - AJXP_METADATA_SCOPE_REPOSITORY - ); - if ($elementId != -1) { - if (!is_array($metadata["element"][$elementId])) { - $metadata["element"][$elementId] = array(); - } - $metadata["element"][$elementId]["short_form_url"] = $shortUrl; - } else { - if(isSet($metadata["shares"])){ - $key = array_pop(array_keys($metadata["shares"])); - $metadata["shares"][$key]["short_form_url"] = $shortUrl; - }else{ - $metadata['short_form_url'] = $shortUrl; - } - } - $node->setMetadata( - "ajxp_shared", - $metadata, - true, - AJXP_METADATA_SCOPE_REPOSITORY - ); - } - } - -} diff --git a/core/src/plugins/shorten.bitly/i18n/conf/de.php b/core/src/plugins/shorten.bitly/i18n/conf/de.php deleted file mode 100644 index 43364849c9..0000000000 --- a/core/src/plugins/shorten.bitly/i18n/conf/de.php +++ /dev/null @@ -1,28 +0,0 @@ - -* This file is part of Pydio. -* -* Pydio 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. -* -* Pydio 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 Pydio. If not, see . -* -* The latest code can be found at . -*/ -$mess=array( -"Bit.ly URL Shortener" => "Bit.ly URL-Kürzungsdienst", -"Shorten Download Links before sending them back to the user. Requires a Bit.ly account." => "Links zum Herunterladen von Dateien vor dem Versand an Benutzer kürzen. Erfordert einen Bit.ly Zugang.", -"User Name" => "Benutzername", -"Bit.ly account user name" => "Benutzername bei Bit.ly", -"API Key" => "API Key", -"Bit.ly account API Key" => "Bit.ly API-Key", -); diff --git a/core/src/plugins/shorten.bitly/i18n/conf/en.php b/core/src/plugins/shorten.bitly/i18n/conf/en.php deleted file mode 100644 index 23f26b89ef..0000000000 --- a/core/src/plugins/shorten.bitly/i18n/conf/en.php +++ /dev/null @@ -1,28 +0,0 @@ - -* This file is part of Pydio. -* -* Pydio 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. -* -* Pydio 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 Pydio. If not, see . -* -* The latest code can be found at . -*/ -$mess=array( -"Bit.ly URL Shortener" => "Bit.ly URL Shortener", -"Shorten Download Links before sending them back to the user. Requires a Bit.ly account." => "Shorten Download Links before sending them back to the user. Requires a Bit.ly account.", -"User Name" => "User Name", -"Bit.ly account user name" => "Bit.ly account user name", -"API Key" => "API Key", -"Bit.ly account API Key" => "Bit.ly account API Key", -); diff --git a/core/src/plugins/shorten.bitly/i18n/conf/fr.php b/core/src/plugins/shorten.bitly/i18n/conf/fr.php deleted file mode 100644 index 53f165234d..0000000000 --- a/core/src/plugins/shorten.bitly/i18n/conf/fr.php +++ /dev/null @@ -1,28 +0,0 @@ - -* This file is part of Pydio. -* -* Pydio 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. -* -* Pydio 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 Pydio. If not, see . -* -* The latest code can be found at . -*/ -$mess=array( -"Bit.ly URL Shortener" => "Raccourcisseur d'URL Bit.ly", -"Shorten Download Links before sending them back to the user. Requires a Bit.ly account." => "Raccourcir les liens de téléchargement avant de les renvoyer à l'utilisateur. Nécessite un compte Bit.ly.", -"User Name" => "Nom d'utilisateur", -"Bit.ly account user name" => "Nom d'utilisateur Bit.ly", -"API Key" => "Clef API", -"Bit.ly account API Key" => "Clef d'API Bit.ly", -); diff --git a/core/src/plugins/shorten.bitly/i18n/conf/it.php b/core/src/plugins/shorten.bitly/i18n/conf/it.php deleted file mode 100644 index a4c3798320..0000000000 --- a/core/src/plugins/shorten.bitly/i18n/conf/it.php +++ /dev/null @@ -1,28 +0,0 @@ - -* This file is part of Pydio. -* -* Pydio 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. -* -* Pydio 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 Pydio. If not, see . -* -* The latest code can be found at . -*/ -$mess=array( -"Bit.ly URL Shortener" => "Abbreviatore URL Bit.ly", -"Shorten Download Links before sending them back to the user. Requires a Bit.ly account." => "Abbrevia i link di download prima di mostrarli all'utente. Richiede un account su 'Bit.ly'.", -"User Name" => "Nome Utente", -"Bit.ly account user name" => "Nome utente per Bit.ly", -"API Key" => "Chiave API", -"Bit.ly account API Key" => "Chiave API per Bit.ly", -); diff --git a/core/src/plugins/shorten.bitly/i18n/conf/pt.php b/core/src/plugins/shorten.bitly/i18n/conf/pt.php deleted file mode 100644 index 8acf8d9df0..0000000000 --- a/core/src/plugins/shorten.bitly/i18n/conf/pt.php +++ /dev/null @@ -1,29 +0,0 @@ - -* This file is part of Pydio. -* -* Pydio 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. -* -* Pydio 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 Pydio. If not, see . -* -* The latest code can be found at . -*/ -$mess=array( -"Bit.ly URL Shortener" => "Redutor de URLs Bit.ly", -"Shorten Download URLs before sending them back to the user. Requires a Bit.ly account." => "Diminui o tamanho dos URLs de transferência antes de os dar ao utilizador. Requer uma conta Bit.ly.", -"User Name" => "Nome de Utilizador", -"Bit.ly account user name" => "Nome de utilizador Bit.ly", -"API Key" => "Chave API", -"Bit.ly account API Key" => "Chave API da conta Bit.ly", -"Shorten Download Links before sending them back to the user. Requires a Bit.ly account." => "Shorten Download Links before sending them back to the user. Requires a Bit.ly account.", -); diff --git a/core/src/plugins/shorten.bitly/manifest.xml b/core/src/plugins/shorten.bitly/manifest.xml deleted file mode 100644 index 8d30ae3bf4..0000000000 --- a/core/src/plugins/shorten.bitly/manifest.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/core/src/plugins/shorten.bitly/plugin_doc.html b/core/src/plugins/shorten.bitly/plugin_doc.html deleted file mode 100644 index 27c4ed1b53..0000000000 --- a/core/src/plugins/shorten.bitly/plugin_doc.html +++ /dev/null @@ -1 +0,0 @@ -

      Uses the Bit.ly webservice to automatically shorten the links generated for public files.

      \ No newline at end of file diff --git a/core/src/plugins/shorten.multi/MultiShortener.php b/core/src/plugins/shorten.multi/MultiShortener.php new file mode 100644 index 0000000000..791916efdc --- /dev/null +++ b/core/src/plugins/shorten.multi/MultiShortener.php @@ -0,0 +1,156 @@ +generateLink($ctx, $url); + } + + /** + * @param ContextInterface $ctx + * @param $url + * @return bool|mixed|null|string + */ + protected function generateLink(ContextInterface $ctx, $url){ + + $type = $this->getContextualOption($ctx, "SHORTEN_TYPE"); + if(empty($type)) return null; + switch (intval($type["shorten_type"])) { + case 0: + if (!isSet($type["ADFLY_TYPE"]) || !isSet($type["ADFLY_APIKEY"]) || !isSet($type["ADFLY_UID"]) || !isSet($type["ADFLY_DOMAIN"])) { + $this->logError("Config", "adFly Shortener : you must set the api key!"); + break; + } + $adfly_type = $type["ADFLY_TYPE"]; + $adfly_api = $type["ADFLY_APIKEY"]; + $adfly_uid = $type["ADFLY_UID"]; + $adfly_dom = $type["ADFLY_DOMAIN"]; + $adfly = 'http://api.adf.ly/api.php?key='.$adfly_api.'&uid='.$adfly_uid.'&advert_type='.$adfly_type.'&domain='.$adfly_dom.'&url='.urlencode($url); + $response = FileHelper::getRemoteContent($adfly); + $response = strip_tags($response, ''); + $response = strip_tags($response); + if (isSet($response)) { + return $response; + } + break; + + case 1: + if (!isSet($type["BITLY_USER"]) || !isSet($type["BITLY_APIKEY"])) { + $this->logError("Config", "Bitly Shortener : you must drop the conf.shorten.bitly.inc file inside conf.php and set the login/api key!"); + break; + } + $bitly_login = $type["BITLY_USER"]; + $bitly_api = $type["BITLY_APIKEY"]; + $format = 'json'; + $version = '2.0.1'; + $bitly = 'http://api.bit.ly/shorten?version='.$version.'&longUrl='.urlencode($url).'&login='.$bitly_login.'&apiKey='.$bitly_api.'&format='.$format; + $response = FileHelper::getRemoteContent($bitly); + $json = json_decode($response, true); + if (isSet($json['results'][$url]['shortUrl'])) { + return $json['results'][$url]['shortUrl']; + } + break; + + case 2: + if (!isSet($type["GOOGL_APIKEY"])) { + $this->logError("Config", "Goo.gl Shortener : you must set the api key!"); + break; + } + $data = array( + 'longUrl' => $url, + 'key' => $type["GOOGL_APIKEY"] + ); + + $options = array( + 'http' => array( + 'method' => 'POST', + 'content' => json_encode( $data ), + 'header'=> "Content-Type: application/json\r\n" . + "Accept: application/json\r\n" + ) + ); + + $goourl = 'https://www.googleapis.com/urlshortener/v1/url?key='.$type["GOOGL_APIKEY"]; + $context = stream_context_create( $options ); + $result = file_get_contents( $goourl, false, $context ); + $json = (array) json_decode( $result ); + if (isSet($json['id'])) { + return $json['id']; + } + break; + + case 3: + if (!isSet($type["POST_APIKEY"])) { + $this->logError("Config", "po.st Shortener : you must set the api key!"); + break; + } + $post_api = $type["POST_APIKEY"]; + $post = 'http://po.st/api/shorten?longUrl='.urlencode($url).'&apiKey='.$post_api.'&format=txt'; + $response = FileHelper::getRemoteContent($post); + if (isSet($response)) { + return $response; + } + break; + + case 4: + if (!isSet($type["YOURLS_DOMAIN"])) { + $this->logError("Config", "yourls Shortener : you must set the domain name"); + return null; + } + if (!isSet($type["YOURLS_APIKEY"])) { + $this->logError("Config", "yourls Shortener : you must set the api key"); + return null; + } + $useidn = false; + if (isSet($type["YOURLS_USEIDN"])) { + $useidn = $type["YOURLS_USEIDN"]; + } + $yourls_domain = $type["YOURLS_DOMAIN"]; + $yourls_api = $type["YOURLS_APIKEY"]; + $yourls = 'http://'.$yourls_domain.'/yourls-api.php?signature='.$yourls_api.'&action=shorturl&format=simple&url='.urlencode($url); + $response = FileHelper::getRemoteContent($yourls); + if (isSet($response)) { + $shorturl = $response; + if ($useidn) { + // WARNING: idn_to_utf8 requires php-idn module. + // WARNING: http_build_url requires php-pecl-http module. + $purl = parse_url($shorturl); + $purl['host'] = idn_to_utf8($purl['host']); + $shorturl = http_build_url($purl); + } + return $shorturl; + } + break; + + default: + break; + } + + return null; + + } + +} diff --git a/core/src/plugins/shorten.multi/class.multiShortener.php b/core/src/plugins/shorten.multi/class.multiShortener.php deleted file mode 100755 index 2d075cbc35..0000000000 --- a/core/src/plugins/shorten.multi/class.multiShortener.php +++ /dev/null @@ -1,204 +0,0 @@ -generateLink($url); - } - - /** - * @param $action - * @param $httpVars - * @param $params - */ - public function postProcess($action, $httpVars, $params) - { - $type = $this->getFilteredOption("SHORTEN_TYPE"); - if(empty($type)) return; - $jsonData = json_decode($params["ob_output"], true); - $elementId = -1; - if ($jsonData != false) { - $url = $jsonData["publiclet_link"] ; - $elementId = $jsonData["element_id"]; - } else { - $url = $params["ob_output"]; - } - - $res = $this->generateLink($url); - if(!empty($res)){ - $this->updateMetaShort($httpVars["file"], $elementId, $res); - print($res); - }else{ - print($url); - } - } - - protected function generateLink($url){ - - $type = $this->getFilteredOption("SHORTEN_TYPE"); - if(empty($type)) return null; - switch (intval($type["shorten_type"])) { - case 0: - if (!isSet($type["ADFLY_TYPE"]) || !isSet($type["ADFLY_APIKEY"]) || !isSet($type["ADFLY_UID"]) || !isSet($type["ADFLY_DOMAIN"])) { - $this->logError("Config", "adFly Shortener : you must set the api key!"); - break; - } - $adfly_type = $type["ADFLY_TYPE"]; - $adfly_api = $type["ADFLY_APIKEY"]; - $adfly_uid = $type["ADFLY_UID"]; - $adfly_dom = $type["ADFLY_DOMAIN"]; - $adfly = 'http://api.adf.ly/api.php?key='.$adfly_api.'&uid='.$adfly_uid.'&advert_type='.$adfly_type.'&domain='.$adfly_dom.'&url='.urlencode($url); - $response = AJXP_Utils::getRemoteContent($adfly); - $response = strip_tags($response, ''); - $response = strip_tags($response); - if (isSet($response)) { - return $response; - } - break; - - case 1: - if (!isSet($type["BITLY_USER"]) || !isSet($type["BITLY_APIKEY"])) { - $this->logError("Config", "Bitly Shortener : you must drop the conf.shorten.bitly.inc file inside conf.php and set the login/api key!"); - break; - } - $bitly_login = $type["BITLY_USER"]; - $bitly_api = $type["BITLY_APIKEY"]; - $format = 'json'; - $version = '2.0.1'; - $bitly = 'http://api.bit.ly/shorten?version='.$version.'&longUrl='.urlencode($url).'&login='.$bitly_login.'&apiKey='.$bitly_api.'&format='.$format; - $response = AJXP_Utils::getRemoteContent($bitly); - $json = json_decode($response, true); - if (isSet($json['results'][$url]['shortUrl'])) { - return $json['results'][$url]['shortUrl']; - } - break; - - case 2: - if (!isSet($type["GOOGL_APIKEY"])) { - $this->logError("Config", "Goo.gl Shortener : you must set the api key!"); - break; - } - $data = array( - 'longUrl' => $url, - 'key' => $type["GOOGL_APIKEY"] - ); - - $options = array( - 'http' => array( - 'method' => 'POST', - 'content' => json_encode( $data ), - 'header'=> "Content-Type: application/json\r\n" . - "Accept: application/json\r\n" - ) - ); - - $goourl = 'https://www.googleapis.com/urlshortener/v1/url?key='.$type["GOOGL_APIKEY"]; - $context = stream_context_create( $options ); - $result = file_get_contents( $goourl, false, $context ); - $json = (array) json_decode( $result ); - if (isSet($json['id'])) { - return $json['id']; - } - break; - - case 3: - if (!isSet($type["POST_APIKEY"])) { - $this->logError("Config", "po.st Shortener : you must set the api key!"); - break; - } - $post_api = $type["POST_APIKEY"]; - $post = 'http://po.st/api/shorten?longUrl='.urlencode($url).'&apiKey='.$post_api.'&format=txt'; - $response = AJXP_Utils::getRemoteContent($post); - if (isSet($response)) { - return $response; - } - break; - - case 4: - if (!isSet($type["YOURLS_DOMAIN"])) { - $this->logError("Config", "yourls Shortener : you must set the domain name"); - return null; - } - if (!isSet($type["YOURLS_APIKEY"])) { - $this->logError("Config", "yourls Shortener : you must set the api key"); - return null; - } - $useidn = false; - if (isSet($type["YOURLS_USEIDN"])) { - $useidn = $type["YOURLS_USEIDN"]; - } - $yourls_domain = $type["YOURLS_DOMAIN"]; - $yourls_api = $type["YOURLS_APIKEY"]; - $yourls = 'http://'.$yourls_domain.'/yourls-api.php?signature='.$yourls_api.'&action=shorturl&format=simple&url='.urlencode($url); - $response = AJXP_Utils::getRemoteContent($yourls); - if (isSet($response)) { - $shorturl = $response; - if ($useidn) { - // WARNING: idn_to_utf8 requires php-idn module. - // WARNING: http_build_url requires php-pecl-http module. - $purl = parse_url($shorturl); - $purl['host'] = idn_to_utf8($purl['host']); - $shorturl = http_build_url($purl); - } - return $shorturl; - } - break; - - default: - break; - } - - return null; - - } - - protected function updateMetaShort($file, $elementId, $shortUrl) - { - $context = new UserSelection(ConfService::getRepository()); - $baseUrl = $context->currentBaseUrl(); - $node = new AJXP_Node($baseUrl.$file); - if ($node->hasMetaStore()) { - $metadata = $node->retrieveMetadata( - "ajxp_shared", - true, - AJXP_METADATA_SCOPE_REPOSITORY - ); - if ($elementId != -1) { - if (!is_array($metadata["element"][$elementId])) { - $metadata["element"][$elementId] = array(); - } - $metadata["element"][$elementId]["short_form_url"] = $shortUrl; - } else { - if(isSet($metadata["shares"])){ - $key = array_pop(array_keys($metadata["shares"])); - $metadata["shares"][$key]["short_form_url"] = $shortUrl; - }else{ - $metadata['short_form_url'] = $shortUrl; - } - } - $node->setMetadata( - "ajxp_shared", - $metadata, - true, - AJXP_METADATA_SCOPE_REPOSITORY - ); - } - } - -} diff --git a/core/src/plugins/shorten.multi/manifest.xml b/core/src/plugins/shorten.multi/manifest.xml index f1dbc7086f..6438d280b3 100755 --- a/core/src/plugins/shorten.multi/manifest.xml +++ b/core/src/plugins/shorten.multi/manifest.xml @@ -29,19 +29,11 @@ - - - - - - - - - + diff --git a/core/src/plugins/uploader.flex/FlexUpload.php b/core/src/plugins/uploader.flex/FlexUpload.php new file mode 100644 index 0000000000..82888769f9 --- /dev/null +++ b/core/src/plugins/uploader.flex/FlexUpload.php @@ -0,0 +1,125 @@ + + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Uploader\Processor; + +use Pydio\Core\Controller\Controller; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\PluginFramework\Plugin; +use Pydio\Core\Services\ConfService; +use Pydio\Core\Services\UsersService; +use Pydio\Core\Utils\Vars\StatHelper; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Legacy Flash plugin for upload + * @package AjaXplorer_Plugins + * @subpackage Uploader + */ +class FlexUpload extends Plugin +{ + private static $active = false; + + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Psr\Http\Message\ResponseInterface $response + */ + public function getTemplate(\Psr\Http\Message\ServerRequestInterface &$request, \Psr\Http\Message\ResponseInterface &$response){ + + /** @var ContextInterface $ctx */ + $ctx = $request->getAttribute("ctx"); + $confMaxSize = StatHelper::convertBytes(ConfService::getContextConf($ctx, "UPLOAD_MAX_SIZE", "uploader")); + $UploadMaxSize = min(StatHelper::convertBytes(ini_get('upload_max_filesize')), StatHelper::convertBytes(ini_get('post_max_size'))); + if($confMaxSize != 0) $UploadMaxSize = min ($UploadMaxSize, $confMaxSize); + $confTotalNumber = ConfService::getContextConf($ctx, "UPLOAD_MAX_NUMBER", "uploader"); + $confTotalSize = ConfService::getContextConf($ctx, "UPLOAD_MAX_SIZE_TOTAL", "uploader"); + $confTotalNumber = ConfService::getContextConf($ctx, "UPLOAD_MAX_NUMBER", "uploader"); + $maxLength = ConfService::getContextConf($ctx, "NODENAME_MAX_LENGTH"); + + $FlashVar = '&totalUploadSize='.$confTotalSize.'&fileSizeLimit='.$UploadMaxSize.'&maxFileNumber='.$confTotalNumber.'&maxFilenameLength='.$maxLength; + $pluginConfigs = $this->getConfigs(); + include($this->getBaseDir()."/flash_tpl.html"); + + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Psr\Http\Message\ResponseInterface $response + */ + public function preProcess(\Psr\Http\Message\ServerRequestInterface &$request, \Psr\Http\Message\ResponseInterface &$response) + { + /** @var ContextInterface $ctx */ + $ctx = $request->getAttribute("ctx"); + + //------------------------------------------------------------ + // SPECIAL HANDLING FOR FLEX UPLOADER RIGHTS FOR THIS ACTION + //------------------------------------------------------------ + if (UsersService::usersEnabled()) { + $loggedUser = $ctx->getUser(); + if ($request->getAttribute("action") == "upload" && + ($loggedUser == null || !$loggedUser->canWrite($ctx->getRepositoryId()."")) + && isSet($request->getUploadedFiles()['Filedata'])) { + $response = $response->withStatus(410, "Not authorized"); + return; + } + } + + $fileVars = $request->getUploadedFiles(); + if (isSet($fileVars["Filedata"])) { + self::$active = true; + $httpVars = $request->getParsedBody(); + $this->logDebug("Dir before base64", $httpVars); + $httpVars["dir"] = base64_decode(urldecode($httpVars["dir"])); + $request = $request->withParsedBody($httpVars); + + $existingUp = $_FILES["Filedata"]; + $filename = $httpVars["Filename"]; + // Rebuild UploadedFile object + $request = $request->withUploadedFiles(["userfile_0" => new \Zend\Diactoros\UploadedFile($existingUp["tmp_name"], $existingUp["size"], $existingUp["error"], $filename)]); + $this->logDebug("Setting FlexProc active"); + } + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Psr\Http\Message\ResponseInterface $response + * @throws \Exception + */ + public function postProcess(\Psr\Http\Message\ServerRequestInterface &$request, \Psr\Http\Message\ResponseInterface &$response) + { + if (!self::$active) { + return; + } + $result = $request->getAttribute("upload_process_result"); + if (isSet($result["SUCCESS"]) && $result["SUCCESS"] === true) { + $response = new \Zend\Diactoros\Response\EmptyResponse(200); + if (iSset($result["UPDATED_NODE"])) { + Controller::applyHook("node.change", array($result["UPDATED_NODE"], $result["UPDATED_NODE"], false)); + } else { + Controller::applyHook("node.change", array(null, $result["CREATED_NODE"], false)); + } + } else if (isSet($result["ERROR"]) && is_array($result["ERROR"])) { + $code = $result["ERROR"]["CODE"]; + $message = $result["ERROR"]["MESSAGE"]; + $response = $response->withStatus($code, $message); + } + } +} diff --git a/core/src/plugins/uploader.flex/class.FlexUploadProcessor.php b/core/src/plugins/uploader.flex/class.FlexUploadProcessor.php deleted file mode 100644 index 1c5d7c7445..0000000000 --- a/core/src/plugins/uploader.flex/class.FlexUploadProcessor.php +++ /dev/null @@ -1,68 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Legacy Flash plugin for upload - * @package AjaXplorer_Plugins - * @subpackage Uploader - */ -class FlexUploadProcessor extends AJXP_Plugin -{ - private static $active = false; - - public function preProcess($action, &$httpVars, &$fileVars) - { - if (isSet($fileVars["Filedata"])) { - self::$active = true; - $this->logDebug("Dir before base64", $httpVars); - $httpVars["dir"] = base64_decode(urldecode($httpVars["dir"])); - $fileVars["userfile_0"] = $fileVars["Filedata"]; - unset($fileVars["Filedata"]); - $this->logDebug("Setting FlexProc active"); - } - } - - public function postProcess($action, $httpVars, $postProcessData) - { - if (!self::$active) { - return false; - } - $this->logDebug("FlexProc is active=".self::$active, $postProcessData); - $result = $postProcessData["processor_result"]; - if (isSet($result["SUCCESS"]) && $result["SUCCESS"] === true) { - header('HTTP/1.0 200 OK'); - if (iSset($result["UPDATED_NODE"])) { - AJXP_Controller::applyHook("node.change", array($result["UPDATED_NODE"], $result["UPDATED_NODE"], false)); - } else { - AJXP_Controller::applyHook("node.change", array(null, $result["CREATED_NODE"], false)); - } - //die("200 OK"); - } else if (isSet($result["ERROR"]) && is_array($result["ERROR"])) { - $code = $result["ERROR"]["CODE"]; - $message = $result["ERROR"]["MESSAGE"]; - - //header("HTTP/1.0 $code $message"); - die("Error $code $message"); - } - } -} diff --git a/core/src/plugins/uploader.flex/flash_tpl.html b/core/src/plugins/uploader.flex/flash_tpl.html index 1c0155cd13..8651d29f97 100644 --- a/core/src/plugins/uploader.flex/flash_tpl.html +++ b/core/src/plugins/uploader.flex/flash_tpl.html @@ -1,15 +1,3 @@ -findPlugin("uploader", "flex")->getConfigs(); -?> @@ -116,7 +104,7 @@ flashVarExt = '&fileTypes='+exts.join(";")+'&fileTypeDescription='+escape(configs.get("ALLOWED_EXTENSIONS_READABLE")); } -var url = '?get_action=upload&secure_token='+parent.Connexion.SECURE_TOKEN+'&ajxp_sessid='; +var url = '?get_action=upload&secure_token='+parent.Connexion.SECURE_TOKEN+'&ajxp_sessid=&XDEBUG_SESSION=xdebug_ajaxplorer'; var FlashObject = "plugins/uploader.flex/FlashFileUpload.swf"; var FlashVarValue = "" + flashVarExt + "&uploadPage=" + escape(url) + ""; diff --git a/core/src/plugins/uploader.flex/i18n/conf/de.php b/core/src/plugins/uploader.flex/i18n/conf/de.php index 667d1d4467..39576b6f92 100644 --- a/core/src/plugins/uploader.flex/i18n/conf/de.php +++ b/core/src/plugins/uploader.flex/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Flash uploader" => "Flash", diff --git a/core/src/plugins/uploader.flex/i18n/conf/en.php b/core/src/plugins/uploader.flex/i18n/conf/en.php index 76e74c493d..ba72aef4f4 100644 --- a/core/src/plugins/uploader.flex/i18n/conf/en.php +++ b/core/src/plugins/uploader.flex/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Flash uploader" => "Flash", diff --git a/core/src/plugins/uploader.flex/i18n/conf/fr.php b/core/src/plugins/uploader.flex/i18n/conf/fr.php index eab8a1c96e..c34e25e635 100644 --- a/core/src/plugins/uploader.flex/i18n/conf/fr.php +++ b/core/src/plugins/uploader.flex/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Flash uploader" => "Programme d'envoi Flash", diff --git a/core/src/plugins/uploader.flex/i18n/conf/it.php b/core/src/plugins/uploader.flex/i18n/conf/it.php index 23c7bc63cf..7f20b07902 100644 --- a/core/src/plugins/uploader.flex/i18n/conf/it.php +++ b/core/src/plugins/uploader.flex/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Flash uploader" => "Flash", diff --git a/core/src/plugins/uploader.flex/i18n/conf/pt.php b/core/src/plugins/uploader.flex/i18n/conf/pt.php index b4cbde8a11..defe40f416 100644 --- a/core/src/plugins/uploader.flex/i18n/conf/pt.php +++ b/core/src/plugins/uploader.flex/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "Flash uploader" => "Enviar usando Flash", diff --git a/core/src/plugins/uploader.flex/manifest.xml b/core/src/plugins/uploader.flex/manifest.xml index d8e20e6da5..9cb19577fd 100644 --- a/core/src/plugins/uploader.flex/manifest.xml +++ b/core/src/plugins/uploader.flex/manifest.xml @@ -1,6 +1,6 @@ - + @@ -11,6 +11,11 @@ + + + + + @@ -35,7 +40,7 @@ ]]> + * This file is part of Pydio. + * + * Pydio 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. + * + * Pydio 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 Pydio. If not, see . + * + * The latest code can be found at . + */ +namespace Pydio\Uploader\Processor; + +use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Access\Core\Model\NodesDiff; +use Pydio\Access\Core\Model\UserSelection; +use Pydio\Core\Controller\Controller; +use Pydio\Core\Exception\AuthRequiredException; +use Pydio\Core\Exception\PydioException; +use Pydio\Core\Model\ContextInterface; +use Pydio\Core\Services\ApiKeysService; +use Pydio\Core\Services\LocaleService; +use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Core\Http\Message\ExternalUploadedFile; + +use Pydio\Core\Utils\Vars\XMLFilter; +use Pydio\Core\PluginFramework\Plugin; +use Pydio\Core\Utils\XMLHelper; +use Zend\Diactoros\Response\TextResponse; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +/** + * Processor for standard POST upload + * @package AjaXplorer_Plugins + * @subpackage Uploader + */ +class SimpleUpload extends Plugin +{ + /** + * @param $action + * @param $httpVars + * @param $fileVars + */ + public function getDropBg($action, $httpVars, $fileVars) + { + $lang = LocaleService::getLanguage(); + $img = AJXP_INSTALL_PATH."/plugins/uploader.html/i18n/$lang-dropzone.png"; + if(!is_file($img)) $img = AJXP_INSTALL_PATH."/plugins/uploader.html/i18n/en-dropzone.png"; + header("Content-Type: image/png; name=\"dropzone.png\""); + header("Content-Length: ".filesize($img)); + header('Cache-Control: public'); + readfile($img); + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Psr\Http\Message\ResponseInterface $response + * @throws \Pydio\Core\Exception\PydioException + */ + public function preProcess(\Psr\Http\Message\ServerRequestInterface &$request, \Psr\Http\Message\ResponseInterface &$response) + { + $httpVars = $request->getParsedBody(); + $serverData = $request->getServerParams(); + + if (!isSet($httpVars["input_stream"]) || isSet($httpVars["force_post"])) { + // Nothing to do + return; + } + + $this->logDebug("SimpleUpload::preProcess", $httpVars); + + // Setting the stream data + if (isset($serverData['HTTP_X_FILE_TMP_LOCATION'])) { + // The file has already been transferred + + // Checking headers + if (!isset($serverData['HTTP_X_FILE_SIZE'])) { + exit('Warning, wrong headers'); + } + + $fileNameH = $serverData['HTTP_X_FILE_NAME']; + $fileSizeH = (int)$serverData['HTTP_X_FILE_SIZE']; + + // Clean up dir name (backward compat) + if (dirname($httpVars["dir"]) == "/" && basename($httpVars["dir"]) == $fileNameH) { + $httpVars["dir"] = "/"; + } + + $clientFileName = basename($fileNameH); + + // Setting the stream to point to the file location + $streamOrFile = $serverData['HTTP_X_FILE_TMP_LOCATION']; + $errorStatus = UPLOAD_ERR_OK; + // Update UploadedFile object built on input stream with file name and size + $uploadedFile = new \Zend\Diactoros\UploadedFile( + $serverData['HTTP_X_FILE_TMP_LOCATION'], + $fileSizeH, + UPLOAD_ERR_OK, + $clientFileName + ); + + + } else if(isSet($serverData['HTTP_X_FILE_DIRECT_UPLOAD'])){ + + // Mandatory headers + $externalUploadStatus = $serverData['HTTP_X_FILE_DIRECT_UPLOAD']; + $fileNameH = $serverData['HTTP_X_FILE_NAME']; + $fileSizeH = (int)$serverData['HTTP_X_FILE_SIZE']; + + // Faking data if not present + if (empty($fileNameH)) { + $fileNameH = "fake-name"; + } + + if (empty($fileSieH)) { + $fileSizeH = 1; + } + + if(!ExternalUploadedFile::isValidStatus($externalUploadStatus)){ + throw new PydioException("Unrecognized direct upload status ". $externalUploadStatus); + } + + if($externalUploadStatus === ExternalUploadedFile::STATUS_REQUEST_OPTIONS){ + + if(!ApiKeysService::requestHasValidHeadersForAdminTask($request->getServerParams(), PYDIO_BOOSTER_TASK_IDENTIFIER)){ + throw new AuthRequiredException(); + } + + } + + $uploadedFile = new ExternalUploadedFile($externalUploadStatus, $fileSizeH, $fileNameH); + + } else { + + // The file is the post data stream + + // Mandatory headers + if (!isset($serverData['CONTENT_LENGTH'], $serverData['HTTP_X_FILE_NAME'])) { + throw new PydioException("Warning, missing headers!"); + } + + $fileNameH = $serverData['HTTP_X_FILE_NAME']; + $fileSizeH = (int)$serverData['HTTP_X_FILE_SIZE']; + + // Clean up dir name (backward compat) + if (dirname($httpVars["dir"]) == "/" && basename($httpVars["dir"]) == $fileNameH) { + $httpVars["dir"] = "/"; + } + + $clientFileName = basename($fileNameH); + + + // Checking headers + if (isSet($serverData['HTTP_X_FILE_SIZE'])) { + if ($serverData['CONTENT_LENGTH'] != $serverData['HTTP_X_FILE_SIZE']) { + $response = new TextResponse("Warning, wrong headers"); + } + } + + // Setting the stream to point to the post data + $streamOrFile = array_shift($request->getUploadedFiles())->getStream(); + // Update UploadedFile object built on input stream with file name and size + $uploadedFile = new \Zend\Diactoros\UploadedFile( + $streamOrFile, + $fileSizeH, + $streamOrFile->getError(), + $clientFileName + ); + + + } + + $request = $request->withUploadedFiles(["userfile_0" => $uploadedFile]); + } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Psr\Http\Message\ResponseInterface $response + */ + public function postProcess(\Psr\Http\Message\ServerRequestInterface &$request, \Psr\Http\Message\ResponseInterface &$response) + { + $httpVars = $request->getParsedBody(); + if ($request->getAttribute("api")!="v2" && !isSet($httpVars["simple_uploader"]) && !isSet($httpVars["xhr_uploader"]) && !isSet($httpVars["force_post"])) { + return; + } + $this->logDebug("SimpleUploadProc is active"); + $result = $request->getAttribute("upload_process_result"); + if(empty($result)){ + // Ignore + return; + } + $nodesDiff = new NodesDiff(); + if(isSet($result["CREATED_NODE"])){ + $nodesDiff->add($result["CREATED_NODE"]); + } + if(isSet($result["UPDATED_NODE"])){ + $nodesDiff->update($result["UPDATED_NODE"]); + } + + if (isSet($httpVars["simple_uploader"])) { + $response = $response->withHeader("Content-type", "text/html; charset=UTF-8"); + print(""); + } else { + if (isSet($result["ERROR"])) { + $message = $result["ERROR"]["MESSAGE"]." (".$result["ERROR"]["CODE"].")"; + $response = $response->withHeader("Content-type", "text/plain; charset=UTF-8"); + $response->getBody()->write($message); + } else { + + $nodesDiffXML = ""; + if (isSet($result["CREATED_NODE"]) || isSet($result["UPDATED_NODE"])) { + $nodesDiffXML = $nodesDiff->toXML(); + } + $response = $response->withHeader("Content-type", "text/xml; charset=UTF-8"); + $response->getBody()->write(XMLHelper::wrapDocument($nodesDiffXML)); + + /* for further implementation */ + if (!isSet($result["PREVENT_NOTIF"])) { + if (isset($result["CREATED_NODE"])) { + Controller::applyHook("node.change", array(null, $result["CREATED_NODE"], false)); + } else if (isSet($result["UPDATED_NODE"])) { + Controller::applyHook("node.change", array($result["UPDATED_NODE"], $result["UPDATED_NODE"], false)); + } + } + } + } + } + + /** + * @param $action + * @param $httpVars + * @param $fileVars + * @param \Pydio\Core\Model\ContextInterface $contextInterface + * @throws \Exception + */ + public function unifyChunks($action, $httpVars, $fileVars, \Pydio\Core\Model\ContextInterface $contextInterface) + { + $selection = UserSelection::fromContext($contextInterface, []); + $dir = InputFilter::decodeSecureMagic($httpVars["dir"]); + $destStreamURL = $selection->currentBaseUrl().$dir."/"; + $filename = InputFilter::decodeSecureMagic($httpVars["file_name"]); + + $chunks = array(); + $index = 0; + while (isSet($httpVars["chunk_".$index])) { + $chunks[] = InputFilter::decodeSecureMagic($httpVars["chunk_" . $index]); + $index++; + } + + $newDest = fopen($destStreamURL.$filename, "w"); + for ($i = 0; $i < count($chunks) ; $i++) { + $part = fopen($destStreamURL.$chunks[$i], "r"); + if(is_resource($part)){ + while (!feof($part)) { + fwrite($newDest, fread($part, 4096)); + } + fclose($part); + } + unlink($destStreamURL.$chunks[$i]); + } + fclose($newDest); + Controller::applyHook("node.change", array(null, new AJXP_Node($newDest), false)); + } +} diff --git a/core/src/plugins/uploader.html/class.MultiUploader.js b/core/src/plugins/uploader.html/class.MultiUploader.js index bab80af53d..42c1ee5708 100644 --- a/core/src/plugins/uploader.html/class.MultiUploader.js +++ b/core/src/plugins/uploader.html/class.MultiUploader.js @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . * Credit: * Original class by Stickman -- http://www.the-stickman.com * with thanks to: @@ -295,9 +295,9 @@ Class.create("MultiUploader", { return; } var stateImg = $(row).select("img")[0]; - if(state == 'loading') stateImg.src = ajxpResourcesFolder+'/images/yellowled.png'; - else if(state == 'done') stateImg.src = ajxpResourcesFolder+'/images/greenled.png'; - else if(state == 'error') stateImg.src = ajxpResourcesFolder+'/images/redled.png'; + if(state == 'loading') stateImg.src = pydio.Parameters.get('APPLICATION_ROOT')+'plugins/uploader.html/img/yellowled.png'; + else if(state == 'done') stateImg.src = pydio.Parameters.get('APPLICATION_ROOT')+'plugins/uploader.html/img/greenled.png'; + else if(state == 'error') stateImg.src = pydio.Parameters.get('APPLICATION_ROOT')+'plugins/uploader.html/img/redled.png'; }, diff --git a/core/src/plugins/uploader.html/class.SimpleUploadProcessor.php b/core/src/plugins/uploader.html/class.SimpleUploadProcessor.php deleted file mode 100644 index a8884ecaf0..0000000000 --- a/core/src/plugins/uploader.html/class.SimpleUploadProcessor.php +++ /dev/null @@ -1,157 +0,0 @@ - - * This file is part of Pydio. - * - * Pydio 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. - * - * Pydio 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 Pydio. If not, see . - * - * The latest code can be found at . - */ - -defined('AJXP_EXEC') or die( 'Access not allowed'); - -/** - * Processor for standard POST upload - * @package AjaXplorer_Plugins - * @subpackage Uploader - */ -class SimpleUploadProcessor extends AJXP_Plugin -{ - public function getDropBg($action, $httpVars, $fileVars) - { - $lang = ConfService::getLanguage(); - $img = AJXP_INSTALL_PATH."/plugins/uploader.html/i18n/$lang-dropzone.png"; - if(!is_file($img)) $img = AJXP_INSTALL_PATH."/plugins/uploader.html/i18n/en-dropzone.png"; - header("Content-Type: image/png; name=\"dropzone.png\""); - header("Content-Length: ".filesize($img)); - header('Cache-Control: public'); - readfile($img); - } - - public function preProcess($action, &$httpVars, &$fileVars) - { - if (!isSet($httpVars["input_stream"]) || isSet($httpVars["force_post"])) { - return false; - } - - $headersCheck = isset( - $_SERVER['CONTENT_LENGTH'], - $_SERVER['HTTP_X_FILE_NAME'] - ) ; - if (isSet($_SERVER['HTTP_X_FILE_SIZE'])) { - if ($_SERVER['CONTENT_LENGTH'] != $_SERVER['HTTP_X_FILE_SIZE']) { - exit('Warning, wrong headers'); - } - } - $fileNameH = $_SERVER['HTTP_X_FILE_NAME']; - $fileSizeH = $_SERVER['CONTENT_LENGTH']; - - if (dirname($httpVars["dir"]) == "/" && basename($httpVars["dir"]) == $fileNameH) { - $httpVars["dir"] = "/"; - } - $this->logDebug("SimpleUpload::preProcess", $httpVars); - - if ($headersCheck) { - // create the object and assign property - $fileVars["userfile_0"] = array( - "input_upload" => true, - "name" => SystemTextEncoding::fromUTF8(basename($fileNameH)), - "size" => $fileSizeH - ); - } else { - exit("Warning, missing headers!"); - } - } - - public function postProcess($action, $httpVars, $postProcessData) - { - if (!isSet($httpVars["simple_uploader"]) && !isSet($httpVars["xhr_uploader"]) && !isSet($httpVars["force_post"])) { - return false; - } - $this->logDebug("SimpleUploadProc is active"); - $result = $postProcessData["processor_result"]; - - if (isSet($httpVars["simple_uploader"])) { - print(""); - } else { - if (isSet($result["ERROR"])) { - $message = $result["ERROR"]["MESSAGE"]." (".$result["ERROR"]["CODE"].")"; - exit($message); - } else { - AJXP_XMLWriter::header(); - if (isSet($result["CREATED_NODE"]) || isSet($result["UPDATED_NODE"])) { - AJXP_XMLWriter::writeNodesDiff(array((isSet($result["UPDATED_NODE"])?"UPDATE":"ADD") => array($result[(isSet($result["UPDATED_NODE"])?"UPDATED":"CREATED")."_NODE"])), true); - } - AJXP_XMLWriter::close(); - /* for further implementation */ - if (!isSet($result["PREVENT_NOTIF"])) { - if (isset($result["CREATED_NODE"])) { - AJXP_Controller::applyHook("node.change", array(null, $result["CREATED_NODE"], false)); - } else if (isSet($result["UPDATED_NODE"])) { - AJXP_Controller::applyHook("node.change", array($result["UPDATED_NODE"], $result["UPDATED_NODE"], false)); - } - } - //exit("OK"); - } - } - - } - - public function unifyChunks($action, $httpVars, $fileVars) - { - $repository = ConfService::getRepository(); - if (!$repository->detectStreamWrapper(true)) { - return false; - } - $selection = new UserSelection($repository); - $dir = AJXP_Utils::decodeSecureMagic($httpVars["dir"]); - $destStreamURL = $selection->currentBaseUrl().$dir."/"; - $filename = AJXP_Utils::decodeSecureMagic($httpVars["file_name"]); - - $chunks = array(); - $index = 0; - while (isSet($httpVars["chunk_".$index])) { - $chunks[] = AJXP_Utils::decodeSecureMagic($httpVars["chunk_".$index]); - $index++; - } - - $newDest = fopen($destStreamURL.$filename, "w"); - for ($i = 0; $i < count($chunks) ; $i++) { - $part = fopen($destStreamURL.$chunks[$i], "r"); - if(is_resource($part)){ - while (!feof($part)) { - fwrite($newDest, fread($part, 4096)); - } - fclose($part); - } - unlink($destStreamURL.$chunks[$i]); - } - fclose($newDest); - AJXP_Controller::applyHook("node.change", array(null, new AJXP_Node($newDest), false)); - } -} diff --git a/core/src/plugins/uploader.html/class.XHRUploader.js b/core/src/plugins/uploader.html/class.XHRUploader.js index a5f58bd6a6..e2a9a52790 100644 --- a/core/src/plugins/uploader.html/class.XHRUploader.js +++ b/core/src/plugins/uploader.html/class.XHRUploader.js @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * - * The latest code can be found at . + * The latest code can be found at . * Description : Class for simple XHR multiple upload, HTML5 only */ Class.create("XHRUploader", { @@ -24,19 +24,21 @@ Class.create("XHRUploader", { listTarget : null, mainForm: null, id : null, - rowAsProgressBar: false, dataModel: null, contextNode: null, currentBackgroundPanel: null, // Copy get/setUserPrefs from AjxpPane getUserPreference: AjxpPane.prototype.getUserPreference, setUserPreference: AjxpPane.prototype.setUserPreference, + configs: null, initialize : function( formObject, mask ){ window.UploaderInstanceRunning = true; + this.configs = pydio.getPluginConfigs("mq"); + formObject = $(formObject); // Main form this.htmlElement = this.mainForm = formObject; @@ -44,8 +46,6 @@ Class.create("XHRUploader", { // Where to write the list this.listTarget = formObject.down('div.uploadFilesList'); - this.rowAsProgressBar = this.listTarget.hasClassName('rowAsProgressBar'); - // How many elements? this.count = 0; // Current index @@ -270,14 +270,6 @@ Class.create("XHRUploader", { '+MessageHash[256]+' : 0%\ '); } - var options = { - animate : false, // Animate the progress? - default: true - showText : false, // show text with percentage in next to the progressbar? - default : true - width : 154, // Width of the progressbar - don't forget to adjust your image too!!! - boxImage : ajxpResourcesFolder+'/images/progress_box.gif', // boxImage : image around the progress bar - barImage : ajxpResourcesFolder+'/images/progress_bar.gif', // Image to use in the progressbar. Can be an array of images too. - height : 4 // Height of the progressbar - don't forget to adjust your image too!!! - }; this.mainForm.down('#clear_list_button').observe("click", function(e){ pydio.getController().multi_selector.clearList(); pydio.getController().multi_selector.updateTotalData(); @@ -285,10 +277,6 @@ Class.create("XHRUploader", { this.optionPane = this.createOptionsPane(); this.optionPane.loadData(); - if(this.mainForm.down('#phBar_total')){ - this.totalProgressBar = new JS_BRAMUS.jsProgressBar($('pgBar_total'), 0, options); - this.mainForm.PROGRESSBAR = this.totalProgressBar; - } this.totalStrings = $('totalStrings'); this.uploadedString = $('uploadedString'); @@ -553,75 +541,13 @@ Class.create("XHRUploader", { } var id = 'pgBar_' + (this.listTarget.childNodes.length + 1); - if(this.rowAsProgressBar){ - this.createInnerProgressBar(item, id); - }else{ - this.createProgressBar(item, id); - } + this.createInnerProgressBar(item, id); item.file = file; item.updateStatus('new'); this.updateTotalData(); this.sendButton.removeClassName("disabled"); }, - - createProgressBar : function(item, id){ - var div = new Element('div', {id:id, style:'-moz-border-radius:2px;border-radius:2px;'}); - div.setStyle({ - border:'1px solid #ccc', - backgroundColor: 'white', - marginTop: '7px', - height:'4px', - padding:0, - width:'154px' - }); - var percentText = new Element('span', {style:"float:right;display:block;width:30px;text-align:center;"}); - var statusText = new Element('span', {style:"float:right;display:block;width:66px;overflow:hidden;text-align:right;"}); - var container = new Element('div', {style:'border:none;padding:0;padding-right:5px;color: #777;'}); - container.insert(statusText); - container.insert(percentText); - container.insert(div); - item.insert(container); - item.percentText = percentText; - var options = { - animate : false, - showText : false, - width : 154, - boxImage : ajxpResourcesFolder+'/images/progress_box.gif', - barImage : ajxpResourcesFolder+'/images/progress_bar.gif', - height : 4 - }; - item.pgBar = new JS_BRAMUS.jsProgressBar(div, 0, options); - item.statusText = statusText; - item.updateProgress = function(computableEvent, percentage){ - if(percentage == null){ - percentage = Math.round((computableEvent.loaded * 100) / computableEvent.total); - this.bytesLoaded = computableEvent.loaded; - } - if(!this.percentValue || this.percentValue != percentage){ - this.percentText.innerHTML = percentage + '%'; - this.pgBar.setPercentage(percentage); - } - this.percentValue = percentage; - }.bind(item); - var oThis = this; - item.updateStatus = function(status){ - this.status = status; - var messageIds = { - "new" : 433, - "loading":434, - "loaded":435, - "error":436 - }; - try{ - status = window.MessageHash[messageIds[status]]; - }catch(e){} - if(oThis.currentBackgroundPanel){ - oThis.currentBackgroundPanel.update(item.file.name.stripTags() + ' ['+status+']'); - } - this.statusText.innerHTML = "["+status+"]"; - }.bind(item); - }, - + createInnerProgressBar : function(item, id){ var statusText = new Element('span', {className:"statusText"}); var percentText = new Element('span', {className:"percentText"}); @@ -742,16 +668,40 @@ Class.create("XHRUploader", { return false; }, - initializeXHR : function(item, queryStringParam, forceDir){ + initializeXHR : function(item, queryStringParam, forceDir, callback){ var currentDir = this.contextNode.getPath(); if(forceDir) currentDir = forceDir; var xhr = new XMLHttpRequest(); - var uri = ajxpBootstrap.parameters.get('ajxpServerAccess')+"&get_action=upload&xhr_uploader=true&dir="+encodeURIComponent(currentDir); - if(queryStringParam){ - uri += '&' + queryStringParam; - } + var uri; + + if (!this.configs.get("UPLOAD_ACTIVE")) { + uri = ajxpBootstrap.parameters.get('ajxpServerAccess') + "&get_action=upload&xhr_uploader=true&dir=" + encodeURIComponent(currentDir); + + if (queryStringParam) { + uri += '&' + queryStringParam; + } + } else { + + var secure = this.configs.get("BOOSTER_MAIN_SECURE"); + if(this.configs.get("BOOSTER_UPLOAD_ADVANCED") && this.configs.get("BOOSTER_UPLOAD_ADVANCED")['booster_upload_advanced'] === 'custom' && this.configs.get("BOOSTER_UPLOAD_ADVANCED")['UPLOAD_SECURE']){ + secure = this.configs.get("BOOSTER_UPLOAD_ADVANCED")['UPLOAD_SECURE']; + } + var host = this.configs.get("BOOSTER_MAIN_HOST"); + if(this.configs.get("BOOSTER_UPLOAD_ADVANCED") && this.configs.get("BOOSTER_UPLOAD_ADVANCED")['booster_upload_advanced'] === 'custom' && this.configs.get("BOOSTER_UPLOAD_ADVANCED")['UPLOAD_HOST']){ + host = this.configs.get("BOOSTER_UPLOAD_ADVANCED")['UPLOAD_HOST']; + } + var port = this.configs.get("BOOSTER_MAIN_PORT"); + if(this.configs.get("BOOSTER_UPLOAD_ADVANCED") && this.configs.get("BOOSTER_UPLOAD_ADVANCED")['booster_upload_advanced'] === 'custom' && this.configs.get("BOOSTER_UPLOAD_ADVANCED")['UPLOAD_PORT']){ + port = this.configs.get("BOOSTER_UPLOAD_ADVANCED")['UPLOAD_PORT']; + } + + xhr.withCredentials = true; + + uri = "http"+(secure?"s":"")+"://"+host+":"+port+"/"+this.configs.get("UPLOAD_PATH")+"/"+pydio.user.activeRepository + currentDir + item.file.name; + } + var upload = xhr.upload; upload.addEventListener("progress", function(e){ @@ -759,6 +709,7 @@ Class.create("XHRUploader", { item.updateProgress(e); this.updateTotalData(); }.bind(this), false); + xhr.onreadystatechange = function() { if (xhr.readyState == 4) { item.updateProgress(null, 100); @@ -776,14 +727,23 @@ Class.create("XHRUploader", { } }.bind(this); - upload.onerror = function(){ + upload.onerror = function() { + if (this.configs.get("UPLOAD_ACTIVE")) { + this.configs.set("UPLOAD_ACTIVE", false); + return this.initializeXHR(item, queryStringParam, forceDir, callback); + } + item.updateStatus('error'); - }; + }.bind(this); xhr.open("POST", uri, true); - try {if(Prototype.Browser.IE10) xhr.responseType = 'msxml-document'; } catch(e){} - return xhr; - + + try { + if(Prototype.Browser.IE10) xhr.responseType = 'msxml-document'; + } catch(e){ + } + + return callback(xhr); }, sendFileMultipart : function(item){ @@ -853,24 +813,32 @@ Class.create("XHRUploader", { }else{ // 'overwrite' : do nothing! } - - var xhr = this.initializeXHR(item, (auto_rename?"auto_rename=true":""), currentDir); - var file = item.file; + + var xhr = this.initializeXHR(item, (auto_rename?"auto_rename=true":""), currentDir, function (item) { + return function (xhr) { + + var file = item.file; + + if(window.FormData){ + this.sendFileUsingFormData(xhr, file); + } else if(window.FileReader) { + var fileReader = new FileReader(); + fileReader.onload = function(e){ + this.xhrSendAsBinary(xhr, file.name, e.target.result, item) + }.bind(this); + fileReader.readAsBinaryString(file); + }else if(file.getAsBinary){ + window.testFile = file; + var data = file.getAsBinary(); + this.xhrSendAsBinary(xhr, file.name, data, item) + } + + return xhr; + } + }(item).bind(this)); + item.updateProgress(null, 0); - item.updateStatus('loading'); - if(window.FormData){ - this.sendFileUsingFormData(xhr, file); - }else if(window.FileReader){ - var fileReader = new FileReader(); - fileReader.onload = function(e){ - this.xhrSendAsBinary(xhr, file.name, e.target.result, item) - }.bind(this); - fileReader.readAsBinaryString(file); - }else if(file.getAsBinary){ - window.testFile = file; - var data = file.getAsBinary(); - this.xhrSendAsBinary(xhr, file.name, data, item) - } + item.updateStatus('loading'); }, sendFileUsingFormData : function(xhr, file){ @@ -1103,4 +1071,4 @@ if(!XMLHttpRequest.prototype.sendAsBinary){ var blob = bb.getBlob(); this.send(blob); }; -} \ No newline at end of file +} diff --git a/core/src/plugins/uploader.html/i18n/conf/cs.php b/core/src/plugins/uploader.html/i18n/conf/cs.php index 5a8efa11f3..c7cd4bad83 100644 --- a/core/src/plugins/uploader.html/i18n/conf/cs.php +++ b/core/src/plugins/uploader.html/i18n/conf/cs.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "HTML Uploader" => "Stadartní", diff --git a/core/src/plugins/uploader.html/i18n/conf/de.php b/core/src/plugins/uploader.html/i18n/conf/de.php index 4f503a657f..17777174f0 100644 --- a/core/src/plugins/uploader.html/i18n/conf/de.php +++ b/core/src/plugins/uploader.html/i18n/conf/de.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "HTML Uploader" => "Standard", diff --git a/core/src/plugins/uploader.html/i18n/conf/en.php b/core/src/plugins/uploader.html/i18n/conf/en.php index f4545ac73d..ef5bff69c6 100644 --- a/core/src/plugins/uploader.html/i18n/conf/en.php +++ b/core/src/plugins/uploader.html/i18n/conf/en.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "HTML Uploader" => "Standard", diff --git a/core/src/plugins/uploader.html/i18n/conf/fr.php b/core/src/plugins/uploader.html/i18n/conf/fr.php index 1bd050fa13..1488164d62 100644 --- a/core/src/plugins/uploader.html/i18n/conf/fr.php +++ b/core/src/plugins/uploader.html/i18n/conf/fr.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "HTML Uploader" => "Programme d'envoi HTML", diff --git a/core/src/plugins/uploader.html/i18n/conf/it.php b/core/src/plugins/uploader.html/i18n/conf/it.php index 1036e51706..971f39489c 100644 --- a/core/src/plugins/uploader.html/i18n/conf/it.php +++ b/core/src/plugins/uploader.html/i18n/conf/it.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "HTML Uploader" => "Standard", diff --git a/core/src/plugins/uploader.html/i18n/conf/pt.php b/core/src/plugins/uploader.html/i18n/conf/pt.php index 7fe63213c8..1437947556 100644 --- a/core/src/plugins/uploader.html/i18n/conf/pt.php +++ b/core/src/plugins/uploader.html/i18n/conf/pt.php @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with Pydio. If not, see . * -* The latest code can be found at . +* The latest code can be found at . */ $mess=array( "HTML Uploader" => "Envio por HTML", diff --git a/core/src/plugins/gui.ajax/res/themes/orbit/images/greenled.png b/core/src/plugins/uploader.html/img/greenled.png similarity index 100% rename from core/src/plugins/gui.ajax/res/themes/orbit/images/greenled.png rename to core/src/plugins/uploader.html/img/greenled.png diff --git a/core/src/plugins/gui.ajax/res/themes/orbit/images/redled.png b/core/src/plugins/uploader.html/img/redled.png similarity index 100% rename from core/src/plugins/gui.ajax/res/themes/orbit/images/redled.png rename to core/src/plugins/uploader.html/img/redled.png diff --git a/core/src/plugins/gui.ajax/res/themes/orbit/images/yellowled.png b/core/src/plugins/uploader.html/img/yellowled.png similarity index 100% rename from core/src/plugins/gui.ajax/res/themes/orbit/images/yellowled.png rename to core/src/plugins/uploader.html/img/yellowled.png diff --git a/core/src/plugins/uploader.html/js/react/UploaderGlobalListener.js b/core/src/plugins/uploader.html/js/react/UploaderGlobalListener.js new file mode 100644 index 0000000000..1394927880 --- /dev/null +++ b/core/src/plugins/uploader.html/js/react/UploaderGlobalListener.js @@ -0,0 +1,85 @@ +(function(global){ + + var initUploaderExtension = function(){ + + var dropzones = $$('[ajxpClass="FilesList"]'); + if(!dropzones.length) { + return; + } + + var dropzone = dropzones[0]; + dropzone.addClassName('droparea'); + var selector = '#content_pane,div.webfx-tree-item,.ajxpNodeProvider'; + var dragOverFunc = function(event) { + var el = Event.findElement(event, selector); + if(el.hasClassName('ajxpNodeProvider') && el.ajxpNode.isLeaf()){ + el = Event.findElement(event, '#content_pane'); + } + el.addClassName("dropareaHover"); + event.preventDefault(); + }; + var dropFunc = function(event) { + event.preventDefault(); + var el = Event.findElement(event, selector); + if(el.hasClassName('ajxpNodeProvider') && el.ajxpNode.isLeaf()){ + el = Event.findElement(event, '#content_pane'); + } + el.removeClassName("dropareaHover"); + var items = event.dataTransfer.items || []; + var files = event.dataTransfer.files; + ResourcesManager.loadClassesAndApply(["UploaderModel"], function(){ + var passItems, contextNode; + if (items.length && items[0] && (items[0].getAsEntry || items[0].webkitGetAsEntry)) { + passItems = items; + } + if(el.ajxpNode) { + contextNode = el.ajxpNode; + } else { + contextNode = pydio.getContextHolder().getContextNode(); + } + UploaderModel.Store.getInstance().handleDropEventResults(passItems, files, contextNode); + if(!UploaderModel.Store.getInstance().getAutoStart()){ + pydio.getController().fireAction('upload'); + } + }); + }; + var enterFunc = function(){ + var el = Event.findElement(event, selector); + if(el.hasClassName('ajxpNodeProvider') && el.ajxpNode.isLeaf()){ + el = Event.findElement(event, '#content_pane'); + } + el.addClassName("dropareaHover"); + }; + var leaveFunc = function(){ + var el = Event.findElement(event, selector); + if(el.hasClassName('ajxpNodeProvider') && el.ajxpNode.isLeaf()){ + el = Event.findElement(event, '#content_pane'); + } + el.removeClassName("dropareaHover"); + }; + AjxpDroppables.dragOverHook = dragOverFunc; + AjxpDroppables.dropHook = dropFunc; + AjxpDroppables.dragEnterHook = enterFunc; + AjxpDroppables.dragLeaveHook = leaveFunc; + dropzone.addEventListener("dragover", dragOverFunc, true); + dropzone.addEventListener("drop", dropFunc, true); + dropzone.addEventListener("dragenter", enterFunc, true); + dropzone.addEventListener("dragleave", leaveFunc, true); + document.observeOnce("ajaxplorer:trigger_repository_switch", function(){ + dropzone.removeClassName('droparea'); + dropzone.removeEventListener("dragover", dragOverFunc, true); + dropzone.removeEventListener("drop", dropFunc, true); + dropzone.removeEventListener("dragenter", enterFunc, true); + dropzone.removeEventListener("dragleave", leaveFunc, true); + AjxpDroppables.dragOverHook = null; + AjxpDroppables.dropHook = null; + AjxpDroppables.dragEnterHook = null; + AjxpDroppables.dragLeaveHook = null; + }); + } + + var ns = global.UploaderGlobalListener || {}; + ns.initUploaderExtension = initUploaderExtension; + global.UploaderGlobalListener = ns; + +})(window); \ No newline at end of file diff --git a/core/src/plugins/uploader.html/js/react/UploaderModel.js b/core/src/plugins/uploader.html/js/react/UploaderModel.js new file mode 100644 index 0000000000..d41684a9ad --- /dev/null +++ b/core/src/plugins/uploader.html/js/react/UploaderModel.js @@ -0,0 +1,645 @@ +(function(global){ + + class StatusItem extends Observable{ + constructor(type){ + super(); + this._status = 'new'; + this._type = type; + this._id = Math.random(); + this._errorMessage = null; + } + getId(){ + return this._id; + } + getLabel(){ + + } + getType(){ + return this._type; + } + getStatus(){ + return this._status; + } + setStatus(status){ + this._status = status; + this.notify('status'); + } + getErrorMessage(){ + return this._errorMessage || ''; + } + onError(errorMessage){ + this._errorMessage = errorMessage; + this.setStatus('error'); + } + process(completeCallback){ + this._doProcess(completeCallback); + } + abort(completeCallback){ + if(this._status !== 'loading') return; + console.log('Should Stop XHR'); + this._doAbort(completeCallback); + } + } + + class UploadItem extends StatusItem{ + + constructor(file, targetNode, relativePath = null){ + super('file'); + this._file = file; + this._status = 'new'; + this._progress = 0; + this._targetNode = targetNode; + this._repositoryId = global.pydio.user.activeRepository; + this._relativePath = relativePath; + } + getMqConfigs(){ + return global.pydio.getPluginConfigs('mq'); + } + getFile(){ + return this._file; + } + getSize(){ + return this._file.size; + } + getLabel(){ + return this._relativePath ? this._relativePath : this._file.name; + } + getProgress(){ + return this._progress; + } + setProgress(newValue, bytes = null){ + this._progress = newValue; + this.notify('progress', newValue); + if(bytes !== null) { + this.notify('bytes', bytes); + } + } + getRelativePath(){ + return this._relativePath; + } + buildQueryString(){ + + let fullPath = this._targetNode.getPath(); + if(this._relativePath) { + fullPath += PathUtils.getDirname(this._relativePath); + } + let currentRepo = global.pydio.user.activeRepository; + + let queryString = '&get_action=upload&xhr_uploader=true&dir=' + encodeURIComponent(fullPath); + + let dataModel = global.pydio.getContextHolder(); + let nodeName = PathUtils.getBasename(this._file.name); + var newNode = new AjxpNode(fullPath+"/"+nodeName); + if(this._file.size){ + newNode.getMetadata().set("filesize", this._file.size); + } + try{ + let params = null; + if(currentRepo !== this._repositoryId) { + params = {tmp_repository_id:this._repositoryId}; + } + dataModel.applyCheckHook(newNode, params); + }catch(e){ + throw new Error('Error while checking before uploads'); + } + let overwriteStatus = UploaderConfigs.getInstance().getOption("DEFAULT_EXISTING", "upload_existing"); + if(overwriteStatus === 'rename'){ + queryString += '&auto_rename=true'; + }else if(overwriteStatus === 'alert' && !this._relativePath && currentRepo === this._repositoryId){ + if(dataModel.fileNameExists(nodeName, false, this._targetNode)){ + if(!global.confirm(MessageHash[124])){ + throw new Error('File already exists'); + } + } + } + if(currentRepo !== this._repositoryId){ + queryString += '&tmp_repository_id=' + this._repositoryId; + } + return queryString; + } + _parseXHRResponse(){ + if(!this.xhr) return; + if (this.xhr.responseXML){ + var result = PydioApi.getClient().parseXmlMessage(this.xhr.responseXML); + if(!result) this.onError('Empty response'); + }else if (this.xhr.responseText && this.xhr.responseText != 'OK') { + this.onError('Unexpected response: ' + this.xhr.responseText); + } + } + _doProcess(completeCallback){ + let complete = function(){ + this.setStatus('loaded'); + this._parseXHRResponse(); + completeCallback(); + }.bind(this); + let error = function(){ + this.setStatus('error'); + completeCallback(); + }.bind(this); + let progress = function(computableEvent){ + let percentage = Math.round((computableEvent.loaded * 100) / computableEvent.total); + let bytesLoaded = computableEvent.loaded; + this.setProgress(percentage, bytesLoaded); + }.bind(this); + this.setStatus('loading'); + + let maxUpload = parseFloat(UploaderConfigs.getInstance().getOption('UPLOAD_MAX_SIZE')); + if(this.getSize() > maxUpload){ + this.onError('File is too big: contact your admin to raise the upload value, or use the desktop client.'); + completeCallback(); + return; + } + + let queryString; + try{ + queryString = this.buildQueryString(); + }catch(e){ + this.onError(e.message); + completeCallback(); + return; + } + + // Checks applied. + if(this.getMqConfigs().get('UPLOAD_ACTIVE')){ + + this.tryAlternativeUpload(complete, progress, function(){ + // Failed, switch back to normal upload. + this.xhr = PydioApi.getClient().uploadFile(this._file,'userfile_0',queryString,complete,error,progress); + }.bind(this)); + + }else{ + + this.xhr = PydioApi.getClient().uploadFile(this._file,'userfile_0',queryString,complete,error,progress); + + } + + } + _doAbort(completeCallback){ + if(this.xhr){ + try{ + this.xhr.abort(); + }catch(e){} + } + } + + tryAlternativeUpload(completeCallback, progressCallback, errorCallback){ + let configs = this.getMqConfigs(); + var secure = configs.get("BOOSTER_MAIN_SECURE"); + if(configs.get("BOOSTER_UPLOAD_ADVANCED") && configs.get("BOOSTER_UPLOAD_ADVANCED")['booster_upload_advanced'] === 'custom' && configs.get("BOOSTER_UPLOAD_ADVANCED")['UPLOAD_SECURE']){ + secure = this.configs.get("BOOSTER_UPLOAD_ADVANCED")['UPLOAD_SECURE']; + } + var host = configs.get("BOOSTER_MAIN_HOST"); + if(configs.get("BOOSTER_UPLOAD_ADVANCED") && configs.get("BOOSTER_UPLOAD_ADVANCED")['booster_upload_advanced'] === 'custom' && configs.get("BOOSTER_UPLOAD_ADVANCED")['UPLOAD_HOST']){ + host = configs.get("BOOSTER_UPLOAD_ADVANCED")['UPLOAD_HOST']; + } + var port = configs.get("BOOSTER_MAIN_PORT"); + if(configs.get("BOOSTER_UPLOAD_ADVANCED") && configs.get("BOOSTER_UPLOAD_ADVANCED")['booster_upload_advanced'] === 'custom' && configs.get("BOOSTER_UPLOAD_ADVANCED")['UPLOAD_PORT']){ + port = configs.get("BOOSTER_UPLOAD_ADVANCED")['UPLOAD_PORT']; + } + let fullPath = this._targetNode.getPath(); + if(this._relativePath) { + fullPath += PathUtils.getDirname(this._relativePath); + } + fullPath += '/' + PathUtils.getBasename(this._file.name); + + let url = "http"+(secure?"s":"")+"://"+host+":"+port+"/"+configs.get("UPLOAD_PATH")+"/"+this._repositoryId + fullPath; + let queryString = ''; + let overwriteStatus = UploaderConfigs.getInstance().getOption("DEFAULT_EXISTING", "upload_existing"); + if(overwriteStatus === 'rename') { + queryString += 'auto_rename=true'; + } + try{ + this.xhr = PydioApi.getClient().uploadFile(this._file,'userfile_0',queryString,completeCallback,errorCallback,progressCallback, url, {withCredentials:true}); + }catch(e){ + errorCallback(); + } + } + } + + class FolderItem extends StatusItem{ + constructor(path, targetNode){ + super('folder'); + this._path = path; + this._targetNode = targetNode; + } + getPath(){ + return this._path; + } + getLabel(){ + return PathUtils.getBasename(this._path); + } + _doProcess(completeCallback){ + let fullPath = this._targetNode.getPath() + this._path; + let params = { + get_action: 'mkdir', + dir: PathUtils.getDirname(fullPath), + dirname:PathUtils.getBasename(fullPath), + ignore_exists:true, + }; + PydioApi.getClient().request(params, function(t){ + this.setStatus('loaded'); + + var result = PydioApi.getClient().parseXmlMessage(t.responseXML); + if(!result) this.onError('Empty response'); + + completeCallback(); + }.bind(this)); + } + _doAbort(completeCallback){ + if(global.console) global.console.log('Cannot abort folder creation'); + } + } + + class UploadTask extends PydioTasks.Task{ + + constructor(){ + super({ + id : 'local-upload-task', + userId : global.pydio.user.id, + wsId : global.pydio.user.activeRepository, + flags : PydioTasks.Task.FLAG_HAS_PROGRESS|PydioTasks.Task.FLAG_STOPPABLE, + label : "Uploading files to server...", + status : PydioTasks.Task.STATUS_COMPLETE, + statusMessage : '' + }); + } + + setProgress(progress){ + this._internal['progress'] = progress; + this.updateStatus(PydioTasks.Task.STATUS_RUNNING); + } + setPending(queueSize){ + this._internal['statusMessage'] = queueSize + ' files waiting for upload'; + this.updateStatus(PydioTasks.Task.STATUS_PENDING); + } + setRunning(queueSize){ + this._internal['statusMessage'] = 'Uploading ' + queueSize + ' files'; + this.updateStatus(PydioTasks.Task.STATUS_RUNNING); + } + setIdle(){ + this._internal['statusMessage'] = ''; + this.updateStatus(PydioTasks.Task.STATUS_COMPLETE); + } + updateStatus(status){ + this._internal['status'] = status; + this.notifyMainStore(); + } + + notifyMainStore(){ + PydioTasks.Store.getInstance().notify("tasks_updated"); + } + + hasOpenablePane(){ + return true; + } + openDetailPane(){ + global.pydio.Controller.fireAction("upload"); + } + + static getInstance(){ + if(!UploadTask.__INSTANCE) { + UploadTask.__INSTANCE = new UploadTask(); + PydioTasks.Store.getInstance().enqueueLocalTask(UploadTask.__INSTANCE); + } + return UploadTask.__INSTANCE; + } + + } + + class UploaderStore extends Observable{ + + constructor(){ + super(); + this._folders = []; + this._uploads = []; + this._processing = []; + this._processed = []; + // Todo + this._queueCounter = 0; + this._maxQueueSize = 2; + } + recomputeGlobalProgress(){ + let totalCount = 0; + let totalProgress = 0; + this._uploads.concat(this._processing).forEach(function(item){ + if(!item.getProgress) return; + totalCount ++; + totalProgress += item.getProgress(); + }); + let progress; + if(!totalCount) { + progress = 0; + }else{ + progress = Math.ceil(totalProgress / totalCount); + } + return progress; + } + getAutoStart(){ + return UploaderConfigs.getInstance().getOptionAsBool("DEFAULT_AUTO_START", "upload_auto_send"); + } + getAutoClose(){ + return UploaderConfigs.getInstance().getOptionAsBool("DEFAULT_AUTO_CLOSE", "upload_auto_close"); + } + pushFolder(folderItem){ + this._folders.push(folderItem); + UploadTask.getInstance().setPending(this.getQueueSize()); + if(this.getAutoStart() && !this._processing.length) { + this.processNext(); + } // Autostart with queue was empty before + this.notify('update'); + } + pushFile(uploadItem){ + this._uploads.push(uploadItem); + UploadTask.getInstance().setPending(this.getQueueSize()); + uploadItem.observe("progress", function(){ + let pg = this.recomputeGlobalProgress(); + UploadTask.getInstance().setProgress(pg); + }.bind(this)); + if(this.getAutoStart() && !this._processing.length) { + this.processNext(); + } // Autostart with queue was empty before + this.notify('update'); + } + log(){ + if(global.console){ + global.console.log("Uploads", this._uploads); + global.console.log("Folders", this._folders); + } + } + processQueue(){ + let next = this.getNext(); + while(next !== null){ + next.process(function(){ + this._processed.push(next); + this.notify("update"); + }.bind(this)); + next = this.getNext(); + } + } + getQueueSize(){ + return this._folders.length + this._uploads.length + this._processing.length; + } + clearAll(){ + this._folders = []; + this._uploads = []; + this._processing = []; + this._processed = []; + this.notify('update'); + UploadTask.getInstance().setIdle(); + } + processNext(){ + let processable = this.getNext(); + if(processable){ + this._processing.push(processable); + UploadTask.getInstance().setRunning(this.getQueueSize()); + processable.process(function(){ + this._processing = LangUtils.arrayWithout(this._processing, this._processing.indexOf(processable)); + this._processed.push(processable); + this.processNext(); + this.notify("update"); + }.bind(this)); + }else{ + UploadTask.getInstance().setIdle(); + if(this.hasErrors()){ + if(!pydio.getController().react_selector){ + global.pydio.getController().fireAction("upload"); + } + }else if(this.getAutoClose()){ + this.notify("auto_close"); + } + } + } + getNext(){ + if(this._folders.length){ + return this._folders.shift(); + } + if(this._uploads.length){ + return this._uploads.shift(); + } + } + stopOrRemoveItem(item){ + item.abort(); + ['_uploads', '_folders', '_processing', '_processed'].forEach(function(key){ + let arr = this[key]; + if(arr.indexOf(item) !== -1) { + this[key] = LangUtils.arrayWithout(arr, arr.indexOf(item)); + } + }.bind(this)); + this.notify("update"); + } + getItems(){ + return { + processing: this._processing, + pending: this._folders.concat(this._uploads), + processed: this._processed + }; + } + hasErrors(){ + let result = false; + this._processed.map(function(item){ + if(item.getStatus() === 'error'){ + result = true; + } + }); + return result; + } + static getInstance(){ + if(!UploaderStore.__INSTANCE){ + UploaderStore.__INSTANCE = new UploaderStore(); + } + return UploaderStore.__INSTANCE; + } + + handleFolderPickerResult(files, targetNode){ + var folders = {}; + for(var i=0;i + } + let folderButton, startButton; + let e = global.document.createElement('input'); + e.setAttribute('type', 'file'); + if('webkitdirectory' in e){ + folderButton = ; + } + e = null; + let configs = UploaderModel.Configs.getInstance(); + if(!configs.getOptionAsBool('DEFAULT_AUTO_START', 'upload_auto_send', true)){ + startButton = + } + return ( +
      +
      + + + {folderButton} + {startButton} + +
      + + + + {options} +
      + ); + + } + + }); + + var TransferFile = React.createClass({ + + propTypes: { + item: React.PropTypes.object.isRequired, + className:React.PropTypes.string + }, + + componentDidMount: function(){ + this.props.item.observe('progress', function(value){ + this.setState({progress: value}); + }.bind(this)); + this.props.item.observe('status', function(value){ + this.setState({status: value}); + }.bind(this)); + }, + + getInitialState: function(){ + return { + progress: this.props.item.getProgress(), + status: this.props.item.getStatus() + }; + }, + + abortTransfer: function(){ + UploaderModel.Store.getInstance().stopOrRemoveItem(this.props.item); + }, + + render: function(){ + let style; + var messageIds = { + "new" : 433, + "loading":434, + "loaded":435, + "error":436 + }; + let statusMessage = this.props.item.getStatus(); + let stopButton; + if(statusMessage === 'loading'){ + stopButton = ; + }else{ + stopButton = ; + } + if(statusMessage === 'error' && this.props.item.getErrorMessage()){ + statusMessage = this.props.item.getErrorMessage(); + } + if(global.pydio.MessageHash[messageIds[statusMessage]]){ + statusMessage = global.pydio.MessageHash[messageIds[statusMessage]]; + } + if(this.props.item.getRelativePath()){ + var relativeMessage = {this.props.item.getRelativePath()}; + } + if(this.state && this.state.progress){ + style = {width: this.state.progress + '%'}; + } + return ( +
      + {this.props.item.getFile().name} + {relativeMessage} + {statusMessage} + {stopButton} +
      +
      + ); + } + }); + + var TransferFolder = React.createClass({ + + propTypes: { + item: React.PropTypes.object.isRequired + }, + + render: function(){ + let statusMessage; + if(this.props.item.getStatus() === 'loaded'){ + statusMessage = 'Created'; + } + return ( +
      + {this.props.item.getPath()} {statusMessage} +
      + ); + } + }); + + var TransfersList = React.createClass({ + + componentDidMount: function(){ + let store = UploaderModel.Store.getInstance(); + this._storeObserver = function(){ + if(!this.isMounted()) return; + this.setState({items: store.getItems()}); + }.bind(this); + store.observe("update", this._storeObserver); + store.observe("auto_close", function(){ + pydio.UI.modal.dismiss(); + }); + this.setState({items: store.getItems()}); + }, + + componentWillUnmount: function(){ + if(this._storeObserver){ + UploaderModel.Store.getInstance().stopObserving("update", this._storeObserver); + UploaderModel.Store.getInstance().stopObserving("auto_close"); + } + }, + + renderSection: function(accumulator, items, title = "", className=""){ + if(title && items.length){ + accumulator.push(
      {title}
      ); + } + items.sort(function(a, b){ + let aType = a instanceof UploaderModel.FolderItem? 'folder' : 'file'; + let bType = b instanceof UploaderModel.FolderItem? 'folder' : 'file'; + if(aType === bType){ + return 0; + }else{ + return aType === 'folder' ? -1 : 1; + } + }); + items.forEach(function(f){ + if(f instanceof UploaderModel.FolderItem){ + accumulator.push( ); + }else{ + accumulator.push( ); + } + }); + }, + + render: function(){ + let items = []; + if(this.state && this.state.items){ + this.renderSection(items, this.state.items.processing, 'Processing', 'section-processing'); + this.renderSection(items, this.state.items.pending, 'Pending', 'section-pending'); + this.renderSection(items, this.state.items.processed, 'Processed', 'section-processed'); + } + return ( +
      + {items} +
      + ) + } + }); + + var UploadOptionsPane = React.createClass({ + + propTypes: { + onDismiss: React.PropTypes.func.isRequired + }, + + getInitialState: function(){ + + let configs = UploaderModel.Configs.getInstance(); + return { + configs: configs + }; + + }, + + updateField: function(fName, event){ + if(fName === 'autostart'){ + let toggleStart = this.state.configs.getOptionAsBool('DEFAULT_AUTO_START', 'upload_auto_send', true); + toggleStart = !toggleStart; + this.state.configs.updateOption('upload_auto_send', toggleStart, true); + }else if(fName === 'autoclose'){ + let toggleStart = this.state.configs.getOptionAsBool('DEFAULT_AUTO_CLOSE', 'upload_auto_close', true); + toggleStart = !toggleStart; + this.state.configs.updateOption('upload_auto_close', toggleStart, true); + }else if(fName === 'existing'){ + this.state.configs.updateOption('upload_existing', event.target.getSelectedValue()); + }else if(fName === 'show_processed'){ + let toggleShowProcessed = this.state.configs.getOptionAsBool('UPLOAD_SHOW_PROCESSED', 'upload_show_processed', false); + toggleShowProcessed = !toggleShowProcessed; + this.state.configs.updateOption('upload_show_processed', toggleShowProcessed, true); + } + this.setState({random: Math.random()}); + }, + + radioChange: function(e, newValue){ + this.state.configs.updateOption('upload_existing', newValue); + this.setState({random: Math.random()}); + }, + + render: function(){ + + let maxUpload = this.state.configs.getOption('UPLOAD_MAX_SIZE'); + let maxUploadMessage = MessageHash[282] + ': ' + PathUtils.roundFileSize(maxUpload, ''); + let toggleStart = this.state.configs.getOptionAsBool('DEFAULT_AUTO_START', 'upload_auto_send'); + let toggleClose = this.state.configs.getOptionAsBool('DEFAULT_AUTO_CLOSE', 'upload_auto_close'); + let toggleShowProcessed = this.state.configs.getOptionAsBool('UPLOAD_SHOW_PROCESSED', 'upload_show_processed', false); + let overwriteType = this.state.configs.getOption('DEFAULT_EXISTING', 'upload_existing'); + + return ( +
      + +
      {maxUploadMessage}
      +
      +
      +
      +
      +
      If a file with the same name exists
      + + + + + +
      +
      + ); + } + + }); + + var ns = global.UploaderView || {}; + ns.DropUploader = DropUploader; + global.UploaderView = ns; + +})(window); \ No newline at end of file diff --git a/core/src/plugins/uploader.html/manifest.xml b/core/src/plugins/uploader.html/manifest.xml index 6f7fd26c36..8f3817d7f9 100644 --- a/core/src/plugins/uploader.html/manifest.xml +++ b/core/src/plugins/uploader.html/manifest.xml @@ -1,6 +1,6 @@ - + @@ -9,7 +9,9 @@ - + + + @@ -42,165 +44,37 @@ - - - - ]]> - - - - ]]> - -