Forráskód Böngészése

Add PENS plugin - refs CT#288

Yannick Warnier 9 éve
szülő
commit
b22023e8fd

+ 674 - 0
plugin/pens/COPYING

@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is 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.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  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.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  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 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. Use with the GNU Affero General Public License.
+
+  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 Affero 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 special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 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 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 General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  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 GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.

+ 248 - 0
plugin/pens/chamilo_pens.php

@@ -0,0 +1,248 @@
+<?php
+/**
+ * This file is part of chamilo-pens.
+ * 
+ * chamilo-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * chamilo-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with chamilo-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+ /**
+ * ChamiloPens
+ * 
+ * This file provides the ChamiloPens class
+ * 
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+require_once __DIR__ . '/../../main/inc/global.inc.php';
+require_once __DIR__ . '/lib/pens.php';
+
+/**
+ * ChamiloPens
+ * 
+ * Model class that stores a PENS request made to a Chamilo server into the database
+ * 
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+class ChamiloPens extends Plugin {
+	/**
+	 * Database table to be used
+	 */
+	const TABLE_NAME = "plugin_pens";
+	
+	/**
+	 * Id of the object
+	 * @var int
+	 */
+	protected $_id = null;
+	
+	/**
+	 * PENS Version to be used. Currently, the only valid value is 1.0.0. Required.
+	 * @var string
+	 */
+	protected $_pens_version = null;
+	
+	/**
+	 * Package type being used. The only valid values are aicc-pkg, scorm-pif, ims-qti. Required
+	 * @var string
+	 */
+	protected $_package_type = null;
+	
+	/**
+	 * Package type version. Required
+	 * @var string
+	 */
+	protected $_package_type_version = null;
+	
+	/**
+	 * Package format. The only valid values are zip, url, jar, war and xml. Required
+	 * @var string
+	 */
+	protected $_package_format = null;
+	
+	/**
+	 * Package id. Requires a valid URI according to RFC 2396. Required
+	 * @var string
+	 */
+	protected $_package_id = null;
+	
+	/**
+	 * Name or ID for client submitting the content package to the target system. Required.
+	 * @var string
+	 */
+	protected $_client = null;
+	
+	/**
+	 * Unstructured character string that may be used to transfer vendor-specific data such as processing hints or deployment information. Optional.
+	 * @var string
+	 */
+	protected $_vendor_data = null;
+	
+	/**
+	 * Package name
+	 * @var string
+	 */
+	protected $_package_name = null;
+	
+	/**
+	 * Date of creation
+	 * @var DateTime
+	 */
+	protected $_created_at = null;
+	
+	/**
+	 * Update date
+	 * @var DateTime
+	 */
+	protected $_updated_at = null;
+	
+	/**
+	 * Constructor. Takes a PENSRequest as an argument
+	 * 
+	 * @param PENSRequest Request
+	 */
+	public function __construct($request) {
+		if($request instanceof PENSRequest) {
+			$this->_id = 0;
+			$this->_pens_version = $request->getPensVersion();
+			$this->_package_type = $request->getPackageType();
+			$this->_package_type_version = $request->getPackageTypeVersion();
+			$this->_package_format = $request->getPackageFormat();
+			$this->_package_id = $request->getPackageId();
+			$this->_client = $request->getClient();
+			$this->_vendor_data = $request->getVendorData();
+			$this->_package_name = $request->getFilename();
+		} else if(is_array($request)) {
+			$this->_id = $request['id'];
+			$this->_pens_version = $request['pens_version'];
+			$this->_package_type = $request['package_type'];
+			$this->_package_type_version = $request['package_type_version'];
+			$this->_package_format = $request['package_format'];
+			$this->_package_id = $request['package_id'];
+			$this->_client = $request['client'];
+			$this->_vendor_data = $request['vendor_data'];
+			$this->_package_name = $request['package_name'];
+			$this->_created_at = new DateTime($request['created_at'], new DateTimeZone('UTC'));
+			if(!empty($request['updated_at'])) {
+				$this->_updated_at = new DateTime($request['updated_at'], new DateTimeZone('UTC'));
+			}
+		}
+	}
+	
+	/**
+	 * Saves the object in the DB
+	 */
+	public function save() {
+		$clean_package_type_version = Database::escape_string($this->_package_type_version);
+		$clean_package_id = Database::escape_string($this->_package_id);
+		$clean_client = Database::escape_string($this->_client);
+		$clean_vendor_data = Database::escape_string($this->_vendor_data);
+		$created_at = api_get_utc_datetime();
+		$table = Database::get_main_table(ChamiloPens::TABLE_NAME);
+		$sql_query = "INSERT INTO $table (pens_version, package_type, package_type_version, package_format, package_id, client, vendor_data, package_name, created_at) VALUES (" .
+			"'". $this->_pens_version. "', " .
+			"'".$this->_package_type. "', " .
+			"'".$clean_package_type_version. "', " .
+			"'".$this->_package_format. "', " .
+			"'".$clean_package_id. "', " .
+			"'".$clean_client."', " .
+			"'".$clean_vendor_data."', " .
+			"'".$this->_package_name."', " .
+			"'".$created_at."') ON DUPLICATE KEY UPDATE " .
+			"pens_version = VALUES(pens_version), ".
+			"package_type = VALUES(package_type), ".
+			"package_type_version = VALUES(package_type_version), ".
+			"package_format = VALUES(package_format), ".
+			"client = VALUES(client), ".
+			"vendor_data = VALUES(vendor_data), ".
+			"package_name = VALUES(package_name), ".
+			"updated_at = '".$created_at."';";
+		Database::query($sql_query);
+	}
+	
+	/**
+	 * Returns a ChamiloPens object, based on package id
+	 * 
+	 * @param string Package id
+	 */
+	public static function findByPackageId($package_id) {
+		$table = Database::get_main_table(ChamiloPens::TABLE_NAME);
+		$sql_query = "SELECT * FROM $table WHERE package_id = '".$package_id."';";
+		$results = Database::query($sql_query);
+		$number = Database::num_rows($results);
+		if($number == 1) {
+			$obj = Database::fetch_assoc($results);
+			return new ChamiloPens($obj);
+		} else {
+			return null;
+		}
+	}
+	
+	/**
+	 * Returns an array of all the objects of the DB
+	 * 
+	 * @return array Array of ChamiloPens objects
+	 */
+	public static function findAll() {
+		$table = Database::get_main_table(ChamiloPens::TABLE_NAME);
+		$sql_query = "SELECT * FROM $table ORDER BY created_at;";
+		$results = Database::query($sql_query);
+		$return = array();
+		while($assoc = Database::fetch_assoc($results)) {
+			$return[] = new ChamiloPens($assoc);
+		}
+		return $return;
+	}
+	
+	public function getId() {
+		return $this->_id;
+	}
+	
+	public function getPensVersion() {
+		return $this->_pens_version;
+	}
+	
+	public function getPackageType() {
+		return $this->_package_type;
+	}
+	
+	public function getPackageTypeVersion() {
+		return $this->_package_type_version;
+	}
+	
+	public function getPackageFormat() {
+		return $this->_package_format;
+	}
+	
+	public function getPackageId() {
+		return $this->_package_id;
+	}
+	
+	public function getClient() {
+		return $this->_client;
+	}
+	
+	public function getVendorData() {
+		return $this->_vendor_data;
+	}
+	
+	public function getPackageName() {
+		return $this->_package_name;
+	}
+	
+	public function getCreatedAt() {
+		return $this->_created_at;
+	}
+}

+ 674 - 0
plugin/pens/lib/COPYING

@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is 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.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  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.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  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 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. Use with the GNU Affero General Public License.
+
+  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 Affero 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 special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 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 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 General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  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 GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.

+ 177 - 0
plugin/pens/lib/PENSPlugin.php

@@ -0,0 +1,177 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+/**
+ * Class PENSPlugin
+ * This class is used to add an advanced subscription allowing the admin to
+ * create user queues requesting a subscribe to a session
+ * @package chamilo.plugin.pens
+ */
+class PENSPlugin extends Plugin implements HookPluginInterface
+{
+    protected $strings;
+    private $errorMessages;
+    const TABLE_PENS = 'plugin_pens';
+
+
+    /**
+     * Constructor
+     */
+    public function __construct()
+    {
+        $parameters = array(
+        );
+
+        parent::__construct($this->get_version(), $this->get_author(), $parameters);
+
+        $this->errorMessages = array();
+    }
+
+    /**
+     * Instance the plugin
+     * @staticvar null $result
+     * @return AdvancedSubscriptionPlugin
+     */
+    public static function create()
+    {
+        static $result = null;
+
+        return $result ? $result : $result = new self();
+    }
+
+    /**
+     * Install the plugin
+     * @return void
+     */
+    public function install()
+    {
+        $this->installDatabase();
+        $this->installHook();
+    }
+
+    /**
+     * Uninstall the plugin
+     * @return void
+     */
+    public function uninstall()
+    {
+        $setting = api_get_setting('plugin_pens');
+        if (!empty($setting)) {
+            $this->uninstallHook();
+            // Note: Keeping area field data is intended so it will not be removed
+            $this->uninstallDatabase();
+        }
+    }
+
+    /**
+     * Create the database tables for the plugin
+     * @return void
+     */
+    private function installDatabase()
+    {
+        $pensTable = Database::get_main_table(PENSPlugin::TABLE_PENS);
+
+        $sql = "CREATE TABLE $pensTable (
+	        id int unsigned NOT NULL auto_increment,
+	        pens_version varchar(255) NOT NULL,
+	        package_type varchar(255) NOT NULL,
+	        package_type_version varchar(255) NOT NULL,
+        	package_format varchar(255) NOT NULL,
+	        package_id varchar (255) NOT NULL,
+	        client varchar(255) NOT NULL,
+	        vendor_data text,
+	        package_name varchar(255) NOT NULL,
+	        created_at datetime NOT NULL,
+	        updated_at datetime NULL,
+	        PRIMARY KEY (id),
+	        UNIQUE KEY package_id (package_id)
+	        ";
+        Database::query($sql);
+    }
+
+    /**
+     * Drop the database tables for the plugin
+     * @return void
+     */
+    private function uninstallDatabase()
+    {
+        /* Drop plugin tables */
+        $pensTable = Database::get_main_table(PENSPlugin::TABLE_PENS);
+
+        $sql = "DROP TABLE IF EXISTS $pensTable; ";
+        Database::query($sql);
+
+        /* Delete settings */
+        $settingsTable = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
+        Database::query("DELETE FROM $settingsTable WHERE subkey = 'plugin_pens'");
+    }
+
+    /**
+     * Get the error messages list
+     * @return array The message list
+     */
+    public function getErrorMessages()
+    {
+        return $this->errorMessages;
+    }
+
+    /**
+     * This method will call the Hook management insertHook to add Hook observer from this plugin
+     * @return int
+     */
+    public function installHook()
+    {
+    }
+
+    /**
+     * This method will call the Hook management deleteHook to disable Hook observer from this plugin
+     * @return int
+     */
+    public function uninstallHook()
+    {
+    }
+
+    /**
+     * Copied and fixed from plugin.class.php
+     * Returns the "system" name of the plugin in lowercase letters
+     * @return string
+     */
+    public function get_name()
+    {
+        return 'PENS';
+    }
+
+    /**
+     * Get author(s)
+     * @return string
+     */
+    public function get_author()
+    {
+        return 'Guillaume Viguier-Just, Yannick Warnier';
+    }
+
+    /**
+     * Returns the plugin version
+     * @return string
+     */
+    public function get_version()
+    {
+        return '1.1';
+    }
+    /**
+     * Get generic plugin info
+     * @return array
+     */
+    public function get_info()
+    {
+        $result = array();
+        $result['title'] = $this->get_name();
+        $result['comment'] = 'Provides support for the PENS course exchange standard. Read the readme.txt file in the plugin/pens/ folder for a complete installation.';
+        $result['version'] = $this->get_version();
+        $result['author'] = $this->get_author();
+        $result['plugin_class'] = get_class($this);
+        $result['is_course_plugin'] = false;
+        $result['is_mail_plugin'] = false;
+        return $result;
+    }
+}

