diff --git a/administrator/components/com_categories/models/category.php b/administrator/components/com_categories/models/category.php index fb63bb8bb2f13..1fdd3ba12b93e 100644 --- a/administrator/components/com_categories/models/category.php +++ b/administrator/components/com_categories/models/category.php @@ -572,7 +572,7 @@ public function save($data) if ($assoc) { // Adding self to the association - $associations = $data['associations']; + $associations = isset($data['associations']) ? $data['associations'] : array(); // Unset any invalid associations $associations = Joomla\Utilities\ArrayHelper::toInteger($associations); diff --git a/administrator/components/com_finder/finder.xml b/administrator/components/com_finder/finder.xml index 81b231ca67754..5967bc0b2ef04 100644 --- a/administrator/components/com_finder/finder.xml +++ b/administrator/components/com_finder/finder.xml @@ -21,7 +21,6 @@ js - images css diff --git a/administrator/components/com_menus/models/forms/item.xml b/administrator/components/com_menus/models/forms/item.xml index 70c5104e7bb1a..04eb44dcb9204 100644 --- a/administrator/components/com_menus/models/forms/item.xml +++ b/administrator/components/com_menus/models/forms/item.xml @@ -135,7 +135,6 @@ id="access" label="JFIELD_ACCESS_LABEL" description="JFIELD_ACCESS_DESC" - default="1" filter="integer"/> diff --git a/administrator/components/com_menus/models/item.php b/administrator/components/com_menus/models/item.php index fd3f480b92d3e..1fa034067b663 100644 --- a/administrator/components/com_menus/models/item.php +++ b/administrator/components/com_menus/models/item.php @@ -593,7 +593,7 @@ protected function loadFormData() $filters = JFactory::getApplication()->getUserState('com_menus.items.filter'); $data['published'] = (isset($filters['published']) ? $filters['published'] : null); $data['language'] = (isset($filters['language']) ? $filters['language'] : null); - $data['access'] = (isset($filters['access']) ? $filters['access'] : null); + $data['access'] = (isset($filters['access']) ? $filters['access'] : JFactory::getConfig()->get('access')); } if (isset($data['menutype']) && !$this->getState('item.menutypeid')) diff --git a/administrator/language/en-GB/en-GB.ini b/administrator/language/en-GB/en-GB.ini index 39c4ecb1728c0..048db581353c5 100644 --- a/administrator/language/en-GB/en-GB.ini +++ b/administrator/language/en-GB/en-GB.ini @@ -160,6 +160,7 @@ JERROR_CORE_DELETE_NOT_PERMITTED="Delete not permitted." JERROR_COULD_NOT_FIND_TEMPLATE="Could not find template "_QQ_"%s"_QQ_"." JERROR_INVALID_CONTROLLER="Invalid controller" JERROR_INVALID_CONTROLLER_CLASS="Invalid controller class" +JERROR_LAYOUT_PREVIOUS_ERROR="Previous Error" JERROR_LOADFILE_FAILED="Error loading form file" JERROR_LOADING_MENUS="Error loading Menus: %s" JERROR_LOGIN_DENIED="You do not have access to the Administrator section of this site." diff --git a/administrator/language/en-GB/en-GB.plg_system_languagefilter.ini b/administrator/language/en-GB/en-GB.plg_system_languagefilter.ini index 1c2b264f2ddb4..d6e765e41adfb 100644 --- a/administrator/language/en-GB/en-GB.plg_system_languagefilter.ini +++ b/administrator/language/en-GB/en-GB.plg_system_languagefilter.ini @@ -5,7 +5,7 @@ PLG_SYSTEM_LANGUAGEFILTER="System - Language Filter" PLG_SYSTEM_LANGUAGEFILTER_BROWSER_SETTINGS="Browser Settings" -PLG_SYSTEM_LANGUAGEFILTER_FIELD_ALTERNATE_META_DESC="Add alternative meta tags for menu items with associated menu items in other languages." +PLG_SYSTEM_LANGUAGEFILTER_FIELD_ALTERNATE_META_DESC="Add alternative meta tags for items with associated items in other languages." PLG_SYSTEM_LANGUAGEFILTER_FIELD_ALTERNATE_META_LABEL="Add Alternate Meta Tags" PLG_SYSTEM_LANGUAGEFILTER_FIELD_AUTOMATIC_CHANGE_DESC="This option will automatically change the content language used in the Frontend when a user site language is changed." PLG_SYSTEM_LANGUAGEFILTER_FIELD_AUTOMATIC_CHANGE_LABEL="Automatic Language Change" @@ -13,7 +13,7 @@ PLG_SYSTEM_LANGUAGEFILTER_FIELD_COOKIE_DESC="Language cookies can be set to expi PLG_SYSTEM_LANGUAGEFILTER_FIELD_COOKIE_LABEL="Cookie Lifetime" PLG_SYSTEM_LANGUAGEFILTER_FIELD_DETECT_BROWSER_DESC="Choose site default language or try to detect the browser settings language. It will default to site language if browser settings can't be found." PLG_SYSTEM_LANGUAGEFILTER_FIELD_DETECT_BROWSER_LABEL="Language Selection for new Visitors" -PLG_SYSTEM_LANGUAGEFILTER_FIELD_ITEM_ASSOCIATIONS_DESC="This option will allow item associations when switching from one language to another." +PLG_SYSTEM_LANGUAGEFILTER_FIELD_ITEM_ASSOCIATIONS_DESC="This option will allow item associations when switching from one language to another. Default home menu items are always associated." PLG_SYSTEM_LANGUAGEFILTER_FIELD_ITEM_ASSOCIATIONS_LABEL="Item Associations" PLG_SYSTEM_LANGUAGEFILTER_FIELD_XDEFAULT_DESC="This option will add x-default meta tag to improve SEO." PLG_SYSTEM_LANGUAGEFILTER_FIELD_XDEFAULT_LABEL="Add x-default Meta Tag" diff --git a/administrator/templates/hathor/error.php b/administrator/templates/hathor/error.php index cb7a8b7558e91..aafeb82db5ebc 100644 --- a/administrator/templates/hathor/error.php +++ b/administrator/templates/hathor/error.php @@ -37,7 +37,24 @@

error->getMessage(), ENT_QUOTES, 'UTF-8'); ?>

debug) : ?> - renderBacktrace(); ?> +
+ renderBacktrace(); ?> + + error->getPrevious()) : ?> + + _error here and in the loop as setError() assigns errors to this property and we need this for the backtrace to work correctly ?> + + setError($this->_error->getPrevious()); ?> + +

+

_error->getMessage(), ENT_QUOTES, 'UTF-8'); ?>

+ renderBacktrace(); ?> + setError($this->_error->getPrevious()); ?> + + + setError($this->error); ?> + +
diff --git a/administrator/templates/isis/error.php b/administrator/templates/isis/error.php index bf93ee411c977..3d0af14888087 100644 --- a/administrator/templates/isis/error.php +++ b/administrator/templates/isis/error.php @@ -234,7 +234,24 @@ error->getCode(); ?> error->getMessage(), ENT_QUOTES, 'UTF-8');?> debug) : ?> - renderBacktrace(); ?> +
+ renderBacktrace(); ?> + + error->getPrevious()) : ?> + + _error here and in the loop as setError() assigns errors to this property and we need this for the backtrace to work correctly ?> + + setError($this->_error->getPrevious()); ?> + +

+

_error->getMessage(), ENT_QUOTES, 'UTF-8'); ?>

+ renderBacktrace(); ?> + setError($this->_error->getPrevious()); ?> + + + setError($this->error); ?> + +

diff --git a/administrator/templates/system/error.php b/administrator/templates/system/error.php index efa211d41ec97..8806a6c7ccfde 100644 --- a/administrator/templates/system/error.php +++ b/administrator/templates/system/error.php @@ -29,11 +29,26 @@

error->getMessage(), ENT_QUOTES, 'UTF-8'); ?>

-

- debug) : - echo $this->renderBacktrace(); - endif; ?> -

+ debug) : ?> +
+ renderBacktrace(); ?> + + error->getPrevious()) : ?> + + _error here and in the loop as setError() assigns errors to this property and we need this for the backtrace to work correctly ?> + + setError($this->_error->getPrevious()); ?> + +

+

_error->getMessage(), ENT_QUOTES, 'UTF-8'); ?>

+ renderBacktrace(); ?> + setError($this->_error->getPrevious()); ?> + + + setError($this->error); ?> + +
+ diff --git a/language/en-GB/en-GB.ini b/language/en-GB/en-GB.ini index e95066b85a96a..79f7aaedd4bce 100644 --- a/language/en-GB/en-GB.ini +++ b/language/en-GB/en-GB.ini @@ -118,6 +118,7 @@ JERROR_LAYOUT_NOT_ABLE_TO_VISIT="You may not be able to visit this page because JERROR_LAYOUT_PAGE_NOT_FOUND="The requested page can't be found." JERROR_LAYOUT_PLEASE_CONTACT_THE_SYSTEM_ADMINISTRATOR="If difficulties persist, please contact the System Administrator of this site and report the error below." JERROR_LAYOUT_PLEASE_TRY_ONE_OF_THE_FOLLOWING_PAGES="Please try one of the following pages:" +JERROR_LAYOUT_PREVIOUS_ERROR="Previous Error" JERROR_LAYOUT_REQUESTED_RESOURCE_WAS_NOT_FOUND="The requested resource was not found." JERROR_LAYOUT_SEARCH="You may wish to search the site or visit the home page." JERROR_LAYOUT_SEARCH_ENGINE_OUT_OF_DATE_LISTING="a search engine that has an out-of-date listing for this site" diff --git a/libraries/fof/LICENSE.txt b/libraries/fof/LICENSE.txt new file mode 100644 index 0000000000000..56e598d43901e --- /dev/null +++ b/libraries/fof/LICENSE.txt @@ -0,0 +1,345 @@ +================================================================================ +Historical note +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +On February 21st, 2013 FOF changed its license to GPLv2 or later. +================================================================================ + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE 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. + + 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 +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision 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, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This 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. \ No newline at end of file diff --git a/libraries/fof/autoloader/component.php b/libraries/fof/autoloader/component.php index c97d2333df014..f9a2a8e564630 100644 --- a/libraries/fof/autoloader/component.php +++ b/libraries/fof/autoloader/component.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage autoloader - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2, or later */ diff --git a/libraries/fof/autoloader/fof.php b/libraries/fof/autoloader/fof.php index d771df89cf3b2..602c9192a77d5 100644 --- a/libraries/fof/autoloader/fof.php +++ b/libraries/fof/autoloader/fof.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage autoloader - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2, or later */ diff --git a/libraries/fof/config/domain/dispatcher.php b/libraries/fof/config/domain/dispatcher.php index b9726ccbe2899..cbe0526088a04 100644 --- a/libraries/fof/config/domain/dispatcher.php +++ b/libraries/fof/config/domain/dispatcher.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage config - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2, or later */ diff --git a/libraries/fof/config/domain/interface.php b/libraries/fof/config/domain/interface.php index ea5935e9cec5b..d2901ab954efa 100644 --- a/libraries/fof/config/domain/interface.php +++ b/libraries/fof/config/domain/interface.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage config - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2, or later */ diff --git a/libraries/fof/config/domain/tables.php b/libraries/fof/config/domain/tables.php index e5ae9167fa7b3..3e4e4dd826ad4 100644 --- a/libraries/fof/config/domain/tables.php +++ b/libraries/fof/config/domain/tables.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage config - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2, or later */ diff --git a/libraries/fof/config/domain/views.php b/libraries/fof/config/domain/views.php index 8674d8c9eec29..1c18b62ea0849 100644 --- a/libraries/fof/config/domain/views.php +++ b/libraries/fof/config/domain/views.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage config - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2, or later */ diff --git a/libraries/fof/config/provider.php b/libraries/fof/config/provider.php index 4faba8f399008..4b335371c9e76 100644 --- a/libraries/fof/config/provider.php +++ b/libraries/fof/config/provider.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage config - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2, or later */ diff --git a/libraries/fof/controller/controller.php b/libraries/fof/controller/controller.php index 458164fecef8e..d972f077d52ef 100644 --- a/libraries/fof/controller/controller.php +++ b/libraries/fof/controller/controller.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage controller - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ @@ -480,6 +480,13 @@ public function __construct($config = array()) { $mName = $rMethod->getName(); + // If the developer screwed up and declared one of the helper method public do NOT make them available as + // tasks. + if ((substr($mName, 0, 8) == 'onBefore') || (substr($mName, 0, 7) == 'onAfter') || substr($mName, 0, 1) == '_') + { + continue; + } + // Add default display method if not explicitly declared. if (!in_array($mName, $xMethods) || in_array($mName, $iMethods)) { @@ -1049,8 +1056,9 @@ public function display($cachable = false, $urlparams = false, $tpl = null) $groups = $user->groups; } - // Set up safe URL parameters + $importantParameters = array(); + // Set up safe URL parameters if (!is_array($urlparams)) { $urlparams = array( @@ -1090,6 +1098,9 @@ public function display($cachable = false, $urlparams = false, $tpl = null) { // Add your safe url parameters with variable type as value {@see JFilterInput::clean()}. $registeredurlparams->$key = $value; + + // Add the URL-important parameters into the array + $importantParameters[$key] = $this->input->get($key, null, $value); } if (version_compare(JVERSION, '3.0', 'ge')) @@ -1103,7 +1114,7 @@ public function display($cachable = false, $urlparams = false, $tpl = null) } // Create the cache ID after setting the registered URL params, as they are used to generate the ID - $cacheId = md5(serialize(array(JCache::makeId(), $view->getName(), $this->doTask, $groups))); + $cacheId = md5(serialize(array(JCache::makeId(), $view->getName(), $this->doTask, $groups, $importantParameters))); // Get the cached view or cache the current view $cache->get($view, 'display', $cacheId); @@ -1347,7 +1358,7 @@ public function apply() $customURL = base64_decode($customURL); } - $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->component . '&view=' . $this->view . '&task=edit&id=' . $id . $this->getItemidURLSuffix(); + $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->component . '&view=' . $this->view . '&task=edit&id=' . $id . $this->getItemidURLSuffix(); $this->setRedirect($url, JText::_($textkey)); } diff --git a/libraries/fof/database/database.php b/libraries/fof/database/database.php new file mode 100644 index 0000000000000..dc5a7188c7419 --- /dev/null +++ b/libraries/fof/database/database.php @@ -0,0 +1,199 @@ +execute(); + } + + /** + * Get a list of available database connectors. The list will only be populated with connectors that both + * the class exists and the static test method returns true. This gives us the ability to have a multitude + * of connector classes that are self-aware as to whether or not they are able to be used on a given system. + * + * @return array An array of available database connectors. + * + * @since 11.1 + * @deprecated 13.1 (Platform) & 4.0 (CMS) + */ + public static function getConnectors() + { + if (class_exists('JLog')) + { + JLog::add('FOFDatabase::getConnectors() is deprecated, use FOFDatabaseDriver::getConnectors() instead.', JLog::WARNING, 'deprecated'); + } + + return FOFDatabaseDriver::getConnectors(); + } + + /** + * Gets the error message from the database connection. + * + * @param boolean $escaped True to escape the message string for use in JavaScript. + * + * @return string The error message for the most recent query. + * + * @deprecated 13.3 (Platform) & 4.0 (CMS) + * @since 11.1 + */ + public function getErrorMsg($escaped = false) + { + if (class_exists('JLog')) + { + JLog::add('FOFDatabase::getErrorMsg() is deprecated, use exception handling instead.', JLog::WARNING, 'deprecated'); + } + + if ($escaped) + { + return addslashes($this->errorMsg); + } + else + { + return $this->errorMsg; + } + } + + /** + * Gets the error number from the database connection. + * + * @return integer The error number for the most recent query. + * + * @since 11.1 + * @deprecated 13.3 (Platform) & 4.0 (CMS) + */ + public function getErrorNum() + { + if (class_exists('JLog')) + { + JLog::add('FOFDatabase::getErrorNum() is deprecated, use exception handling instead.', JLog::WARNING, 'deprecated'); + } + + return $this->errorNum; + } + + /** + * Method to return a FOFDatabaseDriver instance based on the given options. There are three global options and then + * the rest are specific to the database driver. The 'driver' option defines which FOFDatabaseDriver class is + * used for the connection -- the default is 'mysqli'. The 'database' option determines which database is to + * be used for the connection. The 'select' option determines whether the connector should automatically select + * the chosen database. + * + * Instances are unique to the given options and new objects are only created when a unique options array is + * passed into the method. This ensures that we don't end up with unnecessary database connection resources. + * + * @param array $options Parameters to be passed to the database driver. + * + * @return FOFDatabaseDriver A database object. + * + * @since 11.1 + * @deprecated 13.1 (Platform) & 4.0 (CMS) + */ + public static function getInstance($options = array()) + { + if (class_exists('JLog')) + { + JLog::add('FOFDatabase::getInstance() is deprecated, use FOFDatabaseDriver::getInstance() instead.', JLog::WARNING, 'deprecated'); + } + + return FOFDatabaseDriver::getInstance($options); + } + + /** + * Splits a string of multiple queries into an array of individual queries. + * + * @param string $query Input SQL string with which to split into individual queries. + * + * @return array The queries from the input string separated into an array. + * + * @since 11.1 + * @deprecated 13.1 (Platform) & 4.0 (CMS) + */ + public static function splitSql($query) + { + if (class_exists('JLog')) + { + JLog::add('FOFDatabase::splitSql() is deprecated, use FOFDatabaseDriver::splitSql() instead.', JLog::WARNING, 'deprecated'); + } + + return FOFDatabaseDriver::splitSql($query); + } + + /** + * Return the most recent error message for the database connector. + * + * @param boolean $showSQL True to display the SQL statement sent to the database as well as the error. + * + * @return string The error message for the most recent query. + * + * @since 11.1 + * @deprecated 13.3 (Platform) & 4.0 (CMS) + */ + public function stderr($showSQL = false) + { + if (class_exists('JLog')) + { + JLog::add('FOFDatabase::stderr() is deprecated.', JLog::WARNING, 'deprecated'); + } + + if ($this->errorNum != 0) + { + return JText::sprintf('JLIB_DATABASE_ERROR_FUNCTION_FAILED', $this->errorNum, $this->errorMsg) + . ($showSQL ? "
SQL =
$this->sql
" : ''); + } + else + { + return JText::_('JLIB_DATABASE_FUNCTION_NOERROR'); + } + } + + /** + * Test to see if the connector is available. + * + * @return boolean True on success, false otherwise. + * + * @since 11.1 + * @deprecated 12.3 (Platform) & 4.0 (CMS) - Use FOFDatabaseDriver::isSupported() instead. + */ + public static function test() + { + if (class_exists('JLog')) + { + JLog::add('FOFDatabase::test() is deprecated. Use FOFDatabaseDriver::isSupported() instead.', JLog::WARNING, 'deprecated'); + } + + return static::isSupported(); + } +} diff --git a/libraries/fof/database/driver.php b/libraries/fof/database/driver.php new file mode 100644 index 0000000000000..27b17efc2eeb1 --- /dev/null +++ b/libraries/fof/database/driver.php @@ -0,0 +1,2255 @@ +getFilename(); + + // Only load for php files. + if (!$file->isFile() || $file->getExtension() != 'php') + { + continue; + } + + // Block the ext/mysql driver for PHP 7 + if ($fileName === 'mysql.php' && PHP_MAJOR_VERSION >= 7) + { + continue; + } + + // Derive the class name from the type. + $class = str_ireplace('.php', '', 'FOFDatabaseDriver' . ucfirst(trim($fileName))); + + // If the class doesn't exist we have nothing left to do but look at the next type. We did our best. + if (!class_exists($class)) + { + continue; + } + + // Sweet! Our class exists, so now we just need to know if it passes its test method. + if ($class::isSupported()) + { + // Connector names should not have file extensions. + $connectors[] = str_ireplace('.php', '', $fileName); + } + } + + return $connectors; + } + + /** + * Method to return a FOFDatabaseDriver instance based on the given options. There are three global options and then + * the rest are specific to the database driver. The 'driver' option defines which FOFDatabaseDriver class is + * used for the connection -- the default is 'mysqli'. The 'database' option determines which database is to + * be used for the connection. The 'select' option determines whether the connector should automatically select + * the chosen database. + * + * Instances are unique to the given options and new objects are only created when a unique options array is + * passed into the method. This ensures that we don't end up with unnecessary database connection resources. + * + * @param array $options Parameters to be passed to the database driver. + * + * @return FOFDatabaseDriver A database object. + * + * @since 11.1 + * @throws RuntimeException + */ + public static function getInstance($options = array()) + { + // Sanitize the database connector options. + $options['driver'] = (isset($options['driver'])) ? preg_replace('/[^A-Z0-9_\.-]/i', '', $options['driver']) : 'mysqli'; + $options['database'] = (isset($options['database'])) ? $options['database'] : null; + $options['select'] = (isset($options['select'])) ? $options['select'] : true; + + // If the selected driver is `mysql` and we are on PHP 7 or greater, switch to the `mysqli` driver. + if ($options['driver'] === 'mysql' && PHP_MAJOR_VERSION >= 7) + { + // Check if we have support for the other MySQL drivers + $mysqliSupported = FOFDatabaseDriverMysqli::isSupported(); + $pdoMysqlSupported = FOFDatabaseDriverPdomysql::isSupported(); + + // If neither is supported, then the user cannot use MySQL; throw an exception + if (!$mysqliSupported && !$pdoMysqlSupported) + { + throw new RuntimeException( + 'The PHP `ext/mysql` extension is removed in PHP 7, cannot use the `mysql` driver.' + . ' Also, this system does not support MySQLi or PDO MySQL. Cannot instantiate database driver.' + ); + } + + // Prefer MySQLi as it is a closer replacement for the removed MySQL driver, otherwise use the PDO driver + if ($mysqliSupported) + { + if (class_exists('JLog')) + { + JLog::add( + 'The PHP `ext/mysql` extension is removed in PHP 7, cannot use the `mysql` driver. Trying `mysqli` instead.', + JLog::WARNING, + 'deprecated' + ); + } + + $options['driver'] = 'mysqli'; + } + else + { + if (class_exists('JLog')) + { + JLog::add( + 'The PHP `ext/mysql` extension is removed in PHP 7, cannot use the `mysql` driver. Trying `pdomysql` instead.', + JLog::WARNING, + 'deprecated' + ); + } + + $options['driver'] = 'pdomysql'; + } + } + + // Get the options signature for the database connector. + $signature = md5(serialize($options)); + + // If we already have a database connector instance for these options then just use that. + if (empty(self::$instances[$signature])) + { + // Derive the class name from the driver. + $class = 'FOFDatabaseDriver' . ucfirst(strtolower($options['driver'])); + + // If the class still doesn't exist we have nothing left to do but throw an exception. We did our best. + if (!class_exists($class)) + { + throw new RuntimeException(sprintf('Unable to load Database Driver: %s', $options['driver'])); + } + + // Create our new FOFDatabaseDriver connector based on the options given. + try + { + $instance = new $class($options); + } + catch (RuntimeException $e) + { + throw new RuntimeException(sprintf('Unable to connect to the Database: %s', $e->getMessage()), $e->getCode(), $e); + } + + // Set the new connector to the global instances based on signature. + self::$instances[$signature] = $instance; + } + + return self::$instances[$signature]; + } + + /** + * Splits a string of multiple queries into an array of individual queries. + * + * @param string $sql Input SQL string with which to split into individual queries. + * + * @return array The queries from the input string separated into an array. + * + * @since 11.1 + */ + public static function splitSql($sql) + { + $start = 0; + $open = false; + $char = ''; + $end = strlen($sql); + $queries = array(); + + for ($i = 0; $i < $end; $i++) + { + $current = substr($sql, $i, 1); + + if (($current == '"' || $current == '\'')) + { + $n = 2; + + while (substr($sql, $i - $n + 1, 1) == '\\' && $n < $i) + { + $n++; + } + + if ($n % 2 == 0) + { + if ($open) + { + if ($current == $char) + { + $open = false; + $char = ''; + } + } + else + { + $open = true; + $char = $current; + } + } + } + + if (($current == ';' && !$open) || $i == $end - 1) + { + $queries[] = substr($sql, $start, ($i - $start + 1)); + $start = $i + 1; + } + } + + return $queries; + } + + /** + * Magic method to provide method alias support for quote() and quoteName(). + * + * @param string $method The called method. + * @param array $args The array of arguments passed to the method. + * + * @return mixed The aliased method's return value or null. + * + * @since 11.1 + */ + public function __call($method, $args) + { + if (empty($args)) + { + return; + } + + switch ($method) + { + case 'q': + return $this->quote($args[0], isset($args[1]) ? $args[1] : true); + break; + case 'qn': + return $this->quoteName($args[0], isset($args[1]) ? $args[1] : null); + break; + } + } + + /** + * Constructor. + * + * @param array $options List of options used to configure the connection + * + * @since 11.1 + */ + public function __construct($options) + { + // Initialise object variables. + $this->_database = (isset($options['database'])) ? $options['database'] : ''; + $this->tablePrefix = (isset($options['prefix'])) ? $options['prefix'] : 'jos_'; + $this->connection = array_key_exists('connection', $options) ? $options['connection'] : null; + + $this->count = 0; + $this->errorNum = 0; + $this->log = array(); + + // Set class options. + $this->options = $options; + } + + /** + * Alter database's character set, obtaining query string from protected member. + * + * @param string $dbName The database name that will be altered + * + * @return string The query that alter the database query string + * + * @since 12.2 + * @throws RuntimeException + */ + public function alterDbCharacterSet($dbName) + { + if (is_null($dbName)) + { + throw new RuntimeException('Database name must not be null.'); + } + + $this->setQuery($this->getAlterDbCharacterSet($dbName)); + + return $this->execute(); + } + + /** + * Alter a table's character set, obtaining an array of queries to do so from a protected method. The conversion is + * wrapped in a transaction, if supported by the database driver. Otherwise the table will be locked before the + * conversion. This prevents data corruption. + * + * @param string $tableName The name of the table to alter + * @param boolean $rethrow True to rethrow database exceptions. Default: false (exceptions are suppressed) + * + * @return boolean True if successful + * + * @since CMS 3.5.0 + * @throws RuntimeException If the table name is empty + * @throws Exception Relayed from the database layer if a database error occurs and $rethrow == true + */ + public function alterTableCharacterSet($tableName, $rethrow = false) + { + if (is_null($tableName)) + { + throw new RuntimeException('Table name must not be null.'); + } + + $queries = $this->getAlterTableCharacterSet($tableName); + + if (empty($queries)) + { + return false; + } + + $hasTransaction = true; + + try + { + $this->transactionStart(); + } + catch (Exception $e) + { + $hasTransaction = false; + $this->lockTable($tableName); + } + + foreach ($queries as $query) + { + try + { + $this->setQuery($query)->execute(); + } + catch (Exception $e) + { + if ($hasTransaction) + { + $this->transactionRollback(); + } + else + { + $this->unlockTables(); + } + + if ($rethrow) + { + throw $e; + } + + return false; + } + } + + if ($hasTransaction) + { + try + { + $this->transactionCommit(); + } + catch (Exception $e) + { + $this->transactionRollback(); + + if ($rethrow) + { + throw $e; + } + + return false; + } + } + else + { + $this->unlockTables(); + } + + return true; + } + + /** + * Connects to the database if needed. + * + * @return void Returns void if the database connected successfully. + * + * @since 12.1 + * @throws RuntimeException + */ + abstract public function connect(); + + /** + * Determines if the connection to the server is active. + * + * @return boolean True if connected to the database engine. + * + * @since 11.1 + */ + abstract public function connected(); + + /** + * Create a new database using information from $options object, obtaining query string + * from protected member. + * + * @param stdClass $options Object used to pass user and database name to database driver. + * This object must have "db_name" and "db_user" set. + * @param boolean $utf True if the database supports the UTF-8 character set. + * + * @return string The query that creates database + * + * @since 12.2 + * @throws RuntimeException + */ + public function createDatabase($options, $utf = true) + { + if (is_null($options)) + { + throw new RuntimeException('$options object must not be null.'); + } + elseif (empty($options->db_name)) + { + throw new RuntimeException('$options object must have db_name set.'); + } + elseif (empty($options->db_user)) + { + throw new RuntimeException('$options object must have db_user set.'); + } + + $this->setQuery($this->getCreateDatabaseQuery($options, $utf)); + + return $this->execute(); + } + + /** + * Disconnects the database. + * + * @return void + * + * @since 12.1 + */ + abstract public function disconnect(); + + /** + * Adds a function callable just before disconnecting the database. Parameter of the callable is $this FOFDatabaseDriver + * + * @param callable $callable Function to call in disconnect() method just before disconnecting from database + * + * @return void + * + * @since CMS 3.1.2 + */ + public function addDisconnectHandler($callable) + { + $this->disconnectHandlers[] = $callable; + } + + /** + * Drops a table from the database. + * + * @param string $table The name of the database table to drop. + * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. + * + * @return FOFDatabaseDriver Returns this object to support chaining. + * + * @since 11.4 + * @throws RuntimeException + */ + public abstract function dropTable($table, $ifExists = true); + + /** + * Escapes a string for usage in an SQL statement. + * + * @param string $text The string to be escaped. + * @param boolean $extra Optional parameter to provide extra escaping. + * + * @return string The escaped string. + * + * @since 11.1 + */ + abstract public function escape($text, $extra = false); + + /** + * Method to fetch a row from the result set cursor as an array. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * + * @return mixed Either the next row from the result set or false if there are no more rows. + * + * @since 11.1 + */ + abstract protected function fetchArray($cursor = null); + + /** + * Method to fetch a row from the result set cursor as an associative array. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * + * @return mixed Either the next row from the result set or false if there are no more rows. + * + * @since 11.1 + */ + abstract protected function fetchAssoc($cursor = null); + + /** + * Method to fetch a row from the result set cursor as an object. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * @param string $class The class name to use for the returned row object. + * + * @return mixed Either the next row from the result set or false if there are no more rows. + * + * @since 11.1 + */ + abstract protected function fetchObject($cursor = null, $class = 'stdClass'); + + /** + * Method to free up the memory used for the result set. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * + * @return void + * + * @since 11.1 + */ + abstract protected function freeResult($cursor = null); + + /** + * Get the number of affected rows for the previous executed SQL statement. + * + * @return integer The number of affected rows. + * + * @since 11.1 + */ + abstract public function getAffectedRows(); + + /** + * Return the query string to alter the database character set. + * + * @param string $dbName The database name + * + * @return string The query that alter the database query string + * + * @since 12.2 + */ + public function getAlterDbCharacterSet($dbName) + { + $charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8'; + + return 'ALTER DATABASE ' . $this->quoteName($dbName) . ' CHARACTER SET `' . $charset . '`'; + } + + /** + * Get the query strings to alter the character set and collation of a table. + * + * @param string $tableName The name of the table + * + * @return string[] The queries required to alter the table's character set and collation + * + * @since CMS 3.5.0 + */ + public function getAlterTableCharacterSet($tableName) + { + $charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8'; + $collation = $charset . '_general_ci'; + + $quotedTableName = $this->quoteName($tableName); + + $queries = array(); + $queries[] = "ALTER TABLE $quotedTableName CONVERT TO CHARACTER SET $charset COLLATE $collation"; + + /** + * We also need to convert each text column, modifying their character set and collation. This allows us to + * change, for example, a utf8_bin collated column to a utf8mb4_bin collated column. + */ + $sql = "SHOW FULL COLUMNS FROM $quotedTableName"; + $this->setQuery($sql); + $columns = $this->loadAssocList(); + $columnMods = array(); + + if (is_array($columns)) + { + foreach ($columns as $column) + { + // Make sure we are redefining only columns which do support a collation + $col = (object) $column; + + if (empty($col->Collation)) + { + continue; + } + + // Default new collation: utf8_general_ci or utf8mb4_general_ci + $newCollation = $charset . '_general_ci'; + $collationParts = explode('_', $col->Collation); + + /** + * If the collation is in the form charset_collationType_ci or charset_collationType we have to change + * the charset but leave the collationType intact (e.g. utf8_bin must become utf8mb4_bin, NOT + * utf8mb4_general_ci). + */ + if (count($collationParts) >= 2) + { + $ci = array_pop($collationParts); + $collationType = array_pop($collationParts); + $newCollation = $charset . '_' . $collationType . '_' . $ci; + + /** + * When the last part of the old collation is not _ci we have a charset_collationType format, + * something like utf8_bin. Therefore the new collation only has *two* parts. + */ + if ($ci != 'ci') + { + $newCollation = $charset . '_' . $ci; + } + } + + // If the old and new collation is the same we don't have to change the collation type + if (strtolower($newCollation) == strtolower($col->Collation)) + { + continue; + } + + $null = $col->Null == 'YES' ? 'NULL' : 'NOT NULL'; + $default = is_null($col->Default) ? '' : "DEFAULT '" . $this->q($col->Default) . "'"; + $columnMods[] = "MODIFY COLUMN `{$col->Field}` {$col->Type} CHARACTER SET $charset COLLATE $newCollation $null $default"; + } + } + + if (count($columnMods)) + { + $queries[] = "ALTER TABLE $quotedTableName " . + implode(',', $columnMods) . + " CHARACTER SET $charset COLLATE $collation"; + } + + return $queries; + } + + /** + * Automatically downgrade a CREATE TABLE or ALTER TABLE query from utf8mb4 (UTF-8 Multibyte) to plain utf8. Used + * when the server doesn't support UTF-8 Multibyte. + * + * @param string $query The query to convert + * + * @return string The converted query + */ + public function convertUtf8mb4QueryToUtf8($query) + { + if ($this->hasUTF8mb4Support()) + { + return $query; + } + + // If it's not an ALTER TABLE or CREATE TABLE command there's nothing to convert + $beginningOfQuery = substr($query, 0, 12); + $beginningOfQuery = strtoupper($beginningOfQuery); + + if (!in_array($beginningOfQuery, array('ALTER TABLE ', 'CREATE TABLE'))) + { + return $query; + } + + // Replace utf8mb4 with utf8 + return str_replace('utf8mb4', 'utf8', $query); + } + + /** + * Return the query string to create new Database. + * Each database driver, other than MySQL, need to override this member to return correct string. + * + * @param stdClass $options Object used to pass user and database name to database driver. + * This object must have "db_name" and "db_user" set. + * @param boolean $utf True if the database supports the UTF-8 character set. + * + * @return string The query that creates database + * + * @since 12.2 + */ + protected function getCreateDatabaseQuery($options, $utf) + { + if ($utf) + { + $charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8'; + + return 'CREATE DATABASE ' . $this->quoteName($options->db_name) . ' CHARACTER SET `' . $charset . '`'; + } + + return 'CREATE DATABASE ' . $this->quoteName($options->db_name); + } + + /** + * Method to get the database collation in use by sampling a text field of a table in the database. + * + * @return mixed The collation in use by the database or boolean false if not supported. + * + * @since 11.1 + */ + abstract public function getCollation(); + + /** + * Method to get the database connection collation, as reported by the driver. If the connector doesn't support + * reporting this value please return an empty string. + * + * @return string + */ + public function getConnectionCollation() + { + return ''; + } + + /** + * Method that provides access to the underlying database connection. Useful for when you need to call a + * proprietary method such as postgresql's lo_* methods. + * + * @return resource The underlying database connection resource. + * + * @since 11.1 + */ + public function getConnection() + { + return $this->connection; + } + + /** + * Get the total number of SQL statements executed by the database driver. + * + * @return integer + * + * @since 11.1 + */ + public function getCount() + { + return $this->count; + } + + /** + * Gets the name of the database used by this conneciton. + * + * @return string + * + * @since 11.4 + */ + protected function getDatabase() + { + return $this->_database; + } + + /** + * Returns a PHP date() function compliant date format for the database driver. + * + * @return string The format string. + * + * @since 11.1 + */ + public function getDateFormat() + { + return 'Y-m-d H:i:s'; + } + + /** + * Get the database driver SQL statement log. + * + * @return array SQL statements executed by the database driver. + * + * @since 11.1 + */ + public function getLog() + { + return $this->log; + } + + /** + * Get the database driver SQL statement log. + * + * @return array SQL statements executed by the database driver. + * + * @since CMS 3.1.2 + */ + public function getTimings() + { + return $this->timings; + } + + /** + * Get the database driver SQL statement log. + * + * @return array SQL statements executed by the database driver. + * + * @since CMS 3.1.2 + */ + public function getCallStacks() + { + return $this->callStacks; + } + + /** + * Get the minimum supported database version. + * + * @return string The minimum version number for the database driver. + * + * @since 12.1 + */ + public function getMinimum() + { + return static::$dbMinimum; + } + + /** + * Get the null or zero representation of a timestamp for the database driver. + * + * @return string Null or zero representation of a timestamp. + * + * @since 11.1 + */ + public function getNullDate() + { + return $this->nullDate; + } + + /** + * Get the number of returned rows for the previous executed SQL statement. + * + * @param resource $cursor An optional database cursor resource to extract the row count from. + * + * @return integer The number of returned rows. + * + * @since 11.1 + */ + abstract public function getNumRows($cursor = null); + + /** + * Get the common table prefix for the database driver. + * + * @return string The common database table prefix. + * + * @since 11.1 + */ + public function getPrefix() + { + return $this->tablePrefix; + } + + /** + * Gets an exporter class object. + * + * @return FOFDatabaseExporter An exporter object. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getExporter() + { + // Derive the class name from the driver. + $class = 'FOFDatabaseExporter' . ucfirst($this->name); + + // Make sure we have an exporter class for this driver. + if (!class_exists($class)) + { + // If it doesn't exist we are at an impasse so throw an exception. + throw new RuntimeException('Database Exporter not found.'); + } + + $o = new $class; + $o->setDbo($this); + + return $o; + } + + /** + * Gets an importer class object. + * + * @return FOFDatabaseImporter An importer object. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getImporter() + { + // Derive the class name from the driver. + $class = 'FOFDatabaseImporter' . ucfirst($this->name); + + // Make sure we have an importer class for this driver. + if (!class_exists($class)) + { + // If it doesn't exist we are at an impasse so throw an exception. + throw new RuntimeException('Database Importer not found'); + } + + $o = new $class; + $o->setDbo($this); + + return $o; + } + + /** + * Get the name of the database driver. If $this->name is not set it will try guessing the driver name from the + * class name. + * + * @return string + * + * @since CMS 3.5.0 + */ + public function getName() + { + if (empty($this->name)) + { + $className = get_class($this); + $className = str_replace('FOFDatabaseDriver', '', $className); + $this->name = strtolower($className); + } + + return $this->name; + } + + /** + * Get the server family type, e.g. mysql, postgresql, oracle, sqlite, mssql. If $this->serverType is not set it + * will attempt guessing the server family type from the driver name. If this is not possible the driver name will + * be returned instead. + * + * @return string + * + * @since CMS 3.5.0 + */ + public function getServerType() + { + if (empty($this->serverType)) + { + $name = $this->getName(); + + if (stristr($name, 'mysql') !== false) + { + $this->serverType = 'mysql'; + } + elseif (stristr($name, 'postgre') !== false) + { + $this->serverType = 'postgresql'; + } + elseif (stristr($name, 'oracle') !== false) + { + $this->serverType = 'oracle'; + } + elseif (stristr($name, 'sqlite') !== false) + { + $this->serverType = 'sqlite'; + } + elseif (stristr($name, 'sqlsrv') !== false) + { + $this->serverType = 'mssql'; + } + elseif (stristr($name, 'mssql') !== false) + { + $this->serverType = 'mssql'; + } + else + { + $this->serverType = $name; + } + } + + return $this->serverType; + } + + /** + * Get the current query object or a new FOFDatabaseQuery object. + * + * @param boolean $new False to return the current query object, True to return a new FOFDatabaseQuery object. + * + * @return FOFDatabaseQuery The current query object or a new object extending the FOFDatabaseQuery class. + * + * @since 11.1 + * @throws RuntimeException + */ + public function getQuery($new = false) + { + if ($new) + { + // Derive the class name from the driver. + $class = 'FOFDatabaseQuery' . ucfirst($this->name); + + // Make sure we have a query class for this driver. + if (!class_exists($class)) + { + // If it doesn't exist we are at an impasse so throw an exception. + throw new RuntimeException('Database Query Class not found.'); + } + + return new $class($this); + } + else + { + return $this->sql; + } + } + + /** + * Get a new iterator on the current query. + * + * @param string $column An option column to use as the iterator key. + * @param string $class The class of object that is returned. + * + * @return FOFDatabaseIterator A new database iterator. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getIterator($column = null, $class = 'stdClass') + { + // Derive the class name from the driver. + $iteratorClass = 'FOFDatabaseIterator' . ucfirst($this->name); + + // Make sure we have an iterator class for this driver. + if (!class_exists($iteratorClass)) + { + // If it doesn't exist we are at an impasse so throw an exception. + throw new RuntimeException(sprintf('class *%s* is not defined', $iteratorClass)); + } + + // Return a new iterator + return new $iteratorClass($this->execute(), $column, $class); + } + + /** + * Retrieves field information about the given tables. + * + * @param string $table The name of the database table. + * @param boolean $typeOnly True (default) to only return field types. + * + * @return array An array of fields by table. + * + * @since 11.1 + * @throws RuntimeException + */ + abstract public function getTableColumns($table, $typeOnly = true); + + /** + * Shows the table CREATE statement that creates the given tables. + * + * @param mixed $tables A table name or a list of table names. + * + * @return array A list of the create SQL for the tables. + * + * @since 11.1 + * @throws RuntimeException + */ + abstract public function getTableCreate($tables); + + /** + * Retrieves field information about the given tables. + * + * @param mixed $tables A table name or a list of table names. + * + * @return array An array of keys for the table(s). + * + * @since 11.1 + * @throws RuntimeException + */ + abstract public function getTableKeys($tables); + + /** + * Method to get an array of all tables in the database. + * + * @return array An array of all the tables in the database. + * + * @since 11.1 + * @throws RuntimeException + */ + abstract public function getTableList(); + + /** + * Determine whether or not the database engine supports UTF-8 character encoding. + * + * @return boolean True if the database engine supports UTF-8 character encoding. + * + * @since 11.1 + * @deprecated 12.3 (Platform) & 4.0 (CMS) - Use hasUTFSupport() instead + */ + public function getUTFSupport() + { + if (class_exists('JLog')) + { + JLog::add('FOFDatabaseDriver::getUTFSupport() is deprecated. Use FOFDatabaseDriver::hasUTFSupport() instead.', JLog::WARNING, 'deprecated'); + } + + return $this->hasUTFSupport(); + } + + /** + * Determine whether or not the database engine supports UTF-8 character encoding. + * + * @return boolean True if the database engine supports UTF-8 character encoding. + * + * @since 12.1 + */ + public function hasUTFSupport() + { + return $this->utf; + } + + /** + * Determine whether the database engine support the UTF-8 Multibyte (utf8mb4) character encoding. This applies to + * MySQL databases. + * + * @return boolean True if the database engine supports UTF-8 Multibyte. + * + * @since CMS 3.5.0 + */ + public function hasUTF8mb4Support() + { + return $this->utf8mb4; + } + + /** + * Get the version of the database connector + * + * @return string The database connector version. + * + * @since 11.1 + */ + abstract public function getVersion(); + + /** + * Method to get the auto-incremented value from the last INSERT statement. + * + * @return mixed The value of the auto-increment field from the last inserted row. + * + * @since 11.1 + */ + abstract public function insertid(); + + /** + * Inserts a row into a table based on an object's properties. + * + * @param string $table The name of the database table to insert into. + * @param object &$object A reference to an object whose public properties match the table fields. + * @param string $key The name of the primary key. If provided the object property is updated. + * + * @return boolean True on success. + * + * @since 11.1 + * @throws RuntimeException + */ + public function insertObject($table, &$object, $key = null) + { + $fields = array(); + $values = array(); + + // Iterate over the object variables to build the query fields and values. + foreach (get_object_vars($object) as $k => $v) + { + // Only process non-null scalars. + if (is_array($v) or is_object($v) or $v === null) + { + continue; + } + + // Ignore any internal fields. + if ($k[0] == '_') + { + continue; + } + + // Prepare and sanitize the fields and values for the database query. + $fields[] = $this->quoteName($k); + $values[] = $this->quote($v); + } + + // Create the base insert statement. + $query = $this->getQuery(true) + ->insert($this->quoteName($table)) + ->columns($fields) + ->values(implode(',', $values)); + + // Set the query and execute the insert. + $this->setQuery($query); + + if (!$this->execute()) + { + return false; + } + + // Update the primary key if it exists. + $id = $this->insertid(); + + if ($key && $id && is_string($key)) + { + $object->$key = $id; + } + + return true; + } + + /** + * Method to check whether the installed database version is supported by the database driver + * + * @return boolean True if the database version is supported + * + * @since 12.1 + */ + public function isMinimumVersion() + { + return version_compare($this->getVersion(), static::$dbMinimum) >= 0; + } + + /** + * Method to get the first row of the result set from the database query as an associative array + * of ['field_name' => 'row_value']. + * + * @return mixed The return value or null if the query failed. + * + * @since 11.1 + * @throws RuntimeException + */ + public function loadAssoc() + { + $this->connect(); + + $ret = null; + + // Execute the query and get the result set cursor. + if (!($cursor = $this->execute())) + { + return null; + } + + // Get the first row from the result set as an associative array. + if ($array = $this->fetchAssoc($cursor)) + { + $ret = $array; + } + + // Free up system resources and return. + $this->freeResult($cursor); + + return $ret; + } + + /** + * Method to get an array of the result set rows from the database query where each row is an associative array + * of ['field_name' => 'row_value']. The array of rows can optionally be keyed by a field name, but defaults to + * a sequential numeric array. + * + * NOTE: Chosing to key the result array by a non-unique field name can result in unwanted + * behavior and should be avoided. + * + * @param string $key The name of a field on which to key the result array. + * @param string $column An optional column name. Instead of the whole row, only this column value will be in + * the result array. + * + * @return mixed The return value or null if the query failed. + * + * @since 11.1 + * @throws RuntimeException + */ + public function loadAssocList($key = null, $column = null) + { + $this->connect(); + + $array = array(); + + // Execute the query and get the result set cursor. + if (!($cursor = $this->execute())) + { + return null; + } + + // Get all of the rows from the result set. + while ($row = $this->fetchAssoc($cursor)) + { + $value = ($column) ? (isset($row[$column]) ? $row[$column] : $row) : $row; + + if ($key) + { + $array[$row[$key]] = $value; + } + else + { + $array[] = $value; + } + } + + // Free up system resources and return. + $this->freeResult($cursor); + + return $array; + } + + /** + * Method to get an array of values from the $offset field in each row of the result set from + * the database query. + * + * @param integer $offset The row offset to use to build the result array. + * + * @return mixed The return value or null if the query failed. + * + * @since 11.1 + * @throws RuntimeException + */ + public function loadColumn($offset = 0) + { + $this->connect(); + + $array = array(); + + // Execute the query and get the result set cursor. + if (!($cursor = $this->execute())) + { + return null; + } + + // Get all of the rows from the result set as arrays. + while ($row = $this->fetchArray($cursor)) + { + $array[] = $row[$offset]; + } + + // Free up system resources and return. + $this->freeResult($cursor); + + return $array; + } + + /** + * Method to get the next row in the result set from the database query as an object. + * + * @param string $class The class name to use for the returned row object. + * + * @return mixed The result of the query as an array, false if there are no more rows. + * + * @since 11.1 + * @throws RuntimeException + * @deprecated 12.3 (Platform) & 4.0 (CMS) - Use getIterator() instead + */ + public function loadNextObject($class = 'stdClass') + { + if (class_exists('JLog')) + { + JLog::add(__METHOD__ . '() is deprecated. Use FOFDatabaseDriver::getIterator() instead.', JLog::WARNING, 'deprecated'); + } + + $this->connect(); + + static $cursor = null; + + // Execute the query and get the result set cursor. + if ( is_null($cursor) ) + { + if (!($cursor = $this->execute())) + { + return $this->errorNum ? null : false; + } + } + + // Get the next row from the result set as an object of type $class. + if ($row = $this->fetchObject($cursor, $class)) + { + return $row; + } + + // Free up system resources and return. + $this->freeResult($cursor); + $cursor = null; + + return false; + } + + /** + * Method to get the next row in the result set from the database query as an array. + * + * @return mixed The result of the query as an array, false if there are no more rows. + * + * @since 11.1 + * @throws RuntimeException + * @deprecated 4.0 (CMS) Use FOFDatabaseDriver::getIterator() instead + */ + public function loadNextRow() + { + if (class_exists('JLog')) + { + JLog::add(__METHOD__ . '() is deprecated. Use FOFDatabaseDriver::getIterator() instead.', JLog::WARNING, 'deprecated'); + } + + $this->connect(); + + static $cursor = null; + + // Execute the query and get the result set cursor. + if ( is_null($cursor) ) + { + if (!($cursor = $this->execute())) + { + return $this->errorNum ? null : false; + } + } + + // Get the next row from the result set as an object of type $class. + if ($row = $this->fetchArray($cursor)) + { + return $row; + } + + // Free up system resources and return. + $this->freeResult($cursor); + $cursor = null; + + return false; + } + + /** + * Method to get the first row of the result set from the database query as an object. + * + * @param string $class The class name to use for the returned row object. + * + * @return mixed The return value or null if the query failed. + * + * @since 11.1 + * @throws RuntimeException + */ + public function loadObject($class = 'stdClass') + { + $this->connect(); + + $ret = null; + + // Execute the query and get the result set cursor. + if (!($cursor = $this->execute())) + { + return null; + } + + // Get the first row from the result set as an object of type $class. + if ($object = $this->fetchObject($cursor, $class)) + { + $ret = $object; + } + + // Free up system resources and return. + $this->freeResult($cursor); + + return $ret; + } + + /** + * Method to get an array of the result set rows from the database query where each row is an object. The array + * of objects can optionally be keyed by a field name, but defaults to a sequential numeric array. + * + * NOTE: Choosing to key the result array by a non-unique field name can result in unwanted + * behavior and should be avoided. + * + * @param string $key The name of a field on which to key the result array. + * @param string $class The class name to use for the returned row objects. + * + * @return mixed The return value or null if the query failed. + * + * @since 11.1 + * @throws RuntimeException + */ + public function loadObjectList($key = '', $class = 'stdClass') + { + $this->connect(); + + $array = array(); + + // Execute the query and get the result set cursor. + if (!($cursor = $this->execute())) + { + return null; + } + + // Get all of the rows from the result set as objects of type $class. + while ($row = $this->fetchObject($cursor, $class)) + { + if ($key) + { + $array[$row->$key] = $row; + } + else + { + $array[] = $row; + } + } + + // Free up system resources and return. + $this->freeResult($cursor); + + return $array; + } + + /** + * Method to get the first field of the first row of the result set from the database query. + * + * @return mixed The return value or null if the query failed. + * + * @since 11.1 + * @throws RuntimeException + */ + public function loadResult() + { + $this->connect(); + + $ret = null; + + // Execute the query and get the result set cursor. + if (!($cursor = $this->execute())) + { + return null; + } + + // Get the first row from the result set as an array. + if ($row = $this->fetchArray($cursor)) + { + $ret = $row[0]; + } + + // Free up system resources and return. + $this->freeResult($cursor); + + return $ret; + } + + /** + * Method to get the first row of the result set from the database query as an array. Columns are indexed + * numerically so the first column in the result set would be accessible via $row[0], etc. + * + * @return mixed The return value or null if the query failed. + * + * @since 11.1 + * @throws RuntimeException + */ + public function loadRow() + { + $this->connect(); + + $ret = null; + + // Execute the query and get the result set cursor. + if (!($cursor = $this->execute())) + { + return null; + } + + // Get the first row from the result set as an array. + if ($row = $this->fetchArray($cursor)) + { + $ret = $row; + } + + // Free up system resources and return. + $this->freeResult($cursor); + + return $ret; + } + + /** + * Method to get an array of the result set rows from the database query where each row is an array. The array + * of objects can optionally be keyed by a field offset, but defaults to a sequential numeric array. + * + * NOTE: Choosing to key the result array by a non-unique field can result in unwanted + * behavior and should be avoided. + * + * @param string $key The name of a field on which to key the result array. + * + * @return mixed The return value or null if the query failed. + * + * @since 11.1 + * @throws RuntimeException + */ + public function loadRowList($key = null) + { + $this->connect(); + + $array = array(); + + // Execute the query and get the result set cursor. + if (!($cursor = $this->execute())) + { + return null; + } + + // Get all of the rows from the result set as arrays. + while ($row = $this->fetchArray($cursor)) + { + if ($key !== null) + { + $array[$row[$key]] = $row; + } + else + { + $array[] = $row; + } + } + + // Free up system resources and return. + $this->freeResult($cursor); + + return $array; + } + + /** + * Locks a table in the database. + * + * @param string $tableName The name of the table to unlock. + * + * @return FOFDatabaseDriver Returns this object to support chaining. + * + * @since 11.4 + * @throws RuntimeException + */ + public abstract function lockTable($tableName); + + /** + * Quotes and optionally escapes a string to database requirements for use in database queries. + * + * @param mixed $text A string or an array of strings to quote. + * @param boolean $escape True (default) to escape the string, false to leave it unchanged. + * + * @return string The quoted input string. + * + * @note Accepting an array of strings was added in 12.3. + * @since 11.1 + */ + public function quote($text, $escape = true) + { + if (is_array($text)) + { + foreach ($text as $k => $v) + { + $text[$k] = $this->quote($v, $escape); + } + + return $text; + } + else + { + return '\'' . ($escape ? $this->escape($text) : $text) . '\''; + } + } + + /** + * Wrap an SQL statement identifier name such as column, table or database names in quotes to prevent injection + * risks and reserved word conflicts. + * + * @param mixed $name The identifier name to wrap in quotes, or an array of identifier names to wrap in quotes. + * Each type supports dot-notation name. + * @param mixed $as The AS query part associated to $name. It can be string or array, in latter case it has to be + * same length of $name; if is null there will not be any AS part for string or array element. + * + * @return mixed The quote wrapped name, same type of $name. + * + * @since 11.1 + */ + public function quoteName($name, $as = null) + { + if (is_string($name)) + { + $quotedName = $this->quoteNameStr(explode('.', $name)); + + $quotedAs = ''; + + if (!is_null($as)) + { + settype($as, 'array'); + $quotedAs .= ' AS ' . $this->quoteNameStr($as); + } + + return $quotedName . $quotedAs; + } + else + { + $fin = array(); + + if (is_null($as)) + { + foreach ($name as $str) + { + $fin[] = $this->quoteName($str); + } + } + elseif (is_array($name) && (count($name) == count($as))) + { + $count = count($name); + + for ($i = 0; $i < $count; $i++) + { + $fin[] = $this->quoteName($name[$i], $as[$i]); + } + } + + return $fin; + } + } + + /** + * Quote strings coming from quoteName call. + * + * @param array $strArr Array of strings coming from quoteName dot-explosion. + * + * @return string Dot-imploded string of quoted parts. + * + * @since 11.3 + */ + protected function quoteNameStr($strArr) + { + $parts = array(); + $q = $this->nameQuote; + + foreach ($strArr as $part) + { + if (is_null($part)) + { + continue; + } + + if (strlen($q) == 1) + { + $parts[] = $q . $part . $q; + } + else + { + $parts[] = $q{0} . $part . $q{1}; + } + } + + return implode('.', $parts); + } + + /** + * This function replaces a string identifier $prefix with the string held is the + * tablePrefix class variable. + * + * @param string $sql The SQL statement to prepare. + * @param string $prefix The common table prefix. + * + * @return string The processed SQL statement. + * + * @since 11.1 + */ + public function replacePrefix($sql, $prefix = '#__') + { + $startPos = 0; + $literal = ''; + + $sql = trim($sql); + $n = strlen($sql); + + while ($startPos < $n) + { + $ip = strpos($sql, $prefix, $startPos); + + if ($ip === false) + { + break; + } + + $j = strpos($sql, "'", $startPos); + $k = strpos($sql, '"', $startPos); + + if (($k !== false) && (($k < $j) || ($j === false))) + { + $quoteChar = '"'; + $j = $k; + } + else + { + $quoteChar = "'"; + } + + if ($j === false) + { + $j = $n; + } + + $literal .= str_replace($prefix, $this->tablePrefix, substr($sql, $startPos, $j - $startPos)); + $startPos = $j; + + $j = $startPos + 1; + + if ($j >= $n) + { + break; + } + + // Quote comes first, find end of quote + while (true) + { + $k = strpos($sql, $quoteChar, $j); + $escaped = false; + + if ($k === false) + { + break; + } + + $l = $k - 1; + + while ($l >= 0 && $sql{$l} == '\\') + { + $l--; + $escaped = !$escaped; + } + + if ($escaped) + { + $j = $k + 1; + continue; + } + + break; + } + + if ($k === false) + { + // Error in the query - no end quote; ignore it + break; + } + + $literal .= substr($sql, $startPos, $k - $startPos + 1); + $startPos = $k + 1; + } + + if ($startPos < $n) + { + $literal .= substr($sql, $startPos, $n - $startPos); + } + + return $literal; + } + + /** + * Renames a table in the database. + * + * @param string $oldTable The name of the table to be renamed + * @param string $newTable The new name for the table. + * @param string $backup Table prefix + * @param string $prefix For the table - used to rename constraints in non-mysql databases + * + * @return FOFDatabaseDriver Returns this object to support chaining. + * + * @since 11.4 + * @throws RuntimeException + */ + public abstract function renameTable($oldTable, $newTable, $backup = null, $prefix = null); + + /** + * Select a database for use. + * + * @param string $database The name of the database to select for use. + * + * @return boolean True if the database was successfully selected. + * + * @since 11.1 + * @throws RuntimeException + */ + abstract public function select($database); + + /** + * Sets the database debugging state for the driver. + * + * @param boolean $level True to enable debugging. + * + * @return boolean The old debugging level. + * + * @since 11.1 + */ + public function setDebug($level) + { + $previous = $this->debug; + $this->debug = (bool) $level; + + return $previous; + } + + /** + * Sets the SQL statement string for later execution. + * + * @param mixed $query The SQL statement to set either as a FOFDatabaseQuery object or a string. + * @param integer $offset The affected row offset to set. + * @param integer $limit The maximum affected rows to set. + * + * @return FOFDatabaseDriver This object to support method chaining. + * + * @since 11.1 + */ + public function setQuery($query, $offset = 0, $limit = 0) + { + $this->sql = $query; + + if ($query instanceof FOFDatabaseQueryLimitable) + { + if (!$limit && $query->limit) + { + $limit = $query->limit; + } + + if (!$offset && $query->offset) + { + $offset = $query->offset; + } + + $query->setLimit($limit, $offset); + } + else + { + $this->limit = (int) max(0, $limit); + $this->offset = (int) max(0, $offset); + } + + return $this; + } + + /** + * Set the connection to use UTF-8 character encoding. + * + * @return boolean True on success. + * + * @since 11.1 + */ + abstract public function setUtf(); + + /** + * Method to commit a transaction. + * + * @param boolean $toSavepoint If true, commit to the last savepoint. + * + * @return void + * + * @since 11.1 + * @throws RuntimeException + */ + abstract public function transactionCommit($toSavepoint = false); + + /** + * Method to roll back a transaction. + * + * @param boolean $toSavepoint If true, rollback to the last savepoint. + * + * @return void + * + * @since 11.1 + * @throws RuntimeException + */ + abstract public function transactionRollback($toSavepoint = false); + + /** + * Method to initialize a transaction. + * + * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. + * + * @return void + * + * @since 11.1 + * @throws RuntimeException + */ + abstract public function transactionStart($asSavepoint = false); + + /** + * Method to truncate a table. + * + * @param string $table The table to truncate + * + * @return void + * + * @since 11.3 + * @throws RuntimeException + */ + public function truncateTable($table) + { + $this->setQuery('TRUNCATE TABLE ' . $this->quoteName($table)); + $this->execute(); + } + + /** + * Updates a row in a table based on an object's properties. + * + * @param string $table The name of the database table to update. + * @param object &$object A reference to an object whose public properties match the table fields. + * @param array $key The name of the primary key. + * @param boolean $nulls True to update null fields or false to ignore them. + * + * @return boolean True on success. + * + * @since 11.1 + * @throws RuntimeException + */ + public function updateObject($table, &$object, $key, $nulls = false) + { + $fields = array(); + $where = array(); + + if (is_string($key)) + { + $key = array($key); + } + + if (is_object($key)) + { + $key = (array) $key; + } + + // Create the base update statement. + $statement = 'UPDATE ' . $this->quoteName($table) . ' SET %s WHERE %s'; + + // Iterate over the object variables to build the query fields/value pairs. + foreach (get_object_vars($object) as $k => $v) + { + // Only process scalars that are not internal fields. + if (is_array($v) or is_object($v) or $k[0] == '_') + { + continue; + } + + // Set the primary key to the WHERE clause instead of a field to update. + if (in_array($k, $key)) + { + $where[] = $this->quoteName($k) . '=' . $this->quote($v); + continue; + } + + // Prepare and sanitize the fields and values for the database query. + if ($v === null) + { + // If the value is null and we want to update nulls then set it. + if ($nulls) + { + $val = 'NULL'; + } + // If the value is null and we do not want to update nulls then ignore this field. + else + { + continue; + } + } + // The field is not null so we prep it for update. + else + { + $val = $this->quote($v); + } + + // Add the field to be updated. + $fields[] = $this->quoteName($k) . '=' . $val; + } + + // We don't have any fields to update. + if (empty($fields)) + { + return true; + } + + // Set the query and execute the update. + $this->setQuery(sprintf($statement, implode(",", $fields), implode(' AND ', $where))); + + return $this->execute(); + } + + /** + * Execute the SQL statement. + * + * @return mixed A database cursor resource on success, boolean false on failure. + * + * @since 12.1 + * @throws RuntimeException + */ + abstract public function execute(); + + /** + * Unlocks tables in the database. + * + * @return FOFDatabaseDriver Returns this object to support chaining. + * + * @since 11.4 + * @throws RuntimeException + */ + public abstract function unlockTables(); +} diff --git a/libraries/fof/database/driver/joomla.php b/libraries/fof/database/driver/joomla.php new file mode 100644 index 0000000000000..63b67627fec77 --- /dev/null +++ b/libraries/fof/database/driver/joomla.php @@ -0,0 +1,778 @@ +dbo = JFactory::getDbo(); + + $reflection = new ReflectionClass($this->dbo); + + try + { + $refProp = $reflection->getProperty('nameQuote'); + $refProp->setAccessible(true); + $this->nameQuote = $refProp->getValue($this->dbo); + } + catch (Exception $e) + { + $this->nameQuote = '`'; + } + } + + public function close() + { + if (method_exists($this->dbo, 'close')) + { + $this->dbo->close(); + } + elseif (method_exists($this->dbo, 'disconnect')) + { + $this->dbo->disconnect(); + } + } + + public function disconnect() + { + $this->close(); + } + + public function open() + { + if (method_exists($this->dbo, 'open')) + { + $this->dbo->open(); + } + elseif (method_exists($this->dbo, 'connect')) + { + $this->dbo->connect(); + } + } + + public function connect() + { + $this->open(); + } + + public function connected() + { + if (method_exists($this->dbo, 'connected')) + { + return $this->dbo->connected(); + } + + return true; + } + + public function escape($text, $extra = false) + { + return $this->dbo->escape($text, $extra); + } + + public function execute() + { + if (method_exists($this->dbo, 'execute')) + { + return $this->dbo->execute(); + } + + return $this->dbo->query(); + } + + public function getAffectedRows() + { + if (method_exists($this->dbo, 'getAffectedRows')) + { + return $this->dbo->getAffectedRows(); + } + + return 0; + } + + public function getCollation() + { + if (method_exists($this->dbo, 'getCollation')) + { + return $this->dbo->getCollation(); + } + + return 'utf8_general_ci'; + } + + public function getConnection() + { + if (method_exists($this->dbo, 'getConnection')) + { + return $this->dbo->getConnection(); + } + + return null; + } + + public function getCount() + { + if (method_exists($this->dbo, 'getCount')) + { + return $this->dbo->getCount(); + } + + return 0; + } + + public function getDateFormat() + { + if (method_exists($this->dbo, 'getDateFormat')) + { + return $this->dbo->getDateFormat(); + } + + return 'Y-m-d H:i:s';; + } + + public function getMinimum() + { + if (method_exists($this->dbo, 'getMinimum')) + { + return $this->dbo->getMinimum(); + } + + return '5.0.40'; + } + + public function getNullDate() + { + if (method_exists($this->dbo, 'getNullDate')) + { + return $this->dbo->getNullDate(); + } + + return '0000-00-00 00:00:00'; + } + + public function getNumRows($cursor = null) + { + if (method_exists($this->dbo, 'getNumRows')) + { + return $this->dbo->getNumRows($cursor); + } + + return 0; + } + + public function getQuery($new = false) + { + if (method_exists($this->dbo, 'getQuery')) + { + return $this->dbo->getQuery($new); + } + + return null; + } + + public function getTableColumns($table, $typeOnly = true) + { + if (method_exists($this->dbo, 'getTableColumns')) + { + return $this->dbo->getTableColumns($table, $typeOnly); + } + + $result = $this->dbo->getTableFields(array($table), $typeOnly); + + return $result[$table]; + } + + public function getTableKeys($tables) + { + if (method_exists($this->dbo, 'getTableKeys')) + { + return $this->dbo->getTableKeys($tables); + } + + return array(); + } + + public function getTableList() + { + if (method_exists($this->dbo, 'getTableList')) + { + return $this->dbo->getTableList(); + } + + return array(); + } + + public function getVersion() + { + if (method_exists($this->dbo, 'getVersion')) + { + return $this->dbo->getVersion(); + } + + return '5.0.40'; + } + + public function insertid() + { + if (method_exists($this->dbo, 'insertid')) + { + return $this->dbo->insertid(); + } + + return null; + } + + public function insertObject($table, &$object, $key = null) + { + if (method_exists($this->dbo, 'insertObject')) + { + return $this->dbo->insertObject($table, $object, $key); + } + + return null; + } + + public function loadAssoc() + { + if (method_exists($this->dbo, 'loadAssoc')) + { + return $this->dbo->loadAssoc(); + } + + return null; + } + + public function loadAssocList($key = null, $column = null) + { + if (method_exists($this->dbo, 'loadAssocList')) + { + return $this->dbo->loadAssocList($key, $column); + } + + return null; + } + + public function loadObject($class = 'stdClass') + { + if (method_exists($this->dbo, 'loadObject')) + { + return $this->dbo->loadObject($class); + } + + return null; + } + + public function loadObjectList($key = '', $class = 'stdClass') + { + if (method_exists($this->dbo, 'loadObjectList')) + { + return $this->dbo->loadObjectList($key, $class); + } + + return null; + } + + public function loadResult() + { + if (method_exists($this->dbo, 'loadResult')) + { + return $this->dbo->loadResult(); + } + + return null; + } + + public function loadRow() + { + if (method_exists($this->dbo, 'loadRow')) + { + return $this->dbo->loadRow(); + } + + return null; + } + + public function loadRowList($key = null) + { + if (method_exists($this->dbo, 'loadRowList')) + { + return $this->dbo->loadRowList($key); + } + + return null; + } + + public function lockTable($tableName) + { + if (method_exists($this->dbo, 'lockTable')) + { + return $this->dbo->lockTable($this); + } + + return $this; + } + + public function quote($text, $escape = true) + { + if (method_exists($this->dbo, 'quote')) + { + return $this->dbo->quote($text, $escape); + } + + return $text; + } + + public function select($database) + { + if (method_exists($this->dbo, 'select')) + { + return $this->dbo->select($database); + } + + return false; + } + + public function setQuery($query, $offset = 0, $limit = 0) + { + if (method_exists($this->dbo, 'setQuery')) + { + return $this->dbo->setQuery($query, $offset, $limit); + } + + return false; + } + + public function transactionCommit($toSavepoint = false) + { + if (method_exists($this->dbo, 'transactionCommit')) + { + $this->dbo->transactionCommit($toSavepoint); + } + } + + public function transactionRollback($toSavepoint = false) + { + if (method_exists($this->dbo, 'transactionRollback')) + { + $this->dbo->transactionRollback($toSavepoint); + } + } + + public function transactionStart($asSavepoint = false) + { + if (method_exists($this->dbo, 'transactionStart')) + { + $this->dbo->transactionStart($asSavepoint); + } + } + + public function unlockTables() + { + if (method_exists($this->dbo, 'unlockTables')) + { + return $this->dbo->unlockTables(); + } + + return $this; + } + + public function updateObject($table, &$object, $key, $nulls = false) + { + if (method_exists($this->dbo, 'updateObject')) + { + return $this->dbo->updateObject($table, $object, $key, $nulls); + } + + return false; + } + + public function getLog() + { + if (method_exists($this->dbo, 'getLog')) + { + return $this->dbo->getLog(); + } + + return array(); + } + + public function dropTable($table, $ifExists = true) + { + if (method_exists($this->dbo, 'dropTable')) + { + return $this->dbo->dropTable($table, $ifExists); + } + + return $this; + } + + public function getTableCreate($tables) + { + if (method_exists($this->dbo, 'getTableCreate')) + { + return $this->dbo->getTableCreate($tables); + } + + return array(); + } + + public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) + { + if (method_exists($this->dbo, 'renameTable')) + { + return $this->dbo->renameTable($oldTable, $newTable, $backup, $prefix); + } + + return $this; + } + + public function setUtf() + { + if (method_exists($this->dbo, 'setUtf')) + { + return $this->dbo->setUtf(); + } + + return false; + } + + + protected function freeResult($cursor = null) + { + return false; + } + + /** + * Method to get an array of values from the $offset field in each row of the result set from + * the database query. + * + * @param integer $offset The row offset to use to build the result array. + * + * @return mixed The return value or null if the query failed. + * + * @since 11.1 + * @throws RuntimeException + */ + public function loadColumn($offset = 0) + { + if (method_exists($this->dbo, 'loadColumn')) + { + return $this->dbo->loadColumn($offset); + } + + return $this->dbo->loadResultArray($offset); + } + + /** + * Wrap an SQL statement identifier name such as column, table or database names in quotes to prevent injection + * risks and reserved word conflicts. + * + * @param mixed $name The identifier name to wrap in quotes, or an array of identifier names to wrap in quotes. + * Each type supports dot-notation name. + * @param mixed $as The AS query part associated to $name. It can be string or array, in latter case it has to be + * same length of $name; if is null there will not be any AS part for string or array element. + * + * @return mixed The quote wrapped name, same type of $name. + * + * @since 11.1 + */ + public function quoteName($name, $as = null) + { + if (is_string($name)) + { + $quotedName = $this->quoteNameStr(explode('.', $name)); + + $quotedAs = ''; + + if (!is_null($as)) + { + settype($as, 'array'); + $quotedAs .= ' AS ' . $this->quoteNameStr($as); + } + + return $quotedName . $quotedAs; + } + else + { + $fin = array(); + + if (is_null($as)) + { + foreach ($name as $str) + { + $fin[] = $this->quoteName($str); + } + } + elseif (is_array($name) && (count($name) == count($as))) + { + $count = count($name); + + for ($i = 0; $i < $count; $i++) + { + $fin[] = $this->quoteName($name[$i], $as[$i]); + } + } + + return $fin; + } + } + + /** + * Quote strings coming from quoteName call. + * + * @param array $strArr Array of strings coming from quoteName dot-explosion. + * + * @return string Dot-imploded string of quoted parts. + * + * @since 11.3 + */ + protected function quoteNameStr($strArr) + { + $parts = array(); + $q = $this->nameQuote; + + foreach ($strArr as $part) + { + if (is_null($part)) + { + continue; + } + + if (strlen($q) == 1) + { + $parts[] = $q . $part . $q; + } + else + { + $parts[] = $q{0} . $part . $q{1}; + } + } + + return implode('.', $parts); + } + + /** + * Gets the error message from the database connection. + * + * @param boolean $escaped True to escape the message string for use in JavaScript. + * + * @return string The error message for the most recent query. + * + * @since 11.1 + */ + public function getErrorMsg($escaped = false) + { + if (method_exists($this->dbo, 'getErrorMsg')) + { + $errorMessage = $this->dbo->getErrorMsg(); + } + else + { + $errorMessage = $this->errorMsg; + } + + if ($escaped) + { + return addslashes($errorMessage); + } + + return $errorMessage; + } + + /** + * Gets the error number from the database connection. + * + * @return integer The error number for the most recent query. + * + * @since 11.1 + * @deprecated 13.3 (Platform) & 4.0 (CMS) + */ + public function getErrorNum() + { + if (method_exists($this->dbo, 'getErrorNum')) + { + $errorNum = $this->dbo->getErrorNum(); + } + else + { + $errorNum = $this->getErrorNum; + } + + return $errorNum; + } + + /** + * Return the most recent error message for the database connector. + * + * @param boolean $showSQL True to display the SQL statement sent to the database as well as the error. + * + * @return string The error message for the most recent query. + */ + public function stderr($showSQL = false) + { + if (method_exists($this->dbo, 'stderr')) + { + return $this->dbo->stderr($showSQL); + } + + return parent::stderr($showSQL); + } + + /** + * Magic method to proxy all calls to the loaded database driver object + */ + public function __call($name, array $arguments) + { + if (is_null($this->dbo)) + { + throw new Exception('FOF database driver is not loaded'); + } + + if (method_exists($this->dbo, $name) || in_array($name, array('q', 'nq', 'qn', 'query'))) + { + switch ($name) + { + case 'execute': + $name = 'query'; + break; + + case 'q': + $name = 'quote'; + break; + + case 'qn': + case 'nq': + switch (count($arguments)) + { + case 0 : + $result = $this->quoteName(); + break; + case 1 : + $result = $this->quoteName($arguments[0]); + break; + case 2: + default: + $result = $this->quoteName($arguments[0], $arguments[1]); + break; + } + return $result; + + break; + } + + switch (count($arguments)) + { + case 0 : + $result = $this->dbo->$name(); + break; + case 1 : + $result = $this->dbo->$name($arguments[0]); + break; + case 2: + $result = $this->dbo->$name($arguments[0], $arguments[1]); + break; + case 3: + $result = $this->dbo->$name($arguments[0], $arguments[1], $arguments[2]); + break; + case 4: + $result = $this->dbo->$name($arguments[0], $arguments[1], $arguments[2], $arguments[3]); + break; + case 5: + $result = $this->dbo->$name($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4]); + break; + default: + // Resort to using call_user_func_array for many segments + $result = call_user_func_array(array($this->dbo, $name), $arguments); + } + + if (class_exists('JDatabase') && is_object($result) && ($result instanceof JDatabase)) + { + return $this; + } + + return $result; + } + else + { + throw new \Exception('Method ' . $name . ' not found in FOFDatabase'); + } + } + + public function __get($name) + { + if (isset($this->dbo->$name) || property_exists($this->dbo, $name)) + { + return $this->dbo->$name; + } + else + { + $this->dbo->$name = null; + user_error('Database driver does not support property ' . $name); + } + } + + public function __set($name, $value) + { + if (isset($this->dbo->name) || property_exists($this->dbo, $name)) + { + $this->dbo->$name = $value; + } + else + { + $this->dbo->$name = null; + user_error('Database driver not support property ' . $name); + } + } +} diff --git a/libraries/fof/database/driver/mysql.php b/libraries/fof/database/driver/mysql.php new file mode 100644 index 0000000000000..5e35879f3b0ee --- /dev/null +++ b/libraries/fof/database/driver/mysql.php @@ -0,0 +1,577 @@ += 7) + { + throw new RuntimeException( + 'This driver is unsupported in PHP 7, please use the MySQLi or PDO MySQL driver instead.' + ); + } + + // Get some basic values from the options. + $options['host'] = (isset($options['host'])) ? $options['host'] : 'localhost'; + $options['user'] = (isset($options['user'])) ? $options['user'] : 'root'; + $options['password'] = (isset($options['password'])) ? $options['password'] : ''; + $options['database'] = (isset($options['database'])) ? $options['database'] : ''; + $options['select'] = (isset($options['select'])) ? (bool) $options['select'] : true; + + // Finalize initialisation. + parent::__construct($options); + } + + /** + * Destructor. + * + * @since 12.1 + */ + public function __destruct() + { + $this->disconnect(); + } + + /** + * Connects to the database if needed. + * + * @return void Returns void if the database connected successfully. + * + * @since 12.1 + * @throws RuntimeException + */ + public function connect() + { + if ($this->connection) + { + return; + } + + // Make sure the MySQL extension for PHP is installed and enabled. + if (!self::isSupported()) + { + throw new RuntimeException('Could not connect to MySQL.'); + } + + // Attempt to connect to the server. + if (!($this->connection = @ mysql_connect($this->options['host'], $this->options['user'], $this->options['password'], true))) + { + throw new RuntimeException('Could not connect to MySQL.'); + } + + // Set sql_mode to non_strict mode + mysql_query("SET @@SESSION.sql_mode = '';", $this->connection); + + // If auto-select is enabled select the given database. + if ($this->options['select'] && !empty($this->options['database'])) + { + $this->select($this->options['database']); + } + + // Pre-populate the UTF-8 Multibyte compatibility flag based on server version + $this->utf8mb4 = $this->serverClaimsUtf8mb4Support(); + + // Set the character set (needed for MySQL 4.1.2+). + $this->utf = $this->setUtf(); + + // Turn MySQL profiling ON in debug mode: + if ($this->debug && $this->hasProfiling()) + { + mysql_query("SET profiling = 1;", $this->connection); + } + } + + /** + * Disconnects the database. + * + * @return void + * + * @since 12.1 + */ + public function disconnect() + { + // Close the connection. + if (is_resource($this->connection)) + { + foreach ($this->disconnectHandlers as $h) + { + call_user_func_array($h, array( &$this)); + } + + mysql_close($this->connection); + } + + $this->connection = null; + } + + /** + * Method to escape a string for usage in an SQL statement. + * + * @param string $text The string to be escaped. + * @param boolean $extra Optional parameter to provide extra escaping. + * + * @return string The escaped string. + * + * @since 12.1 + */ + public function escape($text, $extra = false) + { + $this->connect(); + + $result = mysql_real_escape_string($text, $this->getConnection()); + + if ($extra) + { + $result = addcslashes($result, '%_'); + } + + return $result; + } + + /** + * Test to see if the MySQL connector is available. + * + * @return boolean True on success, false otherwise. + * + * @since 12.1 + */ + public static function isSupported() + { + return (function_exists('mysql_connect')); + } + + /** + * Determines if the connection to the server is active. + * + * @return boolean True if connected to the database engine. + * + * @since 12.1 + */ + public function connected() + { + if (is_resource($this->connection)) + { + return @mysql_ping($this->connection); + } + + return false; + } + + /** + * Get the number of affected rows by the last INSERT, UPDATE, REPLACE or DELETE for the previous executed SQL statement. + * + * @return integer The number of affected rows. + * + * @since 12.1 + */ + public function getAffectedRows() + { + $this->connect(); + + return mysql_affected_rows($this->connection); + } + + /** + * Get the number of returned rows for the previous executed SQL statement. + * This command is only valid for statements like SELECT or SHOW that return an actual result set. + * To retrieve the number of rows affected by a INSERT, UPDATE, REPLACE or DELETE query, use getAffectedRows(). + * + * @param resource $cursor An optional database cursor resource to extract the row count from. + * + * @return integer The number of returned rows. + * + * @since 12.1 + */ + public function getNumRows($cursor = null) + { + $this->connect(); + + return mysql_num_rows($cursor ? $cursor : $this->cursor); + } + + /** + * Get the version of the database connector. + * + * @return string The database connector version. + * + * @since 12.1 + */ + public function getVersion() + { + $this->connect(); + + return mysql_get_server_info($this->connection); + } + + /** + * Method to get the auto-incremented value from the last INSERT statement. + * + * @return integer The value of the auto-increment field from the last inserted row. + * + * @since 12.1 + */ + public function insertid() + { + $this->connect(); + + return mysql_insert_id($this->connection); + } + + /** + * Execute the SQL statement. + * + * @return mixed A database cursor resource on success, boolean false on failure. + * + * @since 12.1 + * @throws RuntimeException + */ + public function execute() + { + $this->connect(); + + if (!is_resource($this->connection)) + { + if (class_exists('JLog')) + { + JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); + } + throw new RuntimeException($this->errorMsg, $this->errorNum); + } + + // Take a local copy so that we don't modify the original query and cause issues later + $query = $this->replacePrefix((string) $this->sql); + + if (!($this->sql instanceof FOFDatabaseQuery) && ($this->limit > 0 || $this->offset > 0)) + { + $query .= ' LIMIT ' . $this->offset . ', ' . $this->limit; + } + + // Increment the query counter. + $this->count++; + + // Reset the error values. + $this->errorNum = 0; + $this->errorMsg = ''; + + // If debugging is enabled then let's log the query. + if ($this->debug) + { + // Add the query to the object queue. + $this->log[] = $query; + + if (class_exists('JLog')) + { + JLog::add($query, JLog::DEBUG, 'databasequery'); + } + + $this->timings[] = microtime(true); + } + + // Execute the query. Error suppression is used here to prevent warnings/notices that the connection has been lost. + $this->cursor = @mysql_query($query, $this->connection); + + if ($this->debug) + { + $this->timings[] = microtime(true); + + if (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) + { + $this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + } + else + { + $this->callStacks[] = debug_backtrace(); + } + } + + // If an error occurred handle it. + if (!$this->cursor) + { + // Get the error number and message before we execute any more queries. + $this->errorNum = $this->getErrorNumber(); + $this->errorMsg = $this->getErrorMessage($query); + + // Check if the server was disconnected. + if (!$this->connected()) + { + try + { + // Attempt to reconnect. + $this->connection = null; + $this->connect(); + } + // If connect fails, ignore that exception and throw the normal exception. + catch (RuntimeException $e) + { + // Get the error number and message. + $this->errorNum = $this->getErrorNumber(); + $this->errorMsg = $this->getErrorMessage($query); + + // Throw the normal query exception. + if (class_exists('JLog')) + { + JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); + } + + throw new RuntimeException($this->errorMsg, $this->errorNum, $e); + } + + // Since we were able to reconnect, run the query again. + return $this->execute(); + } + // The server was not disconnected. + else + { + // Throw the normal query exception. + if (class_exists('JLog')) + { + JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); + } + + throw new RuntimeException($this->errorMsg, $this->errorNum); + } + } + + return $this->cursor; + } + + /** + * Select a database for use. + * + * @param string $database The name of the database to select for use. + * + * @return boolean True if the database was successfully selected. + * + * @since 12.1 + * @throws RuntimeException + */ + public function select($database) + { + $this->connect(); + + if (!$database) + { + return false; + } + + if (!mysql_select_db($database, $this->connection)) + { + throw new RuntimeException('Could not connect to database'); + } + + return true; + } + + /** + * Set the connection to use UTF-8 character encoding. + * + * @return boolean True on success. + * + * @since 12.1 + */ + public function setUtf() + { + // If UTF is not supported return false immediately + if (!$this->utf) + { + return false; + } + + // Make sure we're connected to the server + $this->connect(); + + // Which charset should I use, plain utf8 or multibyte utf8mb4? + $charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8'; + + $result = @mysql_set_charset($charset, $this->connection); + + /** + * If I could not set the utf8mb4 charset then the server doesn't support utf8mb4 despite claiming otherwise. + * This happens on old MySQL server versions (less than 5.5.3) using the mysqlnd PHP driver. Since mysqlnd + * masks the server version and reports only its own we can not be sure if the server actually does support + * UTF-8 Multibyte (i.e. it's MySQL 5.5.3 or later). Since the utf8mb4 charset is undefined in this case we + * catch the error and determine that utf8mb4 is not supported! + */ + if (!$result && $this->utf8mb4) + { + $this->utf8mb4 = false; + $result = @mysql_set_charset('utf8', $this->connection); + } + + return $result; + } + + /** + * Method to fetch a row from the result set cursor as an array. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * + * @return mixed Either the next row from the result set or false if there are no more rows. + * + * @since 12.1 + */ + protected function fetchArray($cursor = null) + { + return mysql_fetch_row($cursor ? $cursor : $this->cursor); + } + + /** + * Method to fetch a row from the result set cursor as an associative array. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * + * @return mixed Either the next row from the result set or false if there are no more rows. + * + * @since 12.1 + */ + protected function fetchAssoc($cursor = null) + { + return mysql_fetch_assoc($cursor ? $cursor : $this->cursor); + } + + /** + * Method to fetch a row from the result set cursor as an object. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * @param string $class The class name to use for the returned row object. + * + * @return mixed Either the next row from the result set or false if there are no more rows. + * + * @since 12.1 + */ + protected function fetchObject($cursor = null, $class = 'stdClass') + { + return mysql_fetch_object($cursor ? $cursor : $this->cursor, $class); + } + + /** + * Method to free up the memory used for the result set. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * + * @return void + * + * @since 12.1 + */ + protected function freeResult($cursor = null) + { + mysql_free_result($cursor ? $cursor : $this->cursor); + } + + /** + * Internal function to check if profiling is available + * + * @return boolean + * + * @since 3.1.3 + */ + private function hasProfiling() + { + try + { + $res = mysql_query("SHOW VARIABLES LIKE 'have_profiling'", $this->connection); + $row = mysql_fetch_assoc($res); + + return isset($row); + } + catch (Exception $e) + { + return false; + } + } + + /** + * Does the database server claim to have support for UTF-8 Multibyte (utf8mb4) collation? + * + * libmysql supports utf8mb4 since 5.5.3 (same version as the MySQL server). mysqlnd supports utf8mb4 since 5.0.9. + * + * @return boolean + * + * @since CMS 3.5.0 + */ + private function serverClaimsUtf8mb4Support() + { + $client_version = mysql_get_client_info(); + + if (strpos($client_version, 'mysqlnd') !== false) + { + $client_version = preg_replace('/^\D+([\d.]+).*/', '$1', $client_version); + + return version_compare($client_version, '5.0.9', '>='); + } + else + { + return version_compare($client_version, '5.5.3', '>='); + } + } + + /** + * Return the actual SQL Error number + * + * @return integer The SQL Error number + * + * @since 3.4.6 + */ + protected function getErrorNumber() + { + return (int) mysql_errno($this->connection); + } + + /** + * Return the actual SQL Error message + * + * @param string $query The SQL Query that fails + * + * @return string The SQL Error message + * + * @since 3.4.6 + */ + protected function getErrorMessage($query) + { + $errorMessage = (string) mysql_error($this->connection); + + // Replace the Databaseprefix with `#__` if we are not in Debug + if (!$this->debug) + { + $errorMessage = str_replace($this->tablePrefix, '#__', $errorMessage); + $query = str_replace($this->tablePrefix, '#__', $query); + } + + return $errorMessage . ' SQL=' . $query; + } +} diff --git a/libraries/fof/database/driver/mysqli.php b/libraries/fof/database/driver/mysqli.php new file mode 100644 index 0000000000000..bc271f1c3b970 --- /dev/null +++ b/libraries/fof/database/driver/mysqli.php @@ -0,0 +1,1019 @@ +disconnect(); + } + + /** + * Connects to the database if needed. + * + * @return void Returns void if the database connected successfully. + * + * @since 12.1 + * @throws RuntimeException + */ + public function connect() + { + if ($this->connection) + { + return; + } + + /* + * Unlike mysql_connect(), mysqli_connect() takes the port and socket as separate arguments. Therefore, we + * have to extract them from the host string. + */ + $port = isset($this->options['port']) ? $this->options['port'] : 3306; + $regex = '/^(?P((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))(:(?P.+))?$/'; + + if (preg_match($regex, $this->options['host'], $matches)) + { + // It's an IPv4 address with ot without port + $this->options['host'] = $matches['host']; + + if (!empty($matches['port'])) + { + $port = $matches['port']; + } + } + elseif (preg_match('/^(?P\[.*\])(:(?P.+))?$/', $this->options['host'], $matches)) + { + // We assume square-bracketed IPv6 address with or without port, e.g. [fe80:102::2%eth1]:3306 + $this->options['host'] = $matches['host']; + + if (!empty($matches['port'])) + { + $port = $matches['port']; + } + } + elseif (preg_match('/^(?P(\w+:\/{2,3})?[a-z0-9\.\-]+)(:(?P[^:]+))?$/i', $this->options['host'], $matches)) + { + // Named host (e.g domain.com or localhost) with ot without port + $this->options['host'] = $matches['host']; + + if (!empty($matches['port'])) + { + $port = $matches['port']; + } + } + elseif (preg_match('/^:(?P[^:]+)$/', $this->options['host'], $matches)) + { + // Empty host, just port, e.g. ':3306' + $this->options['host'] = 'localhost'; + $port = $matches['port']; + } + // ... else we assume normal (naked) IPv6 address, so host and port stay as they are or default + + // Get the port number or socket name + if (is_numeric($port)) + { + $this->options['port'] = (int) $port; + } + else + { + $this->options['socket'] = $port; + } + + // Make sure the MySQLi extension for PHP is installed and enabled. + if (!function_exists('mysqli_connect')) + { + throw new RuntimeException('The MySQL adapter mysqli is not available'); + } + + $this->connection = @mysqli_connect( + $this->options['host'], $this->options['user'], $this->options['password'], null, $this->options['port'], $this->options['socket'] + ); + + // Attempt to connect to the server. + if (!$this->connection) + { + throw new RuntimeException('Could not connect to MySQL.'); + } + + // Set sql_mode to non_strict mode + mysqli_query($this->connection, "SET @@SESSION.sql_mode = '';"); + + // If auto-select is enabled select the given database. + if ($this->options['select'] && !empty($this->options['database'])) + { + $this->select($this->options['database']); + } + + // Pre-populate the UTF-8 Multibyte compatibility flag based on server version + $this->utf8mb4 = $this->serverClaimsUtf8mb4Support(); + + // Set the character set (needed for MySQL 4.1.2+). + $this->utf = $this->setUtf(); + + // Turn MySQL profiling ON in debug mode: + if ($this->debug && $this->hasProfiling()) + { + mysqli_query($this->connection, "SET profiling_history_size = 100;"); + mysqli_query($this->connection, "SET profiling = 1;"); + } + } + + /** + * Disconnects the database. + * + * @return void + * + * @since 12.1 + */ + public function disconnect() + { + // Close the connection. + if ($this->connection) + { + foreach ($this->disconnectHandlers as $h) + { + call_user_func_array($h, array( &$this)); + } + + mysqli_close($this->connection); + } + + $this->connection = null; + } + + /** + * Method to escape a string for usage in an SQL statement. + * + * @param string $text The string to be escaped. + * @param boolean $extra Optional parameter to provide extra escaping. + * + * @return string The escaped string. + * + * @since 12.1 + */ + public function escape($text, $extra = false) + { + $this->connect(); + + $result = mysqli_real_escape_string($this->getConnection(), $text); + + if ($extra) + { + $result = addcslashes($result, '%_'); + } + + return $result; + } + + /** + * Test to see if the MySQL connector is available. + * + * @return boolean True on success, false otherwise. + * + * @since 12.1 + */ + public static function isSupported() + { + return (function_exists('mysqli_connect')); + } + + /** + * Determines if the connection to the server is active. + * + * @return boolean True if connected to the database engine. + * + * @since 12.1 + */ + public function connected() + { + if (is_object($this->connection)) + { + return mysqli_ping($this->connection); + } + + return false; + } + + /** + * Drops a table from the database. + * + * @param string $tableName The name of the database table to drop. + * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. + * + * @return FOFDatabaseDriverMysqli Returns this object to support chaining. + * + * @since 12.2 + * @throws RuntimeException + */ + public function dropTable($tableName, $ifExists = true) + { + $this->connect(); + + $query = $this->getQuery(true); + + $this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $query->quoteName($tableName)); + + $this->execute(); + + return $this; + } + + /** + * Get the number of affected rows by the last INSERT, UPDATE, REPLACE or DELETE for the previous executed SQL statement. + * + * @return integer The number of affected rows. + * + * @since 12.1 + */ + public function getAffectedRows() + { + $this->connect(); + + return mysqli_affected_rows($this->connection); + } + + /** + * Method to get the database collation. + * + * @return mixed The collation in use by the database (string) or boolean false if not supported. + * + * @since 12.2 + * @throws RuntimeException + */ + public function getCollation() + { + $this->connect(); + + // Attempt to get the database collation by accessing the server system variable. + $this->setQuery('SHOW VARIABLES LIKE "collation_database"'); + $result = $this->loadObject(); + + if (property_exists($result, 'Value')) + { + return $result->Value; + } + else + { + return false; + } + } + + /** + * Method to get the database connection collation, as reported by the driver. If the connector doesn't support + * reporting this value please return an empty string. + * + * @return string + */ + public function getConnectionCollation() + { + $this->connect(); + + // Attempt to get the database collation by accessing the server system variable. + $this->setQuery('SHOW VARIABLES LIKE "collation_connection"'); + $result = $this->loadObject(); + + if (property_exists($result, 'Value')) + { + return $result->Value; + } + else + { + return false; + } + } + + /** + * Get the number of returned rows for the previous executed SQL statement. + * This command is only valid for statements like SELECT or SHOW that return an actual result set. + * To retrieve the number of rows affected by a INSERT, UPDATE, REPLACE or DELETE query, use getAffectedRows(). + * + * @param resource $cursor An optional database cursor resource to extract the row count from. + * + * @return integer The number of returned rows. + * + * @since 12.1 + */ + public function getNumRows($cursor = null) + { + return mysqli_num_rows($cursor ? $cursor : $this->cursor); + } + + /** + * Shows the table CREATE statement that creates the given tables. + * + * @param mixed $tables A table name or a list of table names. + * + * @return array A list of the create SQL for the tables. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getTableCreate($tables) + { + $this->connect(); + + $result = array(); + + // Sanitize input to an array and iterate over the list. + settype($tables, 'array'); + + foreach ($tables as $table) + { + // Set the query to get the table CREATE statement. + $this->setQuery('SHOW CREATE table ' . $this->quoteName($this->escape($table))); + $row = $this->loadRow(); + + // Populate the result array based on the create statements. + $result[$table] = $row[1]; + } + + return $result; + } + + /** + * Retrieves field information about a given table. + * + * @param string $table The name of the database table. + * @param boolean $typeOnly True to only return field types. + * + * @return array An array of fields for the database table. + * + * @since 12.2 + * @throws RuntimeException + */ + public function getTableColumns($table, $typeOnly = true) + { + $this->connect(); + + $result = array(); + + // Set the query to get the table fields statement. + $this->setQuery('SHOW FULL COLUMNS FROM ' . $this->quoteName($this->escape($table))); + $fields = $this->loadObjectList(); + + // If we only want the type as the value add just that to the list. + if ($typeOnly) + { + foreach ($fields as $field) + { + $result[$field->Field] = preg_replace("/[(0-9)]/", '', $field->Type); + } + } + // If we want the whole field data object add that to the list. + else + { + foreach ($fields as $field) + { + $result[$field->Field] = $field; + } + } + + return $result; + } + + /** + * Get the details list of keys for a table. + * + * @param string $table The name of the table. + * + * @return array An array of the column specification for the table. + * + * @since 12.2 + * @throws RuntimeException + */ + public function getTableKeys($table) + { + $this->connect(); + + // Get the details columns information. + $this->setQuery('SHOW KEYS FROM ' . $this->quoteName($table)); + $keys = $this->loadObjectList(); + + return $keys; + } + + /** + * Method to get an array of all tables in the database. + * + * @return array An array of all the tables in the database. + * + * @since 12.2 + * @throws RuntimeException + */ + public function getTableList() + { + $this->connect(); + + // Set the query to get the tables statement. + $this->setQuery('SHOW TABLES'); + $tables = $this->loadColumn(); + + return $tables; + } + + /** + * Get the version of the database connector. + * + * @return string The database connector version. + * + * @since 12.1 + */ + public function getVersion() + { + $this->connect(); + + return mysqli_get_server_info($this->connection); + } + + /** + * Method to get the auto-incremented value from the last INSERT statement. + * + * @return mixed The value of the auto-increment field from the last inserted row. + * If the value is greater than maximal int value, it will return a string. + * + * @since 12.1 + */ + public function insertid() + { + $this->connect(); + + return mysqli_insert_id($this->connection); + } + + /** + * Locks a table in the database. + * + * @param string $table The name of the table to unlock. + * + * @return FOFDatabaseDriverMysqli Returns this object to support chaining. + * + * @since 12.2 + * @throws RuntimeException + */ + public function lockTable($table) + { + $this->setQuery('LOCK TABLES ' . $this->quoteName($table) . ' WRITE')->execute(); + + return $this; + } + + /** + * Execute the SQL statement. + * + * @return mixed A database cursor resource on success, boolean false on failure. + * + * @since 12.1 + * @throws RuntimeException + */ + public function execute() + { + $this->connect(); + + if (!is_object($this->connection)) + { + if (class_exists('JLog')) + { + JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); + } + throw new RuntimeException($this->errorMsg, $this->errorNum); + } + + // Take a local copy so that we don't modify the original query and cause issues later + $query = $this->replacePrefix((string) $this->sql); + + if (!($this->sql instanceof FOFDatabaseQuery) && ($this->limit > 0 || $this->offset > 0)) + { + $query .= ' LIMIT ' . $this->offset . ', ' . $this->limit; + } + + // Increment the query counter. + $this->count++; + + // Reset the error values. + $this->errorNum = 0; + $this->errorMsg = ''; + $memoryBefore = null; + + // If debugging is enabled then let's log the query. + if ($this->debug) + { + // Add the query to the object queue. + $this->log[] = $query; + + if (class_exists('JLog')) + { + JLog::add($query, JLog::DEBUG, 'databasequery'); + } + + $this->timings[] = microtime(true); + + if (is_object($this->cursor)) + { + // Avoid warning if result already freed by third-party library + @$this->freeResult(); + } + + $memoryBefore = memory_get_usage(); + } + + // Execute the query. Error suppression is used here to prevent warnings/notices that the connection has been lost. + $this->cursor = @mysqli_query($this->connection, $query); + + if ($this->debug) + { + $this->timings[] = microtime(true); + + if (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) + { + $this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + } + else + { + $this->callStacks[] = debug_backtrace(); + } + + $this->callStacks[count($this->callStacks) - 1][0]['memory'] = array( + $memoryBefore, + memory_get_usage(), + is_object($this->cursor) ? $this->getNumRows() : null + ); + } + + // If an error occurred handle it. + if (!$this->cursor) + { + // Get the error number and message before we execute any more queries. + $this->errorNum = $this->getErrorNumber(); + $this->errorMsg = $this->getErrorMessage($query); + + // Check if the server was disconnected. + if (!$this->connected()) + { + try + { + // Attempt to reconnect. + $this->connection = null; + $this->connect(); + } + // If connect fails, ignore that exception and throw the normal exception. + catch (RuntimeException $e) + { + // Get the error number and message. + $this->errorNum = $this->getErrorNumber(); + $this->errorMsg = $this->getErrorMessage($query); + + if (class_exists('JLog')) + { + JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); + } + + throw new RuntimeException($this->errorMsg, $this->errorNum, $e); + } + + // Since we were able to reconnect, run the query again. + return $this->execute(); + } + // The server was not disconnected. + else + { + if (class_exists('JLog')) + { + JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); + } + + throw new RuntimeException($this->errorMsg, $this->errorNum); + } + } + + return $this->cursor; + } + + /** + * Renames a table in the database. + * + * @param string $oldTable The name of the table to be renamed + * @param string $newTable The new name for the table. + * @param string $backup Not used by MySQL. + * @param string $prefix Not used by MySQL. + * + * @return FOFDatabaseDriverMysqli Returns this object to support chaining. + * + * @since 12.2 + * @throws RuntimeException + */ + public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) + { + $this->setQuery('RENAME TABLE ' . $oldTable . ' TO ' . $newTable)->execute(); + + return $this; + } + + /** + * Select a database for use. + * + * @param string $database The name of the database to select for use. + * + * @return boolean True if the database was successfully selected. + * + * @since 12.1 + * @throws RuntimeException + */ + public function select($database) + { + $this->connect(); + + if (!$database) + { + return false; + } + + if (!mysqli_select_db($this->connection, $database)) + { + throw new RuntimeException('Could not connect to database.'); + } + + return true; + } + + /** + * Set the connection to use UTF-8 character encoding. + * + * @return boolean True on success. + * + * @since 12.1 + */ + public function setUtf() + { + // If UTF is not supported return false immediately + if (!$this->utf) + { + return false; + } + + // Make sure we're connected to the server + $this->connect(); + + // Which charset should I use, plain utf8 or multibyte utf8mb4? + $charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8'; + + $result = @$this->connection->set_charset($charset); + + /** + * If I could not set the utf8mb4 charset then the server doesn't support utf8mb4 despite claiming otherwise. + * This happens on old MySQL server versions (less than 5.5.3) using the mysqlnd PHP driver. Since mysqlnd + * masks the server version and reports only its own we can not be sure if the server actually does support + * UTF-8 Multibyte (i.e. it's MySQL 5.5.3 or later). Since the utf8mb4 charset is undefined in this case we + * catch the error and determine that utf8mb4 is not supported! + */ + if (!$result && $this->utf8mb4) + { + $this->utf8mb4 = false; + $result = @$this->connection->set_charset('utf8'); + } + + return $result; + } + + /** + * Method to commit a transaction. + * + * @param boolean $toSavepoint If true, commit to the last savepoint. + * + * @return void + * + * @since 12.2 + * @throws RuntimeException + */ + public function transactionCommit($toSavepoint = false) + { + $this->connect(); + + if (!$toSavepoint || $this->transactionDepth <= 1) + { + if ($this->setQuery('COMMIT')->execute()) + { + $this->transactionDepth = 0; + } + + return; + } + + $this->transactionDepth--; + } + + /** + * Method to roll back a transaction. + * + * @param boolean $toSavepoint If true, rollback to the last savepoint. + * + * @return void + * + * @since 12.2 + * @throws RuntimeException + */ + public function transactionRollback($toSavepoint = false) + { + $this->connect(); + + if (!$toSavepoint || $this->transactionDepth <= 1) + { + if ($this->setQuery('ROLLBACK')->execute()) + { + $this->transactionDepth = 0; + } + + return; + } + + $savepoint = 'SP_' . ($this->transactionDepth - 1); + $this->setQuery('ROLLBACK TO SAVEPOINT ' . $this->quoteName($savepoint)); + + if ($this->execute()) + { + $this->transactionDepth--; + } + } + + /** + * Method to initialize a transaction. + * + * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. + * + * @return void + * + * @since 12.2 + * @throws RuntimeException + */ + public function transactionStart($asSavepoint = false) + { + $this->connect(); + + if (!$asSavepoint || !$this->transactionDepth) + { + if ($this->setQuery('START TRANSACTION')->execute()) + { + $this->transactionDepth = 1; + } + + return; + } + + $savepoint = 'SP_' . $this->transactionDepth; + $this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint)); + + if ($this->execute()) + { + $this->transactionDepth++; + } + } + + /** + * Method to fetch a row from the result set cursor as an array. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * + * @return mixed Either the next row from the result set or false if there are no more rows. + * + * @since 12.1 + */ + protected function fetchArray($cursor = null) + { + return mysqli_fetch_row($cursor ? $cursor : $this->cursor); + } + + /** + * Method to fetch a row from the result set cursor as an associative array. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * + * @return mixed Either the next row from the result set or false if there are no more rows. + * + * @since 12.1 + */ + protected function fetchAssoc($cursor = null) + { + return mysqli_fetch_assoc($cursor ? $cursor : $this->cursor); + } + + /** + * Method to fetch a row from the result set cursor as an object. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * @param string $class The class name to use for the returned row object. + * + * @return mixed Either the next row from the result set or false if there are no more rows. + * + * @since 12.1 + */ + protected function fetchObject($cursor = null, $class = 'stdClass') + { + return mysqli_fetch_object($cursor ? $cursor : $this->cursor, $class); + } + + /** + * Method to free up the memory used for the result set. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * + * @return void + * + * @since 12.1 + */ + protected function freeResult($cursor = null) + { + mysqli_free_result($cursor ? $cursor : $this->cursor); + + if ((! $cursor) || ($cursor === $this->cursor)) + { + $this->cursor = null; + } + } + + /** + * Unlocks tables in the database. + * + * @return FOFDatabaseDriverMysqli Returns this object to support chaining. + * + * @since 12.1 + * @throws RuntimeException + */ + public function unlockTables() + { + $this->setQuery('UNLOCK TABLES')->execute(); + + return $this; + } + + /** + * Internal function to check if profiling is available + * + * @return boolean + * + * @since 3.1.3 + */ + private function hasProfiling() + { + try + { + $res = mysqli_query($this->connection, "SHOW VARIABLES LIKE 'have_profiling'"); + $row = mysqli_fetch_assoc($res); + + return isset($row); + } + catch (Exception $e) + { + return false; + } + } + + /** + * Does the database server claim to have support for UTF-8 Multibyte (utf8mb4) collation? + * + * libmysql supports utf8mb4 since 5.5.3 (same version as the MySQL server). mysqlnd supports utf8mb4 since 5.0.9. + * + * @return boolean + * + * @since CMS 3.5.0 + */ + private function serverClaimsUtf8mb4Support() + { + $client_version = mysqli_get_client_info(); + + if (strpos($client_version, 'mysqlnd') !== false) + { + $client_version = preg_replace('/^\D+([\d.]+).*/', '$1', $client_version); + + return version_compare($client_version, '5.0.9', '>='); + } + else + { + return version_compare($client_version, '5.5.3', '>='); + } + } + + /** + * Return the actual SQL Error number + * + * @return integer The SQL Error number + * + * @since 3.4.6 + */ + protected function getErrorNumber() + { + return (int) mysqli_errno($this->connection); + } + + /** + * Return the actual SQL Error message + * + * @param string $query The SQL Query that fails + * + * @return string The SQL Error message + * + * @since 3.4.6 + */ + protected function getErrorMessage($query) + { + $errorMessage = (string) mysqli_error($this->connection); + + // Replace the Databaseprefix with `#__` if we are not in Debug + if (!$this->debug) + { + $errorMessage = str_replace($this->tablePrefix, '#__', $errorMessage); + $query = str_replace($this->tablePrefix, '#__', $query); + } + + return $errorMessage . ' SQL=' . $query; + } +} diff --git a/libraries/fof/database/driver/oracle.php b/libraries/fof/database/driver/oracle.php new file mode 100644 index 0000000000000..155246e5de1fb --- /dev/null +++ b/libraries/fof/database/driver/oracle.php @@ -0,0 +1,712 @@ +charset = $options['charset']; + $this->dateformat = $options['dateformat']; + + // Finalize initialisation + parent::__construct($options); + } + + /** + * Destructor. + * + * @since 12.1 + */ + public function __destruct() + { + $this->freeResult(); + unset($this->connection); + } + + /** + * Connects to the database if needed. + * + * @return void Returns void if the database connected successfully. + * + * @since 12.1 + * @throws RuntimeException + */ + public function connect() + { + if ($this->connection) + { + return; + } + + parent::connect(); + + if (isset($this->options['schema'])) + { + $this->setQuery('ALTER SESSION SET CURRENT_SCHEMA = ' . $this->quoteName($this->options['schema']))->execute(); + } + + $this->setDateFormat($this->dateformat); + } + + /** + * Disconnects the database. + * + * @return void + * + * @since 12.1 + */ + public function disconnect() + { + // Close the connection. + $this->freeResult(); + unset($this->connection); + } + + /** + * Drops a table from the database. + * + * Note: The IF EXISTS flag is unused in the Oracle driver. + * + * @param string $tableName The name of the database table to drop. + * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. + * + * @return FOFDatabaseDriverOracle Returns this object to support chaining. + * + * @since 12.1 + */ + public function dropTable($tableName, $ifExists = true) + { + $this->connect(); + + $query = $this->getQuery(true) + ->setQuery('DROP TABLE :tableName'); + $query->bind(':tableName', $tableName); + + $this->setQuery($query); + + $this->execute(); + + return $this; + } + + /** + * Method to get the database collation in use by sampling a text field of a table in the database. + * + * @return mixed The collation in use by the database or boolean false if not supported. + * + * @since 12.1 + */ + public function getCollation() + { + return $this->charset; + } + + /** + * Method to get the database connection collation, as reported by the driver. If the connector doesn't support + * reporting this value please return an empty string. + * + * @return string + */ + public function getConnectionCollation() + { + return $this->charset; + } + + /** + * Get a query to run and verify the database is operational. + * + * @return string The query to check the health of the DB. + * + * @since 12.2 + */ + public function getConnectedQuery() + { + return 'SELECT 1 FROM dual'; + } + + /** + * Returns the current date format + * This method should be useful in the case that + * somebody actually wants to use a different + * date format and needs to check what the current + * one is to see if it needs to be changed. + * + * @return string The current date format + * + * @since 12.1 + */ + public function getDateFormat() + { + return $this->dateformat; + } + + /** + * Shows the table CREATE statement that creates the given tables. + * + * Note: You must have the correct privileges before this method + * will return usable results! + * + * @param mixed $tables A table name or a list of table names. + * + * @return array A list of the create SQL for the tables. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getTableCreate($tables) + { + $this->connect(); + + $result = array(); + $query = $this->getQuery(true) + ->select('dbms_metadata.get_ddl(:type, :tableName)') + ->from('dual') + ->bind(':type', 'TABLE'); + + // Sanitize input to an array and iterate over the list. + settype($tables, 'array'); + + foreach ($tables as $table) + { + $query->bind(':tableName', $table); + $this->setQuery($query); + $statement = (string) $this->loadResult(); + $result[$table] = $statement; + } + + return $result; + } + + /** + * Retrieves field information about a given table. + * + * @param string $table The name of the database table. + * @param boolean $typeOnly True to only return field types. + * + * @return array An array of fields for the database table. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getTableColumns($table, $typeOnly = true) + { + $this->connect(); + + $columns = array(); + $query = $this->getQuery(true); + + $fieldCasing = $this->getOption(PDO::ATTR_CASE); + + $this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER); + + $table = strtoupper($table); + + $query->select('*'); + $query->from('ALL_TAB_COLUMNS'); + $query->where('table_name = :tableName'); + + $prefixedTable = str_replace('#__', strtoupper($this->tablePrefix), $table); + $query->bind(':tableName', $prefixedTable); + $this->setQuery($query); + $fields = $this->loadObjectList(); + + if ($typeOnly) + { + foreach ($fields as $field) + { + $columns[$field->COLUMN_NAME] = $field->DATA_TYPE; + } + } + else + { + foreach ($fields as $field) + { + $columns[$field->COLUMN_NAME] = $field; + $columns[$field->COLUMN_NAME]->Default = null; + } + } + + $this->setOption(PDO::ATTR_CASE, $fieldCasing); + + return $columns; + } + + /** + * Get the details list of keys for a table. + * + * @param string $table The name of the table. + * + * @return array An array of the column specification for the table. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getTableKeys($table) + { + $this->connect(); + + $query = $this->getQuery(true); + + $fieldCasing = $this->getOption(PDO::ATTR_CASE); + + $this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER); + + $table = strtoupper($table); + $query->select('*') + ->from('ALL_CONSTRAINTS') + ->where('table_name = :tableName') + ->bind(':tableName', $table); + + $this->setQuery($query); + $keys = $this->loadObjectList(); + + $this->setOption(PDO::ATTR_CASE, $fieldCasing); + + return $keys; + } + + /** + * Method to get an array of all tables in the database (schema). + * + * @param string $databaseName The database (schema) name + * @param boolean $includeDatabaseName Whether to include the schema name in the results + * + * @return array An array of all the tables in the database. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getTableList($databaseName = null, $includeDatabaseName = false) + { + $this->connect(); + + $query = $this->getQuery(true); + + if ($includeDatabaseName) + { + $query->select('owner, table_name'); + } + else + { + $query->select('table_name'); + } + + $query->from('all_tables'); + + if ($databaseName) + { + $query->where('owner = :database') + ->bind(':database', $databaseName); + } + + $query->order('table_name'); + + $this->setQuery($query); + + if ($includeDatabaseName) + { + $tables = $this->loadAssocList(); + } + else + { + $tables = $this->loadColumn(); + } + + return $tables; + } + + /** + * Get the version of the database connector. + * + * @return string The database connector version. + * + * @since 12.1 + */ + public function getVersion() + { + $this->connect(); + + $this->setQuery("select value from nls_database_parameters where parameter = 'NLS_RDBMS_VERSION'"); + + return $this->loadResult(); + } + + /** + * Select a database for use. + * + * @param string $database The name of the database to select for use. + * + * @return boolean True if the database was successfully selected. + * + * @since 12.1 + * @throws RuntimeException + */ + public function select($database) + { + $this->connect(); + + return true; + } + + /** + * Sets the Oracle Date Format for the session + * Default date format for Oracle is = DD-MON-RR + * The default date format for this driver is: + * 'RRRR-MM-DD HH24:MI:SS' since it is the format + * that matches the MySQL one used within most Joomla + * tables. + * + * @param string $dateFormat Oracle Date Format String + * + * @return boolean + * + * @since 12.1 + */ + public function setDateFormat($dateFormat = 'DD-MON-RR') + { + $this->connect(); + + $this->setQuery("ALTER SESSION SET NLS_DATE_FORMAT = '$dateFormat'"); + + if (!$this->execute()) + { + return false; + } + + $this->setQuery("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = '$dateFormat'"); + + if (!$this->execute()) + { + return false; + } + + $this->dateformat = $dateFormat; + + return true; + } + + /** + * Set the connection to use UTF-8 character encoding. + * + * Returns false automatically for the Oracle driver since + * you can only set the character set when the connection + * is created. + * + * @return boolean True on success. + * + * @since 12.1 + */ + public function setUtf() + { + return false; + } + + /** + * Locks a table in the database. + * + * @param string $table The name of the table to unlock. + * + * @return FOFDatabaseDriverOracle Returns this object to support chaining. + * + * @since 12.1 + * @throws RuntimeException + */ + public function lockTable($table) + { + $this->setQuery('LOCK TABLE ' . $this->quoteName($table) . ' IN EXCLUSIVE MODE')->execute(); + + return $this; + } + + /** + * Renames a table in the database. + * + * @param string $oldTable The name of the table to be renamed + * @param string $newTable The new name for the table. + * @param string $backup Not used by Oracle. + * @param string $prefix Not used by Oracle. + * + * @return FOFDatabaseDriverOracle Returns this object to support chaining. + * + * @since 12.1 + * @throws RuntimeException + */ + public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) + { + $this->setQuery('RENAME ' . $oldTable . ' TO ' . $newTable)->execute(); + + return $this; + } + + /** + * Unlocks tables in the database. + * + * @return FOFDatabaseDriverOracle Returns this object to support chaining. + * + * @since 12.1 + * @throws RuntimeException + */ + public function unlockTables() + { + $this->setQuery('COMMIT')->execute(); + + return $this; + } + + /** + * Test to see if the PDO ODBC connector is available. + * + * @return boolean True on success, false otherwise. + * + * @since 12.1 + */ + public static function isSupported() + { + return class_exists('PDO') && in_array('oci', PDO::getAvailableDrivers()); + } + + /** + * This function replaces a string identifier $prefix with the string held is the + * tablePrefix class variable. + * + * @param string $query The SQL statement to prepare. + * @param string $prefix The common table prefix. + * + * @return string The processed SQL statement. + * + * @since 11.1 + */ + public function replacePrefix($query, $prefix = '#__') + { + $startPos = 0; + $quoteChar = "'"; + $literal = ''; + + $query = trim($query); + $n = strlen($query); + + while ($startPos < $n) + { + $ip = strpos($query, $prefix, $startPos); + + if ($ip === false) + { + break; + } + + $j = strpos($query, "'", $startPos); + + if ($j === false) + { + $j = $n; + } + + $literal .= str_replace($prefix, $this->tablePrefix, substr($query, $startPos, $j - $startPos)); + $startPos = $j; + + $j = $startPos + 1; + + if ($j >= $n) + { + break; + } + + // Quote comes first, find end of quote + while (true) + { + $k = strpos($query, $quoteChar, $j); + $escaped = false; + + if ($k === false) + { + break; + } + + $l = $k - 1; + + while ($l >= 0 && $query{$l} == '\\') + { + $l--; + $escaped = !$escaped; + } + + if ($escaped) + { + $j = $k + 1; + continue; + } + + break; + } + + if ($k === false) + { + // Error in the query - no end quote; ignore it + break; + } + + $literal .= substr($query, $startPos, $k - $startPos + 1); + $startPos = $k + 1; + } + + if ($startPos < $n) + { + $literal .= substr($query, $startPos, $n - $startPos); + } + + return $literal; + } + + /** + * Method to commit a transaction. + * + * @param boolean $toSavepoint If true, commit to the last savepoint. + * + * @return void + * + * @since 12.3 + * @throws RuntimeException + */ + public function transactionCommit($toSavepoint = false) + { + $this->connect(); + + if (!$toSavepoint || $this->transactionDepth <= 1) + { + parent::transactionCommit($toSavepoint); + } + else + { + $this->transactionDepth--; + } + } + + /** + * Method to roll back a transaction. + * + * @param boolean $toSavepoint If true, rollback to the last savepoint. + * + * @return void + * + * @since 12.3 + * @throws RuntimeException + */ + public function transactionRollback($toSavepoint = false) + { + $this->connect(); + + if (!$toSavepoint || $this->transactionDepth <= 1) + { + parent::transactionRollback($toSavepoint); + } + else + { + $savepoint = 'SP_' . ($this->transactionDepth - 1); + $this->setQuery('ROLLBACK TO SAVEPOINT ' . $this->quoteName($savepoint)); + + if ($this->execute()) + { + $this->transactionDepth--; + } + } + } + + /** + * Method to initialize a transaction. + * + * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. + * + * @return void + * + * @since 12.3 + * @throws RuntimeException + */ + public function transactionStart($asSavepoint = false) + { + $this->connect(); + + if (!$asSavepoint || !$this->transactionDepth) + { + return parent::transactionStart($asSavepoint); + } + + $savepoint = 'SP_' . $this->transactionDepth; + $this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint)); + + if ($this->execute()) + { + $this->transactionDepth++; + } + } +} diff --git a/libraries/fof/database/driver/pdo.php b/libraries/fof/database/driver/pdo.php new file mode 100644 index 0000000000000..ebc59effaccd4 --- /dev/null +++ b/libraries/fof/database/driver/pdo.php @@ -0,0 +1,1106 @@ +disconnect(); + } + + /** + * Connects to the database if needed. + * + * @return void Returns void if the database connected successfully. + * + * @since 12.1 + * @throws RuntimeException + */ + public function connect() + { + if ($this->connection) + { + return; + } + + // Make sure the PDO extension for PHP is installed and enabled. + if (!self::isSupported()) + { + throw new RuntimeException('PDO Extension is not available.', 1); + } + + $replace = array(); + $with = array(); + + // Find the correct PDO DSN Format to use: + switch ($this->options['driver']) + { + case 'cubrid': + $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 33000; + + $format = 'cubrid:host=#HOST#;port=#PORT#;dbname=#DBNAME#'; + + $replace = array('#HOST#', '#PORT#', '#DBNAME#'); + $with = array($this->options['host'], $this->options['port'], $this->options['database']); + + break; + + case 'dblib': + $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1433; + + $format = 'dblib:host=#HOST#;port=#PORT#;dbname=#DBNAME#'; + + $replace = array('#HOST#', '#PORT#', '#DBNAME#'); + $with = array($this->options['host'], $this->options['port'], $this->options['database']); + + break; + + case 'firebird': + $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 3050; + + $format = 'firebird:dbname=#DBNAME#'; + + $replace = array('#DBNAME#'); + $with = array($this->options['database']); + + break; + + case 'ibm': + $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 56789; + + if (!empty($this->options['dsn'])) + { + $format = 'ibm:DSN=#DSN#'; + + $replace = array('#DSN#'); + $with = array($this->options['dsn']); + } + else + { + $format = 'ibm:hostname=#HOST#;port=#PORT#;database=#DBNAME#'; + + $replace = array('#HOST#', '#PORT#', '#DBNAME#'); + $with = array($this->options['host'], $this->options['port'], $this->options['database']); + } + + break; + + case 'informix': + $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1526; + $this->options['protocol'] = (isset($this->options['protocol'])) ? $this->options['protocol'] : 'onsoctcp'; + + if (!empty($this->options['dsn'])) + { + $format = 'informix:DSN=#DSN#'; + + $replace = array('#DSN#'); + $with = array($this->options['dsn']); + } + else + { + $format = 'informix:host=#HOST#;service=#PORT#;database=#DBNAME#;server=#SERVER#;protocol=#PROTOCOL#'; + + $replace = array('#HOST#', '#PORT#', '#DBNAME#', '#SERVER#', '#PROTOCOL#'); + $with = array($this->options['host'], $this->options['port'], $this->options['database'], $this->options['server'], $this->options['protocol']); + } + + break; + + case 'mssql': + $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1433; + + $format = 'mssql:host=#HOST#;port=#PORT#;dbname=#DBNAME#'; + + $replace = array('#HOST#', '#PORT#', '#DBNAME#'); + $with = array($this->options['host'], $this->options['port'], $this->options['database']); + + break; + + // The pdomysql case is a special case within the CMS environment + case 'pdomysql': + case 'mysql': + $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 3306; + + $format = 'mysql:host=#HOST#;port=#PORT#;dbname=#DBNAME#;charset=#CHARSET#'; + + $replace = array('#HOST#', '#PORT#', '#DBNAME#', '#CHARSET#'); + $with = array($this->options['host'], $this->options['port'], $this->options['database'], $this->options['charset']); + + break; + + case 'oci': + $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1521; + $this->options['charset'] = (isset($this->options['charset'])) ? $this->options['charset'] : 'AL32UTF8'; + + if (!empty($this->options['dsn'])) + { + $format = 'oci:dbname=#DSN#'; + + $replace = array('#DSN#'); + $with = array($this->options['dsn']); + } + else + { + $format = 'oci:dbname=//#HOST#:#PORT#/#DBNAME#'; + + $replace = array('#HOST#', '#PORT#', '#DBNAME#'); + $with = array($this->options['host'], $this->options['port'], $this->options['database']); + } + + $format .= ';charset=' . $this->options['charset']; + + break; + + case 'odbc': + $format = 'odbc:DSN=#DSN#;UID:#USER#;PWD=#PASSWORD#'; + + $replace = array('#DSN#', '#USER#', '#PASSWORD#'); + $with = array($this->options['dsn'], $this->options['user'], $this->options['password']); + + break; + + case 'pgsql': + $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 5432; + + $format = 'pgsql:host=#HOST#;port=#PORT#;dbname=#DBNAME#'; + + $replace = array('#HOST#', '#PORT#', '#DBNAME#'); + $with = array($this->options['host'], $this->options['port'], $this->options['database']); + + break; + + case 'sqlite': + if (isset($this->options['version']) && $this->options['version'] == 2) + { + $format = 'sqlite2:#DBNAME#'; + } + else + { + $format = 'sqlite:#DBNAME#'; + } + + $replace = array('#DBNAME#'); + $with = array($this->options['database']); + + break; + + case 'sybase': + $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1433; + + $format = 'mssql:host=#HOST#;port=#PORT#;dbname=#DBNAME#'; + + $replace = array('#HOST#', '#PORT#', '#DBNAME#'); + $with = array($this->options['host'], $this->options['port'], $this->options['database']); + + break; + } + + // Create the connection string: + $connectionString = str_replace($replace, $with, $format); + + try + { + $this->connection = new PDO( + $connectionString, + $this->options['user'], + $this->options['password'], + $this->options['driverOptions'] + ); + } + catch (PDOException $e) + { + throw new RuntimeException('Could not connect to PDO: ' . $e->getMessage(), 2, $e); + } + } + + /** + * Disconnects the database. + * + * @return void + * + * @since 12.1 + */ + public function disconnect() + { + foreach ($this->disconnectHandlers as $h) + { + call_user_func_array($h, array( &$this)); + } + + $this->freeResult(); + unset($this->connection); + } + + /** + * Method to escape a string for usage in an SQL statement. + * + * Oracle escaping reference: + * http://www.orafaq.com/wiki/SQL_FAQ#How_does_one_escape_special_characters_when_writing_SQL_queries.3F + * + * SQLite escaping notes: + * http://www.sqlite.org/faq.html#q14 + * + * Method body is as implemented by the Zend Framework + * + * Note: Using query objects with bound variables is + * preferable to the below. + * + * @param string $text The string to be escaped. + * @param boolean $extra Unused optional parameter to provide extra escaping. + * + * @return string The escaped string. + * + * @since 12.1 + */ + public function escape($text, $extra = false) + { + if (is_int($text) || is_float($text)) + { + return $text; + } + + $text = str_replace("'", "''", $text); + + return addcslashes($text, "\000\n\r\\\032"); + } + + /** + * Execute the SQL statement. + * + * @return mixed A database cursor resource on success, boolean false on failure. + * + * @since 12.1 + * @throws RuntimeException + * @throws Exception + */ + public function execute() + { + $this->connect(); + + if (!is_object($this->connection)) + { + if (class_exists('JLog')) + { + JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); + } + throw new RuntimeException($this->errorMsg, $this->errorNum); + } + + // Take a local copy so that we don't modify the original query and cause issues later + $query = $this->replacePrefix((string) $this->sql); + + if (!($this->sql instanceof FOFDatabaseQuery) && ($this->limit > 0 || $this->offset > 0)) + { + // @TODO + $query .= ' LIMIT ' . $this->offset . ', ' . $this->limit; + } + + // Increment the query counter. + $this->count++; + + // Reset the error values. + $this->errorNum = 0; + $this->errorMsg = ''; + + // If debugging is enabled then let's log the query. + if ($this->debug) + { + // Add the query to the object queue. + $this->log[] = $query; + + if (class_exists('JLog')) + { + JLog::add($query, JLog::DEBUG, 'databasequery'); + } + + $this->timings[] = microtime(true); + } + + // Execute the query. + $this->executed = false; + + if ($this->prepared instanceof PDOStatement) + { + // Bind the variables: + if ($this->sql instanceof FOFDatabaseQueryPreparable) + { + $bounded = $this->sql->getBounded(); + + foreach ($bounded as $key => $obj) + { + $this->prepared->bindParam($key, $obj->value, $obj->dataType, $obj->length, $obj->driverOptions); + } + } + + $this->executed = $this->prepared->execute(); + } + + if ($this->debug) + { + $this->timings[] = microtime(true); + + if (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) + { + $this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + } + else + { + $this->callStacks[] = debug_backtrace(); + } + } + + // If an error occurred handle it. + if (!$this->executed) + { + // Get the error number and message before we execute any more queries. + $errorNum = $this->getErrorNumber(); + $errorMsg = $this->getErrorMessage($query); + + // Check if the server was disconnected. + if (!$this->connected()) + { + try + { + // Attempt to reconnect. + $this->connection = null; + $this->connect(); + } + // If connect fails, ignore that exception and throw the normal exception. + catch (RuntimeException $e) + { + // Get the error number and message. + $this->errorNum = $this->getErrorNumber(); + $this->errorMsg = $this->getErrorMessage($query); + + // Throw the normal query exception. + if (class_exists('JLog')) + { + JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); + } + + throw new RuntimeException($this->errorMsg, $this->errorNum, $e); + } + + // Since we were able to reconnect, run the query again. + return $this->execute(); + } + // The server was not disconnected. + else + { + // Get the error number and message from before we tried to reconnect. + $this->errorNum = $errorNum; + $this->errorMsg = $errorMsg; + + // Throw the normal query exception. + if (class_exists('JLog')) + { + JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); + } + + throw new RuntimeException($this->errorMsg, $this->errorNum); + } + } + + return $this->prepared; + } + + /** + * Retrieve a PDO database connection attribute + * http://www.php.net/manual/en/pdo.getattribute.php + * + * Usage: $db->getOption(PDO::ATTR_CASE); + * + * @param mixed $key One of the PDO::ATTR_* Constants + * + * @return mixed + * + * @since 12.1 + */ + public function getOption($key) + { + $this->connect(); + + return $this->connection->getAttribute($key); + } + + /** + * Get a query to run and verify the database is operational. + * + * @return string The query to check the health of the DB. + * + * @since 12.2 + */ + public function getConnectedQuery() + { + return 'SELECT 1'; + } + + /** + * Sets an attribute on the PDO database handle. + * http://www.php.net/manual/en/pdo.setattribute.php + * + * Usage: $db->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER); + * + * @param integer $key One of the PDO::ATTR_* Constants + * @param mixed $value One of the associated PDO Constants + * related to the particular attribute + * key. + * + * @return boolean + * + * @since 12.1 + */ + public function setOption($key, $value) + { + $this->connect(); + + return $this->connection->setAttribute($key, $value); + } + + /** + * Test to see if the PDO extension is available. + * Override as needed to check for specific PDO Drivers. + * + * @return boolean True on success, false otherwise. + * + * @since 12.1 + */ + public static function isSupported() + { + return defined('PDO::ATTR_DRIVER_NAME'); + } + + /** + * Determines if the connection to the server is active. + * + * @return boolean True if connected to the database engine. + * + * @since 12.1 + */ + public function connected() + { + // Flag to prevent recursion into this function. + static $checkingConnected = false; + + if ($checkingConnected) + { + // Reset this flag and throw an exception. + $checkingConnected = true; + die('Recursion trying to check if connected.'); + } + + // Backup the query state. + $query = $this->sql; + $limit = $this->limit; + $offset = $this->offset; + $prepared = $this->prepared; + + try + { + // Set the checking connection flag. + $checkingConnected = true; + + // Run a simple query to check the connection. + $this->setQuery($this->getConnectedQuery()); + $status = (bool) $this->loadResult(); + } + // If we catch an exception here, we must not be connected. + catch (Exception $e) + { + $status = false; + } + + // Restore the query state. + $this->sql = $query; + $this->limit = $limit; + $this->offset = $offset; + $this->prepared = $prepared; + $checkingConnected = false; + + return $status; + } + + /** + * Get the number of affected rows for the previous executed SQL statement. + * Only applicable for DELETE, INSERT, or UPDATE statements. + * + * @return integer The number of affected rows. + * + * @since 12.1 + */ + public function getAffectedRows() + { + $this->connect(); + + if ($this->prepared instanceof PDOStatement) + { + return $this->prepared->rowCount(); + } + else + { + return 0; + } + } + + /** + * Get the number of returned rows for the previous executed SQL statement. + * Only applicable for DELETE, INSERT, or UPDATE statements. + * + * @param resource $cursor An optional database cursor resource to extract the row count from. + * + * @return integer The number of returned rows. + * + * @since 12.1 + */ + public function getNumRows($cursor = null) + { + $this->connect(); + + if ($cursor instanceof PDOStatement) + { + return $cursor->rowCount(); + } + elseif ($this->prepared instanceof PDOStatement) + { + return $this->prepared->rowCount(); + } + else + { + return 0; + } + } + + /** + * Method to get the auto-incremented value from the last INSERT statement. + * + * @return string The value of the auto-increment field from the last inserted row. + * + * @since 12.1 + */ + public function insertid() + { + $this->connect(); + + // Error suppress this to prevent PDO warning us that the driver doesn't support this operation. + return @$this->connection->lastInsertId(); + } + + /** + * Select a database for use. + * + * @param string $database The name of the database to select for use. + * + * @return boolean True if the database was successfully selected. + * + * @since 12.1 + * @throws RuntimeException + */ + public function select($database) + { + $this->connect(); + + return true; + } + + /** + * Sets the SQL statement string for later execution. + * + * @param mixed $query The SQL statement to set either as a FOFDatabaseQuery object or a string. + * @param integer $offset The affected row offset to set. + * @param integer $limit The maximum affected rows to set. + * @param array $driverOptions The optional PDO driver options. + * + * @return FOFDatabaseDriver This object to support method chaining. + * + * @since 12.1 + */ + public function setQuery($query, $offset = null, $limit = null, $driverOptions = array()) + { + $this->connect(); + + $this->freeResult(); + + if (is_string($query)) + { + // Allows taking advantage of bound variables in a direct query: + $query = $this->getQuery(true)->setQuery($query); + } + + if ($query instanceof FOFDatabaseQueryLimitable && !is_null($offset) && !is_null($limit)) + { + $query = $query->processLimit($query, $limit, $offset); + } + + // Create a stringified version of the query (with prefixes replaced): + $sql = $this->replacePrefix((string) $query); + + // Use the stringified version in the prepare call: + $this->prepared = $this->connection->prepare($sql, $driverOptions); + + // Store reference to the original FOFDatabaseQuery instance within the class. + // This is important since binding variables depends on it within execute(): + parent::setQuery($query, $offset, $limit); + + return $this; + } + + /** + * Set the connection to use UTF-8 character encoding. + * + * @return boolean True on success. + * + * @since 12.1 + */ + public function setUtf() + { + return false; + } + + /** + * Method to commit a transaction. + * + * @param boolean $toSavepoint If true, commit to the last savepoint. + * + * @return void + * + * @since 12.1 + * @throws RuntimeException + */ + public function transactionCommit($toSavepoint = false) + { + $this->connect(); + + if (!$toSavepoint || $this->transactionDepth == 1) + { + $this->connection->commit(); + } + + $this->transactionDepth--; + } + + /** + * Method to roll back a transaction. + * + * @param boolean $toSavepoint If true, rollback to the last savepoint. + * + * @return void + * + * @since 12.1 + * @throws RuntimeException + */ + public function transactionRollback($toSavepoint = false) + { + $this->connect(); + + if (!$toSavepoint || $this->transactionDepth == 1) + { + $this->connection->rollBack(); + } + + $this->transactionDepth--; + } + + /** + * Method to initialize a transaction. + * + * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. + * + * @return void + * + * @since 12.1 + * @throws RuntimeException + */ + public function transactionStart($asSavepoint = false) + { + $this->connect(); + + if (!$asSavepoint || !$this->transactionDepth) + { + $this->connection->beginTransaction(); + } + + $this->transactionDepth++; + } + + /** + * Method to fetch a row from the result set cursor as an array. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * + * @return mixed Either the next row from the result set or false if there are no more rows. + * + * @since 12.1 + */ + protected function fetchArray($cursor = null) + { + if (!empty($cursor) && $cursor instanceof PDOStatement) + { + return $cursor->fetch(PDO::FETCH_NUM); + } + + if ($this->prepared instanceof PDOStatement) + { + return $this->prepared->fetch(PDO::FETCH_NUM); + } + } + + /** + * Method to fetch a row from the result set cursor as an associative array. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * + * @return mixed Either the next row from the result set or false if there are no more rows. + * + * @since 12.1 + */ + protected function fetchAssoc($cursor = null) + { + if (!empty($cursor) && $cursor instanceof PDOStatement) + { + return $cursor->fetch(PDO::FETCH_ASSOC); + } + + if ($this->prepared instanceof PDOStatement) + { + return $this->prepared->fetch(PDO::FETCH_ASSOC); + } + } + + /** + * Method to fetch a row from the result set cursor as an object. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * @param string $class Unused, only necessary so method signature will be the same as parent. + * + * @return mixed Either the next row from the result set or false if there are no more rows. + * + * @since 12.1 + */ + protected function fetchObject($cursor = null, $class = 'stdClass') + { + if (!empty($cursor) && $cursor instanceof PDOStatement) + { + return $cursor->fetchObject($class); + } + + if ($this->prepared instanceof PDOStatement) + { + return $this->prepared->fetchObject($class); + } + } + + /** + * Method to free up the memory used for the result set. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * + * @return void + * + * @since 12.1 + */ + protected function freeResult($cursor = null) + { + $this->executed = false; + + if ($cursor instanceof PDOStatement) + { + $cursor->closeCursor(); + $cursor = null; + } + + if ($this->prepared instanceof PDOStatement) + { + $this->prepared->closeCursor(); + $this->prepared = null; + } + } + + /** + * Method to get the next row in the result set from the database query as an object. + * + * @param string $class The class name to use for the returned row object. + * + * @return mixed The result of the query as an array, false if there are no more rows. + * + * @since 12.1 + * @throws RuntimeException + * @deprecated 4.0 (CMS) Use getIterator() instead + */ + public function loadNextObject($class = 'stdClass') + { + if (class_exists('JLog')) + { + JLog::add(__METHOD__ . '() is deprecated. Use FOFDatabaseDriver::getIterator() instead.', JLog::WARNING, 'deprecated'); + } + $this->connect(); + + // Execute the query and get the result set cursor. + if (!$this->executed) + { + if (!($this->execute())) + { + return $this->errorNum ? null : false; + } + } + + // Get the next row from the result set as an object of type $class. + if ($row = $this->fetchObject(null, $class)) + { + return $row; + } + + // Free up system resources and return. + $this->freeResult(); + + return false; + } + + /** + * Method to get the next row in the result set from the database query as an array. + * + * @return mixed The result of the query as an array, false if there are no more rows. + * + * @since 12.1 + * @throws RuntimeException + */ + public function loadNextAssoc() + { + $this->connect(); + + // Execute the query and get the result set cursor. + if (!$this->executed) + { + if (!($this->execute())) + { + return $this->errorNum ? null : false; + } + } + + // Get the next row from the result set as an object of type $class. + if ($row = $this->fetchAssoc()) + { + return $row; + } + + // Free up system resources and return. + $this->freeResult(); + + return false; + } + + /** + * Method to get the next row in the result set from the database query as an array. + * + * @return mixed The result of the query as an array, false if there are no more rows. + * + * @since 12.1 + * @throws RuntimeException + * @deprecated 4.0 (CMS) Use getIterator() instead + */ + public function loadNextRow() + { + if (class_exists('JLog')) + { + JLog::add(__METHOD__ . '() is deprecated. Use FOFDatabaseDriver::getIterator() instead.', JLog::WARNING, 'deprecated'); + } + $this->connect(); + + // Execute the query and get the result set cursor. + if (!$this->executed) + { + if (!($this->execute())) + { + return $this->errorNum ? null : false; + } + } + + // Get the next row from the result set as an object of type $class. + if ($row = $this->fetchArray()) + { + return $row; + } + + // Free up system resources and return. + $this->freeResult(); + + return false; + } + + /** + * PDO does not support serialize + * + * @return array + * + * @since 12.3 + */ + public function __sleep() + { + $serializedProperties = array(); + + $reflect = new ReflectionClass($this); + + // Get properties of the current class + $properties = $reflect->getProperties(); + + foreach ($properties as $property) + { + // Do not serialize properties that are PDO + if ($property->isStatic() == false && !($this->{$property->name} instanceof PDO)) + { + array_push($serializedProperties, $property->name); + } + } + + return $serializedProperties; + } + + /** + * Wake up after serialization + * + * @return array + * + * @since 12.3 + */ + public function __wakeup() + { + // Get connection back + $this->__construct($this->options); + } + + /** + * Return the actual SQL Error number + * + * @return integer The SQL Error number + * + * @since 3.4.6 + */ + protected function getErrorNumber() + { + return (int) $this->connection->errorCode(); + } + + /** + * Return the actual SQL Error message + * + * @param string $query The SQL Query that fails + * + * @return string The SQL Error message + * + * @since 3.4.6 + */ + protected function getErrorMessage($query) + { + // Note we ignoring $query here as it not used in the original code. + + // The SQL Error Information + $errorInfo = implode(", ", $this->connection->errorInfo()); + + // Replace the Databaseprefix with `#__` if we are not in Debug + if (!$this->debug) + { + $errorInfo = str_replace($this->tablePrefix, '#__', $errorInfo); + } + + return 'SQL: ' . $errorInfo; + } +} diff --git a/libraries/fof/database/driver/pdomysql.php b/libraries/fof/database/driver/pdomysql.php new file mode 100644 index 0000000000000..8e975747878ae --- /dev/null +++ b/libraries/fof/database/driver/pdomysql.php @@ -0,0 +1,559 @@ +utf8mb4 = true; + + // Get some basic values from the options. + $options['driver'] = 'mysql'; + $options['charset'] = (isset($options['charset'])) ? $options['charset'] : 'utf8'; + + if ($this->utf8mb4 && ($options['charset'] == 'utf8')) + { + $options['charset'] = 'utf8mb4'; + } + + $this->charset = $options['charset']; + + // Finalize initialisation. + parent::__construct($options); + } + + /** + * Connects to the database if needed. + * + * @return void Returns void if the database connected successfully. + * + * @since 3.4 + * @throws RuntimeException + */ + public function connect() + { + try + { + // Try to connect to MySQL + parent::connect(); + } + catch (\RuntimeException $e) + { + // If the connection failed but not because of the wrong character set bubble up the exception + if (!$this->utf8mb4 || ($this->options['charset'] != 'utf8mb4')) + { + throw $e; + } + + /** + * If the connection failed and I was trying to use the utf8mb4 charset then it is likely that the server + * doesn't support utf8mb4 despite claiming otherwise. + * + * This happens on old MySQL server versions (less than 5.5.3) using the mysqlnd PHP driver. Since mysqlnd + * masks the server version and reports only its own we can not be sure if the server actually does support + * UTF-8 Multibyte (i.e. it's MySQL 5.5.3 or later). Since the utf8mb4 charset is undefined in this case we + * catch the error and determine that utf8mb4 is not supported! + */ + $this->utf8mb4 = false; + $this->options['charset'] = 'utf8'; + + parent::connect(); + } + + $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); + } + + /** + * Test to see if the MySQL connector is available. + * + * @return boolean True on success, false otherwise. + * + * @since 3.4 + */ + public static function isSupported() + { + return class_exists('PDO') && in_array('mysql', PDO::getAvailableDrivers()); + } + + /** + * Drops a table from the database. + * + * @param string $tableName The name of the database table to drop. + * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. + * + * @return FOFDatabaseDriverPdomysql Returns this object to support chaining. + * + * @since 3.4 + * @throws RuntimeException + */ + public function dropTable($tableName, $ifExists = true) + { + $this->connect(); + + $query = $this->getQuery(true); + + $query->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $this->quoteName($tableName)); + + $this->setQuery($query); + + $this->execute(); + + return $this; + } + + /** + * Select a database for use. + * + * @param string $database The name of the database to select for use. + * + * @return boolean True if the database was successfully selected. + * + * @since 3.4 + * @throws RuntimeException + */ + public function select($database) + { + $this->connect(); + + $this->setQuery('USE ' . $this->quoteName($database)); + + $this->execute(); + + return $this; + } + + /** + * Method to get the database collation in use by sampling a text field of a table in the database. + * + * @return mixed The collation in use by the database (string) or boolean false if not supported. + * + * @since 3.4 + * @throws RuntimeException + */ + public function getCollation() + { + $this->connect(); + + // Attempt to get the database collation by accessing the server system variable. + $this->setQuery('SHOW VARIABLES LIKE "collation_database"'); + $result = $this->loadObject(); + + if (property_exists($result, 'Value')) + { + return $result->Value; + } + else + { + return false; + } + } + + /** + * Method to get the database connection collation, as reported by the driver. If the connector doesn't support + * reporting this value please return an empty string. + * + * @return string + */ + public function getConnectionCollation() + { + $this->connect(); + + // Attempt to get the database collation by accessing the server system variable. + $this->setQuery('SHOW VARIABLES LIKE "collation_connection"'); + $result = $this->loadObject(); + + if (property_exists($result, 'Value')) + { + return $result->Value; + } + else + { + return false; + } + } + + /** + * Shows the table CREATE statement that creates the given tables. + * + * @param mixed $tables A table name or a list of table names. + * + * @return array A list of the create SQL for the tables. + * + * @since 3.4 + * @throws RuntimeException + */ + public function getTableCreate($tables) + { + $this->connect(); + + // Initialise variables. + $result = array(); + + // Sanitize input to an array and iterate over the list. + settype($tables, 'array'); + + foreach ($tables as $table) + { + $this->setQuery('SHOW CREATE TABLE ' . $this->quoteName($table)); + + $row = $this->loadRow(); + + // Populate the result array based on the create statements. + $result[$table] = $row[1]; + } + + return $result; + } + + /** + * Retrieves field information about a given table. + * + * @param string $table The name of the database table. + * @param boolean $typeOnly True to only return field types. + * + * @return array An array of fields for the database table. + * + * @since 3.4 + * @throws RuntimeException + */ + public function getTableColumns($table, $typeOnly = true) + { + $this->connect(); + + $result = array(); + + // Set the query to get the table fields statement. + $this->setQuery('SHOW FULL COLUMNS FROM ' . $this->quoteName($table)); + + $fields = $this->loadObjectList(); + + // If we only want the type as the value add just that to the list. + if ($typeOnly) + { + foreach ($fields as $field) + { + $result[$field->Field] = preg_replace("/[(0-9)]/", '', $field->Type); + } + } + // If we want the whole field data object add that to the list. + else + { + foreach ($fields as $field) + { + $result[$field->Field] = $field; + } + } + + return $result; + } + + /** + * Get the details list of keys for a table. + * + * @param string $table The name of the table. + * + * @return array An array of the column specification for the table. + * + * @since 3.4 + * @throws RuntimeException + */ + public function getTableKeys($table) + { + $this->connect(); + + // Get the details columns information. + $this->setQuery('SHOW KEYS FROM ' . $this->quoteName($table)); + + $keys = $this->loadObjectList(); + + return $keys; + } + + /** + * Method to get an array of all tables in the database. + * + * @return array An array of all the tables in the database. + * + * @since 3.4 + * @throws RuntimeException + */ + public function getTableList() + { + $this->connect(); + + // Set the query to get the tables statement. + $this->setQuery('SHOW TABLES'); + $tables = $this->loadColumn(); + + return $tables; + } + + /** + * Get the version of the database connector. + * + * @return string The database connector version. + * + * @since 3.4 + */ + public function getVersion() + { + $this->connect(); + + return $this->getOption(PDO::ATTR_SERVER_VERSION); + } + + /** + * Locks a table in the database. + * + * @param string $table The name of the table to unlock. + * + * @return FOFDatabaseDriverPdomysql Returns this object to support chaining. + * + * @since 3.4 + * @throws RuntimeException + */ + public function lockTable($table) + { + $this->setQuery('LOCK TABLES ' . $this->quoteName($table) . ' WRITE')->execute(); + + return $this; + } + + /** + * Renames a table in the database. + * + * @param string $oldTable The name of the table to be renamed + * @param string $newTable The new name for the table. + * @param string $backup Not used by MySQL. + * @param string $prefix Not used by MySQL. + * + * @return FOFDatabaseDriverPdomysql Returns this object to support chaining. + * + * @since 3.4 + * @throws RuntimeException + */ + public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) + { + $this->setQuery('RENAME TABLE ' . $this->quoteName($oldTable) . ' TO ' . $this->quoteName($newTable)); + + $this->execute(); + + return $this; + } + + /** + * Method to escape a string for usage in an SQL statement. + * + * Oracle escaping reference: + * http://www.orafaq.com/wiki/SQL_FAQ#How_does_one_escape_special_characters_when_writing_SQL_queries.3F + * + * SQLite escaping notes: + * http://www.sqlite.org/faq.html#q14 + * + * Method body is as implemented by the Zend Framework + * + * Note: Using query objects with bound variables is + * preferable to the below. + * + * @param string $text The string to be escaped. + * @param boolean $extra Unused optional parameter to provide extra escaping. + * + * @return string The escaped string. + * + * @since 3.4 + */ + public function escape($text, $extra = false) + { + $this->connect(); + + if (is_int($text) || is_float($text)) + { + return $text; + } + + $result = substr($this->connection->quote($text), 1, -1); + + if ($extra) + { + $result = addcslashes($result, '%_'); + } + + return $result; + } + + /** + * Unlocks tables in the database. + * + * @return FOFDatabaseDriverPdomysql Returns this object to support chaining. + * + * @since 3.4 + * @throws RuntimeException + */ + public function unlockTables() + { + $this->setQuery('UNLOCK TABLES')->execute(); + + return $this; + } + + /** + * Method to commit a transaction. + * + * @param boolean $toSavepoint If true, commit to the last savepoint. + * + * @return void + * + * @since 3.4 + * @throws RuntimeException + */ + public function transactionCommit($toSavepoint = false) + { + $this->connect(); + + if (!$toSavepoint || $this->transactionDepth <= 1) + { + parent::transactionCommit($toSavepoint); + } + else + { + $this->transactionDepth--; + } + } + + /** + * Method to roll back a transaction. + * + * @param boolean $toSavepoint If true, rollback to the last savepoint. + * + * @return void + * + * @since 3.4 + * @throws RuntimeException + */ + public function transactionRollback($toSavepoint = false) + { + $this->connect(); + + if (!$toSavepoint || $this->transactionDepth <= 1) + { + parent::transactionRollback($toSavepoint); + } + else + { + $savepoint = 'SP_' . ($this->transactionDepth - 1); + $this->setQuery('ROLLBACK TO SAVEPOINT ' . $this->quoteName($savepoint)); + + if ($this->execute()) + { + $this->transactionDepth--; + } + } + } + + /** + * Method to initialize a transaction. + * + * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. + * + * @return void + * + * @since 3.4 + * @throws RuntimeException + */ + public function transactionStart($asSavepoint = false) + { + $this->connect(); + + if (!$asSavepoint || !$this->transactionDepth) + { + parent::transactionStart($asSavepoint); + } + else + { + $savepoint = 'SP_' . $this->transactionDepth; + $this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint)); + + if ($this->execute()) + { + $this->transactionDepth++; + } + } + } +} diff --git a/libraries/fof/database/driver/postgresql.php b/libraries/fof/database/driver/postgresql.php new file mode 100644 index 0000000000000..4908377f2c27d --- /dev/null +++ b/libraries/fof/database/driver/postgresql.php @@ -0,0 +1,1529 @@ +disconnect(); + } + + /** + * Connects to the database if needed. + * + * @return void Returns void if the database connected successfully. + * + * @since 12.1 + * @throws RuntimeException + */ + public function connect() + { + if ($this->connection) + { + return; + } + + // Make sure the postgresql extension for PHP is installed and enabled. + if (!function_exists('pg_connect')) + { + throw new RuntimeException('PHP extension pg_connect is not available.'); + } + + // Build the DSN for the connection. + $dsn = ''; + + if (!empty($this->options['host'])) + { + $dsn .= "host={$this->options['host']} "; + } + + $dsn .= "dbname={$this->options['database']} user={$this->options['user']} password={$this->options['password']}"; + + // Attempt to connect to the server. + if (!($this->connection = @pg_connect($dsn))) + { + throw new RuntimeException('Error connecting to PGSQL database.'); + } + + pg_set_error_verbosity($this->connection, PGSQL_ERRORS_DEFAULT); + pg_query('SET standard_conforming_strings=off'); + pg_query('SET escape_string_warning=off'); + } + + /** + * Disconnects the database. + * + * @return void + * + * @since 12.1 + */ + public function disconnect() + { + // Close the connection. + if (is_resource($this->connection)) + { + foreach ($this->disconnectHandlers as $h) + { + call_user_func_array($h, array( &$this)); + } + + pg_close($this->connection); + } + + $this->connection = null; + } + + /** + * Method to escape a string for usage in an SQL statement. + * + * @param string $text The string to be escaped. + * @param boolean $extra Optional parameter to provide extra escaping. + * + * @return string The escaped string. + * + * @since 12.1 + */ + public function escape($text, $extra = false) + { + $this->connect(); + + $result = pg_escape_string($this->connection, $text); + + if ($extra) + { + $result = addcslashes($result, '%_'); + } + + return $result; + } + + /** + * Test to see if the PostgreSQL connector is available + * + * @return boolean True on success, false otherwise. + * + * @since 12.1 + */ + public static function test() + { + return (function_exists('pg_connect')); + } + + /** + * Determines if the connection to the server is active. + * + * @return boolean + * + * @since 12.1 + */ + public function connected() + { + $this->connect(); + + if (is_resource($this->connection)) + { + return pg_ping($this->connection); + } + + return false; + } + + /** + * Drops a table from the database. + * + * @param string $tableName The name of the database table to drop. + * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. + * + * @return boolean + * + * @since 12.1 + * @throws RuntimeException + */ + public function dropTable($tableName, $ifExists = true) + { + $this->connect(); + + $this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $this->quoteName($tableName)); + $this->execute(); + + return true; + } + + /** + * Get the number of affected rows by the last INSERT, UPDATE, REPLACE or DELETE for the previous executed SQL statement. + * + * @return integer The number of affected rows in the previous operation + * + * @since 12.1 + */ + public function getAffectedRows() + { + $this->connect(); + + return pg_affected_rows($this->cursor); + } + + /** + * Method to get the database collation in use by sampling a text field of a table in the database. + * + * @return mixed The collation in use by the database or boolean false if not supported. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getCollation() + { + $this->connect(); + + $this->setQuery('SHOW LC_COLLATE'); + $array = $this->loadAssocList(); + + return $array[0]['lc_collate']; + } + + /** + * Method to get the database connection collation, as reported by the driver. If the connector doesn't support + * reporting this value please return an empty string. + * + * @return string + */ + public function getConnectionCollation() + { + return pg_client_encoding($this->connection); + } + + /** + * Get the number of returned rows for the previous executed SQL statement. + * This command is only valid for statements like SELECT or SHOW that return an actual result set. + * To retrieve the number of rows affected by a INSERT, UPDATE, REPLACE or DELETE query, use getAffectedRows(). + * + * @param resource $cur An optional database cursor resource to extract the row count from. + * + * @return integer The number of returned rows. + * + * @since 12.1 + */ + public function getNumRows($cur = null) + { + $this->connect(); + + return pg_num_rows((int) $cur ? $cur : $this->cursor); + } + + /** + * Get the current or query, or new FOFDatabaseQuery object. + * + * @param boolean $new False to return the last query set, True to return a new FOFDatabaseQuery object. + * @param boolean $asObj False to return last query as string, true to get FOFDatabaseQueryPostgresql object. + * + * @return FOFDatabaseQuery The current query object or a new object extending the FOFDatabaseQuery class. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getQuery($new = false, $asObj = false) + { + if ($new) + { + // Make sure we have a query class for this driver. + if (!class_exists('FOFDatabaseQueryPostgresql')) + { + throw new RuntimeException('FOFDatabaseQueryPostgresql Class not found.'); + } + + $this->queryObject = new FOFDatabaseQueryPostgresql($this); + + return $this->queryObject; + } + else + { + if ($asObj) + { + return $this->queryObject; + } + else + { + return $this->sql; + } + } + } + + /** + * Shows the table CREATE statement that creates the given tables. + * + * This is unsuported by PostgreSQL. + * + * @param mixed $tables A table name or a list of table names. + * + * @return string An empty char because this function is not supported by PostgreSQL. + * + * @since 12.1 + */ + public function getTableCreate($tables) + { + return ''; + } + + /** + * Retrieves field information about a given table. + * + * @param string $table The name of the database table. + * @param boolean $typeOnly True to only return field types. + * + * @return array An array of fields for the database table. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getTableColumns($table, $typeOnly = true) + { + $this->connect(); + + $result = array(); + + $tableSub = $this->replacePrefix($table); + + $this->setQuery(' + SELECT a.attname AS "column_name", + pg_catalog.format_type(a.atttypid, a.atttypmod) as "type", + CASE WHEN a.attnotnull IS TRUE + THEN \'NO\' + ELSE \'YES\' + END AS "null", + CASE WHEN pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) IS NOT NULL + THEN pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) + END as "Default", + CASE WHEN pg_catalog.col_description(a.attrelid, a.attnum) IS NULL + THEN \'\' + ELSE pg_catalog.col_description(a.attrelid, a.attnum) + END AS "comments" + FROM pg_catalog.pg_attribute a + LEFT JOIN pg_catalog.pg_attrdef adef ON a.attrelid=adef.adrelid AND a.attnum=adef.adnum + LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid + WHERE a.attrelid = + (SELECT oid FROM pg_catalog.pg_class WHERE relname=' . $this->quote($tableSub) . ' + AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE + nspname = \'public\') + ) + AND a.attnum > 0 AND NOT a.attisdropped + ORDER BY a.attnum' + ); + + $fields = $this->loadObjectList(); + + if ($typeOnly) + { + foreach ($fields as $field) + { + $result[$field->column_name] = preg_replace("/[(0-9)]/", '', $field->type); + } + } + else + { + foreach ($fields as $field) + { + if (stristr(strtolower($field->type), "character varying")) + { + $field->Default = ""; + } + if (stristr(strtolower($field->type), "text")) + { + $field->Default = ""; + } + // Do some dirty translation to MySQL output. + // TODO: Come up with and implement a standard across databases. + $result[$field->column_name] = (object) array( + 'column_name' => $field->column_name, + 'type' => $field->type, + 'null' => $field->null, + 'Default' => $field->Default, + 'comments' => '', + 'Field' => $field->column_name, + 'Type' => $field->type, + 'Null' => $field->null, + // TODO: Improve query above to return primary key info as well + // 'Key' => ($field->PK == '1' ? 'PRI' : '') + ); + } + } + + /* Change Postgresql's NULL::* type with PHP's null one */ + foreach ($fields as $field) + { + if (preg_match("/^NULL::*/", $field->Default)) + { + $field->Default = null; + } + } + + return $result; + } + + /** + * Get the details list of keys for a table. + * + * @param string $table The name of the table. + * + * @return array An array of the column specification for the table. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getTableKeys($table) + { + $this->connect(); + + // To check if table exists and prevent SQL injection + $tableList = $this->getTableList(); + + if (in_array($table, $tableList)) + { + // Get the details columns information. + $this->setQuery(' + SELECT indexname AS "idxName", indisprimary AS "isPrimary", indisunique AS "isUnique", + CASE WHEN indisprimary = true THEN + ( SELECT \'ALTER TABLE \' || tablename || \' ADD \' || pg_catalog.pg_get_constraintdef(const.oid, true) + FROM pg_constraint AS const WHERE const.conname= pgClassFirst.relname ) + ELSE pg_catalog.pg_get_indexdef(indexrelid, 0, true) + END AS "Query" + FROM pg_indexes + LEFT JOIN pg_class AS pgClassFirst ON indexname=pgClassFirst.relname + LEFT JOIN pg_index AS pgIndex ON pgClassFirst.oid=pgIndex.indexrelid + WHERE tablename=' . $this->quote($table) . ' ORDER BY indkey' + ); + + $keys = $this->loadObjectList(); + + return $keys; + } + + return false; + } + + /** + * Method to get an array of all tables in the database. + * + * @return array An array of all the tables in the database. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getTableList() + { + $this->connect(); + + $query = $this->getQuery(true) + ->select('table_name') + ->from('information_schema.tables') + ->where('table_type=' . $this->quote('BASE TABLE')) + ->where('table_schema NOT IN (' . $this->quote('pg_catalog') . ', ' . $this->quote('information_schema') . ')') + ->order('table_name ASC'); + + $this->setQuery($query); + $tables = $this->loadColumn(); + + return $tables; + } + + /** + * Get the details list of sequences for a table. + * + * @param string $table The name of the table. + * + * @return array An array of sequences specification for the table. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getTableSequences($table) + { + $this->connect(); + + // To check if table exists and prevent SQL injection + $tableList = $this->getTableList(); + + if (in_array($table, $tableList)) + { + $name = array( + 's.relname', 'n.nspname', 't.relname', 'a.attname', 'info.data_type', 'info.minimum_value', 'info.maximum_value', + 'info.increment', 'info.cycle_option' + ); + $as = array('sequence', 'schema', 'table', 'column', 'data_type', 'minimum_value', 'maximum_value', 'increment', 'cycle_option'); + + if (version_compare($this->getVersion(), '9.1.0') >= 0) + { + $name[] .= 'info.start_value'; + $as[] .= 'start_value'; + } + + // Get the details columns information. + $query = $this->getQuery(true) + ->select($this->quoteName($name, $as)) + ->from('pg_class AS s') + ->join('LEFT', "pg_depend d ON d.objid=s.oid AND d.classid='pg_class'::regclass AND d.refclassid='pg_class'::regclass") + ->join('LEFT', 'pg_class t ON t.oid=d.refobjid') + ->join('LEFT', 'pg_namespace n ON n.oid=t.relnamespace') + ->join('LEFT', 'pg_attribute a ON a.attrelid=t.oid AND a.attnum=d.refobjsubid') + ->join('LEFT', 'information_schema.sequences AS info ON info.sequence_name=s.relname') + ->where("s.relkind='S' AND d.deptype='a' AND t.relname=" . $this->quote($table)); + $this->setQuery($query); + $seq = $this->loadObjectList(); + + return $seq; + } + + return false; + } + + /** + * Get the version of the database connector. + * + * @return string The database connector version. + * + * @since 12.1 + */ + public function getVersion() + { + $this->connect(); + $version = pg_version($this->connection); + + return $version['server']; + } + + /** + * Method to get the auto-incremented value from the last INSERT statement. + * To be called after the INSERT statement, it's MANDATORY to have a sequence on + * every primary key table. + * + * To get the auto incremented value it's possible to call this function after + * INSERT INTO query, or use INSERT INTO with RETURNING clause. + * + * @example with insertid() call: + * $query = $this->getQuery(true) + * ->insert('jos_dbtest') + * ->columns('title,start_date,description') + * ->values("'testTitle2nd','1971-01-01','testDescription2nd'"); + * $this->setQuery($query); + * $this->execute(); + * $id = $this->insertid(); + * + * @example with RETURNING clause: + * $query = $this->getQuery(true) + * ->insert('jos_dbtest') + * ->columns('title,start_date,description') + * ->values("'testTitle2nd','1971-01-01','testDescription2nd'") + * ->returning('id'); + * $this->setQuery($query); + * $id = $this->loadResult(); + * + * @return integer The value of the auto-increment field from the last inserted row. + * + * @since 12.1 + */ + public function insertid() + { + $this->connect(); + $insertQuery = $this->getQuery(false, true); + $table = $insertQuery->__get('insert')->getElements(); + + /* find sequence column name */ + $colNameQuery = $this->getQuery(true); + $colNameQuery->select('column_default') + ->from('information_schema.columns') + ->where("table_name=" . $this->quote($this->replacePrefix(str_replace('"', '', $table[0]))), 'AND') + ->where("column_default LIKE '%nextval%'"); + + $this->setQuery($colNameQuery); + $colName = $this->loadRow(); + $changedColName = str_replace('nextval', 'currval', $colName); + + $insertidQuery = $this->getQuery(true); + $insertidQuery->select($changedColName); + $this->setQuery($insertidQuery); + $insertVal = $this->loadRow(); + + return $insertVal[0]; + } + + /** + * Locks a table in the database. + * + * @param string $tableName The name of the table to unlock. + * + * @return FOFDatabaseDriverPostgresql Returns this object to support chaining. + * + * @since 12.1 + * @throws RuntimeException + */ + public function lockTable($tableName) + { + $this->transactionStart(); + $this->setQuery('LOCK TABLE ' . $this->quoteName($tableName) . ' IN ACCESS EXCLUSIVE MODE')->execute(); + + return $this; + } + + /** + * Execute the SQL statement. + * + * @return mixed A database cursor resource on success, boolean false on failure. + * + * @since 12.1 + * @throws RuntimeException + */ + public function execute() + { + $this->connect(); + + if (!is_resource($this->connection)) + { + if (class_exists('JLog')) + { + JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); + } + throw new RuntimeException($this->errorMsg, $this->errorNum); + } + + // Take a local copy so that we don't modify the original query and cause issues later + $query = $this->replacePrefix((string) $this->sql); + + if (!($this->sql instanceof FOFDatabaseQuery) && ($this->limit > 0 || $this->offset > 0)) + { + $query .= ' LIMIT ' . $this->limit . ' OFFSET ' . $this->offset; + } + + // Increment the query counter. + $this->count++; + + // Reset the error values. + $this->errorNum = 0; + $this->errorMsg = ''; + + // If debugging is enabled then let's log the query. + if ($this->debug) + { + // Add the query to the object queue. + $this->log[] = $query; + + if (class_exists('JLog')) + { + JLog::add($query, JLog::DEBUG, 'databasequery'); + } + + $this->timings[] = microtime(true); + } + + // Execute the query. Error suppression is used here to prevent warnings/notices that the connection has been lost. + $this->cursor = @pg_query($this->connection, $query); + + if ($this->debug) + { + $this->timings[] = microtime(true); + + if (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) + { + $this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + } + else + { + $this->callStacks[] = debug_backtrace(); + } + } + + // If an error occurred handle it. + if (!$this->cursor) + { + // Get the error number and message before we execute any more queries. + $errorNum = $this->getErrorNumber(); + $errorMsg = $this->getErrorMessage($query); + + // Check if the server was disconnected. + if (!$this->connected()) + { + try + { + // Attempt to reconnect. + $this->connection = null; + $this->connect(); + } + // If connect fails, ignore that exception and throw the normal exception. + catch (RuntimeException $e) + { + $this->errorNum = $this->getErrorNumber(); + $this->errorMsg = $this->getErrorMessage($query); + + // Throw the normal query exception. + if (class_exists('JLog')) + { + JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); + } + + throw new RuntimeException($this->errorMsg, null, $e); + } + + // Since we were able to reconnect, run the query again. + return $this->execute(); + } + // The server was not disconnected. + else + { + // Get the error number and message from before we tried to reconnect. + $this->errorNum = $errorNum; + $this->errorMsg = $errorMsg; + + // Throw the normal query exception. + if (class_exists('JLog')) + { + JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); + } + + throw new RuntimeException($this->errorMsg); + } + } + + return $this->cursor; + } + + /** + * Renames a table in the database. + * + * @param string $oldTable The name of the table to be renamed + * @param string $newTable The new name for the table. + * @param string $backup Not used by PostgreSQL. + * @param string $prefix Not used by PostgreSQL. + * + * @return FOFDatabaseDriverPostgresql Returns this object to support chaining. + * + * @since 12.1 + * @throws RuntimeException + */ + public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) + { + $this->connect(); + + // To check if table exists and prevent SQL injection + $tableList = $this->getTableList(); + + // Origin Table does not exist + if (!in_array($oldTable, $tableList)) + { + // Origin Table not found + throw new RuntimeException('Table not found in Postgresql database.'); + } + else + { + /* Rename indexes */ + $this->setQuery( + 'SELECT relname + FROM pg_class + WHERE oid IN ( + SELECT indexrelid + FROM pg_index, pg_class + WHERE pg_class.relname=' . $this->quote($oldTable, true) . ' + AND pg_class.oid=pg_index.indrelid );' + ); + + $oldIndexes = $this->loadColumn(); + + foreach ($oldIndexes as $oldIndex) + { + $changedIdxName = str_replace($oldTable, $newTable, $oldIndex); + $this->setQuery('ALTER INDEX ' . $this->escape($oldIndex) . ' RENAME TO ' . $this->escape($changedIdxName)); + $this->execute(); + } + + /* Rename sequence */ + $this->setQuery( + 'SELECT relname + FROM pg_class + WHERE relkind = \'S\' + AND relnamespace IN ( + SELECT oid + FROM pg_namespace + WHERE nspname NOT LIKE \'pg_%\' + AND nspname != \'information_schema\' + ) + AND relname LIKE \'%' . $oldTable . '%\' ;' + ); + + $oldSequences = $this->loadColumn(); + + foreach ($oldSequences as $oldSequence) + { + $changedSequenceName = str_replace($oldTable, $newTable, $oldSequence); + $this->setQuery('ALTER SEQUENCE ' . $this->escape($oldSequence) . ' RENAME TO ' . $this->escape($changedSequenceName)); + $this->execute(); + } + + /* Rename table */ + $this->setQuery('ALTER TABLE ' . $this->escape($oldTable) . ' RENAME TO ' . $this->escape($newTable)); + $this->execute(); + } + + return true; + } + + /** + * Selects the database, but redundant for PostgreSQL + * + * @param string $database Database name to select. + * + * @return boolean Always true + * + * @since 12.1 + */ + public function select($database) + { + return true; + } + + /** + * Custom settings for UTF support + * + * @return integer Zero on success, -1 on failure + * + * @since 12.1 + */ + public function setUtf() + { + $this->connect(); + + return pg_set_client_encoding($this->connection, 'UTF8'); + } + + /** + * This function return a field value as a prepared string to be used in a SQL statement. + * + * @param array $columns Array of table's column returned by ::getTableColumns. + * @param string $field_name The table field's name. + * @param string $field_value The variable value to quote and return. + * + * @return string The quoted string. + * + * @since 12.1 + */ + public function sqlValue($columns, $field_name, $field_value) + { + switch ($columns[$field_name]) + { + case 'boolean': + $val = 'NULL'; + + if ($field_value == 't') + { + $val = 'TRUE'; + } + elseif ($field_value == 'f') + { + $val = 'FALSE'; + } + + break; + + case 'bigint': + case 'bigserial': + case 'integer': + case 'money': + case 'numeric': + case 'real': + case 'smallint': + case 'serial': + case 'numeric,': + $val = strlen($field_value) == 0 ? 'NULL' : $field_value; + break; + + case 'date': + case 'timestamp without time zone': + if (empty($field_value)) + { + $field_value = $this->getNullDate(); + } + + $val = $this->quote($field_value); + break; + + default: + $val = $this->quote($field_value); + break; + } + + return $val; + } + + /** + * Method to commit a transaction. + * + * @param boolean $toSavepoint If true, commit to the last savepoint. + * + * @return void + * + * @since 12.1 + * @throws RuntimeException + */ + public function transactionCommit($toSavepoint = false) + { + $this->connect(); + + if (!$toSavepoint || $this->transactionDepth <= 1) + { + if ($this->setQuery('COMMIT')->execute()) + { + $this->transactionDepth = 0; + } + + return; + } + + $this->transactionDepth--; + } + + /** + * Method to roll back a transaction. + * + * @param boolean $toSavepoint If true, rollback to the last savepoint. + * + * @return void + * + * @since 12.1 + * @throws RuntimeException + */ + public function transactionRollback($toSavepoint = false) + { + $this->connect(); + + if (!$toSavepoint || $this->transactionDepth <= 1) + { + if ($this->setQuery('ROLLBACK')->execute()) + { + $this->transactionDepth = 0; + } + + return; + } + + $savepoint = 'SP_' . ($this->transactionDepth - 1); + $this->setQuery('ROLLBACK TO SAVEPOINT ' . $this->quoteName($savepoint)); + + if ($this->execute()) + { + $this->transactionDepth--; + $this->setQuery('RELEASE SAVEPOINT ' . $this->quoteName($savepoint))->execute(); + } + } + + /** + * Method to initialize a transaction. + * + * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. + * + * @return void + * + * @since 12.1 + * @throws RuntimeException + */ + public function transactionStart($asSavepoint = false) + { + $this->connect(); + + if (!$asSavepoint || !$this->transactionDepth) + { + if ($this->setQuery('START TRANSACTION')->execute()) + { + $this->transactionDepth = 1; + } + + return; + } + + $savepoint = 'SP_' . $this->transactionDepth; + $this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint)); + + if ($this->execute()) + { + $this->transactionDepth++; + } + } + + /** + * Method to fetch a row from the result set cursor as an array. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * + * @return mixed Either the next row from the result set or false if there are no more rows. + * + * @since 12.1 + */ + protected function fetchArray($cursor = null) + { + return pg_fetch_row($cursor ? $cursor : $this->cursor); + } + + /** + * Method to fetch a row from the result set cursor as an associative array. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * + * @return mixed Either the next row from the result set or false if there are no more rows. + * + * @since 12.1 + */ + protected function fetchAssoc($cursor = null) + { + return pg_fetch_assoc($cursor ? $cursor : $this->cursor); + } + + /** + * Method to fetch a row from the result set cursor as an object. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * @param string $class The class name to use for the returned row object. + * + * @return mixed Either the next row from the result set or false if there are no more rows. + * + * @since 12.1 + */ + protected function fetchObject($cursor = null, $class = 'stdClass') + { + return pg_fetch_object(is_null($cursor) ? $this->cursor : $cursor, null, $class); + } + + /** + * Method to free up the memory used for the result set. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * + * @return void + * + * @since 12.1 + */ + protected function freeResult($cursor = null) + { + pg_free_result($cursor ? $cursor : $this->cursor); + } + + /** + * Inserts a row into a table based on an object's properties. + * + * @param string $table The name of the database table to insert into. + * @param object &$object A reference to an object whose public properties match the table fields. + * @param string $key The name of the primary key. If provided the object property is updated. + * + * @return boolean True on success. + * + * @since 12.1 + * @throws RuntimeException + */ + public function insertObject($table, &$object, $key = null) + { + $columns = $this->getTableColumns($table); + + $fields = array(); + $values = array(); + + // Iterate over the object variables to build the query fields and values. + foreach (get_object_vars($object) as $k => $v) + { + // Only process non-null scalars. + if (is_array($v) or is_object($v) or $v === null) + { + continue; + } + + // Ignore any internal fields or primary keys with value 0. + if (($k[0] == "_") || ($k == $key && (($v === 0) || ($v === '0')))) + { + continue; + } + + // Prepare and sanitize the fields and values for the database query. + $fields[] = $this->quoteName($k); + $values[] = $this->sqlValue($columns, $k, $v); + } + + // Create the base insert statement. + $query = $this->getQuery(true) + ->insert($this->quoteName($table)) + ->columns($fields) + ->values(implode(',', $values)); + + $retVal = false; + + if ($key) + { + $query->returning($key); + + // Set the query and execute the insert. + $this->setQuery($query); + + $id = $this->loadResult(); + + if ($id) + { + $object->$key = $id; + $retVal = true; + } + } + else + { + // Set the query and execute the insert. + $this->setQuery($query); + + if ($this->execute()) + { + $retVal = true; + } + } + + return $retVal; + } + + /** + * Test to see if the PostgreSQL connector is available. + * + * @return boolean True on success, false otherwise. + * + * @since 12.1 + */ + public static function isSupported() + { + return (function_exists('pg_connect')); + } + + /** + * Returns an array containing database's table list. + * + * @return array The database's table list. + * + * @since 12.1 + */ + public function showTables() + { + $this->connect(); + + $query = $this->getQuery(true) + ->select('table_name') + ->from('information_schema.tables') + ->where('table_type = ' . $this->quote('BASE TABLE')) + ->where('table_schema NOT IN (' . $this->quote('pg_catalog') . ', ' . $this->quote('information_schema') . ' )'); + + $this->setQuery($query); + $tableList = $this->loadColumn(); + + return $tableList; + } + + /** + * Get the substring position inside a string + * + * @param string $substring The string being sought + * @param string $string The string/column being searched + * + * @return integer The position of $substring in $string + * + * @since 12.1 + */ + public function getStringPositionSql( $substring, $string ) + { + $this->connect(); + + $query = "SELECT POSITION( $substring IN $string )"; + $this->setQuery($query); + $position = $this->loadRow(); + + return $position['position']; + } + + /** + * Generate a random value + * + * @return float The random generated number + * + * @since 12.1 + */ + public function getRandom() + { + $this->connect(); + + $this->setQuery('SELECT RANDOM()'); + $random = $this->loadAssoc(); + + return $random['random']; + } + + /** + * Get the query string to alter the database character set. + * + * @param string $dbName The database name + * + * @return string The query that alter the database query string + * + * @since 12.1 + */ + public function getAlterDbCharacterSet( $dbName ) + { + $query = 'ALTER DATABASE ' . $this->quoteName($dbName) . ' SET CLIENT_ENCODING TO ' . $this->quote('UTF8'); + + return $query; + } + + /** + * Get the query string to create new Database in correct PostgreSQL syntax. + * + * @param object $options object coming from "initialise" function to pass user and database name to database driver. + * @param boolean $utf True if the database supports the UTF-8 character set, not used in PostgreSQL "CREATE DATABASE" query. + * + * @return string The query that creates database, owned by $options['user'] + * + * @since 12.1 + */ + public function getCreateDbQuery($options, $utf) + { + $query = 'CREATE DATABASE ' . $this->quoteName($options->db_name) . ' OWNER ' . $this->quoteName($options->db_user); + + if ($utf) + { + $query .= ' ENCODING ' . $this->quote('UTF-8'); + } + + return $query; + } + + /** + * This function replaces a string identifier $prefix with the string held is the + * tablePrefix class variable. + * + * @param string $query The SQL statement to prepare. + * @param string $prefix The common table prefix. + * + * @return string The processed SQL statement. + * + * @since 12.1 + */ + public function replacePrefix($query, $prefix = '#__') + { + $query = trim($query); + + if (strpos($query, '\'')) + { + // Sequence name quoted with ' ' but need to be replaced + if (strpos($query, 'currval')) + { + $query = explode('currval', $query); + + for ($nIndex = 1; $nIndex < count($query); $nIndex = $nIndex + 2) + { + $query[$nIndex] = str_replace($prefix, $this->tablePrefix, $query[$nIndex]); + } + + $query = implode('currval', $query); + } + + // Sequence name quoted with ' ' but need to be replaced + if (strpos($query, 'nextval')) + { + $query = explode('nextval', $query); + + for ($nIndex = 1; $nIndex < count($query); $nIndex = $nIndex + 2) + { + $query[$nIndex] = str_replace($prefix, $this->tablePrefix, $query[$nIndex]); + } + + $query = implode('nextval', $query); + } + + // Sequence name quoted with ' ' but need to be replaced + if (strpos($query, 'setval')) + { + $query = explode('setval', $query); + + for ($nIndex = 1; $nIndex < count($query); $nIndex = $nIndex + 2) + { + $query[$nIndex] = str_replace($prefix, $this->tablePrefix, $query[$nIndex]); + } + + $query = implode('setval', $query); + } + + $explodedQuery = explode('\'', $query); + + for ($nIndex = 0; $nIndex < count($explodedQuery); $nIndex = $nIndex + 2) + { + if (strpos($explodedQuery[$nIndex], $prefix)) + { + $explodedQuery[$nIndex] = str_replace($prefix, $this->tablePrefix, $explodedQuery[$nIndex]); + } + } + + $replacedQuery = implode('\'', $explodedQuery); + } + else + { + $replacedQuery = str_replace($prefix, $this->tablePrefix, $query); + } + + return $replacedQuery; + } + + /** + * Method to release a savepoint. + * + * @param string $savepointName Savepoint's name to release + * + * @return void + * + * @since 12.1 + */ + public function releaseTransactionSavepoint( $savepointName ) + { + $this->connect(); + $this->setQuery('RELEASE SAVEPOINT ' . $this->quoteName($this->escape($savepointName))); + $this->execute(); + } + + /** + * Method to create a savepoint. + * + * @param string $savepointName Savepoint's name to create + * + * @return void + * + * @since 12.1 + */ + public function transactionSavepoint( $savepointName ) + { + $this->connect(); + $this->setQuery('SAVEPOINT ' . $this->quoteName($this->escape($savepointName))); + $this->execute(); + } + + /** + * Unlocks tables in the database, this command does not exist in PostgreSQL, + * it is automatically done on commit or rollback. + * + * @return FOFDatabaseDriverPostgresql Returns this object to support chaining. + * + * @since 12.1 + * @throws RuntimeException + */ + public function unlockTables() + { + $this->transactionCommit(); + + return $this; + } + + /** + * Updates a row in a table based on an object's properties. + * + * @param string $table The name of the database table to update. + * @param object &$object A reference to an object whose public properties match the table fields. + * @param array $key The name of the primary key. + * @param boolean $nulls True to update null fields or false to ignore them. + * + * @return boolean True on success. + * + * @since 12.1 + * @throws RuntimeException + */ + public function updateObject($table, &$object, $key, $nulls = false) + { + $columns = $this->getTableColumns($table); + $fields = array(); + $where = array(); + + if (is_string($key)) + { + $key = array($key); + } + + if (is_object($key)) + { + $key = (array) $key; + } + + // Create the base update statement. + $statement = 'UPDATE ' . $this->quoteName($table) . ' SET %s WHERE %s'; + + // Iterate over the object variables to build the query fields/value pairs. + foreach (get_object_vars($object) as $k => $v) + { + // Only process scalars that are not internal fields. + if (is_array($v) or is_object($v) or $k[0] == '_') + { + continue; + } + + // Set the primary key to the WHERE clause instead of a field to update. + if (in_array($k, $key)) + { + $key_val = $this->sqlValue($columns, $k, $v); + $where[] = $this->quoteName($k) . '=' . $key_val; + continue; + } + + // Prepare and sanitize the fields and values for the database query. + if ($v === null) + { + // If the value is null and we want to update nulls then set it. + if ($nulls) + { + $val = 'NULL'; + } + // If the value is null and we do not want to update nulls then ignore this field. + else + { + continue; + } + } + // The field is not null so we prep it for update. + else + { + $val = $this->sqlValue($columns, $k, $v); + } + + // Add the field to be updated. + $fields[] = $this->quoteName($k) . '=' . $val; + } + + // We don't have any fields to update. + if (empty($fields)) + { + return true; + } + + // Set the query and execute the update. + $this->setQuery(sprintf($statement, implode(",", $fields), implode(' AND ', $where))); + + return $this->execute(); + } + + /** + * Return the actual SQL Error number + * + * @return integer The SQL Error number + * + * @since 3.4.6 + */ + protected function getErrorNumber() + { + return (int) pg_result_error_field($this->cursor, PGSQL_DIAG_SQLSTATE) . ' '; + } + + /** + * Return the actual SQL Error message + * + * @param string $query The SQL Query that fails + * + * @return string The SQL Error message + * + * @since 3.4.6 + */ + protected function getErrorMessage($query) + { + $errorMessage = (string) pg_last_error($this->connection); + + // Replace the Databaseprefix with `#__` if we are not in Debug + if (!$this->debug) + { + $errorMessage = str_replace($this->tablePrefix, '#__', $errorMessage); + $query = str_replace($this->tablePrefix, '#__', $query); + } + + return $errorMessage . "SQL=" . $query; + } +} diff --git a/libraries/fof/database/driver/sqlazure.php b/libraries/fof/database/driver/sqlazure.php new file mode 100644 index 0000000000000..90e0a4f8c0db2 --- /dev/null +++ b/libraries/fof/database/driver/sqlazure.php @@ -0,0 +1,30 @@ +freeResult(); + unset($this->connection); + } + + /** + * Disconnects the database. + * + * @return void + * + * @since 12.1 + */ + public function disconnect() + { + $this->freeResult(); + unset($this->connection); + } + + /** + * Drops a table from the database. + * + * @param string $tableName The name of the database table to drop. + * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. + * + * @return FOFDatabaseDriverSqlite Returns this object to support chaining. + * + * @since 12.1 + */ + public function dropTable($tableName, $ifExists = true) + { + $this->connect(); + + $query = $this->getQuery(true); + + $this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $query->quoteName($tableName)); + + $this->execute(); + + return $this; + } + + /** + * Method to escape a string for usage in an SQLite statement. + * + * Note: Using query objects with bound variables is + * preferable to the below. + * + * @param string $text The string to be escaped. + * @param boolean $extra Unused optional parameter to provide extra escaping. + * + * @return string The escaped string. + * + * @since 12.1 + */ + public function escape($text, $extra = false) + { + if (is_int($text) || is_float($text)) + { + return $text; + } + + return SQLite3::escapeString($text); + } + + /** + * Method to get the database collation in use by sampling a text field of a table in the database. + * + * @return mixed The collation in use by the database or boolean false if not supported. + * + * @since 12.1 + */ + public function getCollation() + { + return $this->charset; + } + + /** + * Method to get the database connection collation, as reported by the driver. If the connector doesn't support + * reporting this value please return an empty string. + * + * @return string + */ + public function getConnectionCollation() + { + return $this->charset; + } + + /** + * Shows the table CREATE statement that creates the given tables. + * + * Note: Doesn't appear to have support in SQLite + * + * @param mixed $tables A table name or a list of table names. + * + * @return array A list of the create SQL for the tables. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getTableCreate($tables) + { + $this->connect(); + + // Sanitize input to an array and iterate over the list. + settype($tables, 'array'); + + return $tables; + } + + /** + * Retrieves field information about a given table. + * + * @param string $table The name of the database table. + * @param boolean $typeOnly True to only return field types. + * + * @return array An array of fields for the database table. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getTableColumns($table, $typeOnly = true) + { + $this->connect(); + + $columns = array(); + $query = $this->getQuery(true); + + $fieldCasing = $this->getOption(PDO::ATTR_CASE); + + $this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER); + + $table = strtoupper($table); + + $query->setQuery('pragma table_info(' . $table . ')'); + + $this->setQuery($query); + $fields = $this->loadObjectList(); + + if ($typeOnly) + { + foreach ($fields as $field) + { + $columns[$field->NAME] = $field->TYPE; + } + } + else + { + foreach ($fields as $field) + { + // Do some dirty translation to MySQL output. + // TODO: Come up with and implement a standard across databases. + $columns[$field->NAME] = (object) array( + 'Field' => $field->NAME, + 'Type' => $field->TYPE, + 'Null' => ($field->NOTNULL == '1' ? 'NO' : 'YES'), + 'Default' => $field->DFLT_VALUE, + 'Key' => ($field->PK != '0' ? 'PRI' : '') + ); + } + } + + $this->setOption(PDO::ATTR_CASE, $fieldCasing); + + return $columns; + } + + /** + * Get the details list of keys for a table. + * + * @param string $table The name of the table. + * + * @return array An array of the column specification for the table. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getTableKeys($table) + { + $this->connect(); + + $keys = array(); + $query = $this->getQuery(true); + + $fieldCasing = $this->getOption(PDO::ATTR_CASE); + + $this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER); + + $table = strtoupper($table); + $query->setQuery('pragma table_info( ' . $table . ')'); + + // $query->bind(':tableName', $table); + + $this->setQuery($query); + $rows = $this->loadObjectList(); + + foreach ($rows as $column) + { + if ($column->PK == 1) + { + $keys[$column->NAME] = $column; + } + } + + $this->setOption(PDO::ATTR_CASE, $fieldCasing); + + return $keys; + } + + /** + * Method to get an array of all tables in the database (schema). + * + * @return array An array of all the tables in the database. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getTableList() + { + $this->connect(); + + $type = 'table'; + + $query = $this->getQuery(true) + ->select('name') + ->from('sqlite_master') + ->where('type = :type') + ->bind(':type', $type) + ->order('name'); + + $this->setQuery($query); + + $tables = $this->loadColumn(); + + return $tables; + } + + /** + * Get the version of the database connector. + * + * @return string The database connector version. + * + * @since 12.1 + */ + public function getVersion() + { + $this->connect(); + + $this->setQuery("SELECT sqlite_version()"); + + return $this->loadResult(); + } + + /** + * Select a database for use. + * + * @param string $database The name of the database to select for use. + * + * @return boolean True if the database was successfully selected. + * + * @since 12.1 + * @throws RuntimeException + */ + public function select($database) + { + $this->connect(); + + return true; + } + + /** + * Set the connection to use UTF-8 character encoding. + * + * Returns false automatically for the Oracle driver since + * you can only set the character set when the connection + * is created. + * + * @return boolean True on success. + * + * @since 12.1 + */ + public function setUtf() + { + $this->connect(); + + return false; + } + + /** + * Locks a table in the database. + * + * @param string $table The name of the table to unlock. + * + * @return FOFDatabaseDriverSqlite Returns this object to support chaining. + * + * @since 12.1 + * @throws RuntimeException + */ + public function lockTable($table) + { + return $this; + } + + /** + * Renames a table in the database. + * + * @param string $oldTable The name of the table to be renamed + * @param string $newTable The new name for the table. + * @param string $backup Not used by Sqlite. + * @param string $prefix Not used by Sqlite. + * + * @return FOFDatabaseDriverSqlite Returns this object to support chaining. + * + * @since 12.1 + * @throws RuntimeException + */ + public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) + { + $this->setQuery('ALTER TABLE ' . $oldTable . ' RENAME TO ' . $newTable)->execute(); + + return $this; + } + + /** + * Unlocks tables in the database. + * + * @return FOFDatabaseDriverSqlite Returns this object to support chaining. + * + * @since 12.1 + * @throws RuntimeException + */ + public function unlockTables() + { + return $this; + } + + /** + * Test to see if the PDO ODBC connector is available. + * + * @return boolean True on success, false otherwise. + * + * @since 12.1 + */ + public static function isSupported() + { + return class_exists('PDO') && in_array('sqlite', PDO::getAvailableDrivers()); + } + + /** + * Method to commit a transaction. + * + * @param boolean $toSavepoint If true, commit to the last savepoint. + * + * @return void + * + * @since 12.3 + * @throws RuntimeException + */ + public function transactionCommit($toSavepoint = false) + { + $this->connect(); + + if (!$toSavepoint || $this->transactionDepth <= 1) + { + parent::transactionCommit($toSavepoint); + } + else + { + $this->transactionDepth--; + } + } + + /** + * Method to roll back a transaction. + * + * @param boolean $toSavepoint If true, rollback to the last savepoint. + * + * @return void + * + * @since 12.3 + * @throws RuntimeException + */ + public function transactionRollback($toSavepoint = false) + { + $this->connect(); + + if (!$toSavepoint || $this->transactionDepth <= 1) + { + parent::transactionRollback($toSavepoint); + } + else + { + $savepoint = 'SP_' . ($this->transactionDepth - 1); + $this->setQuery('ROLLBACK TO ' . $this->quoteName($savepoint)); + + if ($this->execute()) + { + $this->transactionDepth--; + } + } + } + + /** + * Method to initialize a transaction. + * + * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. + * + * @return void + * + * @since 12.3 + * @throws RuntimeException + */ + public function transactionStart($asSavepoint = false) + { + $this->connect(); + + if (!$asSavepoint || !$this->transactionDepth) + { + parent::transactionStart($asSavepoint); + } + + $savepoint = 'SP_' . $this->transactionDepth; + $this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint)); + + if ($this->execute()) + { + $this->transactionDepth++; + } + } +} diff --git a/libraries/fof/database/driver/sqlsrv.php b/libraries/fof/database/driver/sqlsrv.php new file mode 100644 index 0000000000000..cf47e2b873d7a --- /dev/null +++ b/libraries/fof/database/driver/sqlsrv.php @@ -0,0 +1,1171 @@ +disconnect(); + } + + /** + * Connects to the database if needed. + * + * @return void Returns void if the database connected successfully. + * + * @since 12.1 + * @throws RuntimeException + */ + public function connect() + { + if ($this->connection) + { + return; + } + + // Build the connection configuration array. + $config = array( + 'Database' => $this->options['database'], + 'uid' => $this->options['user'], + 'pwd' => $this->options['password'], + 'CharacterSet' => 'UTF-8', + 'ReturnDatesAsStrings' => true); + + // Make sure the SQLSRV extension for PHP is installed and enabled. + if (!function_exists('sqlsrv_connect')) + { + throw new RuntimeException('PHP extension sqlsrv_connect is not available.'); + } + + // Attempt to connect to the server. + if (!($this->connection = @ sqlsrv_connect($this->options['host'], $config))) + { + throw new RuntimeException('Database sqlsrv_connect failed'); + } + + // Make sure that DB warnings are not returned as errors. + sqlsrv_configure('WarningsReturnAsErrors', 0); + + // If auto-select is enabled select the given database. + if ($this->options['select'] && !empty($this->options['database'])) + { + $this->select($this->options['database']); + } + + // Set charactersets. + $this->utf = $this->setUtf(); + } + + /** + * Disconnects the database. + * + * @return void + * + * @since 12.1 + */ + public function disconnect() + { + // Close the connection. + if (is_resource($this->connection)) + { + foreach ($this->disconnectHandlers as $h) + { + call_user_func_array($h, array( &$this)); + } + + sqlsrv_close($this->connection); + } + + $this->connection = null; + } + + /** + * Get table constraints + * + * @param string $tableName The name of the database table. + * + * @return array Any constraints available for the table. + * + * @since 12.1 + */ + protected function getTableConstraints($tableName) + { + $this->connect(); + + $query = $this->getQuery(true); + + $this->setQuery( + 'SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = ' . $query->quote($tableName) + ); + + return $this->loadColumn(); + } + + /** + * Rename constraints. + * + * @param array $constraints Array(strings) of table constraints + * @param string $prefix A string + * @param string $backup A string + * + * @return void + * + * @since 12.1 + */ + protected function renameConstraints($constraints = array(), $prefix = null, $backup = null) + { + $this->connect(); + + foreach ($constraints as $constraint) + { + $this->setQuery('sp_rename ' . $constraint . ',' . str_replace($prefix, $backup, $constraint)); + $this->execute(); + } + } + + /** + * Method to escape a string for usage in an SQL statement. + * + * The escaping for MSSQL isn't handled in the driver though that would be nice. Because of this we need + * to handle the escaping ourselves. + * + * @param string $text The string to be escaped. + * @param boolean $extra Optional parameter to provide extra escaping. + * + * @return string The escaped string. + * + * @since 12.1 + */ + public function escape($text, $extra = false) + { + $result = addslashes($text); + $result = str_replace("\'", "''", $result); + $result = str_replace('\"', '"', $result); + $result = str_replace('\/', '/', $result); + + if ($extra) + { + // We need the below str_replace since the search in sql server doesn't recognize _ character. + $result = str_replace('_', '[_]', $result); + } + + return $result; + } + + /** + * Determines if the connection to the server is active. + * + * @return boolean True if connected to the database engine. + * + * @since 12.1 + */ + public function connected() + { + // TODO: Run a blank query here + return true; + } + + /** + * Drops a table from the database. + * + * @param string $tableName The name of the database table to drop. + * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. + * + * @return FOFDatabaseDriverSqlsrv Returns this object to support chaining. + * + * @since 12.1 + */ + public function dropTable($tableName, $ifExists = true) + { + $this->connect(); + + $query = $this->getQuery(true); + + if ($ifExists) + { + $this->setQuery( + 'IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = ' . $query->quote($tableName) . ') DROP TABLE ' . $tableName + ); + } + else + { + $this->setQuery('DROP TABLE ' . $tableName); + } + + $this->execute(); + + return $this; + } + + /** + * Get the number of affected rows for the previous executed SQL statement. + * + * @return integer The number of affected rows. + * + * @since 12.1 + */ + public function getAffectedRows() + { + $this->connect(); + + return sqlsrv_rows_affected($this->cursor); + } + + /** + * Method to get the database collation in use by sampling a text field of a table in the database. + * + * @return mixed The collation in use by the database or boolean false if not supported. + * + * @since 12.1 + */ + public function getCollation() + { + // TODO: Not fake this + return 'MSSQL UTF-8 (UCS2)'; + } + + /** + * Method to get the database connection collation, as reported by the driver. If the connector doesn't support + * reporting this value please return an empty string. + * + * @return string + */ + public function getConnectionCollation() + { + // TODO: Not fake this + return 'MSSQL UTF-8 (UCS2)'; + } + + /** + * Get the number of returned rows for the previous executed SQL statement. + * + * @param resource $cursor An optional database cursor resource to extract the row count from. + * + * @return integer The number of returned rows. + * + * @since 12.1 + */ + public function getNumRows($cursor = null) + { + $this->connect(); + + return sqlsrv_num_rows($cursor ? $cursor : $this->cursor); + } + + /** + * Retrieves field information about the given tables. + * + * @param mixed $table A table name + * @param boolean $typeOnly True to only return field types. + * + * @return array An array of fields. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getTableColumns($table, $typeOnly = true) + { + $result = array(); + + $table_temp = $this->replacePrefix((string) $table); + + // Set the query to get the table fields statement. + $this->setQuery( + 'SELECT column_name as Field, data_type as Type, is_nullable as \'Null\', column_default as \'Default\'' . + ' FROM information_schema.columns WHERE table_name = ' . $this->quote($table_temp) + ); + $fields = $this->loadObjectList(); + + // If we only want the type as the value add just that to the list. + if ($typeOnly) + { + foreach ($fields as $field) + { + $result[$field->Field] = preg_replace("/[(0-9)]/", '', $field->Type); + } + } + // If we want the whole field data object add that to the list. + else + { + foreach ($fields as $field) + { + if (stristr(strtolower($field->Type), "nvarchar")) + { + $field->Default = ""; + } + $result[$field->Field] = $field; + } + } + + return $result; + } + + /** + * Shows the table CREATE statement that creates the given tables. + * + * This is unsupported by MSSQL. + * + * @param mixed $tables A table name or a list of table names. + * + * @return array A list of the create SQL for the tables. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getTableCreate($tables) + { + $this->connect(); + + return ''; + } + + /** + * Get the details list of keys for a table. + * + * @param string $table The name of the table. + * + * @return array An array of the column specification for the table. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getTableKeys($table) + { + $this->connect(); + + // TODO To implement. + return array(); + } + + /** + * Method to get an array of all tables in the database. + * + * @return array An array of all the tables in the database. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getTableList() + { + $this->connect(); + + // Set the query to get the tables statement. + $this->setQuery('SELECT name FROM ' . $this->getDatabase() . '.sys.Tables WHERE type = \'U\';'); + $tables = $this->loadColumn(); + + return $tables; + } + + /** + * Get the version of the database connector. + * + * @return string The database connector version. + * + * @since 12.1 + */ + public function getVersion() + { + $this->connect(); + + $version = sqlsrv_server_info($this->connection); + + return $version['SQLServerVersion']; + } + + /** + * Inserts a row into a table based on an object's properties. + * + * @param string $table The name of the database table to insert into. + * @param object &$object A reference to an object whose public properties match the table fields. + * @param string $key The name of the primary key. If provided the object property is updated. + * + * @return boolean True on success. + * + * @since 12.1 + * @throws RuntimeException + */ + public function insertObject($table, &$object, $key = null) + { + $fields = array(); + $values = array(); + $statement = 'INSERT INTO ' . $this->quoteName($table) . ' (%s) VALUES (%s)'; + + foreach (get_object_vars($object) as $k => $v) + { + // Only process non-null scalars. + if (is_array($v) or is_object($v) or $v === null) + { + continue; + } + + if (!$this->checkFieldExists($table, $k)) + { + continue; + } + + if ($k[0] == '_') + { + // Internal field + continue; + } + + if ($k == $key && $key == 0) + { + continue; + } + + $fields[] = $this->quoteName($k); + $values[] = $this->Quote($v); + } + // Set the query and execute the insert. + $this->setQuery(sprintf($statement, implode(',', $fields), implode(',', $values))); + + if (!$this->execute()) + { + return false; + } + + $id = $this->insertid(); + + if ($key && $id) + { + $object->$key = $id; + } + + return true; + } + + /** + * Method to get the auto-incremented value from the last INSERT statement. + * + * @return integer The value of the auto-increment field from the last inserted row. + * + * @since 12.1 + */ + public function insertid() + { + $this->connect(); + + // TODO: SELECT IDENTITY + $this->setQuery('SELECT @@IDENTITY'); + + return (int) $this->loadResult(); + } + + /** + * Method to get the first field of the first row of the result set from the database query. + * + * @return mixed The return value or null if the query failed. + * + * @since 12.1 + * @throws RuntimeException + */ + public function loadResult() + { + $ret = null; + + // Execute the query and get the result set cursor. + if (!($cursor = $this->execute())) + { + return null; + } + + // Get the first row from the result set as an array. + if ($row = sqlsrv_fetch_array($cursor, SQLSRV_FETCH_NUMERIC)) + { + $ret = $row[0]; + } + + // Free up system resources and return. + $this->freeResult($cursor); + + // For SQLServer - we need to strip slashes + $ret = stripslashes($ret); + + return $ret; + } + + /** + * Execute the SQL statement. + * + * @return mixed A database cursor resource on success, boolean false on failure. + * + * @since 12.1 + * @throws RuntimeException + * @throws Exception + */ + public function execute() + { + $this->connect(); + + if (!is_resource($this->connection)) + { + if (class_exists('JLog')) + { + JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); + } + throw new RuntimeException($this->errorMsg, $this->errorNum); + } + + // Take a local copy so that we don't modify the original query and cause issues later + $query = $this->replacePrefix((string) $this->sql); + + if (!($this->sql instanceof FOFDatabaseQuery) && ($this->limit > 0 || $this->offset > 0)) + { + $query = $this->limit($query, $this->limit, $this->offset); + } + + // Increment the query counter. + $this->count++; + + // Reset the error values. + $this->errorNum = 0; + $this->errorMsg = ''; + + // If debugging is enabled then let's log the query. + if ($this->debug) + { + // Add the query to the object queue. + $this->log[] = $query; + + if (class_exists('JLog')) + { + JLog::add($query, JLog::DEBUG, 'databasequery'); + } + + $this->timings[] = microtime(true); + } + + // SQLSrv_num_rows requires a static or keyset cursor. + if (strncmp(ltrim(strtoupper($query)), 'SELECT', strlen('SELECT')) == 0) + { + $array = array('Scrollable' => SQLSRV_CURSOR_KEYSET); + } + else + { + $array = array(); + } + + // Execute the query. Error suppression is used here to prevent warnings/notices that the connection has been lost. + $this->cursor = @sqlsrv_query($this->connection, $query, array(), $array); + + if ($this->debug) + { + $this->timings[] = microtime(true); + + if (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) + { + $this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + } + else + { + $this->callStacks[] = debug_backtrace(); + } + } + + // If an error occurred handle it. + if (!$this->cursor) + { + // Get the error number and message before we execute any more queries. + $errorNum = $this->getErrorNumber(); + $errorMsg = $this->getErrorMessage($query); + + // Check if the server was disconnected. + if (!$this->connected()) + { + try + { + // Attempt to reconnect. + $this->connection = null; + $this->connect(); + } + // If connect fails, ignore that exception and throw the normal exception. + catch (RuntimeException $e) + { + // Get the error number and message. + $this->errorNum = $this->getErrorNumber(); + $this->errorMsg = $this->getErrorMessage($query); + + // Throw the normal query exception. + if (class_exists('JLog')) + { + JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); + } + + throw new RuntimeException($this->errorMsg, $this->errorNum, $e); + } + + // Since we were able to reconnect, run the query again. + return $this->execute(); + } + // The server was not disconnected. + else + { + // Get the error number and message from before we tried to reconnect. + $this->errorNum = $errorNum; + $this->errorMsg = $errorMsg; + + // Throw the normal query exception. + if (class_exists('JLog')) + { + JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); + } + + throw new RuntimeException($this->errorMsg, $this->errorNum); + } + } + + return $this->cursor; + } + + /** + * This function replaces a string identifier $prefix with the string held is the + * tablePrefix class variable. + * + * @param string $query The SQL statement to prepare. + * @param string $prefix The common table prefix. + * + * @return string The processed SQL statement. + * + * @since 12.1 + */ + public function replacePrefix($query, $prefix = '#__') + { + $startPos = 0; + $literal = ''; + + $query = trim($query); + $n = strlen($query); + + while ($startPos < $n) + { + $ip = strpos($query, $prefix, $startPos); + + if ($ip === false) + { + break; + } + + $j = strpos($query, "N'", $startPos); + $k = strpos($query, '"', $startPos); + + if (($k !== false) && (($k < $j) || ($j === false))) + { + $quoteChar = '"'; + $j = $k; + } + else + { + $quoteChar = "'"; + } + + if ($j === false) + { + $j = $n; + } + + $literal .= str_replace($prefix, $this->tablePrefix, substr($query, $startPos, $j - $startPos)); + $startPos = $j; + + $j = $startPos + 1; + + if ($j >= $n) + { + break; + } + + // Quote comes first, find end of quote + while (true) + { + $k = strpos($query, $quoteChar, $j); + $escaped = false; + + if ($k === false) + { + break; + } + + $l = $k - 1; + + while ($l >= 0 && $query{$l} == '\\') + { + $l--; + $escaped = !$escaped; + } + + if ($escaped) + { + $j = $k + 1; + continue; + } + + break; + } + + if ($k === false) + { + // Error in the query - no end quote; ignore it + break; + } + + $literal .= substr($query, $startPos, $k - $startPos + 1); + $startPos = $k + 1; + } + + if ($startPos < $n) + { + $literal .= substr($query, $startPos, $n - $startPos); + } + + return $literal; + } + + /** + * Select a database for use. + * + * @param string $database The name of the database to select for use. + * + * @return boolean True if the database was successfully selected. + * + * @since 12.1 + * @throws RuntimeException + */ + public function select($database) + { + $this->connect(); + + if (!$database) + { + return false; + } + + if (!sqlsrv_query($this->connection, 'USE ' . $database, null, array('scrollable' => SQLSRV_CURSOR_STATIC))) + { + throw new RuntimeException('Could not connect to database'); + } + + return true; + } + + /** + * Set the connection to use UTF-8 character encoding. + * + * @return boolean True on success. + * + * @since 12.1 + */ + public function setUtf() + { + return false; + } + + /** + * Method to commit a transaction. + * + * @param boolean $toSavepoint If true, commit to the last savepoint. + * + * @return void + * + * @since 12.1 + * @throws RuntimeException + */ + public function transactionCommit($toSavepoint = false) + { + $this->connect(); + + if (!$toSavepoint || $this->transactionDepth <= 1) + { + if ($this->setQuery('COMMIT TRANSACTION')->execute()) + { + $this->transactionDepth = 0; + } + + return; + } + + $this->transactionDepth--; + } + + /** + * Method to roll back a transaction. + * + * @param boolean $toSavepoint If true, rollback to the last savepoint. + * + * @return void + * + * @since 12.1 + * @throws RuntimeException + */ + public function transactionRollback($toSavepoint = false) + { + $this->connect(); + + if (!$toSavepoint || $this->transactionDepth <= 1) + { + if ($this->setQuery('ROLLBACK TRANSACTION')->execute()) + { + $this->transactionDepth = 0; + } + + return; + } + + $savepoint = 'SP_' . ($this->transactionDepth - 1); + $this->setQuery('ROLLBACK TRANSACTION ' . $this->quoteName($savepoint)); + + if ($this->execute()) + { + $this->transactionDepth--; + } + } + + /** + * Method to initialize a transaction. + * + * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. + * + * @return void + * + * @since 12.1 + * @throws RuntimeException + */ + public function transactionStart($asSavepoint = false) + { + $this->connect(); + + if (!$asSavepoint || !$this->transactionDepth) + { + if ($this->setQuery('BEGIN TRANSACTION')->execute()) + { + $this->transactionDepth = 1; + } + + return; + } + + $savepoint = 'SP_' . $this->transactionDepth; + $this->setQuery('BEGIN TRANSACTION ' . $this->quoteName($savepoint)); + + if ($this->execute()) + { + $this->transactionDepth++; + } + } + + /** + * Method to fetch a row from the result set cursor as an array. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * + * @return mixed Either the next row from the result set or false if there are no more rows. + * + * @since 12.1 + */ + protected function fetchArray($cursor = null) + { + return sqlsrv_fetch_array($cursor ? $cursor : $this->cursor, SQLSRV_FETCH_NUMERIC); + } + + /** + * Method to fetch a row from the result set cursor as an associative array. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * + * @return mixed Either the next row from the result set or false if there are no more rows. + * + * @since 12.1 + */ + protected function fetchAssoc($cursor = null) + { + return sqlsrv_fetch_array($cursor ? $cursor : $this->cursor, SQLSRV_FETCH_ASSOC); + } + + /** + * Method to fetch a row from the result set cursor as an object. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * @param string $class The class name to use for the returned row object. + * + * @return mixed Either the next row from the result set or false if there are no more rows. + * + * @since 12.1 + */ + protected function fetchObject($cursor = null, $class = 'stdClass') + { + return sqlsrv_fetch_object($cursor ? $cursor : $this->cursor, $class); + } + + /** + * Method to free up the memory used for the result set. + * + * @param mixed $cursor The optional result set cursor from which to fetch the row. + * + * @return void + * + * @since 12.1 + */ + protected function freeResult($cursor = null) + { + sqlsrv_free_stmt($cursor ? $cursor : $this->cursor); + } + + /** + * Method to check and see if a field exists in a table. + * + * @param string $table The table in which to verify the field. + * @param string $field The field to verify. + * + * @return boolean True if the field exists in the table. + * + * @since 12.1 + */ + protected function checkFieldExists($table, $field) + { + $this->connect(); + + $table = $this->replacePrefix((string) $table); + $query = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '$table' AND COLUMN_NAME = '$field'" . + " ORDER BY ORDINAL_POSITION"; + $this->setQuery($query); + + if ($this->loadResult()) + { + return true; + } + else + { + return false; + } + } + + /** + * Method to wrap an SQL statement to provide a LIMIT and OFFSET behavior for scrolling through a result set. + * + * @param string $query The SQL statement to process. + * @param integer $limit The maximum affected rows to set. + * @param integer $offset The affected row offset to set. + * + * @return string The processed SQL statement. + * + * @since 12.1 + */ + protected function limit($query, $limit, $offset) + { + if ($limit == 0 && $offset == 0) + { + return $query; + } + + $start = $offset + 1; + $end = $offset + $limit; + + $orderBy = stristr($query, 'ORDER BY'); + + if (is_null($orderBy) || empty($orderBy)) + { + $orderBy = 'ORDER BY (select 0)'; + } + + $query = str_ireplace($orderBy, '', $query); + + $rowNumberText = ', ROW_NUMBER() OVER (' . $orderBy . ') AS RowNumber FROM '; + + $query = preg_replace('/\sFROM\s/i', $rowNumberText, $query, 1); + + return $query; + } + + /** + * Renames a table in the database. + * + * @param string $oldTable The name of the table to be renamed + * @param string $newTable The new name for the table. + * @param string $backup Table prefix + * @param string $prefix For the table - used to rename constraints in non-mysql databases + * + * @return FOFDatabaseDriverSqlsrv Returns this object to support chaining. + * + * @since 12.1 + * @throws RuntimeException + */ + public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) + { + $constraints = array(); + + if (!is_null($prefix) && !is_null($backup)) + { + $constraints = $this->getTableConstraints($oldTable); + } + + if (!empty($constraints)) + { + $this->renameConstraints($constraints, $prefix, $backup); + } + + $this->setQuery("sp_rename '" . $oldTable . "', '" . $newTable . "'"); + + return $this->execute(); + } + + /** + * Locks a table in the database. + * + * @param string $tableName The name of the table to lock. + * + * @return FOFDatabaseDriverSqlsrv Returns this object to support chaining. + * + * @since 12.1 + * @throws RuntimeException + */ + public function lockTable($tableName) + { + return $this; + } + + /** + * Unlocks tables in the database. + * + * @return FOFDatabaseDriverSqlsrv Returns this object to support chaining. + * + * @since 12.1 + * @throws RuntimeException + */ + public function unlockTables() + { + return $this; + } + + /** + * Return the actual SQL Error number + * + * @return integer The SQL Error number + * + * @since 3.4.6 + */ + protected function getErrorNumber() + { + $errors = sqlsrv_errors(); + + return $errors[0]['SQLSTATE']; + } + + /** + * Return the actual SQL Error message + * + * @param string $query The SQL Query that fails + * + * @return string The SQL Error message + * + * @since 3.4.6 + */ + protected function getErrorMessage($query) + { + $errors = sqlsrv_errors(); + $errorMessage = (string) $errors[0]['message']; + + // Replace the Databaseprefix with `#__` if we are not in Debug + if (!$this->debug) + { + $errorMessage = str_replace($this->tablePrefix, '#__', $errorMessage); + $query = str_replace($this->tablePrefix, '#__', $query); + } + + return $errorMessage . ' SQL=' . $query; + } +} diff --git a/libraries/fof/database/factory.php b/libraries/fof/database/factory.php new file mode 100644 index 0000000000000..681247adfb25b --- /dev/null +++ b/libraries/fof/database/factory.php @@ -0,0 +1,127 @@ +getMessage()), $e->getCode(), $e); + } + + return $instance; + } + + /** + * Gets an instance of the factory object. + * + * @return FOFDatabaseFactory + * + * @since 12.1 + */ + public static function getInstance() + { + return self::$_instance ? self::$_instance : new FOFDatabaseFactory; + } + + /** + * Get the current query object or a new FOFDatabaseQuery object. + * + * @param string $name Name of the driver you want an query object for. + * @param FOFDatabaseDriver $db Optional FOFDatabaseDriver instance + * + * @return FOFDatabaseQuery The current query object or a new object extending the FOFDatabaseQuery class. + * + * @since 12.1 + * @throws RuntimeException + */ + public function getQuery($name, FOFDatabaseDriver $db = null) + { + // Derive the class name from the driver. + $class = 'FOFDatabaseQuery' . ucfirst(strtolower($name)); + + // Make sure we have a query class for this driver. + if (!class_exists($class)) + { + // If it doesn't exist we are at an impasse so throw an exception. + throw new RuntimeException('Database Query class not found'); + } + + return new $class($db); + } + + /** + * Gets an instance of a factory object to return on subsequent calls of getInstance. + * + * @param FOFDatabaseFactory $instance A FOFDatabaseFactory object. + * + * @return void + * + * @since 12.1 + */ + public static function setInstance(FOFDatabaseFactory $instance = null) + { + self::$_instance = $instance; + } +} diff --git a/libraries/fof/database/installer.php b/libraries/fof/database/installer.php index b14ea25eecac7..c7aec16ebdede 100644 --- a/libraries/fof/database/installer.php +++ b/libraries/fof/database/installer.php @@ -1,13 +1,17 @@ action tags to find all tables - $tables = array(); /** @var SimpleXMLElement $actions */ $actions = $xml->sql->children(); + /** + * The meta/autocollation node defines if I should automatically apply the correct collation (utf8 or utf8mb4) + * to the database tables managed by the schema updater. When enabled (default) the queries are automatically + * converted to the correct collation (utf8mb4_unicode_ci or utf8_general_ci) depending on whether your Joomla! + * and MySQL server support Multibyte UTF-8 (UTF8MB4). Moreover, if UTF8MB4 is supported, all CREATE TABLE + * queries are analyzed and the tables referenced in them are auto-converted to the proper utf8mb4 collation. + */ + $autoCollationConversion = true; + + if ($xml->meta->autocollation) + { + $value = (string) $xml->meta->autocollation; + $value = trim($value); + $value = strtolower($value); + + $autoCollationConversion = in_array($value, array('true', '1', 'on', 'yes')); + } + + try + { + $hasUtf8mb4Support = $this->db->hasUTF8mb4Support(); + } + catch (\Exception $e) + { + $hasUtf8mb4Support = false; + } + + $tablesToConvert = array(); + /** @var SimpleXMLElement $action */ foreach ($actions as $action) { @@ -155,7 +190,7 @@ public function updateSchema() $attributes = $action->attributes(); // Get the table / view name - $table = $attributes->table ? $attributes->table : ''; + $table = $attributes->table ? (string)$attributes->table : ''; if (empty($table)) { @@ -190,7 +225,7 @@ public function updateSchema() break; case 'nor': - $shouldExecute = $shouldExecute || !$condition; + $shouldExecute = !$shouldExecute && !$condition; break; case 'xor': @@ -207,14 +242,15 @@ public function updateSchema() } } - if (!$shouldExecute) - { - break; - } + // DO NOT USE BOOLEAN SHORT CIRCUIT EVALUATION! + // if (!$shouldExecute) break; } - // Make sure all conditions are met - if (!$shouldExecute) + // Do I have to only collect the tables from CREATE TABLE queries? + $onlyCollectTables = !$shouldExecute && $autoCollationConversion && $hasUtf8mb4Support; + + // Make sure all conditions are met OR I have to collect tables from CREATE TABLE queries. + if (!$shouldExecute && !$onlyCollectTables) { continue; } @@ -224,44 +260,48 @@ public function updateSchema() { if ($node->getName() == 'query') { - $canFail = $node->attributes->canfail ? $node->attributes->canfail : $canFailAction; + $query = (string) $node; - $this->db->setQuery((string) $node); + if ($autoCollationConversion && $hasUtf8mb4Support) + { + $this->extractTablesToConvert($query, $tablesToConvert); + } - try + // If we're only collecting tables do not run the queries + if ($onlyCollectTables) { - if (version_compare(JVERSION, '3.1', 'lt')) - { - $handlers = array( - E_NOTICE => JError::getErrorHandling(E_NOTICE), - E_WARNING => JError::getErrorHandling(E_WARNING), - E_ERROR => JError::getErrorHandling(E_ERROR), - ); - $handlers[E_NOTICE]['options'] = isset($handlers[E_NOTICE]['options']) ? $handlers[E_NOTICE]['options'] : null; - $handlers[E_WARNING]['options'] = isset($handlers[E_WARNING]['options']) ? $handlers[E_WARNING]['options'] : null; - $handlers[E_ERROR]['options'] = isset($handlers[E_ERROR]['options']) ? $handlers[E_ERROR]['options'] : null; - JError::setErrorHandling(E_NOTICE, 'ignore'); - JError::setErrorHandling(E_WARNING, 'ignore'); - JError::setErrorHandling(E_ERROR, 'ignore'); - } + continue; + } - $this->db->execute(); + $canFail = $node->attributes->canfail ? (string)$node->attributes->canfail : $canFailAction; + + if (is_string($canFail)) + { + $canFail = strtoupper($canFail); + } + + $canFail = (in_array($canFail, array(true, 1, 'YES', 'TRUE'))); - if (version_compare(JVERSION, '3.1', 'lt')) + // Do I need to automatically convert the collation of all CREATE / ALTER queries? + if ($autoCollationConversion) + { + if ($hasUtf8mb4Support) { - JError::setErrorHandling(E_NOTICE, $handlers[E_NOTICE]['mode'], $handlers[E_NOTICE]['options']); - JError::setErrorHandling(E_WARNING, $handlers[E_WARNING]['mode'], $handlers[E_WARNING]['options']); - JError::setErrorHandling(E_ERROR, $handlers[E_ERROR]['mode'], $handlers[E_ERROR]['options']); + // We have UTF8MB4 support. Convert all queries to UTF8MB4. + $query = $this->convertUtf8QueryToUtf8mb4($query); } - - if (version_compare(JVERSION, '3.1', 'lt') && $this->db->getErrorNum()) + else { - if (!$canFail) - { - JError::raise(E_WARNING, $this->db->getErrorNum(), $this->db->getErrorMsg()); - } + // We do not have UTF8MB4 support. Convert all queries to plain old UTF8. + $query = $this->convertUtf8mb4QueryToUtf8($query); } - /**/ + } + + $this->db->setQuery($query); + + try + { + $this->db->execute(); } catch (Exception $e) { @@ -274,6 +314,12 @@ public function updateSchema() } } } + + // Auto-convert the collation of tables if we are told to do so, have utf8mb4 support and a list of tables. + if ($autoCollationConversion && $hasUtf8mb4Support && !empty($tablesToConvert)) + { + $this->convertTablesToUtf8mb4($tablesToConvert); + } } /** @@ -427,16 +473,14 @@ protected function findSchemaXml() */ protected function conditionMet($table, SimpleXMLElement $node) { - static $allTables = null; - - if (empty($allTables)) + if (empty(static::$allTables)) { - $allTables = $this->db->getTableList(); + static::$allTables = $this->db->getTableList(); } // Does the table exist? $tableNormal = $this->db->replacePrefix($table); - $tableExists = in_array($tableNormal, $allTables); + $tableExists = in_array($tableNormal, static::$allTables); // Initialise $condition = false; @@ -450,7 +494,6 @@ protected function conditionMet($table, SimpleXMLElement $node) { // Check if a table or column is missing case 'missing': - $tableName = (string)$table; $fieldName = (string)$value; if (empty($fieldName)) @@ -459,14 +502,31 @@ protected function conditionMet($table, SimpleXMLElement $node) } else { - $tableColumns = $this->db->getTableColumns($tableNormal, true); + try + { + $tableColumns = $this->db->getTableColumns($tableNormal, true); + } + catch (\Exception $e) + { + $tableColumns = array(); + } + $condition = !array_key_exists($fieldName, $tableColumns); } + break; // Check if a column type matches the "coltype" attribute case 'type': - $tableColumns = $this->db->getTableColumns($table, false); + try + { + $tableColumns = $this->db->getTableColumns($tableNormal, false); + } + catch (\Exception $e) + { + $tableColumns = array(); + } + $condition = false; if (array_key_exists($value, $tableColumns)) @@ -484,6 +544,54 @@ protected function conditionMet($table, SimpleXMLElement $node) break; + // Check if a (named) index exists on the table. Currently only supported on MySQL. + case 'index': + $indexName = (string) $value; + $condition = true; + + if (!empty($indexName)) + { + $indexName = str_replace('#__', $this->db->getPrefix(), $indexName); + $condition = $this->hasIndex($tableNormal, $indexName); + } + + break; + + // Check if a table or column needs to be upgraded to utf8mb4 + case 'utf8mb4upgrade': + $condition = false; + + // Check if the driver and the database connection have UTF8MB4 support + try + { + $hasUtf8mb4Support = $this->db->hasUTF8mb4Support(); + } + catch (\Exception $e) + { + $hasUtf8mb4Support = false; + } + + if ($hasUtf8mb4Support) + { + $fieldName = (string)$value; + + if (empty($fieldName)) + { + $collation = $this->getTableCollation($tableNormal); + } + else + { + $collation = $this->getColumnCollation($tableNormal, $fieldName); + } + + $parts = explode('_', $collation, 3); + $encoding = empty($parts[0]) ? '' : strtolower($parts[0]); + + $condition = $encoding != 'utf8mb4'; + } + + break; + // Check if the result of a query matches our expectation case 'equals': $query = (string)$node; @@ -513,4 +621,400 @@ protected function conditionMet($table, SimpleXMLElement $node) return $condition; } + + /** + * Get the collation of a table. Uses an internal cache for efficiency. + * + * @param string $tableName The name of the table + * + * @return string The collation, e.g. "utf8_general_ci" + */ + private function getTableCollation($tableName) + { + static $cache = array(); + + $tableName = $this->db->replacePrefix($tableName); + + if (!isset($cache[$tableName])) + { + $cache[$tableName] = $this->realGetTableCollation($tableName); + } + + return $cache[$tableName]; + } + + /** + * Get the collation of a table. This is the internal method used by getTableCollation. + * + * @param string $tableName The name of the table + * + * @return string The collation, e.g. "utf8_general_ci" + */ + private function realGetTableCollation($tableName) + { + try + { + $utf8Support = $this->db->hasUTFSupport(); + } + catch (\Exception $e) + { + $utf8Support = false; + } + + try + { + $utf8mb4Support = $utf8Support && $this->db->hasUTF8mb4Support(); + } + catch (\Exception $e) + { + $utf8mb4Support = false; + } + + $collation = $utf8mb4Support ? 'utf8mb4_unicode_ci' : ($utf8Support ? 'utf_general_ci' : 'latin1_swedish_ci'); + + $query = 'SHOW TABLE STATUS LIKE ' . $this->db->q($tableName); + + try + { + $row = $this->db->setQuery($query)->loadAssoc(); + } + catch (\Exception $e) + { + return $collation; + } + + if (empty($row)) + { + return $collation; + } + + if (!isset($row['Collation'])) + { + return $collation; + } + + if (empty($row['Collation'])) + { + return $collation; + } + + return $row['Collation']; + } + + /** + * Get the collation of a column. Uses an internal cache for efficiency. + * + * @param string $tableName The name of the table + * @param string $columnName The name of the column + * + * @return string The collation, e.g. "utf8_general_ci" + */ + private function getColumnCollation($tableName, $columnName) + { + static $cache = array(); + + $tableName = $this->db->replacePrefix($tableName); + $columnName = $this->db->replacePrefix($columnName); + + if (!isset($cache[$tableName])) + { + $cache[$tableName] = array(); + } + + if (!isset($cache[$tableName][$columnName])) + { + $cache[$tableName][$columnName] = $this->realGetColumnCollation($tableName, $columnName); + } + + return $cache[$tableName][$columnName]; + } + + /** + * Get the collation of a column. This is the internal method used by getColumnCollation. + * + * @param string $tableName The name of the table + * @param string $columnName The name of the column + * + * @return string The collation, e.g. "utf8_general_ci" + */ + private function realGetColumnCollation($tableName, $columnName) + { + $collation = $this->getTableCollation($tableName); + + $query = 'SHOW FULL COLUMNS FROM ' . $this->db->qn($tableName) . ' LIKE ' . $this->db->q($columnName); + + try + { + $row = $this->db->setQuery($query)->loadAssoc(); + } + catch (\Exception $e) + { + return $collation; + } + + if (empty($row)) + { + return $collation; + } + + if (!isset($row['Collation'])) + { + return $collation; + } + + if (empty($row['Collation'])) + { + return $collation; + } + + return $row['Collation']; + } + + /** + * Automatically downgrade a CREATE TABLE or ALTER TABLE query from utf8mb4 (UTF-8 Multibyte) to plain utf8. + * + * We use our own method so we can be site it works even on Joomla! 3.4 or earlier, where UTF8MB4 support is not + * implemented. + * + * @param string $query The query to convert + * + * @return string The converted query + */ + private function convertUtf8mb4QueryToUtf8($query) + { + // If it's not an ALTER TABLE or CREATE TABLE command there's nothing to convert + $beginningOfQuery = substr($query, 0, 12); + $beginningOfQuery = strtoupper($beginningOfQuery); + + if (!in_array($beginningOfQuery, array('ALTER TABLE ', 'CREATE TABLE'))) + { + return $query; + } + + // Replace utf8mb4 with utf8 + $from = array( + 'utf8mb4_unicode_ci', + 'utf8mb4_', + 'utf8mb4', + ); + + $to = array( + 'utf8_general_ci', // Yeah, we convert utf8mb4_unicode_ci to utf8_general_ci per Joomla!'s conventions + 'utf8_', + 'utf8', + ); + + return str_replace($from, $to, $query); + } + + /** + * Automatically upgrade a CREATE TABLE or ALTER TABLE query from plain utf8 to utf8mb4 (UTF-8 Multibyte). + * + * @param string $query The query to convert + * + * @return string The converted query + */ + private function convertUtf8QueryToUtf8mb4($query) + { + // If it's not an ALTER TABLE or CREATE TABLE command there's nothing to convert + $beginningOfQuery = substr($query, 0, 12); + $beginningOfQuery = strtoupper($beginningOfQuery); + + if (!in_array($beginningOfQuery, array('ALTER TABLE ', 'CREATE TABLE'))) + { + return $query; + } + + // Replace utf8 with utf8mb4 + $from = array( + 'utf8_general_ci', + 'utf8_', + 'utf8', + ); + + $to = array( + 'utf8mb4_unicode_ci', // Yeah, we convert utf8_general_ci to utf8mb4_unicode_ci per Joomla!'s conventions + 'utf8mb4_', + 'utf8mb4', + ); + + return str_replace($from, $to, $query); + } + + /** + * Analyzes a query. If it's a CREATE TABLE query the table is added to the $tables array. + * + * @param string $query The query to analyze + * @param string $tables The array where the name of the detected table is added + * + * @return void + */ + private function extractTablesToConvert($query, &$tables) + { + // Normalize the whitespace of the query + $query = trim($query); + $query = str_replace(array("\r\n", "\r", "\n"), ' ', $query); + + while (strstr($query, ' ') !== false) + { + $query = str_replace(' ', ' ', $query); + } + + // Is it a create table query? + $queryStart = substr($query, 0, 12); + $queryStart = strtoupper($queryStart); + + if ($queryStart != 'CREATE TABLE') + { + return; + } + + // Remove the CREATE TABLE keyword. Also, If there's an IF NOT EXISTS clause remove it. + $query = substr($query, 12); + $query = str_ireplace('IF NOT EXISTS', '', $query); + $query = trim($query); + + // Make sure there is a space between the table name and its definition, denoted by an open parenthesis + $query = str_replace('(', ' (', $query); + + // Now we should have the name of the table, a space and the rest of the query. Extract the table name. + $parts = explode(' ', $query, 2); + $tableName = $parts[0]; + + /** + * The table name may be quoted. Since UTF8MB4 is only supported in MySQL, the table name can only be + * quoted with surrounding backticks. Therefore we can trim backquotes from the table name to unquote it! + **/ + $tableName = trim($tableName, '`'); + + // Finally, add the table name to $tables if it doesn't already exist. + if (!in_array($tableName, $tables)) + { + $tables[] = $tableName; + } + } + + /** + * Converts the collation of tables listed in $tablesToConvert to utf8mb4_unicode_ci + * + * @param array $tablesToConvert The list of tables to convert + * + * @return void + */ + private function convertTablesToUtf8mb4($tablesToConvert) + { + try + { + $utf8mb4Support = $this->db->hasUTF8mb4Support(); + } + catch (\Exception $e) + { + $utf8mb4Support = false; + } + + // Make sure the database driver REALLY has support for converting character sets + if (!$utf8mb4Support) + { + return; + } + + asort($tablesToConvert); + + foreach ($tablesToConvert as $tableName) + { + $collation = $this->getTableCollation($tableName); + + $parts = explode('_', $collation, 3); + $encoding = empty($parts[0]) ? '' : strtolower($parts[0]); + + if ($encoding != 'utf8mb4') + { + $queries = $this->db->getAlterTableCharacterSet($tableName); + + try + { + foreach ($queries as $query) + { + $this->db->setQuery($query)->execute(); + } + } + catch (\Exception $e) + { + // We ignore failed conversions. Remember, you MUST change your indices MANUALLY. + } + } + } + } + + /** + * Returns true if table $tableName has an index named $indexName or if it's impossible to retrieve index names for + * the table (not enough privileges, not a MySQL database, ...) + * + * @param string $tableName The name of the table + * @param string $indexName The name of the index + * + * @return bool + */ + private function hasIndex($tableName, $indexName) + { + static $isMySQL = null; + static $cache = array(); + + if (is_null($isMySQL)) + { + $driverType = $this->db->name; + $driverType = strtolower($driverType); + $isMySQL = true; + + if ( + !strpos($driverType, 'mysql') === 0 + && !(substr($driverType, -5) == 'mysql') + && !(substr($driverType, -6) == 'mysqli') + ) + { + $isMySQL = false; + } + } + + // Not MySQL? Lie and return true. + if (!$isMySQL) + { + return true; + } + + if (!isset($cache[$tableName])) + { + $cache[$tableName] = array(); + } + + if (!isset($cache[$tableName][$indexName])) + { + $cache[$tableName][$indexName] = true; + + try + { + $indices = array(); + $query = 'SHOW INDEXES FROM ' . $this->db->qn($tableName); + $indexDefinitions = $this->db->setQuery($query)->loadAssocList(); + + if (!empty($indexDefinitions) && is_array($indexDefinitions)) + { + foreach ($indexDefinitions as $def) + { + $indices[] = $def['Key_name']; + } + + $indices = array_unique($indices); + } + + $cache[$tableName][$indexName] = in_array($indexName, $indices); + } + catch (\Exception $e) + { + // Ignore errors + } + } + + return $cache[$tableName][$indexName]; + } } diff --git a/libraries/fof/database/interface.php b/libraries/fof/database/interface.php new file mode 100644 index 0000000000000..21be611274a57 --- /dev/null +++ b/libraries/fof/database/interface.php @@ -0,0 +1,37 @@ +quote($args[0], isset($args[1]) ? $args[1] : true); + break; + + case 'qn': + return $this->quoteName($args[0], isset($args[1]) ? $args[1] : null); + break; + + case 'e': + return $this->escape($args[0], isset($args[1]) ? $args[1] : false); + break; + } + } + + /** + * Class constructor. + * + * @param FOFDatabaseDriver $db The database driver. + * + * @since 11.1 + */ + public function __construct(FOFDatabaseDriver $db = null) + { + $this->db = $db; + } + + /** + * Magic function to convert the query to a string. + * + * @return string The completed query. + * + * @since 11.1 + */ + public function __toString() + { + $query = ''; + + if ($this->sql) + { + return $this->sql; + } + + switch ($this->type) + { + case 'element': + $query .= (string) $this->element; + break; + + case 'select': + $query .= (string) $this->select; + $query .= (string) $this->from; + + if ($this->join) + { + // Special case for joins + foreach ($this->join as $join) + { + $query .= (string) $join; + } + } + + if ($this->where) + { + $query .= (string) $this->where; + } + + if ($this->group) + { + $query .= (string) $this->group; + } + + if ($this->having) + { + $query .= (string) $this->having; + } + + if ($this->order) + { + $query .= (string) $this->order; + } + + if ($this->union) + { + $query .= (string) $this->union; + } + + break; + + case 'delete': + $query .= (string) $this->delete; + $query .= (string) $this->from; + + if ($this->join) + { + // Special case for joins + foreach ($this->join as $join) + { + $query .= (string) $join; + } + } + + if ($this->where) + { + $query .= (string) $this->where; + } + + if ($this->order) + { + $query .= (string) $this->order; + } + + break; + + case 'update': + $query .= (string) $this->update; + + if ($this->join) + { + // Special case for joins + foreach ($this->join as $join) + { + $query .= (string) $join; + } + } + + $query .= (string) $this->set; + + if ($this->where) + { + $query .= (string) $this->where; + } + + if ($this->order) + { + $query .= (string) $this->order; + } + + break; + + case 'insert': + $query .= (string) $this->insert; + + // Set method + if ($this->set) + { + $query .= (string) $this->set; + } + // Columns-Values method + elseif ($this->values) + { + if ($this->columns) + { + $query .= (string) $this->columns; + } + + $elements = $this->values->getElements(); + + if (!($elements[0] instanceof $this)) + { + $query .= ' VALUES '; + } + + $query .= (string) $this->values; + } + + break; + + case 'call': + $query .= (string) $this->call; + break; + + case 'exec': + $query .= (string) $this->exec; + break; + } + + if ($this instanceof FOFDatabaseQueryLimitable) + { + $query = $this->processLimit($query, $this->limit, $this->offset); + } + + return $query; + } + + /** + * Magic function to get protected variable value + * + * @param string $name The name of the variable. + * + * @return mixed + * + * @since 11.1 + */ + public function __get($name) + { + return isset($this->$name) ? $this->$name : null; + } + + /** + * Add a single column, or array of columns to the CALL clause of the query. + * + * Note that you must not mix insert, update, delete and select method calls when building a query. + * The call method can, however, be called multiple times in the same query. + * + * Usage: + * $query->call('a.*')->call('b.id'); + * $query->call(array('a.*', 'b.id')); + * + * @param mixed $columns A string or an array of field names. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 12.1 + */ + public function call($columns) + { + $this->type = 'call'; + + if (is_null($this->call)) + { + $this->call = new FOFDatabaseQueryElement('CALL', $columns); + } + else + { + $this->call->append($columns); + } + + return $this; + } + + /** + * Casts a value to a char. + * + * Ensure that the value is properly quoted before passing to the method. + * + * Usage: + * $query->select($query->castAsChar('a')); + * + * @param string $value The value to cast as a char. + * + * @return string Returns the cast value. + * + * @since 11.1 + */ + public function castAsChar($value) + { + return $value; + } + + /** + * Gets the number of characters in a string. + * + * Note, use 'length' to find the number of bytes in a string. + * + * Usage: + * $query->select($query->charLength('a')); + * + * @param string $field A value. + * @param string $operator Comparison operator between charLength integer value and $condition + * @param string $condition Integer value to compare charLength with. + * + * @return string The required char length call. + * + * @since 11.1 + */ + public function charLength($field, $operator = null, $condition = null) + { + return 'CHAR_LENGTH(' . $field . ')' . (isset($operator) && isset($condition) ? ' ' . $operator . ' ' . $condition : ''); + } + + /** + * Clear data from the query or a specific clause of the query. + * + * @param string $clause Optionally, the name of the clause to clear, or nothing to clear the whole query. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 11.1 + */ + public function clear($clause = null) + { + $this->sql = null; + + switch ($clause) + { + case 'select': + $this->select = null; + $this->type = null; + break; + + case 'delete': + $this->delete = null; + $this->type = null; + break; + + case 'update': + $this->update = null; + $this->type = null; + break; + + case 'insert': + $this->insert = null; + $this->type = null; + $this->autoIncrementField = null; + break; + + case 'from': + $this->from = null; + break; + + case 'join': + $this->join = null; + break; + + case 'set': + $this->set = null; + break; + + case 'where': + $this->where = null; + break; + + case 'group': + $this->group = null; + break; + + case 'having': + $this->having = null; + break; + + case 'order': + $this->order = null; + break; + + case 'columns': + $this->columns = null; + break; + + case 'values': + $this->values = null; + break; + + case 'exec': + $this->exec = null; + $this->type = null; + break; + + case 'call': + $this->call = null; + $this->type = null; + break; + + case 'limit': + $this->offset = 0; + $this->limit = 0; + break; + + case 'offset': + $this->offset = 0; + break; + + case 'union': + $this->union = null; + break; + + case 'unionAll': + $this->unionAll = null; + break; + + default: + $this->type = null; + $this->select = null; + $this->delete = null; + $this->update = null; + $this->insert = null; + $this->from = null; + $this->join = null; + $this->set = null; + $this->where = null; + $this->group = null; + $this->having = null; + $this->order = null; + $this->columns = null; + $this->values = null; + $this->autoIncrementField = null; + $this->exec = null; + $this->call = null; + $this->union = null; + $this->unionAll = null; + $this->offset = 0; + $this->limit = 0; + break; + } + + return $this; + } + + /** + * Adds a column, or array of column names that would be used for an INSERT INTO statement. + * + * @param mixed $columns A column name, or array of column names. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 11.1 + */ + public function columns($columns) + { + if (is_null($this->columns)) + { + $this->columns = new FOFDatabaseQueryElement('()', $columns); + } + else + { + $this->columns->append($columns); + } + + return $this; + } + + /** + * Concatenates an array of column names or values. + * + * Usage: + * $query->select($query->concatenate(array('a', 'b'))); + * + * @param array $values An array of values to concatenate. + * @param string $separator As separator to place between each value. + * + * @return string The concatenated values. + * + * @since 11.1 + */ + public function concatenate($values, $separator = null) + { + if ($separator) + { + return 'CONCATENATE(' . implode(' || ' . $this->quote($separator) . ' || ', $values) . ')'; + } + else + { + return 'CONCATENATE(' . implode(' || ', $values) . ')'; + } + } + + /** + * Gets the current date and time. + * + * Usage: + * $query->where('published_up < '.$query->currentTimestamp()); + * + * @return string + * + * @since 11.1 + */ + public function currentTimestamp() + { + return 'CURRENT_TIMESTAMP()'; + } + + /** + * Returns a PHP date() function compliant date format for the database driver. + * + * This method is provided for use where the query object is passed to a function for modification. + * If you have direct access to the database object, it is recommended you use the getDateFormat method directly. + * + * @return string The format string. + * + * @since 11.1 + */ + public function dateFormat() + { + if (!($this->db instanceof FOFDatabaseDriver)) + { + throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT'); + } + + return $this->db->getDateFormat(); + } + + /** + * Creates a formatted dump of the query for debugging purposes. + * + * Usage: + * echo $query->dump(); + * + * @return string + * + * @since 11.3 + */ + public function dump() + { + return '
' . str_replace('#__', $this->db->getPrefix(), $this) . '
'; + } + + /** + * Add a table name to the DELETE clause of the query. + * + * Note that you must not mix insert, update, delete and select method calls when building a query. + * + * Usage: + * $query->delete('#__a')->where('id = 1'); + * + * @param string $table The name of the table to delete from. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 11.1 + */ + public function delete($table = null) + { + $this->type = 'delete'; + $this->delete = new FOFDatabaseQueryElement('DELETE', null); + + if (!empty($table)) + { + $this->from($table); + } + + return $this; + } + + /** + * Method to escape a string for usage in an SQL statement. + * + * This method is provided for use where the query object is passed to a function for modification. + * If you have direct access to the database object, it is recommended you use the escape method directly. + * + * Note that 'e' is an alias for this method as it is in FOFDatabaseDriver. + * + * @param string $text The string to be escaped. + * @param boolean $extra Optional parameter to provide extra escaping. + * + * @return string The escaped string. + * + * @since 11.1 + * @throws RuntimeException if the internal db property is not a valid object. + */ + public function escape($text, $extra = false) + { + if (!($this->db instanceof FOFDatabaseDriver)) + { + throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT'); + } + + return $this->db->escape($text, $extra); + } + + /** + * Add a single column, or array of columns to the EXEC clause of the query. + * + * Note that you must not mix insert, update, delete and select method calls when building a query. + * The exec method can, however, be called multiple times in the same query. + * + * Usage: + * $query->exec('a.*')->exec('b.id'); + * $query->exec(array('a.*', 'b.id')); + * + * @param mixed $columns A string or an array of field names. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 12.1 + */ + public function exec($columns) + { + $this->type = 'exec'; + + if (is_null($this->exec)) + { + $this->exec = new FOFDatabaseQueryElement('EXEC', $columns); + } + else + { + $this->exec->append($columns); + } + + return $this; + } + + /** + * Add a table to the FROM clause of the query. + * + * Note that while an array of tables can be provided, it is recommended you use explicit joins. + * + * Usage: + * $query->select('*')->from('#__a'); + * + * @param mixed $tables A string or array of table names. + * This can be a FOFDatabaseQuery object (or a child of it) when used + * as a subquery in FROM clause along with a value for $subQueryAlias. + * @param string $subQueryAlias Alias used when $tables is a FOFDatabaseQuery. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @throws RuntimeException + * + * @since 11.1 + */ + public function from($tables, $subQueryAlias = null) + { + if (is_null($this->from)) + { + if ($tables instanceof $this) + { + if (is_null($subQueryAlias)) + { + throw new RuntimeException('JLIB_DATABASE_ERROR_NULL_SUBQUERY_ALIAS'); + } + + $tables = '( ' . (string) $tables . ' ) AS ' . $this->quoteName($subQueryAlias); + } + + $this->from = new FOFDatabaseQueryElement('FROM', $tables); + } + else + { + $this->from->append($tables); + } + + return $this; + } + + /** + * Used to get a string to extract year from date column. + * + * Usage: + * $query->select($query->year($query->quoteName('dateColumn'))); + * + * @param string $date Date column containing year to be extracted. + * + * @return string Returns string to extract year from a date. + * + * @since 12.1 + */ + public function year($date) + { + return 'YEAR(' . $date . ')'; + } + + /** + * Used to get a string to extract month from date column. + * + * Usage: + * $query->select($query->month($query->quoteName('dateColumn'))); + * + * @param string $date Date column containing month to be extracted. + * + * @return string Returns string to extract month from a date. + * + * @since 12.1 + */ + public function month($date) + { + return 'MONTH(' . $date . ')'; + } + + /** + * Used to get a string to extract day from date column. + * + * Usage: + * $query->select($query->day($query->quoteName('dateColumn'))); + * + * @param string $date Date column containing day to be extracted. + * + * @return string Returns string to extract day from a date. + * + * @since 12.1 + */ + public function day($date) + { + return 'DAY(' . $date . ')'; + } + + /** + * Used to get a string to extract hour from date column. + * + * Usage: + * $query->select($query->hour($query->quoteName('dateColumn'))); + * + * @param string $date Date column containing hour to be extracted. + * + * @return string Returns string to extract hour from a date. + * + * @since 12.1 + */ + public function hour($date) + { + return 'HOUR(' . $date . ')'; + } + + /** + * Used to get a string to extract minute from date column. + * + * Usage: + * $query->select($query->minute($query->quoteName('dateColumn'))); + * + * @param string $date Date column containing minute to be extracted. + * + * @return string Returns string to extract minute from a date. + * + * @since 12.1 + */ + public function minute($date) + { + return 'MINUTE(' . $date . ')'; + } + + /** + * Used to get a string to extract seconds from date column. + * + * Usage: + * $query->select($query->second($query->quoteName('dateColumn'))); + * + * @param string $date Date column containing second to be extracted. + * + * @return string Returns string to extract second from a date. + * + * @since 12.1 + */ + public function second($date) + { + return 'SECOND(' . $date . ')'; + } + + /** + * Add a grouping column to the GROUP clause of the query. + * + * Usage: + * $query->group('id'); + * + * @param mixed $columns A string or array of ordering columns. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 11.1 + */ + public function group($columns) + { + if (is_null($this->group)) + { + $this->group = new FOFDatabaseQueryElement('GROUP BY', $columns); + } + else + { + $this->group->append($columns); + } + + return $this; + } + + /** + * A conditions to the HAVING clause of the query. + * + * Usage: + * $query->group('id')->having('COUNT(id) > 5'); + * + * @param mixed $conditions A string or array of columns. + * @param string $glue The glue by which to join the conditions. Defaults to AND. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 11.1 + */ + public function having($conditions, $glue = 'AND') + { + if (is_null($this->having)) + { + $glue = strtoupper($glue); + $this->having = new FOFDatabaseQueryElement('HAVING', $conditions, " $glue "); + } + else + { + $this->having->append($conditions); + } + + return $this; + } + + /** + * Add an INNER JOIN clause to the query. + * + * Usage: + * $query->innerJoin('b ON b.id = a.id')->innerJoin('c ON c.id = b.id'); + * + * @param string $condition The join condition. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 11.1 + */ + public function innerJoin($condition) + { + $this->join('INNER', $condition); + + return $this; + } + + /** + * Add a table name to the INSERT clause of the query. + * + * Note that you must not mix insert, update, delete and select method calls when building a query. + * + * Usage: + * $query->insert('#__a')->set('id = 1'); + * $query->insert('#__a')->columns('id, title')->values('1,2')->values('3,4'); + * $query->insert('#__a')->columns('id, title')->values(array('1,2', '3,4')); + * + * @param mixed $table The name of the table to insert data into. + * @param boolean $incrementField The name of the field to auto increment. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 11.1 + */ + public function insert($table, $incrementField=false) + { + $this->type = 'insert'; + $this->insert = new FOFDatabaseQueryElement('INSERT INTO', $table); + $this->autoIncrementField = $incrementField; + + return $this; + } + + /** + * Add a JOIN clause to the query. + * + * Usage: + * $query->join('INNER', 'b ON b.id = a.id); + * + * @param string $type The type of join. This string is prepended to the JOIN keyword. + * @param string $conditions A string or array of conditions. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 11.1 + */ + public function join($type, $conditions) + { + if (is_null($this->join)) + { + $this->join = array(); + } + + $this->join[] = new FOFDatabaseQueryElement(strtoupper($type) . ' JOIN', $conditions); + + return $this; + } + + /** + * Add a LEFT JOIN clause to the query. + * + * Usage: + * $query->leftJoin('b ON b.id = a.id')->leftJoin('c ON c.id = b.id'); + * + * @param string $condition The join condition. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 11.1 + */ + public function leftJoin($condition) + { + $this->join('LEFT', $condition); + + return $this; + } + + /** + * Get the length of a string in bytes. + * + * Note, use 'charLength' to find the number of characters in a string. + * + * Usage: + * query->where($query->length('a').' > 3'); + * + * @param string $value The string to measure. + * + * @return int + * + * @since 11.1 + */ + public function length($value) + { + return 'LENGTH(' . $value . ')'; + } + + /** + * Get the null or zero representation of a timestamp for the database driver. + * + * This method is provided for use where the query object is passed to a function for modification. + * If you have direct access to the database object, it is recommended you use the nullDate method directly. + * + * Usage: + * $query->where('modified_date <> '.$query->nullDate()); + * + * @param boolean $quoted Optionally wraps the null date in database quotes (true by default). + * + * @return string Null or zero representation of a timestamp. + * + * @since 11.1 + */ + public function nullDate($quoted = true) + { + if (!($this->db instanceof FOFDatabaseDriver)) + { + throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT'); + } + + $result = $this->db->getNullDate($quoted); + + if ($quoted) + { + return $this->db->quote($result); + } + + return $result; + } + + /** + * Add a ordering column to the ORDER clause of the query. + * + * Usage: + * $query->order('foo')->order('bar'); + * $query->order(array('foo','bar')); + * + * @param mixed $columns A string or array of ordering columns. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 11.1 + */ + public function order($columns) + { + if (is_null($this->order)) + { + $this->order = new FOFDatabaseQueryElement('ORDER BY', $columns); + } + else + { + $this->order->append($columns); + } + + return $this; + } + + /** + * Add an OUTER JOIN clause to the query. + * + * Usage: + * $query->outerJoin('b ON b.id = a.id')->outerJoin('c ON c.id = b.id'); + * + * @param string $condition The join condition. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 11.1 + */ + public function outerJoin($condition) + { + $this->join('OUTER', $condition); + + return $this; + } + + /** + * Method to quote and optionally escape a string to database requirements for insertion into the database. + * + * This method is provided for use where the query object is passed to a function for modification. + * If you have direct access to the database object, it is recommended you use the quote method directly. + * + * Note that 'q' is an alias for this method as it is in FOFDatabaseDriver. + * + * Usage: + * $query->quote('fulltext'); + * $query->q('fulltext'); + * $query->q(array('option', 'fulltext')); + * + * @param mixed $text A string or an array of strings to quote. + * @param boolean $escape True to escape the string, false to leave it unchanged. + * + * @return string The quoted input string. + * + * @since 11.1 + * @throws RuntimeException if the internal db property is not a valid object. + */ + public function quote($text, $escape = true) + { + if (!($this->db instanceof FOFDatabaseDriver)) + { + throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT'); + } + + return $this->db->quote($text, $escape); + } + + /** + * Wrap an SQL statement identifier name such as column, table or database names in quotes to prevent injection + * risks and reserved word conflicts. + * + * This method is provided for use where the query object is passed to a function for modification. + * If you have direct access to the database object, it is recommended you use the quoteName method directly. + * + * Note that 'qn' is an alias for this method as it is in FOFDatabaseDriver. + * + * Usage: + * $query->quoteName('#__a'); + * $query->qn('#__a'); + * + * @param mixed $name The identifier name to wrap in quotes, or an array of identifier names to wrap in quotes. + * Each type supports dot-notation name. + * @param mixed $as The AS query part associated to $name. It can be string or array, in latter case it has to be + * same length of $name; if is null there will not be any AS part for string or array element. + * + * @return mixed The quote wrapped name, same type of $name. + * + * @since 11.1 + * @throws RuntimeException if the internal db property is not a valid object. + */ + public function quoteName($name, $as = null) + { + if (!($this->db instanceof FOFDatabaseDriver)) + { + throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT'); + } + + return $this->db->quoteName($name, $as); + } + + /** + * Add a RIGHT JOIN clause to the query. + * + * Usage: + * $query->rightJoin('b ON b.id = a.id')->rightJoin('c ON c.id = b.id'); + * + * @param string $condition The join condition. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 11.1 + */ + public function rightJoin($condition) + { + $this->join('RIGHT', $condition); + + return $this; + } + + /** + * Add a single column, or array of columns to the SELECT clause of the query. + * + * Note that you must not mix insert, update, delete and select method calls when building a query. + * The select method can, however, be called multiple times in the same query. + * + * Usage: + * $query->select('a.*')->select('b.id'); + * $query->select(array('a.*', 'b.id')); + * + * @param mixed $columns A string or an array of field names. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 11.1 + */ + public function select($columns) + { + $this->type = 'select'; + + if (is_null($this->select)) + { + $this->select = new FOFDatabaseQueryElement('SELECT', $columns); + } + else + { + $this->select->append($columns); + } + + return $this; + } + + /** + * Add a single condition string, or an array of strings to the SET clause of the query. + * + * Usage: + * $query->set('a = 1')->set('b = 2'); + * $query->set(array('a = 1', 'b = 2'); + * + * @param mixed $conditions A string or array of string conditions. + * @param string $glue The glue by which to join the condition strings. Defaults to ,. + * Note that the glue is set on first use and cannot be changed. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 11.1 + */ + public function set($conditions, $glue = ',') + { + if (is_null($this->set)) + { + $glue = strtoupper($glue); + $this->set = new FOFDatabaseQueryElement('SET', $conditions, "\n\t$glue "); + } + else + { + $this->set->append($conditions); + } + + return $this; + } + + /** + * Allows a direct query to be provided to the database + * driver's setQuery() method, but still allow queries + * to have bounded variables. + * + * Usage: + * $query->setQuery('select * from #__users'); + * + * @param mixed $sql An SQL Query + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 12.1 + */ + public function setQuery($sql) + { + $this->sql = $sql; + + return $this; + } + + /** + * Add a table name to the UPDATE clause of the query. + * + * Note that you must not mix insert, update, delete and select method calls when building a query. + * + * Usage: + * $query->update('#__foo')->set(...); + * + * @param string $table A table to update. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 11.1 + */ + public function update($table) + { + $this->type = 'update'; + $this->update = new FOFDatabaseQueryElement('UPDATE', $table); + + return $this; + } + + /** + * Adds a tuple, or array of tuples that would be used as values for an INSERT INTO statement. + * + * Usage: + * $query->values('1,2,3')->values('4,5,6'); + * $query->values(array('1,2,3', '4,5,6')); + * + * @param string $values A single tuple, or array of tuples. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 11.1 + */ + public function values($values) + { + if (is_null($this->values)) + { + $this->values = new FOFDatabaseQueryElement('()', $values, '),('); + } + else + { + $this->values->append($values); + } + + return $this; + } + + /** + * Add a single condition, or an array of conditions to the WHERE clause of the query. + * + * Usage: + * $query->where('a = 1')->where('b = 2'); + * $query->where(array('a = 1', 'b = 2')); + * + * @param mixed $conditions A string or array of where conditions. + * @param string $glue The glue by which to join the conditions. Defaults to AND. + * Note that the glue is set on first use and cannot be changed. + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 11.1 + */ + public function where($conditions, $glue = 'AND') + { + if (is_null($this->where)) + { + $glue = strtoupper($glue); + $this->where = new FOFDatabaseQueryElement('WHERE', $conditions, " $glue "); + } + else + { + $this->where->append($conditions); + } + + return $this; + } + + /** + * Method to provide deep copy support to nested objects and + * arrays when cloning. + * + * @return void + * + * @since 11.3 + */ + public function __clone() + { + foreach ($this as $k => $v) + { + if ($k === 'db') + { + continue; + } + + if (is_object($v) || is_array($v)) + { + $this->{$k} = unserialize(serialize($v)); + } + } + } + + /** + * Add a query to UNION with the current query. + * Multiple unions each require separate statements and create an array of unions. + * + * Usage (the $query base query MUST be a select query): + * $query->union('SELECT name FROM #__foo') + * $query->union('SELECT name FROM #__foo', true) + * $query->union(array('SELECT name FROM #__foo','SELECT name FROM #__bar')) + * $query->union($query2)->union($query3) + * $query->union(array($query2, $query3)) + * + * @param mixed $query The FOFDatabaseQuery object or string to union. + * @param boolean $distinct True to only return distinct rows from the union. + * @param string $glue The glue by which to join the conditions. + * + * @return mixed The FOFDatabaseQuery object on success or boolean false on failure. + * + * @link http://dev.mysql.com/doc/refman/5.0/en/union.html + * + * @since 12.1 + */ + public function union($query, $distinct = false, $glue = '') + { + // Set up the DISTINCT flag, the name with parentheses, and the glue. + if ($distinct) + { + $name = 'UNION DISTINCT ()'; + $glue = ')' . PHP_EOL . 'UNION DISTINCT ('; + } + else + { + $glue = ')' . PHP_EOL . 'UNION ('; + $name = 'UNION ()'; + } + + // Get the FOFDatabaseQueryElement if it does not exist + if (is_null($this->union)) + { + $this->union = new FOFDatabaseQueryElement($name, $query, "$glue"); + } + // Otherwise append the second UNION. + else + { + $this->union->append($query); + } + + return $this; + } + + /** + * Add a query to UNION DISTINCT with the current query. Simply a proxy to union with the DISTINCT keyword. + * + * Usage: + * $query->unionDistinct('SELECT name FROM #__foo') + * + * @param mixed $query The FOFDatabaseQuery object or string to union. + * @param string $glue The glue by which to join the conditions. + * + * @return mixed The FOFDatabaseQuery object on success or boolean false on failure. + * + * @see union + * + * @since 12.1 + */ + public function unionDistinct($query, $glue = '') + { + $distinct = true; + + // Apply the distinct flag to the union. + return $this->union($query, $distinct, $glue); + } + + /** + * Find and replace sprintf-like tokens in a format string. + * Each token takes one of the following forms: + * %% - A literal percent character. + * %[t] - Where [t] is a type specifier. + * %[n]$[x] - Where [n] is an argument specifier and [t] is a type specifier. + * + * Types: + * a - Numeric: Replacement text is coerced to a numeric type but not quoted or escaped. + * e - Escape: Replacement text is passed to $this->escape(). + * E - Escape (extra): Replacement text is passed to $this->escape() with true as the second argument. + * n - Name Quote: Replacement text is passed to $this->quoteName(). + * q - Quote: Replacement text is passed to $this->quote(). + * Q - Quote (no escape): Replacement text is passed to $this->quote() with false as the second argument. + * r - Raw: Replacement text is used as-is. (Be careful) + * + * Date Types: + * - Replacement text automatically quoted (use uppercase for Name Quote). + * - Replacement text should be a string in date format or name of a date column. + * y/Y - Year + * m/M - Month + * d/D - Day + * h/H - Hour + * i/I - Minute + * s/S - Second + * + * Invariable Types: + * - Takes no argument. + * - Argument index not incremented. + * t - Replacement text is the result of $this->currentTimestamp(). + * z - Replacement text is the result of $this->nullDate(false). + * Z - Replacement text is the result of $this->nullDate(true). + * + * Usage: + * $query->format('SELECT %1$n FROM %2$n WHERE %3$n = %4$a', 'foo', '#__foo', 'bar', 1); + * Returns: SELECT `foo` FROM `#__foo` WHERE `bar` = 1 + * + * Notes: + * The argument specifier is optional but recommended for clarity. + * The argument index used for unspecified tokens is incremented only when used. + * + * @param string $format The formatting string. + * + * @return string Returns a string produced according to the formatting string. + * + * @since 12.3 + */ + public function format($format) + { + $query = $this; + $args = array_slice(func_get_args(), 1); + array_unshift($args, null); + + $i = 1; + $func = function ($match) use ($query, $args, &$i) + { + if (isset($match[6]) && $match[6] == '%') + { + return '%'; + } + + // No argument required, do not increment the argument index. + switch ($match[5]) + { + case 't': + return $query->currentTimestamp(); + break; + + case 'z': + return $query->nullDate(false); + break; + + case 'Z': + return $query->nullDate(true); + break; + } + + // Increment the argument index only if argument specifier not provided. + $index = is_numeric($match[4]) ? (int) $match[4] : $i++; + + if (!$index || !isset($args[$index])) + { + // TODO - What to do? sprintf() throws a Warning in these cases. + $replacement = ''; + } + else + { + $replacement = $args[$index]; + } + + switch ($match[5]) + { + case 'a': + return 0 + $replacement; + break; + + case 'e': + return $query->escape($replacement); + break; + + case 'E': + return $query->escape($replacement, true); + break; + + case 'n': + return $query->quoteName($replacement); + break; + + case 'q': + return $query->quote($replacement); + break; + + case 'Q': + return $query->quote($replacement, false); + break; + + case 'r': + return $replacement; + break; + + // Dates + case 'y': + return $query->year($query->quote($replacement)); + break; + + case 'Y': + return $query->year($query->quoteName($replacement)); + break; + + case 'm': + return $query->month($query->quote($replacement)); + break; + + case 'M': + return $query->month($query->quoteName($replacement)); + break; + + case 'd': + return $query->day($query->quote($replacement)); + break; + + case 'D': + return $query->day($query->quoteName($replacement)); + break; + + case 'h': + return $query->hour($query->quote($replacement)); + break; + + case 'H': + return $query->hour($query->quoteName($replacement)); + break; + + case 'i': + return $query->minute($query->quote($replacement)); + break; + + case 'I': + return $query->minute($query->quoteName($replacement)); + break; + + case 's': + return $query->second($query->quote($replacement)); + break; + + case 'S': + return $query->second($query->quoteName($replacement)); + break; + } + + return ''; + }; + + /** + * Regexp to find an replace all tokens. + * Matched fields: + * 0: Full token + * 1: Everything following '%' + * 2: Everything following '%' unless '%' + * 3: Argument specifier and '$' + * 4: Argument specifier + * 5: Type specifier + * 6: '%' if full token is '%%' + */ + return preg_replace_callback('#%(((([\d]+)\$)?([aeEnqQryYmMdDhHiIsStzZ]))|(%))#', $func, $format); + } + + /** + * Add to the current date and time. + * Usage: + * $query->select($query->dateAdd()); + * Prefixing the interval with a - (negative sign) will cause subtraction to be used. + * Note: Not all drivers support all units. + * + * @param datetime $date The date to add to. May be date or datetime + * @param string $interval The string representation of the appropriate number of units + * @param string $datePart The part of the date to perform the addition on + * + * @return string The string with the appropriate sql for addition of dates + * + * @see http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_date-add + * @since 13.1 + */ + public function dateAdd($date, $interval, $datePart) + { + return trim("DATE_ADD('" . $date . "', INTERVAL " . $interval . ' ' . $datePart . ')'); + } + + /** + * Add a query to UNION ALL with the current query. + * Multiple unions each require separate statements and create an array of unions. + * + * Usage: + * $query->union('SELECT name FROM #__foo') + * $query->union(array('SELECT name FROM #__foo','SELECT name FROM #__bar')) + * + * @param mixed $query The FOFDatabaseQuery object or string to union. + * @param boolean $distinct Not used - ignored. + * @param string $glue Not used - ignored. + * + * @return mixed The FOFDatabaseQuery object on success or boolean false on failure. + * + * @see union + * + * @since 13.1 + */ + public function unionAll($query, $distinct = false, $glue = '') + { + $glue = ')' . PHP_EOL . 'UNION ALL ('; + $name = 'UNION ALL ()'; + + // Get the FOFDatabaseQueryElement if it does not exist + if (is_null($this->unionAll)) + { + $this->unionAll = new FOFDatabaseQueryElement($name, $query, "$glue"); + } + + // Otherwise append the second UNION. + else + { + $this->unionAll->append($query); + } + + return $this; + } +} diff --git a/libraries/fof/database/query/element.php b/libraries/fof/database/query/element.php new file mode 100644 index 0000000000000..f3a5bae8a2d25 --- /dev/null +++ b/libraries/fof/database/query/element.php @@ -0,0 +1,132 @@ +elements = array(); + $this->name = $name; + $this->glue = $glue; + + $this->append($elements); + } + + /** + * Magic function to convert the query element to a string. + * + * @return string + * + * @since 11.1 + */ + public function __toString() + { + if (substr($this->name, -2) == '()') + { + return PHP_EOL . substr($this->name, 0, -2) . '(' . implode($this->glue, $this->elements) . ')'; + } + else + { + return PHP_EOL . $this->name . ' ' . implode($this->glue, $this->elements); + } + } + + /** + * Appends element parts to the internal list. + * + * @param mixed $elements String or array. + * + * @return void + * + * @since 11.1 + */ + public function append($elements) + { + if (is_array($elements)) + { + $this->elements = array_merge($this->elements, $elements); + } + else + { + $this->elements = array_merge($this->elements, array($elements)); + } + } + + /** + * Gets the elements of this element. + * + * @return array + * + * @since 11.1 + */ + public function getElements() + { + return $this->elements; + } + + /** + * Method to provide deep copy support to nested objects and arrays + * when cloning. + * + * @return void + * + * @since 11.3 + */ + public function __clone() + { + foreach ($this as $k => $v) + { + if (is_object($v) || is_array($v)) + { + $this->{$k} = unserialize(serialize($v)); + } + } + } +} diff --git a/libraries/fof/database/query/limitable.php b/libraries/fof/database/query/limitable.php new file mode 100644 index 0000000000000..5d450e5babfd9 --- /dev/null +++ b/libraries/fof/database/query/limitable.php @@ -0,0 +1,72 @@ +setLimit(100, 0); (retrieve 100 rows, starting at first record) + * $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th record) + * + * @param integer $limit The limit for the result set + * @param integer $offset The offset for the result set + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 12.1 + */ + public function setLimit($limit = 0, $offset = 0); + } +} + +/** + * Joomla Database Query Limitable Interface. + * Adds bind/unbind methods as well as a getBounded() method + * to retrieve the stored bounded variables on demand prior to + * query execution. + * + * @since 12.1 + */ +interface FOFDatabaseQueryLimitable extends JDatabaseQueryLimitable +{ +} diff --git a/libraries/fof/database/query/mysql.php b/libraries/fof/database/query/mysql.php new file mode 100644 index 0000000000000..73db5b31cfe38 --- /dev/null +++ b/libraries/fof/database/query/mysql.php @@ -0,0 +1,23 @@ + 0 || $offset > 0) + { + $query .= ' LIMIT ' . $offset . ', ' . $limit; + } + + return $query; + } + + /** + * Concatenates an array of column names or values. + * + * @param array $values An array of values to concatenate. + * @param string $separator As separator to place between each value. + * + * @return string The concatenated values. + * + * @since 11.1 + */ + public function concatenate($values, $separator = null) + { + if ($separator) + { + $concat_string = 'CONCAT_WS(' . $this->quote($separator); + + foreach ($values as $value) + { + $concat_string .= ', ' . $value; + } + + return $concat_string . ')'; + } + else + { + return 'CONCAT(' . implode(',', $values) . ')'; + } + } + + /** + * Sets the offset and limit for the result set, if the database driver supports it. + * + * Usage: + * $query->setLimit(100, 0); (retrieve 100 rows, starting at first record) + * $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th record) + * + * @param integer $limit The limit for the result set + * @param integer $offset The offset for the result set + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 12.1 + */ + public function setLimit($limit = 0, $offset = 0) + { + $this->limit = (int) $limit; + $this->offset = (int) $offset; + + return $this; + } + + /** + * Return correct regexp operator for mysqli. + * + * Ensure that the regexp operator is mysqli compatible. + * + * Usage: + * $query->where('field ' . $query->regexp($search)); + * + * @param string $value The regex pattern. + * + * @return string Returns the regex operator. + * + * @since 11.3 + */ + public function regexp($value) + { + return ' REGEXP ' . $value; + } + + /** + * Return correct rand() function for Mysql. + * + * Ensure that the rand() function is Mysql compatible. + * + * Usage: + * $query->Rand(); + * + * @return string The correct rand function. + * + * @since 3.5 + */ + public function Rand() + { + return ' RAND() '; + } +} diff --git a/libraries/fof/database/query/oracle.php b/libraries/fof/database/query/oracle.php new file mode 100644 index 0000000000000..62206908cd5a0 --- /dev/null +++ b/libraries/fof/database/query/oracle.php @@ -0,0 +1,205 @@ +bounded = array(); + + return $this; + } + + // Case 2: Key Provided, null value (unset key from $bounded array) + if (is_null($value)) + { + if (isset($this->bounded[$key])) + { + unset($this->bounded[$key]); + } + + return $this; + } + + $obj = new stdClass; + + $obj->value = &$value; + $obj->dataType = $dataType; + $obj->length = $length; + $obj->driverOptions = $driverOptions; + + // Case 3: Simply add the Key/Value into the bounded array + $this->bounded[$key] = $obj; + + return $this; + } + + /** + * Retrieves the bound parameters array when key is null and returns it by reference. If a key is provided then that item is + * returned. + * + * @param mixed $key The bounded variable key to retrieve. + * + * @return mixed + * + * @since 12.1 + */ + public function &getBounded($key = null) + { + if (empty($key)) + { + return $this->bounded; + } + else + { + if (isset($this->bounded[$key])) + { + return $this->bounded[$key]; + } + } + } + + /** + * Clear data from the query or a specific clause of the query. + * + * @param string $clause Optionally, the name of the clause to clear, or nothing to clear the whole query. + * + * @return FOFDatabaseQueryOracle Returns this object to allow chaining. + * + * @since 12.1 + */ + public function clear($clause = null) + { + switch ($clause) + { + case null: + $this->bounded = array(); + break; + } + + parent::clear($clause); + + return $this; + } + + /** + * Method to modify a query already in string format with the needed + * additions to make the query limited to a particular number of + * results, or start at a particular offset. This method is used + * automatically by the __toString() method if it detects that the + * query implements the FOFDatabaseQueryLimitable interface. + * + * @param string $query The query in string format + * @param integer $limit The limit for the result set + * @param integer $offset The offset for the result set + * + * @return string + * + * @since 12.1 + */ + public function processLimit($query, $limit, $offset = 0) + { + // Check if we need to mangle the query. + if ($limit || $offset) + { + $query = "SELECT joomla2.* + FROM ( + SELECT joomla1.*, ROWNUM AS joomla_db_rownum + FROM ( + " . $query . " + ) joomla1 + ) joomla2"; + + // Check if the limit value is greater than zero. + if ($limit > 0) + { + $query .= ' WHERE joomla2.joomla_db_rownum BETWEEN ' . ($offset + 1) . ' AND ' . ($offset + $limit); + } + else + { + // Check if there is an offset and then use this. + if ($offset) + { + $query .= ' WHERE joomla2.joomla_db_rownum > ' . ($offset + 1); + } + } + } + + return $query; + } + + /** + * Sets the offset and limit for the result set, if the database driver supports it. + * + * Usage: + * $query->setLimit(100, 0); (retrieve 100 rows, starting at first record) + * $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th record) + * + * @param integer $limit The limit for the result set + * @param integer $offset The offset for the result set + * + * @return FOFDatabaseQueryOracle Returns this object to allow chaining. + * + * @since 12.1 + */ + public function setLimit($limit = 0, $offset = 0) + { + $this->limit = (int) $limit; + $this->offset = (int) $offset; + + return $this; + } +} diff --git a/libraries/fof/database/query/pdo.php b/libraries/fof/database/query/pdo.php new file mode 100644 index 0000000000000..13d361989baa9 --- /dev/null +++ b/libraries/fof/database/query/pdo.php @@ -0,0 +1,22 @@ +type) + { + case 'select': + $query .= (string) $this->select; + $query .= (string) $this->from; + + if ($this->join) + { + // Special case for joins + foreach ($this->join as $join) + { + $query .= (string) $join; + } + } + + if ($this->where) + { + $query .= (string) $this->where; + } + + if ($this->group) + { + $query .= (string) $this->group; + } + + if ($this->having) + { + $query .= (string) $this->having; + } + + if ($this->order) + { + $query .= (string) $this->order; + } + + if ($this->forUpdate) + { + $query .= (string) $this->forUpdate; + } + else + { + if ($this->forShare) + { + $query .= (string) $this->forShare; + } + } + + if ($this->noWait) + { + $query .= (string) $this->noWait; + } + + break; + + case 'update': + $query .= (string) $this->update; + $query .= (string) $this->set; + + if ($this->join) + { + $onWord = ' ON '; + + // Workaround for special case of JOIN with UPDATE + foreach ($this->join as $join) + { + $joinElem = $join->getElements(); + + $joinArray = explode($onWord, $joinElem[0]); + + $this->from($joinArray[0]); + $this->where($joinArray[1]); + } + + $query .= (string) $this->from; + } + + if ($this->where) + { + $query .= (string) $this->where; + } + + break; + + case 'insert': + $query .= (string) $this->insert; + + if ($this->values) + { + if ($this->columns) + { + $query .= (string) $this->columns; + } + + $elements = $this->values->getElements(); + + if (!($elements[0] instanceof $this)) + { + $query .= ' VALUES '; + } + + $query .= (string) $this->values; + + if ($this->returning) + { + $query .= (string) $this->returning; + } + } + + break; + + default: + $query = parent::__toString(); + break; + } + + if ($this instanceof FOFDatabaseQueryLimitable) + { + $query = $this->processLimit($query, $this->limit, $this->offset); + } + + return $query; + } + + /** + * Clear data from the query or a specific clause of the query. + * + * @param string $clause Optionally, the name of the clause to clear, or nothing to clear the whole query. + * + * @return FOFDatabaseQueryPostgresql Returns this object to allow chaining. + * + * @since 11.3 + */ + public function clear($clause = null) + { + switch ($clause) + { + case 'limit': + $this->limit = null; + break; + + case 'offset': + $this->offset = null; + break; + + case 'forUpdate': + $this->forUpdate = null; + break; + + case 'forShare': + $this->forShare = null; + break; + + case 'noWait': + $this->noWait = null; + break; + + case 'returning': + $this->returning = null; + break; + + case 'select': + case 'update': + case 'delete': + case 'insert': + case 'from': + case 'join': + case 'set': + case 'where': + case 'group': + case 'having': + case 'order': + case 'columns': + case 'values': + parent::clear($clause); + break; + + default: + $this->type = null; + $this->limit = null; + $this->offset = null; + $this->forUpdate = null; + $this->forShare = null; + $this->noWait = null; + $this->returning = null; + parent::clear($clause); + break; + } + + return $this; + } + + /** + * Casts a value to a char. + * + * Ensure that the value is properly quoted before passing to the method. + * + * Usage: + * $query->select($query->castAsChar('a')); + * + * @param string $value The value to cast as a char. + * + * @return string Returns the cast value. + * + * @since 11.3 + */ + public function castAsChar($value) + { + return $value . '::text'; + } + + /** + * Concatenates an array of column names or values. + * + * Usage: + * $query->select($query->concatenate(array('a', 'b'))); + * + * @param array $values An array of values to concatenate. + * @param string $separator As separator to place between each value. + * + * @return string The concatenated values. + * + * @since 11.3 + */ + public function concatenate($values, $separator = null) + { + if ($separator) + { + return implode(' || ' . $this->quote($separator) . ' || ', $values); + } + else + { + return implode(' || ', $values); + } + } + + /** + * Gets the current date and time. + * + * @return string Return string used in query to obtain + * + * @since 11.3 + */ + public function currentTimestamp() + { + return 'NOW()'; + } + + /** + * Sets the FOR UPDATE lock on select's output row + * + * @param string $table_name The table to lock + * @param string $glue The glue by which to join the conditions. Defaults to ',' . + * + * @return FOFDatabaseQueryPostgresql FOR UPDATE query element + * + * @since 11.3 + */ + public function forUpdate($table_name, $glue = ',') + { + $this->type = 'forUpdate'; + + if (is_null($this->forUpdate)) + { + $glue = strtoupper($glue); + $this->forUpdate = new FOFDatabaseQueryElement('FOR UPDATE', 'OF ' . $table_name, "$glue "); + } + else + { + $this->forUpdate->append($table_name); + } + + return $this; + } + + /** + * Sets the FOR SHARE lock on select's output row + * + * @param string $table_name The table to lock + * @param string $glue The glue by which to join the conditions. Defaults to ',' . + * + * @return FOFDatabaseQueryPostgresql FOR SHARE query element + * + * @since 11.3 + */ + public function forShare($table_name, $glue = ',') + { + $this->type = 'forShare'; + + if (is_null($this->forShare)) + { + $glue = strtoupper($glue); + $this->forShare = new FOFDatabaseQueryElement('FOR SHARE', 'OF ' . $table_name, "$glue "); + } + else + { + $this->forShare->append($table_name); + } + + return $this; + } + + /** + * Used to get a string to extract year from date column. + * + * Usage: + * $query->select($query->year($query->quoteName('dateColumn'))); + * + * @param string $date Date column containing year to be extracted. + * + * @return string Returns string to extract year from a date. + * + * @since 12.1 + */ + public function year($date) + { + return 'EXTRACT (YEAR FROM ' . $date . ')'; + } + + /** + * Used to get a string to extract month from date column. + * + * Usage: + * $query->select($query->month($query->quoteName('dateColumn'))); + * + * @param string $date Date column containing month to be extracted. + * + * @return string Returns string to extract month from a date. + * + * @since 12.1 + */ + public function month($date) + { + return 'EXTRACT (MONTH FROM ' . $date . ')'; + } + + /** + * Used to get a string to extract day from date column. + * + * Usage: + * $query->select($query->day($query->quoteName('dateColumn'))); + * + * @param string $date Date column containing day to be extracted. + * + * @return string Returns string to extract day from a date. + * + * @since 12.1 + */ + public function day($date) + { + return 'EXTRACT (DAY FROM ' . $date . ')'; + } + + /** + * Used to get a string to extract hour from date column. + * + * Usage: + * $query->select($query->hour($query->quoteName('dateColumn'))); + * + * @param string $date Date column containing hour to be extracted. + * + * @return string Returns string to extract hour from a date. + * + * @since 12.1 + */ + public function hour($date) + { + return 'EXTRACT (HOUR FROM ' . $date . ')'; + } + + /** + * Used to get a string to extract minute from date column. + * + * Usage: + * $query->select($query->minute($query->quoteName('dateColumn'))); + * + * @param string $date Date column containing minute to be extracted. + * + * @return string Returns string to extract minute from a date. + * + * @since 12.1 + */ + public function minute($date) + { + return 'EXTRACT (MINUTE FROM ' . $date . ')'; + } + + /** + * Used to get a string to extract seconds from date column. + * + * Usage: + * $query->select($query->second($query->quoteName('dateColumn'))); + * + * @param string $date Date column containing second to be extracted. + * + * @return string Returns string to extract second from a date. + * + * @since 12.1 + */ + public function second($date) + { + return 'EXTRACT (SECOND FROM ' . $date . ')'; + } + + /** + * Sets the NOWAIT lock on select's output row + * + * @return FOFDatabaseQueryPostgresql NO WAIT query element + * + * @since 11.3 + */ + public function noWait () + { + $this->type = 'noWait'; + + if (is_null($this->noWait)) + { + $this->noWait = new FOFDatabaseQueryElement('NOWAIT', null); + } + + return $this; + } + + /** + * Set the LIMIT clause to the query + * + * @param integer $limit An int of how many row will be returned + * + * @return FOFDatabaseQueryPostgresql Returns this object to allow chaining. + * + * @since 11.3 + */ + public function limit($limit = 0) + { + if (is_null($this->limit)) + { + $this->limit = new FOFDatabaseQueryElement('LIMIT', (int) $limit); + } + + return $this; + } + + /** + * Set the OFFSET clause to the query + * + * @param integer $offset An int for skipping row + * + * @return FOFDatabaseQueryPostgresql Returns this object to allow chaining. + * + * @since 11.3 + */ + public function offset($offset = 0) + { + if (is_null($this->offset)) + { + $this->offset = new FOFDatabaseQueryElement('OFFSET', (int) $offset); + } + + return $this; + } + + /** + * Add the RETURNING element to INSERT INTO statement. + * + * @param mixed $pkCol The name of the primary key column. + * + * @return FOFDatabaseQueryPostgresql Returns this object to allow chaining. + * + * @since 11.3 + */ + public function returning($pkCol) + { + if (is_null($this->returning)) + { + $this->returning = new FOFDatabaseQueryElement('RETURNING', $pkCol); + } + + return $this; + } + + /** + * Sets the offset and limit for the result set, if the database driver supports it. + * + * Usage: + * $query->setLimit(100, 0); (retrieve 100 rows, starting at first record) + * $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th record) + * + * @param integer $limit The limit for the result set + * @param integer $offset The offset for the result set + * + * @return FOFDatabaseQueryPostgresql Returns this object to allow chaining. + * + * @since 12.1 + */ + public function setLimit($limit = 0, $offset = 0) + { + $this->limit = (int) $limit; + $this->offset = (int) $offset; + + return $this; + } + + /** + * Method to modify a query already in string format with the needed + * additions to make the query limited to a particular number of + * results, or start at a particular offset. + * + * @param string $query The query in string format + * @param integer $limit The limit for the result set + * @param integer $offset The offset for the result set + * + * @return string + * + * @since 12.1 + */ + public function processLimit($query, $limit, $offset = 0) + { + if ($limit > 0) + { + $query .= ' LIMIT ' . $limit; + } + + if ($offset > 0) + { + $query .= ' OFFSET ' . $offset; + } + + return $query; + } + + /** + * Add to the current date and time in Postgresql. + * Usage: + * $query->select($query->dateAdd()); + * Prefixing the interval with a - (negative sign) will cause subtraction to be used. + * + * @param datetime $date The date to add to + * @param string $interval The string representation of the appropriate number of units + * @param string $datePart The part of the date to perform the addition on + * + * @return string The string with the appropriate sql for addition of dates + * + * @since 13.1 + * @note Not all drivers support all units. Check appropriate references + * @link http://www.postgresql.org/docs/9.0/static/functions-datetime.html. + */ + public function dateAdd($date, $interval, $datePart) + { + if (substr($interval, 0, 1) != '-') + { + return "timestamp '" . $date . "' + interval '" . $interval . " " . $datePart . "'"; + } + else + { + return "timestamp '" . $date . "' - interval '" . ltrim($interval, '-') . " " . $datePart . "'"; + } + } + + /** + * Return correct regexp operator for Postgresql. + * + * Ensure that the regexp operator is Postgresql compatible. + * + * Usage: + * $query->where('field ' . $query->regexp($search)); + * + * @param string $value The regex pattern. + * + * @return string Returns the regex operator. + * + * @since 11.3 + */ + public function regexp($value) + { + return ' ~* ' . $value; + } + + /** + * Return correct rand() function for Postgresql. + * + * Ensure that the rand() function is Postgresql compatible. + * + * Usage: + * $query->Rand(); + * + * @return string The correct rand function. + * + * @since 3.5 + */ + public function Rand() + { + return ' RANDOM() '; + } +} diff --git a/libraries/fof/database/query/preparable.php b/libraries/fof/database/query/preparable.php new file mode 100644 index 0000000000000..8f58bfa81a999 --- /dev/null +++ b/libraries/fof/database/query/preparable.php @@ -0,0 +1,62 @@ +bounded = array(); + + return $this; + } + + // Case 2: Key Provided, null value (unset key from $bounded array) + if (is_null($value)) + { + if (isset($this->bounded[$key])) + { + unset($this->bounded[$key]); + } + + return $this; + } + + $obj = new stdClass; + + $obj->value = &$value; + $obj->dataType = $dataType; + $obj->length = $length; + $obj->driverOptions = $driverOptions; + + // Case 3: Simply add the Key/Value into the bounded array + $this->bounded[$key] = $obj; + + return $this; + } + + /** + * Retrieves the bound parameters array when key is null and returns it by reference. If a key is provided then that item is + * returned. + * + * @param mixed $key The bounded variable key to retrieve. + * + * @return mixed + * + * @since 12.1 + */ + public function &getBounded($key = null) + { + if (empty($key)) + { + return $this->bounded; + } + else + { + if (isset($this->bounded[$key])) + { + return $this->bounded[$key]; + } + } + } + + /** + * Gets the number of characters in a string. + * + * Note, use 'length' to find the number of bytes in a string. + * + * Usage: + * $query->select($query->charLength('a')); + * + * @param string $field A value. + * @param string $operator Comparison operator between charLength integer value and $condition + * @param string $condition Integer value to compare charLength with. + * + * @return string The required char length call. + * + * @since 13.1 + */ + public function charLength($field, $operator = null, $condition = null) + { + return 'length(' . $field . ')' . (isset($operator) && isset($condition) ? ' ' . $operator . ' ' . $condition : ''); + } + + /** + * Clear data from the query or a specific clause of the query. + * + * @param string $clause Optionally, the name of the clause to clear, or nothing to clear the whole query. + * + * @return FOFDatabaseQuerySqlite Returns this object to allow chaining. + * + * @since 12.1 + */ + public function clear($clause = null) + { + switch ($clause) + { + case null: + $this->bounded = array(); + break; + } + + parent::clear($clause); + + return $this; + } + + /** + * Concatenates an array of column names or values. + * + * Usage: + * $query->select($query->concatenate(array('a', 'b'))); + * + * @param array $values An array of values to concatenate. + * @param string $separator As separator to place between each value. + * + * @return string The concatenated values. + * + * @since 11.1 + */ + public function concatenate($values, $separator = null) + { + if ($separator) + { + return implode(' || ' . $this->quote($separator) . ' || ', $values); + } + else + { + return implode(' || ', $values); + } + } + + /** + * Method to modify a query already in string format with the needed + * additions to make the query limited to a particular number of + * results, or start at a particular offset. This method is used + * automatically by the __toString() method if it detects that the + * query implements the FOFDatabaseQueryLimitable interface. + * + * @param string $query The query in string format + * @param integer $limit The limit for the result set + * @param integer $offset The offset for the result set + * + * @return string + * + * @since 12.1 + */ + public function processLimit($query, $limit, $offset = 0) + { + if ($limit > 0 || $offset > 0) + { + $query .= ' LIMIT ' . $offset . ', ' . $limit; + } + + return $query; + } + + /** + * Sets the offset and limit for the result set, if the database driver supports it. + * + * Usage: + * $query->setLimit(100, 0); (retrieve 100 rows, starting at first record) + * $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th record) + * + * @param integer $limit The limit for the result set + * @param integer $offset The offset for the result set + * + * @return FOFDatabaseQuerySqlite Returns this object to allow chaining. + * + * @since 12.1 + */ + public function setLimit($limit = 0, $offset = 0) + { + $this->limit = (int) $limit; + $this->offset = (int) $offset; + + return $this; + } + + /** + * Add to the current date and time. + * Usage: + * $query->select($query->dateAdd()); + * Prefixing the interval with a - (negative sign) will cause subtraction to be used. + * + * @param datetime $date The date or datetime to add to + * @param string $interval The string representation of the appropriate number of units + * @param string $datePart The part of the date to perform the addition on + * + * @return string The string with the appropriate sql for addition of dates + * + * @since 13.1 + * @link http://www.sqlite.org/lang_datefunc.html + */ + public function dateAdd($date, $interval, $datePart) + { + // SQLite does not support microseconds as a separate unit. Convert the interval to seconds + if (strcasecmp($datePart, 'microseconds') == 0) + { + $interval = .001 * $interval; + $datePart = 'seconds'; + } + + if (substr($interval, 0, 1) != '-') + { + return "datetime('" . $date . "', '+" . $interval . " " . $datePart . "')"; + } + else + { + return "datetime('" . $date . "', '" . $interval . " " . $datePart . "')"; + } + } + + /** + * Gets the current date and time. + * + * Usage: + * $query->where('published_up < '.$query->currentTimestamp()); + * + * @return string + * + * @since 3.4 + */ + public function currentTimestamp() + { + return 'CURRENT_TIMESTAMP'; + } +} diff --git a/libraries/fof/database/query/sqlsrv.php b/libraries/fof/database/query/sqlsrv.php new file mode 100644 index 0000000000000..bee0f80716ed9 --- /dev/null +++ b/libraries/fof/database/query/sqlsrv.php @@ -0,0 +1,380 @@ +type) + { + case 'select': + $query .= (string) $this->select; + $query .= (string) $this->from; + + if ($this->join) + { + // Special case for joins + foreach ($this->join as $join) + { + $query .= (string) $join; + } + } + + if ($this->where) + { + $query .= (string) $this->where; + } + + if ($this->group) + { + $query .= (string) $this->group; + } + + if ($this->order) + { + $query .= (string) $this->order; + } + + if ($this->having) + { + $query .= (string) $this->having; + } + + if ($this instanceof FOFDatabaseQueryLimitable && ($this->limit > 0 || $this->offset > 0)) + { + $query = $this->processLimit($query, $this->limit, $this->offset); + } + + break; + + case 'insert': + $query .= (string) $this->insert; + + // Set method + if ($this->set) + { + $query .= (string) $this->set; + } + // Columns-Values method + elseif ($this->values) + { + if ($this->columns) + { + $query .= (string) $this->columns; + } + + $elements = $this->insert->getElements(); + $tableName = array_shift($elements); + + $query .= 'VALUES '; + $query .= (string) $this->values; + + if ($this->autoIncrementField) + { + $query = 'SET IDENTITY_INSERT ' . $tableName . ' ON;' . $query . 'SET IDENTITY_INSERT ' . $tableName . ' OFF;'; + } + + if ($this->where) + { + $query .= (string) $this->where; + } + } + + break; + + case 'delete': + $query .= (string) $this->delete; + $query .= (string) $this->from; + + if ($this->join) + { + // Special case for joins + foreach ($this->join as $join) + { + $query .= (string) $join; + } + } + + if ($this->where) + { + $query .= (string) $this->where; + } + + if ($this->order) + { + $query .= (string) $this->order; + } + + break; + + case 'update': + $query .= (string) $this->update; + + if ($this->join) + { + // Special case for joins + foreach ($this->join as $join) + { + $query .= (string) $join; + } + } + + $query .= (string) $this->set; + + if ($this->where) + { + $query .= (string) $this->where; + } + + if ($this->order) + { + $query .= (string) $this->order; + } + + break; + + default: + $query = parent::__toString(); + break; + } + + return $query; + } + + /** + * Casts a value to a char. + * + * Ensure that the value is properly quoted before passing to the method. + * + * @param string $value The value to cast as a char. + * + * @return string Returns the cast value. + * + * @since 11.1 + */ + public function castAsChar($value) + { + return 'CAST(' . $value . ' as NVARCHAR(10))'; + } + + /** + * Gets the function to determine the length of a character string. + * + * @param string $field A value. + * @param string $operator Comparison operator between charLength integer value and $condition + * @param string $condition Integer value to compare charLength with. + * + * @return string The required char length call. + * + * @since 11.1 + */ + public function charLength($field, $operator = null, $condition = null) + { + return 'DATALENGTH(' . $field . ')' . (isset($operator) && isset($condition) ? ' ' . $operator . ' ' . $condition : ''); + } + + /** + * Concatenates an array of column names or values. + * + * @param array $values An array of values to concatenate. + * @param string $separator As separator to place between each value. + * + * @return string The concatenated values. + * + * @since 11.1 + */ + public function concatenate($values, $separator = null) + { + if ($separator) + { + return '(' . implode('+' . $this->quote($separator) . '+', $values) . ')'; + } + else + { + return '(' . implode('+', $values) . ')'; + } + } + + /** + * Gets the current date and time. + * + * @return string + * + * @since 11.1 + */ + public function currentTimestamp() + { + return 'GETDATE()'; + } + + /** + * Get the length of a string in bytes. + * + * @param string $value The string to measure. + * + * @return integer + * + * @since 11.1 + */ + public function length($value) + { + return 'LEN(' . $value . ')'; + } + + /** + * Add to the current date and time. + * Usage: + * $query->select($query->dateAdd()); + * Prefixing the interval with a - (negative sign) will cause subtraction to be used. + * + * @param datetime $date The date to add to; type may be time or datetime. + * @param string $interval The string representation of the appropriate number of units + * @param string $datePart The part of the date to perform the addition on + * + * @return string The string with the appropriate sql for addition of dates + * + * @since 13.1 + * @note Not all drivers support all units. + * @link http://msdn.microsoft.com/en-us/library/ms186819.aspx for more information + */ + public function dateAdd($date, $interval, $datePart) + { + return "DATEADD('" . $datePart . "', '" . $interval . "', '" . $date . "'" . ')'; + } + + /** + * Method to modify a query already in string format with the needed + * additions to make the query limited to a particular number of + * results, or start at a particular offset. + * + * @param string $query The query in string format + * @param integer $limit The limit for the result set + * @param integer $offset The offset for the result set + * + * @return string + * + * @since 12.1 + */ + public function processLimit($query, $limit, $offset = 0) + { + if ($limit == 0 && $offset == 0) + { + return $query; + } + + $start = $offset + 1; + $end = $offset + $limit; + + $orderBy = stristr($query, 'ORDER BY'); + + if (is_null($orderBy) || empty($orderBy)) + { + $orderBy = 'ORDER BY (select 0)'; + } + + $query = str_ireplace($orderBy, '', $query); + + $rowNumberText = ', ROW_NUMBER() OVER (' . $orderBy . ') AS RowNumber FROM '; + + $query = preg_replace('/\sFROM\s/i', $rowNumberText, $query, 1); + $query = 'SELECT * FROM (' . $query . ') A WHERE A.RowNumber BETWEEN ' . $start . ' AND ' . $end; + + return $query; + } + + /** + * Sets the offset and limit for the result set, if the database driver supports it. + * + * Usage: + * $query->setLimit(100, 0); (retrieve 100 rows, starting at first record) + * $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th record) + * + * @param integer $limit The limit for the result set + * @param integer $offset The offset for the result set + * + * @return FOFDatabaseQuery Returns this object to allow chaining. + * + * @since 12.1 + */ + public function setLimit($limit = 0, $offset = 0) + { + $this->limit = (int) $limit; + $this->offset = (int) $offset; + + return $this; + } + + /** + * Return correct rand() function for MSSQL. + * + * Ensure that the rand() function is MSSQL compatible. + * + * Usage: + * $query->Rand(); + * + * @return string The correct rand function. + * + * @since 3.5 + */ + public function Rand() + { + return ' NEWID() '; + } +} diff --git a/libraries/fof/dispatcher/dispatcher.php b/libraries/fof/dispatcher/dispatcher.php index fc1cccc547045..e253b15db7ac9 100644 --- a/libraries/fof/dispatcher/dispatcher.php +++ b/libraries/fof/dispatcher/dispatcher.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage dispatcher - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/download/adapter/abstract.php b/libraries/fof/download/adapter/abstract.php index 4bf4be7e0d63e..19f46f2f2ce00 100644 --- a/libraries/fof/download/adapter/abstract.php +++ b/libraries/fof/download/adapter/abstract.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage dispatcher - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/download/adapter/cacert.pem b/libraries/fof/download/adapter/cacert.pem new file mode 100644 index 0000000000000..f9126f8f284ed --- /dev/null +++ b/libraries/fof/download/adapter/cacert.pem @@ -0,0 +1,5104 @@ +## +## Bundle of CA Root Certificates +## +## Certificate data from CentOS as of Oct 3 2015 +## +## This is a bundle of X.509 certificates of public Certificate Authorities +## (CA). These were automatically extracted from CentOS /etc/pki/tls/certs/ca-bundle.crt +## +## It contains the certificates in PEM format and therefore +## can be directly used with curl / libcurl / php_curl, or with +## an Apache+mod_ssl webserver for SSL client authentication. +## Just configure this file as the SSLCACertificateFile. +## + +-----BEGIN CERTIFICATE----- +MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD +VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv +bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv +b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV +UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU +cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds +b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH +iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS +r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4 +04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r +GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9 +3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P +lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkEx +FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD +VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEm +MCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wHhcNOTYwODAx +MDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3 +dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNl +cyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3 +DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD +gY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91 +yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCX +L+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGj +EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG +7oWDTSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e +QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZ +qdq5snUb9kLy78fyGPmJvKP/iiMucEc= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkEx +FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD +VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2Vy +dmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29t +MB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpB +MRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsG +A1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRp +b24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNl +cnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNv +bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkE +VdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQ +ug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMR +uHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG +9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI +hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JM +pAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV +UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy +dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 +MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx +dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f +BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A +cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC +AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ +MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm +aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw +ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj +IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF +MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA +A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y +7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh +1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDKTCCApKgAwIBAgIENnAVljANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJV +UzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQL +EwhEU1RDQSBFMTAeFw05ODEyMTAxODEwMjNaFw0xODEyMTAxODQwMjNaMEYxCzAJ +BgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4x +ETAPBgNVBAsTCERTVENBIEUxMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCg +bIGpzzQeJN3+hijM3oMv+V7UQtLodGBmE5gGHKlREmlvMVW5SXIACH7TpWJENySZ +j9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+LthzfNHwJmm8fOR6Hh8AMthyUQncWlV +Sn5JTe2io74CTADKAqjuAQIxZA9SLRN0dja1erQtcQIBA6OCASQwggEgMBEGCWCG +SAGG+EIBAQQEAwIABzBoBgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMx +JDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMI +RFNUQ0EgRTExDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMTAxODEw +MjNagQ8yMDE4MTIxMDE4MTAyM1owCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFGp5 +fpFpRhgTCgJ3pVlbYJglDqL4MB0GA1UdDgQWBBRqeX6RaUYYEwoCd6VZW2CYJQ6i ++DAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqG +SIb3DQEBBQUAA4GBACIS2Hod3IEGtgllsofIH160L+nEHvI8wbsEkBFKg05+k7lN +QseSJqBcNJo4cvj9axY+IO6CizEqkzaFI4iKPANo08kJD038bKTaKHKTDomAsH3+ +gG9lbRgzl4vCa4nuYD3Im+9/KzJic5PLPON74nZ4RbyhkwS7hp86W0N6w4pl +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDKTCCApKgAwIBAgIENm7TzjANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJV +UzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQL +EwhEU1RDQSBFMjAeFw05ODEyMDkxOTE3MjZaFw0xODEyMDkxOTQ3MjZaMEYxCzAJ +BgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4x +ETAPBgNVBAsTCERTVENBIEUyMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC/ +k48Xku8zExjrEH9OFr//Bo8qhbxe+SSmJIi2A7fBw18DW9Fvrn5C6mYjuGODVvso +LeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87eZfCocfdPJmyMvMa1795JJ/9IKn3o +TQPMx7JSxhcxEzu1TdvIxPbDDyQq2gyd55FbgM2UnQIBA6OCASQwggEgMBEGCWCG +SAGG+EIBAQQEAwIABzBoBgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMx +JDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMI +RFNUQ0EgRTIxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMDkxOTE3 +MjZagQ8yMDE4MTIwOTE5MTcyNlowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFB6C +TShlgDzJQW6sNS5ay97u+DlbMB0GA1UdDgQWBBQegk0oZYA8yUFurDUuWsve7vg5 +WzAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqG +SIb3DQEBBQUAA4GBAEeNg61i8tuwnkUiBbmi1gMOOHLnnvx75pO2mqWilMg0HZHR +xdf0CiUPPXiBng+xZ8SQTGPdXqfiup/1902lMXucKS1M/mQ+7LZT/uqb7YLbdHVL +B3luHtgZg3Pe9T7Qtd7nS2h9Qy4qIOF+oHhEngj1mPnHfxsb1gYgAlihw6ID +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz +cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 +MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV +BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN +ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE +BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is +I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G +CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do +lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc +AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEEzH6qqYPnHTkxD4PTqJkZIwDQYJKoZIhvcNAQEFBQAwgcExCzAJ +BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh +c3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy +MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp +emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X +DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw +FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMSBQdWJsaWMg +UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo +YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5 +MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQCq0Lq+Fi24g9TK0g+8djHKlNgdk4xWArzZbxpvUjZudVYK +VdPfQ4chEWWKfo+9Id5rMj8bhDSVBZ1BNeuS65bdqlk/AVNtmU/t5eIqWpDBucSm +Fc/IReumXY6cPvBkJHalzasab7bYe1FhbqZ/h8jit+U03EGI6glAvnOSPWvndQID +AQABMA0GCSqGSIb3DQEBBQUAA4GBAKlPww3HZ74sy9mozS11534Vnjty637rXC0J +h9ZrbWB85a7FkCMMXErQr7Fd88e2CtvgFZMN3QO8x3aKtd1Pw5sTdbgBwObJW2ul +uIncrKTdcu1OofdPvAbT6shkdHvClUGcZXNY8ZCaPGqxmMnEh7zPRW1F4m4iP/68 +DzFc6PLZ +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDAzCCAmwCEQC5L2DMiJ+hekYJuFtwbIqvMA0GCSqGSIb3DQEBBQUAMIHBMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0Ns +YXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH +MjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9y +aXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazAe +Fw05ODA1MTgwMDAwMDBaFw0yODA4MDEyMzU5NTlaMIHBMQswCQYDVQQGEwJVUzEX +MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGlj +IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMx +KGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEAp4gBIXQs5xoD8JjhlzwPIQjxnNuX6Zr8wgQGE75fUsjM +HiwSViy4AWkszJkfrbCWrnkE8hM5wXuYuggs6MKEEyyqaekJ9MepAqRCwiNPStjw +DqL7MWzJ5m+ZJwf15vRMeJ5t60aG+rmGyVTyssSv1EYcWskVMP8NbPUtDm3Of3cC +AwEAATANBgkqhkiG9w0BAQUFAAOBgQByLvl/0fFx+8Se9sVeUYpAmLho+Jscg9ji +nb3/7aHmZuovCfTK1+qlK5X2JGCGTUQug6XELaDTrnhpb3LabK4I8GOSN+a7xDAX +rXfMSTWqz9iP0b63GJZHc2pUIjRkLbYWm1lbtFFZOrMLFPQS32eg9K0yZF6xRnIn +jBJ7xUS0rg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ +BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh +c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy +MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp +emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X +DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw +FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg +UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo +YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5 +MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4 +pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0 +13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID +AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk +U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i +F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY +oJ2daZH9 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw +MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B +AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz +yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE +38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP +AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad +DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME +HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz +BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y +aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG +9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIyMjM0OFoXDTE5MDYy +NTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y +azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw +Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl +cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9Y +LqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+ +TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8Y +TfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0 +LBwGlN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLW +I8sogTLDAHkY7FkXicnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPw +nXS3qT6gpf+2SQMT2iLM7XGCK5nPOrf1LXLI +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz +BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y +aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG +9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy +NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y +azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw +Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl +cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY +dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9 +WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS +v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v +UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu +IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC +W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz +BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y +aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG +9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMjIzM1oXDTE5MDYy +NjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y +azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw +Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl +cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfD +cnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs +2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqY +JJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliE +Zwgs3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJ +n0WuPIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A +PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCLW3VWhFSFCwDPrzhIzrGkMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN2E1Lm0+afY8wR4 +nN493GwTFtl63SRRZsDHJlkNrAYIwpTRMx/wgzUfbhvI3qpuFU5UJ+/EbRrsC+MO +8ESlV8dAWB6jRx9x7GD2bZTIGDnt/kIYVt/kTEkQeE4BdjVjEjbdZrwBBDajVWjV +ojYJrKshJlQGrT/KFOCsyq0GHZXi+J3x4GD/wn91K0zM2v6HmSHquv4+VNfSWXjb +PG7PoBMAGrgnoeS+Z5bKoMWznN3JdZ7rMJpfo83ZrngZPyPpXNspva1VyBtUjGP2 +6KbqxzcSXKMpHgLZ2x87tNcPVkeBFQRKr4Mn0cVYiMHd9qqnoxjaaKptEVHhv2Vr +n5Z20T0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAq2aN17O6x5q25lXQBfGfMY1a +qtmqRiYPce2lrVNWYgFHKkTp/j90CxObufRNG7LRX7K20ohcs5/Ny9Sn2WCVhDr4 +wTcdYcrnsMXlkdpUpqwxga6X3s0IrLjAl4B/bnKk52kTlWUfxJM8/XmPBNQ+T+r3 +ns7NZ3xPZQL/kYVUc8f/NveGLezQXk//EZ9yBta4GvFMDSZl4kSAHsef493oCtrs +pSCAaWihT37ha88HQfqDjrw43bAuEbFrskLMmrz5SCJ5ShkPshw+IHTZasO+8ih4 +E1Z5T21Q6huwtVexN2ZYI/PcD98Kh8TvhgXVOBRgmaNL3gaWcSzy27YfpO8/7g== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEGTCCAwECEGFwy0mMX5hFKeewptlQW3owDQYJKoZIhvcNAQEFBQAwgcoxCzAJ +BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVy +aVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24s +IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNp +Z24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eSAtIEczMB4XDTk5MTAwMTAwMDAwMFoXDTM2MDcxNjIzNTk1OVowgcoxCzAJBgNV +BAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNp +Z24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIElu +Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24g +Q2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt +IEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArwoNwtUs22e5LeWU +J92lvuCwTY+zYVY81nzD9M0+hsuiiOLh2KRpxbXiv8GmR1BeRjmL1Za6tW8UvxDO +JxOeBUebMXoT2B/Z0wI3i60sR/COgQanDTAM6/c8DyAd3HJG7qUCyFvDyVZpTMUY +wZF7C9UTAJu878NIPkZgIIUq1ZC2zYugzDLdt/1AVbJQHFauzI13TccgTacxdu9o +koqQHgiBVrKtaaNS0MscxCM9H5n+TOgWY47GCI72MfbS+uV23bUckqNJzc0BzWjN +qWm6o+sdDZykIKbBoMXRRkwXbdKsZj+WjOCE1Db/IlnF+RFgqF8EffIa9iVCYQ/E +Srg+iQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA0JhU8wI1NQ0kdvekhktdmnLfe +xbjQ5F1fdiLAJvmEOjr5jLX77GDx6M4EsMjdpwOPMPOY36TmpDHf0xwLRtxyID+u +7gU8pDM/CzmscHhzS5kr3zDCVLCoO1Wh/hYozUK9dG6A2ydEp85EXdQbkJgNHkKU +sQAsBNB0owIFImNjzYO1+8FtYmtpdf1dcEG59b98377BMnMiIYtYgXsVkXq642RI +sH/7NiXaldDxJBQX3RiAa0YjOVT1jmIJBB2UkKab5iXiQkWquJCtvgiPqQtCGJTP +cjnhsUPgKM+351psE2tJs//jGHyJizNdrDPXp/naOlXJWBD5qu9ats9LS98q +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b +N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t +KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu +kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm +CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ +Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu +imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te +2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe +DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p +F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt +TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK3LpRFpxlmr8Y+1 +GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaStBO3IFsJ ++mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0Gbd +U6LM8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLm +NxdLMEYH5IBtptiWLugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XY +ufTsgsbSPZUd5cBPhMnZo0QoBmrXRazwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ +ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAj/ola09b5KROJ1WrIhVZPMq1 +CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXttmhwwjIDLk5Mq +g6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm +fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c +2NU8Qh0XwRJdRTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/ +bLvSHgCwIe34QWKCudiyxLtGUPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC +VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u +ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc +KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u +ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1 +MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE +ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j +b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF +bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg +U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA +A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/ +I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3 +wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC +AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb +oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5 +BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p +dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk +MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp +b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu +dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0 +MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi +E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa +MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI +hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN +95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd +2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEc +MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBT +ZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIw +MDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0VxdWlmYXggU2Vj +dXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEdsb2JhbCBlQnVzaW5l +c3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRVPEnC +UdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc +58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/ +o5brhTMhHD4ePmBudpxnhcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAH +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1dr +aGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUA +A4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkA +Z70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv +8qIYNMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEc +MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBT +ZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQw +MDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5j +LjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwgZ8wDQYJ +KoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ1MRo +RvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBu +WqDZQu4aIZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKw +Env+j6YDAgMBAAGjZjBkMBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTAD +AQH/MB8GA1UdIwQYMBaAFEp4MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRK +eDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZM +zfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+ +WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN +/Bf+KpYrtWKmpj29f5JZzVoqgrI3eQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 +b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMw +MTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYD +VQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ul +CDtbKRY654eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6n +tGO0/7Gcrjyvd7ZWxbWroulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyl +dI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJch +PXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC ++Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0O +BBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBl +MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk +ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENB +IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0MkhHma6X +7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0PhiVYrqW9yTkkz +43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY +eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJl +pz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOA +WiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs +IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 +MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux +FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h +bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v +dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt +H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 +uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX +mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX +a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN +E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 +WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD +VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 +Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU +cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx +IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN +AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH +YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC +Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX +c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a +mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 +b3JrMSAwHgYDVQQDExdBZGRUcnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAx +MDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtB +ZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIDAeBgNV +BAMTFOFkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV +6tsfSlbunyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nX +GCwwfQ56HmIexkvA/X1id9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnP +dzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSGAa2Il+tmzV7R/9x98oTaunet3IAIx6eH +1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAwHM+A+WD+eeSI8t0A65RF +62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0GA1UdDgQW +BBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDEL +MAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRU +cnVzdCBUVFAgTmV0d29yazEgMB4GA1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJv +b3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4JNojVhaTdt02KLmuG7jD8WS6 +IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL+YPoRNWyQSW/ +iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao +GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh +4SINhwBk/ox9Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQm +XiLsks3/QppEIW1cxeMiHV9HEufOX1362KqxMy3ZdvJOOjMMK7MtkAY= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 +b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1 +MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYTAlNFMRQwEgYDVQQK +EwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIzAh +BgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwq +xBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G +87B4pfYOQnrjfxvM0PC3KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i +2O+tCBGaKZnhqkRFmhJePp1tUvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8U +WfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c1 +0cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0G +A1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6Fr +pGkwZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL +ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlm +aWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2VhlRO6aQTv +hsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxGGuoYQ992zPlm +hpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X +dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3 +P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9Y +iQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5no +xqE= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6 +MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJp +dHkgMjA0OCBWMzAeFw0wMTAyMjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAX +BgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJTQSBTZWN1cml0eSAy +MDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt49VcdKA3Xtp +eafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7Jylg +/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGl +wSMiuLgbWhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnh +AMFRD0xS+ARaqn1y07iHKrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2 +PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpu +AWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4EFgQUB8NR +MKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYc +HnmYv/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/ +Zb5gEydxiKRz44Rj0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+ +f00/FGj1EVDVwfSQpQgdMWD/YIwjVAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVO +rSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395nzIlQnQFgCi/vcEkllgVsRch +6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kApKnXwiJPZ9d3 +7CAFYd4= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg +R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 +9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq +fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv +iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU +1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ +bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW +MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA +ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l +uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn +Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS +tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF +PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un +hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV +5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBr +MQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRl +cm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv +bW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2WhcNMjIwNjI0MDAxNjEyWjBrMQsw +CQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5h +dGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1l +cmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h +2mCxlCfLF9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4E +lpF7sDPwsRROEW+1QK8bRaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdV +ZqW1LS7YgFmypw23RuwhY/81q6UCzyr0TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq +299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI/k4+oKsGGelT84ATB+0t +vz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzsGHxBvfaL +dXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUF +AAOCAQEAX/FBfXxcCLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcR +zCSs00Rsca4BIGsDoo8Ytyk6feUWYFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3 +LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGIxHYdkFsd +7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBuYQa7FkKMcPcw +++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt +398znM/jra6O1I7mT1GvFpLgXPYHDw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6xwS7TT3zNJc4YPk/E +jG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdLkKWo +ePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GI +ULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapu +Ob7kky/ZR6By6/qmW6/KUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUg +AKpoC6EahQGcxEZjgoi2IrHu/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7 +HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA +uI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQa +TOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTg +xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q +CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x +O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs +6GAqm4VKQPNriiTsBhYscw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRp +ZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVow +fjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAiBgNV +BAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPM +cm3ye5drswfxdySRXyWP9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3S +HpR7LZQdqnXXs5jLrLxkU0C8j6ysNstcrbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996 +CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rCoznl2yY4rYsK7hljxxwk +3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3Vp6ea5EQz +6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNV +HQ4EFgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud +EwEB/wQFMAMBAf8wgYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2Rv +Y2EuY29tL1NlY3VyZUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRw +Oi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmww +DQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm4J4oqF7Tt/Q0 +5qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj +Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtI +gKvcnDe4IRRLDXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJ +aD61JlfutuC23bkpgHl9j6PwpCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDl +izeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1HRR3B7Hzs/Sk= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0 +aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEwMDAwMDBaFw0yODEyMzEyMzU5NTla +MH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO +BgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUwIwYD +VQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWW +fnJSoBVC21ndZHoa0Lh73TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMt +TGo87IvDktJTdyR0nAducPy9C1t2ul/y/9c3S0pgePfw+spwtOpZqqPOSC+pw7IL +fhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6juljatEPmsbS9Is6FARW +1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsSivnkBbA7 +kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0G +A1UdDgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21v +ZG9jYS5jb20vVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRo +dHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMu +Y3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8NtwuleGFTQQuS9/ +HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 +pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxIS +jBc/lDb+XbDABHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+ +xqFx7D+gIIxmOom0jtTYsU0lR+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/Atyjcn +dBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O9y5Xt5hwXsjEeLBi +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz +MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw +IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR +dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp +li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D +rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ +WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug +F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU +xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC +Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv +dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw +ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl +IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh +c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy +ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI +KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T +KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq +y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p +dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD +VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL +MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk +fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8 +7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R +cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y +mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW +xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK +SnQ2+Q== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY +MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t +dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5 +WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD +VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8 +9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ +DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9 +Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N +QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ +xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G +A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG +kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr +Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5 +Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU +JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot +RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBJDANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP +MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MxIENBMB4XDTAx +MDQwNjEwNDkxM1oXDTIxMDQwNjEwNDkxM1owOTELMAkGA1UEBhMCRkkxDzANBgNV +BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMSBDQTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBALWJHytPZwp5/8Ue+H887dF+2rDNbS82rDTG +29lkFwhjMDMiikzujrsPDUJVyZ0upe/3p4zDq7mXy47vPxVnqIJyY1MPQYx9EJUk +oVqlBvqSV536pQHydekfvFYmUk54GWVYVQNYwBSujHxVX3BbdyMGNpfzJLWaRpXk +3w0LBUXl0fIdgrvGE+D+qnr9aTCU89JFhfzyMlsy3uhsXR/LpCJ0sICOXZT3BgBL +qdReLjVQCfOAl/QMF6452F/NM8EcyonCIvdFEu1eEpOdY6uCLrnrQkFEy0oaAIIN +nvmLVz5MxxftLItyM19yejhW1ebZrgUaHXVFsculJRwSVzb9IjcCAwEAAaMzMDEw +DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQIR+IMi/ZTiFIwCwYDVR0PBAQDAgEG +MA0GCSqGSIb3DQEBBQUAA4IBAQCLGrLJXWG04bkruVPRsoWdd44W7hE928Jj2VuX +ZfsSZ9gqXLar5V7DtxYvyOirHYr9qxp81V9jz9yw3Xe5qObSIjiHBxTZ/75Wtf0H +DjxVyhbMp6Z3N/vbXB9OWQaHowND9Rart4S9Tu+fMTfwRvFAttEMpWT4Y14h21VO +TzF2nBBhjrZTOqMRvq9tfB69ri3iDGnHhVNoomG6xT60eVR4ngrHAr5i0RGCS2Uv +kVrCqIexVmiUefkl98HVrhq4uz2PqYo4Ffdz0Fpg0YCw8NzVUM1O7pJIae2yIx4w +zMiUyLb1O4Z/P6Yun/Y+LLWSlj7fLJOK/4GMDw9ZIRlXvVWa +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP +MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx +MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV +BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o +Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt +5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s +3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej +vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu +8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw +DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG +MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil +zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/ +3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD +FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6 +Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2 +ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJO +TDEeMBwGA1UEChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEy +MTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVTdGFhdCBkZXIgTmVk +ZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxhbmRlbiBSb290IENB +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFtvszn +ExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw71 +9tV2U02PjLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MO +hXeiD+EwR+4A5zN9RGcaC1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+U +tFE5A3+y3qcym7RHjm+0Sq7lr7HcsBthvJly3uSJt3omXdozSVtSnA71iq3DuD3o +BmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn622r+I/q85Ej0ZytqERAh +SQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRVHSAAMDww +OgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMv +cm9vdC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA +7Jbg0zTBLL9s+DANBgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k +/rvuFbQvBgwp8qiSpGEN/KtcCFtREytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzm +eafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbwMVcoEoJz6TMvplW0C5GUR5z6 +u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3ynGQI0DvDKcWy +7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR +iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB +kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw +IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD +VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu +dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6 +E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ +D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK +4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq +lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW +bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB +o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT +MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js +LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr +BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB +AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft +Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj +j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH +KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv +2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3 +mfnGV/TJVTl4uix5yaaIK/QI +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEojCCA4qgAwIBAgIQRL4Mi1AAJLQR0zYlJWfJiTANBgkqhkiG9w0BAQUFADCB +rjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xNjA0BgNVBAMTLVVUTi1VU0VSRmlyc3Qt +Q2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBFbWFpbDAeFw05OTA3MDkxNzI4NTBa +Fw0xOTA3MDkxNzM2NThaMIGuMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAV +BgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5l +dHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UE +AxMtVVROLVVTRVJGaXJzdC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWls +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsjmFpPJ9q0E7YkY3rs3B +YHW8OWX5ShpHornMSMxqmNVNNRm5pELlzkniii8efNIxB8dOtINknS4p1aJkxIW9 +hVE1eaROaJB7HHqkkqgX8pgV8pPMyaQylbsMTzC9mKALi+VuG6JG+ni8om+rWV6l +L8/K2m2qL+usobNqqrcuZzWLeeEeaYji5kbNoKXqvgvOdjp6Dpvq/NonWz1zHyLm +SGHGTPNpsaguG7bUMSAsvIKKjqQOpdeJQ/wWWq8dcdcRWdq6hw2v+vPhwvCkxWeM +1tZUOt4KpLoDd7NlyP0e03RiqhjKaJMeoYV+9Udly/hNVyh00jT/MLbu9mIwFIws +6wIDAQABo4G5MIG2MAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBSJgmd9xJ0mcABLtFBIfN49rgRufTBYBgNVHR8EUTBPME2gS6BJhkdodHRw +Oi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLVVTRVJGaXJzdC1DbGllbnRBdXRoZW50 +aWNhdGlvbmFuZEVtYWlsLmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwQwDQYJKoZIhvcNAQEFBQADggEBALFtYV2mGn98q0rkMPxTbyUkxsrt4jFcKw7u +7mFVbwQ+zznexRtJlOTrIEy05p5QLnLZjfWqo7NK2lYcYJeA3IKirUq9iiv/Cwm0 +xtcgBEXkzYABurorbs6q15L+5K/r9CYdFip/bDCVNy8zEqx/3cfREYxRmLLQo5HQ +rfafnoOTHh1CuEava2bwm3/q4wMC5QJRwarVNZ1yQAOJujEdxRBoUp7fooXFXAim +eOZTT7Hot9MUnpOmw2TjrH5xzbyf6QMbzPvprDHBr3wVdAKZw7JHpsIyYdfHb0gk +USeh1YdV8nuPmD0Wnu51tvjQjvLzxq4oW6fw8zYX/MMF08oDSlQ= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB +lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt +SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG +A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe +MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v +d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh +cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn +0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ +M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a +MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd +oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI +DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy +oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0 +dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy +bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF +BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM +//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli +CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE +CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t +3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS +KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEZjCCA06gAwIBAgIQRL4Mi1AAJLQR0zYt4LNfGzANBgkqhkiG9w0BAQUFADCB +lTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHTAbBgNVBAMTFFVUTi1VU0VSRmlyc3Qt +T2JqZWN0MB4XDTk5MDcwOTE4MzEyMFoXDTE5MDcwOTE4NDAzNlowgZUxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxHjAc +BgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMYaHR0cDovL3d3 +dy51c2VydHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNFUkZpcnN0LU9iamVjdDCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6qgT+jo2F4qjEAVZURnicP +HxzfOpuCaDDASmEd8S8O+r5596Uj71VRloTN2+O5bj4x2AogZ8f02b+U60cEPgLO +KqJdhwQJ9jCdGIqXsqoc/EHSoTbL+z2RuufZcDX65OeQw5ujm9M89RKZd7G3CeBo +5hy485RjiGpq/gt2yb70IuRnuasaXnfBhQfdDWy/7gbHd2pBnqcP1/vulBe3/IW+ +pKvEHDHd17bR5PDv3xaPslKT16HUiaEHLr/hARJCHhrh2JU022R5KP+6LhHC5ehb +kkj7RwvCbNqtMoNB86XlQXD9ZZBt+vpRxPm9lisZBCzTbafc8H9vg2XiaquHhnUC +AwEAAaOBrzCBrDALBgNVHQ8EBAMCAcYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQU2u1kdBScFDyr3ZmpvVsoTYs8ydgwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDov +L2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmlyc3QtT2JqZWN0LmNybDApBgNV +HSUEIjAgBggrBgEFBQcDAwYIKwYBBQUHAwgGCisGAQQBgjcKAwQwDQYJKoZIhvcN +AQEFBQADggEBAAgfUrE3RHjb/c652pWWmKpVZIC1WkDdIaXFwfNfLEzIR1pp6ujw +NTX00CXzyKakh0q9G7FzCL3Uw8q2NbtZhncxzaeAFK4T7/yxSPlrJSUtUbYsbUXB +mMiKVl0+7kNOPmsnjtA6S4ULX9Ptaqd1y9Fahy85dRNacrACgZ++8A+EVCBibGnU +4U3GDZlDAQ0Slox4nb9QorFEqmrPF3rPbw/U+CRVX/A0FklmPlBGyWNxODFiuGK5 +81OtbLUrohKqGU8J2l7nk8aOFAj+8DCAGKCGhU3IfdeLA/5u1fedFqySLKAj5ZyR +Uh+U3xeUc8OzwcFxBSAAeL0TUh2oPs0AH8g= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEn +MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL +ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMg +b2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAxNjEzNDNaFw0zNzA5MzAxNjEzNDRa +MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBB +ODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIw +IAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0B +AQEFAAOCAQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtb +unXF/KGIJPov7coISjlUxFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0d +BmpAPrMMhe5cG3nCYsS4No41XQEMIwRHNaqbYE6gZj3LJgqcQKH0XZi/caulAGgq +7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jWDA+wWFjbw2Y3npuRVDM3 +0pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFVd9oKDMyX +roDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIG +A1UdEwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5j +aGFtYmVyc2lnbi5vcmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p +26EpW1eLTXYGduHRooowDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIA +BzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hhbWJlcnNpZ24ub3JnMCcGA1Ud +EgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYDVR0gBFEwTzBN +BgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz +aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEB +AAxBl8IahsAifJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZd +p0AJPaxJRUXcLo0waLIJuvvDL8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi +1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wNUPf6s+xCX6ndbcj0dc97wXImsQEc +XCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/nADydb47kMgkdTXg0 +eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1erfu +tGWaIZDgqtCYvDi1czyL+Nw= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEn +MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL +ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENo +YW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYxNDE4WhcNMzcwOTMwMTYxNDE4WjB9 +MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgy +NzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4G +A1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUA +A4IBDQAwggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0 +Mi+ITaFgCPS3CU6gSS9J1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/s +QJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8Oby4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpV +eAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl6DJWk0aJqCWKZQbua795 +B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c8lCrEqWh +z0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0T +AQH/BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1i +ZXJzaWduLm9yZy9jaGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4w +TcbOX60Qq+UDpfqpFDAOBgNVHQ8BAf8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAH +MCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBjaGFtYmVyc2lnbi5vcmcwKgYD +VR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9yZzBbBgNVHSAE +VDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh +bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0B +AQUFAAOCAQEAPDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUM +bKGKfKX0j//U2K0X1S0E0T9YgOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXi +ryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJPJ7oKXqJ1/6v/2j1pReQvayZzKWG +VwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4IBHNfTIzSJRUTN3c +ecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREest2d/ +AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIG0TCCBbmgAwIBAgIBezANBgkqhkiG9w0BAQUFADCByTELMAkGA1UEBhMCSFUx +ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 +b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMUIwQAYDVQQD +EzlOZXRMb2NrIE1pbm9zaXRldHQgS296amVneXpvaSAoQ2xhc3MgUUEpIFRhbnVz +aXR2YW55a2lhZG8xHjAcBgkqhkiG9w0BCQEWD2luZm9AbmV0bG9jay5odTAeFw0w +MzAzMzAwMTQ3MTFaFw0yMjEyMTUwMTQ3MTFaMIHJMQswCQYDVQQGEwJIVTERMA8G +A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh +Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxQjBABgNVBAMTOU5l +dExvY2sgTWlub3NpdGV0dCBLb3pqZWd5em9pIChDbGFzcyBRQSkgVGFudXNpdHZh +bnlraWFkbzEeMBwGCSqGSIb3DQEJARYPaW5mb0BuZXRsb2NrLmh1MIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx1Ilstg91IRVCacbvWy5FPSKAtt2/Goq +eKvld/Bu4IwjZ9ulZJm53QE+b+8tmjwi8F3JV6BVQX/yQ15YglMxZc4e8ia6AFQe +r7C8HORSjKAyr7c3sVNnaHRnUPYtLmTeriZ539+Zhqurf4XsoPuAzPS4DB6TRWO5 +3Lhbm+1bOdRfYrCnjnxmOCyqsQhjF2d9zL2z8cM/z1A57dEZgxXbhxInlrfa6uWd +vLrqOU+L73Sa58XQ0uqGURzk/mQIKAR5BevKxXEOC++r6uwSEaEYBTJp0QwsGj0l +mT+1fMptsK6ZmfoIYOcZwvK9UdPM0wKswREMgM6r3JSda6M5UzrWhQIDAMV9o4IC +wDCCArwwEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8EBAMCAQYwggJ1Bglg +hkgBhvhCAQ0EggJmFoICYkZJR1lFTEVNISBFemVuIHRhbnVzaXR2YW55IGEgTmV0 +TG9jayBLZnQuIE1pbm9zaXRldHQgU3pvbGdhbHRhdGFzaSBTemFiYWx5emF0YWJh +biBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBBIG1pbm9zaXRldHQg +ZWxla3Ryb25pa3VzIGFsYWlyYXMgam9naGF0YXMgZXJ2ZW55ZXN1bGVzZW5laywg +dmFsYW1pbnQgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYSBNaW5vc2l0ZXR0IFN6 +b2xnYWx0YXRhc2kgU3phYmFseXphdGJhbiwgYXogQWx0YWxhbm9zIFN6ZXJ6b2Rl +c2kgRmVsdGV0ZWxla2JlbiBlbG9pcnQgZWxsZW5vcnplc2kgZWxqYXJhcyBtZWd0 +ZXRlbGUuIEEgZG9rdW1lbnR1bW9rIG1lZ3RhbGFsaGF0b2sgYSBodHRwczovL3d3 +dy5uZXRsb2NrLmh1L2RvY3MvIGNpbWVuIHZhZ3kga2VyaGV0b2sgYXogaW5mb0Bu +ZXRsb2NrLm5ldCBlLW1haWwgY2ltZW4uIFdBUk5JTkchIFRoZSBpc3N1YW5jZSBh +bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGFyZSBzdWJqZWN0IHRvIHRo +ZSBOZXRMb2NrIFF1YWxpZmllZCBDUFMgYXZhaWxhYmxlIGF0IGh0dHBzOi8vd3d3 +Lm5ldGxvY2suaHUvZG9jcy8gb3IgYnkgZS1tYWlsIGF0IGluZm9AbmV0bG9jay5u +ZXQwHQYDVR0OBBYEFAlqYhaSsFq7VQ7LdTI6MuWyIckoMA0GCSqGSIb3DQEBBQUA +A4IBAQCRalCc23iBmz+LQuM7/KbD7kPgz/PigDVJRXYC4uMvBcXxKufAQTPGtpvQ +MznNwNuhrWw3AkxYQTvyl5LGSKjN5Yo5iWH5Upfpvfb5lHTocQ68d4bDBsxafEp+ +NFAwLvt/MpqNPfMgW/hqyobzMUwsWYACff44yTB1HLdV47yfuqhthCgFdbOLDcCR +VCHnpgu0mfVRQdzNo0ci2ccBgcTcR08m6h/t280NmPSjnLRzMkqWmf68f8glWPhY +83ZmiVSkpj7EUFy6iRiCdUgh0k8T6GB+B3bbELVR5qq5aKrN9p2QdRLqOBrKROi3 +macqaJVmlaut74nLYKkGEsaUR+ko +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhV +MRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMe +TmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0 +dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFzcyBB +KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oXDTE5MDIxOTIzMTQ0 +N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhC +dWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQu +MRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBL +b3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSMD7tM9DceqQWC2ObhbHDqeLVu0ThEDaiD +zl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZz+qMkjvN9wfcZnSX9EUi +3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC/tmwqcm8 +WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LY +Oph7tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2Esi +NCubMvJIH5+hCoR64sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCC +ApswDgYDVR0PAQH/BAQDAgAGMBIGA1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4 +QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZRUxFTSEgRXplbiB0 +YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRhdGFz +aSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu +IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtm +ZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMg +ZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVs +amFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJhc2EgbWVndGFsYWxoYXRv +IGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBzOi8vd3d3 +Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6 +ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1 +YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3Qg +dG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRs +b2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNAbmV0bG9jay5uZXQuMA0G +CSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5ayZrU3/b39/zcT0mwBQO +xmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjPytoUMaFP +0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQ +QeJBCWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxk +f1qbFFgBJ34TUMdrKuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK +8CtmdWOMovsEPoMOmzbwGOQmIMOM8CgHrTwXZoi1/baI +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUx +ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 +b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQD +EylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikgVGFudXNpdHZhbnlraWFkbzAeFw05 +OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYDVQQGEwJIVTERMA8G +A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh +Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5l +dExvY2sgVXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqG +SIb3DQEBAQUAA4GNADCBiQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xK +gZjupNTKihe5In+DCnVMm8Bp2GQ5o+2So/1bXHQawEfKOml2mrriRBf8TKPV/riX +iK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr1nGTLbO/CVRY7QbrqHvc +Q7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8E +BAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1G +SUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFu +b3MgU3pvbGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBh +bGFwamFuIGtlc3p1bHQuIEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExv +Y2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGln +aXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0 +IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh +c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGph +biBhIGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJo +ZXRvIGF6IGVsbGVub3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBP +UlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmlj +YXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBo +dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNA +bmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06 +sPgzTEdM43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXa +n3BukxowOR0w2y7jfLKRstE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKS +NitjrFgBazMpUIaD8QFI +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUx +ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 +b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQD +EytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBDKSBUYW51c2l0dmFueWtpYWRvMB4X +DTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJBgNVBAYTAkhVMREw +DwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9u +c2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMr +TmV0TG9jayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzAN +BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNA +OoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3ZW3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC +2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63euyucYT2BDMIJTLrdKwW +RMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQwDgYDVR0P +AQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEW +ggJNRklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0 +YWxhbm9zIFN6b2xnYWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFz +b2sgYWxhcGphbiBrZXN6dWx0LiBBIGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBO +ZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1iaXp0b3NpdGFzYSB2ZWRpLiBB +IGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0ZWxlIGF6IGVs +b2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs +ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25s +YXBqYW4gYSBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kg +a2VyaGV0byBheiBlbGxlbm9yemVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4g +SU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5kIHRoZSB1c2Ugb2YgdGhpcyBjZXJ0 +aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQUyBhdmFpbGFibGUg +YXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwgYXQg +Y3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmY +ta3UzbM2xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2g +pO0u9f38vf5NNwgMvOOWgyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4 +Fp1hBWeAyNDYpQcCNJgEjTME1A== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB +gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk +MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY +UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx +NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3 +dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy +dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6 +38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP +KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q +DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4 +qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa +JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi +PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P +BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs +jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0 +eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR +vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa +IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy +i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ +O+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh +MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE +YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 +MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo +ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg +MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN +ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA +PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w +wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi +EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY +avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ +YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE +sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h +/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 +IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy +OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P +TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER +dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf +ReYNnyicsbkqWletNw+vHX/bvZ8= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl +MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp +U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw +NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp +ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 +DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf +8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN ++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 +X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa +K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA +1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G +A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR +zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 +YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD +bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 +L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D +eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp +VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY +WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg +Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9 +MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi +U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh +cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk +pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf +OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C +Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT +Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi +HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM +Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w ++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+ +Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3 +Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B +26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID +AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE +FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j +ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js +LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM +BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0 +Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy +dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh +cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh +YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg +dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp +bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ +YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT +TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ +9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8 +jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW +FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz +ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1 +ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L +EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu +L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq +yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC +O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V +um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh +NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1 +nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex +t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz +SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG +BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+ +rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/ +NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E +BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH +BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv +MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE +p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y +5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK +WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ +4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N +hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c +JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP +mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ +wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 +VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ +AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB +AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun +pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC +dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf +fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm +NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx +H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE +AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw +CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ +BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND +VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb +qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY +HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo +G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA +lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr +IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ +0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH +k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 +4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO +m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa +cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl +uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI +KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls +ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG +AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT +VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG +CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA +cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA +QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA +7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA +cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA +QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA +czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu +aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt +aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud +DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF +BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp +D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU +JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m +AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD +vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms +tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH +7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA +h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF +d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H +pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UE +AwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00x +CzAJBgNVBAYTAkVTMB4XDTA4MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEW +MBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZF +RElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC +AgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHkWLn7 +09gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7 +XBZXehuDYAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5P +Grjm6gSSrj0RuVFCPYewMYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAK +t0SdE3QrwqXrIhWYENiLxQSfHY9g5QYbm8+5eaA9oiM/Qj9r+hwDezCNzmzAv+Yb +X79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbkHQl/Sog4P75n/TSW9R28 +MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTTxKJxqvQU +fecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI +2Sf23EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyH +K9caUPgn6C9D4zq92Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEae +ZAwUswdbxcJzbPEHXEUkFDWug/FqTYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAP +BgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz4SsrSbbXc6GqlPUB53NlTKxQ +MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU9QHnc2VMrFAw +RAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv +bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWIm +fQwng4/F9tqgaHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3 +gvoFNTPhNahXwOf9jU8/kzJPeGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKe +I6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1PwkzQSulgUV1qzOMPPKC8W64iLgpq0i +5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1ThCojz2GuHURwCRi +ipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oIKiMn +MCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZ +o5NjEFIqnxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6 +zqylfDJKZ0DcMDQj3dcEI2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacN +GHk0vFQYXlPKNFHtRQrmjseCNj6nOGOpMCwXEGCSn1WHElkQwg9naRHMTh5+Spqt +r0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3otkYNbn5XOmeUwssfnHdK +Z05phkOTOPu220+DkdRgfks+KzgHVZhepA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIGZjCCBE6gAwIBAgIPB35Sk3vgFeNX8GmMy+wMMA0GCSqGSIb3DQEBBQUAMHsx +CzAJBgNVBAYTAkNPMUcwRQYDVQQKDD5Tb2NpZWRhZCBDYW1lcmFsIGRlIENlcnRp +ZmljYWNpw7NuIERpZ2l0YWwgLSBDZXJ0aWPDoW1hcmEgUy5BLjEjMCEGA1UEAwwa +QUMgUmHDrXogQ2VydGljw6FtYXJhIFMuQS4wHhcNMDYxMTI3MjA0NjI5WhcNMzAw +NDAyMjE0MjAyWjB7MQswCQYDVQQGEwJDTzFHMEUGA1UECgw+U29jaWVkYWQgQ2Ft +ZXJhbCBkZSBDZXJ0aWZpY2FjacOzbiBEaWdpdGFsIC0gQ2VydGljw6FtYXJhIFMu +QS4xIzAhBgNVBAMMGkFDIFJhw616IENlcnRpY8OhbWFyYSBTLkEuMIICIjANBgkq +hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq2uJo1PMSCMI+8PPUZYILrgIem08kBeG +qentLhM0R7LQcNzJPNCNyu5LF6vQhbCnIwTLqKL85XXbQMpiiY9QngE9JlsYhBzL +fDe3fezTf3MZsGqy2IiKLUV0qPezuMDU2s0iiXRNWhU5cxh0T7XrmafBHoi0wpOQ +Y5fzp6cSsgkiBzPZkc0OnB8OIMfuuzONj8LSWKdf/WU34ojC2I+GdV75LaeHM/J4 +Ny+LvB2GNzmxlPLYvEqcgxhaBvzz1NS6jBUJJfD5to0EfhcSM2tXSExP2yYe68yQ +54v5aHxwD6Mq0Do43zeX4lvegGHTgNiRg0JaTASJaBE8rF9ogEHMYELODVoqDA+b +MMCm8Ibbq0nXl21Ii/kDwFJnmxL3wvIumGVC2daa49AZMQyth9VXAnow6IYm+48j +ilSH5L887uvDdUhfHjlvgWJsxS3EF1QZtzeNnDeRyPYL1epjb4OsOMLzP96a++Ej +YfDIJss2yKHzMI+ko6Kh3VOz3vCaMh+DkXkwwakfU5tTohVTP92dsxA7SH2JD/zt +A/X7JWR1DhcZDY8AFmd5ekD8LVkH2ZD6mq093ICK5lw1omdMEWux+IBkAC1vImHF +rEsm5VoQgpukg3s0956JkSCXjrdCx2bD0Omk1vUgjcTDlaxECp1bczwmPS9KvqfJ +pxAe+59QafMCAwEAAaOB5jCB4zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjAdBgNVHQ4EFgQU0QnQ6dfOeXRU+Tows/RtLAMDG2gwgaAGA1UdIASBmDCB +lTCBkgYEVR0gADCBiTArBggrBgEFBQcCARYfaHR0cDovL3d3dy5jZXJ0aWNhbWFy +YS5jb20vZHBjLzBaBggrBgEFBQcCAjBOGkxMaW1pdGFjaW9uZXMgZGUgZ2FyYW50 +7WFzIGRlIGVzdGUgY2VydGlmaWNhZG8gc2UgcHVlZGVuIGVuY29udHJhciBlbiBs +YSBEUEMuMA0GCSqGSIb3DQEBBQUAA4ICAQBclLW4RZFNjmEfAygPU3zmpFmps4p6 +xbD/CHwso3EcIRNnoZUSQDWDg4902zNc8El2CoFS3UnUmjIz75uny3XlesuXEpBc +unvFm9+7OSPI/5jOCk0iAUgHforA1SBClETvv3eiiWdIG0ADBaGJ7M9i4z0ldma/ +Jre7Ir5v/zlXdLp6yQGVwZVR6Kss+LGGIOk/yzVb0hfpKv6DExdA7ohiZVvVO2Dp +ezy4ydV/NgIlqmjCMRW3MGXrfx1IebHPOeJCgBbT9ZMj/EyXyVo3bHwi2ErN0o42 +gzmRkBDI8ck1fj+404HGIGQatlDCIaR43NAvO2STdPCWkPHv+wlaNECW8DYSwaN0 +jJN+Qd53i+yG2dIPPy3RzECiiWZIHiCznCNZc6lEc7wkeZBWN7PGKX6jD/EpOe9+ +XCgycDWs2rjIdWb8m0w5R44bb5tNAlQiM+9hup4phO9OSzNHdpdqy35f/RWmnkJD +W2ZaiogN9xa5P1FlK2Zqi9E4UqLWRhH6/JocdJ6PlwsCT2TG9WjTSy3/pDceiz+/ +RL5hRqGEPQgnTIEgd4kI6mdAXmwIUV80WoyWaM3X94nCHNMyAK9Sy9NgWyo6R35r +MDOhYil/SrnhLecUIw4OGEfhefwVVdCx/CVxY3UzHCMrr1zZ7Ud3YA47Dx7SwNxk +BYn8eNZcLCZDqQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE +BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w +MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC +SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 +ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv +UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX +4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 +KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ +gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb +rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ +51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F +be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe +KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F +v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn +fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 +jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz +ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL +e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 +jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz +WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V +SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j +pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX +X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok +fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R +K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU +ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU +LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT +LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP +Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr +ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL +MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 +yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr +VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ +nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG +XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj +vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt +Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g +N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC +nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y +YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua +kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL +QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp +6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG +yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i +QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO +tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu +QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ +Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u +olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 +x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz +dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG +A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U +cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf +qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ +JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ ++jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS +s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 +HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 +70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG +V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S +qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S +5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia +C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX +OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE +FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 +KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B +8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ +MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc +0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ +u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF +u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH +YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 +GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO +RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e +KeC2uAloGRwYQw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC +VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ +cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ +BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt +VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D +0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 +ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G +A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs +aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I +flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEc +MBoGA1UEChMTSmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRp +b25DQTAeFw0wNzEyMTIxNTAwMDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYT +AkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zlcm5tZW50MRYwFAYDVQQLEw1BcHBs +aWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp23gdE6H +j6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4fl+K +f5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55 +IrmTwcrNwVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cw +FO5cjFW6WY2H/CPek9AEjP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDiht +QWEjdnjDuGWk81quzMKq2edY3rZ+nYVunyoKb58DKTCXKB28t89UKU5RMfkntigm +/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRUWssmP3HMlEYNllPqa0jQ +k/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNVBAYTAkpQ +MRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOC +seODvOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADlqRHZ3ODrso2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJ +hyzjVOGjprIIC8CFqMjSnHH2HZ9g/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+ +eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYDio+nEhEMy/0/ecGc/WLuo89U +DNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmWdupwX3kSa+Sj +B1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL +rosot4LKGAfmt1t06SAZf7IbiVQ= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE +AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG +EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM +FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC +REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp +Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM +VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ +SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ +4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L +cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi +eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG +A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 +DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j +vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP +DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc +maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D +lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv +KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJB +VDFIMEYGA1UECgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBp +bSBlbGVrdHIuIERhdGVudmVya2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5R +dWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5RdWFsLTAzMB4XDTA1MDgxNzIyMDAw +MFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgwRgYDVQQKDD9BLVRy +dXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0ZW52 +ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMM +EEEtVHJ1c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCtPWFuA/OQO8BBC4SAzewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUj +lUC5B3ilJfYKvUWG6Nm9wASOhURh73+nyfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZ +znF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPESU7l0+m0iKsMrmKS1GWH +2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4iHQF63n1 +k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs +2e3Vcuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYD +VR0OBAoECERqlWdVeRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC +AQEAVdRU0VlIXLOThaq/Yy/kgM40ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fG +KOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmrsQd7TZjTXLDR8KdCoLXEjq/+ +8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZdJXDRZslo+S4R +FGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS +mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmE +DNuxUCAKGkq6ahq97BvIxYSazQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy +MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD +VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv +ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl +AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF +661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9 +am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1 +ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481 +PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS +3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k +SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF +3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM +ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g +StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz +Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB +jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3Mg +Q2xhc3MgMiBDQSAxMB4XDTA2MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzEL +MAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MR0wGwYD +VQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7McXA0 +ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLX +l18xoS830r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVB +HfCuuCkslFJgNJQ72uA40Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B +5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/RuFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3 +WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0PAQH/BAQD +AgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLP +gcIV1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+ +DKhQ7SLHrQVMdvvt7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKu +BctN518fV4bVIJwo+28TOPX2EZL2fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHs +h7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5wwDX3OaJdZtB7WZ+oRxKaJyOk +LY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr +6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV +L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 +1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx +MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ +QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB +arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr +Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi +FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS +P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN +9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz +uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h +9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t +OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo ++fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 +KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 +DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us +H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ +I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 +5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h +3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz +Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3Mg +Q2xhc3MgMyBDQSAxMB4XDTA1MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzEL +MAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MR0wGwYD +VQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKxifZg +isRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//z +NIqeKNc0n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI ++MkcVyzwPX6UvCWThOiaAJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2R +hzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+ +mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0PAQH/BAQD +AgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFP +Bdy7pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27s +EzNxZy5p+qksP2bAEllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2 +mSlf56oBzKwzqBwKu5HEA6BvtjT5htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yC +e/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQjel/wroQk5PMr+4okoyeYZdow +dXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y +ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E +N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 +tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX +0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c +/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X +KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY +zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS +O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D +34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP +K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv +Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj +QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS +IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 +HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa +O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv +033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u +dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE +kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 +3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD +u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq +4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzET +MBEGA1UEBxMKQnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UE +AxMIQ0EgRGlzaWcwHhcNMDYwMzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQsw +CQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcg +YS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgmGErE +Nx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnX +mjxUizkDPw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYD +XcDtab86wYqg6I7ZuUUohwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhW +S8+2rT+MitcE5eN4TPWGqvWP+j1scaMtymfraHtuM6kMgiioTGohQBUgDCZbg8Kp +FhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8wgfwwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0PAQH/BAQD +AgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cu +ZGlzaWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5z +ay9jYS9jcmwvY2FfZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2sv +Y2EvY3JsL2NhX2Rpc2lnLmNybDAaBgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEw +DQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59tWDYcPQuBDRIrRhCA/ec8J9B6 +yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3mkkp7M5+cTxq +EEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/ +CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeB +EicTXxChds6KezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFN +PGO+I++MzVpQuGhU+QqZMxEA4Z7CRneC9VkGjCFMhwnN5ag= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQy +MDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjEw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy3QRk +D2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/o +OI7bm+V8u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3A +fQ+lekLZWnDZv6fXARz2m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJe +IgpFy4QxTaz+29FHuvlglzmxZcfe+5nkCiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8n +oc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTaYVKvJrT1cU/J19IG32PK +/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6vpmumwKj +rckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD +3AjLLhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE +7cderVC6xkGbrPAXZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkC +yC2fg69naQanMVXVz0tv/wQFx1isXxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLd +qvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ04IwDQYJKoZI +hvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR +xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaA +SfX8MPWbTx9BLxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXo +HqJPYNcHKfyyo6SdbhWSVhlMCrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpB +emOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5GfbVSUZP/3oNn6z4eGBrxEWi1CXYBmC +AMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85YmLLW1AL14FABZyb +7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKSds+x +DzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvk +F7mGnjixlAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqF +a3qdnom2piiZk4hA9z7NUaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsT +Q6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJa7+h89n07eLw4+1knj0vllJPgFOL +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy +MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe +NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH +PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I +x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe +QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR +yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO +QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 +H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ +QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD +i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs +nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 +rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI +hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf +GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb +lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka ++elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal +TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i +nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 +gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr +G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os +zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x +L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV +BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X +DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ +BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4 +QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny +gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw +zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q +130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2 +JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw +ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT +AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj +AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG +9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h +bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc +fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu +HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w +t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjET +MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAk +BgNVBAMMHUNlcnRpbm9taXMgLSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4 +Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNl +cnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYwJAYDVQQDDB1DZXJ0 +aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jY +F1AMnmHawE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N +8y4oH3DfVS9O7cdxbwlyLu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWe +rP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K +/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92NjMD2AR5vpTESOH2VwnHu +7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9qc1pkIuVC +28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6 +lSTClrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1E +nn1So2+WLhl+HPNbxxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB +0iSVL1N6aaLwD4ZFjliCK0wi1F6g530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql09 +5gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna4NH4+ej9Uji29YnfAgMBAAGj +WzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQN +jLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ +KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9s +ov3/4gbIOZ/xWqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZM +OH8oMDX/nyNTt7buFHAAQCvaR6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q +619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40nJ+U8/aGH88bc62UeYdocMMzpXDn +2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1BCxMjidPJC+iKunqj +o3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjvJL1v +nxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG +5ERQL1TEqkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWq +pdEdnV1j6CTmNhTih60bWfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZb +dsLLO7XSAPCjDuGtbkD326C00EauFddEwk01+dIL8hf2rGbVJLJP0RyZwG71fet0 +BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/vgt2Fl43N+bYdJeimUV5 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw +PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz +cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9 +MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz +IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ +ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR +VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL +kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd +EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas +H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0 +HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud +DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4 +QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu +Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/ +AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8 +yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR +FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA +ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB +kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 +l7+ijrRU +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT +AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD +QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP +MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do +0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ +UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d +RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ +OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv +JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C +AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O +BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ +LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY +MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ +44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I +Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw +i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN +9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM +MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D +ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU +cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 +WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg +Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw +IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH +UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM +TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU +BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM +kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x +AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y +sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL +I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 +J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY +VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx +MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP +T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 +sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL +TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 +/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp +7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz +EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt +hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP +a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot +aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg +TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV +PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv +cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL +tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd +BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT +ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL +jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS +ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy +P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 +xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d +Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN +5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe +/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z +AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ +5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD +VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 +IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 +MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJz +IG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEyMjk1MFoXDTM4MDcz +MTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBj +dXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIw +EAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEp +MCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW9 +28sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKAXuFixrYp4YFs8r/lfTJq +VKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorjh40G072Q +DuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR +5gN/ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfL +ZEFHcpOrUMPrCXZkNNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05a +Sd+pZgvMPMZ4fKecHePOjlO+Bd5gD2vlGts/4+EhySnB8esHnFIbAURRPHsl18Tl +UlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331lubKgdaX8ZSD6e2wsWsSaR6s ++12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ0wlf2eOKNcx5 +Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAx +hduub+84Mxh2EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNV +HQ4EFgQU+SSsD7K1+HnA+mCIG8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1 ++HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpN +YWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29t +L2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVy +ZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAt +IDIwMDiCCQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRV +HSAAMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20w +DQYJKoZIhvcNAQEFBQADggIBAJASryI1wqM58C7e6bXpeHxIvj99RZJe6dqxGfwW +PJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH3qLPaYRgM+gQDROpI9CF +5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbURWpGqOt1 +glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaH +FoI6M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2 +pSB7+R5KBWIBpih1YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MD +xvbxrN8y8NmBGuScvfaAFPDRLLmF9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QG +tjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcKzBIKinmwPQN/aUv0NCB9szTq +jktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvGnrDQWzilm1De +fhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZ +d0jQ +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMC +Q04xMjAwBgNVBAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24g +Q2VudGVyMUcwRQYDVQQDDD5DaGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0 +aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMgUm9vdDAeFw0xMDA4MzEwNzExMjVa +Fw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAGA1UECgwpQ2hpbmEg +SW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMMPkNo +aW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRp +ZmljYXRlcyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z +7r07eKpkQ0H1UN+U8i6yjUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA// +DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV98YPjUesWgbdYavi7NifFy2cyjw1l1Vx +zUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2HklY0bBoQCxfVWhyXWIQ8 +hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23KzhmBsUs +4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54u +gQEC7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oY +NJKiyoOCWTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E +FgQUfHJLOcfA22KlT5uqGDSSosqDglkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3 +j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd50XPFtQO3WKwMVC/GVhMPMdoG +52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM7+czV0I664zB +echNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws +ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrI +zo9uoV1/A3U05K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATy +wy39FCqQmbkHzJ8= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJD +TjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2 +MDcwOTE0WhcNMjcwNDE2MDcwOTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMF +Q05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzDo+/hn7E7SIX1mlwh +IhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tizVHa6 +dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZO +V/kbZKKTVrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrC +GHn2emU1z5DrvTOTn1OrczvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gN +v7Sg2Ca+I19zN38m5pIEo3/PIKe38zrKy5nLAgMBAAGjczBxMBEGCWCGSAGG+EIB +AQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscCwQ7vptU7ETAPBgNVHRMB +Af8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991SlgrHAsEO +76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnK +OOK5Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvH +ugDnuL8BV8F3RTIMO/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7Hgvi +yJA/qIYM/PmLXoXLT1tLYhFHxUV8BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fL +buXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2G8kS1sHNzYDzAgE8yGnLRUhj +2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5mmxE= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB +gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV +BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw +MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl +YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P +RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 +UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI +2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 +Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp ++2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ +DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O +nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW +/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g +PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u +QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY +SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv +IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 +zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd +BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB +ZQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT +IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw +MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy +ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N +T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR +FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J +cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW +BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm +fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv +GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDkzCCAnugAwIBAgIQFBOWgxRVjOp7Y+X8NId3RDANBgkqhkiG9w0BAQUFADA0 +MRMwEQYDVQQDEwpDb21TaWduIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQG +EwJJTDAeFw0wNDAzMjQxMTMyMThaFw0yOTAzMTkxNTAyMThaMDQxEzARBgNVBAMT +CkNvbVNpZ24gQ0ExEDAOBgNVBAoTB0NvbVNpZ24xCzAJBgNVBAYTAklMMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8ORUaSvTx49qROR+WCf4C9DklBKK +8Rs4OC8fMZwG1Cyn3gsqrhqg455qv588x26i+YtkbDqthVVRVKU4VbirgwTyP2Q2 +98CNQ0NqZtH3FyrV7zb6MBBC11PN+fozc0yz6YQgitZBJzXkOPqUm7h65HkfM/sb +2CEJKHxNGGleZIp6GZPKfuzzcuc3B1hZKKxC+cX/zT/npfo4sdAMx9lSGlPWgcxC +ejVb7Us6eva1jsz/D3zkYDaHL63woSV9/9JLEYhwVKZBqGdTUkJe5DSe5L6j7Kpi +Xd3DTKaCQeQzC6zJMw9kglcq/QytNuEMrkvF7zuZ2SOzW120V+x0cAwqTwIDAQAB +o4GgMIGdMAwGA1UdEwQFMAMBAf8wPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDovL2Zl +ZGlyLmNvbXNpZ24uY28uaWwvY3JsL0NvbVNpZ25DQS5jcmwwDgYDVR0PAQH/BAQD +AgGGMB8GA1UdIwQYMBaAFEsBmz5WGmU2dst7l6qSBe4y5ygxMB0GA1UdDgQWBBRL +AZs+VhplNnbLe5eqkgXuMucoMTANBgkqhkiG9w0BAQUFAAOCAQEA0Nmlfv4pYEWd +foPPbrxHbvUanlR2QnG0PFg/LUAlQvaBnPGJEMgOqnhPOAlXsDzACPw1jvFIUY0M +cXS6hMTXcpuEfDhOZAYnKuGntewImbQKDdSFc8gS4TXt8QUxHXOZDOuWyt3T5oWq +8Ir7dcHyCTxlZWTzTNity4hp8+SDtwy9F1qWF8pb/627HOkthIDYIb6FUtnUdLlp +hbpN7Sgy6/lhSuTENh4Z3G+EER+V9YMoGKgzkkMn3V0TBEVPh9VGzT2ouvDzuFYk +Res3x+F2T3I5GN9+dHLHcy056mDmrRGiVod7w2ia/viMcKjfZTL0pECMocJEAw6U +AGegcQCCSA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAw +PDEbMBkGA1UEAxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWdu +MQswCQYDVQQGEwJJTDAeFw0wNDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwx +GzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBDQTEQMA4GA1UEChMHQ29tU2lnbjEL +MAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGtWhf +HZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs49oh +gHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sW +v+bznkqH7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ue +Mv5WJDmyVIRD9YTC2LxBkMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr +9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d19guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt +6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUwAwEB/zBEBgNVHR8EPTA7 +MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29tU2lnblNl +Y3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58 +ADsAj8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkq +hkiG9w0BAQUFAAOCAQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7p +iL1DRYHjZiM/EoZNGeQFsOY3wo3aBijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtC +dsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtpFhpFfTMDZflScZAmlaxMDPWL +kz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP51qJThRv4zdL +hfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz +OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYG +A1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2Jh +bCBSb290MB4XDTA2MTIxNTA4MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UE +ChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBS +b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+Mi8vRRQZhP/8NN5 +7CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW0ozS +J8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2y +HLtgwEZLAfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iP +t3sMpTjr3kfb1V05/Iin89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNz +FtApD0mpSPCzqrdsxacwOUBdrsTiXSZT8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAY +XSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/ +MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2MDSgMqAw +hi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3Js +MB8GA1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUA +A4IBAQBW7wojoFROlZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMj +Wqd8BfP9IjsO0QbE2zZMcwSO5bAi5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUx +XOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2hO0j9n0Hq0V+09+zv+mKts2o +omcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+TX3EJIrduPuoc +A06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc +MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj +IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB +IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE +RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl +U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290 +IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU +ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC +QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr +rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S +NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc +QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH +txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP +BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC +AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp +tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa +IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl +6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+ +xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU +Cm26OWMohpLzGITY+9HPBVZkVw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBb +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3Qx +ETAPBgNVBAsTCERTVCBBQ0VTMRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0w +MzExMjAyMTE5NThaFw0xNzExMjAyMTE5NThaMFsxCzAJBgNVBAYTAlVTMSAwHgYD +VQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UECxMIRFNUIEFDRVMx +FzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPu +ktKe1jzIDZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7 +gLFViYsx+tC3dr5BPTCapCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZH +fAjIgrrep4c9oW24MFbCswKBXy314powGCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4a +ahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPyMjwmR/onJALJfh1biEIT +ajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rk +c3QuY29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjto +dHRwOi8vd3d3LnRydXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMt +aW5kZXguaHRtbDAdBgNVHQ4EFgQUCXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZI +hvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V25FYrnJmQ6AgwbN99Pe7lv7Uk +QIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6tFr8hlxCBPeP/ +h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq +nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpR +rscL9yuwNwXsvFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf2 +9w4LTJxoeHtxMcfrHuBnQfO3oKfN5XozNmr6mis= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow +PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD +Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O +rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq +OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b +xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw +7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD +aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG +SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69 +ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr +AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz +R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5 +JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo +Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha +ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM +HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 +UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 +tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R +ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM +lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp +/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G +A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy +MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl +cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js +L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL +BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni +acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K +zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 +PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y +Johw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw +NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV +BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn +ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 +3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z +qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR +p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 +HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw +ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea +HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw +Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh +c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E +RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt +dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku +Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp +3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF +CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na +xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX +KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNV +BAMML0VCRyBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx +c8SxMTcwNQYDVQQKDC5FQkcgQmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXpt +ZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAeFw0wNjA4MTcwMDIxMDlaFw0xNjA4 +MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25payBTZXJ0aWZpa2Eg +SGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2ltIFRl +a25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h +4fuXd7hxlugTlkaDT7byX3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAk +tiHq6yOU/im/+4mRDGSaBUorzAzu8T2bgmmkTPiab+ci2hC6X5L8GCcKqKpE+i4s +tPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfreYteIAbTdgtsApWjluTL +dlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZTqNGFav4 +c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8Um +TDGyY5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z ++kI2sSXFCjEmN1ZnuqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0O +Lna9XvNRiYuoP1Vzv9s6xiQFlpJIqkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMW +OeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vmExH8nYQKE3vwO9D8owrXieqW +fo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0Nokb+Clsi7n2 +l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgw +FoAU587GT/wWZ5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+ +8ygjdsZs93/mQJ7ANtyVDR2tFcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI +6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgmzJNSroIBk5DKd8pNSe/iWtkqvTDO +TLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64kXPBfrAowzIpAoHME +wfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqTbCmY +Iai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJn +xk1Gj7sURT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4Q +DgZxGhBM/nV+/x5XOULK1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9q +Kd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11t +hie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQY9iJSrSq3RZj9W6+YKH4 +7ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9AahH3eU7 +QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB +8zELMAkGA1UEBhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2Vy +dGlmaWNhY2lvIChOSUYgUS0wODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1 +YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYDVQQLEyxWZWdldSBodHRwczovL3d3 +dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UECxMsSmVyYXJxdWlh +IEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMTBkVD +LUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQG +EwJFUzE7MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8g +KE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBD +ZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQu +bmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJhcnF1aWEgRW50aXRhdHMg +ZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUNDMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R +85iKw5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm +4CgPukLjbo73FCeTae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaV +HMf5NLWUhdWZXqBIoH7nF2W4onW4HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNd +QlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0aE9jD2z3Il3rucO2n5nzbcc8t +lGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw0JDnJwIDAQAB +o4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4 +opvpXY0wfwYDVR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBo +dHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidW +ZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAwDQYJKoZIhvcN +AQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJlF7W2u++AVtd0x7Y +/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNaAl6k +SBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhy +Rp/7SNVel+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOS +Agu+TGbrIP65y7WZf+a2E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xl +nJ2lYJU6Un/10asIbvPuW/mIPX64b24D5EI= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1 +MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1 +czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG +CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy +MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl +ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS +b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy +euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO +bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw +WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d +MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE +1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/ +zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB +BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF +BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV +v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG +E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u +uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW +iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v +GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML +RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 +IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3 +MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 +LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp +YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG +A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq +K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe +sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX +MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT +XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ +HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH +4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub +j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo +U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b +u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ +bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er +fF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 +Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW +KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw +NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw +NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy +ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV +BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo +Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 +4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 +KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI +rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi +94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB +sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi +gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo +kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE +vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t +O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua +AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP +9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ +eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m +0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 +d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu +dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq +RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy +MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD +VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g +Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi +A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt +ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH +Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC +R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX +hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 +cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs +IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz +dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy +NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu +dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt +dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 +aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T +RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN +cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW +wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 +U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 +jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN +BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ +jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v +1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R +nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH +VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw +IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL +SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH +SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh +ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X +DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1 +TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ +fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA +sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU +WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS +nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH +dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip +NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC +AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF +MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB +uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl +PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP +JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/ +gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2 +j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6 +5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB +o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS +/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z +Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE +W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D +hNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV +BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC +aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV +BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1 +Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz +MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+ +BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp +em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY +B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH +D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF +Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo +q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D +k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH +fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut +dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM +ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8 +zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX +U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6 +Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5 +XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF +Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR +HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY +GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c +77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3 ++GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK +vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6 +FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl +yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P +AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD +y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d +NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFs +IENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg +R2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDvPE1A +PRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/NTL8 +Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hL +TytCOb1kLUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL +5mkWRxHCJ1kDs6ZgwiFAVvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7 +S4wMcoKK+xfNAGw6EzywhIdLFnopsk/bHdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe +2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNHK266ZUap +EBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6td +EPx7srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv +/NgdRN3ggX+d6YvhZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywN +A0ZF66D0f0hExghAzN4bcLUprbqLOzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0 +abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkCx1YAzUm5s2x7UwQa4qjJqhIF +I8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqFH4z1Ir+rzoPz +4iIprn2DQKi6bA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY +MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo +R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx +MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9 +AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA +ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0 +7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W +kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI +mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ +KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1 +6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl +4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K +oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj +UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU +AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL +MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj +KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2 +MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw +NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV +BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH +MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL +So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal +tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG +CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT +qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz +rD6ogRLQy7rQkgu2npaqBA+K +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT +MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIzNTk1OVowgZgxCzAJ +BgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0 +BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz ++uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5jK/BGvESyiaHAKAxJcCGVn2TAppMSAmUm +hsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdEc5IiaacDiGydY8hS2pgn +5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3CIShwiP/W +JmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exAL +DmKudlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZC +huOl1UcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IB +AQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9cr5HqQ6XErhK8WTTOd8lNNTB +zU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbEAp7aDHdlDkQN +kv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUH +SJsMC8tJP33st/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2G +spki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy +c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0 +IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV +VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8 +cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT +QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh +F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v +c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w +mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd +VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX +teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ +f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe +Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+ +nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB +/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY +MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX +IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn +ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z +uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN +Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja +QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW +koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9 +ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt +DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm +bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy +c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD +VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1 +c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81 +WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG +FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq +XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL +se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb +KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd +IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73 +y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt +hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc +QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4 +Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV +HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ +KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ +L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr +Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo +ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY +T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz +GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m +1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV +OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH +6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX +QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYD +VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 +IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 +MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMxNDBaFw0zODA3MzEx +MjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy +cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG +A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl +BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZI +hvcNAQEBBQADggIPADCCAgoCggIBAMDfVtPkOpt2RbQT2//BthmLN0EYlVJH6xed +KYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXfXjaOcNFccUMd2drvXNL7 +G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0ZJJ0YPP2 +zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4 +ddPB/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyG +HoiMvvKRhI9lNNgATH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2 +Id3UwD2ln58fQ1DJu7xsepeY7s2MH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3V +yJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfeOx2YItaswTXbo6Al/3K1dh3e +beksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSFHTynyQbehP9r +6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsog +zCtLkykPAgMBAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQW +BBS5CcqcHtvTbDprru1U8VuTBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDpr +ru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UEBhMCRVUxQzBBBgNVBAcTOk1hZHJp +ZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJmaXJtYS5jb20vYWRk +cmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJmaXJt +YSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiC +CQDJzdPp1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCow +KAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZI +hvcNAQEFBQADggIBAICIf3DekijZBZRG/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZ +UohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6ReAJ3spED8IXDneRRXoz +X1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/sdZ7LoR/x +fxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVz +a2Mg9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yyd +Yhz2rXzdpjEetrHHfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMd +SqlapskD7+3056huirRXhOukP9DuqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9O +AP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETrP3iZ8ntxPjzxmKfFGBI/5rso +M0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVqc5iJWzouE4ge +v8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ +FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F +uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX +kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs +ewv4n4Q= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 +MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL +v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 +eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq +tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd +C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa +zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB +mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH +V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n +bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG +3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs +J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO +291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS +ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd +AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 +MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 +RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT +gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm +KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd +QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ +XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o +LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU +RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp +jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK +6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX +mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs +Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH +WD9f +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1Ix +RDBCBgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1p +YyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIFJvb3RDQSAyMDExMB4XDTExMTIw +NjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYTAkdSMUQwQgYDVQQK +EztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENl +cnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPz +dYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJ +fel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa71HFK9+WXesyHgLacEns +bgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u8yBRQlqD +75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSP +FEDH3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNV +HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp +5dgTBCPuQSUwRwYDVR0eBEAwPqA8MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQu +b3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQub3JnMA0GCSqGSIb3DQEBBQUA +A4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVtXdMiKahsog2p +6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7 +dIsXRSZMFpGD/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8Acys +Nnq/onN694/BtZqhFLKPM58N7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXI +l7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx +FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg +Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG +A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr +b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ +jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn +PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh +ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9 +nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h +q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED +MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC +mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3 +7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB +oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs +EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO +fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi +AmvZWg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu +VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw +MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw +JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT +3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU ++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp +S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 +bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi +T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL +vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK +Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK +dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT +c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv +l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N +iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD +ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt +LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 +nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 ++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK +W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT +AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq +l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG +4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ +mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A +7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu +VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN +MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 +MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 +ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy +RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS +bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF +/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R +3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw +EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy +9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V +GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ +2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV +WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD +W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN +AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV +DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 +TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G +lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW +mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df +WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 ++bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ +tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA +GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv +8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYT +AkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQ +TS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG +9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMB4XDTAyMTIxMzE0MjkyM1oXDTIw +MTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAM +BgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEO +MAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2 +LmZyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaI +s9z4iPf930Pfeo2aSVz2TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2 +xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCWSo7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4 +u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYyHF2fYPepraX/z9E0+X1b +F8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNdfrGoRpAx +Vs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGd +PDPQtQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNV +HSAEDjAMMAoGCCqBegF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAx +NjAfBgNVHSMEGDAWgBSjBS8YYFDCiQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUF +AAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RKq89toB9RlPhJy3Q2FLwV3duJ +L92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3QMZsyK10XZZOY +YLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg +Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2a +NjSaTFR+FwNIlQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R +0982gaEbeC9xs/FZTEYYKKuF0mBWWg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 +MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 +ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD +VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq +scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO +xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H +LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX +uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD +yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ +JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q +rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN +BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L +hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB +QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ +HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu +Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg +QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB +BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA +A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb +laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 +awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo +JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw +LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT +VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk +LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb +UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ +QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ +naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls +QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcN +AQkBFglwa2lAc2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZp +dHNlZXJpbWlza2Vza3VzMRAwDgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMw +MVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMQsw +CQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEQ +MA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOB +SvZiF3tfTQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkz +ABpTpyHhOEvWgxutr2TC+Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvH +LCu3GFH+4Hv2qEivbDtPL+/40UceJlfwUR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMP +PbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDaTpxt4brNj3pssAki14sL +2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQFMAMBAf8w +ggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwIC +MIHDHoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDk +AGwAagBhAHMAdABhAHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0 +AHMAZQBlAHIAaQBtAGkAcwBrAGUAcwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABz +AGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABrAGkAbgBuAGkAdABhAG0AaQBz +AGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nwcy8wKwYDVR0f +BCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE +FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcY +P2/v6X2+MA4GA1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOi +CfP+JmeaUOTDBS8rNXiRTHyoERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+g +kcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyLabVAyJRld/JXIWY7zoVAtjNjGr95 +HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678IIbsSt4beDI3poHS +na9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkhMp6q +qIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0Z +TbvGRNs2yyqcjg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G +CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y +OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx +FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp +Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP +kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc +cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U +fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 +N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC +xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 ++rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM +Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG +SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h +mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk +ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c +2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t +HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAw +cjELMAkGA1UEBhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNy +b3NlYyBMdGQuMRQwEgYDVQQLEwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9z +ZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0MDYxMjI4NDRaFw0xNzA0MDYxMjI4 +NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEWMBQGA1UEChMN +TWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMTGU1p +Y3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2u +uO/TEdyB5s87lozWbxXGd36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+ +LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/NoqdNAoI/gqyFxuEPkEeZlApxcpMqyabA +vjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjcQR/Ji3HWVBTji1R4P770 +Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJPqW+jqpx +62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcB +AQRbMFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3Aw +LQYIKwYBBQUHMAKGIWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAP +BgNVHRMBAf8EBTADAQH/MIIBcwYDVR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIB +AQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3LmUtc3ppZ25vLmh1L1NaU1ov +MIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0AdAB2AOEAbgB5 +ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn +AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABT +AHoAbwBsAGcA4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABh +ACAAcwB6AGUAcgBpAG4AdAAgAGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABo +AHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMAegBpAGcAbgBvAC4AaAB1AC8AUwBa +AFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6Ly93d3cuZS1zemln +bm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NOPU1p +Y3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxP +PU1pY3Jvc2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZv +Y2F0aW9uTGlzdDtiaW5hcnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuB +EGluZm9AZS1zemlnbm8uaHWkdzB1MSMwIQYDVQQDDBpNaWNyb3NlYyBlLVN6aWdu +w7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhTWjEWMBQGA1UEChMNTWlj +cm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhVMIGsBgNV +HSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJI +VTERMA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDAS +BgNVBAsTC2UtU3ppZ25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBS +b290IENBghEAzLjnv04pGv2i3GalHCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS +8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMTnGZjWS7KXHAM/IO8VbH0jgds +ZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FEaGAHQzAxQmHl +7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a +86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfR +hUZLphK3dehKyVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/ +MPMMNz7UwiiAc7EBt51alhQBS6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG +EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 +MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl +cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR +dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB +pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM +b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz +IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT +lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz +AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 +VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG +ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 +BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG +AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M +U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh +bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C ++C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F +uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 +XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi +MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp +dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV +UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO +ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz +c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP +OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl +mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF +BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4 +qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw +gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu +bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp +dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8 +6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/ +h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH +/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN +pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCB +ijELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHly +aWdodCAoYykgMjAwNTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl +ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQSBDQTAeFw0w +NTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYDVQQGEwJDSDEQMA4G +A1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIwIAYD +VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBX +SVNlS2V5IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAy0+zAJs9Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxR +VVuuk+g3/ytr6dTqvirdqFEr12bDYVxgAsj1znJ7O7jyTmUIms2kahnBAbtzptf2 +w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbDd50kc3vkDIzh2TbhmYsF +mQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ/yxViJGg +4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t9 +4B3RLoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQw +EAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOx +SPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vImMMkQyh2I+3QZH4VFvbBsUfk2 +ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4+vg1YFkCExh8 +vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZi +Fj4A4xylNoEYokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ +/L7fCg0= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1 +dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9s +YW5vMQswCQYDVQQGEwJWRTEQMA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlz +dHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0 +aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBlcmludGVuZGVuY2lh +IGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUwIwYJ +KoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEw +MFoXDTIwMTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHBy +b2NlcnQubmV0LnZlMQ8wDQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGEx +KjAoBgNVBAsTIVByb3ZlZWRvciBkZSBDZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQG +A1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9u +aWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIwDQYJKoZI +hvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo9 +7BVCwfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74 +BCXfgI8Qhd19L3uA3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38G +ieU89RLAu9MLmV+QfI4tL3czkkohRqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9 +JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmOEO8GqQKJ/+MMbpfg353bIdD0 +PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG20qCZyFSTXai2 +0b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH +0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/ +6mnbVSKVUyqUtd+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1m +v6JpIzi4mWCZDlZTOpx+FIywBm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7 +K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvpr2uKGcfLFFb14dq12fy/czja+eev +bqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/AgEBMDcGA1UdEgQw +MC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAzNi0w +MB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFD +gBStuyIdxuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0 +b3JpZGFkIGRlIENlcnRpZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xh +bm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQHEwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0 +cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5hY2lvbmFsIGRlIENlcnRp +ZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5kZW5jaWEg +ZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkq +hkiG9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQD +AgEGME0GA1UdEQRGMESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0w +MDAwMDKgGwYFYIZeAgKgEgwQUklGLUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEag +RKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9sY3IvQ0VSVElGSUNBRE8t +UkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNyYWl6LnN1c2Nl +cnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v +Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsG +AQUFBwIBFh5odHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcN +AQELBQADggIBACtZ6yKZu4SqT96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS +1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmNg7+mvTV+LFwxNG9s2/NkAZiqlCxB +3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4quxtxj7mkoP3Yldmv +Wb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1n8Gh +HVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHm +pHmJWhSnFFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXz +sOfIt+FTvZLm8wyWuevo5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bE +qCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq3TNWOByyrYDT13K9mmyZY+gAu0F2Bbdb +mRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5poLWccret9W6aAjtmcz9 +opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3YeMLEYC/H +YvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 +MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV +wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe +rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 +68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh +4Pw5qlPafX7PGglTvFOFBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp +UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o +abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc +3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G +KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt +hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO +Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt +zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD +ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 +cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN +qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 +YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv +b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 +8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k +NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj +ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp +q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt +nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa +GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg +Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J +WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB +rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp ++ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 +ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i +Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz +PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og +/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH +oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI +yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud +EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 +A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL +MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f +BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn +g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl +fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K +WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha +B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc +hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR +TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD +mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z +ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y +4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza +8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM +V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB +4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr +H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd +8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv +vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT +mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe +btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc +T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt +WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ +c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A +4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD +VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG +CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 +aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu +dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw +czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G +A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg +Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 +7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem +d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd ++LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B +4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN +t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x +DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 +k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s +zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j +Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT +mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK +4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 +MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR +/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu +FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR +U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c +ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR +FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k +A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw +eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl +sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp +VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q +A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ +ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD +ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI +FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv +oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg +u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP +0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf +3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl +8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ +DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN +PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ +ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJF +UzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJ +R1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcN +MDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3WjBoMQswCQYDVQQGEwJFUzEfMB0G +A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScw +JQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+ +WmmmO3I2F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKj +SgbwJ/BXufjpTjJ3Cj9BZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGl +u6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQD0EbtFpKd71ng+CT516nDOeB0/RSrFOy +A8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXteJajCq+TA81yc477OMUxk +Hl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMBAAGjggM7 +MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBr +aS5ndmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIIC +IwYKKwYBBAG/VQIBADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8A +cgBpAGQAYQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIA +YQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIAYQBsAGkAdABhAHQAIABWAGEA +bABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQByAGEAYwBpAPMA +bgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA +aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMA +aQBvAG4AYQBtAGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQA +ZQAgAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEA +YwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBuAHQAcgBhACAAZQBuACAAbABhACAA +ZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAAOgAvAC8AdwB3AHcA +LgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0dHA6 +Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+y +eAT8MIGVBgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQsw +CQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0G +A1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVu +Y2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRhTvW1yEICKrNcda3Fbcrn +lD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdzCkj+IHLt +b8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg +9J63NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XF +ducTZnV+ZfsBn5OHiJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmC +IoaZM3Fa6hlXPZHNqcCjbgcTpsnt+GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx +MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg +Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ +iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa +/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ +jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI +HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7 +sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w +gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw +KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG +AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L +URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO +H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm +I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY +iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr +MCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoG +A1UEAxMTU2VjdXJlU2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0 +MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZp +Y2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RD +QTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvLTJsz +i1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8 +h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOV +MdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9 +UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni +8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsC +h8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYD +VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB +AKChOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xm +KbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQ +X5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWr +QbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR7SXf+Of5 +pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN +QSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz +MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv +cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz +Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO +0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao +wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj +7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS +8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT +BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg +JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3 +6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/ +3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm +D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS +CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMh +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIz +MloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09N +IFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNlY3VyaXR5IENvbW11 +bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSE +RMqm4miO/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gO +zXppFodEtZDkBp2uoQSXWHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5 +bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4zZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDF +MxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4bepJz11sS6/vmsJWXMY1 +VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK9U2vP9eC +OKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HW +tWS3irO4G8za+6xmiEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZ +q51ihPZRwSzJIxXYKLerJRO1RuGGAv8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDb +EJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnWmHyojf6GPgcWkuF75x3sM3Z+ +Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEWT1MKZPlO9L9O +VL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX +DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy +dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj +YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV +OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr +zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM +VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ +hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO +ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw +awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs +OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF +coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc +okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 +t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy +1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ +SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIGGTCCBAGgAwIBAgIIPtVRGeZNzn4wDQYJKoZIhvcNAQELBQAwajEhMB8GA1UE +AxMYU0cgVFJVU1QgU0VSVklDRVMgUkFDSU5FMRwwGgYDVQQLExMwMDAyIDQzNTI1 +Mjg5NTAwMDIyMRowGAYDVQQKExFTRyBUUlVTVCBTRVJWSUNFUzELMAkGA1UEBhMC +RlIwHhcNMTAwOTA2MTI1MzQyWhcNMzAwOTA1MTI1MzQyWjBqMSEwHwYDVQQDExhT +RyBUUlVTVCBTRVJWSUNFUyBSQUNJTkUxHDAaBgNVBAsTEzAwMDIgNDM1MjUyODk1 +MDAwMjIxGjAYBgNVBAoTEVNHIFRSVVNUIFNFUlZJQ0VTMQswCQYDVQQGEwJGUjCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANqoVgLsfJXwTukK0rcHoyKL +ULO5Lhk9V9sZqtIr5M5C4myh5F0lHjMdtkXRtPpZilZwyW0IdmlwmubHnAgwE/7m +0ZJoYT5MEfJu8rF7V1ZLCb3cD9lxDOiaN94iEByZXtaxFwfTpDktwhpz/cpLKQfC +eSnIyCauLMT8I8hL4oZWDyj9tocbaF85ZEX9aINsdSQePHWZYfrSFPipS7HYfad4 +0hNiZbXWvn5qA7y1svxkMMPQwpk9maTTzdGxxFOHe0wTE2Z/v9VlU2j5XB7ltP82 +mUWjn2LAfxGCAVTeD2WlOa6dSEyJoxA74OaD9bDaLB56HFwfAKzMq6dgZLPGxXvH +VUZ0PJCBDkqOWZ1UsEixUkw7mO6r2jS3U81J2i/rlb4MVxH2lkwEeVyZ1eXkvm/q +R+5RS+8iJq612BGqQ7t4vwt+tN3PdB0lqYljseI0gcSINTjiAg0PE8nVKoIV8IrE +QzJW5FMdHay2z32bll0eZOl0c8RW5BZKUm2SOdPhTQ4/YrnerbUdZbldUv5dCamc +tKQM2S9FdqXPjmqanqqwEaHrYcbrPx78ZrQSnUZ/MhaJvnFFr5Eh2f2Tv7QCkUL/ +SR/tixVo3R+OrJvdggWcRGkWZBdWX0EPSk8ED2VQhpOX7EW/XcIc3M/E2DrmeAXQ +xVVVqV7+qzohu+VyFPcLAgMBAAGjgcIwgb8wHQYDVR0OBBYEFCkgy/HDD9oGjhOT +h/5fYBopu/O2MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUKSDL8cMP2gaO +E5OH/l9gGim787YwEQYDVR0gBAowCDAGBgRVHSAAMEkGA1UdHwRCMEAwPqA8oDqG +OGh0dHA6Ly9jcmwuc2d0cnVzdHNlcnZpY2VzLmNvbS9yYWNpbmUtR3JvdXBlU0cv +TGF0ZXN0Q1JMMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEATEZn +4ERQ9cW2urJRCiUTHbfHiC4fuStkoMuTiFJZqmD1zClSF/8E5ze0MRFGfisebKeL +PEeaXvSqXZA7RT2fSsmKe47A7j55i5KjyJRKuCgRa6YlX129x8j7g09VMeZc8BN8 +471/Kiw3N5RJr4QfFCeiWBCPCjk3GhIgQY8Z9qkfGe2yNLKtfTNEi18KB0PydkVF +La3kjQ4A/QQIqudr+xe9sAhWDjUqcvCz5006Tw3c82ASszhkjNv54SaNL+9O6CRH +PjY0imkPKGuLh8a9hSb50+tpIVZgkdb34GLCqHGuLt5mI7VSRqakSDcsfwEWVxH3 +Jw0O5Q/WkEXhHj8h3NL8FhgTPk1qsiZqQF4leP049KxYejcbmEAEx47J1MRnYbGY +rvDNDty5r2WDewoEij9hqvddQYbmxkzCTzpcVuooO6dEz8hKZPVyYC3jQ7hK4HU8 +MuSqFtcRucFF2ZtmY2blIrc07rrVdC8lZPOBVMt33lfUk+OsBzE6PlwDg1dTx/D+ +aNglUE0SyObhlY1nqzyTPxcCujjXnvcwpT09RAEzGpqfjtCf8e4wiHPvriQZupdz +FcHscQyEZLV77LxpPqRtCRY2yko5isune8YdfucziMm+MG2chZUh6Uc7Bn6B4upG +5nBYgOao8p0LadEziVkw82TTC/bOKwn7fRB2LhA= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gRVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0y +MjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIg +TmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBS +b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkkSzrS +M4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nC +UiY4iKTWO0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3d +Z//BYY1jTw+bbRcwJu+r0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46p +rfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13l +pJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gVXJrm0w912fxBmJc+qiXb +j5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr08C+eKxC +KFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS +/ZbV0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0X +cgOPvZuM5l5Tnrmd74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH +1vI4gnPah1vlPNOePqc7nvQDs/nxfRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrP +px9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwaivsnuL8wbqg7 +MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI +eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u +2dfOWBfoqSmuc0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHS +v4ilf0X8rLiltTMMgsT7B/Zq5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTC +wPTxGfARKbalGAKb12NMcIxHowNDXLldRqANb/9Zjr7dn3LDWyvfjFvO5QxGbJKy +CqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tNf1zuacpzEPuKqf2e +vTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi5Dp6 +Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIa +Gl6I6lD4WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeL +eG9QgkRQP2YGiqtDhFZKDyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8 +FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGyeUN51q1veieQA6TqJIc/2b3Z6fJfUEkc +7uzXLg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oX +DTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ5291 +qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8Sp +uOUfiUtnvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPU +Z5uW6M7XxgpT0GtJlvOjCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvE +pMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiile7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp +5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCROME4HYYEhLoaJXhena/M +UGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpICT0ugpTN +GmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy +5V6548r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv +6q012iDTiIJh8BIitrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEK +eN5KzlW/HdXZt1bv8Hb/C3m1r737qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6 +B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMBAAGjgZcwgZQwDwYDVR0TAQH/ +BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcCARYxaHR0cDov +L3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqG +SIb3DQEBCwUAA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLyS +CZa59sCrI2AGeYwRTlHSeYAz+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen +5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwjf/ST7ZwaUb7dRUG/kSS0H4zpX897 +IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaNkqbG9AclVMwWVxJK +gnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfkCpYL ++63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxL +vJxxcypFURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkm +bEgeqmiSBeGCc1qb3AdbCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvk +N1trSt8sV4pAWja63XVECDdCcAz+3F4hoKOKwJCcaNpQ5kUQR3i2TtJlycM33+FC +Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z +ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloX +DTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4yolQP +cPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WW +IkYFsO2tx1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqX +xz8ecAgwoNzFs21v0IJyEavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFy +KJLZWyNtZrVtB0LrpjPOktvA9mxjeM3KTj215VKb8b475lRgsGYeCasH/lSJEULR +9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUurmkVLoR9BvUhTFXFkC4az +5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU51nus6+N8 +6U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7 +Ngzp07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHP +bMk7ccHViLVlvMDoFxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXt +BznaqB16nzaeErAMZRKQFWDZJkBE41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTt +XUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleuyjWcLhL75Lpd +INyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD +U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwp +LiniyMMB8jPqKqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8 +Ipf3YF3qKS9Ysr1YvY2WTxB1v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixp +gZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA8KCWAg8zxXHzniN9lLf9OtMJgwYh +/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b8KKaa8MFSu1BYBQw +0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0rmj1A +fsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq +4BZ+Extq1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR +1VmiiXTTn74eS9fGbbeIJG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/ +QFH1T/U67cjF68IeHRaVesd+QnGTbksVtzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM +94B7IWcnMFk= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg +Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM3WhcNMzYwOTE3MTk0NjM2WjB9 +MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi +U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh +cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk +pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf +OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C +Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT +Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi +HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM +Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w ++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+ +Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3 +Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B +26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID +AQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFul +F2mHMMo0aEPQQa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCC +ATgwLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w +ZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2ludGVybWVk +aWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENvbW1lcmNpYWwgKFN0 +YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0aGUg +c2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93 +d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgG +CWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5fPGFf59Jb2vKXfuM/gTF +wWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWmN3PH/UvS +Ta0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst +0OcNOrg+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNc +pRJvkrKTlMeIFw6Ttn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKl +CcWw0bdT82AUuoVpaiF8H3VhFyAXe2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVF +P0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA2MFrLH9ZXF2RsXAiV+uKa0hK +1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBsHvUwyKMQ5bLm +KhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE +JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ +8dCAWZvLMdibD4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnm +fyWl8kgAwKQB2j8= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkgRzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1 +OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoG +A1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRzIwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8Oo1XJ +JZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsD +vfOpL9HG4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnoo +D/Uefyf3lLE3PbfHkffiAez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/ +Q0kGi4xDuFby2X8hQxfqp0iVAXV16iulQ5XqFYSdCI0mblWbq9zSOdIxHWDirMxW +RST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbsO+wmETRIjfaAKxojAuuK +HDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8HvKTlXcxN +nw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM +0D4LnMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/i +UUjXuG+v+E5+M5iSFGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9 +Ha90OrInwMEePnWjFqmveiJdnxMaz6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHg +TuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJKoZIhvcNAQEL +BQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K +2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfX +UfEpY9Z1zRbkJ4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl +6/2o1PXWT6RbdejF0mCy2wl+JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK +9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG/+gyRr61M3Z3qAFdlsHB1b6uJcDJ +HgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTcnIhT76IxW1hPkWLI +wpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/XldblhY +XzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5l +IxKVCCIcl85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoo +hdVddLHRDiBYmxOlsGOm7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulr +so8uBtjRkcfGEvRM/TAXw8HaOFvjqermobp573PYtlNXLfbQ4ddI +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEezCCA2OgAwIBAgIQNxkY5lNUfBq1uMtZWts1tzANBgkqhkiG9w0BAQUFADCB +rjELMAkGA1UEBhMCREUxIDAeBgNVBAgTF0JhZGVuLVd1ZXJ0dGVtYmVyZyAoQlcp +MRIwEAYDVQQHEwlTdHV0dGdhcnQxKTAnBgNVBAoTIERldXRzY2hlciBTcGFya2Fz +c2VuIFZlcmxhZyBHbWJIMT4wPAYDVQQDEzVTLVRSVVNUIEF1dGhlbnRpY2F0aW9u +IGFuZCBFbmNyeXB0aW9uIFJvb3QgQ0EgMjAwNTpQTjAeFw0wNTA2MjIwMDAwMDBa +Fw0zMDA2MjEyMzU5NTlaMIGuMQswCQYDVQQGEwJERTEgMB4GA1UECBMXQmFkZW4t +V3VlcnR0ZW1iZXJnIChCVykxEjAQBgNVBAcTCVN0dXR0Z2FydDEpMCcGA1UEChMg +RGV1dHNjaGVyIFNwYXJrYXNzZW4gVmVybGFnIEdtYkgxPjA8BgNVBAMTNVMtVFJV +U1QgQXV0aGVudGljYXRpb24gYW5kIEVuY3J5cHRpb24gUm9vdCBDQSAyMDA1OlBO +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2bVKwdMz6tNGs9HiTNL1 +toPQb9UY6ZOvJ44TzbUlNlA0EmQpoVXhOmCTnijJ4/Ob4QSwI7+Vio5bG0F/WsPo +TUzVJBY+h0jUJ67m91MduwwA7z5hca2/OnpYH5Q9XIHV1W/fuJvS9eXLg3KSwlOy +ggLrra1fFi2SU3bxibYs9cEv4KdKb6AwajLrmnQDaHgTncovmwsdvs91DSaXm8f1 +XgqfeN+zvOyauu9VjxuapgdjKRdZYgkqeQd3peDRF2npW932kKvimAoA0SVtnteF +hy+S8dF2g08LOlk3KC8zpxdQ1iALCvQm+Z845y2kuJuJja2tyWp9iRe79n+Ag3rm +7QIDAQABo4GSMIGPMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEG +MCkGA1UdEQQiMCCkHjAcMRowGAYDVQQDExFTVFJvbmxpbmUxLTIwNDgtNTAdBgNV +HQ4EFgQUD8oeXHngovMpttKFswtKtWXsa1IwHwYDVR0jBBgwFoAUD8oeXHngovMp +ttKFswtKtWXsa1IwDQYJKoZIhvcNAQEFBQADggEBAK8B8O0ZPCjoTVy7pWMciDMD +pwCHpB8gq9Yc4wYfl35UvbfRssnV2oDsF9eK9XvCAPbpEW+EoFolMeKJ+aQAPzFo +LtU96G7m1R08P7K9n3frndOMusDXtk3sU5wPBG7qNWdX4wple5A64U8+wwCSersF +iXOMy6ZNwPv2AtawB6MDwidAnwzkhYItr5pCHdDHjfhA7p0GVxzZotiAFP7hYy0y +h9WUUpY6RsZxlj33mA6ykaqP2vROJAA5VeitF7nTNCtKqUDMFypVZUF0Qn71wK/I +k63yGFs9iQzbRzkk+OBM8h+wPQrKBU6JIRrjKpms/H+h8Q8bHz2eBIPdltkdOpQ= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIID2DCCAsCgAwIBAgIQYFbFSyNAW2TU7SXa2dYeHjANBgkqhkiG9w0BAQsFADCB +hTELMAkGA1UEBhMCREUxKTAnBgNVBAoTIERldXRzY2hlciBTcGFya2Fzc2VuIFZl +cmxhZyBHbWJIMScwJQYDVQQLEx5TLVRSVVNUIENlcnRpZmljYXRpb24gU2Vydmlj +ZXMxIjAgBgNVBAMTGVMtVFJVU1QgVW5pdmVyc2FsIFJvb3QgQ0EwHhcNMTMxMDIy +MDAwMDAwWhcNMzgxMDIxMjM1OTU5WjCBhTELMAkGA1UEBhMCREUxKTAnBgNVBAoT +IERldXRzY2hlciBTcGFya2Fzc2VuIFZlcmxhZyBHbWJIMScwJQYDVQQLEx5TLVRS +VVNUIENlcnRpZmljYXRpb24gU2VydmljZXMxIjAgBgNVBAMTGVMtVFJVU1QgVW5p +dmVyc2FsIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCo +4wvfETeFgpq1bGZ8YT/ARxodRuOwVWTluII5KAd+F//0m4rwkYHqOD8heGxI7Gsv +otOKcrKn19nqf7TASWswJYmM67fVQGGY4tw8IJLNZUpynxqOjPolFb/zIYMoDYuv +WRGCQ1ybTSVRf1gYY2A7s7WKi1hjN0hIkETCQN1d90NpKZhcEmVeq5CSS2bf1XUS +U1QYpt6K1rtXAzlZmRgFDPn9FcaQZEYXgtfCSkE9/QC+V3IYlHcbU1qJAfYzcg6T +OtzoHv0FBda8c+CI3KtP7LUYhk95hA5IKmYq3TLIeGXIC51YAQVx7YH1aBduyw20 +S9ih7K446xxYL6FlAzQvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P +AQH/BAQDAgEGMB0GA1UdDgQWBBSafdfr639UmEUptCCrbQuWIxmkwjANBgkqhkiG +9w0BAQsFAAOCAQEATpYS2353XpInniEXGIJ22D+8pQkEZoiJrdtVszNqxmXEj03z +MjbceQSWqXcy0Zf1GGuMuu3OEdBEx5LxtESO7YhSSJ7V/Vn4ox5R+wFS5V/let2q +JE8ii912RvaloA812MoPmLkwXSBvwoEevb3A/hXTOCoJk5gnG5N70Cs0XmilFU/R +UsOgyqCDRR319bdZc11ZAY+qwkcvFHHVKeMQtUeTJcwjKdq3ctiR1OwbSIoi5MEq +9zpok59FGW5Dt8z+uJGaYRo2aWNkkijzb2GShROfyQcsi1fc65551cLeCNVUsldO +KjKNoeI60RAgIjl9NEVvcTvDHfz/sk+o4vYwHg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBk +MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0 +YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg +Q0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4MTgyMjA2MjBaMGQxCzAJBgNVBAYT +AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp +Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9 +m2BtRsiMMW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdih +FvkcxC7mlSpnzNApbjyFNDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/ +TilftKaNXXsLmREDA/7n29uj/x2lzZAeAR81sH8A25Bvxn570e56eqeqDFdvpG3F +EzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkCb6dJtDZd0KTeByy2dbco +kdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn7uHbHaBu +HYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNF +vJbNcA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo +19AOeCMgkckkKmUpWyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjC +L3UcPX7ape8eYIVpQtPM+GP+HkM5haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJW +bjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNYMUJDLXT5xp6mig/p/r+D5kNX +JLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw +FDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j +BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzc +K6FptWfUjNP9MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzf +ky9NfEBWMXrrpA9gzXrzvsMnjgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7Ik +Vh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQMbFamIp1TpBcahQq4FJHgmDmHtqB +sfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4HVtA4oJVwIHaM190e +3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtlvrsR +ls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ip +mXeascClOS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HH +b6D0jqTsNFFbjCYDcKF31QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksf +rK/7DZBaZmBwXarNeNQk7shBoJMBkpxqnvy5JMWzFYJ+vq6VK+uxwNrjAWALXmms +hFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCyx/yP2FS1k2Kdzs9Z+z0Y +zirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMWNY6E0F/6 +MBr1mmz0DlP5OlvRHA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBk +MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0 +YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg +Q0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2MjUwNzM4MTRaMGQxCzAJBgNVBAYT +AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp +Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvEr +jw0DzpPMLgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r +0rk0X2s682Q2zsKwzxNoysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f +2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJwDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVP +ACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpHWrumnf2U5NGKpV+GY3aF +y6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1aSgJA/MTA +tukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL +6yxSNLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0 +uPoTXGiTOmekl9AbmbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrAL +acywlKinh/LTSlDcX3KwFnUey7QYYpqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velh +k6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3qPyZ7iVNTA6z00yPhOgpD/0Q +VAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw +FDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O +BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqh +b97iEoHF8TwuMA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4R +fbgZPnm3qKhyN2abGu2sEzsOv2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv +/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ82YqZh6NM4OKb3xuqFp1mrjX2lhI +REeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLzo9v/tdhZsnPdTSpx +srpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcsa0vv +aGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciAT +woCqISxxOQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99n +Bjx8Oto0QuFmtEYE3saWmA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5W +t6NlUe07qxS/TFED6F+KBZvuim6c779o+sjaC+NCydAXFJy3SuCvkychVSa1ZC+N +8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TCrvJcwhbtkj6EPnNgiLx2 +9CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX5OfNeOI5 +wSsSnqaeG8XmDtkx2Q== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF4DCCA8igAwIBAgIRAPL6ZOJ0Y9ON/RAdBB92ylgwDQYJKoZIhvcNAQELBQAw +ZzELMAkGA1UEBhMCY2gxETAPBgNVBAoTCFN3aXNzY29tMSUwIwYDVQQLExxEaWdp +dGFsIENlcnRpZmljYXRlIFNlcnZpY2VzMR4wHAYDVQQDExVTd2lzc2NvbSBSb290 +IEVWIENBIDIwHhcNMTEwNjI0MDk0NTA4WhcNMzEwNjI1MDg0NTA4WjBnMQswCQYD +VQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2Vy +dGlmaWNhdGUgU2VydmljZXMxHjAcBgNVBAMTFVN3aXNzY29tIFJvb3QgRVYgQ0Eg +MjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMT3HS9X6lds93BdY7Bx +UglgRCgzo3pOCvrY6myLURYaVa5UJsTMRQdBTxB5f3HSek4/OE6zAMaVylvNwSqD +1ycfMQ4jFrclyxy0uYAyXhqdk/HoPGAsp15XGVhRXrwsVgu42O+LgrQ8uMIkqBPH +oCE2G3pXKSinLr9xJZDzRINpUKTk4RtiGZQJo/PDvO/0vezbE53PnUgJUmfANykR +HvvSEaeFGHR55E+FFOtSN+KxRdjMDUN/rhPSays/p8LiqG12W0OfvrSdsyaGOx9/ +5fLoZigWJdBLlzin5M8J0TbDC77aO0RYjb7xnglrPvMyxyuHxuxenPaHZa0zKcQv +idm5y8kDnftslFGXEBuGCxobP/YCfnvUxVFkKJ3106yDgYjTdLRZncHrYTNaRdHL +OdAGalNgHa/2+2m8atwBz735j9m9W8E6X47aD0upm50qKGsaCnw8qyIL5XctcfaC +NYGu+HuB5ur+rPQam3Rc6I8k9l2dRsQs0h4rIWqDJ2dVSqTjyDKXZpBy2uPUZC5f +46Fq9mDU5zXNysRojddxyNMkM3OxbPlq4SjbX8Y96L5V5jcb7STZDxmPX2MYWFCB +UWVv8p9+agTnNCRxunZLWB4ZvRVgRaoMEkABnRDixzgHcgplwLa7JSnaFp6LNYth +7eVxV4O1PHGf40+/fh6Bn0GXAgMBAAGjgYYwgYMwDgYDVR0PAQH/BAQDAgGGMB0G +A1UdIQQWMBQwEgYHYIV0AVMCAgYHYIV0AVMCAjASBgNVHRMBAf8ECDAGAQH/AgED +MB0GA1UdDgQWBBRF2aWBbj2ITY1x0kbBbkUe88SAnTAfBgNVHSMEGDAWgBRF2aWB +bj2ITY1x0kbBbkUe88SAnTANBgkqhkiG9w0BAQsFAAOCAgEAlDpzBp9SSzBc1P6x +XCX5145v9Ydkn+0UjrgEjihLj6p7jjm02Vj2e6E1CqGdivdj5eu9OYLU43otb98T +PLr+flaYC/NUn81ETm484T4VvwYmneTwkLbUwp4wLh/vx3rEUMfqe9pQy3omywC0 +Wqu1kx+AiYQElY2NfwmTv9SoqORjbdlk5LgpWgi/UOGED1V7XwgiG/W9mR4U9s70 +WBCCswo9GcG/W6uqmdjyMb3lOGbcWAXH7WMaLgqXfIeTK7KK4/HsGOV1timH59yL +Gn602MnTihdsfSlEvoqq9X46Lmgxk7lq2prg2+kupYTNHAq4Sgj5nPFhJpiTt3tm +7JFe3VE/23MPrQRYCd0EApUKPtN236YQHoA96M2kZNEzx5LH4k5E4wnJTsJdhw4S +nr8PyQUQ3nqjsTzyP6WqJ3mtMX0f/fwZacXduT98zca0wjAefm6S139hdlqP65VN +vBFuIXxZN5nQBrz5Bm0yFqXZaajh3DyAHmBR3NdUIR7KYndP+tiPsys6DXhyyWhB +WkdKwqPrGtcKqzwyVcgKEZzfdNbwQBUdyLmPtTbFr/giuMod89a2GQ+fYWVq6nTI +fI/DT11lgh/ZDYnadXL77/FHZxOzyNEZiCcmmpl5fx7kLD977vHeTYuWl8PVP3wb +I+2ksx0WckNLIOFZfsLorSa/ovc= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV +BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln +biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF +MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT +d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8 +76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+ +bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c +6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE +emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd +MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt +MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y +MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y +FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi +aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM +gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB +qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7 +lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn +8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6 +45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO +UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5 +O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC +bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv +GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a +77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC +hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3 +92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp +Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w +ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt +Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFwTCCA6mgAwIBAgIITrIAZwwDXU8wDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE +BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEjMCEGA1UEAxMaU3dpc3NTaWdu +IFBsYXRpbnVtIENBIC0gRzIwHhcNMDYxMDI1MDgzNjAwWhcNMzYxMDI1MDgzNjAw +WjBJMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMSMwIQYDVQQD +ExpTd2lzc1NpZ24gUGxhdGludW0gQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAMrfogLi2vj8Bxax3mCq3pZcZB/HL37PZ/pEQtZ2Y5Wu669y +IIpFR4ZieIbWIDkm9K6j/SPnpZy1IiEZtzeTIsBQnIJ71NUERFzLtMKfkr4k2Htn +IuJpX+UFeNSH2XFwMyVTtIc7KZAoNppVRDBopIOXfw0enHb/FZ1glwCNioUD7IC+ +6ixuEFGSzH7VozPY1kneWCqv9hbrS3uQMpe5up1Y8fhXSQQeol0GcN1x2/ndi5ob +jM89o03Oy3z2u5yg+gnOI2Ky6Q0f4nIoj5+saCB9bzuohTEJfwvH6GXp43gOCWcw +izSC+13gzJ2BbWLuCB4ELE6b7P6pT1/9aXjvCR+htL/68++QHkwFix7qepF6w9fl ++zC8bBsQWJj3Gl/QKTIDE0ZNYWqFTFJ0LwYfexHihJfGmfNtf9dng34TaNhxKFrY +zt3oEBSa/m0jh26OWnA81Y0JAKeqvLAxN23IhBQeW71FYyBrS3SMvds6DsHPWhaP +pZjydomyExI7C3d3rLvlPClKknLKYRorXkzig3R3+jVIeoVNjZpTxN94ypeRSCtF +KwH3HBqi7Ri6Cr2D+m+8jVeTO9TUps4e8aCxzqv9KyiaTxvXw3LbpMS/XUz13XuW +ae5ogObnmLo2t/5u7Su9IPhlGdpVCX4l3P5hYnL5fhgC72O00Puv5TtjjGePAgMB +AAGjgawwgakwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFFCvzAeHFUdvOMW0ZdHelarp35zMMB8GA1UdIwQYMBaAFFCvzAeHFUdvOMW0 +ZdHelarp35zMMEYGA1UdIAQ/MD0wOwYJYIV0AVkBAQEBMC4wLAYIKwYBBQUHAgEW +IGh0dHA6Ly9yZXBvc2l0b3J5LnN3aXNzc2lnbi5jb20vMA0GCSqGSIb3DQEBBQUA +A4ICAQAIhab1Fgz8RBrBY+D5VUYI/HAcQiiWjrfFwUF1TglxeeVtlspLpYhg0DB0 +uMoI3LQwnkAHFmtllXcBrqS3NQuB2nEVqXQXOHtYyvkv+8Bldo1bAbl93oI9ZLi+ +FHSjClTTLJUYFzX1UWs/j6KWYTl4a0vlpqD4U99REJNi54Av4tHgvI42Rncz7Lj7 +jposiU0xEQ8mngS7twSNC/K5/FqdOxa3L8iYq/6KUFkuozv8KV2LwUvJ4ooTHbG/ +u0IdUt1O2BReEMYxB+9xJ/cbOQncguqLs5WGXv312l0xpuAxtpTmREl0xRbl9x8D +YSjFyMsSoEJL+WuICI20MhjzdZ/EfwBPBZWcoxcCw7NTm6ogOSkrZvqdr16zktK1 +puEa+S1BaYEUtLS17Yk9zvupnTVCRLEcFHOBzyoBNZox1S2PbYTfgE1X4z/FhHXa +icYwu+uPyyIIoK6q8QNsOktNCaUOcsZWayFCTiMlFGiudgp8DAdwZPmaL/YFOSbG +DI8Zf0NebvRbFS/bYV3mZy8/CJT5YLSYMdp08YSTcU1f+2BY0fvEwW2JorsgH51x +kcsymxM9Pn2SUjWskpSi0xjCfMfqr3YFFt1nJ8J+HAciIfNAChs0B0QTwoRqjt8Z +Wr9/6x3iGjjRXK9HkmuAtTClyY3YqzGBH9/CZjfTk6mFhnll0g== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE +BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu +IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow +RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY +U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv +Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br +YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF +nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH +6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt +eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/ +c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ +MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH +HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf +jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6 +5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB +rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c +wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB +AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp +WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9 +xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ +2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ +IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8 +aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X +em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR +dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/ +OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+ +hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy +tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/ +MQswCQYDVQQGEwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5MB4XDTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1ow +PzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dvdmVybm1lbnQgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +AJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qNw8XR +IePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1q +gQdW8or5BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKy +yhwOeYHWtXBiCAEuTk8O1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAts +F/tnyMKtsc2AtJfcdgEWFelq16TheEfOhtX7MfP6Mb40qij7cEwdScevLJ1tZqa2 +jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wovJ5pGfaENda1UhhXcSTvx +ls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7Q3hub/FC +VGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHK +YS1tB6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoH +EgKXTiCQ8P8NHuJBO9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThN +Xo+EHWbNxWCWtFJaBYmOlXqYwZE8lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1Ud +DgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNVHRMEBTADAQH/MDkGBGcqBwAE +MTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg209yewDL7MTqK +UWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ +TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyf +qzvS/3WXy6TjZwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaK +ZEk9GhiHkASfQlK3T8v+R0F2Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFE +JPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlUD7gsL0u8qV1bYH+Mh6XgUmMqvtg7 +hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6QzDxARvBMB1uUO07+1 +EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+HbkZ6Mm +nD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WX +udpVBrkk7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44Vbnz +ssQwmSNOXfJIoRIM3BKQCZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDe +LMDDav7v3Aun+kbfYNucpllQdSNpc5Oy+fwC00fmcc4QAu4njIT/rEUNE1yDMuAl +pYYsfPQS +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjEL +MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV +BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0 +Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYwMTEyMTQzODQzWhcNMjUxMjMxMjI1 +OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i +SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UEAxMc +VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jf +tMjWQ+nEdVl//OEd+DFwIxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKg +uNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2J +XjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQXa7pIXSSTYtZgo+U4+lK +8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7uSNQZu+99 +5OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3 +kUrL84J6E1wIqzCB7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRy +dXN0Y2VudGVyLmRlL2NybC92Mi90Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6 +Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBUcnVzdENlbnRlciUyMENsYXNz +JTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21iSCxPVT1yb290 +Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iS +GNn3Bzn1LL4GdXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprt +ZjluS5TmVfwLG4t3wVMTZonZKNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8 +au0WOB9/WIFaGusyiC2y8zl3gK9etmF1KdsjTYjKUCjLhdLTEKJZbtOTVAB6okaV +hgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kPJOzHdiEoZa5X6AeI +dUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfkvQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjEL +MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV +BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0 +Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYwMTEyMTQ0MTU3WhcNMjUxMjMxMjI1 +OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i +SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UEAxMc +VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJW +Ht4bNwcwIi9v8Qbxq63WyKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+Q +Vl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo6SI7dYnWRBpl8huXJh0obazovVkdKyT2 +1oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZuV3bOx4a+9P/FRQI2Alq +ukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk2ZyqBwi1 +Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NX +XAek0CSnwPIA1DCB7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRy +dXN0Y2VudGVyLmRlL2NybC92Mi90Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6 +Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBUcnVzdENlbnRlciUyMENsYXNz +JTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21iSCxPVT1yb290 +Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlN +irTzwppVMXzEO2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8 +TtXqluJucsG7Kv5sbviRmEb8yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6 +g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9IJqDnxrcOfHFcqMRA/07QlIp2+gB +95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal092Y+tTmBvTwtiBj +S+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc5A== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTEL +MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNV +BAsTG1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1 +c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcNMDYwMzIyMTU1NDI4WhcNMjUxMjMx +MjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIg +R21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYwJAYD +VQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSR +JJZ4Hgmgm5qVSkr1YnwCqMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3T +fCZdzHd55yx4Oagmcw6iXSVphU9VDprvxrlE4Vc93x9UIuVvZaozhDrzznq+VZeu +jRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtwag+1m7Z3W0hZneTvWq3z +wZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9OgdwZu5GQ +fezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYD +VR0jBBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0G +CSqGSIb3DQEBBQUAA4IBAQAo0uCG1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X1 +7caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/CyvwbZ71q+s2IhtNerNXxTPqYn +8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3ghUJGooWMNjs +ydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT +ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/ +2TYcuiUaUj0a7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw +NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv +b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD +VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2 +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F +VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1 +7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X +Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+ +/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs +81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm +dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe +Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu +sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4 +pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs +slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ +arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD +VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG +9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl +dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj +TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed +Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7 +Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI +OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7 +vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW +t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn +HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx +SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDNjCCAp+gAwIBAgIQNhIilsXjOKUgodJfTNcJVDANBgkqhkiG9w0BAQUFADCB +zjELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJ +Q2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE +CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhh +d3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl +cnZlckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIxMDEwMTIzNTk1OVow +gc4xCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcT +CUNhcGUgVG93bjEdMBsGA1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNV +BAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRo +YXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1z +ZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2 +aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560 +ZXUCTe/LCaIhUdib0GfQug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j ++ao6hnO2RlNYyIkFvYMRuHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/ +BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQBlkKyID1bZ5jA01CbH0FDxkt5r1DmI +CSLGpmODA/eZd9iy5Ri4XWPz1HP7bJyZePFLeH0ZJMMrAoT4vCLZiiLXoPxx7JGH +IPG47LHlVYCsPVLIOQ7C8MAFT9aCdYy9X9LcdpoFEsmvcsPcJX6kTY4XpeCHf+Ga +WuFg3GQjPEIuTQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw +NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j +LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG +A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs +W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta +3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk +6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6 +Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J +NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP +r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU +DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz +YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2 +/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/ +LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7 +jVaMaA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp +IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi +BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw +MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig +YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v +dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/ +BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6 +papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K +DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3 +KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox +XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCB +rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV +BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0wODA0MDIwMDAwMDBa +Fw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3Rl +LCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9u +MTgwNgYDVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXpl +ZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndm +gcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2AtP0LMqmsywCPLLEHd5N/8 +YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC+BsUa0Lf +b1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS9 +9irY7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2S +zhkGcuYMXDhpxwTWvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUk +OQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV +HQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJKoZIhvcNAQELBQADggEBABpA +2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweKA3rD6z8KLFIW +oCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c +KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM +m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu +MdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBF +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQL +ExNUcnVzdGlzIEZQUyBSb290IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTEx +MzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1RydXN0aXMgTGltaXRlZDEc +MBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQRUN+ +AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihH +iTHcDnlkH5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjj +vSkCqPoc4Vu5g6hBSLwacY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA +0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zto3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlB +OrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEAAaNTMFEwDwYDVR0TAQH/ +BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAdBgNVHQ4E +FgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01 +GX2cGE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmW +zaD+vkAMXBJV+JOCyinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP4 +1BIy+Q7DsdwyhEQsb8tGD+pmQQ9P8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZE +f1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHVl/9D7S3B2l0pKoU/rGXuhg8F +jZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYliB6XzCGcKQEN +ZetX2fNXlrtIzYE= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd +AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC +FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi +1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq +jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ +wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ +WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy +NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC +uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw +IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 +g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP +BSeOE6Fuwg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN +8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ +RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 +hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 +ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM +EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 +A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy +WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ +1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 +6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT +91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p +TpPDpFQUWw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRS +MRgwFgYDVQQHDA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJp +bGltc2VsIHZlIFRla25vbG9qaWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSw +VEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ryb25payB2ZSBLcmlwdG9sb2ppIEFy +YcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNVBAsMGkthbXUgU2Vy +dGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUgS8O2 +ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAe +Fw0wNzA4MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIx +GDAWBgNVBAcMD0dlYnplIC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmls +aW1zZWwgdmUgVGVrbm9sb2ppayBBcmHFn3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBU +QUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZlIEtyaXB0b2xvamkgQXJh +xZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2FtdSBTZXJ0 +aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7Zr +IFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4h +gb46ezzb8R1Sf1n68yJMlaCQvEhOEav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yK +O7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1xnnRFDDtG1hba+818qEhTsXO +fJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR6Oqeyjh1jmKw +lZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL +hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQID +AQABo0IwQDAdBgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmP +NOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4N5EY3ATIZJkrGG2AA1nJrvhY0D7t +wyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLTy9LQQfMmNkqblWwM +7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYhLBOh +gLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5n +oN+J1q2MdqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUs +yZyQ2uypQjyttgI= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOc +UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx +c8SxMQswCQYDVQQGDAJUUjEPMA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykg +MjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8 +dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMxMDI3MTdaFw0xNTAz +MjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsgU2Vy +dGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYD +VQQHDAZBTktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kg +xLBsZXRpxZ9pbSB2ZSBCaWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEu +xZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7 +XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GXyGl8hMW0kWxsE2qkVa2k +heiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8iSi9BB35J +YbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5C +urKZ8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1 +JuTm5Rh8i27fbMx4W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51 +b0dewQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV +9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46sWrv7/hg0Uw2ZkUd82YCdAR7 +kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxEq8Sn5RTOPEFh +fEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy +B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdA +aLX/7KfS0zgYnNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKS +RGQDJereW26fyfJOrN3H +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOc +UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx +c8SxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xS +S1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kg +SGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4XDTA3MTIyNTE4Mzcx +OVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxla3Ry +b25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMC +VFIxDzANBgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDE +sGxldGnFn2ltIHZlIEJpbGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7F +ni4gKGMpIEFyYWzEsWsgMjAwNzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9NYvDdE3ePYakqtdTyuTFY +KTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQvKUmi8wUG ++7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveG +HtyaKhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6P +IzdezKKqdfcYbwnTrqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M +733WB2+Y8a+xwXrXgTW4qhe04MsCAwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHk +Yb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/sPx+EnWVUXKgW +AkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I +aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5 +mxRZNTZPz/OOXl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsa +XRik7r4EW5nVcV9VZWRi1aKbBFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZ +qxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAKpoRq0Tl9 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOc +UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx +c8SxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xS +S1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kg +SGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcNMDUxMTA3MTAwNzU3 +WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVrdHJv +bmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJU +UjEPMA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSw +bGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWe +LiAoYykgS2FzxLFtIDIwMDUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqeLCDe2JAOCtFp0if7qnef +J1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKIx+XlZEdh +R3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJ +Qv2gQrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGX +JHpsmxcPbe9TmJEr5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1p +zpwACPI2/z7woQ8arBT9pmAPAgMBAAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58S +Fq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/nttRbj2hWyfIvwq +ECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4 +Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFz +gw2lGh1uEpJ+hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotH +uFEJjOp9zYhys2AzsfAKRO8P9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LS +y3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5UrbnBEI= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx +EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT +VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 +NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT +B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF +10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz +0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh +MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH +zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc +46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 +yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi +laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP +oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA +BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE +qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm +4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL +1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF +H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo +RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ +nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh +15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW +6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW +nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j +wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz +aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy +KwbQBM0= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES +MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU +V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz +WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO +LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE +AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH +K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX +RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z +rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx +3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq +hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC +MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls +XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D +lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn +aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ +YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCED9pHoGc8JpK83P/uUii5N0wDQYJKoZIhvcNAQEFBQAwXzELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz +cyAxIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 +MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV +BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAxIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN +ADCBiQKBgQDlGb9to1ZhLZlIcfZn3rmN67eehoAKkQ76OCWvRoiC5XOooJskXQ0f +zGVuDLDQVoQYh5oGmxChc9+0WDlrbsH2FdWoqD+qEgaNMax/sDTXjzRniAnNFBHi +TkVWaR94AoDa3EeRKbs2yWNcxeDXLYd7obcysHswuiovMaruo2fa2wIDAQABMA0G +CSqGSIb3DQEBBQUAA4GBAFgVKTk8d6PaXCUDfGD67gmZPCcQcMgMCeazh88K4hiW +NWLMv5sneYlfycQJ9M61Hd8qveXbhpxoJeUwfLaJFf5n0a3hUKw8fGJLj7qE1xIV +Gx/KXQ/BUpQqEZnae88MNhPVNdwQGVnqlMEAv3WP2fr9dgTbYruQagPZRjXZ+Hxb +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz +cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 +MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV +BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN +ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE +BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is +I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G +CSqGSIb3DQEBBQUAA4GBABByUqkFFBkyCEHwxWsKzH4PIRnN5GfcX6kb5sroc50i +2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWXbj9T/UWZYB2oK0z5XqcJ +2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/D/xwzoiQ +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp +U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg +SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln +biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm +GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve +fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ +aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj +aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW +kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC +4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga +FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB +vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W +ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX +MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0 +IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y +IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh +bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF +9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH +H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H +LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN +/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT +rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw +WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs +exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4 +sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+ +seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz +4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+ +BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR +lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3 +7M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMx +IDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxs +cyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9v +dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDcxMjEzMTcwNzU0WhcNMjIxMjE0 +MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdl +bGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQD +DC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+r +WxxTkqxtnt3CxC5FlAM1iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjU +Dk/41itMpBb570OYj7OeUt9tkTmPOL13i0Nj67eT/DBMHAGTthP796EfvyXhdDcs +HqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8bJVhHlfXBIEyg1J55oNj +z7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiBK0HmOFaf +SZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/Slwxl +AgMBAAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqG +KGh0dHA6Ly9jcmwucGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0P +AQH/BAQDAgHGMB0GA1UdDgQWBBQmlRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0j +BIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGBi6SBiDCBhTELMAkGA1UEBhMC +VVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNX +ZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEB +ALkVsUSRzCPIK0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd +/ZDJPHV3V3p9+N701NX3leZ0bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pB +A4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSljqHyita04pO2t/caaH/+Xc/77szWn +k4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+esE2fDbbFwRnzVlhE9 +iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJtylv +2G0xffX8oRAHh84vWdw+WNs= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFdjCCA16gAwIBAgIQXmjWEXGUY1BWAGjzPsnFkTANBgkqhkiG9w0BAQUFADBV +MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxKjAoBgNV +BAMTIUNlcnRpZmljYXRpb24gQXV0aG9yaXR5IG9mIFdvU2lnbjAeFw0wOTA4MDgw +MTAwMDFaFw0zOTA4MDgwMTAwMDFaMFUxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFX +b1NpZ24gQ0EgTGltaXRlZDEqMCgGA1UEAxMhQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgb2YgV29TaWduMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvcqN +rLiRFVaXe2tcesLea9mhsMMQI/qnobLMMfo+2aYpbxY94Gv4uEBf2zmoAHqLoE1U +fcIiePyOCbiohdfMlZdLdNiefvAA5A6JrkkoRBoQmTIPJYhTpA2zDxIIFgsDcScc +f+Hb0v1naMQFXQoOXXDX2JegvFNBmpGN9J42Znp+VsGQX+axaCA2pIwkLCxHC1l2 +ZjC1vt7tj/id07sBMOby8w7gLJKA84X5KIq0VC6a7fd2/BVoFutKbOsuEo/Uz/4M +x1wdC34FMr5esAkqQtXJTpCzWQ27en7N1QhatH/YHGkR+ScPewavVIMYe+HdVHpR +aG53/Ma/UkpmRqGyZxq7o093oL5d//xWC0Nyd5DKnvnyOfUNqfTq1+ezEC8wQjch +zDBwyYaYD8xYTYO7feUapTeNtqwylwA6Y3EkHp43xP901DfA4v6IRmAR3Qg/UDar +uHqklWJqbrDKaiFaafPz+x1wOZXzp26mgYmhiMU7ccqjUu6Du/2gd/Tkb+dC221K +mYo0SLwX3OSACCK28jHAPwQ+658geda4BmRkAjHXqc1S+4RFaQkAKtxVi8QGRkvA +Sh0JWzko/amrzgD5LkhLJuYwTKVYyrREgk/nkR4zw7CT/xH8gdLKH3Ep3XZPkiWv +HYG3Dy+MwwbMLyejSuQOmbp8HkUff6oZRZb9/D0CAwEAAaNCMEAwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOFmzw7R8bNLtwYgFP6H +EtX2/vs+MA0GCSqGSIb3DQEBBQUAA4ICAQCoy3JAsnbBfnv8rWTjMnvMPLZdRtP1 +LOJwXcgu2AZ9mNELIaCJWSQBnfmvCX0KI4I01fx8cpm5o9dU9OpScA7F9dY74ToJ +MuYhOZO9sxXqT2r09Ys/L3yNWC7F4TmgPsc9SnOeQHrAK2GpZ8nzJLmzbVUsWh2e +JXLOC62qx1ViC777Y7NhRCOjy+EaDveaBk3e1CNOIZZbOVtXHS9dCF4Jef98l7VN +g64N1uajeeAz0JmWAjCnPv/So0M/BVoG6kQC2nz4SNAzqfkHx5Xh9T71XXG68pWp +dIhhWeO/yloTunK0jF02h+mmxTwTv97QRCbut+wucPrXnbes5cVAWubXbHssw1ab +R80LzvobtCHXt2a49CUwi1wNuepnsvRtrtWhnk/Yn+knArAdBtaP4/tIEp9/EaEQ +PkxROpaw0RPxx9gmrjrKkcRpnd8BKWRRb2jaFOwIQZeQjdCygPLPwj2/kWjFgGce +xGATVdVhmVd8upUPYUk6ynW8yQqTP2cOEvIo4jEbwFcW3wh8GcF+Dx+FHgo2fFt+ +J7x6v+Db9NpSvd4MVHAxkUOVyLzwPt0JfjBkUO1/AaQzZ01oT74V77D2AhGiGxMl +OtzCWfHjXEa7ZywCRuoeSKbmW9m1vFGikpbbqsY3Iqb+zCB0oy2pLmvLwIIRIbWT +ee5Ehr7XHuQe+w== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFWDCCA0CgAwIBAgIQUHBrzdgT/BtOOzNy0hFIjTANBgkqhkiG9w0BAQsFADBG +MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxGzAZBgNV +BAMMEkNBIOayg+mAmuagueivgeS5pjAeFw0wOTA4MDgwMTAwMDFaFw0zOTA4MDgw +MTAwMDFaMEYxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRl +ZDEbMBkGA1UEAwwSQ0Eg5rKD6YCa5qC56K+B5LmmMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA0EkhHiX8h8EqwqzbdoYGTufQdDTc7WU1/FDWiD+k8H/r +D195L4mx/bxjWDeTmzj4t1up+thxx7S8gJeNbEvxUNUqKaqoGXqW5pWOdO2XCld1 +9AXbbQs5uQF/qvbW2mzmBeCkTVL829B0txGMe41P/4eDrv8FAxNXUDf+jJZSEExf +v5RxadmWPgxDT74wwJ85dE8GRV2j1lY5aAfMh09Qd5Nx2UQIsYo06Yms25tO4dnk +UkWMLhQfkWsZHWgpLFbE4h4TV2TwYeO5Ed+w4VegG63XX9Gv2ystP9Bojg/qnw+L +NVgbExz03jWhCl3W6t8Sb8D7aQdGctyB9gQjF+BNdeFyb7Ao65vh4YOhn0pdr8yb ++gIgthhid5E7o9Vlrdx8kHccREGkSovrlXLp9glk3Kgtn3R46MGiCWOc76DbT52V +qyBPt7D3h1ymoOQ3OMdc4zUPLK2jgKLsLl3Az+2LBcLmc272idX10kaO6m1jGx6K +yX2m+Jzr5dVjhU1zZmkR/sgO9MHHZklTfuQZa/HpelmjbX7FF+Ynxu8b22/8DU0G +AbQOXDBGVWCvOGU6yke6rCzMRh+yRpY/8+0mBe53oWprfi1tWFxK1I5nuPHa1UaK +J/kR8slC/k7e3x9cxKSGhxYzoacXGKUN5AXlK8IrC6KVkLn9YDxOiT7nnO4fuwEC +AwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFOBNv9ybQV0T6GTwp+kVpOGBwboxMA0GCSqGSIb3DQEBCwUAA4ICAQBqinA4 +WbbaixjIvirTthnVZil6Xc1bL3McJk6jfW+rtylNpumlEYOnOXOvEESS5iVdT2H6 +yAa+Tkvv/vMx/sZ8cApBWNromUuWyXi8mHwCKe0JgOYKOoICKuLJL8hWGSbueBwj +/feTZU7n85iYr83d2Z5AiDEoOqsuC7CsDCT6eiaY8xJhEPRdF/d+4niXVOKM6Cm6 +jBAyvd0zaziGfjk9DgNyp115j0WKWa5bIW4xRtVZjc8VX90xJc/bYNaBRHIpAlf2 +ltTW/+op2znFuCyKGo3Oy+dCMYYFaA6eFN0AkLppRQjbbpCBhqcqBT/mhDn4t/lX +X0ykeVoQDF7Va/81XwVRHmyjdanPUIPTfPRm94KNPQx96N97qA4bLJyuQHCH2u2n +FoJavjVsIE4iYdm8UXrNemHcSxH5/mc0zy4EZmFcV5cjjPOGG0jfKq+nwf/Yjj4D +u9gqsPoUJbJRa4ZDhS4HIxaAjUz7tGM7zMN07RujHv41D198HRaG9Q7DlfEvr10l +O1Hm13ZBONFLAzkopR6RctR9q5czxNM+4Gm2KHmgCY0c0f9BckgG/Jou5yD5m6Le +ie2uPAmvylezkolwQOQvT8Jwg0DXJCxr5wkf09XHwQj02w47HAcLQxGEIYbpgNR1 +2KvxAmLBsX5VYc8T1yaw15zLKYs4SgsOkI26oQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEMDCCA5mgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBxzELMAkGA1UEBhMCVVMx +FzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMR8wHQYDVQQHExZSZXNlYXJjaCBUcmlh +bmdsZSBQYXJrMRYwFAYDVQQKEw1SZWQgSGF0LCBJbmMuMSEwHwYDVQQLExhSZWQg +SGF0IE5ldHdvcmsgU2VydmljZXMxIzAhBgNVBAMTGlJITlMgQ2VydGlmaWNhdGUg +QXV0aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9yaG5zQHJlZGhhdC5jb20wHhcNMDAw +ODIzMjI0NTU1WhcNMDMwODI4MjI0NTU1WjCBxzELMAkGA1UEBhMCVVMxFzAVBgNV +BAgTDk5vcnRoIENhcm9saW5hMR8wHQYDVQQHExZSZXNlYXJjaCBUcmlhbmdsZSBQ +YXJrMRYwFAYDVQQKEw1SZWQgSGF0LCBJbmMuMSEwHwYDVQQLExhSZWQgSGF0IE5l +dHdvcmsgU2VydmljZXMxIzAhBgNVBAMTGlJITlMgQ2VydGlmaWNhdGUgQXV0aG9y +aXR5MR4wHAYJKoZIhvcNAQkBFg9yaG5zQHJlZGhhdC5jb20wgZ8wDQYJKoZIhvcN +AQEBBQADgY0AMIGJAoGBAMBoKxIw4iEtIsZycVu/F6CTEOmb48mNOy2sxLuVO+DK +VTLclcIQswSyUfvohWEWNKW0HWdcp3f08JLatIuvlZNi82YprsCIt2SEDkiQYPhg +PgB/VN0XpqwY4ELefL6Qgff0BYUKCMzV8p/8JIt3pT3pSKnvDztjo/6mg0zo3At3 +AgMBAAGjggEoMIIBJDAdBgNVHQ4EFgQUVBXNnyz37A0f0qi+TAesiD77mwowgfQG +A1UdIwSB7DCB6YAUVBXNnyz37A0f0qi+TAesiD77mwqhgc2kgcowgccxCzAJBgNV +BAYTAlVTMRcwFQYDVQQIEw5Ob3J0aCBDYXJvbGluYTEfMB0GA1UEBxMWUmVzZWFy +Y2ggVHJpYW5nbGUgUGFyazEWMBQGA1UEChMNUmVkIEhhdCwgSW5jLjEhMB8GA1UE +CxMYUmVkIEhhdCBOZXR3b3JrIFNlcnZpY2VzMSMwIQYDVQQDExpSSE5TIENlcnRp +ZmljYXRlIEF1dGhvcml0eTEeMBwGCSqGSIb3DQEJARYPcmhuc0ByZWRoYXQuY29t +ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAkwGIiGdnkYye0BIU +kHESh1UK8lIbrfLTBx2vcJm7sM2AI8ntK3PpY7HQs4xgxUJkpsGVVpDFNQYDWPWO +K9n5qaAQqZn3FUKSpVDXEQfxAtXgcORVbirOJfhdzQsvEGH49iBCzMOJ+IpPgiQS +zzl/IagsjVKXUsX3X0KlhwlmsMw= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIID7jCCA1egAwIBAgIBADANBgkqhkiG9w0BAQQFADCBsTELMAkGA1UEBhMCVVMx +FzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMRAwDgYDVQQHEwdSYWxlaWdoMRYwFAYD +VQQKEw1SZWQgSGF0LCBJbmMuMRgwFgYDVQQLEw9SZWQgSGF0IE5ldHdvcmsxIjAg +BgNVBAMTGVJITiBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEW +EnJobi1ub2NAcmVkaGF0LmNvbTAeFw0wMjA5MDUyMDQ1MTZaFw0wNzA5MDkyMDQ1 +MTZaMIGxMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExEDAO +BgNVBAcTB1JhbGVpZ2gxFjAUBgNVBAoTDVJlZCBIYXQsIEluYy4xGDAWBgNVBAsT +D1JlZCBIYXQgTmV0d29yazEiMCAGA1UEAxMZUkhOIENlcnRpZmljYXRlIEF1dGhv +cml0eTEhMB8GCSqGSIb3DQEJARYScmhuLW5vY0ByZWRoYXQuY29tMIGfMA0GCSqG +SIb3DQEBAQUAA4GNADCBiQKBgQCzFrfF9blpUR/NtD1wz2BXhaQqp10oIg7sGeKS +90iXpqYfUZWDEY+amKKQ4MtKJBmUqIpLiLQGbM531xU7PM1mg88jHQ28CgzLH8tA ++/PZ/iq0hSx7yaH+849oHfISsaQWGc4PuJqc2bxfSWKylZPOXS7deTzxW6a3orU5 +DY4SMQIDAQABo4IBEjCCAQ4wHQYDVR0OBBYEFH8bZKEuAsWofbjRsYsGnaOpUGOS +MIHeBgNVHSMEgdYwgdOAFH8bZKEuAsWofbjRsYsGnaOpUGOSoYG3pIG0MIGxMQsw +CQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExEDAOBgNVBAcTB1Jh +bGVpZ2gxFjAUBgNVBAoTDVJlZCBIYXQsIEluYy4xGDAWBgNVBAsTD1JlZCBIYXQg +TmV0d29yazEiMCAGA1UEAxMZUkhOIENlcnRpZmljYXRlIEF1dGhvcml0eTEhMB8G +CSqGSIb3DQEJARYScmhuLW5vY0ByZWRoYXQuY29tggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEEBQADgYEAKE1C5TQi3caGYwR1UmcXRXLyOyErRVlyc/dZNp1X +Q8bclA8O/xNcT1A3hbLkwh81n3T051P7oQa4Oc7kCoZ7XyhdxxGeEqXWuWzpGAnV +8ELnVLWRniOtEnqqcnw5PIP4daR7A5L/KtTFdhkS+rQ7sIkslYwBkA3YugYFYQCs +ldo= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIID7jCCA1egAwIBAgIBADANBgkqhkiG9w0BAQQFADCBsTELMAkGA1UEBhMCVVMx +FzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMRAwDgYDVQQHEwdSYWxlaWdoMRYwFAYD +VQQKEw1SZWQgSGF0LCBJbmMuMRgwFgYDVQQLEw9SZWQgSGF0IE5ldHdvcmsxIjAg +BgNVBAMTGVJITiBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEW +EnJobi1ub2NAcmVkaGF0LmNvbTAeFw0wMzA4MjkwMjEwNTVaFw0xMzA4MjYwMjEw +NTVaMIGxMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExEDAO +BgNVBAcTB1JhbGVpZ2gxFjAUBgNVBAoTDVJlZCBIYXQsIEluYy4xGDAWBgNVBAsT +D1JlZCBIYXQgTmV0d29yazEiMCAGA1UEAxMZUkhOIENlcnRpZmljYXRlIEF1dGhv +cml0eTEhMB8GCSqGSIb3DQEJARYScmhuLW5vY0ByZWRoYXQuY29tMIGfMA0GCSqG +SIb3DQEBAQUAA4GNADCBiQKBgQC/YWPrPYsrRUjmwvt80iEhuOyQk0EwfCyNedUU +6Q5+P+/WCpsKpgJSAS0mlqTtvameqggDwWEKQYDqrnTMYSbQBZFVPmYUoiCz1p1x +DKt3zPTwEbUlM4pOIpoQNmf6EW1Idjof0uNEe4lmvrSF+y+mqhP6mm3JuxjEBK9P +FWmJmwIDAQABo4IBEjCCAQ4wHQYDVR0OBBYEFGlEJwXcLu2l9IHE13hF50Rd+IdH +MIHeBgNVHSMEgdYwgdOAFGlEJwXcLu2l9IHE13hF50Rd+IdHoYG3pIG0MIGxMQsw +CQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExEDAOBgNVBAcTB1Jh +bGVpZ2gxFjAUBgNVBAoTDVJlZCBIYXQsIEluYy4xGDAWBgNVBAsTD1JlZCBIYXQg +TmV0d29yazEiMCAGA1UEAxMZUkhOIENlcnRpZmljYXRlIEF1dGhvcml0eTEhMB8G +CSqGSIb3DQEJARYScmhuLW5vY0ByZWRoYXQuY29tggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEEBQADgYEAI8nKB59eljmD4E7a3UeEMMrU1TiG+d6Ig8osRyY2 +q/QUHigp3n0QSl6RPlqZBwypLuP7eERJxTLW6HqX/ynQM64munYGfnmXFwxPLSqL +iqxBWa7pxFUtuYjfm3tB+DIu7snAWeIwV143RynALXgz086jK9yE2r87Lku2s7ZO +noA= +-----END CERTIFICATE----- + diff --git a/libraries/fof/download/adapter/curl.php b/libraries/fof/download/adapter/curl.php index 3f6af8f92c8f5..70035bb65b129 100644 --- a/libraries/fof/download/adapter/curl.php +++ b/libraries/fof/download/adapter/curl.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage dispatcher - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -77,7 +77,7 @@ public function downloadAndReturn($url, $from = null, $to = null, array $params CURLOPT_BINARYTRANSFER => 1, CURLOPT_RETURNTRANSFER => 1, CURLOPT_FOLLOWLOCATION => 1, - CURLOPT_CAINFO => JPATH_LIBRARIES . 'joomla/http/transport/cacert.pem', + CURLOPT_CAINFO => __DIR__ . '/cacert.pem', CURLOPT_HEADERFUNCTION => array($this, 'reponseHeaderCallback') ); @@ -150,7 +150,7 @@ public function getFileSize($url) curl_setopt($ch, CURLOPT_HEADER, true ); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true ); @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true ); - @curl_setopt($ch, CURLOPT_CAINFO, JPATH_LIBRARIES . 'joomla/http/transport/cacert.pem'); + @curl_setopt($ch, CURLOPT_CAINFO, __DIR__ . '/cacert.pem'); $data = curl_exec($ch); curl_close($ch); diff --git a/libraries/fof/download/adapter/fopen.php b/libraries/fof/download/adapter/fopen.php index ffbef825651da..aa9cb3df9701a 100644 --- a/libraries/fof/download/adapter/fopen.php +++ b/libraries/fof/download/adapter/fopen.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage dispatcher - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -81,7 +81,7 @@ public function downloadAndReturn($url, $from = null, $to = null, array $params ), 'ssl' => array( 'verify_peer' => true, - 'cafile' => JPATH_LIBRARIES . 'joomla/http/transport/cacert.pem', + 'cafile' => __DIR__ . '/cacert.pem', 'verify_depth' => 5, ) ); @@ -99,7 +99,7 @@ public function downloadAndReturn($url, $from = null, $to = null, array $params ), 'ssl' => array( 'verify_peer' => true, - 'cafile' => JPATH_LIBRARIES . 'joomla/http/transport/cacert.pem', + 'cafile' => __DIR__ . '/cacert.pem', 'verify_depth' => 5, ) ); diff --git a/libraries/fof/download/download.php b/libraries/fof/download/download.php index 0df518fe48cca..3d346b95ef640 100644 --- a/libraries/fof/download/download.php +++ b/libraries/fof/download/download.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage dispatcher - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -63,7 +63,7 @@ public function __construct() } // Load the language strings - FOFPlatform::getInstance()->loadTranslations('lib_fof'); + FOFPlatform::getInstance()->loadTranslations('lib_f0f'); } /** diff --git a/libraries/fof/download/interface.php b/libraries/fof/download/interface.php index 9958b8d019d76..b5089fa578c1d 100644 --- a/libraries/fof/download/interface.php +++ b/libraries/fof/download/interface.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage dispatcher - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/encrypt/aes.php b/libraries/fof/encrypt/aes.php index 035986035f50b..a0d6ede1fb9c5 100644 --- a/libraries/fof/encrypt/aes.php +++ b/libraries/fof/encrypt/aes.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage encrypt - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -17,59 +17,78 @@ */ class FOFEncryptAes { - /** @var string The AES cipher to use (this is an mcrypt identifier, not the bit strength) */ - private $_cipherType = 0; - - /** @var string Cipher mode. Can be CBC or ECB. We recommend using CBC */ - private $_cipherMode = 0; + /** + * The cipher key. + * + * @var string + */ + private $key = ''; - /** @var string The cipher key (password) */ - private $_keyString = ''; + /** + * The AES encryption adapter in use. + * + * @var FOFEncryptAesInterface + */ + private $adapter; /** - * Initialise the AES encryption object + * Initialise the AES encryption object. * - * @param string $key The encryption key (password). It can be a raw key (32 bytes) or a passphrase. - * @param int $strength Bit strength (128, 192 or 256) - * @param string $mode Ecnryption mode. Can be ebc or cbc. We recommend using cbc. + * Note: If the key is not 16 bytes this class will do a stupid key expansion for legacy reasons (produce the + * SHA-256 of the key string and throw away half of it). + * + * @param string $key The encryption key (password). It can be a raw key (16 bytes) or a passphrase. + * @param int $strength Bit strength (128, 192 or 256) – ALWAYS USE 128 BITS. THIS PARAMETER IS DEPRECATED. + * @param string $mode Encryption mode. Can be ebc or cbc. We recommend using cbc. + * @param FOFUtilsPhpfunc $phpfunc For testing */ - public function __construct($key, $strength = 256, $mode = 'cbc') + public function __construct($key, $strength = 128, $mode = 'cbc', FOFUtilsPhpfunc $phpfunc = null) { - $this->_keyString = $key; + $this->adapter = new FOFEncryptAesOpenssl(); - switch ($strength) + if (!$this->adapter->isSupported($phpfunc)) { - case 256: - default: - $this->_cipherType = MCRYPT_RIJNDAEL_256; - break; + $this->adapter = new FOFEncryptAesMcrypt(); + } - case 192: - $this->_cipherType = MCRYPT_RIJNDAEL_192; - break; + $this->adapter->setEncryptionMode($mode, $strength); + $this->setPassword($key, true); + } - case 128: - $this->_cipherType = MCRYPT_RIJNDAEL_128; - break; - } + /** + * Sets the password for this instance. + * + * WARNING: Do not use the legacy mode, it's insecure + * + * @param string $password The password (either user-provided password or binary encryption key) to use + * @param bool $legacyMode True to use the legacy key expansion. We recommend against using it. + */ + public function setPassword($password, $legacyMode = false) + { + $this->key = $password; - switch (strtoupper($mode)) + $passLength = strlen($password); + + if (function_exists('mb_strlen')) { - case 'ECB': - $this->_cipherMode = MCRYPT_MODE_ECB; - break; + $passLength = mb_strlen($password, 'ASCII'); + } - case 'CBC': - $this->_cipherMode = MCRYPT_MODE_CBC; - break; + // Legacy mode was doing something stupid, requiring a key of 32 bytes. DO NOT USE LEGACY MODE! + if ($legacyMode && ($passLength != 32)) + { + // Legacy mode: use the sha256 of the password + $this->key = hash('sha256', $password, true); + // We have to trim or zero pad the password (we end up throwing half of it away in Rijndael-128 / AES...) + $this->key = $this->adapter->resizeKey($this->key, $this->adapter->getBlockSize()); } } /** * Encrypts a string using AES * - * @param string $stringToEncrypt The plaintext to encrypt - * @param bool $base64encoded Should I Base64-encode the result? + * @param string $stringToEncrypt The plaintext to encrypt + * @param bool $base64encoded Should I Base64-encode the result? * * @return string The cryptotext. Please note that the first 16 bytes of * the raw string is the IV (initialisation vector) which @@ -77,34 +96,12 @@ public function __construct($key, $strength = 256, $mode = 'cbc') */ public function encryptString($stringToEncrypt, $base64encoded = true) { - if (strlen($this->_keyString) != 32) - { - $key = hash('sha256', $this->_keyString, true); - } - else - { - $key = $this->_keyString; - } - - // Set up the IV (Initialization Vector) - $iv_size = mcrypt_get_iv_size($this->_cipherType, $this->_cipherMode); - $iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM); - - if (empty($iv)) - { - $iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_RANDOM); - } + $blockSize = $this->adapter->getBlockSize(); + $randVal = new FOFEncryptRandval(); + $iv = $randVal->generate($blockSize); - if (empty($iv)) - { - $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND); - } - - // Encrypt the data - $cipherText = mcrypt_encrypt($this->_cipherType, $key, $stringToEncrypt, $this->_cipherMode, $iv); - - // Prepend the IV to the ciphertext - $cipherText = $iv . $cipherText; + $key = $this->getExpandedKey($blockSize, $iv); + $cipherText = $this->adapter->encrypt($stringToEncrypt, $key, $iv); // Optionally pass the result through Base64 encoding if ($base64encoded) @@ -119,36 +116,26 @@ public function encryptString($stringToEncrypt, $base64encoded = true) /** * Decrypts a ciphertext into a plaintext string using AES * - * @param string $stringToDecrypt The ciphertext to decrypt. The first 16 bytes of the raw string must contain the IV (initialisation vector). - * @param bool $base64encoded Should I Base64-decode the data before decryption? + * @param string $stringToDecrypt The ciphertext to decrypt. The first 16 bytes of the raw string must contain + * the IV (initialisation vector). + * @param bool $base64encoded Should I Base64-decode the data before decryption? * * @return string The plain text string */ public function decryptString($stringToDecrypt, $base64encoded = true) { - if (strlen($this->_keyString) != 32) - { - $key = hash('sha256', $this->_keyString, true); - } - else - { - $key = $this->_keyString; - } - if ($base64encoded) { $stringToDecrypt = base64_decode($stringToDecrypt); } - // Calculate the IV size - $iv_size = mcrypt_get_iv_size($this->_cipherType, $this->_cipherMode); - // Extract IV - $iv = substr($stringToDecrypt, 0, $iv_size); - $stringToDecrypt = substr($stringToDecrypt, $iv_size); + $iv_size = $this->adapter->getBlockSize(); + $iv = substr($stringToDecrypt, 0, $iv_size); + $key = $this->getExpandedKey($iv_size, $iv); // Decrypt the data - $plainText = mcrypt_decrypt($this->_cipherType, $key, $stringToDecrypt, $this->_cipherMode, $iv); + $plainText = $this->adapter->decrypt($stringToDecrypt, $key); return $plainText; } @@ -156,84 +143,130 @@ public function decryptString($stringToDecrypt, $base64encoded = true) /** * Is AES encryption supported by this PHP installation? * + * @param FOFUtilsPhpfunc $phpfunc + * * @return boolean */ - public static function isSupported() + public static function isSupported(FOFUtilsPhpfunc $phpfunc = null) { - if (!function_exists('mcrypt_get_key_size')) + if (!is_object($phpfunc) || !($phpfunc instanceof $phpfunc)) { - return false; + $phpfunc = new FOFUtilsPhpfunc(); } - if (!function_exists('mcrypt_get_iv_size')) + $adapter = new FOFEncryptAesMcrypt(); + + if (!$adapter->isSupported($phpfunc)) { - return false; + $adapter = new FOFEncryptAesOpenssl(); } - if (!function_exists('mcrypt_create_iv')) + if (!$adapter->isSupported($phpfunc)) { return false; } - if (!function_exists('mcrypt_encrypt')) + if (!$phpfunc->function_exists('base64_encode')) { return false; } - if (!function_exists('mcrypt_decrypt')) + if (!$phpfunc->function_exists('base64_decode')) { return false; } - if (!function_exists('mcrypt_list_algorithms')) + if (!$phpfunc->function_exists('hash_algos')) { return false; } - if (!function_exists('hash')) + $algorightms = $phpfunc->hash_algos(); + + if (!in_array('sha256', $algorightms)) { return false; } - if (!function_exists('hash_algos')) + return true; + } + + /** + * @param $blockSize + * @param $iv + * + * @return string + */ + public function getExpandedKey($blockSize, $iv) + { + $key = $this->key; + $passLength = strlen($key); + + if (function_exists('mb_strlen')) { - return false; + $passLength = mb_strlen($key, 'ASCII'); } - if (!function_exists('base64_encode')) + if ($passLength != $blockSize) { - return false; + $iterations = 1000; + $salt = $this->adapter->resizeKey($iv, 16); + $key = hash_pbkdf2('sha256', $this->key, $salt, $iterations, $blockSize, true); } - if (!function_exists('base64_decode')) + return $key; + } +} + +if (!function_exists('hash_pbkdf2')) +{ + function hash_pbkdf2($algo, $password, $salt, $count, $length = 0, $raw_output = false) + { + if (!in_array(strtolower($algo), hash_algos())) { - return false; + trigger_error(__FUNCTION__ . '(): Unknown hashing algorithm: ' . $algo, E_USER_WARNING); } - $algorightms = mcrypt_list_algorithms(); + if (!is_numeric($count)) + { + trigger_error(__FUNCTION__ . '(): expects parameter 4 to be long, ' . gettype($count) . ' given', E_USER_WARNING); + } - if (!in_array('rijndael-128', $algorightms)) + if (!is_numeric($length)) { - return false; + trigger_error(__FUNCTION__ . '(): expects parameter 5 to be long, ' . gettype($length) . ' given', E_USER_WARNING); } - if (!in_array('rijndael-192', $algorightms)) + if ($count <= 0) { - return false; + trigger_error(__FUNCTION__ . '(): Iterations must be a positive integer: ' . $count, E_USER_WARNING); } - if (!in_array('rijndael-256', $algorightms)) + if ($length < 0) { - return false; + trigger_error(__FUNCTION__ . '(): Length must be greater than or equal to 0: ' . $length, E_USER_WARNING); } - $algorightms = hash_algos(); + $output = ''; + $block_count = $length ? ceil($length / strlen(hash($algo, '', $raw_output))) : 1; - if (!in_array('sha256', $algorightms)) + for ($i = 1; $i <= $block_count; $i++) { - return false; + $last = $xorsum = hash_hmac($algo, $salt . pack('N', $i), $password, true); + + for ($j = 1; $j < $count; $j++) + { + $xorsum ^= ($last = hash_hmac($algo, $last, $password, true)); + } + + $output .= $xorsum; } - return true; + if (!$raw_output) + { + $output = bin2hex($output); + } + + return $length ? substr($output, 0, $length) : $output; } } diff --git a/libraries/fof/encrypt/aes/abstract.php b/libraries/fof/encrypt/aes/abstract.php new file mode 100644 index 0000000000000..981f653b7b052 --- /dev/null +++ b/libraries/fof/encrypt/aes/abstract.php @@ -0,0 +1,88 @@ + $size) + { + if (function_exists('mb_substr')) + { + return mb_substr($key, 0, $size, 'ASCII'); + } + + return substr($key, 0, $size); + } + + return $key . str_repeat("\0", ($size - $keyLength)); + } + + /** + * Returns null bytes to append to the string so that it's zero padded to the specified block size + * + * @param string $string The binary string which will be zero padded + * @param int $blockSize The block size + * + * @return string The zero bytes to append to the string to zero pad it to $blockSize + */ + protected function getZeroPadding($string, $blockSize) + { + $stringSize = strlen($string); + + if (function_exists('mb_strlen')) + { + $stringSize = mb_strlen($string, 'ASCII'); + } + + if ($stringSize == $blockSize) + { + return ''; + } + + if ($stringSize < $blockSize) + { + return str_repeat("\0", $blockSize - $stringSize); + } + + $paddingBytes = $stringSize % $blockSize; + + return str_repeat("\0", $blockSize - $paddingBytes); + } +} \ No newline at end of file diff --git a/libraries/fof/encrypt/aes/interface.php b/libraries/fof/encrypt/aes/interface.php new file mode 100644 index 0000000000000..e8edaebef45ff --- /dev/null +++ b/libraries/fof/encrypt/aes/interface.php @@ -0,0 +1,83 @@ +cipherType = MCRYPT_RIJNDAEL_128; + break; + + case '192': + $this->cipherType = MCRYPT_RIJNDAEL_192; + break; + + case '256': + $this->cipherType = MCRYPT_RIJNDAEL_256; + break; + } + + switch (strtolower($mode)) + { + case 'ecb': + $this->cipherMode = MCRYPT_MODE_ECB; + break; + + default: + case 'cbc': + $this->cipherMode = MCRYPT_MODE_CBC; + break; + } + + } + + public function encrypt($plainText, $key, $iv = null) + { + $iv_size = $this->getBlockSize(); + $key = $this->resizeKey($key, $iv_size); + $iv = $this->resizeKey($iv, $iv_size); + + if (empty($iv)) + { + $randVal = new FOFEncryptRandval(); + $iv = $randVal->generate($iv_size); + } + + $cipherText = mcrypt_encrypt($this->cipherType, $key, $plainText, $this->cipherMode, $iv); + $cipherText = $iv . $cipherText; + + return $cipherText; + } + + public function decrypt($cipherText, $key) + { + $iv_size = $this->getBlockSize(); + $key = $this->resizeKey($key, $iv_size); + $iv = substr($cipherText, 0, $iv_size); + $cipherText = substr($cipherText, $iv_size); + $plainText = mcrypt_decrypt($this->cipherType, $key, $cipherText, $this->cipherMode, $iv); + + return $plainText; + } + + public function isSupported(FOFUtilsPhpfunc $phpfunc = null) + { + if (!is_object($phpfunc) || !($phpfunc instanceof $phpfunc)) + { + $phpfunc = new FOFUtilsPhpfunc(); + } + + if (!$phpfunc->function_exists('mcrypt_get_key_size')) + { + return false; + } + + if (!$phpfunc->function_exists('mcrypt_get_iv_size')) + { + return false; + } + + if (!$phpfunc->function_exists('mcrypt_create_iv')) + { + return false; + } + + if (!$phpfunc->function_exists('mcrypt_encrypt')) + { + return false; + } + + if (!$phpfunc->function_exists('mcrypt_decrypt')) + { + return false; + } + + if (!$phpfunc->function_exists('mcrypt_list_algorithms')) + { + return false; + } + + if (!$phpfunc->function_exists('hash')) + { + return false; + } + + if (!$phpfunc->function_exists('hash_algos')) + { + return false; + } + + $algorightms = $phpfunc->mcrypt_list_algorithms(); + + if (!in_array('rijndael-128', $algorightms)) + { + return false; + } + + if (!in_array('rijndael-192', $algorightms)) + { + return false; + } + + if (!in_array('rijndael-256', $algorightms)) + { + return false; + } + + $algorightms = $phpfunc->hash_algos(); + + if (!in_array('sha256', $algorightms)) + { + return false; + } + + return true; + } + + public function getBlockSize() + { + return mcrypt_get_iv_size($this->cipherType, $this->cipherMode); + } +} \ No newline at end of file diff --git a/libraries/fof/encrypt/aes/openssl.php b/libraries/fof/encrypt/aes/openssl.php new file mode 100644 index 0000000000000..094b1e502a040 --- /dev/null +++ b/libraries/fof/encrypt/aes/openssl.php @@ -0,0 +1,172 @@ +openSSLOptions = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING; + } + + public function setEncryptionMode($mode = 'cbc', $strength = 128) + { + static $availableAlgorithms = null; + static $defaultAlgo = 'aes-128-cbc'; + + if (!is_array($availableAlgorithms)) + { + $availableAlgorithms = openssl_get_cipher_methods(); + + foreach (array('aes-256-cbc', 'aes-256-ecb', 'aes-192-cbc', + 'aes-192-ecb', 'aes-128-cbc', 'aes-128-ecb') as $algo) + { + if (in_array($algo, $availableAlgorithms)) + { + $defaultAlgo = $algo; + break; + } + } + } + + $strength = (int) $strength; + $mode = strtolower($mode); + + if (!in_array($strength, array(128, 192, 256))) + { + $strength = 256; + } + + if (!in_array($mode, array('cbc', 'ebc'))) + { + $mode = 'cbc'; + } + + $algo = 'aes-' . $strength . '-' . $mode; + + if (!in_array($algo, $availableAlgorithms)) + { + $algo = $defaultAlgo; + } + + $this->method = $algo; + } + + public function encrypt($plainText, $key, $iv = null) + { + $iv_size = $this->getBlockSize(); + $key = $this->resizeKey($key, $iv_size); + $iv = $this->resizeKey($iv, $iv_size); + + if (empty($iv)) + { + $randVal = new FOFEncryptRandval(); + $iv = $randVal->generate($iv_size); + } + + $plainText .= $this->getZeroPadding($plainText, $iv_size); + $cipherText = openssl_encrypt($plainText, $this->method, $key, $this->openSSLOptions, $iv); + $cipherText = $iv . $cipherText; + + return $cipherText; + } + + public function decrypt($cipherText, $key) + { + $iv_size = $this->getBlockSize(); + $key = $this->resizeKey($key, $iv_size); + $iv = substr($cipherText, 0, $iv_size); + $cipherText = substr($cipherText, $iv_size); + $plainText = openssl_decrypt($cipherText, $this->method, $key, $this->openSSLOptions, $iv); + + return $plainText; + } + + public function isSupported(FOFUtilsPhpfunc $phpfunc = null) + { + if (!is_object($phpfunc) || !($phpfunc instanceof $phpfunc)) + { + $phpfunc = new FOFUtilsPhpfunc(); + } + + if (!$phpfunc->function_exists('openssl_get_cipher_methods')) + { + return false; + } + + if (!$phpfunc->function_exists('openssl_random_pseudo_bytes')) + { + return false; + } + + if (!$phpfunc->function_exists('openssl_cipher_iv_length')) + { + return false; + } + + if (!$phpfunc->function_exists('openssl_encrypt')) + { + return false; + } + + if (!$phpfunc->function_exists('openssl_decrypt')) + { + return false; + } + + if (!$phpfunc->function_exists('hash')) + { + return false; + } + + if (!$phpfunc->function_exists('hash_algos')) + { + return false; + } + + $algorightms = $phpfunc->openssl_get_cipher_methods(); + + if (!in_array('aes-128-cbc', $algorightms)) + { + return false; + } + + $algorightms = $phpfunc->hash_algos(); + + if (!in_array('sha256', $algorightms)) + { + return false; + } + + return true; + } + + /** + * @return int + */ + public function getBlockSize() + { + return openssl_cipher_iv_length($this->method); + } +} \ No newline at end of file diff --git a/libraries/fof/encrypt/base32.php b/libraries/fof/encrypt/base32.php index 11bc11877b3d2..768e064dee1d9 100644 --- a/libraries/fof/encrypt/base32.php +++ b/libraries/fof/encrypt/base32.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage encrypt - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('FOF_INCLUDED') or die; diff --git a/libraries/fof/encrypt/randval.php b/libraries/fof/encrypt/randval.php new file mode 100644 index 0000000000000..d6a511561e67d --- /dev/null +++ b/libraries/fof/encrypt/randval.php @@ -0,0 +1,196 @@ +phpfunc = $phpfunc; + } + + /** + * + * Returns a cryptographically secure random value. + * + * @param integer $bytes How many bytes to return + * + * @return string + */ + public function generate($bytes = 32) + { + if ($this->phpfunc->extension_loaded('openssl') && (version_compare(PHP_VERSION, '5.3.4') >= 0 || IS_WIN)) + { + $strong = false; + $randBytes = openssl_random_pseudo_bytes($bytes, $strong); + + if ($strong) + { + return $randBytes; + } + } + + if ($this->phpfunc->extension_loaded('mcrypt')) + { + return $this->phpfunc->mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM); + } + + return $this->genRandomBytes($bytes); + } + + /** + * Generate random bytes. Adapted from Joomla! 3.2. + * + * @param integer $length Length of the random data to generate + * + * @return string Random binary data + */ + public function genRandomBytes($length = 32) + { + $length = (int) $length; + $sslStr = ''; + + /* + * Collect any entropy available in the system along with a number + * of time measurements of operating system randomness. + */ + $bitsPerRound = 2; + $maxTimeMicro = 400; + $shaHashLength = 20; + $randomStr = ''; + $total = $length; + + // Check if we can use /dev/urandom. + $urandom = false; + $handle = null; + + // This is PHP 5.3.3 and up + if ($this->phpfunc->function_exists('stream_set_read_buffer') && @is_readable('/dev/urandom')) + { + $handle = @fopen('/dev/urandom', 'rb'); + + if ($handle) + { + $urandom = true; + } + } + + while ($length > strlen($randomStr)) + { + $bytes = ($total > $shaHashLength)? $shaHashLength : $total; + $total -= $bytes; + + /* + * Collect any entropy available from the PHP system and filesystem. + * If we have ssl data that isn't strong, we use it once. + */ + $entropy = rand() . uniqid(mt_rand(), true) . $sslStr; + $entropy .= implode('', @fstat(fopen(__FILE__, 'r'))); + $entropy .= memory_get_usage(); + $sslStr = ''; + + if ($urandom) + { + stream_set_read_buffer($handle, 0); + $entropy .= @fread($handle, $bytes); + } + else + { + /* + * There is no external source of entropy so we repeat calls + * to mt_rand until we are assured there's real randomness in + * the result. + * + * Measure the time that the operations will take on average. + */ + $samples = 3; + $duration = 0; + + for ($pass = 0; $pass < $samples; ++$pass) + { + $microStart = microtime(true) * 1000000; + $hash = sha1(mt_rand(), true); + + for ($count = 0; $count < 50; ++$count) + { + $hash = sha1($hash, true); + } + + $microEnd = microtime(true) * 1000000; + $entropy .= $microStart . $microEnd; + + if ($microStart >= $microEnd) + { + $microEnd += 1000000; + } + + $duration += $microEnd - $microStart; + } + + $duration = $duration / $samples; + + /* + * Based on the average time, determine the total rounds so that + * the total running time is bounded to a reasonable number. + */ + $rounds = (int) (($maxTimeMicro / $duration) * 50); + + /* + * Take additional measurements. On average we can expect + * at least $bitsPerRound bits of entropy from each measurement. + */ + $iter = $bytes * (int) ceil(8 / $bitsPerRound); + + for ($pass = 0; $pass < $iter; ++$pass) + { + $microStart = microtime(true); + $hash = sha1(mt_rand(), true); + + for ($count = 0; $count < $rounds; ++$count) + { + $hash = sha1($hash, true); + } + + $entropy .= $microStart . microtime(true); + } + } + + $randomStr .= sha1($entropy, true); + } + + if ($urandom) + { + @fclose($handle); + } + + return substr($randomStr, 0, $length); + } +} diff --git a/libraries/fof/encrypt/randvalinterface.php b/libraries/fof/encrypt/randvalinterface.php new file mode 100644 index 0000000000000..85cd6bd7bcf0d --- /dev/null +++ b/libraries/fof/encrypt/randvalinterface.php @@ -0,0 +1,22 @@ +name; $title = $this->element['ordertitle'] ? (string) $this->element['ordertitle'] : $this->item->getColumnAlias('title'); - $db = JFactory::getDbo(); + $db = FOFPlatform::getInstance()->getDbo(); $query = $db->getQuery(true); $query->select(array($db->quoteName($ordering, 'value'), $db->quoteName($title, 'text'))) ->from($db->quoteName($this->item->getTableName())) diff --git a/libraries/fof/form/field/password.php b/libraries/fof/form/field/password.php index b1337a481d790..43dc9de91b0d8 100644 --- a/libraries/fof/form/field/password.php +++ b/libraries/fof/form/field/password.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/field/plugins.php b/libraries/fof/form/field/plugins.php index 6e9f1abe28279..ed0fc5a57d0b1 100644 --- a/libraries/fof/form/field/plugins.php +++ b/libraries/fof/form/field/plugins.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/field/published.php b/libraries/fof/form/field/published.php index ca3f0b859fe8a..ceea5439cec54 100644 --- a/libraries/fof/form/field/published.php +++ b/libraries/fof/form/field/published.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/field/radio.php b/libraries/fof/form/field/radio.php index cc7ddafb94ea0..c636bc3028255 100644 --- a/libraries/fof/form/field/radio.php +++ b/libraries/fof/form/field/radio.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/field/relation.php b/libraries/fof/form/field/relation.php index 535c0bc7496fa..70f0efede2e66 100644 --- a/libraries/fof/form/field/relation.php +++ b/libraries/fof/form/field/relation.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/field/rules.php b/libraries/fof/form/field/rules.php index 0f132210ae09f..8cc324baa1c44 100644 --- a/libraries/fof/form/field/rules.php +++ b/libraries/fof/form/field/rules.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/field/selectrow.php b/libraries/fof/form/field/selectrow.php index fd30c427bedbc..eec7cea8b0473 100644 --- a/libraries/fof/form/field/selectrow.php +++ b/libraries/fof/form/field/selectrow.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/field/sessionhandler.php b/libraries/fof/form/field/sessionhandler.php index 07f94f2713171..40082b769fac6 100644 --- a/libraries/fof/form/field/sessionhandler.php +++ b/libraries/fof/form/field/sessionhandler.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/field/spacer.php b/libraries/fof/form/field/spacer.php index 4bfe9a2bf7552..045dc53e89c70 100644 --- a/libraries/fof/form/field/spacer.php +++ b/libraries/fof/form/field/spacer.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/field/sql.php b/libraries/fof/form/field/sql.php index bc3b7c1b52886..a8214328284e5 100644 --- a/libraries/fof/form/field/sql.php +++ b/libraries/fof/form/field/sql.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/field/tag.php b/libraries/fof/form/field/tag.php index 17e8b6957151c..ef5d0b4a62e9d 100644 --- a/libraries/fof/form/field/tag.php +++ b/libraries/fof/form/field/tag.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -80,7 +80,7 @@ protected function getOptions() $db = FOFPlatform::getInstance()->getDbo(); $query = $db->getQuery(true) - ->select('a.id AS value, a.path, a.title AS text, a.level, a.published') + ->select('DISTINCT a.id AS value, a.path, a.title AS text, a.level, a.published, a.lft') ->from('#__tags AS a') ->join('LEFT', $db->quoteName('#__tags') . ' AS b ON a.lft > b.lft AND a.rgt < b.rgt'); @@ -112,22 +112,13 @@ protected function getOptions() $this->value = $db->loadColumn(); } - // Ajax tag only loads assigned values - if (!$this->isNested()) - { - // Only item assigned values - $values = (array) $this->value; - FOFUtilsArray::toInteger($values); - $query->where('a.id IN (' . implode(',', $values) . ')'); - } - // Filter language if (!empty($this->element['language'])) { $query->where('a.language = ' . $db->quote($this->element['language'])); } - $query->where($db->quoteName('a.alias') . ' <> ' . $db->quote('root')); + $query->where($db->qn('a.lft') . ' > 0'); // Filter to only load active items @@ -142,8 +133,7 @@ protected function getOptions() $query->where('a.published IN (' . implode(',', $published) . ')'); } - $query->group('a.id, a.title, a.level, a.lft, a.rgt, a.parent_id, a.published, a.path') - ->order('a.lft ASC'); + $query->order('a.lft ASC'); // Get the options. $db->setQuery($query); diff --git a/libraries/fof/form/field/tel.php b/libraries/fof/form/field/tel.php index 2f8827d9b1e9e..66f070a26a04d 100644 --- a/libraries/fof/form/field/tel.php +++ b/libraries/fof/form/field/tel.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/field/text.php b/libraries/fof/form/field/text.php index 157381ec0cb8b..89751f071ab13 100644 --- a/libraries/fof/form/field/text.php +++ b/libraries/fof/form/field/text.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/field/textarea.php b/libraries/fof/form/field/textarea.php index 1694380ce7e1c..03c327dd8a5e8 100644 --- a/libraries/fof/form/field/textarea.php +++ b/libraries/fof/form/field/textarea.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/field/timezone.php b/libraries/fof/form/field/timezone.php index 196d5274e5c1b..272295d23ede6 100644 --- a/libraries/fof/form/field/timezone.php +++ b/libraries/fof/form/field/timezone.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/field/title.php b/libraries/fof/form/field/title.php index b8f15330e6c86..8ed993e3f98f9 100644 --- a/libraries/fof/form/field/title.php +++ b/libraries/fof/form/field/title.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/field/url.php b/libraries/fof/form/field/url.php index 0c6885996cd48..db3b2c1aaf8e4 100644 --- a/libraries/fof/form/field/url.php +++ b/libraries/fof/form/field/url.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/field/user.php b/libraries/fof/form/field/user.php index 179c42963c652..f2d6f9bc001e8 100644 --- a/libraries/fof/form/field/user.php +++ b/libraries/fof/form/field/user.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/field/usergroup.php b/libraries/fof/form/field/usergroup.php index 5e300a8a03977..fd9d4c07d5c71 100644 --- a/libraries/fof/form/field/usergroup.php +++ b/libraries/fof/form/field/usergroup.php @@ -3,7 +3,7 @@ * @package FrameworkOnFramework * @subpackage form * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -80,7 +80,7 @@ public function getStatic() $params = $this->getOptions(); - $db = JFactory::getDbo(); + $db = FOFPlatform::getInstance()->getDbo(); $query = $db->getQuery(true); $query->select('a.id AS value, a.title AS text'); @@ -122,7 +122,7 @@ public function getRepeatable() { $class = $this->element['class'] ? (string) $this->element['class'] : ''; - $db = JFactory::getDbo(); + $db = FOFPlatform::getInstance()->getDbo(); $query = $db->getQuery(true); $query->select('a.id AS value, a.title AS text'); diff --git a/libraries/fof/form/form.php b/libraries/fof/form/form.php index b01af9a1e7472..9fe7983b70f85 100644 --- a/libraries/fof/form/form.php +++ b/libraries/fof/form/form.php @@ -2,12 +2,19 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; +if (version_compare(JVERSION, '2.5.0', 'lt')) +{ + jimport('joomla.form.form'); + jimport('joomla.form.formfield'); + jimport('joomla.form.formrule'); +} + /** * FOFForm is an extension to JForm which support not only edit views but also * browse (record list) and read (single record display) views based on XML @@ -512,6 +519,39 @@ protected function loadHeader($element, $group = null, $value = null) return false; } } + + /** + * Method to remove a header from the form definition. + * + * @param string $name The name of the form field for which remove. + * @param string $group The optional dot-separated form group path on which to find the field. + * + * @return boolean True on success, false otherwise. + * + * @throws UnexpectedValueException + */ + public function removeHeader($name, $group = null) + { + // Make sure there is a valid JForm XML document. + if (!($this->xml instanceof SimpleXMLElement)) + { + throw new UnexpectedValueException(sprintf('%s::getFieldAttribute `xml` is not an instance of SimpleXMLElement', get_class($this))); + } + + // Find the form field element from the definition. + $element = $this->findHeader($name, $group); + + // If the element exists remove it from the form definition. + if ($element instanceof SimpleXMLElement) + { + $dom = dom_import_simplexml($element); + $dom->parentNode->removeChild($dom); + + return true; + } + + return false; + } /** * Proxy for {@link FOFFormHelper::loadFieldType()}. diff --git a/libraries/fof/form/header.php b/libraries/fof/form/header.php index 52921b549245a..e7fc70e8be6e9 100644 --- a/libraries/fof/form/header.php +++ b/libraries/fof/form/header.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/header/accesslevel.php b/libraries/fof/form/header/accesslevel.php index 7d0ad03e0fecf..7de32e13e099b 100644 --- a/libraries/fof/form/header/accesslevel.php +++ b/libraries/fof/form/header/accesslevel.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/header/field.php b/libraries/fof/form/header/field.php index 452ed27f075b1..91f663560f425 100644 --- a/libraries/fof/form/header/field.php +++ b/libraries/fof/form/header/field.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/header/fielddate.php b/libraries/fof/form/header/fielddate.php index cc7aa2f275b41..aa060426efb52 100644 --- a/libraries/fof/form/header/fielddate.php +++ b/libraries/fof/form/header/fielddate.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/header/fieldfilterable.php b/libraries/fof/form/header/fieldfilterable.php index 342e069f309e6..f51187af2887b 100644 --- a/libraries/fof/form/header/fieldfilterable.php +++ b/libraries/fof/form/header/fieldfilterable.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/header/fieldsearchable.php b/libraries/fof/form/header/fieldsearchable.php index 7583949de2a29..cc7bcaf22d983 100644 --- a/libraries/fof/form/header/fieldsearchable.php +++ b/libraries/fof/form/header/fieldsearchable.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/header/fieldselectable.php b/libraries/fof/form/header/fieldselectable.php index 5150b7f473cc3..9e6a56b66e9ff 100644 --- a/libraries/fof/form/header/fieldselectable.php +++ b/libraries/fof/form/header/fieldselectable.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/header/fieldsql.php b/libraries/fof/form/header/fieldsql.php index 811a653982f75..e714b4ea8536f 100644 --- a/libraries/fof/form/header/fieldsql.php +++ b/libraries/fof/form/header/fieldsql.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/header/filterdate.php b/libraries/fof/form/header/filterdate.php index c4b35b6f9d0cd..cef04f8a6b487 100644 --- a/libraries/fof/form/header/filterdate.php +++ b/libraries/fof/form/header/filterdate.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/header/filterfilterable.php b/libraries/fof/form/header/filterfilterable.php index 17e0fa76de553..fc1260e76ac28 100644 --- a/libraries/fof/form/header/filterfilterable.php +++ b/libraries/fof/form/header/filterfilterable.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/header/filtersearchable.php b/libraries/fof/form/header/filtersearchable.php index 199beeca7bc43..0d3c207e8b568 100644 --- a/libraries/fof/form/header/filtersearchable.php +++ b/libraries/fof/form/header/filtersearchable.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/header/filterselectable.php b/libraries/fof/form/header/filterselectable.php index 4f9011cbbd923..171b006a4ad23 100644 --- a/libraries/fof/form/header/filterselectable.php +++ b/libraries/fof/form/header/filterselectable.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/header/filtersql.php b/libraries/fof/form/header/filtersql.php index cf6458f77c739..6adf71ff1cb13 100644 --- a/libraries/fof/form/header/filtersql.php +++ b/libraries/fof/form/header/filtersql.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/header/language.php b/libraries/fof/form/header/language.php index 16c9e874d700e..8ddfdb65def25 100644 --- a/libraries/fof/form/header/language.php +++ b/libraries/fof/form/header/language.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/header/model.php b/libraries/fof/form/header/model.php index de1a9981f483d..e27d889547f46 100644 --- a/libraries/fof/form/header/model.php +++ b/libraries/fof/form/header/model.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/header/ordering.php b/libraries/fof/form/header/ordering.php index b5f47fb6705ca..467ab83e841ca 100644 --- a/libraries/fof/form/header/ordering.php +++ b/libraries/fof/form/header/ordering.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/header/published.php b/libraries/fof/form/header/published.php index 50ffab3a25667..8d48a674cf46c 100644 --- a/libraries/fof/form/header/published.php +++ b/libraries/fof/form/header/published.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/header/rowselect.php b/libraries/fof/form/header/rowselect.php index f1a360f2cf9cc..340a6832163ad 100644 --- a/libraries/fof/form/header/rowselect.php +++ b/libraries/fof/form/header/rowselect.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/form/helper.php b/libraries/fof/form/helper.php index 414dccd510a81..073e35189b42a 100644 --- a/libraries/fof/form/helper.php +++ b/libraries/fof/form/helper.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage form - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/hal/document.php b/libraries/fof/hal/document.php index 17a72cf38e791..aeee468ac124a 100644 --- a/libraries/fof/hal/document.php +++ b/libraries/fof/hal/document.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage hal - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('FOF_INCLUDED') or die; diff --git a/libraries/fof/hal/link.php b/libraries/fof/hal/link.php index d3ef0e2314e84..90660a5f4ec43 100644 --- a/libraries/fof/hal/link.php +++ b/libraries/fof/hal/link.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage hal - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('FOF_INCLUDED') or die; diff --git a/libraries/fof/hal/links.php b/libraries/fof/hal/links.php index 7031e566d9dc4..d24d5d27c1c83 100644 --- a/libraries/fof/hal/links.php +++ b/libraries/fof/hal/links.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage hal - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('FOF_INCLUDED') or die; diff --git a/libraries/fof/hal/render/interface.php b/libraries/fof/hal/render/interface.php index b2822a352d31e..c8644f0b8c30d 100644 --- a/libraries/fof/hal/render/interface.php +++ b/libraries/fof/hal/render/interface.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage hal - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('FOF_INCLUDED') or die; diff --git a/libraries/fof/hal/render/json.php b/libraries/fof/hal/render/json.php index 5c8cb863fc918..61776814bb30a 100644 --- a/libraries/fof/hal/render/json.php +++ b/libraries/fof/hal/render/json.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage hal - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('FOF_INCLUDED') or die; diff --git a/libraries/fof/include.php b/libraries/fof/include.php index 3f07c4982356d..e7940713e1ca7 100644 --- a/libraries/fof/include.php +++ b/libraries/fof/include.php @@ -12,7 +12,7 @@ if (!defined('FOF_INCLUDED')) { - define('FOF_INCLUDED', '2.4.3'); + define('FOF_INCLUDED', '2.5.5'); // Register the FOF autoloader require_once __DIR__ . '/autoloader/fof.php'; diff --git a/libraries/fof/inflector/inflector.php b/libraries/fof/inflector/inflector.php index 004b224f64ef9..880df47a041db 100644 --- a/libraries/fof/inflector/inflector.php +++ b/libraries/fof/inflector/inflector.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage inflector - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('FOF_INCLUDED') or die; diff --git a/libraries/fof/input/input.php b/libraries/fof/input/input.php index d84144ccb34f1..cdd1e2e9db235 100644 --- a/libraries/fof/input/input.php +++ b/libraries/fof/input/input.php @@ -2,12 +2,30 @@ /** * @package FrameworkOnFramework * @subpackage input - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; +if (version_compare(JVERSION, '1.7.0', 'lt')) +{ + jimport('joomla.filter.input'); + jimport('joomla.filter.filterinput'); + jimport('joomla.base.object'); + + require_once __DIR__ . '/jinput/input.php'; + require_once __DIR__ . '/jinput/cli.php'; + require_once __DIR__ . '/jinput/cookie.php'; + require_once __DIR__ . '/jinput/files.php'; + require_once __DIR__ . '/jinput/json.php'; +} +elseif (version_compare(JVERSION, '2.5.0', 'lt')) +{ + jimport('joomla.application.input'); + jimport('joomla.input.input'); +} + /** * FrameworkOnFramework input handling class. Extends upon the JInput class. * diff --git a/libraries/fof/input/jinput/cli.php b/libraries/fof/input/jinput/cli.php new file mode 100644 index 0000000000000..3557793771245 --- /dev/null +++ b/libraries/fof/input/jinput/cli.php @@ -0,0 +1,200 @@ +filter = $options['filter']; + } + else + { + $this->filter = JFilterInput::getInstance(); + } + + // Get the command line options + $this->parseArguments(); + + // Set the options for the class. + $this->options = $options; + } + + /** + * Method to serialize the input. + * + * @return string The serialized input. + * + * @since 12.1 + */ + public function serialize() + { + // Load all of the inputs. + $this->loadAllInputs(); + + // Remove $_ENV and $_SERVER from the inputs. + $inputs = $this->inputs; + unset($inputs['env']); + unset($inputs['server']); + + // Serialize the executable, args, options, data, and inputs. + return serialize(array($this->executable, $this->args, $this->options, $this->data, $inputs)); + } + + /** + * Method to unserialize the input. + * + * @param string $input The serialized input. + * + * @return JInput The input object. + * + * @since 12.1 + */ + public function unserialize($input) + { + // Unserialize the executable, args, options, data, and inputs. + list($this->executable, $this->args, $this->options, $this->data, $this->inputs) = unserialize($input); + + // Load the filter. + if (isset($this->options['filter'])) + { + $this->filter = $this->options['filter']; + } + else + { + $this->filter = JFilterInput::getInstance(); + } + } + + /** + * Initialise the options and arguments + * + * Not supported: -abc c-value + * + * @return void + * + * @since 11.1 + */ + protected function parseArguments() + { + $argv = $_SERVER['argv']; + + $this->executable = array_shift($argv); + + $out = array(); + + for ($i = 0, $j = count($argv); $i < $j; $i++) + { + $arg = $argv[$i]; + + // --foo --bar=baz + if (substr($arg, 0, 2) === '--') + { + $eqPos = strpos($arg, '='); + + // --foo + if ($eqPos === false) + { + $key = substr($arg, 2); + + // --foo value + if ($i + 1 < $j && $argv[$i + 1][0] !== '-') + { + $value = $argv[$i + 1]; + $i++; + } + else + { + $value = isset($out[$key]) ? $out[$key] : true; + } + + $out[$key] = $value; + } + + // --bar=baz + else + { + $key = substr($arg, 2, $eqPos - 2); + $value = substr($arg, $eqPos + 1); + $out[$key] = $value; + } + } + elseif (substr($arg, 0, 1) === '-') + // -k=value -abc + { + // -k=value + if (substr($arg, 2, 1) === '=') + { + $key = substr($arg, 1, 1); + $value = substr($arg, 3); + $out[$key] = $value; + } + else + // -abc + { + $chars = str_split(substr($arg, 1)); + + foreach ($chars as $char) + { + $key = $char; + $value = isset($out[$key]) ? $out[$key] : true; + $out[$key] = $value; + } + + // -a a-value + if ((count($chars) === 1) && ($i + 1 < $j) && ($argv[$i + 1][0] !== '-')) + { + $out[$key] = $argv[$i + 1]; + $i++; + } + } + } + else + { + // Plain-arg + $this->args[] = $arg; + } + } + + $this->data = $out; + } +} diff --git a/libraries/fof/input/jinput/cookie.php b/libraries/fof/input/jinput/cookie.php new file mode 100644 index 0000000000000..0392049500a8e --- /dev/null +++ b/libraries/fof/input/jinput/cookie.php @@ -0,0 +1,89 @@ +filter = $options['filter']; + } + else + { + $this->filter = JFilterInput::getInstance(); + } + + // Set the data source. + $this->data = & $_COOKIE; + + // Set the options for the class. + $this->options = $options; + } + + /** + * Sets a value + * + * @param string $name Name of the value to set. + * @param mixed $value Value to assign to the input. + * @param integer $expire The time the cookie expires. This is a Unix timestamp so is in number + * of seconds since the epoch. In other words, you'll most likely set this + * with the time() function plus the number of seconds before you want it + * to expire. Or you might use mktime(). time()+60*60*24*30 will set the + * cookie to expire in 30 days. If set to 0, or omitted, the cookie will + * expire at the end of the session (when the browser closes). + * @param string $path The path on the server in which the cookie will be available on. If set + * to '/', the cookie will be available within the entire domain. If set to + * '/foo/', the cookie will only be available within the /foo/ directory and + * all sub-directories such as /foo/bar/ of domain. The default value is the + * current directory that the cookie is being set in. + * @param string $domain The domain that the cookie is available to. To make the cookie available + * on all subdomains of example.com (including example.com itself) then you'd + * set it to '.example.com'. Although some browsers will accept cookies without + * the initial ., RFC 2109 requires it to be included. Setting the domain to + * 'www.example.com' or '.www.example.com' will make the cookie only available + * in the www subdomain. + * @param boolean $secure Indicates that the cookie should only be transmitted over a secure HTTPS + * connection from the client. When set to TRUE, the cookie will only be set + * if a secure connection exists. On the server-side, it's on the programmer + * to send this kind of cookie only on secure connection (e.g. with respect + * to $_SERVER["HTTPS"]). + * @param boolean $httpOnly When TRUE the cookie will be made accessible only through the HTTP protocol. + * This means that the cookie won't be accessible by scripting languages, such + * as JavaScript. This setting can effectively help to reduce identity theft + * through XSS attacks (although it is not supported by all browsers). + * + * @return void + * + * @link http://www.ietf.org/rfc/rfc2109.txt + * @see setcookie() + * @since 11.1 + */ + public function set($name, $value, $expire = 0, $path = '', $domain = '', $secure = false, $httpOnly = false) + { + setcookie($name, $value, $expire, $path, $domain, $secure, $httpOnly); + + $this->data[$name] = $value; + } +} diff --git a/libraries/fof/input/jinput/files.php b/libraries/fof/input/jinput/files.php new file mode 100644 index 0000000000000..7cd537e93fd4e --- /dev/null +++ b/libraries/fof/input/jinput/files.php @@ -0,0 +1,136 @@ +filter = $options['filter']; + } + else + { + $this->filter = JFilterInput::getInstance(); + } + + // Set the data source. + $this->data = & $_FILES; + + // Set the options for the class. + $this->options = $options; + } + + /** + * Gets a value from the input data. + * + * @param string $name The name of the input property (usually the name of the files INPUT tag) to get. + * @param mixed $default The default value to return if the named property does not exist. + * @param string $filter The filter to apply to the value. + * + * @return mixed The filtered input value. + * + * @see JFilterInput::clean() + * @since 11.1 + */ + public function get($name, $default = null, $filter = 'cmd') + { + if (isset($this->data[$name])) + { + $results = $this->decodeData( + array( + $this->data[$name]['name'], + $this->data[$name]['type'], + $this->data[$name]['tmp_name'], + $this->data[$name]['error'], + $this->data[$name]['size'] + ) + ); + + // Prevent returning an unsafe file unless speciffically requested + if ($filter != 'raw') + { + $isSafe = JFilterInput::isSafeFile($results); + + if (!$isSafe) + { + return $default; + } + } + + return $results; + } + + return $default; + } + + /** + * Method to decode a data array. + * + * @param array $data The data array to decode. + * + * @return array + * + * @since 11.1 + */ + protected function decodeData(array $data) + { + $result = array(); + + if (is_array($data[0])) + { + foreach ($data[0] as $k => $v) + { + $result[$k] = $this->decodeData(array($data[0][$k], $data[1][$k], $data[2][$k], $data[3][$k], $data[4][$k])); + } + + return $result; + } + + return array('name' => $data[0], 'type' => $data[1], 'tmp_name' => $data[2], 'error' => $data[3], 'size' => $data[4]); + } + + /** + * Sets a value. + * + * @param string $name The name of the input property to set. + * @param mixed $value The value to assign to the input property. + * + * @return void + * + * @since 11.1 + */ + public function set($name, $value) + { + } +} diff --git a/libraries/fof/input/jinput/input.php b/libraries/fof/input/jinput/input.php new file mode 100644 index 0000000000000..6a1f27878f4cc --- /dev/null +++ b/libraries/fof/input/jinput/input.php @@ -0,0 +1,383 @@ +filter = $options['filter']; + } + else + { + $this->filter = JFilterInput::getInstance(); + } + + if (is_null($source)) + { + $this->data = &$_REQUEST; + } + else + { + $this->data = $source; + } + + // Set the options for the class. + $this->options = $options; + } + + /** + * Magic method to get an input object + * + * @param mixed $name Name of the input object to retrieve. + * + * @return JInput The request input object + * + * @since 11.1 + */ + public function __get($name) + { + if (isset($this->inputs[$name])) + { + return $this->inputs[$name]; + } + + $className = 'JInput' . ucfirst($name); + + if (class_exists($className)) + { + $this->inputs[$name] = new $className(null, $this->options); + + return $this->inputs[$name]; + } + + $superGlobal = '_' . strtoupper($name); + + if (isset($GLOBALS[$superGlobal])) + { + $this->inputs[$name] = new JInput($GLOBALS[$superGlobal], $this->options); + + return $this->inputs[$name]; + } + + // TODO throw an exception + } + + /** + * Get the number of variables. + * + * @return integer The number of variables in the input. + * + * @since 12.2 + * @see Countable::count() + */ + public function count() + { + return count($this->data); + } + + /** + * Gets a value from the input data. + * + * @param string $name Name of the value to get. + * @param mixed $default Default value to return if variable does not exist. + * @param string $filter Filter to apply to the value. + * + * @return mixed The filtered input value. + * + * @since 11.1 + */ + public function get($name, $default = null, $filter = 'cmd') + { + if (isset($this->data[$name])) + { + return $this->filter->clean($this->data[$name], $filter); + } + + return $default; + } + + /** + * Gets an array of values from the request. + * + * @param array $vars Associative array of keys and filter types to apply. + * If empty and datasource is null, all the input data will be returned + * but filtered using the default case in JFilterInput::clean. + * @param mixed $datasource Array to retrieve data from, or null + * + * @return mixed The filtered input data. + * + * @since 11.1 + */ + public function getArray(array $vars = array(), $datasource = null) + { + if (empty($vars) && is_null($datasource)) + { + $vars = $this->data; + } + + $results = array(); + + foreach ($vars as $k => $v) + { + if (is_array($v)) + { + if (is_null($datasource)) + { + $results[$k] = $this->getArray($v, $this->get($k, null, 'array')); + } + else + { + $results[$k] = $this->getArray($v, $datasource[$k]); + } + } + else + { + if (is_null($datasource)) + { + $results[$k] = $this->get($k, null, $v); + } + elseif (isset($datasource[$k])) + { + $results[$k] = $this->filter->clean($datasource[$k], $v); + } + else + { + $results[$k] = $this->filter->clean(null, $v); + } + } + } + + return $results; + } + + /** + * Sets a value + * + * @param string $name Name of the value to set. + * @param mixed $value Value to assign to the input. + * + * @return void + * + * @since 11.1 + */ + public function set($name, $value) + { + $this->data[$name] = $value; + } + + /** + * Define a value. The value will only be set if there's no value for the name or if it is null. + * + * @param string $name Name of the value to define. + * @param mixed $value Value to assign to the input. + * + * @return void + * + * @since 12.1 + */ + public function def($name, $value) + { + if (isset($this->data[$name])) + { + return; + } + + $this->data[$name] = $value; + } + + /** + * Magic method to get filtered input data. + * + * @param string $name Name of the filter type prefixed with 'get'. + * @param array $arguments [0] The name of the variable [1] The default value. + * + * @return mixed The filtered input value. + * + * @since 11.1 + */ + public function __call($name, $arguments) + { + if (substr($name, 0, 3) == 'get') + { + $filter = substr($name, 3); + + $default = null; + + if (isset($arguments[1])) + { + $default = $arguments[1]; + } + + return $this->get($arguments[0], $default, $filter); + } + } + + /** + * Gets the request method. + * + * @return string The request method. + * + * @since 11.1 + */ + public function getMethod() + { + $method = strtoupper($_SERVER['REQUEST_METHOD']); + + return $method; + } + + /** + * Method to serialize the input. + * + * @return string The serialized input. + * + * @since 12.1 + */ + public function serialize() + { + // Load all of the inputs. + $this->loadAllInputs(); + + // Remove $_ENV and $_SERVER from the inputs. + $inputs = $this->inputs; + unset($inputs['env']); + unset($inputs['server']); + + // Serialize the options, data, and inputs. + return serialize(array($this->options, $this->data, $inputs)); + } + + /** + * Method to unserialize the input. + * + * @param string $input The serialized input. + * + * @return JInput The input object. + * + * @since 12.1 + */ + public function unserialize($input) + { + // Unserialize the options, data, and inputs. + list($this->options, $this->data, $this->inputs) = unserialize($input); + + // Load the filter. + if (isset($this->options['filter'])) + { + $this->filter = $this->options['filter']; + } + else + { + $this->filter = JFilterInput::getInstance(); + } + } + + /** + * Method to load all of the global inputs. + * + * @return void + * + * @since 12.1 + */ + protected function loadAllInputs() + { + static $loaded = false; + + if (!$loaded) + { + // Load up all the globals. + foreach ($GLOBALS as $global => $data) + { + // Check if the global starts with an underscore. + if (strpos($global, '_') === 0) + { + // Convert global name to input name. + $global = strtolower($global); + $global = substr($global, 1); + + // Get the input. + $this->$global; + } + } + + $loaded = true; + } + } +} diff --git a/libraries/fof/input/jinput/json.php b/libraries/fof/input/jinput/json.php new file mode 100644 index 0000000000000..e71dbdd5effa1 --- /dev/null +++ b/libraries/fof/input/jinput/json.php @@ -0,0 +1,72 @@ +filter = $options['filter']; + } + else + { + $this->filter = JFilterInput::getInstance(); + } + + if (is_null($source)) + { + $this->_raw = file_get_contents('php://input'); + $this->data = json_decode($this->_raw, true); + } + else + { + $this->data = & $source; + } + + // Set the options for the class. + $this->options = $options; + } + + /** + * Gets the raw JSON string from the request. + * + * @return string The raw JSON string from the request. + * + * @since 12.2 + */ + public function getRaw() + { + return $this->_raw; + } +} diff --git a/libraries/fof/integration/joomla/filesystem/filesystem.php b/libraries/fof/integration/joomla/filesystem/filesystem.php index a300edf6e1202..a051c158d581b 100644 --- a/libraries/fof/integration/joomla/filesystem/filesystem.php +++ b/libraries/fof/integration/joomla/filesystem/filesystem.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage platformFilesystem - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/integration/joomla/platform.php b/libraries/fof/integration/joomla/platform.php index 705b0b4f8c25a..6fe75a8522317 100644 --- a/libraries/fof/integration/joomla/platform.php +++ b/libraries/fof/integration/joomla/platform.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage platform - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -444,7 +444,7 @@ public function getLanguage() public function getDbo() { - return JFactory::getDbo(); + return FOFDatabaseFactory::getInstance()->getDriver('joomla'); } /** @@ -538,9 +538,16 @@ public function runPlugins($event, $data) { if (!$this->isCli()) { + $app = JFactory::getApplication(); + + if (method_exists($app, 'triggerEvent')) + { + return $app->triggerEvent($event, $data); + } + // IMPORTANT: DO NOT REPLACE THIS INSTANCE OF JDispatcher WITH ANYTHING ELSE. WE NEED JOOMLA!'S PLUGIN EVENT // DISPATCHER HERE, NOT OUR GENERIC EVENTS DISPATCHER - if (version_compare($this->version, '3.0', 'ge')) + if (class_exists('JEventDispatcher')) { $dispatcher = JEventDispatcher::getInstance(); } @@ -791,7 +798,7 @@ public function loginUser($authInfo) // if we're in Joomla 2.5.18+ or 3.2.1+ if($response->status != JAuthentication::STATUS_SUCCESS && method_exists('JUserHelper', 'verifyPassword')) { - $db = JFactory::getDbo(); + $db = $this->getDbo(); $query = $db->getQuery(true) ->select('id, password') ->from('#__users') @@ -860,6 +867,11 @@ public function logoutUser() public function logAddLogger($file) { + if (!class_exists('JLog')) + { + return; + } + JLog::addLogger(array('text_file' => $file), JLog::ALL, array('fof')); } @@ -873,11 +885,21 @@ public function logAddLogger($file) */ public function logDeprecated($message) { + if (!class_exists('JLog')) + { + return; + } + JLog::add($message, JLog::WARNING, 'deprecated'); } public function logDebug($message) { + if (!class_exists('JLog')) + { + return; + } + JLog::add($message, JLog::DEBUG, 'fof'); } diff --git a/libraries/fof/layout/file.php b/libraries/fof/layout/file.php index 15f9f78c8ecd1..f3617c11d5cdc 100644 --- a/libraries/fof/layout/file.php +++ b/libraries/fof/layout/file.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage layout - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/layout/helper.php b/libraries/fof/layout/helper.php index 8d0ec127a357e..16bf9353a4713 100644 --- a/libraries/fof/layout/helper.php +++ b/libraries/fof/layout/helper.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage layout - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/less/formatter/classic.php b/libraries/fof/less/formatter/classic.php index a53fa1b8c6dee..2df9569e98b77 100644 --- a/libraries/fof/less/formatter/classic.php +++ b/libraries/fof/less/formatter/classic.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage less - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/less/formatter/compressed.php b/libraries/fof/less/formatter/compressed.php index d18e8a763bca1..f1c2efdd53a97 100644 --- a/libraries/fof/less/formatter/compressed.php +++ b/libraries/fof/less/formatter/compressed.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage less - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/less/formatter/joomla.php b/libraries/fof/less/formatter/joomla.php index e81250e430ca2..cc09ca51601da 100644 --- a/libraries/fof/less/formatter/joomla.php +++ b/libraries/fof/less/formatter/joomla.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage less - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/less/formatter/lessjs.php b/libraries/fof/less/formatter/lessjs.php index db4f686471e17..8b1238798b7d0 100644 --- a/libraries/fof/less/formatter/lessjs.php +++ b/libraries/fof/less/formatter/lessjs.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage less - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/less/less.php b/libraries/fof/less/less.php index 964670f65d134..d4e14fbd74eb3 100644 --- a/libraries/fof/less/less.php +++ b/libraries/fof/less/less.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage less - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/less/parser/parser.php b/libraries/fof/less/parser/parser.php index 195a88243b15b..fa4021a9f3c25 100644 --- a/libraries/fof/less/parser/parser.php +++ b/libraries/fof/less/parser/parser.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage less - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/model/behavior.php b/libraries/fof/model/behavior.php index d95054404628c..7086d1c43ef77 100644 --- a/libraries/fof/model/behavior.php +++ b/libraries/fof/model/behavior.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage model - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -101,7 +101,7 @@ public function onBeforeReorder(&$model) * list in a model * * @param FOFModel &$model The model which calls this event - * @param JDatabaseQuery &$query The query being built + * @param FOFDatabaseQuery &$query The query being built * * @return void */ @@ -191,7 +191,7 @@ public function onAfterReorder(&$model) * list in a model * * @param FOFModel &$model The model which calls this event - * @param JDatabaseQuery &$query The query being built + * @param FOFDatabaseQuery &$query The query being built * * @return void */ diff --git a/libraries/fof/model/behavior/access.php b/libraries/fof/model/behavior/access.php index f8d3eea13e918..1cafe7f07932b 100644 --- a/libraries/fof/model/behavior/access.php +++ b/libraries/fof/model/behavior/access.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage model - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -22,7 +22,7 @@ class FOFModelBehaviorAccess extends FOFModelBehavior * list in a model. It is used to apply automatic query filters. * * @param FOFModel &$model The model which calls this event - * @param JDatabaseQuery &$query The model which calls this event + * @param FOFDatabaseQuery &$query The model which calls this event * * @return void */ diff --git a/libraries/fof/model/behavior/emptynonzero.php b/libraries/fof/model/behavior/emptynonzero.php index 56ffabd17b102..9efb07c204678 100644 --- a/libraries/fof/model/behavior/emptynonzero.php +++ b/libraries/fof/model/behavior/emptynonzero.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage model - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -21,7 +21,7 @@ class FOFModelBehaviorEmptynonzero extends FOFModelBehavior * list in a model * * @param FOFModel &$model The model which calls this event - * @param JDatabaseQuery &$query The query being built + * @param FOFDatabaseQuery &$query The query being built * * @return void */ diff --git a/libraries/fof/model/behavior/enabled.php b/libraries/fof/model/behavior/enabled.php index 2f6207b4b2f51..c2e358523bb27 100644 --- a/libraries/fof/model/behavior/enabled.php +++ b/libraries/fof/model/behavior/enabled.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage model - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -22,7 +22,7 @@ class FOFModelBehaviorEnabled extends FOFModelBehavior * list in a model. It is used to apply automatic query filters. * * @param FOFModel &$model The model which calls this event - * @param JDatabaseQuery &$query The model which calls this event + * @param FOFDatabaseQuery &$query The model which calls this event * * @return void */ diff --git a/libraries/fof/model/behavior/filters.php b/libraries/fof/model/behavior/filters.php index 6e54cd3c9e385..7cd1ccd1ffbda 100644 --- a/libraries/fof/model/behavior/filters.php +++ b/libraries/fof/model/behavior/filters.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage model - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -21,7 +21,7 @@ class FOFModelBehaviorFilters extends FOFModelBehavior * list in a model. It is used to apply automatic query filters. * * @param FOFModel &$model The model which calls this event - * @param JDatabaseQuery &$query The model which calls this event + * @param FOFDatabaseQuery &$query The model which calls this event * * @return void */ diff --git a/libraries/fof/model/behavior/language.php b/libraries/fof/model/behavior/language.php index f4b091e5409d5..e42911c27cb1a 100644 --- a/libraries/fof/model/behavior/language.php +++ b/libraries/fof/model/behavior/language.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage model - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -22,7 +22,7 @@ class FOFModelBehaviorLanguage extends FOFModelBehavior * list in a model. It is used to blacklist the language filter * * @param FOFModel &$model The model which calls this event - * @param JDatabaseQuery &$query The model which calls this event + * @param FOFDatabaseQuery &$query The model which calls this event * * @return void */ @@ -39,7 +39,7 @@ public function onBeforeBuildQuery(&$model, &$query) * list in a model. It is used to apply automatic query filters. * * @param FOFModel &$model The model which calls this event - * @param JDatabaseQuery &$query The model which calls this event + * @param FOFDatabaseQuery &$query The model which calls this event * * @return void */ diff --git a/libraries/fof/model/behavior/private.php b/libraries/fof/model/behavior/private.php index 85cb9d9a669e3..85ce3d1d3e394 100644 --- a/libraries/fof/model/behavior/private.php +++ b/libraries/fof/model/behavior/private.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage model - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -22,7 +22,7 @@ class FOFModelBehaviorPrivate extends FOFModelBehavior * list in a model. It is used to apply automatic query filters. * * @param FOFModel &$model The model which calls this event - * @param JDatabaseQuery &$query The model which calls this event + * @param FOFDatabaseQuery &$query The model which calls this event * * @return void */ diff --git a/libraries/fof/model/dispatcher/behavior.php b/libraries/fof/model/dispatcher/behavior.php index 554908afe119f..7df41c82f702d 100644 --- a/libraries/fof/model/dispatcher/behavior.php +++ b/libraries/fof/model/dispatcher/behavior.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage model - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/model/field.php b/libraries/fof/model/field.php index 4eeec40c619f6..8a7c04a980c5b 100644 --- a/libraries/fof/model/field.php +++ b/libraries/fof/model/field.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage model - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ @@ -50,7 +50,7 @@ abstract class FOFModelField /** * Constructor * - * @param JDatabaseDriver $db The database object + * @param FOFDatabaseDriver $db The database object * @param object $field The field informations as taken from the db * @param string $table_alias The table alias to use when filtering */ diff --git a/libraries/fof/model/field/boolean.php b/libraries/fof/model/field/boolean.php index 114acce813973..9140d303f3b55 100644 --- a/libraries/fof/model/field/boolean.php +++ b/libraries/fof/model/field/boolean.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage model - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/model/field/date.php b/libraries/fof/model/field/date.php index 4319976dc1ffd..9a18957f05c76 100644 --- a/libraries/fof/model/field/date.php +++ b/libraries/fof/model/field/date.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage model - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/model/field/number.php b/libraries/fof/model/field/number.php index 9d3bd09810778..712e4ecadac1e 100644 --- a/libraries/fof/model/field/number.php +++ b/libraries/fof/model/field/number.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage model - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/model/field/text.php b/libraries/fof/model/field/text.php index 580b6d3ae4dd6..ebeeedede7c26 100644 --- a/libraries/fof/model/field/text.php +++ b/libraries/fof/model/field/text.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage model - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -19,7 +19,7 @@ class FOFModelFieldText extends FOFModelField /** * Constructor * - * @param JDatabaseDriver $db The database object + * @param FOFDatabaseDriver $db The database object * @param object $field The field informations as taken from the db */ public function __construct($db, $field, $table_alias = false) diff --git a/libraries/fof/model/model.php b/libraries/fof/model/model.php index 99a026220acde..5d67e2af8d3a2 100644 --- a/libraries/fof/model/model.php +++ b/libraries/fof/model/model.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage model - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -2054,7 +2054,7 @@ public function getReorderWhere() * * @param boolean $overrideLimits Are we requested to override the set limits? * - * @return JDatabaseQuery + * @return FOFDatabaseQuery */ public function buildQuery($overrideLimits = false) { @@ -3115,7 +3115,7 @@ protected function onAfterReorder(&$table) /** * Method to get the database driver object * - * @return JDatabaseDriver + * @return FOFDatabaseDriver */ public function getDbo() { @@ -3152,7 +3152,7 @@ public function getName() /** * Method to set the database driver object * - * @param JDatabaseDriver $db A JDatabaseDriver based object + * @param FOFDatabaseDriver $db A FOFDatabaseDriver based object * * @return void */ diff --git a/libraries/fof/platform/filesystem/filesystem.php b/libraries/fof/platform/filesystem/filesystem.php index b2291efe5ad12..a1cbd0f2abb47 100644 --- a/libraries/fof/platform/filesystem/filesystem.php +++ b/libraries/fof/platform/filesystem/filesystem.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage platform - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/platform/filesystem/interface.php b/libraries/fof/platform/filesystem/interface.php index 4d57888559280..70cb33fa4a54a 100644 --- a/libraries/fof/platform/filesystem/interface.php +++ b/libraries/fof/platform/filesystem/interface.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage platformFilesystem - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ diff --git a/libraries/fof/platform/interface.php b/libraries/fof/platform/interface.php index e00c138817671..04048df12e221 100644 --- a/libraries/fof/platform/interface.php +++ b/libraries/fof/platform/interface.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage platform - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -325,6 +325,9 @@ public function getDate($time = 'now', $tzOffest = null, $locale = true); public function getLanguage(); + /** + * @return FOFDatabaseDriver + */ public function getDbo(); /** diff --git a/libraries/fof/platform/platform.php b/libraries/fof/platform/platform.php index f3101d697961f..452230f351e92 100644 --- a/libraries/fof/platform/platform.php +++ b/libraries/fof/platform/platform.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage platform - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/query/abstract.php b/libraries/fof/query/abstract.php index 7fde3a3fd8d45..659c6f412d982 100644 --- a/libraries/fof/query/abstract.php +++ b/libraries/fof/query/abstract.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage query - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -20,13 +20,13 @@ abstract class FOFQueryAbstract /** * Returns a new database query class * - * @param JDatabaseDriver $db The DB driver which will provide us with a query object + * @param FOFDatabaseDriver $db The DB driver which will provide us with a query object * * @return FOFQueryAbstract */ public static function &getNew($db = null) { - FOFPlatform::getInstance()->logDeprecated('FOFQueryAbstract is deprecated. Use JDatabaseQuery instead.'); + FOFPlatform::getInstance()->logDeprecated('FOFQueryAbstract is deprecated. Use FOFDatabaseQuery instead.'); if (is_null($db)) { diff --git a/libraries/fof/render/abstract.php b/libraries/fof/render/abstract.php index fe1cdcbedd20b..7b62aaa29fc96 100644 --- a/libraries/fof/render/abstract.php +++ b/libraries/fof/render/abstract.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage render - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('FOF_INCLUDED') or die; diff --git a/libraries/fof/render/joomla.php b/libraries/fof/render/joomla.php index 0f9435f21e910..34964012166f8 100644 --- a/libraries/fof/render/joomla.php +++ b/libraries/fof/render/joomla.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage render - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('FOF_INCLUDED') or die; @@ -61,7 +61,15 @@ public function preRender($view, $task, $input, $config = array()) } else { - JHtml::_('behavior.core'); + if (version_compare(JVERSION, '3.3.0', 'ge')) + { + JHtml::_('behavior.core'); + } + else + { + JHtml::_('behavior.framework', true); + } + JHtml::_('jquery.framework'); } @@ -769,7 +777,7 @@ protected function renderButtons($view, $task, $input, $config = array()) FOFPlatform::getInstance()->loadTranslations('joomla'); $title = JFactory::getApplication()->get('JComponentTitle'); - $bar = JToolBar::getInstance('toolbar'); + $bar = JToolbar::getInstance('toolbar'); // Delete faux links, since if SEF is on, Joomla will follow the link instead of submitting the form $bar_content = str_replace('href="#"', '', $bar->render()); diff --git a/libraries/fof/render/joomla3.php b/libraries/fof/render/joomla3.php index e5376d87679ff..9580e4d1d5e9d 100644 --- a/libraries/fof/render/joomla3.php +++ b/libraries/fof/render/joomla3.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage render - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('FOF_INCLUDED') or die; @@ -55,7 +55,15 @@ public function preRender($view, $task, $input, $config = array()) return; } - JHtml::_('behavior.core'); + if (version_compare(JVERSION, '3.3.0', 'ge')) + { + JHtml::_('behavior.core'); + } + else + { + JHtml::_('behavior.framework', true); + } + JHtml::_('jquery.framework'); if ($platform->isBackend()) diff --git a/libraries/fof/render/strapper.php b/libraries/fof/render/strapper.php index 462d2851ddb6f..71d88bb551d7b 100644 --- a/libraries/fof/render/strapper.php +++ b/libraries/fof/render/strapper.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage render - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('FOF_INCLUDED') or die; @@ -61,7 +61,15 @@ public function preRender($view, $task, $input, $config = array()) } else { - JHtml::_('behavior.core'); + if (version_compare(JVERSION, '3.3.0', 'ge')) + { + JHtml::_('behavior.core'); + } + else + { + JHtml::_('behavior.framework', true); + } + JHtml::_('jquery.framework'); } @@ -445,7 +453,7 @@ protected function renderButtons($view, $task, $input, $config = array()) return; } - $bar = JToolBar::getInstance('toolbar'); + $bar = JToolbar::getInstance('toolbar'); $items = $bar->getItems(); $substitutions = array( diff --git a/libraries/fof/string/utils.php b/libraries/fof/string/utils.php index a3a701a63d4a6..b0ce03561b3a5 100644 --- a/libraries/fof/string/utils.php +++ b/libraries/fof/string/utils.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage utils - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/table/behavior.php b/libraries/fof/table/behavior.php index 487dbbe086b09..4d3f21611045b 100644 --- a/libraries/fof/table/behavior.php +++ b/libraries/fof/table/behavior.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage table - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/table/behavior/assets.php b/libraries/fof/table/behavior/assets.php index c171cdbd4b97e..d1c2151125e57 100644 --- a/libraries/fof/table/behavior/assets.php +++ b/libraries/fof/table/behavior/assets.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage table - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/table/behavior/contenthistory.php b/libraries/fof/table/behavior/contenthistory.php index b86a430e057ec..ce6c8a63b4d4f 100644 --- a/libraries/fof/table/behavior/contenthistory.php +++ b/libraries/fof/table/behavior/contenthistory.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage table - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/table/behavior/tags.php b/libraries/fof/table/behavior/tags.php index 30bae5e085b92..7eb1d27cdf266 100644 --- a/libraries/fof/table/behavior/tags.php +++ b/libraries/fof/table/behavior/tags.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage table - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/table/dispatcher/behavior.php b/libraries/fof/table/dispatcher/behavior.php index 0b331a937d270..d46d3c0084041 100644 --- a/libraries/fof/table/dispatcher/behavior.php +++ b/libraries/fof/table/dispatcher/behavior.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage table - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/table/nested.php b/libraries/fof/table/nested.php index e2a99ce20ba6f..e57679edd8abe 100644 --- a/libraries/fof/table/nested.php +++ b/libraries/fof/table/nested.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage table - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ @@ -41,7 +41,7 @@ class FOFTableNested extends FOFTable * * @param string $table Name of the database table to model. * @param string $key Name of the primary key field in the table. - * @param JDatabaseDriver &$db Database driver + * @param FOFDatabaseDriver &$db Database driver * @param array $config The configuration parameters array * * @throws \RuntimeException When lft/rgt columns are not found @@ -2157,7 +2157,7 @@ public function whereRaw($rawWhereClause) /** * Builds the query for the get() method * - * @return JDatabaseQuery + * @return FOFDatabaseQuery */ protected function buildQuery() { diff --git a/libraries/fof/table/relations.php b/libraries/fof/table/relations.php index fd548e314c59d..a431bf7598ef5 100644 --- a/libraries/fof/table/relations.php +++ b/libraries/fof/table/relations.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage table - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ diff --git a/libraries/fof/table/table.php b/libraries/fof/table/table.php index 33c0eee4c9150..9f56505496f3f 100644 --- a/libraries/fof/table/table.php +++ b/libraries/fof/table/table.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage table - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -69,9 +69,9 @@ class FOFTable extends FOFUtilsObject implements JTableInterface protected $_tbl_key = ''; /** - * JDatabaseDriver object. + * FOFDatabaseDriver object. * - * @var JDatabaseDriver + * @var FOFDatabaseDriver */ protected $_db; @@ -166,7 +166,7 @@ class FOFTable extends FOFUtilsObject implements JTableInterface /** * Extended query including joins with other tables * - * @var JDatabaseQuery + * @var FOFDatabaseQuery */ protected $_queryJoin = null; @@ -509,7 +509,7 @@ public static function forceInstance($key = null, $instance = null) * * @param string $table Name of the database table to model. * @param string $key Name of the primary key field in the table. - * @param JDatabaseDriver &$db Database driver + * @param FOFDatabaseDriver &$db Database driver * @param array $config The configuration parameters array */ public function __construct($table, $key, &$db, $config = array()) @@ -1130,7 +1130,7 @@ public function canDelete($oid = null, $joins = null) { $obj = $this->_db->loadObject(); } - catch (JDatabaseException $e) + catch (Exception $e) { $this->setError($e->getMessage()); } @@ -1285,20 +1285,35 @@ public function store($updateNulls = false) } $updateObject = (object)$updateObject; - // If a primary key exists update the object, otherwise insert it. - if ($this->$k) - { - $result = $this->_db->updateObject($this->_tbl, $updateObject, $this->_tbl_key, $updateNulls); - } - else + /** + * While the documentation for update/insertObject and execute() say they return a boolean, + * not all of the implemtnations. Depending on the version of J! and the specific driver, + * they may return a database object, or boolean, or a mix, or toss an exception. So try/catch, + * and test for false. + */ + + try { - $result = $this->_db->insertObject($this->_tbl, $updateObject, $this->_tbl_key); - } + // If a primary key exists update the object, otherwise insert it. + if ($this->$k) + { + $result = $this->_db->updateObject($this->_tbl, $updateObject, $this->_tbl_key, $updateNulls); + } + else + { + $result = $this->_db->insertObject($this->_tbl, $updateObject, $this->_tbl_key); + } - if ($result !== true) + if ($result === false) + { + $this->setError($this->_db->getErrorMsg()); + + return false; + } + } + catch (Exception $e) { - $this->setError($this->_db->getErrorMsg()); - return false; + $this->setError($e->getMessage()); } $this->bind($updateObject); @@ -1533,7 +1548,16 @@ public function checkout($userId, $oid = null) } $date = FOFPlatform::getInstance()->getDate(); - $time = $date->toSql(); + + if (method_exists($date, 'toSql')) + { + $time = $date->toSql(); + } + else + { + $time = $date->toMySQL(); + } + $query = $this->_db->getQuery(true) ->update($this->_db->qn($this->_tbl)) @@ -1802,7 +1826,7 @@ public function publish($cid = null, $publish = 1, $user_id = 0) { $this->_db->execute(); } - catch (JDatabaseException $e) + catch (Exception $e) { $this->setError($e->getMessage()); } @@ -2247,7 +2271,7 @@ public function setColumnAlias($column, $columnAlias) * * @param boolean $asReference Return an object reference instead of a copy * - * @return JDatabaseQuery Query used to join other tables + * @return FOFDatabaseQuery Query used to join other tables */ public function getQueryJoin($asReference = false) { @@ -2271,11 +2295,11 @@ public function getQueryJoin($asReference = false) /** * Sets the query with joins to other tables * - * @param JDatabaseQuery $query The JOIN query to use + * @param FOFDatabaseQuery $query The JOIN query to use * * @return void */ - public function setQueryJoin(JDatabaseQuery $query) + public function setQueryJoin($query) { $this->_queryJoin = $query; } @@ -2582,7 +2606,7 @@ protected function onBeforeStore($updateNulls) $date = FOFPlatform::getInstance()->getDate('now', null, false); - $this->$created_on = $date->toSql(); + $this->$created_on = method_exists($date, 'toSql') ? $date->toSql() : $date->toMySQL(); } elseif ($hasModifiedOn && $hasModifiedBy) { @@ -2595,7 +2619,7 @@ protected function onBeforeStore($updateNulls) $date = FOFPlatform::getInstance()->getDate('now', null, false); - $this->$modified_on = $date->toSql(); + $this->$modified_on = method_exists($date, 'toSql') ? $date->toSql() : $date->toMySQL(); } } @@ -3429,9 +3453,9 @@ public function getId() } /** - * Method to get the JDatabaseDriver object. + * Method to get the FOFDatabaseDriver object. * - * @return JDatabaseDriver The internal database driver object. + * @return FOFDatabaseDriver The internal database driver object. */ public function getDbo() { @@ -3439,13 +3463,13 @@ public function getDbo() } /** - * Method to set the JDatabaseDriver object. + * Method to set the FOFDatabaseDriver object. * - * @param JDatabaseDriver $db A JDatabaseDriver object to be used by the table object. + * @param FOFDatabaseDriver $db A FOFDatabaseDriver object to be used by the table object. * * @return boolean True on success. */ - public function setDBO(JDatabaseDriver $db) + public function setDBO($db) { $this->_db = $db; diff --git a/libraries/fof/template/utils.php b/libraries/fof/template/utils.php index 511dd86095da1..797a53b66a5d3 100644 --- a/libraries/fof/template/utils.php +++ b/libraries/fof/template/utils.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage template - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/toolbar/toolbar.php b/libraries/fof/toolbar/toolbar.php index 95ee37ff6d065..345aaf9645260 100644 --- a/libraries/fof/toolbar/toolbar.php +++ b/libraries/fof/toolbar/toolbar.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage toolbar - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -210,7 +210,15 @@ public function __construct($config = array()) if (version_compare(JVERSION, '3.0', 'ge')) { JHtml::_('jquery.framework'); - JHtml::_('behavior.core'); + + if (version_compare(JVERSION, '3.3.0', 'ge')) + { + JHtml::_('behavior.core'); + } + else + { + JHtml::_('behavior.framework', true); + } } else { @@ -337,8 +345,8 @@ public function onCpanelsBrowse() $option = $this->input->getCmd('option', 'com_foobar'); - JToolBarHelper::title(JText::_(strtoupper($option)), str_replace('com_', '', $option)); - JToolBarHelper::preferences($option, 550, 875); + JToolbarHelper::title(JText::_(strtoupper($option)), str_replace('com_', '', $option)); + JToolbarHelper::preferences($option, 550, 875); } /** @@ -362,18 +370,18 @@ public function onBrowse() // Set toolbar title $option = $this->input->getCmd('option', 'com_foobar'); $subtitle_key = strtoupper($option . '_TITLE_' . $this->input->getCmd('view', 'cpanel')); - JToolBarHelper::title(JText::_(strtoupper($option)) . ': ' . JText::_($subtitle_key), str_replace('com_', '', $option)); + JToolbarHelper::title(JText::_(strtoupper($option)) . ': ' . JText::_($subtitle_key), str_replace('com_', '', $option)); // Add toolbar buttons if ($this->perms->create) { if (version_compare(JVERSION, '3.0', 'ge')) { - JToolBarHelper::addNew(); + JToolbarHelper::addNew(); } else { - JToolBarHelper::addNewX(); + JToolbarHelper::addNewX(); } } @@ -381,30 +389,30 @@ public function onBrowse() { if (version_compare(JVERSION, '3.0', 'ge')) { - JToolBarHelper::editList(); + JToolbarHelper::editList(); } else { - JToolBarHelper::editListX(); + JToolbarHelper::editListX(); } } if ($this->perms->create || $this->perms->edit) { - JToolBarHelper::divider(); + JToolbarHelper::divider(); } if ($this->perms->editstate) { - JToolBarHelper::publishList(); - JToolBarHelper::unpublishList(); - JToolBarHelper::divider(); + JToolbarHelper::publishList(); + JToolbarHelper::unpublishList(); + JToolbarHelper::divider(); } if ($this->perms->delete) { $msg = JText::_($this->input->getCmd('option', 'com_foobar') . '_CONFIRM_DELETE'); - JToolBarHelper::deleteList(strtoupper($msg)); + JToolbarHelper::deleteList(strtoupper($msg)); } } @@ -431,10 +439,10 @@ public function onRead() // Set toolbar title $subtitle_key = strtoupper($option . '_TITLE_' . $this->input->getCmd('view', 'cpanel') . '_READ'); - JToolBarHelper::title(JText::_(strtoupper($option)) . ': ' . JText::_($subtitle_key), $componentName); + JToolbarHelper::title(JText::_(strtoupper($option)) . ': ' . JText::_($subtitle_key), $componentName); // Set toolbar icons - JToolBarHelper::back(); + JToolbarHelper::back(); } /** @@ -455,24 +463,24 @@ public function onAdd() // Set toolbar title $subtitle_key = strtoupper($option . '_TITLE_' . FOFInflector::pluralize($this->input->getCmd('view', 'cpanel'))) . '_EDIT'; - JToolBarHelper::title(JText::_(strtoupper($option)) . ': ' . JText::_($subtitle_key), $componentName); + JToolbarHelper::title(JText::_(strtoupper($option)) . ': ' . JText::_($subtitle_key), $componentName); // Set toolbar icons if ($this->perms->edit || $this->perms->editown) { // Show the apply button only if I can edit the record, otherwise I'll return to the edit form and get a // 403 error since I can't do that - JToolBarHelper::apply(); + JToolbarHelper::apply(); } - JToolBarHelper::save(); + JToolbarHelper::save(); if ($this->perms->create) { - JToolBarHelper::custom('savenew', 'save-new.png', 'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false); + JToolbarHelper::custom('savenew', 'save-new.png', 'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false); } - JToolBarHelper::cancel(); + JToolbarHelper::cancel(); } /** diff --git a/libraries/fof/utils/array/array.php b/libraries/fof/utils/array/array.php index 4cfb26d28594f..7be251f77adf4 100644 --- a/libraries/fof/utils/array/array.php +++ b/libraries/fof/utils/array/array.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage utils - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ @@ -461,7 +461,7 @@ public static function pivot($source, $key = null) * Utility function to sort an array of objects on a given field * * @param array &$a An array of objects - * @param mixed $k The key (string) or an array of keys to sort on + * @param mixed $k The key (string) or a array of key to sort on * @param mixed $direction Direction (integer) or an array of direction to sort in [1 = Ascending] [-1 = Descending] * @param mixed $caseSensitive Boolean or array of booleans to let sort occur case sensitive or insensitive * @param mixed $locale Boolean or array of booleans to let sort occur using the locale language or not diff --git a/libraries/fof/utils/cache/cleaner.php b/libraries/fof/utils/cache/cleaner.php index 7edaa7b0ab3ff..8b0272a6934b6 100644 --- a/libraries/fof/utils/cache/cleaner.php +++ b/libraries/fof/utils/cache/cleaner.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage utils - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ diff --git a/libraries/fof/utils/config/helper.php b/libraries/fof/utils/config/helper.php new file mode 100644 index 0000000000000..fb5340bac2693 --- /dev/null +++ b/libraries/fof/utils/config/helper.php @@ -0,0 +1,89 @@ +getDbo(); + + $sql = $db->getQuery(true) + ->select($db->qn('params')) + ->from($db->qn('#__extensions')) + ->where($db->qn('type') . ' = ' . $db->q('component')) + ->where($db->qn('element') . " = " . $db->q($component)); + $db->setQuery($sql); + $config_ini = $db->loadResult(); + + // OK, Joomla! 1.6 stores values JSON-encoded so, what do I do? Right! + $config_ini = trim($config_ini); + + if ((substr($config_ini, 0, 1) == '{') && substr($config_ini, -1) == '}') + { + $config_ini = json_decode($config_ini, true); + } + else + { + $config_ini = FOFUtilsIniParser::parse_ini_file($config_ini, false, true); + } + + if (is_null($config_ini) || empty($config_ini)) + { + $config_ini = array(); + } + + self::$componentParams[$component] = $config_ini; + } + + /** + * Retrieves the value of a component configuration parameter without going through JComponentHelper + * + * @param string $component The component for loading the parameter value + * @param string $key The key to retrieve + * @param mixed $default The default value to use in case the key is missing + * + * @return mixed + */ + public final static function getComponentConfigurationValue($component, $key, $default = null) + { + self::loadComponentConfig($component, false); + + if (array_key_exists($key, self::$componentParams[$component])) + { + return self::$componentParams[$component][$key]; + } + else + { + return $default; + } + } +} \ No newline at end of file diff --git a/libraries/fof/utils/filescheck/filescheck.php b/libraries/fof/utils/filescheck/filescheck.php index 2791eceb00019..78d48d23bcdd5 100644 --- a/libraries/fof/utils/filescheck/filescheck.php +++ b/libraries/fof/utils/filescheck/filescheck.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage utils - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ @@ -68,7 +68,7 @@ public function __construct($option, $version, $date) $this->date = $date; // Retrieve the date and version from the #__extensions table - $db = JFactory::getDbo(); + $db = FOFPlatform::getInstance()->getDbo(); $query = $db->getQuery(true)->select('*')->from($db->qn('#__extensions')) ->where($db->qn('element') . ' = ' . $db->q($this->option)) ->where($db->qn('type') . ' = ' . $db->q('component')); diff --git a/libraries/fof/utils/ini/parser.php b/libraries/fof/utils/ini/parser.php new file mode 100644 index 0000000000000..2c8efdf1f83ae --- /dev/null +++ b/libraries/fof/utils/ini/parser.php @@ -0,0 +1,199 @@ +getParent()->getPath('source'); + $cliPath = JPATH_ROOT . '/cli'; + + if (!JFolder::exists($cliPath)) + { + JFolder::create($cliPath); + } + foreach ($this->cliScriptFiles as $script) { - if (JFile::exists(JPATH_ROOT . '/cli/' . $script)) + if (JFile::exists($cliPath . '/' . $script)) { - JFile::delete(JPATH_ROOT . '/cli/' . $script); + JFile::delete($cliPath . '/' . $script); } if (JFile::exists($src . '/' . $this->cliSourcePath . '/' . $script)) { - JFile::copy($src . '/' . $this->cliSourcePath . '/' . $script, JPATH_ROOT . '/cli/' . $script); + JFile::copy($src . '/' . $this->cliSourcePath . '/' . $script, $cliPath . '/' . $script); } } } @@ -605,7 +618,7 @@ protected function renderPostUninstallation($status, $parent) */ protected function bugfixDBFunctionReturnedNoError() { - $db = JFactory::getDbo(); + $db = FOFPlatform::getInstance()->getDbo(); // Fix broken #__assets records $query = $db->getQuery(true); @@ -647,6 +660,7 @@ protected function bugfixDBFunctionReturnedNoError() $query = $db->getQuery(true); $query->select('extension_id') ->from('#__extensions') + ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('element') . ' = ' . $db->q($this->componentName)); $db->setQuery($query); $ids = $db->loadColumn(); @@ -707,12 +721,13 @@ protected function bugfixDBFunctionReturnedNoError() */ protected function bugfixCantBuildAdminMenus() { - $db = JFactory::getDbo(); + $db = FOFPlatform::getInstance()->getDbo(); // If there are multiple #__extensions record, keep one of them $query = $db->getQuery(true); $query->select('extension_id') ->from('#__extensions') + ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('element') . ' = ' . $db->q($this->componentName)); $db->setQuery($query); @@ -862,7 +877,7 @@ protected function installSubextensions($parent) { $src = $parent->getParent()->getPath('source'); - $db = JFactory::getDbo(); + $db = FOFPlatform::getInstance()->getDbo();; $status = new JObject(); $status->modules = array(); @@ -1108,7 +1123,7 @@ protected function installSubextensions($parent) */ protected function uninstallSubextensions($parent) { - $db = JFactory::getDBO(); + $db = FOFPlatform::getInstance()->getDbo(); $status = new stdClass(); $status->modules = array(); @@ -1271,11 +1286,11 @@ protected function installFOF($parent) // Get the target path if (!defined('JPATH_LIBRARIES')) { - $target = JPATH_ROOT . '/libraries/fof'; + $target = JPATH_ROOT . '/libraries/f0f'; } else { - $target = JPATH_LIBRARIES . '/fof'; + $target = JPATH_LIBRARIES . '/f0f'; } // Do I have to install FOF? @@ -1515,7 +1530,7 @@ protected function uninstallObsoleteSubextensions($parent) { JLoader::import('joomla.installer.installer'); - $db = JFactory::getDBO(); + $db = FOFPlatform::getInstance()->getDbo(); $status = new stdClass(); $status->modules = array(); @@ -1601,7 +1616,8 @@ protected function uninstallObsoleteSubextensions($parent) */ private function _createAdminMenus($parent) { - $db = $parent->getParent()->getDbo(); + $db = $db = FOFPlatform::getInstance()->getDbo(); + /** @var JTableMenu $table */ $table = JTable::getInstance('menu'); $option = $parent->get('element'); @@ -1613,6 +1629,7 @@ private function _createAdminMenus($parent) ->join('LEFT', '#__extensions AS e ON m.component_id = e.extension_id') ->where('m.parent_id = 1') ->where('m.client_id = 1') + ->where($db->qn('e') . '.' . $db->qn('type') . ' = ' . $db->q('component')) ->where('e.element = ' . $db->quote($option)); $db->setQuery($query); @@ -1630,6 +1647,7 @@ private function _createAdminMenus($parent) $query->clear() ->select('e.extension_id') ->from('#__extensions AS e') + ->where('e.type = ' . $db->quote('component')) ->where('e.element = ' . $db->quote($option)); $db->setQuery($query); $component_id = $db->loadResult(); @@ -1710,6 +1728,8 @@ private function _createAdminMenus($parent) $data['component_id'] = $component_id; $data['img'] = ((string)$menuElement->attributes()->img) ? (string)$menuElement->attributes()->img : 'class:component'; $data['home'] = 0; + $data['path'] = ''; + $data['params'] = ''; } // No menu element was specified, Let's make a generic menu item else @@ -1726,6 +1746,8 @@ private function _createAdminMenus($parent) $data['component_id'] = $component_id; $data['img'] = 'class:component'; $data['home'] = 0; + $data['path'] = ''; + $data['params'] = ''; } try @@ -1734,7 +1756,10 @@ private function _createAdminMenus($parent) } catch (InvalidArgumentException $e) { - JLog::add($e->getMessage(), JLog::WARNING, 'jerror'); + if (class_exists('JLog')) + { + JLog::add($e->getMessage(), JLog::WARNING, 'jerror'); + } return false; } @@ -1912,7 +1937,8 @@ private function _createAdminMenus($parent) */ private function _reallyPublishAdminMenuItems($parent) { - $db = $parent->getParent()->getDbo(); + $db = FOFPlatform::getInstance()->getDbo(); + $option = $parent->get('element'); $query = $db->getQuery(true) @@ -1921,6 +1947,7 @@ private function _reallyPublishAdminMenuItems($parent) ->set($db->qn('published') . ' = ' . $db->q(1)) ->where('m.parent_id = 1') ->where('m.client_id = 1') + ->where('e.type = ' . $db->quote('component')) ->where('e.element = ' . $db->quote($option)); $db->setQuery($query); @@ -1943,7 +1970,7 @@ private function _rebuildMenu() { /** @var JTableMenu $table */ $table = JTable::getInstance('menu'); - $db = $table->getDbo(); + $db = FOFPlatform::getInstance()->getDbo(); // We need to rebuild the menu based on its root item. By default this is the menu item with ID=1. However, some // crappy upgrade scripts enjoy screwing it up. Hey, ho, the workaround way I go. @@ -2216,7 +2243,7 @@ protected function addPostInstallationMessage(array $options) // Check if the definition exists $tableName = '#__postinstall_messages'; - $db = JFactory::getDbo(); + $db = FOFPlatform::getInstance()->getDbo(); $query = $db->getQuery(true) ->select('*') ->from($db->qn($tableName)) @@ -2284,10 +2311,11 @@ protected function _applyPostInstallationMessages() } // Get the extension ID for our component - $db = JFactory::getDbo(); + $db = FOFPlatform::getInstance()->getDbo(); $query = $db->getQuery(true); $query->select('extension_id') ->from('#__extensions') + ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('element') . ' = ' . $db->q($this->componentName)); $db->setQuery($query); @@ -2329,10 +2357,11 @@ protected function uninstallPostInstallationMessages() } // Get the extension ID for our component - $db = JFactory::getDbo(); + $db = FOFPlatform::getInstance()->getDbo(); $query = $db->getQuery(true); $query->select('extension_id') ->from('#__extensions') + ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('element') . ' = ' . $db->q($this->componentName)); $db->setQuery($query); @@ -2353,7 +2382,7 @@ protected function uninstallPostInstallationMessages() $extension_id = array_shift($ids); $query = $db->getQuery(true) - ->delete($this->postInstallationMessages) + ->delete($db->qn('#__postinstall_messages')) ->where($db->qn('extension_id') . ' = ' . $db->q($extension_id)); try diff --git a/libraries/fof/utils/ip/ip.php b/libraries/fof/utils/ip/ip.php index aa68ae266e140..40f000944fe0d 100644 --- a/libraries/fof/utils/ip/ip.php +++ b/libraries/fof/utils/ip/ip.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage utils - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ @@ -16,6 +16,13 @@ abstract class FOFUtilsIp /** @var string The IP address of the current visitor */ protected static $ip = null; + /** + * Should I allow IP overrides through X-Forwarded-For or Client-Ip HTTP headers? + * + * @var bool + */ + protected static $allowIpOverrides = true; + /** * Get the current visitor's IP address * @@ -362,6 +369,18 @@ public static function workaroundIPIssues() $_SERVER['REMOTE_ADDR'] = $ip; } + /** + * Should I allow the remote client's IP to be overridden by an X-Forwarded-For or Client-Ip HTTP header? + * + * @param bool $newState True to allow the override + * + * @return void + */ + public static function setAllowIpOverrides($newState) + { + self::$allowIpOverrides = $newState ? true : false; + } + /** * Gets the visitor's IP address. Automatically handles reverse proxies * reporting the IPs of intermediate devices, like load balancers. Examples: @@ -406,13 +425,13 @@ protected static function detectIP() if (isset($_SERVER)) { // Do we have an x-forwarded-for HTTP header (e.g. NginX)? - if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) + if (self::$allowIpOverrides && array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) { return $_SERVER['HTTP_X_FORWARDED_FOR']; } // Do we have a client-ip header (e.g. non-transparent proxy)? - if (array_key_exists('HTTP_CLIENT_IP', $_SERVER)) + if (self::$allowIpOverrides && array_key_exists('HTTP_CLIENT_IP', $_SERVER)) { return $_SERVER['HTTP_CLIENT_IP']; } @@ -430,13 +449,13 @@ protected static function detectIP() } // Do we have an x-forwarded-for HTTP header? - if (getenv('HTTP_X_FORWARDED_FOR')) + if (self::$allowIpOverrides && getenv('HTTP_X_FORWARDED_FOR')) { return getenv('HTTP_X_FORWARDED_FOR'); } // Do we have a client-ip header? - if (getenv('HTTP_CLIENT_IP')) + if (self::$allowIpOverrides && getenv('HTTP_CLIENT_IP')) { return getenv('HTTP_CLIENT_IP'); } diff --git a/libraries/fof/utils/object/object.php b/libraries/fof/utils/object/object.php index d62df2dc07bd7..f3fe186a2bf53 100644 --- a/libraries/fof/utils/object/object.php +++ b/libraries/fof/utils/object/object.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage utils - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ diff --git a/libraries/fof/utils/observable/dispatcher.php b/libraries/fof/utils/observable/dispatcher.php index b093f5aef1b87..6dbe38aa94d0d 100644 --- a/libraries/fof/utils/observable/dispatcher.php +++ b/libraries/fof/utils/observable/dispatcher.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage utils - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ diff --git a/libraries/fof/utils/observable/event.php b/libraries/fof/utils/observable/event.php index 78fde068cfe26..49bc8f22a419d 100644 --- a/libraries/fof/utils/observable/event.php +++ b/libraries/fof/utils/observable/event.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage utils - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ diff --git a/libraries/fof/utils/phpfunc/phpfunc.php b/libraries/fof/utils/phpfunc/phpfunc.php new file mode 100644 index 0000000000000..f7311994596d1 --- /dev/null +++ b/libraries/fof/utils/phpfunc/phpfunc.php @@ -0,0 +1,39 @@ +getExtensionUpdateSource($url, 'component', 'com_foobar', JVERSION); + * $extension = new CmsupdateHelperExtension(); + * $updates = $extension->getUpdatesFromExtension($extensionUpdateURL); + * + * @param string $url The extension XML update source URL to read from + * + * @return array An array of update entries + */ + public function getUpdatesFromExtension($url) + { + // Initialise + $ret = array(); + + // Get and parse the XML source + $downloader = new FOFDownload(); + $xmlSource = $downloader->getFromURL($url); + + try + { + $xml = new SimpleXMLElement($xmlSource, LIBXML_NONET); + } + catch (Exception $e) + { + return $ret; + } + + // Sanity check + if (($xml->getName() != 'updates')) + { + unset($xml); + + return $ret; + } + + // Let's populate the list of updates + /** @var SimpleXMLElement $update */ + foreach ($xml->children() as $update) + { + // Sanity check + if ($update->getName() != 'update') + { + continue; + } + + $entry = array( + 'infourl' => array('title' => '', 'url' => ''), + 'downloads' => array(), + 'tags' => array(), + 'targetplatform' => array(), + ); + + $properties = get_object_vars($update); + + foreach ($properties as $nodeName => $nodeContent) + { + switch ($nodeName) + { + default: + $entry[ $nodeName ] = $nodeContent; + break; + + case 'infourl': + case 'downloads': + case 'tags': + case 'targetplatform': + break; + } + } + + $infourlNode = $update->xpath('infourl'); + $entry['infourl']['title'] = (string) $infourlNode[0]['title']; + $entry['infourl']['url'] = (string) $infourlNode[0]; + + $downloadNodes = $update->xpath('downloads/downloadurl'); + foreach ($downloadNodes as $downloadNode) + { + $entry['downloads'][] = array( + 'type' => (string) $downloadNode['type'], + 'format' => (string) $downloadNode['format'], + 'url' => (string) $downloadNode, + ); + } + + $tagNodes = $update->xpath('tags/tag'); + foreach ($tagNodes as $tagNode) + { + $entry['tags'][] = (string) $tagNode; + } + + /** @var SimpleXMLElement[] $targetPlatformNode */ + $targetPlatformNode = $update->xpath('targetplatform'); + + $entry['targetplatform']['name'] = (string) $targetPlatformNode[0]['name']; + $entry['targetplatform']['version'] = (string) $targetPlatformNode[0]['version']; + $client = $targetPlatformNode[0]->xpath('client'); + $entry['targetplatform']['client'] = (is_array($client) && count($client)) ? (string) $client[0] : ''; + $folder = $targetPlatformNode[0]->xpath('folder'); + $entry['targetplatform']['folder'] = is_array($folder) && count($folder) ? (string) $folder[0] : ''; + + $ret[] = $entry; + } + + unset($xml); + + return $ret; + } + /** * Reads a "collection" XML update source and picks the correct source URL * for the extension update source. * - * @param string $url The collection XML update source URL to read from - * @param string $jVersion Joomla! version to fetch updates for, or null to use JVERSION + * @param string $url The collection XML update source URL to read from + * @param string $jVersion Joomla! version to fetch updates for, or null to use JVERSION * * @return string The URL of the extension update source, or empty if no updates are provided / fetching failed */ public function getUpdateSourceFromCollection($url, $jVersion = null) { - $provider = new FOFUtilsUpdateCollection; + $provider = new FOFUtilsUpdateCollection(); + return $provider->getExtensionUpdateSource($url, 'file', 'joomla', $jVersion); } @@ -63,10 +176,14 @@ public function getVersionProperties($jVersion, $currentVersion = null) { // Initialise $ret = array( - 'lts' => true, // Is this an LTS release? False means STS. - 'current' => false, // Is this a release in the $currentVersion branch? - 'upgrade' => 'none', // Upgrade relation of $jVersion to $currentVersion: 'none' (can't upgrade), 'lts' (next or current LTS), 'sts' (next or current STS) or 'current' (same release, no upgrade available) - 'testing' => false, // Is this a testing (alpha, beta, RC) release? + 'lts' => true, + // Is this an LTS release? False means STS. + 'current' => false, + // Is this a release in the $currentVersion branch? + 'upgrade' => 'none', + // Upgrade relation of $jVersion to $currentVersion: 'none' (can't upgrade), 'lts' (next or current LTS), 'sts' (next or current STS) or 'current' (same release, no upgrade available) + 'testing' => false, + // Is this a testing (alpha, beta, RC) release? ); // Get the current version if none is defined @@ -76,8 +193,10 @@ public function getVersionProperties($jVersion, $currentVersion = null) } // Sanitise version numbers - $jVersion = $this->sanitiseVersion($jVersion); + $sameVersion = $jVersion == $currentVersion; + $jVersion = $this->sanitiseVersion($jVersion); $currentVersion = $this->sanitiseVersion($currentVersion); + $sameVersion = $sameVersion || ($jVersion == $currentVersion); // Get the base version $baseVersion = substr($jVersion, 0, 3); @@ -99,40 +218,31 @@ public function getVersionProperties($jVersion, $currentVersion = null) break; case '1.6': - $ret['lts'] = false; + $ret['lts'] = false; $sts_minimum = '1.7'; $sts_maximum = '1.7.999'; $lts_minimum = '2.5'; break; case '1.7': - $ret['lts'] = false; + $ret['lts'] = false; + $sts_minimum = false; + $lts_minimum = '2.5'; + break; + + case '2.5': + $ret['lts'] = true; $sts_minimum = false; $lts_minimum = '2.5'; break; default: - $majorVersion = (int)substr($jVersion, 0, 1); - $minorVersion = (int)substr($jVersion, 2, 1); + $majorVersion = (int) substr($jVersion, 0, 1); + //$minorVersion = (int) substr($jVersion, 2, 1); - if ($minorVersion == 5) - { - $ret['lts'] = true; - // This is an LTS release, it can be superseded by .0 through .4 STS releases on the next branch... - $sts_minimum = ($majorVersion + 1) . '.0'; - $sts_maximum = ($majorVersion + 1) . '.4.9999'; - // ...or a .5 LTS on the next branch - $lts_minimum = ($majorVersion + 1) . '.5'; - } - else - { - $ret['lts'] = false; - // This is an STS release, it can be superseded by a .1/.2/.3/.4 STS release on the same branch... - $sts_minimum = $majorVersion . '.1'; - $sts_maximum = $majorVersion . '.4.9999'; - // ...or a .5 LTS on the same branch - $lts_minimum = $majorVersion . '.5'; - } + $ret['lts'] = true; + $sts_minimum = false; + $lts_minimum = $majorVersion . '.0'; break; } @@ -143,7 +253,7 @@ public function getVersionProperties($jVersion, $currentVersion = null) } // Is this a testing release? - $versionParts = explode('.', $jVersion); + $versionParts = explode('.', $jVersion); $lastVersionPart = array_pop($versionParts); if (in_array(substr($lastVersionPart, 0, 1), array('a', 'b'))) @@ -164,15 +274,15 @@ public function getVersionProperties($jVersion, $currentVersion = null) { $ret['upgrade'] = 'current'; } - elseif(($sts_minimum !== false) && version_compare($jVersion, $sts_minimum, 'ge') && version_compare($jVersion, $sts_maximum, 'le')) + elseif (($sts_minimum !== false) && version_compare($jVersion, $sts_minimum, 'ge') && version_compare($jVersion, $sts_maximum, 'le')) { $ret['upgrade'] = 'sts'; } - elseif(($lts_minimum !== false) && version_compare($jVersion, $lts_minimum, 'ge')) + elseif (($lts_minimum !== false) && version_compare($jVersion, $lts_minimum, 'ge')) { $ret['upgrade'] = 'lts'; } - elseif($baseVersion == $current_minimum) + elseif ($baseVersion == $current_minimum) { $ret['upgrade'] = $ret['lts'] ? 'lts' : 'sts'; } @@ -181,6 +291,11 @@ public function getVersionProperties($jVersion, $currentVersion = null) $ret['upgrade'] = 'none'; } + if ($sameVersion) + { + $ret['upgrade'] = 'none'; + } + return $ret; } @@ -189,8 +304,8 @@ public function getVersionProperties($jVersion, $currentVersion = null) * Filters a list of updates, making sure they apply to the specifed CMS * release. * - * @param array $updates A list of update records returned by the getUpdatesFromExtension method - * @param string $jVersion The current Joomla! version number + * @param array $updates A list of update records returned by the getUpdatesFromExtension method + * @param string $jVersion The current Joomla! version number * * @return array A filtered list of updates. Each update record also includes version relevance information. */ @@ -201,11 +316,11 @@ public function filterApplicableUpdates($updates, $jVersion = null) $jVersion = JVERSION; } - $versionParts = explode('.', $jVersion, 4); - $platformVersionMajor = $versionParts[0]; - $platformVersionMinor = $platformVersionMajor . '.' . $versionParts[1]; + $versionParts = explode('.', $jVersion, 4); + $platformVersionMajor = $versionParts[0]; + $platformVersionMinor = $platformVersionMajor . '.' . $versionParts[1]; $platformVersionNormal = $platformVersionMinor . '.' . $versionParts[2]; - $platformVersionFull = (count($versionParts) > 3) ? $platformVersionNormal . '.' . $versionParts[3] : $platformVersionNormal; + //$platformVersionFull = (count($versionParts) > 3) ? $platformVersionNormal . '.' . $versionParts[3] : $platformVersionNormal; $ret = array(); @@ -219,13 +334,13 @@ public function filterApplicableUpdates($updates, $jVersion = null) $targetPlatformVersion = $update['targetplatform']['version']; - if (($targetPlatformVersion !== $platformVersionMajor) && ($targetPlatformVersion !== $platformVersionMinor) && ($targetPlatformVersion !== $platformVersionNormal) && ($targetPlatformVersion !== $platformVersionFull)) + if (!preg_match('/' . $targetPlatformVersion . '/', $platformVersionMinor)) { continue; } // Get some information from the version number - $updateVersion = $update['version']; + $updateVersion = $update['version']; $versionProperties = $this->getVersionProperties($updateVersion, $jVersion); if ($versionProperties['upgrade'] == 'none') @@ -236,7 +351,7 @@ public function filterApplicableUpdates($updates, $jVersion = null) // The XML files are ill-maintained. Maybe we already have this update? if (!array_key_exists($updateVersion, $ret)) { - $ret[$updateVersion] = array_merge($update, $versionProperties); + $ret[ $updateVersion ] = array_merge($update, $versionProperties); } } @@ -250,18 +365,20 @@ public function filterApplicableUpdates($updates, $jVersion = null) * figure out what was in the mind of the maintainer and translate the * funky version number to an actual PHP-format version string. * - * @param string $version The whatever-format version number + * @param string $version The whatever-format version number * * @return string A standard formatted version number */ public function sanitiseVersion($version) { - $test = strtolower($version); + $test = strtolower($version); $alphaQualifierPosition = strpos($test, 'alpha-'); - $betaQualifierPosition = strpos($test, 'beta-'); - $rcQualifierPosition = strpos($test, 'rc-'); - $rcQualifierPosition2 = strpos($test, 'rc'); - $devQualifiedPosition = strpos($test, 'dev'); + $betaQualifierPosition = strpos($test, 'beta-'); + $betaQualifierPosition2 = strpos($test, '-beta'); + $rcQualifierPosition = strpos($test, 'rc-'); + $rcQualifierPosition2 = strpos($test, '-rc'); + $rcQualifierPosition3 = strpos($test, 'rc'); + $devQualifiedPosition = strpos($test, 'dev'); if ($alphaQualifierPosition !== false) { @@ -281,6 +398,17 @@ public function sanitiseVersion($version) } $test = substr($test, 0, $betaQualifierPosition) . '.b' . $betaRevision; } + elseif ($betaQualifierPosition2 !== false) + { + $betaRevision = substr($test, $betaQualifierPosition2 + 5); + + if (!$betaRevision) + { + $betaRevision = 1; + } + + $test = substr($test, 0, $betaQualifierPosition2) . '.b' . $betaRevision; + } elseif ($rcQualifierPosition !== false) { $betaRevision = substr($test, $rcQualifierPosition + 5); @@ -292,13 +420,26 @@ public function sanitiseVersion($version) } elseif ($rcQualifierPosition2 !== false) { - $betaRevision = substr($test, $rcQualifierPosition2 + 5); + $betaRevision = substr($test, $rcQualifierPosition2 + 3); + if (!$betaRevision) { $betaRevision = 1; } + $test = substr($test, 0, $rcQualifierPosition2) . '.rc' . $betaRevision; } + elseif ($rcQualifierPosition3 !== false) + { + $betaRevision = substr($test, $rcQualifierPosition3 + 5); + + if (!$betaRevision) + { + $betaRevision = 1; + } + + $test = substr($test, 0, $rcQualifierPosition3) . '.rc' . $betaRevision; + } elseif ($devQualifiedPosition !== false) { $betaRevision = substr($test, $devQualifiedPosition + 6); @@ -316,8 +457,8 @@ public function sanitiseVersion($version) * Reloads the list of all updates available for the specified Joomla! version * from the network. * - * @param array $sources The enabled sources to look into - * @param string $jVersion The Joomla! version we are checking updates for + * @param array $sources The enabled sources to look into + * @param string $jVersion The Joomla! version we are checking updates for * * @return array A list of updates for the installed, current, lts and sts versions */ @@ -340,13 +481,14 @@ public function getUpdates($sources = array(), $jVersion = null) } // Get the current branch' min/max versions - $versionParts = explode('.', $jVersion, 4); + $versionParts = explode('.', $jVersion, 4); $currentMinVersion = $versionParts[0] . '.' . $versionParts[1]; $currentMaxVersion = $versionParts[0] . '.' . $versionParts[1] . '.9999'; // Retrieve all updates $allUpdates = array(); + foreach ($sources as $source => $value) { if (($value === false) || empty($value)) @@ -368,6 +510,7 @@ public function getUpdates($sources = array(), $jVersion = null) $url = self::$test_url; break; + default: case 'custom': $url = $value; break; @@ -417,7 +560,7 @@ public function getUpdates($sources = array(), $jVersion = null) 'infourl' => '', ), // Upgrade to LTS release - 'test' => array( + 'test' => array( 'version' => '', 'package' => '', 'infourl' => '', @@ -432,7 +575,7 @@ public function getUpdates($sources = array(), $jVersion = null) { $sections[0] = 'installed'; } - elseif(version_compare($update['version'], $currentMinVersion, 'ge') && version_compare($update['version'], $currentMaxVersion, 'le')) + elseif (version_compare($update['version'], $currentMinVersion, 'ge') && version_compare($update['version'], $currentMaxVersion, 'le')) { $sections[0] = 'current'; } @@ -455,7 +598,7 @@ public function getUpdates($sources = array(), $jVersion = null) continue; } - $existingVersionForSection = $ret[$section]['version']; + $existingVersionForSection = $ret[ $section ]['version']; if (empty($existingVersionForSection)) { @@ -464,9 +607,9 @@ public function getUpdates($sources = array(), $jVersion = null) if (version_compare($update['version'], $existingVersionForSection, 'ge')) { - $ret[$section]['version'] = $update['version']; - $ret[$section]['package'] = $update['downloads'][0]['url']; - $ret[$section]['infourl'] = $update['infourl']['url']; + $ret[ $section ]['version'] = $update['version']; + $ret[ $section ]['package'] = $update['downloads'][0]['url']; + $ret[ $section ]['infourl'] = $update['infourl']['url']; } } } diff --git a/libraries/fof/utils/update/update.php b/libraries/fof/utils/update/update.php index 9bd04a5724b28..d459538fdf35c 100644 --- a/libraries/fof/utils/update/update.php +++ b/libraries/fof/utils/update/update.php @@ -2,13 +2,18 @@ /** * @package FrameworkOnFramework * @subpackage utils - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access defined('FOF_INCLUDED') or die; +if (version_compare(JVERSION, '2.5.0', 'lt')) +{ + jimport('joomla.updater.updater'); +} + /** * A helper Model to interact with Joomla!'s extensions update feature */ @@ -23,9 +28,12 @@ class FOFUtilsUpdate extends FOFModel /** @var string The currently installed version, as reported by the #__extensions table */ protected $version = 'dev'; - /** @var string The name of the component e.g. com_something */ + /** @var string The machine readable name of the component e.g. com_something */ protected $component = 'com_foobar'; + /** @var string The human readable name of the component e.g. Your Component's Name. Used for emails. */ + protected $componentDescription = 'Foobar'; + /** @var string The URL to the component's update XML stream */ protected $updateSite = null; @@ -35,6 +43,75 @@ class FOFUtilsUpdate extends FOFModel /** @var string The extra query to append to (commercial) components' download URLs */ protected $extraQuery = null; + /** @var string The common parameters' key, used for storing data in the #__akeeba_common table */ + protected $commonKey = 'foobar'; + + /** + * The common parameters table. It's a simple table with key(VARCHAR) and value(LONGTEXT) fields. + * Here is an example MySQL CREATE TABLE command to make this kind of table: + * + * CREATE TABLE `#__akeeba_common` ( + * `key` varchar(255) NOT NULL, + * `value` longtext NOT NULL, + * PRIMARY KEY (`key`) + * ) DEFAULT COLLATE utf8_general_ci CHARSET=utf8; + * + * @var string + */ + protected $commonTable = '#__akeeba_common'; + + /** + * Subject of the component update emails + * + * @var string + */ + protected $updateEmailSubject = 'THIS EMAIL IS SENT FROM YOUR SITE "[SITENAME]" - Update available for [COMPONENT], new version [VERSION]'; + + /** + * Body of the component update email + * + * @var string + */ + protected $updateEmailBody= <<< ENDBLOCK +This email IS NOT sent by the authors of [COMPONENT]. +It is sent automatically by your own site, [SITENAME]. + +================================================================================ +UPDATE INFORMATION +================================================================================ + +Your site has determined that there is an updated version of [COMPONENT] +available for download. + +New version number: [VERSION] + +This email is sent to you by your site to remind you of this fact. The authors +of the software will never contact you about available updates. + +================================================================================ +WHY AM I RECEIVING THIS EMAIL? +================================================================================ + +This email has been automatically sent by a CLI script or Joomla! plugin you, or +the person who built or manages your site, has installed and explicitly +activated. This script or plugin looks for updated versions of the software and +sends an email notification to all Super Users. You will receive several similar +emails from your site, up to 6 times per day, until you either update the +software or disable these emails. + +To disable these emails, please contact your site administrator. + +If you do not understand what this means, please do not contact the authors of +the software. They are NOT sending you this email and they cannot help you. +Instead, please contact the person who built or manages your site. + +================================================================================ +WHO SENT ME THIS EMAIL? +================================================================================ + +This email is sent to you by your own site, [SITENAME] +ENDBLOCK; + /** * Public constructor. Initialises the protected members as well. Useful $config keys: * update_component The component name, e.g. com_foobar @@ -62,12 +139,35 @@ public function __construct($config = array()) $this->component = $this->input->getCmd('option', ''); } + // Get the component description + if (isset($config['update_component_description'])) + { + $this->component = $config['update_component_description']; + } + else + { + // Try to auto-translate (hopefully you've loaded the language files) + $key = strtoupper($this->component); + $description = JText::_($key); + } + // Get the component version if (isset($config['update_version'])) { $this->version = $config['update_version']; } + // Get the common key + if (isset($config['common_key'])) + { + $this->commonKey = $config['common_key']; + } + else + { + // Convert com_foobar, pkg_foobar etc to "foobar" + $this->commonKey = substr($this->component, 4); + } + // Get the update site if (isset($config['update_site'])) { @@ -80,14 +180,14 @@ public function __construct($config = array()) $this->extraQuery = $config['update_extraquery']; } - // Get the extra query + // Get the update site's name if (isset($config['update_sitename'])) { $this->updateSiteName = $config['update_sitename']; } // Find the extension ID - $db = $this->getDbo(); + $db = FOFPlatform::getInstance()->getDbo(); $query = $db->getQuery(true) ->select('*') ->from($db->qn('#__extensions')) @@ -115,19 +215,19 @@ public function __construct($config = array()) * version The version of the available update * infoURL The URL to the download page of the update * - * @param bool $force Set to true if you want to forcibly reload the update information + * @param bool $force Set to true if you want to forcibly reload the update information + * @param string $preferredMethod Preferred update method: 'joomla' or 'classic' * * @return array See the method description for more information */ - public function getUpdates($force = false) + public function getUpdates($force = false, $preferredMethod = null) { - $db = $this->getDbo(); - // Default response (no update) $updateResponse = array( 'hasUpdate' => false, 'version' => '', - 'infoURL' => '' + 'infoURL' => '', + 'downloadURL' => '', ); if (empty($this->extension_id)) @@ -135,48 +235,7 @@ public function getUpdates($force = false) return $updateResponse; } - // If we are forcing the reload, set the last_check_timestamp to 0 - // and remove cached component update info in order to force a reload - if ($force) - { - // Find the update site IDs - $updateSiteIds = $this->getUpdateSiteIds(); - - if (empty($updateSiteIds)) - { - return $updateResponse; - } - - // Set the last_check_timestamp to 0 - $query = $db->getQuery(true) - ->update($db->qn('#__update_sites')) - ->set($db->qn('last_check_timestamp') . ' = ' . $db->q('0')) - ->where($db->qn('update_site_id') .' IN ('.implode(', ', $updateSiteIds).')'); - $db->setQuery($query); - $db->execute(); - - // Remove cached component update info from #__updates - $query = $db->getQuery(true) - ->delete($db->qn('#__updates')) - ->where($db->qn('update_site_id') .' IN ('.implode(', ', $updateSiteIds).')'); - $db->setQuery($query); - $db->execute(); - } - - // Use the update cache timeout specified in com_installer - $comInstallerParams = JComponentHelper::getParams('com_installer', false); - $timeout = 3600 * $comInstallerParams->get('cachetimeout', '6'); - - // Load any updates from the network into the #__updates table - $this->updater->findUpdates($this->extension_id, $timeout); - - // Get the update record from the database - $query = $db->getQuery(true) - ->select('*') - ->from($db->qn('#__updates')) - ->where($db->qn('extension_id') . ' = ' . $db->q($this->extension_id)); - $db->setQuery($query); - $updateRecord = $db->loadObject(); + $updateRecord = $this->findUpdates($force, $preferredMethod); // If we have an update record in the database return the information found there if (is_object($updateRecord)) @@ -185,12 +244,40 @@ public function getUpdates($force = false) 'hasUpdate' => true, 'version' => $updateRecord->version, 'infoURL' => $updateRecord->infourl, + 'downloadURL' => $updateRecord->downloadurl, ); } return $updateResponse; } + /** + * Find the available update record object. If we're at the latest version it will return null. + * + * Please see getUpdateMethod for information on how the $preferredMethod is handled and what it means. + * + * @param bool $force Should I forcibly reload the updates from the server? + * @param string $preferredMethod Preferred update method: 'joomla' or 'classic' + * + * @return \stdClass|null + */ + public function findUpdates($force, $preferredMethod = null) + { + $preferredMethod = $this->getUpdateMethod($preferredMethod); + + switch ($preferredMethod) + { + case 'joomla': + return $this->findUpdatesJoomla($force); + break; + + default: + case 'classic': + return $this->findUpdatesClassic($force); + break; + } + } + /** * Gets the update site Ids for our extension. * @@ -198,11 +285,11 @@ public function getUpdates($force = false) */ public function getUpdateSiteIds() { - $db = $this->getDbo(); + $db = FOFPlatform::getInstance()->getDbo(); $query = $db->getQuery(true) - ->select($db->qn('update_site_id')) - ->from($db->qn('#__update_sites_extensions')) - ->where($db->qn('extension_id') . ' = ' . $db->q($this->extension_id)); + ->select($db->qn('update_site_id')) + ->from($db->qn('#__update_sites_extensions')) + ->where($db->qn('extension_id') . ' = ' . $db->q($this->extension_id)); $db->setQuery($query); $updateSiteIds = $db->loadColumn(0); @@ -219,6 +306,56 @@ public function getVersion() return $this->version; } + /** + * Returns the name of the component, e.g. com_foobar + * + * @return string + */ + public function getComponentName() + { + return $this->component; + } + + /** + * Returns the human readable component name, e.g. Foobar Component + * + * @return string + */ + public function getComponentDescription() + { + return $this->componentDescription; + } + + /** + * Returns the numeric extension ID for the component + * + * @return int + */ + public function getExtensionId() + { + return $this->extension_id; + } + + /** + * Returns the update site URL, i.e. the URL to the XML update stream + * + * @return string + */ + public function getUpdateSite() + { + return $this->updateSite; + } + + /** + * Returns the human readable description of the update site + * + * @return string + */ + public function getUpdateSiteName() + { + return $this->updateSiteName; + } + /** * Override the currently installed version as reported by the #__extensions table * @@ -236,98 +373,841 @@ public function setVersion($version) */ public function refreshUpdateSite() { + // Joomla! 1.5 does not have update sites. + if (version_compare(JVERSION, '1.6.0', 'lt')) + { + return; + } + if (empty($this->extension_id)) { return; } + // Remove obsolete update sites that don't match our extension ID but match our name or update site location + $this->removeObsoleteUpdateSites(); + // Create the update site definition we want to store to the database $update_site = array( - 'name' => $this->updateSiteName, - 'type' => 'extension', - 'location' => $this->updateSite, - 'enabled' => 1, - 'last_check_timestamp' => 0, - 'extra_query' => $this->extraQuery + 'name' => $this->updateSiteName, + 'type' => 'extension', + 'location' => $this->updateSite, + 'enabled' => 1, + 'last_check_timestamp' => 0, + 'extra_query' => $this->extraQuery ); // Get a reference to the db driver - $db = $this->getDbo(); + $db = FOFPlatform::getInstance()->getDbo(); // Get the #__update_sites columns $columns = $db->getTableColumns('#__update_sites', true); - if (version_compare(JVERSION, '3.0.0', 'lt') || !array_key_exists('extra_query', $columns)) + if (version_compare(JVERSION, '3.2.0', 'lt') || !array_key_exists('extra_query', $columns)) { unset($update_site['extra_query']); } + if (version_compare(JVERSION, '2.5.0', 'lt') || !array_key_exists('extra_query', $columns)) + { + unset($update_site['last_check_timestamp']); + } + // Get the update sites for our extension $updateSiteIds = $this->getUpdateSiteIds(); - if (!count($updateSiteIds)) + if (empty($updateSiteIds)) + { + $updateSiteIds = array(); + } + + /** @var boolean $needNewUpdateSite Do I need to create a new update site? */ + $needNewUpdateSite = true; + + /** @var int[] $deleteOldSites Old Site IDs to delete */ + $deleteOldSites = array(); + + // Loop through all update sites + foreach ($updateSiteIds as $id) + { + $query = $db->getQuery(true) + ->select('*') + ->from($db->qn('#__update_sites')) + ->where($db->qn('update_site_id') . ' = ' . $db->q($id)); + $db->setQuery($query); + $aSite = $db->loadObject(); + + if (empty($aSite)) + { + // Update site is now up-to-date, don't need to refresh it anymore. + continue; + } + + // We have an update site that looks like ours + if ($needNewUpdateSite && ($aSite->name == $update_site['name']) && ($aSite->location == $update_site['location'])) + { + $needNewUpdateSite = false; + $mustUpdate = false; + + // Is it enabled? If not, enable it. + if (!$aSite->enabled) + { + $mustUpdate = true; + $aSite->enabled = 1; + } + + // Do we have the extra_query property (J 3.2+) and does it match? + if (property_exists($aSite, 'extra_query') && isset($update_site['extra_query']) + && ($aSite->extra_query != $update_site['extra_query'])) + { + $mustUpdate = true; + $aSite->extra_query = $update_site['extra_query']; + } + + // Update the update site if necessary + if ($mustUpdate) + { + $db->updateObject('#__update_sites', $aSite, 'update_site_id', true); + } + + continue; + } + + // In any other case we need to delete this update site, it's obsolete + $deleteOldSites[] = $aSite->update_site_id; + } + + if (!empty($deleteOldSites)) + { + try + { + $obsoleteIDsQuoted = array_map(array($db, 'quote'), $deleteOldSites); + + // Delete update sites + $query = $db->getQuery(true) + ->delete('#__update_sites') + ->where($db->qn('update_site_id') . ' IN (' . implode(',', $obsoleteIDsQuoted) . ')'); + $db->setQuery($query)->execute(); + + // Delete update sites to extension ID records + $query = $db->getQuery(true) + ->delete('#__update_sites_extensions') + ->where($db->qn('update_site_id') . ' IN (' . implode(',', $obsoleteIDsQuoted) . ')'); + $db->setQuery($query)->execute(); + } + catch (\Exception $e) + { + // Do nothing on failure + return; + } + + } + + // Do we still need to create a new update site? + if ($needNewUpdateSite) { // No update sites defined. Create a new one. $newSite = (object)$update_site; $db->insertObject('#__update_sites', $newSite); - $id = $db->insertid(); - + $id = $db->insertid(); $updateSiteExtension = (object)array( - 'update_site_id' => $id, - 'extension_id' => $this->extension_id, + 'update_site_id' => $id, + 'extension_id' => $this->extension_id, ); $db->insertObject('#__update_sites_extensions', $updateSiteExtension); } - else + } + + /** + * Removes any update sites which go by the same name or the same location as our update site but do not match the + * extension ID. + */ + public function removeObsoleteUpdateSites() + { + $db = $this->getDbo(); + + // Get update site IDs + $updateSiteIDs = $this->getUpdateSiteIds(); + + // Find update sites where the name OR the location matches BUT they are not one of the update site IDs + $query = $db->getQuery(true) + ->select($db->qn('update_site_id')) + ->from($db->qn('#__update_sites')) + ->where( + '((' . $db->qn('name') . ' = ' . $db->q($this->updateSiteName) . ') OR ' . + '(' . $db->qn('location') . ' = ' . $db->q($this->updateSite) . '))' + ); + + if (!empty($updateSiteIDs)) { - // Loop through all update sites - foreach ($updateSiteIds as $id) + $updateSitesQuoted = array_map(array($db, 'quote'), $updateSiteIDs); + $query->where($db->qn('update_site_id') . ' NOT IN (' . implode(',', $updateSitesQuoted) . ')'); + } + + try + { + $ids = $db->setQuery($query)->loadColumn(); + + if (!empty($ids)) { + $obsoleteIDsQuoted = array_map(array($db, 'quote'), $ids); + + // Delete update sites $query = $db->getQuery(true) - ->select('*') - ->from($db->qn('#__update_sites')) - ->where($db->qn('update_site_id') . ' = ' . $db->q($id)); + ->delete('#__update_sites') + ->where($db->qn('update_site_id') . ' IN (' . implode(',', $obsoleteIDsQuoted) . ')'); + $db->setQuery($query)->execute(); + + // Delete update sites to extension ID records + $query = $db->getQuery(true) + ->delete('#__update_sites_extensions') + ->where($db->qn('update_site_id') . ' IN (' . implode(',', $obsoleteIDsQuoted) . ')'); + $db->setQuery($query)->execute(); + } + } + catch (\Exception $e) + { + // Do nothing on failure + return; + } + } + + /** + * Get the update method we should use, 'joomla' or 'classic' + * + * You can defined the preferred update method: 'joomla' uses JUpdater whereas 'classic' handles update caching and + * parsing internally. If you are on Joomla! 3.1 or earlier this option is forced to 'classic' since these old + * Joomla! versions couldn't handle updates of commercial components correctly (that's why I contributed the fix to + * that problem, the extra_query field that's present in Joomla! 3.2 onwards). + * + * If 'classic' is defined then it will be used in *all* Joomla! versions. It's the most stable method for fetching + * update information. + * + * @param string $preferred Preferred update method. One of 'joomla' or 'classic'. + * + * @return string + */ + public function getUpdateMethod($preferred = null) + { + $method = $preferred; + + // Make sure the update fetch method is valid, otherwise load the component's "update_method" parameter. + $validMethods = array('joomla', 'classic'); + + if (!in_array($method, $validMethods)) + { + $method = FOFUtilsConfigHelper::getComponentConfigurationValue($this->component, 'update_method', 'joomla'); + } + + // We can't handle updates using Joomla!'s extensions updater in Joomla! 3.1 and earlier + if (($method == 'joomla') && version_compare(JVERSION, '3.2.0', 'lt')) + { + $method = 'classic'; + } + + return $method; + } + + /** + * Find the available update record object. If we're at the latest version it will return null. + * + * @param bool $force Should I forcibly reload the updates from the server? + * + * @return \stdClass|null + */ + protected function findUpdatesJoomla($force = false) + { + $db = FOFPlatform::getInstance()->getDbo(); + + // If we are forcing the reload, set the last_check_timestamp to 0 + // and remove cached component update info in order to force a reload + if ($force) + { + // Find the update site IDs + $updateSiteIds = $this->getUpdateSiteIds(); + + if (empty($updateSiteIds)) + { + return null; + } + + // Set the last_check_timestamp to 0 + if (version_compare(JVERSION, '2.5.0', 'ge')) + { + $query = $db->getQuery(true) + ->update($db->qn('#__update_sites')) + ->set($db->qn('last_check_timestamp') . ' = ' . $db->q('0')) + ->where($db->qn('update_site_id') .' IN ('.implode(', ', $updateSiteIds).')'); $db->setQuery($query); - $aSite = $db->loadObject(); + $db->execute(); + } + + // Remove cached component update info from #__updates + $query = $db->getQuery(true) + ->delete($db->qn('#__updates')) + ->where($db->qn('update_site_id') .' IN ('.implode(', ', $updateSiteIds).')'); + $db->setQuery($query); + $db->execute(); + } + + // Use the update cache timeout specified in com_installer + $timeout = 3600 * FOFUtilsConfigHelper::getComponentConfigurationValue('com_installer', 'cachetimeout', '6'); + + // Load any updates from the network into the #__updates table + $this->updater->findUpdates($this->extension_id, $timeout); + + // Get the update record from the database + $query = $db->getQuery(true) + ->select('*') + ->from($db->qn('#__updates')) + ->where($db->qn('extension_id') . ' = ' . $db->q($this->extension_id)); + $db->setQuery($query); - if (empty($aSite)) + try + { + $updateObject = $db->loadObject(); + } + catch (Exception $e) + { + return null; + } + + if (!is_object($updateObject)) + { + return null; + } + + $updateObject->downloadurl = ''; + + JLoader::import('joomla.updater.update'); + + if (class_exists('JUpdate')) + { + $update = new JUpdate(); + $update->loadFromXML($updateObject->detailsurl); + + if (isset($update->get('downloadurl')->_data)) + { + $url = trim($update->downloadurl->_data); + + $extra_query = isset($updateObject->extra_query) ? $updateObject->extra_query : $this->extraQuery; + + if ($extra_query) { - // Update site not defined. Create a new one. - $update_site['update_site_id'] = $id; - $newSite = (object)$update_site; - $db->insertObject('#__update_sites', $newSite); + if (strpos($url, '?') === false) + { + $url .= '?'; + } + else + { + $url .= '&'; + } - // Update site is now up-to-date, don't need to refresh it anymore. - continue; + $url .= $extra_query; } - // Is it enabled (Joomla! seriously sucks: IT DISABLES UPDATE SITES WITHOUT THE POSSIBILITY TO RE-ENABLE THEM!) - if ($aSite->enabled) + $updateObject->downloadurl = $url; + } + } + + return $updateObject; + } + + /** + * Find the available update record object. If we're at the latest version return null. + * + * @param bool $force Should I forcibly reload the updates from the server? + * + * @return \stdClass|null + */ + protected function findUpdatesClassic($force = false) + { + $allUpdates = $this->loadUpdatesClassic($force); + + if (empty($allUpdates)) + { + return null; + } + + $bestVersion = '0.0.0'; + $bestUpdate = null; + $bestUpdateObject = null; + + foreach($allUpdates as $update) + { + if (!isset($update['version'])) + { + continue; + } + + if (version_compare($bestVersion, $update['version'], 'lt')) + { + $bestVersion = $update['version']; + $bestUpdate = $update; + } + } + + // If the current version is newer or equal to the best one, unset it. Otherwise the user will be always prompted to update + if(version_compare($this->version, $bestVersion, 'ge')) + { + $bestUpdate = null; + $bestVersion = '0.0.0'; + } + + if (!is_null($bestUpdate)) + { + $url = ''; + + if (isset($bestUpdate['downloads']) && isset($bestUpdate['downloads'][0]) + && isset($bestUpdate['downloads'][0]['url'])) + { + $url = $bestUpdate['downloads'][0]['url']; + } + + if ($this->extraQuery) + { + if (strpos($url, '?') === false) + { + $url .= '?'; + } + else + { + $url .= '&'; + } + + $url .= $this->extraQuery; + } + + $bestUpdateObject = (object)array( + 'update_id' => 0, + 'update_site_id' => 0, + 'extension_id' => $this->extension_id, + 'name' => $this->updateSiteName, + 'description' => $bestUpdate['description'], + 'element' => $bestUpdate['element'], + 'type' => $bestUpdate['type'], + 'folder' => count($bestUpdate['folder']) ? $bestUpdate['folder'][0] : '', + 'client_id' => isset($bestUpdate['client']) ? $bestUpdate['client'] : 0, + 'version' => $bestUpdate['version'], + 'data' => '', + 'detailsurl' => $this->updateSite, + 'infourl' => $bestUpdate['infourl']['url'], + 'extra_query' => $this->extraQuery, + 'downloadurl' => $url, + ); + } + + return $bestUpdateObject; + } + + /** + * Load all available updates without going through JUpdate + * + * @param bool $force Should I forcibly reload the updates from the server? + * + * @return array + */ + protected function loadUpdatesClassic($force = false) + { + // Is the cache busted? If it is I set $force = true to make sure I download fresh updates + if (!$force) + { + // Get the cache timeout. On older Joomla! installations it will always default to 6 hours. + $timeout = 3600 * FOFUtilsConfigHelper::getComponentConfigurationValue('com_installer', 'cachetimeout', '6'); + + // Do I need to check for updates? + $lastCheck = $this->getCommonParameter('lastcheck', 0); + $now = time(); + + if (($now - $lastCheck) >= $timeout) + { + $force = true; + } + } + + // Get the cached JSON-encoded updates list + $rawUpdates = $this->getCommonParameter('allUpdates', ''); + + // Am I forced to reload the XML file (explicitly or because the cache is busted)? + if ($force) + { + // Set the timestamp + $now = time(); + $this->setCommonParameter('lastcheck', $now); + + // Get all available updates + $updateHelper = new FOFUtilsUpdateExtension(); + $updates = $updateHelper->getUpdatesFromExtension($this->updateSite); + + // Save the raw updates list in the database + $rawUpdates = json_encode($updates); + $this->setCommonParameter('allUpdates', $rawUpdates); + } + + // Decode the updates list + $updates = json_decode($rawUpdates, true); + + // Walk through the updates and find the ones compatible with our Joomla! and PHP version + $compatibleUpdates = array(); + + // Get the Joomla! version family (e.g. 2.5) + $jVersion = JVERSION; + $jVersionParts = explode('.', $jVersion); + $jVersionShort = $jVersionParts[0] . '.' . $jVersionParts[1]; + + // Get the PHP version family (e.g. 5.6) + $phpVersion = PHP_VERSION; + $phpVersionParts = explode('.', $phpVersion); + $phpVersionShort = $phpVersionParts[0] . '.' . $phpVersionParts[1]; + + foreach ($updates as $update) + { + // No platform? + if (!isset($update['targetplatform'])) + { + continue; + } + + // Wrong platform? + if ($update['targetplatform']['name'] != 'joomla') + { + continue; + } + + // Get the target Joomla! version + $targetJoomlaVersion = $update['targetplatform']['version']; + $targetVersionParts = explode('.', $targetJoomlaVersion); + $targetVersionShort = $targetVersionParts[0] . '.' . $targetVersionParts[1]; + + // The target version MUST be in the same Joomla! branch + if ($jVersionShort != $targetVersionShort) + { + continue; + } + + // If the target version is major.minor.revision we must make sure our current JVERSION is AT LEAST equal to that. + if (version_compare($targetJoomlaVersion, JVERSION, 'gt')) + { + continue; + } + + // Do I have target PHP versions? + if (isset($update['ars-phpcompat'])) + { + $phpCompatible = false; + + foreach ($update['ars-phpcompat'] as $entry) { - // Does the name and location match? - if (($aSite->name == $update_site['name']) && ($aSite->location == $update_site['location'])) + // Get the target PHP version family + $targetPHPVersion = $entry['@attributes']['version']; + $targetPHPVersionParts = explode('.', $targetPHPVersion); + $targetPHPVersionShort = $targetPHPVersionParts[0] . '.' . $targetPHPVersionParts[1]; + + // The target PHP version MUST be in the same PHP branch + if ($phpVersionShort != $targetPHPVersionShort) + { + continue; + } + + // If the target version is major.minor.revision we must make sure our current PHP_VERSION is AT LEAST equal to that. + if (version_compare($targetPHPVersion, PHP_VERSION, 'gt')) { - // Do we have the extra_query property (J 3.2+) and does it match? - if (property_exists($aSite, 'extra_query') && isset($update_site['extra_query'])) - { - if ($aSite->extra_query == $update_site['extra_query']) - { - continue; - } - } - else - { - // Joomla! 3.1 or earlier. Updates may or may not work. - continue; - } + continue; } + + $phpCompatible = true; + break; + } + + if (!$phpCompatible) + { + continue; } + } + + // All checks pass. Add this update to the list of compatible updates. + $compatibleUpdates[] = $update; + } + + return $compatibleUpdates; + } + + /** + * Get a common parameter from the #__akeeba_common table + * + * @param string $key The key to retrieve + * @param mixed $default The default value in case none is set + * + * @return mixed The saved parameter value (or $default, if nothing is currently set) + */ + protected function getCommonParameter($key, $default = null) + { + $dbKey = $this->commonKey . '_autoupdate_' . $key; + + $db = FOFPlatform::getInstance()->getDbo(); + + $query = $db->getQuery(true) + ->select($db->qn('value')) + ->from($db->qn($this->commonTable)) + ->where($db->qn('key') . ' = ' . $db->q($dbKey)); + + $result = $db->setQuery($query)->loadResult(); + + if (!$result) + { + return $default; + } + + return $result; + } + + /** + * Set a common parameter from the #__akeeba_common table + * + * @param string $key The key to set + * @param mixed $value The value to set + * + * @return void + */ + protected function setCommonParameter($key, $value) + { + $dbKey = $this->commonKey . '_autoupdate_' . $key; + + $db = FOFPlatform::getInstance()->getDbo(); + + $query = $db->getQuery(true) + ->select('COUNT(*)') + ->from($db->qn($this->commonTable)) + ->where($db->qn('key') . ' = ' . $db->q($dbKey)); + $count = $db->setQuery($query)->loadResult(); + + if ($count) + { + $query = $db->getQuery(true) + ->update($db->qn($this->commonTable)) + ->set($db->qn('value') . ' = ' . $db->q($value)) + ->where($db->qn('key') . ' = ' . $db->q($dbKey)); + $db->setQuery($query)->execute(); + } + else + { + $data = (object)array( + 'key' => $dbKey, + 'value' => $value, + ); + + $db->insertObject($this->commonTable, $data); + } + } + + /** + * Proxy to updateComponent(). Required since old versions of our software had an updateComponent method declared + * private. If we set the updateComponent() method public we cause a fatal error. + * + * @return string + */ + public function doUpdateComponent() + { + return $this->updateComponent(); + } + + /** + * Automatically install the extension update under Joomla! 1.5.5 or later (web) / 3.0 or later (CLI). + * + * @return string The update message + */ + private function updateComponent() + { + $isCli = FOFPlatform::getInstance()->isCli(); + $minVersion = $isCli ? '3.0.0' : '1.5.5'; + $errorQualifier = $isCli ? ' using an unattended CLI CRON script ' : ' '; + + if (version_compare(JVERSION, $minVersion, 'lt')) + { + return "Extension updates{$errorQualifier}only work with Joomla! $minVersion and later."; + } + + try + { + $updatePackagePath = $this->downloadUpdate(); + } + catch (Exception $e) + { + return $e->getMessage(); + } + + // Unpack the downloaded package file + jimport('joomla.installer.helper'); + jimport('cms.installer.helper'); + $package = JInstallerHelper::unpack($updatePackagePath); + + if (!$package) + { + // Clean up + if (JFile::exists($updatePackagePath)) + { + JFile::delete($updatePackagePath); + } + + return "An error occurred while unpacking the file. Please double check your Joomla temp-directory setting in Global Configuration."; + } + + $installer = new JInstaller; + $installed = $installer->install($package['extractdir']); + + // Let's cleanup the downloaded archive and the temp folder + if (JFolder::exists($package['extractdir'])) + { + JFolder::delete($package['extractdir']); + } + + if (JFile::exists($package['packagefile'])) + { + JFile::delete($package['packagefile']); + } + + if ($installed) + { + return "Component successfully updated"; + } + else + { + return "An error occurred while trying to update the component"; + } + } + + /** + * Downloads the latest update package to Joomla!'s temporary directory + * + * @return string The absolute path to the downloaded update package. + */ + public function downloadUpdate() + { + // Get the update URL + $updateInformation = $this->getUpdates(); + $url = $updateInformation['downloadURL']; + + if (empty($url)) + { + throw new RuntimeException("No download URL was provided in the update information"); + } + + $config = JFactory::getConfig(); + $tmp_dest = $config->get('tmp_path'); + + if (!$tmp_dest) + { + throw new RuntimeException("You must set a non-empty Joomla! temp-directory in Global Configuration before continuing."); + } + + if (!JFolder::exists($tmp_dest)) + { + throw new RuntimeException("Joomla!'s temp-directory does not exist. Please set the correct path in Global Configuration before continuing."); + } + + // Get the target filename + $filename = $this->component . '.zip'; + $filename = rtrim($tmp_dest, '\\/') . '/' . $filename; + + try + { + $downloader = new FOFDownload(); + $data = $downloader->getFromURL($url); + } + catch (Exception $e) + { + $code =$e->getCode(); + $message =$e->getMessage(); + throw new RuntimeException("An error occurred while trying to download the update package. Double check your Download ID and your server's network settings. The error message was: #$code: $message"); + } - $update_site['update_site_id'] = $id; - $newSite = (object)$update_site; - $db->updateObject('#__update_sites', $newSite, 'update_site_id', true); + if (!JFile::write($filename, $data)) + { + if (!file_put_contents($filename, $data)) + { + throw new RuntimeException("Joomla!'s temp-directory is not writeable. Please check its permissions or set a different, writeable path in Global Configuration before continuing."); } } + + return $filename; + } + + /** + * Gets a file name out of a url + * + * @param string $url URL to get name from + * + * @return mixed String filename or boolean false if failed + */ + private function getFilenameFromURL($url) + { + if (is_string($url)) + { + $parts = explode('/', $url); + + return $parts[count($parts) - 1]; + } + + return false; + } + + /** + * Proxy to sendNotificationEmail(). Required since old versions of our software had a sendNotificationEmail method + * declared private. If we set the sendNotificationEmail() method public we cause a fatal error. + * + * @param string $version The new version of our software + * @param string $email The email address to send the notification to + * + * @return mixed The result of JMail::send() + */ + public function doSendNotificationEmail($version, $email) + { + try + { + return $this->sendNotificationEmail($version, $email); + } + catch (\Exception $e) + { + // Joomla! 3.5 is buggy + } + } + + /** + * Sends an update notification email + * + * @param string $version The new version of our software + * @param string $email The email address to send the notification to + * + * @return mixed The result of JMail::send() + */ + private function sendNotificationEmail($version, $email) + { + $email_subject = $this->updateEmailSubject; + $email_body = $this->updateEmailBody; + + $jconfig = JFactory::getConfig(); + $sitename = $jconfig->get('sitename'); + + $substitutions = array( + '[VERSION]' => $version, + '[SITENAME]' => $sitename, + '[COMPONENT]' => $this->componentDescription, + ); + + $email_subject = str_replace(array_keys($substitutions), array_values($substitutions), $email_subject); + $email_body = str_replace(array_keys($substitutions), array_values($substitutions), $email_body); + + $mailer = JFactory::getMailer(); + + $mailfrom = $jconfig->get('mailfrom'); + $fromname = $jconfig->get('fromname'); + + $mailer->setSender(array( $mailfrom, $fromname )); + $mailer->addRecipient($email); + $mailer->setSubject($email_subject); + $mailer->setBody($email_body); + + return $mailer->Send(); } -} \ No newline at end of file +} diff --git a/libraries/fof/version.txt b/libraries/fof/version.txt index 71a2b24a23f6b..4b0b461529702 100644 --- a/libraries/fof/version.txt +++ b/libraries/fof/version.txt @@ -1,2 +1,2 @@ -2.4.3 -2015-04-22 13:15:32 \ No newline at end of file +2.5.5 +2016-08-19 \ No newline at end of file diff --git a/libraries/fof/view/csv.php b/libraries/fof/view/csv.php index f7707a4041bd7..5b4dec29a70df 100644 --- a/libraries/fof/view/csv.php +++ b/libraries/fof/view/csv.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage view - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/view/form.php b/libraries/fof/view/form.php index d5c6c6eaf5441..8710315b15455 100644 --- a/libraries/fof/view/form.php +++ b/libraries/fof/view/form.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage view - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/view/html.php b/libraries/fof/view/html.php index d0f3ecf474f02..c8b85ff55743c 100644 --- a/libraries/fof/view/html.php +++ b/libraries/fof/view/html.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage view - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/view/json.php b/libraries/fof/view/json.php index 035e2983882be..c2874a8fd0130 100644 --- a/libraries/fof/view/json.php +++ b/libraries/fof/view/json.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage view - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access @@ -106,7 +106,7 @@ protected function onDisplay($tpl = null) } // JSONP support - $callback = $this->input->getVar('callback', null); + $callback = $this->input->get('callback', null, 'raw'); if (!empty($callback)) { diff --git a/libraries/fof/view/raw.php b/libraries/fof/view/raw.php index 722959c5f5d5d..99bb0f4231cfa 100644 --- a/libraries/fof/view/raw.php +++ b/libraries/fof/view/raw.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage view - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/fof/view/view.php b/libraries/fof/view/view.php index ac66f73e47f54..768ef761667d1 100644 --- a/libraries/fof/view/view.php +++ b/libraries/fof/view/view.php @@ -2,7 +2,7 @@ /** * @package FrameworkOnFramework * @subpackage view - * @copyright Copyright (C) 2010 - 2015 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. + * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Protect from unauthorized access diff --git a/libraries/joomla/form/fields/subform.php b/libraries/joomla/form/fields/subform.php index 3cb9001ec40d0..f20fe8415ed59 100644 --- a/libraries/joomla/form/fields/subform.php +++ b/libraries/joomla/form/fields/subform.php @@ -121,12 +121,18 @@ public function __set($name, $value) break; case 'max': - $this->max = max(1, (int) $value); + if ($value) + { + $this->max = max(1, (int) $value); + } break; case 'groupByFieldset': - $value = (string) $value; - $this->groupByFieldset = !($value === 'false' || $value === 'off' || $value === '0'); + if ($value !== null) + { + $value = (string) $value; + $this->groupByFieldset = !($value === 'false' || $value === 'off' || $value === '0'); + } break; case 'layout': diff --git a/plugins/editors/tinymce/tinymce.php b/plugins/editors/tinymce/tinymce.php index e1444b1a33d4e..5f33f1a850600 100644 --- a/plugins/editors/tinymce/tinymce.php +++ b/plugins/editors/tinymce/tinymce.php @@ -875,7 +875,7 @@ public function onDisplay($name, $content, $width, $height, $col, $row, $buttons case 0: /* Simple mode*/ $script .= " menubar: false, - toolbar1: \"bold italics underline strikethrough | undo redo | bullist numlist | $toolbar5 | code\", + toolbar1: \"bold italics underline strikethrough | undo redo | bullist numlist | code | $toolbar5\", plugins: \"$dragDropPlg code\", }); "; diff --git a/plugins/system/languagefilter/languagefilter.php b/plugins/system/languagefilter/languagefilter.php index 1f46ee644e309..82982f1cdbb01 100644 --- a/plugins/system/languagefilter/languagefilter.php +++ b/plugins/system/languagefilter/languagefilter.php @@ -691,7 +691,7 @@ public function onAfterDispatch() { $doc = JFactory::getDocument(); - if ($this->app->isSite() && $this->params->get('alternate_meta') && $doc->getType() == 'html') + if ($this->app->isSite() && $this->params->get('alternate_meta', 1) && $doc->getType() == 'html') { $languages = $this->lang_codes; $homes = JLanguageMultilang::getSiteHomePages(); diff --git a/plugins/system/languagefilter/languagefilter.xml b/plugins/system/languagefilter/languagefilter.xml index 517801dcb9769..e72ad199c2128 100644 --- a/plugins/system/languagefilter/languagefilter.xml +++ b/plugins/system/languagefilter/languagefilter.xml @@ -61,7 +61,6 @@ description="PLG_SYSTEM_LANGUAGEFILTER_FIELD_ALTERNATE_META_DESC" default="1" class="btn-group btn-group-yesno" - showon="item_associations:1" > @@ -74,7 +73,7 @@ description="PLG_SYSTEM_LANGUAGEFILTER_FIELD_XDEFAULT_DESC" default="1" class="btn-group btn-group-yesno" - showon="item_associations:1[AND]alternate_meta:1" + showon="alternate_meta:1" > @@ -86,7 +85,7 @@ label="PLG_SYSTEM_LANGUAGEFILTER_FIELD_XDEFAULT_LANGUAGE_LABEL" description="PLG_SYSTEM_LANGUAGEFILTER_FIELD_XDEFAULT_LANGUAGE_DESC" default="default" - showon="item_associations:1[AND]alternate_meta:1[AND]xdefault:1" + showon="alternate_meta:1[AND]xdefault:1" > diff --git a/templates/beez3/error.php b/templates/beez3/error.php index 2903941854609..68a1eb31f1a2d 100644 --- a/templates/beez3/error.php +++ b/templates/beez3/error.php @@ -147,9 +147,26 @@ - debug) : - echo $this->renderBacktrace(); - endif; ?> + debug) : ?> +
+ renderBacktrace(); ?> + + error->getPrevious()) : ?> + + _error here and in the loop as setError() assigns errors to this property and we need this for the backtrace to work correctly ?> + + setError($this->_error->getPrevious()); ?> + +

+

_error->getMessage(), ENT_QUOTES, 'UTF-8'); ?>

+ renderBacktrace(); ?> + setError($this->_error->getPrevious()); ?> + + + setError($this->error); ?> + +
+ diff --git a/templates/protostar/offline.php b/templates/protostar/offline.php index 8811a6ae94df5..aec18c868ddc8 100644 --- a/templates/protostar/offline.php +++ b/templates/protostar/offline.php @@ -9,9 +9,7 @@ defined('_JEXEC') or die; -JLoader::register('UsersHelper', JPATH_ADMINISTRATOR . '/components/com_users/helpers/users.php'); - -$twofactormethods = UsersHelper::getTwoFactorMethods(); +$twofactormethods = JAuthenticationHelper::getTwoFactorMethods(); $app = JFactory::getApplication(); $doc = JFactory::getDocument(); $this->language = $doc->language; diff --git a/templates/system/error.php b/templates/system/error.php index 1ff6de05206c4..f4c1b01c55828 100644 --- a/templates/system/error.php +++ b/templates/system/error.php @@ -56,11 +56,26 @@

error->getMessage(), ENT_QUOTES, 'UTF-8'); ?>

-

- debug) : ?> + debug) : ?> +

renderBacktrace(); ?> - -

+ + error->getPrevious()) : ?> + + _error here and in the loop as setError() assigns errors to this property and we need this for the backtrace to work correctly ?> + + setError($this->_error->getPrevious()); ?> + +

+

_error->getMessage(), ENT_QUOTES, 'UTF-8'); ?>

+ renderBacktrace(); ?> + setError($this->_error->getPrevious()); ?> + + + setError($this->error); ?> + +
+