+ 79 - 0
plugin/pens/lib/README.textile

@@ -0,0 +1,79 @@
+h1. Introduction
+
+This library is an implementation of the PENS specification (see http://pens.lmstesting.com/pages/whatispens.htm) in PHP. It provides a PENSServer class and, in the future, will provide a PENSClient class.
+
+h1. Requirements
+
+You need at least PHP 5.2 with the curl extension installed to use this library.
+
+h1. State
+
+Currently, some features are missing (such as the possibility to send alerts or the possibility to send receipts to a mailto url), and the library is not considered as stable.
+
+h1. How do I create a server ?
+
+Assuming the URL of your server is http://www.myserver.com, create a file called pens.php at the root so that it is accessible through http://www.myserver.com/pens.php.
+
+In this file, write the following code:
+
+pre.. // Assuming the library is accessible through pens/pens.php
+
+require_once("pens/pens.php");
+
+// Create a concrete class that extends PENSPackageHandler and 
+// write your implementation of the processPackage method in it
+class MyPackageHandler extends PENSPackageHandler {
+	// See the documentation of the processPackage method for more information
+	public function processPackage($request, $path_to_package) {
+		// Write your implementation of the package handling here
+	}
+}
+
+// Create an instance of your handler and configure it
+// See the file pens_package_handler.php for more information
+$handler = new MyPackageHandler();
+$handler->setSupportedPackageTypes(array("scorm-pif"));
+$handler->setSupportedPackageFormats(array("zip"));
+
+// Initialize the server
+$server = PENSServer::singleton();
+$server->setPackageHandler($handler);
+
+// The server should only receive collect commands, so call the receiveCollect method
+$server->receiveCollect();
+
+h1. How do I create a client ?
+
+The role of the PENSClient is simply to receive receipts and alerts from the server and process them. Here is how to create a client.
+
+pre.. require_once("pens/pens.php");
+
+// Create a request handler if you need one. This allows you to do some processing on the requests (store them in a DB, send them to an email address etc...)
+// You do not need to create a request handler if you do not want to process the requests
+class MyRequestHandler extends PENSRequestHandler {
+	public function processRequest($request, $response) {
+		// Do the processing of the alert or the receipt here
+	}
+}
+
+// Instantiate the handler
+$handler = new MyRequestHandler();
+
+// Create the client and set the request handler
+$client = PENSClient::singleton();
+$client->setRequestHandler($handler);
+
+// Parse and process the request
+$client->receiveRequest();
+
+h1. Licence
+
+The php-pens library is published under the GNU GPL 3 licence (see COPYING)
+
+h1. Credits
+
+The author of the library is Guillaume Viguier-Just <guillaume@viguierjust.com> and this library has been realized mainly for use in the Chamilo project (http://www.chamilo.org).
+
+This library was realized as part of my work in Beeznest Latino (http://www.beeznest.com), for the Chamilo project.
+
+Also note the use of a library called rfc2396regexes written by Rudy Desjardins and distributed as GPL v2

+ 7 - 0
plugin/pens/lib/pens.php

@@ -0,0 +1,7 @@
+<?php
+
+require_once __DIR__ . '/pens/pens_request_factory.php';
+require_once __DIR__ . '/pens/pens_response.php';
+require_once __DIR__ . '/pens/pens_package_handler.php';
+require_once __DIR__ . '/pens/pens_server.php';
+require_once __DIR__ . '/pens/pens_client.php';

+ 113 - 0
plugin/pens/lib/pens/lib/rfc2396regexes.php

@@ -0,0 +1,113 @@
+<?php
+
+
+/// Copyright 2006 Rudy Desjardins
+
+/// This program is free software; you can redistribute it and/or modify
+/// it under the terms of the GNU General Public License as published by
+/// the Free Software Foundation; either version 2 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 General Public License for more details.
+///
+/// You should have received a copy of the GNU General Public License
+/// along with this program; if not, write to the Free Software
+/// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+///
+/// This file defines constants for use as regular expressions for validating
+/// RFC 2396 compliant URI strings
+///
+/// See Appendix A of RFC 2396 for the origin of these expressions
+///
+
+define('ALPHA_2396', '[a-zA-Z]');
+define('DIGIT_2396', '[0-9]');
+
+define('LOWALPHA_2396', '[a-z]');
+define('UPALPHA_2396', '[A-Z]');
+
+define('ALPHANUM_2396', '(?:'.ALPHA_2396.'|'.DIGIT_2396.')');
+define('ALPHANUM_OPT_2396', '[a-zA-Z0-9]');
+
+define('HEX_2396', '(?:'.DIGIT_2396.'|[a-fA-F])');
+define('HEX_OPT_2396', '[0-9a-fA-F]');
+
+define('ESCAPED_2396', '(?:%'.HEX_OPT_2396.HEX_OPT_2396.')');
+
+define('MARK_2396', '[\\x2D\\x2E\\x5F\\x21\\x7E\\x2A\\x27\\x28\\x29]');
+define('RESERVED_2396', '[\\x3B\\x3F\\x2F3A\\x40\\x26\\x2B\\x3D\\x24\\x2C]');
+define('UNRESERVED_2396', '(?:'.ALPHANUM_OPT_2396.'|'.MARK_2396.')');
+
+define('URIC_2396', '(?:'.RESERVED_2396.'|'.UNRESERVED_2396.'|'.ESCAPED_2396.')');
+
+define('QUERY_2396', '(?:'.URIC_2396.'*)');
+define('FRAGMENT_2396', QUERY_2396);
+
+define('PCHAR_2396', '(?:'.UNRESERVED_2396.'|'.ESCAPED_2396.'|[\\x3A\\x40\\x26\\x3D\\x2B\\x24\\x2C])');
+
+define('PARAM_2396', '(?:'.PCHAR_2396.'*)');
+
+define('SEGMENT_2396', '(?:'.PCHAR_2396.'*(?:;'.PARAM_2396.')*)');
+
+define('PATH_SEGMENTS_2396', '(?:'.SEGMENT_2396.'(?:\\/'.SEGMENT_2396.')*)');
+
+define('ABS_PATH_2396', '(?:\\/'.PATH_SEGMENTS_2396.')');
+
+define('URIC_NO_SLASH_2396', '(?:'.UNRESERVED_2396.'|'.ESCAPED_2396.'|'.'[\\x3A\\x3B\\x3F\\x40\\x26\\x3D\\x24\\x2B\\x2C])');
+
+define('OPAQUE_PART_2396', '(?:'.URIC_NO_SLASH_2396.URIC_2396.'*)');
+
+define('PATH_2396', '(?:'.ABS_PATH_2396.'|'.OPAQUE_PART_2396.')');
+
+/// This is probably wrong... should end in '+', not '*'...
+///
+define('PORT_2396', '(?:'.DIGIT_2396.'*)');
+
+/// Same here... pieces should be '[012]*'.DIGIT_2396.'{1,2}', although that's not perfect either...
+///
+define('IPV4ADDRESS_2396', '(?:'.DIGIT_2396.'+\\.'.DIGIT_2396.'+\\.'.DIGIT_2396.'+\\.'.DIGIT_2396.'+)');
+
+define('TOPLABEL_2396', '(?:'.ALPHA_2396.'|'.ALPHA_2396.'(?:'.ALPHANUM_OPT_2396.'|\\x2D)*'.ALPHANUM_OPT_2396.')');
+define('DOMAINLABEL_2396', '(?:'.ALPHANUM_OPT_2396.'|'.ALPHANUM_OPT_2396.'(?:'.ALPHANUM_OPT_2396.'|\\x2D)*'.ALPHANUM_OPT_2396.')');
+
+define('HOSTNAME_2396', '(?:(?:'.DOMAINLABEL_2396.'\\.)*'.TOPLABEL_2396.'\\.?)');
+
+define('HOST_2396', '(?:'.HOSTNAME_2396.'|'.IPV4ADDRESS_2396.')');
+
+define('HOSTPORT_2396', '(?:'.HOST_2396.PORT_2396.'?)');
+
+define('USERINFO_2396', '(?:(?:'.UNRESERVED_2396.'|'.ESCAPED_2396.'|[\\x26\\x3A\\x3B\\x3D\\x2B\\x24\\x2C])*)');
+
+/// This should probably be '(?:'.USERINFO_2396.'\\x40)?'.HOSTPORT_2396'...
+///
+define('SERVER_2396', '(?:(?:'.USERINFO_2396.'\\x40)?'.HOSTPORT_2396.')');
+
+define('REGNAME_2396', '(?:(?:'.UNRESERVED_2396.'|'.ESCAPED_2396.'|[\\x24\\x2C\\x3A\\x3B\\x40\\x26\\x3D\\x2B])+)');
+
+define('AUTHORITY_2396', '(?:'.SERVER_2396.'|'.REGNAME_2396.')');
+
+define('SCHEME_2396', '(?:'.ALPHA_2396.'(?:'.ALPHA_2396.'|'.DIGIT_2396.'|[\\x2B\\x2D\\x2E])*)');
+
+define('REL_SEGMENT_2396', '(?:(?:'.UNRESERVED_2396.'|'.ESCAPED_2396.'|[\\x40\\x26\\x3B\\x3D\\x2B\\x24\\x2C])+)');
+
+define('REL_PATH_2396', '(?:'.REL_SEGMENT_2396.ABS_PATH_2396.'?)');
+define('NET_PATH_2396', '(?:\\/\\/'.AUTHORITY_2396.ABS_PATH_2396.'?)');
+
+define('HIER_PART_2396', '(?:(?:'.NET_PATH_2396.'|'.ABS_PATH_2396.')(?:\\?'.QUERY_2396.')?)');
+
+define('RELATIVEURI_2396', '(?:('.NET_PATH_2396.'|'.ABS_PATH_2396.'|'.REL_PATH_2396.')(?:\\?'.QUERY_2396.')?)');
+define('ABSOLUTEURI_2396', '(?:'.SCHEME_2396.'\\x3A(?:'.HIER_PART_2396.'|'.OPAQUE_PART_2396.'))');
+
+/// This was the only expression changed to use parens instead of brackets around the URI_2396 options, because
+/// otherwise a blank string would match the whole expression incorrectly...
+///
+define('URI_REFERENCE_2396', '(?:(?:'.ABSOLUTEURI_2396.'|'.RELATIVEURI_2396.')(?:\\x23'.FRAGMENT_2396.')?)');
+
+define('URI_2396', URI_REFERENCE_2396);
+
+?>

+ 113 - 0
plugin/pens/lib/pens/pens_client.php

@@ -0,0 +1,113 @@
+<?php
+/**
+ * This file is part of php-pens.
+ * 
+ * php-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * php-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with php-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+ /**
+ * PENSClient
+ * 
+ * Provides the PENSClient class
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+ 
+require_once(dirname(__FILE__)."/pens_controller.php");
+require_once(dirname(__FILE__)."/pens_request_handler.php");
+
+/**
+ * PENSClient
+ * 
+ * Class that implements the PENS Client
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+class PENSClient extends PENSController {
+	/**
+	 * Instance of the PENSClient
+	 * @var PENSServer
+	 */
+	private static $_instance;
+	
+	/**
+	 * Request handler
+	 * @var PENSRequestHandler
+	 */
+	protected $_request_handler = null;
+	
+	/**
+	 * Private constructor
+	 */
+	private function __construct() {
+	}
+	
+	/**
+	 * Singleton method
+	 */
+	public static function singleton() {
+		if(!isset(self::$_instance)) {
+			$c = __CLASS__;
+			self::$_instance = new $c;
+		}
+		return self::$_instance;
+	}
+	
+	/**
+	 * Prevent users to clone the instance
+	 */
+	public function __clone() {
+		trigger_error('Clone is not allowed', E_USER_ERROR);
+	}
+	
+	public function getRequestHandler() {
+		return $this->_request_handler;
+	}
+	
+	/**
+	 * Sets the request handler. Does nothing if the argument is not an instance of PENSRequestHandler
+	 * 
+	 * @param PENSRequestHandler Request handler
+	 */
+	public function setRequestHandler($request_handler) {
+		if($request_handler instanceof PENSRequestHandler) {
+			$this->_request_handler = $request_handler;
+		}
+	}
+	
+	/**
+	 * Receives a request, calls the handler and displays the response
+	 */
+	public function receiveRequest() {
+		$request = null;
+		try {
+			$request = $this->parseRequest();
+			$command = $request->getCommand();
+			if($command == "alert" || $command == "receipt") {
+				if(!is_null($this->_request_handler)) {
+					// Call the handler
+					$this->_request_handler->processRequest($request, new PENSResponse($_REQUEST));
+				}
+				$this->sendResponse(new PENSResponse(0, $command." received and understood"));
+			}
+		} catch(PENSException $e) {
+			// If we could not parse the request, send the error
+			$this->sendResponse(new PENSResponse($e));
+		}
+	}
+}

+ 69 - 0
plugin/pens/lib/pens/pens_config.php

@@ -0,0 +1,69 @@
+<?php
+/**
+ * This file is part of php-pens.
+ * 
+ * php-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * php-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with php-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+ /**
+ * PENSConfig
+ * 
+ * Provides the PENSConfig class
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+ 
+ /**
+ * PENSConfig
+ * 
+ * This class provides different static variables to configure php-pens.
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+class PENSConfig {
+
+	/**
+	 * PENS version
+	 * @var string
+	 */
+	public static $version = "1.0.0";
+	
+	/**
+	 * Allowed commands
+	 * @var array
+	 */
+	public static $allowed_commands = array("collect", "receipt", "alert");
+	
+	/**
+	 * Allowed package types
+	 * @var array
+	 */
+	public static $allowed_package_types = array("aicc-pkg", "scorm-pif", "ims-qti");
+	
+	/**
+	 * Allowed package formats
+	 * @var array
+	 */
+	public static $allowed_package_formats = array("zip", "url", "jar", "war", "xml");
+	
+	/**
+	 * End of line as specified by the PENS specification
+	 * @var string
+	 */
+	public static $eol = "\r\n";
+}

+ 62 - 0
plugin/pens/lib/pens/pens_controller.php

@@ -0,0 +1,62 @@
+<?php
+/**
+ * This file is part of php-pens.
+ * 
+ * php-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * php-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with php-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+ /**
+ * PENSController
+ * 
+ * Provides the PENSController class
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+require_once(dirname(__FILE__)."/pens_request_factory.php");
+
+/**
+ * PENSController
+ * 
+ * Base class for the PENSServer and PENSClient
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+abstract class PENSController {
+	
+	/**
+	 * Parses a request based on the values present in $_REQUEST
+	 * 
+	 * @return PENSRequest Request generated
+	 * @throws PENSException if the request could not be parsed
+	 */
+	protected function parseRequest() {
+		$request = PENSRequestFactory::createPENSRequest($_REQUEST);
+		return $request;
+	}
+	
+	/**
+	 * Sends the HTTP Response to the client
+	 * 
+	 * @param PENSResponse Response to be sent
+	 */
+	protected function sendResponse($response) {
+		$response->send();
+	}
+	
+}

+ 98 - 0
plugin/pens/lib/pens/pens_exception.php

@@ -0,0 +1,98 @@
+<?php
+/**
+ * This file is part of php-pens.
+ * 
+ * php-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * php-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with php-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * PENSException
+ * 
+ * Provides the PENSException class
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+/**
+ * PENSException
+ * 
+ * Class extending the PHP Exception class
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+class PENSException extends Exception {
+
+	/**
+	 * Array that provides an association between exception codes and messages
+	 * @var array
+	 */
+	protected static $_code_to_messages = array(
+		1101 => "Unable to parse PENS command",
+		1201 => "Attempt to pass an invalid argument",
+		1301 => "Unable to retrieve package",
+		1302 => "Unable to retrieve package via HTTPS",
+		1304 => "Unable to retrieve package via FTP",
+		1306 => "Unable to retrieve package via FTPS",
+		1310 => "Unable to retrieve package at specified URL due to error in URL or lack of response from URL",
+		1312 => "Unable to retrieve package at specified URL due to error with access credential for package URL",
+		1320 => "Expiration date is non-null and in an improper format",
+		1322 => "Current time indicates expiry date has passed",
+		1420 => "PENS version not supported",
+		1421 => "Command not supported",
+		1430 => "Package type not supported",
+		// The following error code is not specified in the PENS specification and was added in this implementation
+		1431 => "Package format not supported",
+		1432 => "Internal package error",
+		1440 => "Insufficient host space/storage available",
+		1500 => "Unable to communicate with provided acknowledgement URL",
+		1510 => "Unsupported acknowledgement protocol",
+		1520 => "Unsupported alert protocol",
+		2001 => "PENS version invalid or not specified",
+		2002 => "PENS command invalid or not specified",
+		2003 => "package-type invalid or not specified",
+		2004 => "package-type-version invalid or not specified",
+		2005 => "package-format invalid or not specified",
+		2007 => "package-id invalid or not specified",
+		2008 => "package-url invalid or not specified",
+		2009 => "package-url-expiry date invalid or not specified",
+		2010 => "client submitting package invalid or not specified",
+		2011 => "receipt url invalid or not specified"
+	);
+	
+	/**
+	 * Redefines the constructor so that code is the first argument
+	 * 
+	 * @param int Exception code
+	 * @param string Message to display
+	 * @return PENSException Exception created
+	 */
+	public function __construct($code, $message = null) {
+		parent::__construct($message, $code);
+		$this->setMessageBasedOnCode();
+	}
+	
+	/**
+	 * Sets the message based on the code
+	 */
+	protected function setMessageBasedOnCode() {
+		if(empty($this->message) && !empty(self::$_code_to_messages[$this->code])) {
+			$this->message = self::$_code_to_messages[$this->code];
+		}
+	}
+	
+}

+ 5 - 0
plugin/pens/lib/pens/pens_message.php

@@ -0,0 +1,5 @@
+<?php
+
+abstract class PENSMessage {
+
+}

+ 114 - 0
plugin/pens/lib/pens/pens_package_handler.php

@@ -0,0 +1,114 @@
+<?php
+/**
+ * This file is part of php-pens.
+ * 
+ * php-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * php-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with php-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+ /**
+ * PENSPackageHandler
+ * 
+ * Provides the PENSPackageHandler abstract class
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+require_once(dirname(__FILE__)."/pens_config.php");
+
+/**
+ * PENSPackageHandler
+ * 
+ * This class is an abstract class used to handle the processing of the package on the server side. Create a class that inherits from
+ * this class and implements the processPackage method, then set the package handler of the PENSServer class using the instance newly created
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+abstract class PENSPackageHandler {
+	/**
+	 * Array of supported package types for this handler. Set by default to PENSConfig::$allowed_package_types
+	 * @var array
+	 */
+	protected $_supported_package_types = null;
+	
+	/**
+	 * Array of supported package formats for this handler. Set by default to PENSConfig::$allowed_package_formats
+	 * @var array
+	 */
+	protected $_supported_package_formats = null;
+	
+	/**
+	 * Constructor. Sets the default values of supported_package_types and supported_package_formats
+	 */
+	public function __construct() {
+		$this->_supported_package_types = PENSConfig::$allowed_package_types;
+		$this->_supported_package_formats = PENSConfig::$allowed_package_formats;
+	}
+	
+	public function getSupportedPackageTypes() {
+		return $this->_supported_package_types;
+	}
+	
+	/**
+	 * Sets the supported package types. All package types in the array provided as an argument
+	 * not present in PENSConfig::$allowed_package_types will be ignored. The supported_package_types
+	 * will be set to null if the argument given is not an array
+	 * 
+	 * @param array Supported package types
+	 */
+	public function setSupportedPackageTypes($supported_package_types) {
+		$this->_supported_package_types = null;
+		if(is_array($supported_package_types)) {
+			foreach($supported_package_types as $package_type) {
+				if(in_array($package_type, PENSConfig::$allowed_package_types)) {
+					$this->_supported_package_types[] = $package_type;
+				}
+			}
+		}
+	}
+	
+	public function getSupportedPackageFormats() {
+		return $this->_supported_package_formats;
+	}
+	
+	/**
+	 * Sets the supported package formats. All package types in the array provided as an argument
+	 * not present in PENSConfig::$allowed_package_formats will be ignored. The supported_package_formats
+	 * will be set to null if the argument given is not an array.
+	 * 
+	 * @param array Supported package formats
+	 */
+	public function setSupportedPackageFormats($supported_package_formats) {
+		$this->_supported_package_formats = null;
+		if(is_array($supported_package_formats)) {
+			foreach($supported_package_formats as $package_format) {
+				if(in_array($package_format, PENSConfig::$allowed_package_formats)) {
+					$this->_supported_package_formats[] = $package_format;
+				}
+			}
+		}
+	}
+	
+	/**
+	 * Processes the package. Must be implemented by subclasses
+	 * 
+	 * @param PENSRequestCollect Collect request sent by the client
+	 * @param string Path to the collected package on the hard drive
+	 */
+	abstract public function processPackage($request, $path_to_package);
+	
+}

+ 495 - 0
plugin/pens/lib/pens/pens_request.php

@@ -0,0 +1,495 @@
+<?php
+/**
+ * This file is part of php-pens.
+ * 
+ * php-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * php-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with php-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * PENSRequest
+ * 
+ * Provides the PENSRequest abstract class
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+require_once(dirname(__FILE__)."/pens_config.php");
+require_once(dirname(__FILE__)."/pens_message.php");
+require_once(dirname(__FILE__)."/pens_exception.php");
+require_once(dirname(__FILE__)."/lib/rfc2396regexes.php");
+
+/**
+ * PENSRequest
+ * 
+ * Base class for all the request types (collect, receipt, alert)
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+abstract class PENSRequest extends PENSMessage {
+
+	/**
+	 * PENS Version to be used. Currently, the only valid value is 1.0.0. Required.
+	 * @var string
+	 */
+	protected $_pens_version = null;
+	
+	/**
+	 * Command being used. The only valid values are collect, alert and receipt.Required
+	 * @var string
+	 */
+	protected $_command = null;
+	
+	/**
+	 * Package type being used. The only valid values are aicc-pkg, scorm-pif, ims-qti. Required
+	 * @var string
+	 */
+	protected $_package_type = null;
+	
+	/**
+	 * Package type version. Required
+	 * @var string
+	 */
+	protected $_package_type_version = null;
+	
+	/**
+	 * Package format. The only valid values are zip, url, jar, war and xml. Required
+	 * @var string
+	 */
+	protected $_package_format = null;
+	
+	/**
+	 * Package id. Requires a valid URI according to RFC 2396. Required
+	 * @var string
+	 */
+	protected $_package_id = null;
+	
+	/**
+	 * Package url. Requires a valid, fully qualified URL including transport protocol and filename extension. Required
+	 * @var string
+	 */
+	protected $_package_url = null;
+	
+	/**
+	 * User id required for system to retrieve package from URL. Optional.
+	 * @var string
+	 */
+	protected $_package_url_user_id = null;
+	
+	/**
+	 * Account required for system to retrieve package from URL. Optional.
+	 * @var string
+	 */
+	protected $_package_url_account = null;
+	
+	/**
+	 * Password required for system to retrieve package from URL. Optional.
+	 * @var string
+	 */
+	protected $_package_url_password = null;
+	
+	/**
+	 * Expiry date for package URL. ISO 8601 format expressed as UTC. Will be transformed into a PHP DateTime object during construction. Required
+	 * @var DateTime
+	 */
+	protected $_package_url_expiry = null;
+	
+	/**
+	 * Name or ID for client submitting the content package to the target system. Required.
+	 * @var string
+	 */
+	protected $_client = null;
+	
+	/**
+	 * User-id or sign-on for target system. Optional
+	 * @var string
+	 */
+	protected $_system_user_id = null;
+	
+	/**
+	 * Either a URL-encoded password token or the null string. If the 
+	 * target system requires a password and the null string value is 
+	 * passed, then the target system is responsible for prompting for a 
+	 * password for target system. Optional
+	 * @var string
+	 */
+	protected $_system_password = null;
+	
+	/**
+	 * URL to send acknowledgement receipt after collecting a package. Any URL, including mailto (as per RFC 2368 and RFC 2822). Required.
+	 * @var string
+	 */
+	protected $_receipt = null;
+	
+	/**
+	 * URL to send alerts to while processing the package. Any URL, including mailto (as per RFC 2368 and RFC 2822). Optional.
+	 * @var string
+	 */
+	protected $_alerts = null;
+	
+	/**
+	 * Unstructured character string that may be used to transfer vendor-specific data such as processing hints or deployment information. Optional.
+	 * @var string
+	 */
+	protected $_vendor_data = null;
+	
+	/**
+	 * Constructor
+	 * 
+	 * Constructs a PENSRequest based class using the arguments given
+	 * 
+	 * @param array Arguments
+	 */
+	public function __construct($arguments) {
+		$this->setPensVersion($arguments["pens-version"]);
+		$this->setPackageType($arguments["package-type"]);
+		$this->setPackageTypeVersion($arguments["package-type-version"]);
+		$this->setPackageFormat($arguments["package-format"]);
+		$this->setPackageId($arguments["package-id"]);
+		$this->setPackageUrl($arguments["package-url"]);
+		$this->setPackageUrlUserId($arguments["package-url-user-id"]);
+		$this->setPackageUrlAccount($arguments["package-url-account"]);
+		$this->setPackageUrlPassword($arguments["package-url-password"]);
+		$this->setPackageUrlExpiry($arguments["package-url-expiry"]);
+		$this->setClient($arguments["client"]);
+		$this->setSystemUserId($arguments["system-user-id"]);
+		$this->setSystemPassword($arguments["system-password"]);
+		$this->setReceipt($arguments["receipt"]);
+		$this->setAlerts($arguments["alerts"]);
+		$this->setVendorData($arguments["vendor-data"]);
+	}
+	
+	public function getPensVersion() {
+		return $this->_pens_version;
+	}
+	
+	/**
+	 * Sets the PENS version
+	 * 
+	 * @param string PENS version
+	 * 
+	 * @throws PENSException with code 2001 if invalid
+	 */
+	public function setPensVersion($pens_version) {
+		if($pens_version == PENSConfig::$version) {
+			$this->_pens_version = $pens_version;
+		} else {
+			throw new PENSException(2001);
+		}
+	}
+	
+	public function getCommand() {
+		return $this->_command;
+	}
+	
+	/**
+	 * Sets the command
+	 * 
+	 * @param string command
+	 * 
+	 * @throws PENSException with code 2002 if invalid
+	 */
+	protected function setCommand($command) {
+		if(in_array($command, PENSConfig::$allowed_commands)) {
+			$this->_command = $command;
+		} else {
+			throw new PENSException(2002);
+		}
+	}
+	
+	public function getPackageType() {
+		return $this->_package_type;
+	}
+	
+	/**
+	 * Sets the package type
+	 * 
+	 * @param string package type
+	 * 
+	 * @throws PENSException with code 2003 if invalid
+	 */
+	public function setPackageType($package_type) {
+		if(in_array($package_type, PENSConfig::$allowed_package_types)) {
+			$this->_package_type = $package_type;
+		} else {
+			throw new PENSException(2003);
+		}
+	}
+	
+	public function getPackageTypeVersion() {
+		return $this->_package_type_version;
+	}
+	
+	/**
+	 * Sets the package type version
+	 * 
+	 * @param string package type version
+	 * 
+	 * @throws PENSException with code 2004 if invalid
+	 */
+	public function setPackageTypeVersion($package_type_version) {
+		if(empty($package_type_version)) {
+			throw new PENSException(2004);
+		} else {
+			$this->_package_type_version = $package_type_version;
+		}
+	}
+	
+	public function getPackageFormat() {
+		return $this->_package_format;
+	}
+	
+	/**
+	 * Sets the package format
+	 * 
+	 * @param string package format
+	 * 
+	 * @throws PENSException with code 2005 if invalid
+	 */
+	public function setPackageFormat($package_format) {
+		if(in_array($package_format, PENSConfig::$allowed_package_formats)) {
+			$this->_package_format = $package_format;
+		} else {
+			throw new PENSException(2005);
+		}
+	}
+	
+	public function getPackageId() {
+		return $this->_package_id;
+	}
+	
+	/**
+	 * Sets the package Id
+	 * 
+	 * @param string package Id
+	 * 
+	 * @throws PENSException with code 2007 if invalid
+	 */
+	public function setPackageId($package_id) {
+		if (preg_match('/'.ABSOLUTEURI_2396.'/', $package_id)) {
+			$this->_package_id = $package_id;
+		} else {
+			throw new PENSException(2007);
+		}
+	}
+	
+	public function getPackageUrl() {
+		return $this->_package_url;
+	}
+	
+	/**
+	 * Sets the package url
+	 * 
+	 * @param string package url
+	 * 
+	 * @throws PENSException with code 2008 if invalid
+	 */
+	public function setPackageUrl($package_url) {
+		if (preg_match('/'.ABSOLUTEURI_2396.'/', $package_url) && substr($package_url, -4) == ".".$this->_package_format) {
+			$this->_package_url = $package_url;
+		} else {
+			throw new PENSException(2008);
+		}
+	}
+	
+	public function getFilename() {
+		return substr(strrchr($this->_package_url, "/"), 1);
+	}
+	
+	public function getPackageUrlUserId() {
+		return $this->_package_url_user_id;
+	}
+	
+	public function setPackageUrlUserId($package_url_user_id) {
+		if(!empty($package_url_user_id)) {
+			$this->_package_url_user_id = $package_url_user_id;
+		}
+	}
+	
+	public function getPackageUrlAccount() {
+		return $this->_package_url_account;
+	}
+	
+	public function setPackageUrlAccount($package_url_account) {
+		if(!empty($package_url_account)) {
+			$this->_package_url_account = $package_url_account;
+		}
+	}
+	
+	public function getPackageUrlPassword() {
+		return $this->_package_url_password;
+	}
+	
+	public function setPackageUrlPassword($package_url_password) {
+		if(!empty($package_url_password)) {
+			$this->_package_url_password = $package_url_password;
+		}
+	}
+	
+	public function getPackageUrlExpiry() {
+		return $this->_package_url_expiry;
+	}
+	
+	/**
+	 * Sets the package url expiry and transforms it into a php DateTime object
+	 * 
+	 * @param string package url expiry
+	 * 
+	 * @throws PENSException with code 2009 if invalid
+	 * @todo Perform a better validation of the date
+	 */
+	public function setPackageUrlExpiry($package_url_expiry) {
+		if(empty($package_url_expiry)) {
+			throw new PENSException(2009);
+		} else {
+			try {
+				$expiry = new DateTime($package_url_expiry, new DateTimeZone('UTC'));
+				$this->_package_url_expiry = $expiry;
+			} catch(Exception $e) {
+				throw new PENSException(2009);
+			}
+		}
+	}
+	
+	public function getClient() {
+		return $this->_client;
+	}
+	
+	/**
+	 * Sets the client
+	 * 
+	 * @param string client
+	 * 
+	 * @throws PENSException with code 2010 if invalid
+	 */
+	public function setClient($client) {
+		if(!empty($client)) {
+			$this->_client = $client;
+		} else {
+			throw new PENSException(2010);
+		}
+	}
+	
+	public function getSystemUserId() {
+		return $this->_system_user_id;
+	}
+	
+	public function setSystemUserId($system_user_id) {
+		if(!empty($system_user_id)) {
+			$this->_system_user_id = $system_user_id;
+		}
+	}
+	
+	public function getSystemPassword() {
+		return $this->_system_password;
+	}
+	
+	public function setSystemPassword($system_password) {
+		if(!empty($system_password)) {
+			$this->_system_password = $system_password;
+		}
+	}
+	
+	public function getReceipt() {
+		return $this->_receipt;
+	}
+	
+	/**
+	 * Sets the receipt url
+	 * 
+	 * @param string receipt url
+	 * 
+	 * @throws PENSException with code 2011 if invalid
+	 */
+	public function setReceipt($receipt) {
+		if($this instanceof PENSRequestCollect) {
+			if (preg_match('/'.ABSOLUTEURI_2396.'/', $receipt)) {
+				$this->_receipt = $receipt;
+			} else {
+				throw new PENSException(2011);
+			}
+		}
+	}
+	
+	public function getAlerts() {
+		return $this->_alerts;
+	}
+	
+	public function setAlerts($alerts) {
+		if(!empty($alerts)) {
+			if(preg_match('/'.ABSOLUTEURI_2396.'/', $alerts)) {
+				$this->_alerts = $alerts;
+			} else {
+				throw new PENSException(1201);
+			}
+		}
+	}
+	
+	public function getVendorData() {
+		return $this->_vendor_data;
+	}
+	
+	public function setVendorData($vendor_data) {
+		if(!empty($vendor_data)) {
+			$this->_vendor_data = $vendor_data;
+		}
+	}
+	
+	/**
+	 * Returns an associative that contains all the fields needed to send a
+	 * receipt or an alert to the client
+	 * 
+	 * @return array Associative array
+	 */
+	protected function getSendReceiptAlertArray() {
+		return array("pens-version" => $this->getPensVersion(),
+			"package-type" => $this->getPackageType(),
+			"package-type-version" => $this->getPackageTypeVersion(),
+			"package-format" => $this->getPackageFormat(),
+			"package-id" => $this->getPackageId(),
+			"package-url" => $this->getPackageUrl(),
+			"package-url-expiry" => $this->getPackageUrlExpiry()->format(DateTime::ISO8601),
+			"client" => $this->getClient());
+	}
+	
+	/**
+	 * Returns an associative that contains all the fields needed to send a
+	 * receipt to the client
+	 * 
+	 * @return array Associative array
+	 */
+	public function getSendReceiptArray() {
+		$receipt = $this->getSendReceiptAlertArray();
+		$receipt["command"] = "receipt";
+		return $receipt;
+	}
+	
+	/**
+	 * Returns an associative that contains all the fields needed to send an
+	 * alert to the client
+	 * 
+	 * @return array Associative array
+	 */
+	public function getSendAlertArray() {
+		$alert = $this->getSendReceiptAlertArray();
+		$alert["command"] = "alert";
+		return $alert;
+	}
+	
+	
+}

+ 48 - 0
plugin/pens/lib/pens/pens_request_alert.php

@@ -0,0 +1,48 @@
+<?php
+/**
+ * This file is part of php-pens.
+ * 
+ * php-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * php-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with php-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * PENSRequestAlert
+ * 
+ * Provides the PENSRequestAlert class
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+require_once __DIR__ . '/pens_request.php';
+
+/**
+ * PENSRequestAlert
+ * 
+ * PENS Request of type alert
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+class PENSRequestAlert extends PENSRequest {
+	
+	public function __construct($arguments) {
+		parent::__construct($arguments);
+		$this->setCommand("alert");
+	}
+
+}

+ 48 - 0
plugin/pens/lib/pens/pens_request_collect.php

@@ -0,0 +1,48 @@
+<?php
+/**
+ * This file is part of php-pens.
+ * 
+ * php-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * php-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with php-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * PENSRequestCollect
+ * 
+ * Provides the PENSRequestCollect class
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+require_once __DIR__ . '/pens_request.php';
+
+/**
+ * PENSRequestCollect
+ * 
+ * PENS Request of type collect
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+class PENSRequestCollect extends PENSRequest {
+	
+	public function __construct($arguments) {
+		parent::__construct($arguments);
+		$this->setCommand("collect");
+	}
+
+}

+ 68 - 0
plugin/pens/lib/pens/pens_request_factory.php

@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * This file is part of php-pens.
+ * 
+ * php-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * php-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with php-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * PENSRequestFactory
+ * 
+ * Provides the PENSRequestFactory class
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+require_once __DIR__ . '/pens_exception.php';
+require_once __DIR__ . '/pens_request_receipt.php';
+require_once __DIR__ . '/pens_request_collect.php';
+require_once __DIR__ . '/pens_request_alert.php';
+
+/**
+ * PENSRequestFactory
+ * 
+ * Class used to create a PENSRequestCollect, PENSRequestReceipt or PENSRequestAlert object, depending on the "command" argument
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+class PENSRequestFactory {
+
+	/**
+	 * Factory method
+	 * 
+	 * Creates the right PENSRequest object, based on the command inside the arguments
+	 * 
+	 * @param array Associative array of arguments
+	 * @return mixed PENSRequestAlert or PENSRequestCollect or PENSRequestReceipt
+	 * 
+	 * @throws PENSException with code 2002 if command is invalid
+	 */
+	public static function createPENSRequest($arguments) {
+		$command = $arguments["command"];
+		if($command == "alert") {
+			return new PENSRequestAlert($arguments);
+		} else if($command == "collect") {
+			return new PENSRequestCollect($arguments);
+		} else if($command == "receipt") {
+			return new PENSRequestReceipt($arguments);
+		} else {
+			throw new PENSException(2002);
+		}
+	}
+}

+ 48 - 0
plugin/pens/lib/pens/pens_request_handler.php

@@ -0,0 +1,48 @@
+<?php
+/**
+ * This file is part of php-pens.
+ * 
+ * php-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * php-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with php-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+ /**
+ * PENSRequestHandler
+ * 
+ * Provides the PENSRequestHandler abstract class
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+/**
+ * PENSRequestHandler
+ * 
+ * This class is an abstract class used to handle the processing of the requests on the client side. Create a class that inherits from
+ * this class and implements the processRequest method, then set the request handler of the PENSClient object.
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+abstract class PENSRequestHandler {
+	/**
+	 * Processes the request. Must be implemented by subclasses
+	 * 
+	 * @param PENSRequest Alert or Receipt request sent by the server
+	 * @param PENSResponse Response sent by the server (error, error-text etc...)
+	 */
+	abstract public function processRequest($request, $response);
+}
+

+ 48 - 0
plugin/pens/lib/pens/pens_request_receipt.php

@@ -0,0 +1,48 @@
+<?php
+/**
+ * This file is part of php-pens.
+ * 
+ * php-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * php-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with php-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * PENSRequestReceipt
+ * 
+ * Provides the PENSRequestReceipt class
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+require_once __DIR__ . '/pens_request.php';
+
+/**
+ * PENSRequestReceipt
+ * 
+ * PENS Request of type receipt
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+class PENSRequestReceipt extends PENSRequest {
+	
+	public function __construct($arguments) {
+		parent::__construct($arguments);
+		$this->setCommand("receipt");
+	}
+
+}

+ 175 - 0
plugin/pens/lib/pens/pens_response.php

@@ -0,0 +1,175 @@
+<?php
+/**
+ * This file is part of php-pens.
+ * 
+ * php-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * php-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with php-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * PENSResponse
+ * 
+ * Provides the PENSResponse class
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+require_once __DIR__ . '/pens_config.php';
+require_once __DIR__ . '/pens_message.php';
+require_once __DIR__ . '/pens_exception.php';
+
+/**
+ * PENSResponse
+ * 
+ * PENSResponse class
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+ class PENSResponse extends PENSMessage {
+	 /**
+	  * Error code
+	  * @var int
+	  */
+	 protected $_error = null;
+	 
+	 /**
+	  * Descriptive text of the error
+	  * @var string
+	  */
+	 protected $_error_text = null;
+	 
+	 /**
+	  * Data linked to the error
+	  * @var string
+	  */
+	 protected $_pens_data = null;
+	 
+	 /**
+	  * Version
+	  * @var string
+	  */
+	 protected $_pens_version = null;
+	 
+	 /**
+	  * Constructor
+	  * 
+	  * @param mixed Can be a PENSException or an error code or a string representing an HTTP response
+	  * @param string error text
+	  * @param string PENS data
+	  */
+	public function __construct($error, $error_text = null, $pens_data = null) {
+		$this->_pens_version = PENSConfig::$version;
+		if($error instanceof PENSException) {
+			$this->_error = $error->getCode();
+			$this->_error_text = $error->getMessage();
+		} else if(is_string($error)){
+			// Parse the string
+			$this->parseResponse($error);
+		} else if(is_array($error)) {
+			// Try to build from array
+			$this->_error = $error["error"];
+			$this->_error_text = $error["error-text"];
+			$this->_pens_data = $error["pens-data"];
+		} else {
+			$this->_error = $error;
+			$this->_error_text = $error_text;
+			$this->_pens_data = $pens_data;
+		}
+		
+	}
+	
+	/**
+	 * Parses an HTTP response and assigns the attributes of the object
+	 * 
+	 * @param string HTTP response
+	 */
+	protected function parseResponse($response) {
+		$lines = explode(PENSConfig::$eol, $response);
+		$i = 1;
+		foreach($lines as $line) {
+			if($i < 5) {
+				$pair = explode("=", $line);
+				if($pair[0] == "error") {
+					$this->_error = intval($pair[1]);
+				} else if($pair[0] == "error-text") {
+					$this->_error_text = $pair[1];
+				} else if($pair[0] == "version") {
+					$this->_pens_version = $pair[1];
+				} else if($pair[0] == "pens-data") {
+					if(!empty($pair[1])) {
+						$this->_pens_data = $pair[1].PENSConfig::$eol;
+					}
+				}
+			} else {
+				if(!empty($line)) {
+					$this->_pens_data .= $line.PENSConfig::$eol;
+				}
+			}
+			$i++;
+		}
+	}
+	
+	public function getError() {
+		return $this->_error;
+	}
+	
+	public function getErrorText() {
+		return $this->_error_text;
+	}
+	
+	public function getPensData() {
+		return $this->_pens_data;
+	}
+	
+	public function getVersion() {
+		return $this->_pens_version;
+	}
+	
+	/**
+	 * Sends the response content through HTTP headers
+	 */
+	public function send() {
+		header("Content-Type: text/plain");
+		print $this;
+	}
+	
+	/**
+	 * Returns an associative array of the response
+	 * 
+	 * @return array Associative array
+	 */
+	public function getArray() {
+		return array("error" => $this->_error,
+			"error-text" => $this->_error_text,
+			"version" => PENSConfig::$version,
+			"pens-data" => $this->_pens_data);
+	}
+	
+	/**
+	 * Transforms the object into a string
+	 */
+	public function __toString() {
+		$eol = PENSConfig::$eol;
+		$return = "error=".$this->_error.$eol;
+		$return .= "error-text=".$this->_error_text.$eol;
+		$return .= "version=".PENSConfig::$version.$eol;
+		$return .= "pens-data=".$this->_pens_data.$eol;
+		return $return;
+	}
+ 
+ }
+ 

+ 313 - 0
plugin/pens/lib/pens/pens_server.php

@@ -0,0 +1,313 @@
+<?php
+/**
+ * This file is part of php-pens.
+ * 
+ * php-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * php-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with php-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+ /**
+ * PENSServer
+ * 
+ * Provides the PENSServer class
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+require_once __DIR__ . '/pens_controller.php';
+require_once __DIR__ . '/pens_package_handler.php';
+require_once __DIR__ . '/pens_exception.php';
+require_once __DIR__ . '/pens_response.php';
+
+/**
+ * PENSServer
+ * 
+ * Class that implements the PENS Server
+ * 
+ * @package PENS
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+class PENSServer extends PENSController {
+
+	/**
+	 * Instance of the PENSServer
+	 * @var PENSServer
+	 */
+	private static $_instance;
+	
+	/**
+	 * Package handler
+	 * @var PENSPackageHandler
+	 */
+	protected $_package_handler = null;
+	
+	/**
+	 * Private constructor
+	 */
+	private function __construct() {
+	}
+	
+	/**
+	 * Singleton method
+	 */
+	public static function singleton() {
+		if(!isset(self::$_instance)) {
+			$c = __CLASS__;
+			self::$_instance = new $c;
+		}
+		return self::$_instance;
+	}
+	
+	/**
+	 * Prevent users to clone the instance
+	 */
+	public function __clone() {
+		trigger_error('Clone is not allowed', E_USER_ERROR);
+	}
+	
+	public function getPackageHandler() {
+		return $this->_package_handler;
+	}
+	
+	/**
+	 * Sets the package handler. Does nothing if the argument is not an instance of PENSPackageHandler
+	 * 
+	 * @param PENSPackageHandler Package handler
+	 */
+	public function setPackageHandler($package_handler) {
+		if($package_handler instanceof PENSPackageHandler) {
+			$this->_package_handler = $package_handler;
+		}
+	}
+	
+	/**
+	 * Receives a collect request and treats it
+	 */
+	public function receiveCollect() {
+		$request = null;
+		try {
+			// First, try to parse the request
+			$request = $this->parseRequest();
+			if($request->getCommand() == "collect") {
+				if(isset($_REQUEST['process'])) {
+					// Collect the package and process it
+					$receipt = null;
+					$path_to_package = null;
+					try {
+						// Collect the package
+						$path_to_package = $this->collectPackage($request);
+						$receipt = new PENSResponse(0, "package successfully collected");
+					} catch(PENSException $e) {
+						$receipt = new PENSResponse($e);
+					}
+					// Send receipt
+					$response = $this->sendReceipt($request, $receipt);
+					if(!is_null($response) && !is_null($path_to_package)) {
+						if($response->getError() === 0) {
+							// Process package
+							$this->processPackage($request, $path_to_package);
+						}
+						unlink($path_to_package);
+					}
+				} else {
+					// Then, send a success response to the client
+					$this->sendResponse(new PENSResponse(0, "collect command received and understood"));
+					// Send a request to process the package: fake multithreading
+					$params = $_REQUEST;
+					$params['process'] = 1;
+					$scheme = "http";
+					if(!empty($_SERVER['HTTPS'])) {
+						$scheme = "https";
+					}
+					$ch = curl_init();
+					curl_setopt($ch, CURLOPT_URL, $scheme."://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
+					curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
+					curl_setopt($ch, CURLOPT_TIMEOUT, 1);
+					curl_exec($ch);
+					curl_close($ch);
+				}
+			}
+				
+		} catch(PENSException $e) {
+			// If we could not parse the request, send the error back to the client
+			$this->sendResponse(new PENSResponse($e));
+		}
+	}
+	
+	/**
+	 * Collects the package onto the local server
+	 * 
+	 * @param PENSRequest request
+	 * @return string Path to the package on the hard drive
+	 * @throws PENSException if an exception occured
+	 */
+	protected function collectPackage($request) {
+		$supported_package_types = $this->_package_handler->getSupportedPackageTypes();
+		if(!in_array($request->getPackageType(), $supported_package_types)) {
+			throw new PENSException(1430);
+		}
+		$supported_package_formats = $this->_package_handler->getSupportedPackageFormats();
+		if(!in_array($request->getPackageFormat(), $supported_package_formats)) {
+			throw new PENSException(1431);
+		}
+		if(!$this->isExpiryDateValid($request->getPackageUrlExpiry())) {
+			throw new PENSException(1322);
+		}
+		
+		// Try to download the package in the temporary directory
+		$tmp = null;
+		if(function_exists("sys_get_temp_dir")) {
+			$tmp = sys_get_temp_dir();
+		} else {
+			$tmp = "/tmp";
+		}
+		$path_to_file = $tmp."/".$request->getFilename();
+		$fp = fopen($path_to_file, 'w');
+		$ch = curl_init();
+		curl_setopt($ch, CURLOPT_URL, $request->getPackageUrl());
+		curl_setopt($ch, CURLOPT_HEADER, false);	
+		curl_setopt($ch, CURLOPT_FILE, $fp);
+		if(!is_null($request->getPackageUrlUserId())) {
+			curl_setopt($ch, CURLOPT_USERPWD, $request->getPackageUrlUserId().":".$request->getPackageUrlPassword());
+		}
+		if(curl_exec($ch) === false) {
+			$errno = curl_errno($ch);
+			curl_close($ch);
+			// Error occured. Throw an exception
+			switch($errno) {
+				case CURLE_UNSUPPORTED_PROTOCOL:
+					throw new PENSException(1301);
+					break;
+				case CURLE_URL_MALFORMAT:
+				case CURLE_COULDNT_RESOLVE_PROXY:
+				case CURLE_COULDNT_RESOLVE_HOST:
+				case CURLE_COULDNT_CONNECT:
+				case CURLE_OPERATION_TIMEOUT:
+				case CURLE_REMOTE_FILE_NOT_FOUND:
+					throw new PENSException(1310);
+					break;
+				case CURLE_REMOTE_ACCESS_DENIED:
+					throw new PENSException(1312);
+					break;
+				default:
+					throw new PENSException(1301);
+					break;
+			}
+			return null;
+				
+		} else {
+			curl_close($ch);
+			return $path_to_file;
+		}
+	}
+	
+	/**
+	 * Verifies that the package url is not expired
+	 * 
+	 * @param DateTime DateTime object to verify against current time
+	 */
+	protected function isExpiryDateValid($expiry) {
+		date_default_timezone_set('UTC');
+		$current_time = time();
+		$expiry_time = strtotime($expiry->format(DateTime::ISO8601));
+		if($current_time > $expiry_time) {
+			return false;
+		} else {
+			return true;
+		}
+	}
+	
+	/**
+	 * Sends an alert or a receipt. Called by sendReceipt and sendAlert
+	 * 
+	 * @param PENSRequest Original collect request
+	 * @param PENSResponse Reponse to send in the receipt or the alert
+	 * @param string Mode (alert | receipt)
+	 * @return PENSResponse Response
+	 */
+	protected function sendAlertOrReceipt($request, $response, $mode) {
+		if($mode == "alert") {
+			$url = $request->getAlerts();
+		} else {
+			$url = $request->getReceipt();
+		}
+		if(!empty($url)) {
+			$url_components = parse_url($url);
+			$scheme = $url_components["scheme"];
+			if($scheme == "mailto") {
+				$to = $url_components["path"];
+				if($mode == "alert") {
+					$subject = "PENS Alert for ".$request->getPackageId();
+				} else {
+					$subject = "PENS Receipt for ".$request->getPackageId();
+				}
+				$message = $response->__toString();
+				mail($to, $subject, $message);
+				return new PENSResponse(0, "");
+			} else if($scheme == "http" || $scheme == "https") {
+				if($mode == "alert") {
+					$params = array_merge($request->getSendAlertArray(), $response->getArray());
+				} else {
+					$params = array_merge($request->getSendReceiptArray(), $response->getArray());
+				}
+				$ch = curl_init($url);
+				curl_setopt($ch, CURLOPT_POST, true);
+				curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
+				curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+				$data = curl_exec($ch);
+				curl_close($ch);
+				if($data === false) {
+					return null;
+				} else {
+					return new PENSResponse($data);
+				}
+			}
+		}
+	}
+	
+	/**
+	 * Sends a receipt. Returns a PENSResponse in case of success, null if a problem occured
+	 * 
+	 * @param PENSRequest Original collect request
+	 * @param PENSResponse Response to send in the receipt
+	 * @return PENSResponse Response
+	 */
+	protected function sendReceipt($request, $receipt) {
+		return $this->sendAlertOrReceipt($request, $receipt, "receipt");
+	}
+	
+	/**
+	 * Processes the package using the handler provided
+	 * 
+	 * @param PENSRequest Original collect request
+	 * @param string Path to the package on the hard drive
+	 */
+	protected function processPackage($request, $path_to_package) {
+		return $this->_package_handler->processPackage($request, $path_to_package);
+	}
+	
+	/**
+	 * Sends an alert to the client. Returns a PENSResponse in case of success, null if a problem occured
+	 * 
+	 * @param PENSRequest Original collect request
+	 * @param PENSResponse Response to send in the alert
+	 * @return PENSResponse Response
+	 */
+	public function sendAlert($request, $alert) {
+		return $this->sendAlertOrReceipt($request, $alert, "alert");
+	}
+	
+}

+ 39 - 0
plugin/pens/lib/pens_client_test.php

@@ -0,0 +1,39 @@
+<?php
+/**
+ * This file is part of php-pens.
+ * 
+ * php-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * php-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with php-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+/**
+ * Provides a test implementation of a PENS Client.
+ * 
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+require_once __DIR__ . '/pens.php';
+
+class MyRequestHandler extends PENSRequestHandler {
+	public function processRequest($request, $response) {
+		// Do the processing of the alert or the receipt here
+	}
+}
+
+$handler = new MyRequestHandler();
+
+$client = PENSClient::singleton();
+$client->setRequestHandler($handler);
+
+$client->receiveRequest();

+ 41 - 0
plugin/pens/lib/pens_server_test.php

@@ -0,0 +1,41 @@
+<?php
+/**
+ * This file is part of php-pens.
+ * 
+ * php-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * php-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with php-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+/**
+ * Provides a test implementation of a PENS Server.
+ * 
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+require_once __DIR__ . '/pens.php';
+
+class MyPackageHandler extends PENSPackageHandler {
+	public function processPackage($request, $path_to_package) {
+		// Do the package processing here
+	}
+}
+
+$handler = new MyPackageHandler();
+$handler->setSupportedPackageTypes(array("scorm-pif", "ims-qti", "aicc-pkg"));
+$handler->setSupportedPackageFormats(array("zip"));
+
+$server = PENSServer::singleton();
+$server->setPackageHandler($handler);
+
+$server->receiveCollect();

+ 87 - 0
plugin/pens/lib/tests/test_pens_package_handler.php

@@ -0,0 +1,87 @@
+<?php
+
+/**
+ * This file is part of php-pens.
+ * 
+ * php-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * php-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with php-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */ 
+
+
+/**
+ * Unit tests for the PENSPackageHandler class
+ * 
+ * This class provides unit tests for the PENSPackageHandler class
+ * 
+ * @package PENS
+ * @subpackage Tests
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+require_once("simpletest/autorun.php");
+require_once(dirname(__FILE__)."/../pens.php");
+
+class MyPackageHandler extends PENSPackageHandler {
+	public function processPackage($request, $path_to_package) {
+	}
+}
+
+/**
+ * Unit tests for the PENSPackageHandler class
+ * 
+ * This class provides unit tests for the PENSPackageHandler class
+ * 
+ * @package PENS
+ * @subpackage Tests
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+class TestPENSPackageHandler extends UnitTestCase {
+	
+	public function testSupportedPackageTypesInvalid() {
+		$object = new MyPackageHandler();
+		$object->setSupportedPackageTypes("testing");
+		$this->assertEqual($object->getSupportedPackageTypes(), null);
+	}
+	
+	public function testSupportedPackageTypesInvalid2() {
+		$object = new MyPackageHandler();
+		$object->setSupportedPackageTypes(array("aicc-pkg", "testing"));
+		$this->assertEqual($object->getSupportedPackageTypes(), array("aicc-pkg"));
+	}
+	
+	public function testSupportedPackageTypesValid() {
+		$object = new MyPackageHandler();
+		$object->setSupportedPackageTypes(array("aicc-pkg", "lms-qti"));
+		$this->assertEqual($object->getSupportedPackageTypes(), array("aicc-pkg", "lms-qti"));
+	}
+	
+	public function testSupportedPackageFormatsInvalid() {
+		$object = new MyPackageHandler();
+		$object->setSupportedPackageFormats("testing");
+		$this->assertEqual($object->getSupportedPackageFormats(), null);
+	}
+	
+	public function testSupportedPackageFormatsInvalid2() {
+		$object = new MyPackageHandler();
+		$object->setSupportedPackageFormats(array("zip", "testing"));
+		$this->assertEqual($object->getSupportedPackageFormats(), array("zip"));
+	}
+	
+	public function testSupportedPackageFormatsValid() {
+		$object = new MyPackageHandler();
+		$object->setSupportedPackageFormats(array("zip", "jar"));
+		$this->assertEqual($object->getSupportedPackageFormats(), array("zip", "jar"));
+	}
+}

+ 213 - 0
plugin/pens/lib/tests/test_pens_request.php

@@ -0,0 +1,213 @@
+<?php
+
+/**
+ * This file is part of php-pens.
+ * 
+ * php-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * php-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with php-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */ 
+
+
+/**
+ * Unit tests for the PENSRequest class
+ * 
+ * This class provides unit tests for the PENSRequest class
+ * 
+ * @package PENS
+ * @subpackage Tests
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+require_once("simpletest/autorun.php");
+require_once(dirname(__FILE__)."/../pens.php");
+
+/**
+ * Unit tests for the PENSRequest class
+ * 
+ * This class provides unit tests for the PENSRequest class
+ * 
+ * @package PENS
+ * @subpackage Tests
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+class TestPENSRequest extends UnitTestCase {
+	
+	/**
+	 * Valid arguments to be used for tests
+	 * 
+	 * @var array
+	 */
+	private $args = null;
+	
+	public function setUp() {
+		$this->args = array(
+			"pens-version" => "1.0.0",
+			"command" => "collect",
+			"package-type" => "aicc-pkg",
+			"package-type-version" => "1.0",
+			"package-format" => "zip",
+			"package-id" => "http://myurl.com/12345",
+			"package-url" => "http://myurl.com/mypackage.zip",
+			"package-url-expiry" => "2006-04-01T06:51:29Z",
+			"client" => "Authorware7",
+			"receipt" => "mailto:name@domain.com",
+			"package-url-user-id" => "guillaumev",
+			"package-url-account" => "toto",
+			"package-url-password" => "12345",
+			"system-user-id" => "guillaumev",
+			"system-password" => "12345",
+			"alerts" => "http://myurl.com/alerts",
+			"vendor-data" => "here are my data");
+	}
+	
+	public function createObject($myargs) {
+		return PENSRequestFactory::createPENSRequest($myargs);
+	}
+	
+	public function exceptionTestForValue($key, $value, $code) {
+		try {
+			$myargs = $this->args;
+			if($value === null) {
+				unset($myargs[$key]);
+			} else {
+				$myargs[$key] = $value;
+			}
+			$object = $this->createObject($myargs);
+			$this->fail();
+		} catch(PENSException $e) {
+			$this->assertEqual($e->getCode(), $code);
+		}
+	}
+	
+	public function testPensVersionInvalid() {
+		$this->exceptionTestForValue("pens-version", "0.8.0", 2001);
+	}
+	
+	public function testPensVersionNull() {
+		$this->exceptionTestForValue("pens-version", null, 2001);
+	}
+	
+	
+	public function testCommandInvalid() {
+		$this->exceptionTestForValue("command", "testing", 2002);
+	}
+	
+	public function testCommandNull() {
+		$this->exceptionTestForValue("command", null, 2002);
+	}
+	
+	public function testPackageTypeInvalid() {
+		$this->exceptionTestForValue("package-type", "testing", 2003);
+	}
+	
+	public function testPackageTypeNull() {
+		$this->exceptionTestForValue("package-type", null, 2003);
+	}
+	
+	public function testPackageTypeVersionNull() {
+		$this->exceptionTestForValue("package-type-version", null, 2004);
+	}
+	
+	public function testPackageFormatInvalid() {
+		$this->exceptionTestForValue("package-format", "testing", 2005);
+	}
+	
+	public function testPackageFormatNull() {
+		$this->exceptionTestForValue("package-format", null, 2005);
+	}
+	
+	public function testPackageIdInvalid() {
+		$this->exceptionTestForValue("package-id", "testing", 2007);
+	}
+	
+	public function testPackageIdNull() {
+		$this->exceptionTestForValue("package-id", null, 2007);
+	}
+	
+	public function testPackageUrlInvalid() {
+		$this->exceptionTestForValue("package-url", "testing", 2008);
+	}
+	
+	public function testPackageUrlInvalid2() {
+		$this->exceptionTestForValue("package-url", "http://myurl.com/mypackage", 2008);
+	}
+	
+	public function testPackageUrlNull() {
+		$this->exceptionTestForValue("package-url", null, 2008);
+	}
+	
+	public function testPackageUrlExpiryNull() {
+		$this->exceptionTestForValue("package-url-expiry", null, 2009);
+	}
+	
+	public function testPackageUrlExpiryInvalid() {
+		$this->exceptionTestForValue("package-url-expiry", "testing", 2009);
+	}
+	
+	public function testClientNull() {
+		$this->exceptionTestForValue("client", null, 2010);
+	}
+	
+	public function testReceiptNull() {
+		$this->exceptionTestForValue("receipt", null, 2011);
+	}
+	
+	public function testReceiptInvalid() {
+		$this->exceptionTestForValue("receipt", "testing", 2011);
+	}
+	
+	public function testAlertsInvalid() {
+		$this->exceptionTestForValue("alerts", "testing", 1201);
+	}
+	
+	public function testReceiptValid() {
+		try {
+			$myargs = $this->args;
+			$myargs["receipt"] = "mailto:name@domain.com,name2@domain.com";
+			$object = $this->createObject($myargs);
+			$this->pass();
+		} catch(PENSException $e) {
+			$this->fail();
+		}
+	}
+	
+	public function testValid() {
+		try {
+			$object = $this->createObject($this->args);
+			$this->assertIsA($object, "PENSRequestCollect");
+			$this->assertEqual($object->getPensVersion(), "1.0.0");
+			$this->assertEqual($object->getPackageType(), "aicc-pkg");
+			$this->assertEqual($object->getPackageTypeVersion(), "1.0");
+			$this->assertEqual($object->getPackageFormat(), "zip");
+			$this->assertEqual($object->getPackageId(), "http://myurl.com/12345");
+			$this->assertEqual($object->getPackageUrl(), "http://myurl.com/mypackage.zip");
+			$this->assertIsA($object->getPackageUrlExpiry(), "DateTime");
+			$this->assertEqual($object->getClient(), "Authorware7");
+			$this->assertEqual($object->getReceipt(), "mailto:name@domain.com");
+			$this->assertEqual($object->getPackageUrlUserId(), "guillaumev");
+			$this->assertEqual($object->getPackageUrlAccount(), "toto");
+			$this->assertEqual($object->getPackageUrlPassword(), "12345");
+			$this->assertEqual($object->getSystemUserId(), "guillaumev");
+			$this->assertEqual($object->getSystemPassword(), "12345");
+			$this->assertEqual($object->getAlerts(), "http://myurl.com/alerts");
+			$this->assertEqual($object->getVendorData(), "here are my data");
+			
+		} catch(PENSException $e) {
+			$this->fail();
+		}
+	}
+	
+	
+}

+ 68 - 0
plugin/pens/lib/tests/test_pens_response.php

@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * This file is part of php-pens.
+ * 
+ * php-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * php-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with php-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */ 
+
+
+/**
+ * Unit tests for the PENSResponse class
+ * 
+ * This class provides unit tests for the PENSResponse class
+ * 
+ * @package PENS
+ * @subpackage Tests
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+require_once("simpletest/autorun.php");
+require_once(dirname(__FILE__)."/../pens.php");
+
+/**
+ * Unit tests for the PENSResponse class
+ * 
+ * This class provides unit tests for the PENSResponse class
+ * 
+ * @package PENS
+ * @subpackage Tests
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+class TestPENSResponse extends UnitTestCase {
+	
+	public function testCreationFromPENSException() {
+		$object = new PENSResponse(new PENSException(1101));
+		$this->assertEqual($object->getError(), 1101);
+		$this->assertNotNull($object->getErrorText());
+	}
+	
+	public function testCreationFromArguments() {
+		$object = new PENSResponse(0, "collect command received and understood");
+		$this->assertIdentical($object->getError(), 0);
+		$this->assertEqual($object->getErrorText(), "collect command received and understood");
+	}
+	
+	public function testCreationFromHTTPResponse() {
+		$eol = PENSConfig::$eol;
+		$response = "error=1101".$eol."error-text=unable to parse PENS command".$eol."version=1.0.0".$eol."pens-data=".$eol;
+		$object = new PENSResponse($response);
+		$this->assertIdentical($object->getError(), 1101);
+		$this->assertIdentical($object->getErrorText(), "unable to parse PENS command");
+		$this->assertIdentical($object->getVersion(), "1.0.0");
+		$this->assertNull($object->getPensData());
+	}
+}

+ 122 - 0
plugin/pens/lib/tests/test_pens_server.php

@@ -0,0 +1,122 @@
+<?php
+
+/**
+ * This file is part of php-pens.
+ * 
+ * php-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * php-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with php-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */ 
+
+
+/**
+ * Functional tests for the PENS Server
+ * 
+ * This class provides functional testing of the PENS Server
+ * 
+ * @package PENS
+ * @subpackage Tests
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+require_once("simpletest/autorun.php");
+require_once("simpletest/web_tester.php");
+
+define(PENS_TEST_SERVER_URL, "http://localhost/pens/pens_server_test.php");
+define(PENS_TEST_RECEIPT_MAILTO, "mailto:test@test.com");
+
+/**
+ * Functional tests for the PENS Server
+ * 
+ * This class provides functional tests for the PENS Server
+ * 
+ * @package PENS
+ * @subpackage Tests
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+class TestPENSServer extends WebTestCase {
+	
+	private $_valid_params = array(
+		"command" => "collect",
+		"pens-version" => "1.0.0",
+		"package-type" => "aicc-pkg",
+		"package-type-version" => "1.0",
+		"package-format" => "zip",
+		"package-id" => "http://myurl.com/12345",
+		"package-url" => "http://www.aicc.org/pens/sample/captivatequiz.zip",
+		"package-url-expiry" => "2099-04-09T14:17:43Z",
+		"client" => "test-pens-server",
+		"receipt" => PENS_TEST_RECEIPT_MAILTO);
+		
+	public function testEmptyQuery() {
+		$this->get(PENS_TEST_SERVER_URL);
+		$this->assertText("error=2002");
+	}
+	
+	public function testValidQuery() {
+		$this->get(PENS_TEST_SERVER_URL, $this->_valid_params);
+		$this->assertText("error=0");
+	}
+	
+	private function invalidParameterTest($name, $value, $error) {
+		$params = $this->_valid_params;
+		$params[$name] = $value;
+		$this->get(PENS_TEST_SERVER_URL, $params);
+		$this->assertText("error=".$error);
+	}
+	
+	private function validParameterTest($name, $value) {
+		$params = $this->_valid_params;
+		$params[$name] = $value;
+		$this->get(PENS_TEST_SERVER_URL, $params);
+		$this->assertText("error=0");
+	}
+	
+	public function testInvalidCommand() {
+		$this->invalidParameterTest("command", "invalid", 2002);
+	}
+	
+	public function testInvalidVersion() {
+		$this->invalidParameterTest("pens-version", "0.8.0", 2001);
+	}
+	
+	public function testInvalidPackageType() {
+		$this->invalidParameterTest("package-type", "invalid", 2003);
+	}
+	
+	public function testEmptyPackageTypeVersion() {
+		$this->invalidParameterTest("package-type-version", null, 2004);
+	}
+	
+	public function testInvalidPackageFormat() {
+		$this->invalidParameterTest("package-format", "invalid", 2005);
+	}
+	
+	public function testInvalidPackageId() {
+		$this->invalidParameterTest("package-id", "invalid", 2007);
+	}
+	
+	public function testInvalidPackageUrl() {
+		$this->invalidParameterTest("package-url", "invalid", 2008);
+	}
+	
+	public function testInvalidPackageUrlExpiry() {
+		$this->invalidParameterTest("package-url-expiry", "invalid", 2009);
+	}
+	
+	public function testValidPackageUrlExpiry() {
+		$this->validParameterTest("package-url-expiry", "2099-01-01");
+	}
+	
+}
+

+ 54 - 0
plugin/pens/pens.php

@@ -0,0 +1,54 @@
+<?php
+/**
+ * This file is part of chamilo-pens.
+ * 
+ * chamilo-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * chamilo-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with chamilo-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+/**
+ * Provides an implementation of the PENS server, using the php-pens library.
+ * This file must be required by a file pens.php accessible at the Chamilo root 
+ * 
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+require_once __DIR__ . '/../../main/inc/global.inc.php';
+require_once __DIR__ . '/lib/pens.php';
+require_once __DIR__ . '/chamilo_pens.php';
+
+class ChamiloPackageHandler extends PENSPackageHandler {
+	public function processPackage($request, $path_to_package) {
+		$server = PENSServer::singleton();
+		// Moves the package to archive/pens
+		$path_to_archives = api_get_path(SYS_ARCHIVE_PATH) . 'pens';
+		if(!is_dir($path_to_archives)) {
+			mkdir($path_to_archives, 0777, true);
+		}
+		rename($path_to_package, $path_to_archives . '/' . $request->getFilename());
+		// Insert the request in the database
+		$chamilo_pens = new ChamiloPens($request);
+		$chamilo_pens->save();
+		$server->sendAlert($request, new PENSResponse(0, 'Package successfully processed'));
+	}
+}
+
+$handler = new ChamiloPackageHandler();
+$handler->setSupportedPackageTypes(array('scorm-pif'));
+$handler->setSupportedPackageFormats(array('zip'));
+
+$server = PENSServer::singleton();
+$server->setPackageHandler($handler);
+
+$server->receiveCollect();

+ 36 - 0
plugin/pens/plugin.php

@@ -0,0 +1,36 @@
+<?php
+/**
+ * This file is part of chamilo-pens.
+ * 
+ * chamilo-pens is free software: you can redistribute it and/or modify 
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * chamilo-pens is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with chamilo-pens.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+/**
+ * Plugin definition file
+ * 
+ * @author Guillaume Viguier-Just <guillaume@viguierjust.com>
+ * @licence http://www.gnu.org/licenses/gpl.txt
+ */
+
+//the plugin title
+$plugin_info['title']='PENS';
+//the comments that go with the plugin
+$plugin_info['comment']="PENS implementation for Chamilo";
+//the locations where this plugin can be shown
+$plugin_info['location']=array();
+//the plugin version
+$plugin_info['version']='1.1';
+//the plugin author
+$plugin_info['author']='Guillaume Viguier-Just, Yannick Warnier';
+$plugin_info = PENSPlugin::create()->get_info();

+ 24 - 0
plugin/pens/readme.txt

@@ -0,0 +1,24 @@
+<h1>Introduction</h1>
+
+This plugin implements PENS in Chamilo, using the library php-pens available here: http://github.com/guillaumev/php-pens
+
+<h1>Requirements</h1>
+
+You need the curl extension installed to use this library.
+
+<h1>How to install ?</h1>
+
+Install the plugin and create a file at the root of your chamilo server called pens.php. Put the following code in the pens.php file you created:
+<pre>
+require_once __DIR__ . '/plugin/pens/pens.php';
+</pre>
+
+<h1>Licence</h1>
+
+The chamilo-pens plugin is published under the GNU/GPLv3+ licence (see COPYING)
+
+<h1>Credits</h1>
+
+The original author of the plugin is Guillaume Viguier-Just <guillaume@viguierjust.com>.
+
+This plugin was realized as part of his work in BeezNest (https://beeznest.com) and is maintained by BeezNest.