diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/LICENSE.txt b/wp-content/plugins/wp2pgpmail/phpcaptcha/LICENSE.txt
index 9a749e68550b0b8671c3149e475843b918e40e7b..889bc2c6455add59099ead4933b8d4d7ac0abf9f 100644
--- a/wp-content/plugins/wp2pgpmail/phpcaptcha/LICENSE.txt
+++ b/wp-content/plugins/wp2pgpmail/phpcaptcha/LICENSE.txt
@@ -1,458 +1,25 @@
-		  GNU LESSER GENERAL PUBLIC LICENSE
-		       Version 2.1, February 1999
+COPYRIGHT:
+    Copyright (c) 2011 Drew Phillips
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without modification,
+    are permitted provided that the following conditions are met:
+
+    - Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    - Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
 
- Copyright (C) 1991, 1999 Free Software Foundation, Inc.
-     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-[This is the first released version of the Lesser GPL.  It also counts
- as the successor of the GNU Library Public License, version 2, hence
- the version number 2.1.]
-
-			    Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-Licenses are intended to guarantee your freedom to share and change
-free software--to make sure the software is free for all its users.
-
-  This license, the Lesser General Public License, applies to some
-specially designated software packages--typically libraries--of the
-Free Software Foundation and other authors who decide to use it.  You
-can use it too, but we suggest you first think carefully about whether
-this license or the ordinary General Public License is the better
-strategy to use in any particular case, based on the explanations below.
-
-  When we speak of free software, we are referring to freedom of use,
-not price.  Our General Public Licenses are designed to make sure that
-you have the freedom to distribute copies of free software (and charge
-for this service if you wish); that you receive source code or can get
-it if you want it; that you can change the software and use pieces of
-it in new free programs; and that you are informed that you can do
-these things.
-
-  To protect your rights, we need to make restrictions that forbid
-distributors to deny you these rights or to ask you to surrender these
-rights.  These restrictions translate to certain responsibilities for
-you if you distribute copies of the library or if you modify it.
-
-  For example, if you distribute copies of the library, whether gratis
-or for a fee, you must give the recipients all the rights that we gave
-you.  You must make sure that they, too, receive or can get the source
-code.  If you link other code with the library, you must provide
-complete object files to the recipients, so that they can relink them
-with the library after making changes to the library and recompiling
-it.  And you must show them these terms so they know their rights.
-
-  We protect your rights with a two-step method: (1) we copyright the
-library, and (2) we offer you this license, which gives you legal
-permission to copy, distribute and/or modify the library.
-
-  To protect each distributor, we want to make it very clear that
-there is no warranty for the free library.  Also, if the library is
-modified by someone else and passed on, the recipients should know
-that what they have is not the original version, so that the original
-author's reputation will not be affected by problems that might be
-introduced by others.
-
-  Finally, software patents pose a constant threat to the existence of
-any free program.  We wish to make sure that a company cannot
-effectively restrict the users of a free program by obtaining a
-restrictive license from a patent holder.  Therefore, we insist that
-any patent license obtained for a version of the library must be
-consistent with the full freedom of use specified in this license.
-
-  Most GNU software, including some libraries, is covered by the
-ordinary GNU General Public License.  This license, the GNU Lesser
-General Public License, applies to certain designated libraries, and
-is quite different from the ordinary General Public License.  We use
-this license for certain libraries in order to permit linking those
-libraries into non-free programs.
-
-  When a program is linked with a library, whether statically or using
-a shared library, the combination of the two is legally speaking a
-combined work, a derivative of the original library.  The ordinary
-General Public License therefore permits such linking only if the
-entire combination fits its criteria of freedom.  The Lesser General
-Public License permits more lax criteria for linking other code with
-the library.
-
-  We call this license the "Lesser" General Public License because it
-does Less to protect the user's freedom than the ordinary General
-Public License.  It also provides other free software developers Less
-of an advantage over competing non-free programs.  These disadvantages
-are the reason we use the ordinary General Public License for many
-libraries.  However, the Lesser license provides advantages in certain
-special circumstances.
-
-  For example, on rare occasions, there may be a special need to
-encourage the widest possible use of a certain library, so that it becomes
-a de-facto standard.  To achieve this, non-free programs must be
-allowed to use the library.  A more frequent case is that a free
-library does the same job as widely used non-free libraries.  In this
-case, there is little to gain by limiting the free library to free
-software only, so we use the Lesser General Public License.
-
-  In other cases, permission to use a particular library in non-free
-programs enables a greater number of people to use a large body of
-free software.  For example, permission to use the GNU C Library in
-non-free programs enables many more people to use the whole GNU
-operating system, as well as its variant, the GNU/Linux operating
-system.
-
-  Although the Lesser General Public License is Less protective of the
-users' freedom, it does ensure that the user of a program that is
-linked with the Library has the freedom and the wherewithal to run
-that program using a modified version of the Library.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.  Pay close attention to the difference between a
-"work based on the library" and a "work that uses the library".  The
-former contains code derived from the library, whereas the latter must
-be combined with the library in order to run.
-
-		  GNU LESSER GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License Agreement applies to any software library or other
-program which contains a notice placed by the copyright holder or
-other authorized party saying it may be distributed under the terms of
-this Lesser General Public License (also called "this License").
-Each licensee is addressed as "you".
-
-  A "library" means a collection of software functions and/or data
-prepared so as to be conveniently linked with application programs
-(which use some of those functions and data) to form executables.
-
-  The "Library", below, refers to any such software library or work
-which has been distributed under these terms.  A "work based on the
-Library" means either the Library or any derivative work under
-copyright law: that is to say, a work containing the Library or a
-portion of it, either verbatim or with modifications and/or translated
-straightforwardly into another language.  (Hereinafter, translation is
-included without limitation in the term "modification".)
-
-  "Source code" for a work means the preferred form of the work for
-making modifications to it.  For a library, complete source code means
-all the source code for all modules it contains, plus any associated
-interface definition files, plus the scripts used to control compilation
-and installation of the library.
-
-  Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running a program using the Library is not restricted, and output from
-such a program is covered only if its contents constitute a work based
-on the Library (independent of the use of the Library in a tool for
-writing it).  Whether that is true depends on what the Library does
-and what the program that uses the Library does.
-
-  1. You may copy and distribute verbatim copies of the Library's
-complete source code as you receive it, in any medium, provided that
-you conspicuously and appropriately publish on each copy an
-appropriate copyright notice and disclaimer of warranty; keep intact
-all the notices that refer to this License and to the absence of any
-warranty; and distribute a copy of this License along with the
-Library.
-
-  You may charge a fee for the physical act of transferring a copy,
-and you may at your option offer warranty protection in exchange for a
-fee.
-
-  2. You may modify your copy or copies of the Library or any portion
-of it, thus forming a work based on the Library, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) The modified work must itself be a software library.
-
-    b) You must cause the files modified to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    c) You must cause the whole of the work to be licensed at no
-    charge to all third parties under the terms of this License.
-
-    d) If a facility in the modified Library refers to a function or a
-    table of data to be supplied by an application program that uses
-    the facility, other than as an argument passed when the facility
-    is invoked, then you must make a good faith effort to ensure that,
-    in the event an application does not supply such function or
-    table, the facility still operates, and performs whatever part of
-    its purpose remains meaningful.
-
-    (For example, a function in a library to compute square roots has
-    a purpose that is entirely well-defined independent of the
-    application.  Therefore, Subsection 2d requires that any
-    application-supplied function or table used by this function must
-    be optional: if the application does not supply it, the square
-    root function must still compute square roots.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Library,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Library, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote
-it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Library.
-
-In addition, mere aggregation of another work not based on the Library
-with the Library (or with a work based on the Library) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may opt to apply the terms of the ordinary GNU General Public
-License instead of this License to a given copy of the Library.  To do
-this, you must alter all the notices that refer to this License, so
-that they refer to the ordinary GNU General Public License, version 2,
-instead of to this License.  (If a newer version than version 2 of the
-ordinary GNU General Public License has appeared, then you can specify
-that version instead if you wish.)  Do not make any other change in
-these notices.
-
-  Once this change is made in a given copy, it is irreversible for
-that copy, so the ordinary GNU General Public License applies to all
-subsequent copies and derivative works made from that copy.
-
-  This option is useful when you wish to copy part of the code of
-the Library into a program that is not a library.
-
-  4. You may copy and distribute the Library (or a portion or
-derivative of it, under Section 2) in object code or executable form
-under the terms of Sections 1 and 2 above provided that you accompany
-it with the complete corresponding machine-readable source code, which
-must be distributed under the terms of Sections 1 and 2 above on a
-medium customarily used for software interchange.
-
-  If distribution of object code is made by offering access to copy
-from a designated place, then offering equivalent access to copy the
-source code from the same place satisfies the requirement to
-distribute the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  5. A program that contains no derivative of any portion of the
-Library, but is designed to work with the Library by being compiled or
-linked with it, is called a "work that uses the Library".  Such a
-work, in isolation, is not a derivative work of the Library, and
-therefore falls outside the scope of this License.
-
-  However, linking a "work that uses the Library" with the Library
-creates an executable that is a derivative of the Library (because it
-contains portions of the Library), rather than a "work that uses the
-library".  The executable is therefore covered by this License.
-Section 6 states terms for distribution of such executables.
-
-  When a "work that uses the Library" uses material from a header file
-that is part of the Library, the object code for the work may be a
-derivative work of the Library even though the source code is not.
-Whether this is true is especially significant if the work can be
-linked without the Library, or if the work is itself a library.  The
-threshold for this to be true is not precisely defined by law.
-
-  If such an object file uses only numerical parameters, data
-structure layouts and accessors, and small macros and small inline
-functions (ten lines or less in length), then the use of the object
-file is unrestricted, regardless of whether it is legally a derivative
-work.  (Executables containing this object code plus portions of the
-Library will still fall under Section 6.)
-
-  Otherwise, if the work is a derivative of the Library, you may
-distribute the object code for the work under the terms of Section 6.
-Any executables containing that work also fall under Section 6,
-whether or not they are linked directly with the Library itself.
-
-  6. As an exception to the Sections above, you may also combine or
-link a "work that uses the Library" with the Library to produce a
-work containing portions of the Library, and distribute that work
-under terms of your choice, provided that the terms permit
-modification of the work for the customer's own use and reverse
-engineering for debugging such modifications.
-
-  You must give prominent notice with each copy of the work that the
-Library is used in it and that the Library and its use are covered by
-this License.  You must supply a copy of this License.  If the work
-during execution displays copyright notices, you must include the
-copyright notice for the Library among them, as well as a reference
-directing the user to the copy of this License.  Also, you must do one
-of these things:
-
-    a) Accompany the work with the complete corresponding
-    machine-readable source code for the Library including whatever
-    changes were used in the work (which must be distributed under
-    Sections 1 and 2 above); and, if the work is an executable linked
-    with the Library, with the complete machine-readable "work that
-    uses the Library", as object code and/or source code, so that the
-    user can modify the Library and then relink to produce a modified
-    executable containing the modified Library.  (It is understood
-    that the user who changes the contents of definitions files in the
-    Library will not necessarily be able to recompile the application
-    to use the modified definitions.)
-
-    b) Use a suitable shared library mechanism for linking with the
-    Library.  A suitable mechanism is one that (1) uses at run time a
-    copy of the library already present on the user's computer system,
-    rather than copying library functions into the executable, and (2)
-    will operate properly with a modified version of the library, if
-    the user installs one, as long as the modified version is
-    interface-compatible with the version that the work was made with.
-
-    c) Accompany the work with a written offer, valid for at
-    least three years, to give the same user the materials
-    specified in Subsection 6a, above, for a charge no more
-    than the cost of performing this distribution.
-
-    d) If distribution of the work is made by offering access to copy
-    from a designated place, offer equivalent access to copy the above
-    specified materials from the same place.
-
-    e) Verify that the user has already received a copy of these
-    materials or that you have already sent this user a copy.
-
-  For an executable, the required form of the "work that uses the
-Library" must include any data and utility programs needed for
-reproducing the executable from it.  However, as a special exception,
-the materials to be distributed need not include anything that is
-normally distributed (in either source or binary form) with the major
-components (compiler, kernel, and so on) of the operating system on
-which the executable runs, unless that component itself accompanies
-the executable.
-
-  It may happen that this requirement contradicts the license
-restrictions of other proprietary libraries that do not normally
-accompany the operating system.  Such a contradiction means you cannot
-use both them and the Library together in an executable that you
-distribute.
-
-  7. You may place library facilities that are a work based on the
-Library side-by-side in a single library together with other library
-facilities not covered by this License, and distribute such a combined
-library, provided that the separate distribution of the work based on
-the Library and of the other library facilities is otherwise
-permitted, and provided that you do these two things:
-
-    a) Accompany the combined library with a copy of the same work
-    based on the Library, uncombined with any other library
-    facilities.  This must be distributed under the terms of the
-    Sections above.
-
-    b) Give prominent notice with the combined library of the fact
-    that part of it is a work based on the Library, and explaining
-    where to find the accompanying uncombined form of the same work.
-
-  8. You may not copy, modify, sublicense, link with, or distribute
-the Library except as expressly provided under this License.  Any
-attempt otherwise to copy, modify, sublicense, link with, or
-distribute the Library is void, and will automatically terminate your
-rights under this License.  However, parties who have received copies,
-or rights, from you under this License will not have their licenses
-terminated so long as such parties remain in full compliance.
-
-  9. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Library or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Library (or any work based on the
-Library), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Library or works based on it.
-
-  10. Each time you redistribute the Library (or any work based on the
-Library), the recipient automatically receives a license from the
-original licensor to copy, distribute, link with or modify the Library
-subject to these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties with
-this License.
-
-  11. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Library at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Library by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Library.
-
-If any portion of this section is held invalid or unenforceable under any
-particular circumstance, the balance of the section is intended to apply,
-and the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  12. If the distribution and/or use of the Library is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Library under this License may add
-an explicit geographical distribution limitation excluding those countries,
-so that distribution is permitted only in or among countries not thus
-excluded.  In such case, this License incorporates the limitation as if
-written in the body of this License.
-
-  13. The Free Software Foundation may publish revised and/or new
-versions of the Lesser General Public License from time to time.
-Such new versions will be similar in spirit to the present version,
-but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Library
-specifies a version number of this License which applies to it and
-"any later version", you have the option of following the terms and
-conditions either of that version or of any later version published by
-the Free Software Foundation.  If the Library does not specify a
-license version number, you may choose any version ever published by
-the Free Software Foundation.
-
-  14. If you wish to incorporate parts of the Library into other free
-programs whose distribution conditions are incompatible with these,
-write to the author to ask for permission.  For software which is
-copyrighted by the Free Software Foundation, write to the Free
-Software Foundation; we sometimes make exceptions for this.  Our
-decision will be guided by the two goals of preserving the free status
-of all derivatives of our free software and of promoting the sharing
-and reuse of software generally.
-
-			    NO WARRANTY
-
-  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
-WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
-KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
-LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
-THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
-WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
-AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
-FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
-CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
-LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
-RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
-FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
-SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGES.
-
-		     END OF TERMS AND CONDITIONS
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/README.md b/wp-content/plugins/wp2pgpmail/phpcaptcha/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..a26bac4f6366d26a03691122d04e54a03d325222
--- /dev/null
+++ b/wp-content/plugins/wp2pgpmail/phpcaptcha/README.md
@@ -0,0 +1,244 @@
+## Name:
+
+**Securimage** - A PHP class for creating captcha images and audio with many options.
+
+## Version:
+
+**3.6.7**
+
+## Author:
+
+Drew Phillips <drew@drew-phillips.com>
+
+## Download:
+
+The latest version can always be found at [phpcaptcha.org](https://www.phpcaptcha.org)
+
+## Documentation:
+
+Online documentation of the class, methods, and variables can be found 
+at http://www.phpcaptcha.org/Securimage_Docs/
+
+## Requirements:
+
+* PHP 5.4 or greater
+* GD  2.0
+* FreeType (Required, for TTF fonts)
+* PDO (if using Sqlite, MySQL, or PostgreSQL)
+
+## Synopsis:
+    
+**Within your HTML form**
+    
+    <form method="post" action="">
+    .. form elements
+    
+    <div>
+        <?php
+            require_once 'securimage.php';
+            echo Securimage::getCaptchaHtml();
+        ?>
+    </div>
+    </form>
+   
+    
+**Within your PHP form processor**
+
+    require_once 'securimage.php';
+
+    // Code Validation
+
+    $image = new Securimage();
+    if ($image->check($_POST['captcha_code']) == true) {
+      echo "Correct!";
+    } else {
+      echo "Sorry, wrong code.";
+    }
+
+## Description:
+
+What is **Securimage**?
+
+Securimage is a PHP class that is used to generate and validate CAPTCHA images.
+    
+The classes uses an existing PHP session or creates its own if none is found to
+store the CAPTCHA code.  In addition, a database can be used instead of 
+session storage.
+    
+Variables within the class are used to control the style and display of the
+image.  The class uses TTF fonts and effects for strengthening the security of
+the image.
+    
+It also creates audible codes which are played for visually impared users.
+
+## UPGRADE NOTICE:
+
+**3.6.3 and below:**
+Securimage 3.6.4 fixed a XSS vulnerability in example_form.ajax.php.  It is
+recommended to upgrade to the latest version or delete example_form.ajax.php
+from the securimage directory on your website.
+
+**3.6.2 and above:**
+
+If you are upgrading to 3.6.2 or greater *AND* are using database storage,
+the table structure has changed in 3.6.2 adding an audio_data column for
+storing audio files in the database in order to support HTTP range
+requests.  Delete your tables and have Securimage recreate them or see
+the function createDatabaseTables() in securimage.php for the new structure
+depending on which database backend you are using and alter the tables as
+needed.  If using SQLite, just overwrite your existing securimage.sq3 file
+with the one from this release.
+
+*If you are not using database tables for storage, ignore this notice.*
+
+## Copyright:
+Script
+    Copyright (c) 2018 Drew Phillips
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    - Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    - Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+
+## Licenses:
+
+**WavFile.php**
+
+    The WavFile.php class used in Securimage by Drew Phillips and Paul Voegler
+    is used under the BSD License.  See WavFile.php for details.
+    Many thanks to Paul Voegler (http://www.voegler.eu/) for contributing to
+    Securimage.
+Script
+---------------------------------------------------------------------------
+
+**Flash code for Securimage**
+
+Flash code created by Age Bosma & Mario Romero (animario@hotmail.com)
+Many thanks for releasing this to the project!
+
+---------------------------------------------------------------------------
+
+**HKCaptcha**
+
+Portions of Securimage contain code from Han-Kwang Nienhuys' PHP captcha
+        
+    Han-Kwang Nienhuys' PHP captcha
+    Copyright June 2007
+    
+    This copyright message and attribution must be preserved upon
+    modification. Redistribution under other licenses is expressly allowed.
+    Other licenses include GPL 2 or higher, BSD, and non-free licenses.
+    The original, unrestricted version can be obtained from
+    http://www.lagom.nl/linux/hkcaptcha/
+    
+---------------------------------------------------------------------------
+
+**AHGBold.ttf**
+
+    AHGBold.ttf (AlteHaasGroteskBold.ttf) font was created by Yann Le Coroller
+    and is distributed as freeware.
+    
+    Alte Haas Grotesk is a typeface that look like an helvetica printed in an
+    old Muller-Brockmann Book.
+    
+    These fonts are freeware and can be distributed as long as they are
+    together with this text file. 
+    
+    I would appreciate very much to see what you have done with it anyway.
+    
+    yann le coroller 
+    www.yannlecoroller.com
+    yann@lecoroller.com
+
+---------------------------------------------------------------------------
+
+**PopForge Flash Library**
+
+Portions of securimage_play.swf use the PopForge flash library for playing audio
+
+    /**
+     * Copyright(C) 2007 Andre Michelle and Joa Ebert
+     *
+     * PopForge is an ActionScript3 code sandbox developed by Andre Michelle
+     * and Joa Ebert
+     * http://sandbox.popforge.de
+     *
+     * PopforgeAS3Audio is free software; you can redistribute it and/or modify
+     * it under the terms of the GNU General Public License as published by
+     * the Free Software Foundation; either version 3 of the License, or
+     * (at your option) any later version.
+     *
+     * PopforgeAS3Audio is distributed in the hope that it will be useful,
+     * but WITHOUT ANY WARRANTY; without even the implied warranty of
+     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+     * GNU General Public License for more details.
+     *
+     * You should have received a copy of the GNU General Public License
+     * along with this program. If not, see <http://www.gnu.org/licenses/>
+     */
+     
+--------------------------------------------------------------------------
+
+**Graphics**
+
+Some graphics used are from the Humility Icon Pack by WorLord
+
+     License: GNU/GPL (http://findicons.com/pack/1723/humility)
+     http://findicons.com/icon/192558/gnome_volume_control
+     http://findicons.com/icon/192562/gtk_refresh
+
+--------------------------------------------------------------------------
+
+
+**Background noise sound files are from SoundJay.com**
+
+http://www.soundjay.com/tos.html
+     
+     All sound effects on this website are created by us and protected under
+     the copyright laws, international treaty provisions and other applicable
+     laws. By downloading sounds, music or any material from this site implies
+     that you have read and accepted these terms and conditions:
+
+     Sound Effects
+     You are allowed to use the sounds free of charge and royalty free in your
+     projects (such as films, videos, games, presentations, animations, stage
+     plays, radio plays, audio books, apps) be it for commercial or
+     non-commercial purposes.
+    
+     But you are NOT allowed to
+     - post the sounds (as sound effects or ringtones) on any website for
+       others to download, copy or use
+     - use them as a raw material to create sound effects or ringtones that
+       you will sell, distribute or offer for downloading
+     - sell, re-sell, license or re-license the sounds (as individual sound
+       effects or as a sound effects library) to anyone else
+     - claim the sounds as yours
+     - link directly to individual sound files
+     - distribute the sounds in apps or computer programs that are clearly
+       sound related in nature (such as sound machine, sound effect
+       generator, ringtone maker, funny sounds app, sound therapy app, etc.)
+       or in apps or computer programs that use the sounds as the program's
+       sound resource library for other people's use (such as animation
+       creator, digital book creator, song maker software, etc.). If you are
+       developing such computer programs, contact us for licensing options.
+    
+     If you use the sound effects, please consider giving us a credit and
+     linking back to us but it's not required.
+     
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/README.txt b/wp-content/plugins/wp2pgpmail/phpcaptcha/README.txt
index b608018d4937b9e1f7ded9cab94ece8754d9dd60..a50a98ca013079defdcf0615c9068e16f932daeb 100644
--- a/wp-content/plugins/wp2pgpmail/phpcaptcha/README.txt
+++ b/wp-content/plugins/wp2pgpmail/phpcaptcha/README.txt
@@ -1,8 +1,10 @@
 NAME:
 
-    Securimage - A PHP class for creating and managing form CAPTCHA images
+    Securimage - A PHP class for creating captcha images and audio with many options.
 
-VERSION: 2.0 BETA
+VERSION:
+
+    3.6.7
 
 AUTHOR:
 
@@ -19,22 +21,33 @@ DOCUMENTATION:
     be found at http://www.phpcaptcha.org/Securimage_Docs/
 
 REQUIREMENTS:
-    PHP 4.3.0
+
+    PHP 5.4 or greater
     GD  2.0
-    FreeType (recommended, required for TTF support)
+    FreeType (Required, for TTF fonts)
+    PDO (if using Sqlite, MySQL, or PostgreSQL)
 
 SYNOPSIS:
 
     require_once 'securimage.php';
-
-    $image = new Securimage();
-
-    $image->show();
+    
+    **Within your HTML form**
+    
+    <form method="post" action="">
+    .. form elements
+    
+    <div>
+        <?php echo Securimage::getCaptchaHtml() ?>
+    </div>
+    </form>
+    
+    
+    **Within your PHP form processor**
 
     // Code Validation
 
     $image = new Securimage();
-    if ($image->check($_POST['code']) == true) {
+    if ($image->check($_POST['captcha_code']) == true) {
       echo "Correct!";
     } else {
       echo "Sorry, wrong code.";
@@ -44,23 +57,75 @@ DESCRIPTION:
 
     What is Securimage?
 
-    Securimage is a PHP class that is used to generate and validate CAPTCHA images.
-    The classes uses an existing PHP session or creates its own if none is found to store the
-    CAPTCHA code.  Variables within the class are used to control the style and display of the image.
-    The class supports TTF fonts and effects for strengthening the security of the image.
-    If TTF support is not available, GD fonts can be used as well, but certain options such as
-    transparent text and angled letters cannot be used.
-
+    Securimage is a PHP class that is used to generate and validate CAPTCHA
+    images.
+    
+    The classes uses an existing PHP session or creates its own if
+    none is found to store the CAPTCHA code.  In addition, a database can be
+    used instead of session storage.
+    
+    Variables within the class are used to control the style and display of
+    the image.  The class uses TTF fonts and effects for strengthening the
+    security of the image.
+    
+    It also creates audible codes which are played for visually impared users.
+
+UPGRADE NOTICE:
+    3.6.3 and below:
+    Securimage 3.6.4 fixed a XSS vulnerability in example_form.ajax.php.  It is
+    recommended to upgrade to the latest version or delete example_form.ajax.php
+    from the securimage directory on your website.
+
+    3.6.2 and above:
+    If you are upgrading to 3.6.2 or greater AND are using database storage,
+    the table structure has changed in 3.6.2 adding an audio_data column for
+    storing audio files in the database in order to support HTTP range
+    requests.  Delete your tables and have Securimage recreate them or see
+    the function createDatabaseTables() in securimage.php for the new structure
+    depending on which database backend you are using and alter the tables as
+    needed.  If using SQLite, just overwrite your existing securimage.sq3 file
+    with the one from this release.
+
+    If you are not using database tables for storage, ignore this notice.
 
 COPYRIGHT:
-    Copyright (c) 2009 Drew Phillips. All rights reserved.
-    This software is released under the GNU Lesser General Public License.
 
-    -----------------------------------------------------------------------------
-    Flash code created for Securimage by Douglas Walsh (www.douglaswalsh.net)
+    Copyright (c) 2018 Drew Phillips
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    - Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    - Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+
+LICENSES:
+
+    The WavFile.php class used in Securimage by Drew Phillips and Paul Voegler
+    is used under the BSD License.  See WavFile.php for details.
+    Many thanks to Paul Voegler (http://www.voegler.eu/) for contributing to
+    Securimage.
+
+    ---------------------------------------------------------------------------
+    Flash code created by Age Bosma & Mario Romero (animario@hotmail.com)
     Many thanks for releasing this to the project!
 
-    ------------------------------------------------------------------------------
+    ---------------------------------------------------------------------------
     Portions of Securimage contain code from Han-Kwang Nienhuys' PHP captcha
         
     Han-Kwang Nienhuys' PHP captcha
@@ -72,10 +137,12 @@ COPYRIGHT:
     The original, unrestricted version can be obtained from
     http://www.lagom.nl/linux/hkcaptcha/
     
-    -------------------------------------------------------------------------------
-    AHGBold.ttf (AlteHaasGroteskBold.ttf) font was created by Yann Le Coroller and is distributed as freeware
+    ---------------------------------------------------------------------------
+    AHGBold.ttf (AlteHaasGroteskBold.ttf) font was created by Yann Le Coroller
+    and is distributed as freeware.
     
-    Alte Haas Grotesk is a typeface that look like an helvetica printed in an old Muller-Brockmann Book.
+    Alte Haas Grotesk is a typeface that look like an helvetica printed in an
+    old Muller-Brockmann Book.
     
     These fonts are freeware and can be distributed as long as they are
     together with this text file. 
@@ -86,3 +153,70 @@ COPYRIGHT:
     www.yannlecoroller.com
     yann@lecoroller.com
 
+    ---------------------------------------------------------------------------
+    Portions of securimage_play.swf use the PopForge flash library for
+    playing audio
+
+    /**
+     * Copyright(C) 2007 Andre Michelle and Joa Ebert
+     *
+     * PopForge is an ActionScript3 code sandbox developed by Andre Michelle
+     * and Joa Ebert
+     * http://sandbox.popforge.de
+     *
+     * PopforgeAS3Audio is free software; you can redistribute it and/or modify
+     * it under the terms of the GNU General Public License as published by
+     * the Free Software Foundation; either version 3 of the License, or
+     * (at your option) any later version.
+     *
+     * PopforgeAS3Audio is distributed in the hope that it will be useful,
+     * but WITHOUT ANY WARRANTY; without even the implied warranty of
+     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+     * GNU General Public License for more details.
+     *
+     * You should have received a copy of the GNU General Public License
+     * along with this program. If not, see <http://www.gnu.org/licenses/>
+     */
+     
+     --------------------------------------------------------------------------
+     Some graphics used are from the Humility Icon Pack by WorLord
+
+     License: GNU/GPL (http://findicons.com/pack/1723/humility)
+     http://findicons.com/icon/192558/gnome_volume_control
+     http://findicons.com/icon/192562/gtk_refresh
+
+     --------------------------------------------------------------------------
+     Background noise sound files are from SoundJay.com
+     http://www.soundjay.com/tos.html
+     
+     All sound effects on this website are created by us and protected under
+     the copyright laws, international treaty provisions and other applicable
+     laws. By downloading sounds, music or any material from this site implies
+     that you have read and accepted these terms and conditions:
+
+     Sound Effects
+     You are allowed to use the sounds free of charge and royalty free in your
+     projects (such as films, videos, games, presentations, animations, stage
+     plays, radio plays, audio books, apps) be it for commercial or
+     non-commercial purposes.
+    
+     But you are NOT allowed to
+     - post the sounds (as sound effects or ringtones) on any website for
+       others to download, copy or use
+     - use them as a raw material to create sound effects or ringtones that
+       you will sell, distribute or offer for downloading
+     - sell, re-sell, license or re-license the sounds (as individual sound
+       effects or as a sound effects library) to anyone else
+     - claim the sounds as yours
+     - link directly to individual sound files
+     - distribute the sounds in apps or computer programs that are clearly
+       sound related in nature (such as sound machine, sound effect
+       generator, ringtone maker, funny sounds app, sound therapy app, etc.)
+       or in apps or computer programs that use the sounds as the program's
+       sound resource library for other people's use (such as animation
+       creator, digital book creator, song maker software, etc.). If you are
+       developing such computer programs, contact us for licensing options.
+    
+     If you use the sound effects, please consider giving us a credit and
+     linking back to us but it's not required.
+     
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/WavFile.php b/wp-content/plugins/wp2pgpmail/phpcaptcha/WavFile.php
new file mode 100644
index 0000000000000000000000000000000000000000..8702d22f83a237040ef2dd7511823d4071275ae2
--- /dev/null
+++ b/wp-content/plugins/wp2pgpmail/phpcaptcha/WavFile.php
@@ -0,0 +1,1913 @@
+<?php
+
+// error_reporting(E_ALL); ini_set('display_errors', 1); // uncomment this line for debugging
+
+/**
+* Project: PHPWavUtils: Classes for creating, reading, and manipulating WAV files in PHP<br />
+* File: WavFile.php<br />
+*
+* Copyright (c) 2014, Drew Phillips
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without modification,
+* are permitted provided that the following conditions are met:
+*
+* - Redistributions of source code must retain the above copyright notice,
+* this list of conditions and the following disclaimer.
+* - Redistributions in binary form must reproduce the above copyright notice,
+* this list of conditions and the following disclaimer in the documentation
+* and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+* POSSIBILITY OF SUCH DAMAGE.
+*
+* Any modifications to the library should be indicated clearly in the source code
+* to inform users that the changes are not a part of the original software.<br /><br />
+*
+* @copyright 2014 Drew Phillips
+* @author Drew Phillips <drew@drew-phillips.com>
+* @author Paul Voegler <http://www.voegler.eu/>
+* @version 1.1.1 (Sep 2015)
+* @package PHPWavUtils
+* @license BSD License
+*
+* Changelog:
+*   1.1.1 (09/08/2015)
+*     - Fix degrade() method to call filter correctly (Rasmus Lerdorf)
+*
+*   1.1 (02/8/2014)
+*     - Add method setIgnoreChunkSizes() to allow reading of wav data with bogus chunk sizes set.
+*       This allows streamed wav data to be processed where the chunk sizes were not known when
+*       writing the header.  Instead calculates the chunk sizes automatically.
+*     - Add simple volume filter to attenuate or amplify the audio signal.
+*
+*   1.0 (10/2/2012)
+*     - Fix insertSilence() creating invalid block size
+*
+*   1.0 RC1 (4/20/2012)
+*     - Initial release candidate
+*     - Supports 8, 16, 24, 32 bit PCM, 32-bit IEEE FLOAT, Extensible Format
+*     - Support for 18 channels of audio
+*     - Ability to read an offset from a file to reduce memory footprint with large files
+*     - Single-pass audio filter processing
+*     - Highly accurate and efficient mix and normalization filters (http://www.voegler.eu/pub/audio/)
+*     - Utility filters for degrading audio, and inserting silence
+*
+*   0.6 (4/12/2012)
+*     - Support 8, 16, 24, 32 bit and PCM float (Paul Voegler)
+*     - Add normalize filter, misc improvements and fixes (Paul Voegler)
+*     - Normalize parameters to filter() to use filter constants as array indices
+*     - Add option to mix filter to loop the target file if the source is longer
+*
+*   0.5 (4/3/2012)
+*     - Fix binary pack routine (Paul Voegler)
+*     - Add improved mixing function (Paul Voegler)
+*
+*/
+
+class WavFile
+{
+    /*%******************************************************************************************%*/
+    // Class constants
+
+    /** @var int Filter flag for mixing two files */
+    const FILTER_MIX       = 0x01;
+
+    /** @var int Filter flag for normalizing audio data */
+    const FILTER_NORMALIZE = 0x02;
+
+    /** @var int Filter flag for degrading audio data */
+    const FILTER_DEGRADE   = 0x04;
+
+    /** @var int Filter flag for amplifying or attenuating audio data. */
+    const FILTER_VOLUME    = 0x08;
+
+    /** @var int Maximum number of channels */
+    const MAX_CHANNEL = 18;
+
+    /** @var int Maximum sample rate */
+    const MAX_SAMPLERATE = 192000;
+
+    /** Channel Locations for ChannelMask */
+    const SPEAKER_DEFAULT               = 0x000000;
+    const SPEAKER_FRONT_LEFT            = 0x000001;
+    const SPEAKER_FRONT_RIGHT           = 0x000002;
+    const SPEAKER_FRONT_CENTER          = 0x000004;
+    const SPEAKER_LOW_FREQUENCY         = 0x000008;
+    const SPEAKER_BACK_LEFT             = 0x000010;
+    const SPEAKER_BACK_RIGHT            = 0x000020;
+    const SPEAKER_FRONT_LEFT_OF_CENTER  = 0x000040;
+    const SPEAKER_FRONT_RIGHT_OF_CENTER = 0x000080;
+    const SPEAKER_BACK_CENTER           = 0x000100;
+    const SPEAKER_SIDE_LEFT             = 0x000200;
+    const SPEAKER_SIDE_RIGHT            = 0x000400;
+    const SPEAKER_TOP_CENTER            = 0x000800;
+    const SPEAKER_TOP_FRONT_LEFT        = 0x001000;
+    const SPEAKER_TOP_FRONT_CENTER      = 0x002000;
+    const SPEAKER_TOP_FRONT_RIGHT       = 0x004000;
+    const SPEAKER_TOP_BACK_LEFT         = 0x008000;
+    const SPEAKER_TOP_BACK_CENTER       = 0x010000;
+    const SPEAKER_TOP_BACK_RIGHT        = 0x020000;
+    const SPEAKER_ALL                   = 0x03FFFF;
+
+    /** @var int PCM Audio Format */
+    const WAVE_FORMAT_PCM           = 0x0001;
+
+    /** @var int IEEE FLOAT Audio Format */
+    const WAVE_FORMAT_IEEE_FLOAT    = 0x0003;
+
+    /** @var int EXTENSIBLE Audio Format - actual audio format defined by SubFormat */
+    const WAVE_FORMAT_EXTENSIBLE    = 0xFFFE;
+
+    /** @var string PCM Audio Format SubType - LE hex representation of GUID {00000001-0000-0010-8000-00AA00389B71} */
+    const WAVE_SUBFORMAT_PCM        = "0100000000001000800000aa00389b71";
+
+    /** @var string IEEE FLOAT Audio Format SubType - LE hex representation of GUID {00000003-0000-0010-8000-00AA00389B71} */
+    const WAVE_SUBFORMAT_IEEE_FLOAT = "0300000000001000800000aa00389b71";
+
+
+    /*%******************************************************************************************%*/
+    // Properties
+
+    /** @var array Log base modifier lookup table for a given threshold (in 0.05 steps) used by normalizeSample.
+     * Adjusts the slope (1st derivative) of the log function at the threshold to 1 for a smooth transition
+     * from linear to logarithmic amplitude output. */
+    protected static $LOOKUP_LOGBASE = array(
+        2.513, 2.667, 2.841, 3.038, 3.262,
+        3.520, 3.819, 4.171, 4.589, 5.093,
+        5.711, 6.487, 7.483, 8.806, 10.634,
+        13.302, 17.510, 24.970, 41.155, 96.088
+    );
+
+    /** @var int The actual physical file size */
+    protected $_actualSize;
+
+    /** @var int The size of the file in RIFF header */
+    protected $_chunkSize;
+
+    /** @var int The size of the "fmt " chunk */
+    protected $_fmtChunkSize;
+
+    /** @var int The size of the extended "fmt " data */
+    protected $_fmtExtendedSize;
+
+    /** @var int The size of the "fact" chunk */
+    protected $_factChunkSize;
+
+    /** @var int Size of the data chunk */
+    protected $_dataSize;
+
+    /** @var int Size of the data chunk in the opened wav file */
+    protected $_dataSize_fp;
+
+    /** @var bool Does _dataSize really reflect strlen($_samples)? Case when a wav file is read with readData = false */
+    protected $_dataSize_valid;
+
+    /** @var int Starting offset of data chunk */
+    protected $_dataOffset;
+
+    /** @var int The audio format - WavFile::WAVE_FORMAT_* */
+    protected $_audioFormat;
+
+    /** @var int|string|null The audio subformat - WavFile::WAVE_SUBFORMAT_* */
+    protected $_audioSubFormat;
+
+    /** @var int Number of channels in the audio file */
+    protected $_numChannels;
+
+    /** @var int The channel mask */
+    protected $_channelMask;
+
+    /** @var int Samples per second */
+    protected $_sampleRate;
+
+    /** @var int Number of bits per sample */
+    protected $_bitsPerSample;
+
+    /** @var int Number of valid bits per sample */
+    protected $_validBitsPerSample;
+
+    /** @var int NumChannels * BitsPerSample/8 */
+    protected $_blockAlign;
+
+    /** @var int Number of sample blocks */
+    protected $_numBlocks;
+
+    /** @var int Bytes per second */
+    protected $_byteRate;
+
+    /** @var bool Ignore chunk sizes when reading wav data (useful when reading data from a stream where chunk sizes contain dummy values) */
+    protected $_ignoreChunkSizes;
+
+    /** @var string Binary string of samples */
+    protected $_samples;
+
+    /** @var resource|null The file pointer used for reading wavs from file or memory */
+    protected $_fp;
+
+
+    /*%******************************************************************************************%*/
+    // Special methods
+
+    /**
+     * WavFile Constructor.
+     *
+     * <code>
+     * $wav1 = new WavFile(2, 44100, 16);         // new wav with 2 channels, at 44100 samples/sec and 16 bits per sample
+     * $wav2 = new WavFile('./audio/sound.wav');  // open and read wav file
+     * </code>
+     *
+     * @param string|int $numChannelsOrFileName  (Optional) If string, the filename of the wav file to open. The number of channels otherwise. Defaults to 1.
+     * @param int|bool $sampleRateOrReadData  (Optional) If opening a file and boolean, decides whether to read the data chunk or not. Defaults to true. The sample rate in samples per second otherwise. 8000 = standard telephone, 16000 = wideband telephone, 32000 = FM radio and 44100 = CD quality. Defaults to 8000.
+     * @param int $bitsPerSample  (Optional) The number of bits per sample. Has to be 8, 16 or 24 for PCM audio or 32 for IEEE FLOAT audio. 8 = telephone, 16 = CD and 24 or 32 = studio quality. Defaults to 8.
+     * @throws WavFormatException
+     * @throws WavFileException
+     */
+    public function __construct($numChannelsOrFileName = null, $sampleRateOrReadData = null, $bitsPerSample = null)
+    {
+        $this->_actualSize         = 44;
+        $this->_chunkSize          = 36;
+        $this->_fmtChunkSize       = 16;
+        $this->_fmtExtendedSize    = 0;
+        $this->_factChunkSize      = 0;
+        $this->_dataSize           = 0;
+        $this->_dataSize_fp        = 0;
+        $this->_dataSize_valid     = true;
+        $this->_dataOffset         = 44;
+        $this->_audioFormat        = self::WAVE_FORMAT_PCM;
+        $this->_audioSubFormat     = null;
+        $this->_numChannels        = 1;
+        $this->_channelMask        = self::SPEAKER_DEFAULT;
+        $this->_sampleRate         = 8000;
+        $this->_bitsPerSample      = 8;
+        $this->_validBitsPerSample = 8;
+        $this->_blockAlign         = 1;
+        $this->_numBlocks          = 0;
+        $this->_byteRate           = 8000;
+        $this->_ignoreChunkSizes   = false;
+        $this->_samples            = '';
+        $this->_fp                 = null;
+
+
+        if (is_string($numChannelsOrFileName)) {
+            $this->openWav($numChannelsOrFileName, is_bool($sampleRateOrReadData) ? $sampleRateOrReadData : true);
+
+        } else {
+            $this->setNumChannels(is_null($numChannelsOrFileName) ? 1 : $numChannelsOrFileName)
+                 ->setSampleRate(is_null($sampleRateOrReadData) ? 8000 : $sampleRateOrReadData)
+                 ->setBitsPerSample(is_null($bitsPerSample) ? 8 : $bitsPerSample);
+        }
+    }
+
+    public function __destruct() {
+        if (is_resource($this->_fp)) $this->closeWav();
+    }
+
+    public function __clone() {
+        $this->_fp = null;
+    }
+
+    /**
+     * Output the wav file headers and data.
+     *
+     * @return string  The encoded file.
+     */
+    public function __toString()
+    {
+        return $this->makeHeader() .
+               $this->getDataSubchunk();
+    }
+
+
+    /*%******************************************************************************************%*/
+    // Static methods
+
+    /**
+     * Unpacks a single binary sample to numeric value.
+     *
+     * @param string $sampleBinary  (Required) The sample to decode.
+     * @param int $bitDepth  (Optional) The bits per sample to decode. If omitted, derives it from the length of $sampleBinary.
+     * @return int|float|null  The numeric sample value. Float for 32-bit samples. Returns null for unsupported bit depths.
+     */
+    public static function unpackSample($sampleBinary, $bitDepth = null)
+    {
+        if ($bitDepth === null) {
+            $bitDepth = strlen($sampleBinary) * 8;
+        }
+
+        switch ($bitDepth) {
+            case 8:
+                // unsigned char
+                return ord($sampleBinary);
+
+            case 16:
+                // signed short, little endian
+                $data = unpack('v', $sampleBinary);
+                $sample = $data[1];
+                if ($sample >= 0x8000) {
+                    $sample -= 0x10000;
+                }
+                return $sample;
+
+            case 24:
+                // 3 byte packed signed integer, little endian
+                $data = unpack('C3', $sampleBinary);
+                $sample = $data[1] | ($data[2] << 8) | ($data[3] << 16);
+                if ($sample >= 0x800000) {
+                    $sample -= 0x1000000;
+                }
+                return $sample;
+
+            case 32:
+                // 32-bit float
+                $data = unpack('f', $sampleBinary);
+                return $data[1];
+
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Packs a single numeric sample to binary.
+     *
+     * @param int|float $sample  (Required) The sample to encode. Has to be within valid range for $bitDepth. Float values only for 32 bits.
+     * @param int $bitDepth  (Required) The bits per sample to encode with.
+     * @return string|null  The encoded binary sample. Returns null for unsupported bit depths.
+     */
+    public static function packSample($sample, $bitDepth)
+    {
+        switch ($bitDepth) {
+            case 8:
+                // unsigned char
+                return chr($sample);
+
+            case 16:
+                // signed short, little endian
+                if ($sample < 0) {
+                    $sample += 0x10000;
+                }
+                return pack('v', $sample);
+
+            case 24:
+                // 3 byte packed signed integer, little endian
+                if ($sample < 0) {
+                    $sample += 0x1000000;
+                }
+                return pack('C3', $sample & 0xff, ($sample >>  8) & 0xff, ($sample >> 16) & 0xff);
+
+            case 32:
+                // 32-bit float
+                return pack('f', $sample);
+
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Unpacks a binary sample block to numeric values.
+     *
+     * @param string $sampleBlock  (Required) The binary sample block (all channels).
+     * @param int $bitDepth  (Required) The bits per sample to decode.
+     * @param int $numChannels  (Optional) The number of channels to decode. If omitted, derives it from the length of $sampleBlock and $bitDepth.
+     * @return array  The sample values as an array of integers of floats for 32 bits. First channel is array index 1.
+     */
+    public static function unpackSampleBlock($sampleBlock, $bitDepth, $numChannels = null) {
+        $sampleBytes = $bitDepth / 8;
+        if ($numChannels === null) {
+            $numChannels = strlen($sampleBlock) / $sampleBytes;
+        }
+
+        $samples = array();
+        for ($i = 0; $i < $numChannels; $i++) {
+            $sampleBinary = substr($sampleBlock, $i * $sampleBytes, $sampleBytes);
+            $samples[$i + 1] = self::unpackSample($sampleBinary, $bitDepth);
+        }
+
+        return $samples;
+    }
+
+    /**
+     * Packs an array of numeric channel samples to a binary sample block.
+     *
+     * @param array $samples  (Required) The array of channel sample values. Expects float values for 32 bits and integer otherwise.
+     * @param int $bitDepth  (Required) The bits per sample to encode with.
+     * @return string  The encoded binary sample block.
+     */
+    public static function packSampleBlock($samples, $bitDepth) {
+        $sampleBlock = '';
+        foreach($samples as $sample) {
+            $sampleBlock .= self::packSample($sample, $bitDepth);
+        }
+
+        return $sampleBlock;
+    }
+
+    /**
+     * Normalizes a float audio sample. Maximum input range assumed for compression is [-2, 2].
+     * See http://www.voegler.eu/pub/audio/ for more information.
+     *
+     * @param float $sampleFloat  (Required) The float sample to normalize.
+     * @param float $threshold  (Required) The threshold or gain factor for normalizing the amplitude. <ul>
+     *     <li> >= 1 - Normalize by multiplying by the threshold (boost - positive gain). <br />
+     *            A value of 1 in effect means no normalization (and results in clipping). </li>
+     *     <li> <= -1 - Normalize by dividing by the the absolute value of threshold (attenuate - negative gain). <br />
+     *            A factor of 2 (-2) is about 6dB reduction in volume.</li>
+     *     <li> [0, 1) - (open inverval - not including 1) - The threshold
+     *            above which amplitudes are comressed logarithmically. <br />
+     *            e.g. 0.6 to leave amplitudes up to 60% "as is" and compress above. </li>
+     *     <li> (-1, 0) - (open inverval - not including -1 and 0) - The threshold
+     *            above which amplitudes are comressed linearly. <br />
+     *            e.g. -0.6 to leave amplitudes up to 60% "as is" and compress above. </li></ul>
+     * @return float  The normalized sample.
+     **/
+    public static function normalizeSample($sampleFloat, $threshold) {
+        // apply positive gain
+        if ($threshold >= 1) {
+            return $sampleFloat * $threshold;
+        }
+
+        // apply negative gain
+        if ($threshold <= -1) {
+            return $sampleFloat / -$threshold;
+        }
+
+        $sign = $sampleFloat < 0 ? -1 : 1;
+        $sampleAbs = abs($sampleFloat);
+
+        // logarithmic compression
+        if ($threshold >= 0 && $threshold < 1 && $sampleAbs > $threshold) {
+            $loga = self::$LOOKUP_LOGBASE[(int)($threshold * 20)]; // log base modifier
+            return $sign * ($threshold + (1 - $threshold) * log(1 + $loga * ($sampleAbs - $threshold) / (2 - $threshold)) / log(1 + $loga));
+        }
+
+        // linear compression
+        $thresholdAbs = abs($threshold);
+        if ($threshold > -1 && $threshold < 0 && $sampleAbs > $thresholdAbs) {
+            return $sign * ($thresholdAbs + (1 - $thresholdAbs) / (2 - $thresholdAbs) * ($sampleAbs - $thresholdAbs));
+        }
+
+        // else ?
+        return $sampleFloat;
+    }
+
+
+    /*%******************************************************************************************%*/
+    // Getter and Setter methods for properties
+
+    public function getActualSize() {
+        return $this->_actualSize;
+    }
+
+    /** @param int $actualSize */
+    protected function setActualSize($actualSize = null) {
+        if (is_null($actualSize)) {
+            $this->_actualSize = 8 + $this->_chunkSize;  // + "RIFF" header (ID + size)
+        } else {
+            $this->_actualSize = $actualSize;
+        }
+
+        return $this;
+    }
+
+    public function getChunkSize() {
+        return $this->_chunkSize;
+    }
+
+    /** @param int $chunkSize */
+    protected function setChunkSize($chunkSize = null) {
+        if (is_null($chunkSize)) {
+            $this->_chunkSize = 4 +                                                            // "WAVE" chunk
+                                8 + $this->_fmtChunkSize +                                     // "fmt " subchunk
+                                ($this->_factChunkSize > 0 ? 8 + $this->_factChunkSize : 0) +  // "fact" subchunk
+                                8 + $this->_dataSize +                                         // "data" subchunk
+                                ($this->_dataSize & 1);                                        // padding byte
+        } else {
+            $this->_chunkSize = $chunkSize;
+        }
+
+        $this->setActualSize();
+
+        return $this;
+    }
+
+    public function getFmtChunkSize() {
+        return $this->_fmtChunkSize;
+    }
+
+    /** @param int $fmtChunkSize */
+    protected function setFmtChunkSize($fmtChunkSize = null) {
+        if (is_null($fmtChunkSize)) {
+            $this->_fmtChunkSize = 16 + $this->_fmtExtendedSize;
+        } else {
+            $this->_fmtChunkSize = $fmtChunkSize;
+        }
+
+        $this->setChunkSize()    // implicit setActualSize()
+             ->setDataOffset();
+
+        return $this;
+    }
+
+    public function getFmtExtendedSize() {
+        return $this->_fmtExtendedSize;
+    }
+
+    /** @param int $fmtExtendedSize */
+    protected function setFmtExtendedSize($fmtExtendedSize = null) {
+        if (is_null($fmtExtendedSize)) {
+            if ($this->_audioFormat == self::WAVE_FORMAT_EXTENSIBLE) {
+                $this->_fmtExtendedSize = 2 + 22;                          // extension size for WAVE_FORMAT_EXTENSIBLE
+            } elseif ($this->_audioFormat != self::WAVE_FORMAT_PCM) {
+                $this->_fmtExtendedSize = 2 + 0;                           // empty extension
+            } else {
+                $this->_fmtExtendedSize = 0;                               // no extension, only for WAVE_FORMAT_PCM
+            }
+        } else {
+            $this->_fmtExtendedSize = $fmtExtendedSize;
+        }
+
+        $this->setFmtChunkSize();  // implicit setSize(), setActualSize(), setDataOffset()
+
+        return $this;
+    }
+
+    public function getFactChunkSize() {
+        return $this->_factChunkSize;
+    }
+
+    /** @param int $factChunkSize */
+    protected function setFactChunkSize($factChunkSize = null) {
+        if (is_null($factChunkSize)) {
+            if ($this->_audioFormat != self::WAVE_FORMAT_PCM) {
+                $this->_factChunkSize = 4;
+            } else {
+                $this->_factChunkSize = 0;
+            }
+        } else {
+            $this->_factChunkSize = $factChunkSize;
+        }
+
+        $this->setChunkSize()    // implicit setActualSize()
+             ->setDataOffset();
+
+        return $this;
+    }
+
+    public function getDataSize() {
+        return $this->_dataSize;
+    }
+
+    /** @param int $dataSize */
+    protected function setDataSize($dataSize = null) {
+        if (is_null($dataSize)) {
+            $this->_dataSize = strlen($this->_samples);
+        } else {
+            $this->_dataSize = $dataSize;
+        }
+
+        $this->setChunkSize()   // implicit setActualSize()
+             ->setNumBlocks();
+        $this->_dataSize_valid = true;
+
+        return $this;
+    }
+
+    public function getDataOffset() {
+        return $this->_dataOffset;
+    }
+
+    /** @param int $dataOffset */
+    protected function setDataOffset($dataOffset = null) {
+        if (is_null($dataOffset)) {
+            $this->_dataOffset = 8 +                                                            // "RIFF" header (ID + size)
+                                 4 +                                                            // "WAVE" chunk
+                                 8 + $this->_fmtChunkSize +                                     // "fmt " subchunk
+                                 ($this->_factChunkSize > 0 ? 8 + $this->_factChunkSize : 0) +  // "fact" subchunk
+                                 8;                                                             // "data" subchunk
+        } else {
+            $this->_dataOffset = $dataOffset;
+        }
+
+        return $this;
+    }
+
+    public function getAudioFormat() {
+        return $this->_audioFormat;
+    }
+
+    /** @param int $audioFormat */
+    protected function setAudioFormat($audioFormat = null) {
+        if (is_null($audioFormat)) {
+            if (($this->_bitsPerSample <= 16 || $this->_bitsPerSample == 32)
+              && $this->_validBitsPerSample == $this->_bitsPerSample
+              && $this->_channelMask == self::SPEAKER_DEFAULT
+              && $this->_numChannels <= 2) {
+                if ($this->_bitsPerSample <= 16) {
+                    $this->_audioFormat = self::WAVE_FORMAT_PCM;
+                } else {
+                    $this->_audioFormat = self::WAVE_FORMAT_IEEE_FLOAT;
+                }
+            } else {
+                $this->_audioFormat = self::WAVE_FORMAT_EXTENSIBLE;
+            }
+        } else {
+            $this->_audioFormat = $audioFormat;
+        }
+
+        $this->setAudioSubFormat()
+             ->setFactChunkSize()     // implicit setSize(), setActualSize(), setDataOffset()
+             ->setFmtExtendedSize();  // implicit setFmtChunkSize(), setSize(), setActualSize(), setDataOffset()
+
+        return $this;
+    }
+
+    public function getAudioSubFormat() {
+        return $this->_audioSubFormat;
+    }
+
+    /** @param int $audioSubFormat */
+    protected function setAudioSubFormat($audioSubFormat = null) {
+        if (is_null($audioSubFormat)) {
+            if ($this->_bitsPerSample == 32) {
+                $this->_audioSubFormat = self::WAVE_SUBFORMAT_IEEE_FLOAT;  // 32 bits are IEEE FLOAT in this class
+            } else {
+                $this->_audioSubFormat = self::WAVE_SUBFORMAT_PCM;         // 8, 16 and 24 bits are PCM in this class
+            }
+        } else {
+            $this->_audioSubFormat = $audioSubFormat;
+        }
+
+        return $this;
+    }
+
+    public function getNumChannels() {
+        return $this->_numChannels;
+    }
+
+    /** @param int $numChannels */
+    public function setNumChannels($numChannels) {
+        if ($numChannels < 1 || $numChannels > self::MAX_CHANNEL) {
+            throw new WavFileException('Unsupported number of channels. Only up to ' . self::MAX_CHANNEL . ' channels are supported.');
+        } elseif ($this->_samples !== '') {
+            trigger_error('Wav already has sample data. Changing the number of channels does not convert and may corrupt the data.', E_USER_NOTICE);
+        }
+
+        $this->_numChannels = (int)$numChannels;
+
+        $this->setAudioFormat()  // implicit setAudioSubFormat(), setFactChunkSize(), setFmtExtendedSize(), setFmtChunkSize(), setSize(), setActualSize(), setDataOffset()
+             ->setByteRate()
+             ->setBlockAlign();  // implicit setNumBlocks()
+
+        return $this;
+    }
+
+    public function getChannelMask() {
+        return $this->_channelMask;
+    }
+
+    public function setChannelMask($channelMask = self::SPEAKER_DEFAULT) {
+        if ($channelMask != 0) {
+            // count number of set bits - Hamming weight
+            $c = (int)$channelMask;
+            $n = 0;
+            while ($c > 0) {
+                $n += $c & 1;
+                $c >>= 1;
+            }
+            if ($n != $this->_numChannels || (((int)$channelMask | self::SPEAKER_ALL) != self::SPEAKER_ALL)) {
+                throw new WavFileException('Invalid channel mask. The number of channels does not match the number of locations in the mask.');
+            }
+        }
+
+        $this->_channelMask = (int)$channelMask;
+
+        $this->setAudioFormat();  // implicit setAudioSubFormat(), setFactChunkSize(), setFmtExtendedSize(), setFmtChunkSize(), setSize(), setActualSize(), setDataOffset()
+
+        return $this;
+    }
+
+    public function getSampleRate() {
+        return $this->_sampleRate;
+    }
+
+    public function setSampleRate($sampleRate) {
+        if ($sampleRate < 1 || $sampleRate > self::MAX_SAMPLERATE) {
+            throw new WavFileException('Invalid sample rate.');
+        } elseif ($this->_samples !== '') {
+            trigger_error('Wav already has sample data. Changing the sample rate does not convert the data and may yield undesired results.', E_USER_NOTICE);
+        }
+
+        $this->_sampleRate = (int)$sampleRate;
+
+        $this->setByteRate();
+
+        return $this;
+    }
+
+    public function getBitsPerSample() {
+        return $this->_bitsPerSample;
+    }
+
+    public function setBitsPerSample($bitsPerSample) {
+        if (!in_array($bitsPerSample, array(8, 16, 24, 32))) {
+            throw new WavFileException('Unsupported bits per sample. Only 8, 16, 24 and 32 bits are supported.');
+        } elseif ($this->_samples !== '') {
+            trigger_error('Wav already has sample data. Changing the bits per sample does not convert and may corrupt the data.', E_USER_NOTICE);
+        }
+
+        $this->_bitsPerSample = (int)$bitsPerSample;
+
+        $this->setValidBitsPerSample()  // implicit setAudioFormat(), setAudioSubFormat(), setFmtChunkSize(), setFactChunkSize(), setSize(), setActualSize(), setDataOffset()
+             ->setByteRate()
+             ->setBlockAlign();         // implicit setNumBlocks()
+
+        return $this;
+    }
+
+    public function getValidBitsPerSample() {
+        return $this->_validBitsPerSample;
+    }
+
+    protected function setValidBitsPerSample($validBitsPerSample = null) {
+        if (is_null($validBitsPerSample)) {
+            $this->_validBitsPerSample = $this->_bitsPerSample;
+        } else {
+            if ($validBitsPerSample < 1 || $validBitsPerSample > $this->_bitsPerSample) {
+                throw new WavFileException('ValidBitsPerSample cannot be greater than BitsPerSample.');
+            }
+            $this->_validBitsPerSample = (int)$validBitsPerSample;
+        }
+
+        $this->setAudioFormat();  // implicit setAudioSubFormat(), setFactChunkSize(), setFmtExtendedSize(), setFmtChunkSize(), setSize(), setActualSize(), setDataOffset()
+
+        return $this;
+    }
+
+    public function getBlockAlign() {
+        return $this->_blockAlign;
+    }
+
+    /** @param int $blockAlign */
+    protected function setBlockAlign($blockAlign = null) {
+        if (is_null($blockAlign)) {
+            $this->_blockAlign = $this->_numChannels * $this->_bitsPerSample / 8;
+        } else {
+            $this->_blockAlign = $blockAlign;
+        }
+
+        $this->setNumBlocks();
+
+        return $this;
+    }
+
+    public function getNumBlocks()
+    {
+        return $this->_numBlocks;
+    }
+
+    /** @param int $numBlocks */
+    protected function setNumBlocks($numBlocks = null) {
+        if (is_null($numBlocks)) {
+            $this->_numBlocks = (int)($this->_dataSize / $this->_blockAlign);  // do not count incomplete sample blocks
+        } else {
+            $this->_numBlocks = $numBlocks;
+        }
+
+        return $this;
+    }
+
+    public function getByteRate() {
+        return $this->_byteRate;
+    }
+
+    /** @param int $byteRate */
+    protected function setByteRate($byteRate = null) {
+        if (is_null($byteRate)) {
+            $this->_byteRate = $this->_sampleRate * $this->_numChannels * $this->_bitsPerSample / 8;
+        } else {
+            $this->_byteRate = $byteRate;
+        }
+
+        return $this;
+    }
+
+    public function getIgnoreChunkSizes()
+    {
+        return $this->_ignoreChunkSizes;
+    }
+
+    public function setIgnoreChunkSizes($ignoreChunkSizes)
+    {
+        $this->_ignoreChunkSizes = (bool)$ignoreChunkSizes;
+        return $this;
+    }
+
+    public function getSamples() {
+        return $this->_samples;
+    }
+
+    public function setSamples(&$samples = '') {
+        if (strlen($samples) % $this->_blockAlign != 0) {
+            throw new WavFileException('Incorrect samples size. Has to be a multiple of BlockAlign.');
+        }
+
+        $this->_samples = $samples;
+
+        $this->setDataSize();  // implicit setSize(), setActualSize(), setNumBlocks()
+
+        return $this;
+    }
+
+
+    /*%******************************************************************************************%*/
+    // Getters
+
+    public function getMinAmplitude()
+    {
+        if ($this->_bitsPerSample == 8) {
+            return 0;
+        } elseif ($this->_bitsPerSample == 32) {
+            return -1.0;
+        } else {
+            return -(1 << ($this->_bitsPerSample - 1));
+        }
+    }
+
+    public function getZeroAmplitude()
+    {
+        if ($this->_bitsPerSample == 8) {
+            return 0x80;
+        } elseif ($this->_bitsPerSample == 32) {
+            return 0.0;
+        } else {
+            return 0;
+        }
+    }
+
+    public function getMaxAmplitude()
+    {
+        if($this->_bitsPerSample == 8) {
+            return 0xFF;
+        } elseif($this->_bitsPerSample == 32) {
+            return 1.0;
+        } else {
+            return (1 << ($this->_bitsPerSample - 1)) - 1;
+        }
+    }
+
+
+    /*%******************************************************************************************%*/
+    // Wave file methods
+
+    /**
+     * Construct a wav header from this object. Includes "fact" chunk if necessary.
+     * http://www-mmsp.ece.mcgill.ca/documents/audioformats/wave/wave.html
+     *
+     * @return string  The RIFF header data.
+     */
+    public function makeHeader()
+    {
+        // reset and recalculate
+        $this->setAudioFormat();                                    // implicit setAudioSubFormat(), setFactChunkSize(), setFmtExtendedSize(), setFmtChunkSize(), setSize(), setActualSize(), setDataOffset()
+        $this->setNumBlocks();
+
+        // RIFF header
+        $header = pack('N', 0x52494646);                            // ChunkID - "RIFF"
+        $header .= pack('V', $this->getChunkSize());                // ChunkSize
+        $header .= pack('N', 0x57415645);                           // Format - "WAVE"
+
+        // "fmt " subchunk
+        $header .= pack('N', 0x666d7420);                           // SubchunkID - "fmt "
+        $header .= pack('V', $this->getFmtChunkSize());             // SubchunkSize
+        $header .= pack('v', $this->getAudioFormat());              // AudioFormat
+        $header .= pack('v', $this->getNumChannels());              // NumChannels
+        $header .= pack('V', $this->getSampleRate());               // SampleRate
+        $header .= pack('V', $this->getByteRate());                 // ByteRate
+        $header .= pack('v', $this->getBlockAlign());               // BlockAlign
+        $header .= pack('v', $this->getBitsPerSample());            // BitsPerSample
+        if($this->getFmtExtendedSize() == 24) {
+            $header .= pack('v', 22);                               // extension size = 24 bytes, cbSize: 24 - 2 = 22 bytes
+            $header .= pack('v', $this->getValidBitsPerSample());   // ValidBitsPerSample
+            $header .= pack('V', $this->getChannelMask());          // ChannelMask
+            $header .= pack('H32', $this->getAudioSubFormat());     // SubFormat
+        } elseif ($this->getFmtExtendedSize() == 2) {
+            $header .= pack('v', 0);                                // extension size = 2 bytes, cbSize: 2 - 2 = 0 bytes
+        }
+
+        // "fact" subchunk
+        if ($this->getFactChunkSize() == 4) {
+            $header .= pack('N', 0x66616374);                       // SubchunkID - "fact"
+            $header .= pack('V', 4);                                // SubchunkSize
+            $header .= pack('V', $this->getNumBlocks());            // SampleLength (per channel)
+        }
+
+        return $header;
+    }
+
+    /**
+     * Construct wav DATA chunk.
+     *
+     * @return string  The DATA header and chunk.
+     */
+    public function getDataSubchunk()
+    {
+        // check preconditions
+        if (!$this->_dataSize_valid) {
+            $this->setDataSize();  // implicit setSize(), setActualSize(), setNumBlocks()
+        }
+
+
+        // create subchunk
+        return pack('N', 0x64617461) .                    // SubchunkID - "data"
+               pack('V', $this->getDataSize()) .          // SubchunkSize
+               $this->_samples .                          // Subchunk data
+               ($this->getDataSize() & 1 ? chr(0) : '');  // padding byte
+    }
+
+    /**
+     * Save the wav data to a file.
+     *
+     * @param string $filename  (Required) The file path to save the wav to.
+     * @throws WavFileException
+     */
+    public function save($filename)
+    {
+        $fp = @fopen($filename, 'w+b');
+        if (!is_resource($fp)) {
+            throw new WavFileException('Failed to open "' . $filename . '" for writing.');
+        }
+
+        fwrite($fp, $this->makeHeader());
+        fwrite($fp, $this->getDataSubchunk());
+        fclose($fp);
+
+        return $this;
+    }
+
+    /**
+     * Reads a wav header and data from a file.
+     *
+     * @param string $filename  (Required) The path to the wav file to read.
+     * @param bool $readData  (Optional) If true, also read the data chunk.
+     * @throws WavFormatException
+     * @throws WavFileException
+     */
+    public function openWav($filename, $readData = true)
+    {
+        // check preconditions
+        if (!file_exists($filename)) {
+            throw new WavFileException('Failed to open "' . $filename . '". File not found.');
+        } elseif (!is_readable($filename)) {
+            throw new WavFileException('Failed to open "' . $filename . '". File is not readable.');
+        } elseif (is_resource($this->_fp)) {
+            $this->closeWav();
+        }
+
+
+        // open the file
+        $this->_fp = @fopen($filename, 'rb');
+        if (!is_resource($this->_fp)) {
+            throw new WavFileException('Failed to open "' . $filename . '".');
+        }
+
+        // read the file
+        return $this->readWav($readData);
+    }
+
+    /**
+     * Close a with openWav() previously opened wav file or free the buffer of setWavData().
+     * Not necessary if the data has been read (readData = true) already.
+     */
+    public function closeWav() {
+        if (is_resource($this->_fp)) fclose($this->_fp);
+
+        return $this;
+    }
+
+    /**
+     * Set the wav file data and properties from a wav file in a string.
+     *
+     * @param string $data  (Required) The wav file data. Passed by reference.
+     * @param bool $free  (Optional) True to free the passed $data after copying.
+     * @throws WavFormatException
+     * @throws WavFileException
+     */
+    public function setWavData(&$data, $free = true)
+    {
+        // check preconditions
+        if (is_resource($this->_fp)) $this->closeWav();
+
+
+        // open temporary stream in memory
+        $this->_fp = @fopen('php://memory', 'w+b');
+        if (!is_resource($this->_fp)) {
+            throw new WavFileException('Failed to open memory stream to write wav data. Use openWav() instead.');
+        }
+
+        // prepare stream
+        fwrite($this->_fp, $data);
+        rewind($this->_fp);
+
+        // free the passed data
+        if ($free) $data = null;
+
+        // read the stream like a file
+        return $this->readWav(true);
+    }
+
+    /**
+     * Read wav file from a stream.
+     *
+     * @param bool $readData  (Optional) If true, also read the data chunk.
+     * @throws WavFormatException
+     * @throws WavFileException
+     */
+    protected function readWav($readData = true)
+    {
+        if (!is_resource($this->_fp)) {
+            throw new WavFileException('No wav file open. Use openWav() first.');
+        }
+
+        try {
+            $this->readWavHeader();
+        } catch (WavFileException $ex) {
+            $this->closeWav();
+            throw $ex;
+        }
+
+        if ($readData) return $this->readWavData();
+
+        return $this;
+    }
+
+    /**
+     * Parse a wav header.
+     * http://www-mmsp.ece.mcgill.ca/documents/audioformats/wave/wave.html
+     *
+     * @throws WavFormatException
+     * @throws WavFileException
+     */
+    protected function readWavHeader()
+    {
+        if (!is_resource($this->_fp)) {
+            throw new WavFileException('No wav file open. Use openWav() first.');
+        }
+
+        // get actual file size
+        $stat = fstat($this->_fp);
+        $actualSize = $stat['size'];
+
+        $this->_actualSize = $actualSize;
+
+
+        // read the common header
+        $header = fread($this->_fp, 36);  // minimum size of the wav header
+        if (strlen($header) < 36) {
+            throw new WavFormatException('Not wav format. Header too short.', 1);
+        }
+
+
+        // check "RIFF" header
+        $RIFF = unpack('NChunkID/VChunkSize/NFormat', $header);
+
+        if ($RIFF['ChunkID'] != 0x52494646) {  // "RIFF"
+            throw new WavFormatException('Not wav format. "RIFF" signature missing.', 2);
+        }
+
+        if ($this->getIgnoreChunkSizes()) {
+            $RIFF['ChunkSize'] = $actualSize - 8;
+        } else if ($actualSize - 8 < $RIFF['ChunkSize']) {
+            trigger_error('"RIFF" chunk size does not match actual file size. Found ' . $RIFF['ChunkSize'] . ', expected ' . ($actualSize - 8) . '.', E_USER_NOTICE);
+            $RIFF['ChunkSize'] = $actualSize - 8;
+        }
+
+        if ($RIFF['Format'] != 0x57415645) {  // "WAVE"
+            throw new WavFormatException('Not wav format. "RIFF" chunk format is not "WAVE".', 4);
+        }
+
+        $this->_chunkSize = $RIFF['ChunkSize'];
+
+
+        // check common "fmt " subchunk
+        $fmt = unpack('NSubchunkID/VSubchunkSize/vAudioFormat/vNumChannels/'
+                     .'VSampleRate/VByteRate/vBlockAlign/vBitsPerSample',
+                     substr($header, 12));
+
+        if ($fmt['SubchunkID'] != 0x666d7420) {  // "fmt "
+            throw new WavFormatException('Bad wav header. Expected "fmt " subchunk.', 11);
+        }
+
+        if ($fmt['SubchunkSize'] < 16) {
+            throw new WavFormatException('Bad "fmt " subchunk size.', 12);
+        }
+
+        if (   $fmt['AudioFormat'] != self::WAVE_FORMAT_PCM
+            && $fmt['AudioFormat'] != self::WAVE_FORMAT_IEEE_FLOAT
+            && $fmt['AudioFormat'] != self::WAVE_FORMAT_EXTENSIBLE)
+        {
+            throw new WavFormatException('Unsupported audio format. Only PCM or IEEE FLOAT (EXTENSIBLE) audio is supported.', 13);
+        }
+
+        if ($fmt['NumChannels'] < 1 || $fmt['NumChannels'] > self::MAX_CHANNEL) {
+            throw new WavFormatException('Invalid number of channels in "fmt " subchunk.', 14);
+        }
+
+        if ($fmt['SampleRate'] < 1 || $fmt['SampleRate'] > self::MAX_SAMPLERATE) {
+            throw new WavFormatException('Invalid sample rate in "fmt " subchunk.', 15);
+        }
+
+        if (   ($fmt['AudioFormat'] == self::WAVE_FORMAT_PCM && !in_array($fmt['BitsPerSample'], array(8, 16, 24)))
+            || ($fmt['AudioFormat'] == self::WAVE_FORMAT_IEEE_FLOAT && $fmt['BitsPerSample'] != 32)
+            || ($fmt['AudioFormat'] == self::WAVE_FORMAT_EXTENSIBLE && !in_array($fmt['BitsPerSample'], array(8, 16, 24, 32))))
+        {
+            throw new WavFormatException('Only 8, 16 and 24-bit PCM and 32-bit IEEE FLOAT (EXTENSIBLE) audio is supported.', 16);
+        }
+
+        $blockAlign = $fmt['NumChannels'] * $fmt['BitsPerSample'] / 8;
+        if ($blockAlign != $fmt['BlockAlign']) {
+            trigger_error('Invalid block align in "fmt " subchunk. Found ' . $fmt['BlockAlign'] . ', expected ' . $blockAlign . '.', E_USER_NOTICE);
+            $fmt['BlockAlign'] = $blockAlign;
+        }
+
+        $byteRate = $fmt['SampleRate'] * $blockAlign;
+        if ($byteRate != $fmt['ByteRate']) {
+            trigger_error('Invalid average byte rate in "fmt " subchunk. Found ' . $fmt['ByteRate'] . ', expected ' . $byteRate . '.', E_USER_NOTICE);
+            $fmt['ByteRate'] = $byteRate;
+        }
+
+        $this->_fmtChunkSize  = $fmt['SubchunkSize'];
+        $this->_audioFormat   = $fmt['AudioFormat'];
+        $this->_numChannels   = $fmt['NumChannels'];
+        $this->_sampleRate    = $fmt['SampleRate'];
+        $this->_byteRate      = $fmt['ByteRate'];
+        $this->_blockAlign    = $fmt['BlockAlign'];
+        $this->_bitsPerSample = $fmt['BitsPerSample'];
+
+
+        // read extended "fmt " subchunk data
+        $extendedFmt = '';
+        if ($fmt['SubchunkSize'] > 16) {
+            // possibly handle malformed subchunk without a padding byte
+            $extendedFmt = fread($this->_fp, $fmt['SubchunkSize'] - 16 + ($fmt['SubchunkSize'] & 1));  // also read padding byte
+            if (strlen($extendedFmt) < $fmt['SubchunkSize'] - 16) {
+                throw new WavFormatException('Not wav format. Header too short.', 1);
+            }
+        }
+
+
+        // check extended "fmt " for EXTENSIBLE Audio Format
+        if ($fmt['AudioFormat'] == self::WAVE_FORMAT_EXTENSIBLE) {
+            if (strlen($extendedFmt) < 24) {
+                throw new WavFormatException('Invalid EXTENSIBLE "fmt " subchunk size. Found ' . $fmt['SubchunkSize'] . ', expected at least 40.', 19);
+            }
+
+            $extensibleFmt = unpack('vSize/vValidBitsPerSample/VChannelMask/H32SubFormat', substr($extendedFmt, 0, 24));
+
+            if (   $extensibleFmt['SubFormat'] != self::WAVE_SUBFORMAT_PCM
+                && $extensibleFmt['SubFormat'] != self::WAVE_SUBFORMAT_IEEE_FLOAT)
+            {
+                throw new WavFormatException('Unsupported audio format. Only PCM or IEEE FLOAT (EXTENSIBLE) audio is supported.', 13);
+            }
+
+            if (   ($extensibleFmt['SubFormat'] == self::WAVE_SUBFORMAT_PCM && !in_array($fmt['BitsPerSample'], array(8, 16, 24)))
+                || ($extensibleFmt['SubFormat'] == self::WAVE_SUBFORMAT_IEEE_FLOAT && $fmt['BitsPerSample'] != 32))
+            {
+                throw new WavFormatException('Only 8, 16 and 24-bit PCM and 32-bit IEEE FLOAT (EXTENSIBLE) audio is supported.', 16);
+            }
+
+            if ($extensibleFmt['Size'] != 22) {
+                trigger_error('Invaid extension size in EXTENSIBLE "fmt " subchunk.', E_USER_NOTICE);
+                $extensibleFmt['Size'] = 22;
+            }
+
+            if ($extensibleFmt['ValidBitsPerSample'] != $fmt['BitsPerSample']) {
+                trigger_error('Invaid or unsupported valid bits per sample in EXTENSIBLE "fmt " subchunk.', E_USER_NOTICE);
+                $extensibleFmt['ValidBitsPerSample'] = $fmt['BitsPerSample'];
+            }
+
+            if ($extensibleFmt['ChannelMask'] != 0) {
+                // count number of set bits - Hamming weight
+                $c = (int)$extensibleFmt['ChannelMask'];
+                $n = 0;
+                while ($c > 0) {
+                    $n += $c & 1;
+                    $c >>= 1;
+                }
+                if ($n != $fmt['NumChannels'] || (((int)$extensibleFmt['ChannelMask'] | self::SPEAKER_ALL) != self::SPEAKER_ALL)) {
+                    trigger_error('Invalid channel mask in EXTENSIBLE "fmt " subchunk. The number of channels does not match the number of locations in the mask.', E_USER_NOTICE);
+                    $extensibleFmt['ChannelMask'] = 0;
+                }
+            }
+
+            $this->_fmtExtendedSize    = strlen($extendedFmt);
+            $this->_validBitsPerSample = $extensibleFmt['ValidBitsPerSample'];
+            $this->_channelMask        = $extensibleFmt['ChannelMask'];
+            $this->_audioSubFormat     = $extensibleFmt['SubFormat'];
+
+        } else {
+            $this->_fmtExtendedSize    = strlen($extendedFmt);
+            $this->_validBitsPerSample = $fmt['BitsPerSample'];
+            $this->_channelMask        = 0;
+            $this->_audioSubFormat     = null;
+        }
+
+
+        // read additional subchunks until "data" subchunk is found
+        $factSubchunk = array();
+        $dataSubchunk = array();
+
+        while (!feof($this->_fp)) {
+            $subchunkHeader = fread($this->_fp, 8);
+            if (strlen($subchunkHeader) < 8) {
+                throw new WavFormatException('Missing "data" subchunk.', 101);
+            }
+
+            $subchunk = unpack('NSubchunkID/VSubchunkSize', $subchunkHeader);
+
+            if ($subchunk['SubchunkID'] == 0x66616374) {        // "fact"
+                // possibly handle malformed subchunk without a padding byte
+                $subchunkData = fread($this->_fp, $subchunk['SubchunkSize'] + ($subchunk['SubchunkSize'] & 1));  // also read padding byte
+                if (strlen($subchunkData) < 4) {
+                    throw new WavFormatException('Invalid "fact" subchunk.', 102);
+                }
+
+                $factParams = unpack('VSampleLength', substr($subchunkData, 0, 4));
+                $factSubchunk = array_merge($subchunk, $factParams);
+
+            } elseif ($subchunk['SubchunkID'] == 0x64617461) {  // "data"
+                $dataSubchunk = $subchunk;
+
+                break;
+
+            } elseif ($subchunk['SubchunkID'] == 0x7761766C) {  // "wavl"
+                throw new WavFormatException('Wave List Chunk ("wavl" subchunk) is not supported.', 106);
+            } else {
+                // skip all other (unknown) subchunks
+                // possibly handle malformed subchunk without a padding byte
+                if ( $subchunk['SubchunkSize'] < 0
+                  || fseek($this->_fp, $subchunk['SubchunkSize'] + ($subchunk['SubchunkSize'] & 1), SEEK_CUR) !== 0) {  // also skip padding byte
+                    throw new WavFormatException('Invalid subchunk (0x' . dechex($subchunk['SubchunkID']) . ') encountered.', 103);
+                }
+            }
+        }
+
+        if (empty($dataSubchunk)) {
+            throw new WavFormatException('Missing "data" subchunk.', 101);
+        }
+
+        // check "data" subchunk
+        $dataOffset = ftell($this->_fp);
+        if ($this->getIgnoreChunkSizes()) {
+            $dataSubchunk['SubchunkSize'] = $actualSize - $dataOffset;
+        } elseif ($dataSubchunk['SubchunkSize'] < 0 || $actualSize - $dataOffset < $dataSubchunk['SubchunkSize']) {
+            trigger_error("Invalid \"data\" subchunk size (found {$dataSubchunk['SubchunkSize']}.", E_USER_NOTICE);
+            $dataSubchunk['SubchunkSize'] = $actualSize - $dataOffset;
+        }
+
+        $this->_dataOffset     = $dataOffset;
+        $this->_dataSize       = $dataSubchunk['SubchunkSize'];
+        $this->_dataSize_fp    = $dataSubchunk['SubchunkSize'];
+        $this->_dataSize_valid = false;
+        $this->_samples        = '';
+
+
+        // check "fact" subchunk
+        $numBlocks = (int)($dataSubchunk['SubchunkSize'] / $fmt['BlockAlign']);
+
+        if (empty($factSubchunk)) {  // construct fake "fact" subchunk
+            $factSubchunk = array('SubchunkSize' => 0, 'SampleLength' => $numBlocks);
+        }
+
+        if ($factSubchunk['SampleLength'] != $numBlocks) {
+            trigger_error('Invalid sample length in "fact" subchunk.', E_USER_NOTICE);
+            $factSubchunk['SampleLength'] = $numBlocks;
+        }
+
+        $this->_factChunkSize = $factSubchunk['SubchunkSize'];
+        $this->_numBlocks     = $factSubchunk['SampleLength'];
+
+
+        return $this;
+
+    }
+
+    /**
+     * Read the wav data from the file into the buffer.
+     *
+     * @param int $dataOffset  (Optional) The byte offset to skip before starting to read. Must be a multiple of BlockAlign.
+     * @param int $dataSize  (Optional) The size of the data to read in bytes. Must be a multiple of BlockAlign. Defaults to all data.
+     * @throws WavFileException
+     */
+    public function readWavData($dataOffset = 0, $dataSize = null)
+    {
+        // check preconditions
+        if (!is_resource($this->_fp)) {
+            throw new WavFileException('No wav file open. Use openWav() first.');
+        }
+
+        if ($dataOffset < 0 || $dataOffset % $this->getBlockAlign() > 0) {
+            throw new WavFileException('Invalid data offset. Has to be a multiple of BlockAlign.');
+        }
+
+        if (is_null($dataSize)) {
+            $dataSize = $this->_dataSize_fp - ($this->_dataSize_fp % $this->getBlockAlign());  // only read complete blocks
+        } elseif ($dataSize < 0 || $dataSize % $this->getBlockAlign() > 0) {
+            throw new WavFileException('Invalid data size to read. Has to be a multiple of BlockAlign.');
+        }
+
+
+        // skip offset
+        if ($dataOffset > 0 && fseek($this->_fp, $dataOffset, SEEK_CUR) !== 0) {
+            throw new WavFileException('Seeking to data offset failed.');
+        }
+
+        // read data
+        $this->_samples .= fread($this->_fp, $dataSize);  // allow appending
+        $this->setDataSize();  // implicit setSize(), setActualSize(), setNumBlocks()
+
+        // close file or memory stream
+        return $this->closeWav();
+    }
+
+
+    /*%******************************************************************************************%*/
+    // Sample manipulation methods
+
+    /**
+     * Return a single sample block from the file.
+     *
+     * @param int $blockNum  (Required) The sample block number. Zero based.
+     * @return string|null  The binary sample block (all channels). Returns null if the sample block number was out of range.
+     */
+    public function getSampleBlock($blockNum)
+    {
+        // check preconditions
+        if (!$this->_dataSize_valid) {
+            $this->setDataSize();  // implicit setSize(), setActualSize(), setNumBlocks()
+        }
+
+        $offset = $blockNum * $this->_blockAlign;
+        if ($offset + $this->_blockAlign > $this->_dataSize || $offset < 0) {
+            return null;
+        }
+
+
+        // read data
+        return substr($this->_samples, $offset, $this->_blockAlign);
+    }
+
+    /**
+     * Set a single sample block. <br />
+     * Allows to append a sample block.
+     *
+     * @param string $sampleBlock  (Required) The binary sample block (all channels).
+     * @param int $blockNum  (Required) The sample block number. Zero based.
+     * @throws WavFileException
+     */
+    public function setSampleBlock($sampleBlock, $blockNum)
+    {
+        // check preconditions
+        $blockAlign = $this->_blockAlign;
+        if (!isset($sampleBlock[$blockAlign - 1]) || isset($sampleBlock[$blockAlign])) {  // faster than: if (strlen($sampleBlock) != $blockAlign)
+            throw new WavFileException('Incorrect sample block size. Got ' . strlen($sampleBlock) . ', expected ' . $blockAlign . '.');
+        }
+
+        if (!$this->_dataSize_valid) {
+            $this->setDataSize();  // implicit setSize(), setActualSize(), setNumBlocks()
+        }
+
+        $numBlocks = (int)($this->_dataSize / $blockAlign);
+        $offset = $blockNum * $blockAlign;
+        if ($blockNum > $numBlocks || $blockNum < 0) {  // allow appending
+            throw new WavFileException('Sample block number is out of range.');
+        }
+
+
+        // replace or append data
+        if ($blockNum == $numBlocks) {
+            // append
+            $this->_samples    .= $sampleBlock;
+            $this->_dataSize   += $blockAlign;
+            $this->_chunkSize  += $blockAlign;
+            $this->_actualSize += $blockAlign;
+            $this->_numBlocks++;
+        } else {
+            // replace
+            for ($i = 0; $i < $blockAlign; ++$i) {
+                $this->_samples[$offset + $i] = $sampleBlock[$i];
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get a float sample value for a specific sample block and channel number.
+     *
+     * @param int $blockNum  (Required) The sample block number to fetch. Zero based.
+     * @param int $channelNum  (Required) The channel number within the sample block to fetch. First channel is 1.
+     * @return float|null  The float sample value. Returns null if the sample block number was out of range.
+     * @throws WavFileException
+     */
+    public function getSampleValue($blockNum, $channelNum)
+    {
+        // check preconditions
+        if ($channelNum < 1 || $channelNum > $this->_numChannels) {
+            throw new WavFileException('Channel number is out of range.');
+        }
+
+        if (!$this->_dataSize_valid) {
+            $this->setDataSize();  // implicit setSize(), setActualSize(), setNumBlocks()
+        }
+
+        $sampleBytes = $this->_bitsPerSample / 8;
+        $offset = $blockNum * $this->_blockAlign + ($channelNum - 1) * $sampleBytes;
+        if ($offset + $sampleBytes > $this->_dataSize || $offset < 0) {
+            return null;
+        }
+
+        // read binary value
+        $sampleBinary = substr($this->_samples, $offset, $sampleBytes);
+
+        // convert binary to value
+        switch ($this->_bitsPerSample) {
+            case 8:
+                // unsigned char
+                return (float)((ord($sampleBinary) - 0x80) / 0x80);
+
+            case 16:
+                // signed short, little endian
+                $data = unpack('v', $sampleBinary);
+                $sample = $data[1];
+                if ($sample >= 0x8000) {
+                    $sample -= 0x10000;
+                }
+                return (float)($sample / 0x8000);
+
+            case 24:
+                // 3 byte packed signed integer, little endian
+                $data = unpack('C3', $sampleBinary);
+                $sample = $data[1] | ($data[2] << 8) | ($data[3] << 16);
+                if ($sample >= 0x800000) {
+                    $sample -= 0x1000000;
+                }
+                return (float)($sample / 0x800000);
+
+            case 32:
+                // 32-bit float
+                $data = unpack('f', $sampleBinary);
+                return (float)$data[1];
+
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Sets a float sample value for a specific sample block number and channel. <br />
+     * Converts float values to appropriate integer values and clips properly. <br />
+     * Allows to append samples (in order).
+     *
+     * @param float $sampleFloat  (Required) The float sample value to set. Converts float values and clips if necessary.
+     * @param int $blockNum  (Required) The sample block number to set or append. Zero based.
+     * @param int $channelNum  (Required) The channel number within the sample block to set or append. First channel is 1.
+     * @throws WavFileException
+     */
+    public function setSampleValue($sampleFloat, $blockNum, $channelNum)
+    {
+        // check preconditions
+        if ($channelNum < 1 || $channelNum > $this->_numChannels) {
+            throw new WavFileException('Channel number is out of range.');
+        }
+
+        if (!$this->_dataSize_valid) {
+            $this->setDataSize();  // implicit setSize(), setActualSize(), setNumBlocks()
+        }
+
+        $dataSize = $this->_dataSize;
+        $bitsPerSample = $this->_bitsPerSample;
+        $sampleBytes = $bitsPerSample / 8;
+        $offset = $blockNum * $this->_blockAlign + ($channelNum - 1) * $sampleBytes;
+        if (($offset + $sampleBytes > $dataSize && $offset != $dataSize) || $offset < 0) { // allow appending
+            throw new WavFileException('Sample block or channel number is out of range.');
+        }
+
+
+        // convert to value, quantize and clip
+        if ($bitsPerSample == 32) {
+            $sample = $sampleFloat < -1.0 ? -1.0 : ($sampleFloat > 1.0 ? 1.0 : $sampleFloat);
+        } else {
+            $p = 1 << ($bitsPerSample - 1); // 2 to the power of _bitsPerSample divided by 2
+
+            // project and quantize (round) float to integer values
+            $sample = $sampleFloat < 0 ? (int)($sampleFloat * $p - 0.5) : (int)($sampleFloat * $p + 0.5);
+
+            // clip if necessary to [-$p, $p - 1]
+            if ($sample < -$p) {
+                $sample = -$p;
+            } elseif ($sample > $p - 1) {
+                $sample = $p - 1;
+            }
+        }
+
+        // convert to binary
+        switch ($bitsPerSample) {
+            case 8:
+                // unsigned char
+                $sampleBinary = chr($sample + 0x80);
+                break;
+
+            case 16:
+                // signed short, little endian
+                if ($sample < 0) {
+                    $sample += 0x10000;
+                }
+                $sampleBinary = pack('v', $sample);
+                break;
+
+            case 24:
+                // 3 byte packed signed integer, little endian
+                if ($sample < 0) {
+                    $sample += 0x1000000;
+                }
+                $sampleBinary = pack('C3', $sample & 0xff, ($sample >>  8) & 0xff, ($sample >> 16) & 0xff);
+                break;
+
+            case 32:
+                // 32-bit float
+                $sampleBinary = pack('f', $sample);
+                break;
+
+            default:
+                $sampleBinary = null;
+                $sampleBytes = 0;
+                break;
+        }
+
+        // replace or append data
+        if ($offset == $dataSize) {
+            // append
+            $this->_samples    .= $sampleBinary;
+            $this->_dataSize   += $sampleBytes;
+            $this->_chunkSize  += $sampleBytes;
+            $this->_actualSize += $sampleBytes;
+            $this->_numBlocks = (int)($this->_dataSize / $this->_blockAlign);
+        } else {
+            // replace
+            for ($i = 0; $i < $sampleBytes; ++$i) {
+                $this->_samples{$offset + $i} = $sampleBinary{$i};
+            }
+        }
+
+        return $this;
+    }
+
+
+    /*%******************************************************************************************%*/
+    // Audio processing methods
+
+    /**
+     * Run samples through audio processing filters.
+     *
+     * <code>
+     * $wav->filter(
+     *      array(
+     *          WavFile::FILTER_MIX => array(          // Filter for mixing 2 WavFile instances.
+     *              'wav' => $wav2,                    // (Required) The WavFile to mix into this WhavFile. If no optional arguments are given, can be passed without the array.
+     *              'loop' => true,                    // (Optional) Loop the selected portion (with warping to the beginning at the end).
+     *              'blockOffset' => 0,                // (Optional) Block number to start mixing from.
+     *              'numBlocks' => null                // (Optional) Number of blocks to mix in or to select for looping. Defaults to the end or all data for looping.
+     *          ),
+     *          WavFile::FILTER_NORMALIZE => 0.6,      // (Required) Normalization of (mixed) audio samples - see threshold parameter for normalizeSample().
+     *          WavFile::FILTER_DEGRADE => 0.9         // (Required) Introduce random noise. The quality relative to the amplitude. 1 = no noise, 0 = max. noise.
+     *          WavFile::FILTER_VOLUME => 1.0          // (Required) Amplify or attenuate the audio signal.  Beware of clipping when amplifying.  Values range from >= 0 - <= 2.  1 = no change in volume; 0.5 = 50% reduction of volume; 1.5 = 150% increase in volume.
+     *      ),
+     *      0,                                         // (Optional) The block number of this WavFile to start with.
+     *      null                                       // (Optional) The number of blocks to process.
+     *  );
+     *  </code>
+     *
+     * @param array $filters  (Required) An array of 1 or more audio processing filters.
+     * @param int $blockOffset  (Optional) The block number to start precessing from.
+     * @param int $numBlocks  (Optional) The maximum  number of blocks to process.
+     * @throws WavFileException
+     */
+    public function filter($filters, $blockOffset = 0, $numBlocks = null)
+    {
+        // check preconditions
+        $totalBlocks = $this->getNumBlocks();
+        $numChannels = $this->getNumChannels();
+        if (is_null($numBlocks)) $numBlocks = $totalBlocks - $blockOffset;
+
+        if (!is_array($filters) || empty($filters) || $blockOffset < 0 || $blockOffset > $totalBlocks || $numBlocks <= 0) {
+            // nothing to do
+            return $this;
+        }
+
+        // check filtes
+        $filter_mix = false;
+        if (array_key_exists(self::FILTER_MIX, $filters)) {
+            if (!is_array($filters[self::FILTER_MIX])) {
+                // assume the 'wav' parameter
+                $filters[self::FILTER_MIX] = array('wav' => $filters[self::FILTER_MIX]);
+            }
+
+            $mix_wav = @$filters[self::FILTER_MIX]['wav'];
+            if (!($mix_wav instanceof WavFile)) {
+                throw new WavFileException("WavFile to mix is missing or invalid.");
+            } elseif ($mix_wav->getSampleRate() != $this->getSampleRate()) {
+                throw new WavFileException("Sample rate of WavFile to mix does not match.");
+            } else if ($mix_wav->getNumChannels() != $this->getNumChannels()) {
+                throw new WavFileException("Number of channels of WavFile to mix does not match.");
+            }
+
+            $mix_loop = @$filters[self::FILTER_MIX]['loop'];
+            if (is_null($mix_loop)) $mix_loop = false;
+
+            $mix_blockOffset = @$filters[self::FILTER_MIX]['blockOffset'];
+            if (is_null($mix_blockOffset)) $mix_blockOffset = 0;
+
+            $mix_totalBlocks = $mix_wav->getNumBlocks();
+            $mix_numBlocks = @$filters[self::FILTER_MIX]['numBlocks'];
+            if (is_null($mix_numBlocks)) $mix_numBlocks = $mix_loop ? $mix_totalBlocks : $mix_totalBlocks - $mix_blockOffset;
+            $mix_maxBlock = min($mix_blockOffset + $mix_numBlocks, $mix_totalBlocks);
+
+            $filter_mix = true;
+        }
+
+        $filter_normalize = false;
+        if (array_key_exists(self::FILTER_NORMALIZE, $filters)) {
+            $normalize_threshold = @$filters[self::FILTER_NORMALIZE];
+
+            if (!is_null($normalize_threshold) && abs($normalize_threshold) != 1) $filter_normalize = true;
+        }
+
+        $filter_degrade = false;
+        if (array_key_exists(self::FILTER_DEGRADE, $filters)) {
+            $degrade_quality = @$filters[self::FILTER_DEGRADE];
+            if (is_null($degrade_quality)) $degrade_quality = 1;
+
+            if ($degrade_quality >= 0 && $degrade_quality < 1) $filter_degrade = true;
+        }
+
+        $filter_vol = false;
+        if (array_key_exists(self::FILTER_VOLUME, $filters)) {
+            $volume_amount = @$filters[self::FILTER_VOLUME];
+            if (is_null($volume_amount)) $volume_amount = 1;
+
+            if ($volume_amount >= 0 && $volume_amount <= 2 && $volume_amount != 1.0) {
+                $filter_vol = true;
+            }
+        }
+
+
+        // loop through all sample blocks
+        for ($block = 0; $block < $numBlocks; ++$block) {
+            // loop through all channels
+            for ($channel = 1; $channel <= $numChannels; ++$channel) {
+                // read current sample
+                $currentBlock = $blockOffset + $block;
+                $sampleFloat = $this->getSampleValue($currentBlock, $channel);
+
+
+                /************* MIX FILTER ***********************/
+                if ($filter_mix) {
+                    if ($mix_loop) {
+                        $mixBlock = ($mix_blockOffset + ($block % $mix_numBlocks)) % $mix_totalBlocks;
+                    } else {
+                        $mixBlock = $mix_blockOffset + $block;
+                    }
+
+                    if ($mixBlock < $mix_maxBlock) {
+                        $sampleFloat += $mix_wav->getSampleValue($mixBlock, $channel);
+                    }
+                }
+
+                /************* NORMALIZE FILTER *******************/
+                if ($filter_normalize) {
+                    $sampleFloat = $this->normalizeSample($sampleFloat, $normalize_threshold);
+                }
+
+                /************* DEGRADE FILTER *******************/
+                if ($filter_degrade) {
+                    $sampleFloat += rand(1000000 * ($degrade_quality - 1), 1000000 * (1 - $degrade_quality)) / 1000000;
+                }
+
+                /************* VOLUME FILTER *******************/
+                if ($filter_vol) {
+                    $sampleFloat *=  $volume_amount;
+                }
+
+                // write current sample
+                $this->setSampleValue($sampleFloat, $currentBlock, $channel);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Append a wav file to the current wav. <br />
+     * The wav files must have the same sample rate, number of bits per sample, and number of channels.
+     *
+     * @param WavFile $wav  (Required) The wav file to append.
+     * @throws WavFileException
+     */
+    public function appendWav(WavFile $wav) {
+        // basic checks
+        if ($wav->getSampleRate() != $this->getSampleRate()) {
+            throw new WavFileException("Sample rate for wav files do not match.");
+        } else if ($wav->getBitsPerSample() != $this->getBitsPerSample()) {
+            throw new WavFileException("Bits per sample for wav files do not match.");
+        } else if ($wav->getNumChannels() != $this->getNumChannels()) {
+            throw new WavFileException("Number of channels for wav files do not match.");
+        }
+
+        $this->_samples .= $wav->_samples;
+        $this->setDataSize();  // implicit setSize(), setActualSize(), setNumBlocks()
+
+        return $this;
+    }
+
+    /**
+     * Mix 2 wav files together. <br />
+     * Both wavs must have the same sample rate and same number of channels.
+     *
+     * @param WavFile $wav  (Required) The WavFile to mix.
+     * @param float $normalizeThreshold  (Optional) See normalizeSample for an explanation.
+     * @throws WavFileException
+     */
+    public function mergeWav(WavFile $wav, $normalizeThreshold = null) {
+        return $this->filter(array(
+            WavFile::FILTER_MIX       => $wav,
+            WavFile::FILTER_NORMALIZE => $normalizeThreshold
+        ));
+    }
+
+    /**
+     * Add silence to the wav file.
+     *
+     * @param float $duration  (Optional) How many seconds of silence. If negative, add to the beginning of the file. Defaults to 1s.
+     */
+    public function insertSilence($duration = 1.0)
+    {
+        $numSamples  = (int)($this->getSampleRate() * abs($duration));
+        $numChannels = $this->getNumChannels();
+
+        $data = str_repeat(self::packSample($this->getZeroAmplitude(), $this->getBitsPerSample()), $numSamples * $numChannels);
+        if ($duration >= 0) {
+            $this->_samples .= $data;
+        } else {
+            $this->_samples = $data . $this->_samples;
+        }
+
+        $this->setDataSize();  // implicit setSize(), setActualSize(), setNumBlocks()
+
+        return $this;
+    }
+
+    /**
+     * Degrade the quality of the wav file by introducing random noise.
+     *
+     * @param float quality  (Optional) The quality relative to the amplitude. 1 = no noise, 0 = max. noise.
+     */
+    public function degrade($quality = 1.0)
+    {
+        return $this->filter(array(
+            self::FILTER_DEGRADE => $quality
+        ));
+    }
+
+    /**
+     * Generate noise at the end of the wav for the specified duration and volume.
+     *
+     * @param float $duration  (Optional) Number of seconds of noise to generate.
+     * @param float $percent  (Optional) The percentage of the maximum amplitude to use. 100 = full amplitude.
+     */
+    public function generateNoise($duration = 1.0, $percent = 100)
+    {
+        $numChannels = $this->getNumChannels();
+        $numSamples  = $this->getSampleRate() * $duration;
+        $minAmp      = $this->getMinAmplitude();
+        $maxAmp      = $this->getMaxAmplitude();
+        $bitDepth    = $this->getBitsPerSample();
+
+        for ($s = 0; $s < $numSamples; ++$s) {
+            if ($bitDepth == 32) {
+                $val = rand(-$percent * 10000, $percent * 10000) / 1000000;
+            } else {
+                $val = rand($minAmp, $maxAmp);
+                $val = (int)($val * $percent / 100);
+            }
+
+            $this->_samples .= str_repeat(self::packSample($val, $bitDepth), $numChannels);
+        }
+
+        $this->setDataSize();  // implicit setSize(), setActualSize(), setNumBlocks()
+
+        return $this;
+    }
+
+    /**
+     * Convert sample data to different bits per sample.
+     *
+     * @param int $bitsPerSample  (Required) The new number of bits per sample;
+     * @throws WavFileException
+     */
+    public function convertBitsPerSample($bitsPerSample) {
+        if ($this->getBitsPerSample() == $bitsPerSample) {
+            return $this;
+        }
+
+        $tempWav = new WavFile($this->getNumChannels(), $this->getSampleRate(), $bitsPerSample);
+        $tempWav->filter(
+            array(self::FILTER_MIX => $this),
+            0,
+            $this->getNumBlocks()
+        );
+
+        $this->setSamples()                       // implicit setDataSize(), setSize(), setActualSize(), setNumBlocks()
+             ->setBitsPerSample($bitsPerSample);  // implicit setValidBitsPerSample(), setAudioFormat(), setAudioSubFormat(), setFmtChunkSize(), setFactChunkSize(), setSize(), setActualSize(), setDataOffset(), setByteRate(), setBlockAlign(), setNumBlocks()
+        $this->_samples = $tempWav->_samples;
+        $this->setDataSize();                     // implicit setSize(), setActualSize(), setNumBlocks()
+
+        return $this;
+    }
+
+
+    /*%******************************************************************************************%*/
+    // Miscellaneous methods
+
+    /**
+     * Output information about the wav object.
+     */
+    public function displayInfo()
+    {
+        $s = "File Size: %u\n"
+            ."Chunk Size: %u\n"
+            ."fmt Subchunk Size: %u\n"
+            ."Extended fmt Size: %u\n"
+            ."fact Subchunk Size: %u\n"
+            ."Data Offset: %u\n"
+            ."Data Size: %u\n"
+            ."Audio Format: %s\n"
+            ."Audio SubFormat: %s\n"
+            ."Channels: %u\n"
+            ."Channel Mask: 0x%s\n"
+            ."Sample Rate: %u\n"
+            ."Bits Per Sample: %u\n"
+            ."Valid Bits Per Sample: %u\n"
+            ."Sample Block Size: %u\n"
+            ."Number of Sample Blocks: %u\n"
+            ."Byte Rate: %uBps\n";
+
+        $s = sprintf($s, $this->getActualSize(),
+                         $this->getChunkSize(),
+                         $this->getFmtChunkSize(),
+                         $this->getFmtExtendedSize(),
+                         $this->getFactChunkSize(),
+                         $this->getDataOffset(),
+                         $this->getDataSize(),
+                         $this->getAudioFormat() == self::WAVE_FORMAT_PCM ? 'PCM' : ($this->getAudioFormat() == self::WAVE_FORMAT_IEEE_FLOAT ? 'IEEE FLOAT' : 'EXTENSIBLE'),
+                         $this->getAudioSubFormat() == self::WAVE_SUBFORMAT_PCM ? 'PCM' : 'IEEE FLOAT',
+                         $this->getNumChannels(),
+                         dechex($this->getChannelMask()),
+                         $this->getSampleRate(),
+                         $this->getBitsPerSample(),
+                         $this->getValidBitsPerSample(),
+                         $this->getBlockAlign(),
+                         $this->getNumBlocks(),
+                         $this->getByteRate());
+
+        if (php_sapi_name() == 'cli') {
+            return $s;
+        } else {
+            return nl2br($s);
+        }
+    }
+}
+
+
+/*%******************************************************************************************%*/
+// Exceptions
+
+/**
+ * WavFileException indicates an illegal state or argument in this class.
+ */
+class WavFileException extends Exception {}
+
+/**
+ * WavFormatException indicates a malformed or unsupported wav file header.
+ */
+class WavFormatException extends WavFileException {}
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/.htaccess b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/.htaccess
new file mode 100644
index 0000000000000000000000000000000000000000..4fdb24a5e5045a0670e571f76fa5f55523870b80
--- /dev/null
+++ b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/.htaccess
@@ -0,0 +1,11 @@
+# Deny access to this folder
+
+# Apache 2.4
+<IfModule mod_authz_core.c>
+	Require all denied
+</IfModule>
+
+# Apache 2.2
+<IfModule !mod_authz_core.c>
+	Deny from all
+</IfModule>
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/0.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/0.wav
new file mode 100644
index 0000000000000000000000000000000000000000..8a4bd6f2cc5dd81832fc94436b8ec2ab1ec23eb8
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/0.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/1.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/1.wav
new file mode 100644
index 0000000000000000000000000000000000000000..fa15b457436ff6dd94866c5214a62ccf39d776a3
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/1.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/10.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/10.wav
new file mode 100644
index 0000000000000000000000000000000000000000..c6d7d73fe7aabdb2be697ae72bb98bfb2d9bd347
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/10.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/11.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/11.wav
new file mode 100644
index 0000000000000000000000000000000000000000..ccc3494de26250ca3064c02ae2bbadca4208c2a9
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/11.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/12.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/12.wav
new file mode 100644
index 0000000000000000000000000000000000000000..bf8f8ef23df52c4ac2d9679176e76ff68d7f2c4a
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/12.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/13.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/13.wav
new file mode 100644
index 0000000000000000000000000000000000000000..0166cce75ae445c3620b088a7e5d48cd3e3ebe43
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/13.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/14.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/14.wav
new file mode 100644
index 0000000000000000000000000000000000000000..01317a297e22a01979ed2ebc612cf70af252f21c
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/14.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/15.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/15.wav
new file mode 100644
index 0000000000000000000000000000000000000000..c0d8e3fe2efeb7f90c428a32cadf1f5cf392a270
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/15.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/16.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/16.wav
new file mode 100644
index 0000000000000000000000000000000000000000..c31039458c186845625b3e01bf036aa532f81908
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/16.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/17.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/17.wav
new file mode 100644
index 0000000000000000000000000000000000000000..1577a40db2772a7d17d40da3dadacbab8f9cd3a5
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/17.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/18.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/18.wav
new file mode 100644
index 0000000000000000000000000000000000000000..3caa0f22e568d61816c7a041f4fc19c7eb943d0d
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/18.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/19.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/19.wav
new file mode 100644
index 0000000000000000000000000000000000000000..54f824355054224149d8fc62e59bb0541eac3753
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/19.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/2.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/2.wav
new file mode 100644
index 0000000000000000000000000000000000000000..2e61d240df1a6617044b8c8da0a51a6c4218775c
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/2.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/20.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/20.wav
new file mode 100644
index 0000000000000000000000000000000000000000..abeebe3be46a6a2eac7553f771105b7e1a4a79a3
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/20.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/3.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/3.wav
new file mode 100644
index 0000000000000000000000000000000000000000..03c61574c6adfb2588bcd9bd258900e81c29a292
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/3.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/4.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/4.wav
new file mode 100644
index 0000000000000000000000000000000000000000..61834ca7bd146dc80f8bb4e6fa1fea095c245aee
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/4.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/5.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/5.wav
new file mode 100644
index 0000000000000000000000000000000000000000..6836a6e8271d4d9ac625bdca2087827333d5d1ac
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/5.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/6.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/6.wav
new file mode 100644
index 0000000000000000000000000000000000000000..01c85f08fc07e52faaa37764bad1f332ca9a57a7
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/6.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/7.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/7.wav
new file mode 100644
index 0000000000000000000000000000000000000000..91b96fad7f1fa87940a2740a101faa69ef678e64
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/7.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/8.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/8.wav
new file mode 100644
index 0000000000000000000000000000000000000000..16cf893848d9a2f11e52366b09bfe880cfbe58b4
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/8.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/9.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/9.wav
new file mode 100644
index 0000000000000000000000000000000000000000..e81e2a47412b7cbf94fbe82cef70f609eae0dec3
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/9.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/A.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/A.wav
new file mode 100644
index 0000000000000000000000000000000000000000..047d9069183539bf74e27b65f7472a27d311459b
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/A.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/B.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/B.wav
new file mode 100644
index 0000000000000000000000000000000000000000..34c1f194b991a26decddce9c8d837639bbd5a3ab
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/B.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/C.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/C.wav
new file mode 100644
index 0000000000000000000000000000000000000000..933f428ab575ae792131fd16e5bb7b77e7a89276
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/C.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/D.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/D.wav
new file mode 100644
index 0000000000000000000000000000000000000000..1879e0b2a1b118666857bb41f80af4988cae4f44
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/D.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/E.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/E.wav
new file mode 100644
index 0000000000000000000000000000000000000000..7e483f1a79b13b709587d2d77b69212688d31be7
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/E.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/F.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/F.wav
new file mode 100644
index 0000000000000000000000000000000000000000..ab3f6b93cf16b6cec9bfb439777642df3f845ef9
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/F.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/G.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/G.wav
new file mode 100644
index 0000000000000000000000000000000000000000..517b38b8c6b81e4f71b7480b4098eb033446650d
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/G.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/H.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/H.wav
new file mode 100644
index 0000000000000000000000000000000000000000..5f4e7619e3a14a5e90cb6f17267d37a0f7cf55f1
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/H.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/I.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/I.wav
new file mode 100644
index 0000000000000000000000000000000000000000..7450519ef5ae7a049c18789c6fa99796f6dc24ab
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/I.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/J.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/J.wav
new file mode 100644
index 0000000000000000000000000000000000000000..7870a5cb0a79ed0ae347223e0082c05c71966d56
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/J.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/K.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/K.wav
new file mode 100644
index 0000000000000000000000000000000000000000..38fbdfeb4bb23488d37fbdc8ff12d44ed65aee72
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/K.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/L.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/L.wav
new file mode 100644
index 0000000000000000000000000000000000000000..da7e679d2b1dde60fc032a9bcb5bb35245d2a737
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/L.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/M.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/M.wav
new file mode 100644
index 0000000000000000000000000000000000000000..c61aab35fe2b6ccb56e7e1299340011d57da4c21
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/M.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/MINUS.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/MINUS.wav
new file mode 100644
index 0000000000000000000000000000000000000000..cb2c0862b263ba49da54cc73c46316573c9e3400
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/MINUS.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/N.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/N.wav
new file mode 100644
index 0000000000000000000000000000000000000000..280ef231c115d150db8b92939ecfd0e6b522c174
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/N.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/O.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/O.wav
new file mode 100644
index 0000000000000000000000000000000000000000..98068d53f1a6af69c08aea2ab70d20987a4e59ce
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/O.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/P.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/P.wav
new file mode 100644
index 0000000000000000000000000000000000000000..546aa73e8d3e96b43c319c3ef8fc00ff33ded971
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/P.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/PLUS.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/PLUS.wav
new file mode 100644
index 0000000000000000000000000000000000000000..f340b6ce7f1568339ead04b07b5a360d3e09896d
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/PLUS.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/Q.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/Q.wav
new file mode 100644
index 0000000000000000000000000000000000000000..57cafe506c21443a510039922e1a24fe78958d08
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/Q.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/R.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/R.wav
new file mode 100644
index 0000000000000000000000000000000000000000..e16d66b493cd985643e43ea6de7897337463b194
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/R.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/S.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/S.wav
new file mode 100644
index 0000000000000000000000000000000000000000..1b4fb82b73bc1e0c03ea25ba2fe87966af91a68e
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/S.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/T.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/T.wav
new file mode 100644
index 0000000000000000000000000000000000000000..347be1a03aae9e81e1032b7522f54fca3eab1419
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/T.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/TIMES.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/TIMES.wav
new file mode 100644
index 0000000000000000000000000000000000000000..85692b8b4a3337bf10beaced51a8b5ad1f22c482
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/TIMES.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/U.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/U.wav
new file mode 100644
index 0000000000000000000000000000000000000000..2ffc415d937a2ce8a825cd8b365ec11d77093d2e
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/U.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/V.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/V.wav
new file mode 100644
index 0000000000000000000000000000000000000000..a9748c70c3f661d79365c7aef20f20f716fc7e72
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/V.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/W.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/W.wav
new file mode 100644
index 0000000000000000000000000000000000000000..aaa9f79ff4b229514372f1aae45e5f67f1aa53a9
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/W.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/X.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/X.wav
new file mode 100644
index 0000000000000000000000000000000000000000..39253b0493d93354762b08e13f2a2a23b8223add
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/X.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/Y.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/Y.wav
new file mode 100644
index 0000000000000000000000000000000000000000..be62a8fb1054be9706906b18dc3cb048c5803e42
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/Y.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/Z.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/Z.wav
new file mode 100644
index 0000000000000000000000000000000000000000..8714c831f4167945c10246ae263296b57fc19a6a
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/Z.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/error.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/error.wav
new file mode 100644
index 0000000000000000000000000000000000000000..35209ab0201142bfecdbf24ce59c359f4d99ea24
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/en/error.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/index.php b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/noise/check-point-1.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/noise/check-point-1.wav
new file mode 100644
index 0000000000000000000000000000000000000000..9533b127c522a2af4619c865465704f4d91d6709
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/noise/check-point-1.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/noise/crowd-talking-1.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/noise/crowd-talking-1.wav
new file mode 100644
index 0000000000000000000000000000000000000000..7f451df452100c6665dd10eaa7fb471c915123f8
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/noise/crowd-talking-1.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/noise/crowd-talking-6.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/noise/crowd-talking-6.wav
new file mode 100644
index 0000000000000000000000000000000000000000..fd9a10ddbb298c4d8e5ba93ef6d4b58bd62728fb
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/noise/crowd-talking-6.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/noise/crowd-talking-7.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/noise/crowd-talking-7.wav
new file mode 100644
index 0000000000000000000000000000000000000000..986f6ae4a5384f6490e850900cc628722fd468b0
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/noise/crowd-talking-7.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/noise/kids-playing-1.wav b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/noise/kids-playing-1.wav
new file mode 100644
index 0000000000000000000000000000000000000000..cb9d17ba5ab8ddf9d62028474fa924be58f8e389
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/audio/noise/kids-playing-1.wav differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/captcha.html b/wp-content/plugins/wp2pgpmail/phpcaptcha/captcha.html
new file mode 100644
index 0000000000000000000000000000000000000000..0343fb0f36a5d3043920cd4624de4129740ea3de
--- /dev/null
+++ b/wp-content/plugins/wp2pgpmail/phpcaptcha/captcha.html
@@ -0,0 +1,136 @@
+<!DOCTYPE html>
+<html>
+<!--
+    The following is a complete HTML snippet that can be used on your form for
+    displaying the captcha image.
+
+    This HTML snippet generates a CAPTCHA image, HTML5 audio controls, a button
+    to refresh the image and audio, as well as an input field for accepting the
+    captcha code input.
+
+    Modify to fit your needs and the website appearance.  The audio section can
+    also be removed if desired.
+
+    The same code is given twice, first as a whole and then repeated with
+    inline comments describing the individual elements.
+
+    Most of this code can be generated automatically with many customization
+    options by using the function Securimage::getCaptchaHtml() instead.
+-->
+
+<head>
+  <meta charset="utf-8">
+  <title>Sample CAPTCHA HTML</title>
+  <link rel="stylesheet" href="securimage.css" media="screen">
+</head>
+<body>
+
+<h4>Note: Running this on a PHP enabled server will likely work, but you should use example_form.php for testing instead.</h4>
+
+<div>
+  <img style="float: left; padding-right: 5px" id="captcha_image" src="securimage_show.php?<?php echo md5(uniqid(time)) ?>" alt="CAPTCHA Image">
+  <div id="captcha_image_audio_div">
+    <audio id="captcha_image_audio" preload="none" style="display: none">
+      <!-- <source id="captcha_image_source_mp3" src="securimage_play.php?id=1234&amp;format=mp3" type="audio/mpeg"> -->
+      <source id="captcha_image_source_wav" src="securimage_play.php?id=1234" type="audio/wav">
+      <object type="application/x-shockwave-flash" data="securimage_play.swf?bgcol=%23ffffff&amp;icon_file=images%2Faudio_icon.png&amp;audio_file=securimage_play.php" height="32" width="32">
+        <param name="movie" value="securimage_play.swf?bgcol=%23ffffff&amp;icon_file=images%2Faudio_icon.png&amp;audio_file=securimage_play.php" />
+      </object>
+    </audio>
+  </div>
+  <div id="captcha_image_audio_controls">
+    <a tabindex="-1" class="captcha_play_button" href="securimage_play.php?id=1234 ?>" onclick="return false">
+      <img class="captcha_play_image" height="32" width="32" src="images/audio_icon.png" alt="Play CAPTCHA Audio" style="border: 0px">
+      <img class="captcha_loading_image rotating" height="32" width="32" src="images/loading.png" alt="Loading audio" style="display: none">
+    </a>
+    <noscript>Enable Javascript for audio controls</noscript>
+  </div>
+
+  <a tabindex="-1" style="border: 0" href="#" title="Refresh Image" onclick="document.getElementById('captcha_image').src = 'securimage_show.php?' + Math.random(); captcha_image_audioObj.refresh(); this.blur(); return false">
+    <img height="32" width="32" src="images/refresh.png" alt="Refresh Image" onclick="this.blur()" style="border: 0px; vertical-align: bottom" />
+  </a>
+  <br>
+
+  <script type="text/javascript" src="securimage.js"></script>
+  <script type="text/javascript">
+    captcha_image_audioObj = new SecurimageAudio({ audioElement: 'captcha_image_audio', controlsElement: 'captcha_image_audio_controls' });
+  </script>
+  <div style="clear: both"></div>
+
+  <label for="captcha_code">Type the text:</label>
+  <input type="text" name="captcha_code" id="captcha_code">
+</div>
+
+
+<!-- and once again with comments for clarity -->
+<!-- all IDs have been changed so this *should* work on a PHP enabled server -->
+<!-- example_form.php should really be used for testing though -->
+<br><br>
+
+
+<div>
+  <!-- captcha image element; the <?php echo md5(...) ?> code throughout is to prevent caching issues -->
+  <img style="float: left; padding-right: 5px" id="captcha_image2" src="securimage_show.php?<?php echo md5(uniqid(time)) ?>" alt="CAPTCHA Image">
+  
+  <!-- invisible div containing captcha audio tag, and optional flash fallback code -->
+  <div id="captcha_image_audio_div2">
+    <!-- the audio tag -->
+    <audio id="captcha_image2_audio" preload="none" style="display: none">
+      <!-- mp3 source tag - uncomment if you have LAME installed -->
+      <!-- <source id="captcha_image2_source_mp3" src="securimage_play.php?id=<?php echo uniqid() ?>&amp;format=mp3" type="audio/mpeg"> -->
+
+      <!-- wav source tag -->
+      <source id="captcha_image2_source_wav" src="securimage_play.php?id=<?php echo uniqid() ?>" type="audio/wav">
+
+      <!-- flash player fallback - delete if you don't want flash fallback -->
+      <object type="application/x-shockwave-flash" data="securimage_play.swf?bgcol=%23ffffff&amp;icon_file=images%2Faudio_icon.png&amp;audio_file=securimage_play.php" height="32" width="32">
+        <param name="movie" value="securimage_play.swf?bgcol=%23ffffff&amp;icon_file=images%2Faudio_icon.png&amp;audio_file=securimage_play.php" />
+      </object>
+    </audio>
+  </div>
+  <!-- div containing the HTML audio controls -->
+  <div id="captcha_image2_audio_controls">
+    <!-- play button and loading image that gets toggled when audio is loading -->
+    <a tabindex="-1" class="captcha_play_button" href="securimage_play.php?id=<?php echo uniqid() ?>" onclick="return false">
+      <img class="captcha_play_image" height="32" width="32" src="images/audio_icon.png" alt="Play CAPTCHA Audio" style="border: 0px">
+      <img class="captcha_loading_image rotating" height="32" width="32" src="images/loading.png" alt="Loading audio" style="display: none">
+    </a>
+    <noscript>Enable Javascript for audio controls</noscript>
+  </div>
+
+  <!-- link to refresh the captcha image and audios -->
+  <a tabindex="-1" style="border: 0" href="#" title="Refresh Image" onclick="document.getElementById('captcha_image2').src = 'securimage_show.php?' + Math.random(); captcha_image2_audioObj.refresh(); this.blur(); return false">
+    <img height="32" width="32" src="images/refresh.png" alt="Refresh Image" onclick="this.blur()" style="border: 0px; vertical-align: bottom" />
+  </a>
+  <br>
+
+  <!-- javascript code for refreshing and playing captcha audio -->
+  <script type="text/javascript" src="securimage.js"></script>
+  <script type="text/javascript">
+    captcha_image2_audioObj = new SecurimageAudio({ audioElement: 'captcha_image2_audio', controlsElement: 'captcha_image2_audio_controls' });
+    /*
+    The SecurimageAudio object accepts a single object paramter with two properties:
+      audioElement: the ID of the div containing the <audio> and <source> HTML tags
+      controlsElement: the ID of the div containing the audio playback controls
+
+    Note: securimage.js automatically finds the play and loading images by looking for images with
+          class names of captcha_play_image and captcha_loading_image, respectively.
+
+          The image inside of the controls div including the class name "captcha_play_image" will have
+          click events registered to start/stop audio playback
+
+          The image inside of the controls div including the class name "captcha_loading_image will be
+          displayed with the audio is loading and hidden again once playback starts.
+
+          Clicking the play button starts and stops audio playback.
+    */
+  </script>
+  <div style="clear: both"></div>
+
+  <!-- captcha input -->
+  <label for="captcha_code2">Type the text:</label>
+  <input type="text" name="captcha_code2" id="captcha_code2">
+</div>
+
+</body>
+</html>
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/config.inc.php.SAMPLE b/wp-content/plugins/wp2pgpmail/phpcaptcha/config.inc.php.SAMPLE
new file mode 100644
index 0000000000000000000000000000000000000000..43dcd54182d4cc06484ccb7b1835a8259829f04e
--- /dev/null
+++ b/wp-content/plugins/wp2pgpmail/phpcaptcha/config.inc.php.SAMPLE
@@ -0,0 +1,87 @@
+<?php
+
+/**
+  Securimage sample config file (rename to config.inc.php to activate)
+
+  Place your custom configuration in this file to make settings global so they
+  are applied to the captcha image, audio playback, and validation.
+
+  Using this file is optional but makes settings managing settings easier,
+  especially when upgrading to a new version.
+
+  When a new Securimage object is created, if config.inc.php is found in the
+  Securimage directory, these settings will be applied *before* any settings
+  passed to the constructor (so options passed in will override these).
+
+  This file is especially useful if you use a custom database or session
+  configuration and is easier than modifying securimage.php directly.
+  Any class property from securimage.php can be used here.
+*/
+
+return array(
+    /**** CAPTCHA Appearance Options ****/
+
+    'image_width'      => 275,       // width of captcha image in pixels
+    'image_height'     => 100,       // height of captcha image in pixels
+    'code_length'      => 6,         // # of characters for captcha code
+    'image_bg_color'   => '#ffffff', // hex color for image background
+    'text_color'       => '#707070', // hex color for captcha text
+    'line_color'       => '#707070', // hex color for lines over text
+    'noise_color'      => '#707070', // color of random noise to draw under text
+    'num_lines'        => 3,         // # of lines to draw over text
+    'noise_level'      => 4,         // how much random noise to add (0-10)
+    'perturbation'     => 0.7,       // distoration level
+
+    'use_random_spaces'   => true,
+    'use_random_baseline' => true,
+    'use_text_angles'     => true,
+    'use_random_boxes' => false,
+
+    'wordlist_file'    => 'words/words.txt', // text file for word captcha
+    'use_wordlist'     => false,             // true to use word list
+    'wordlist_file_encoding' => null,        // character encoding of word file if other than ASCII (e.g. UTF-8, GB2312)
+
+    // example UTF-8 charset (TTF file must support symbols being used
+    // 'charset'          => "абвгдeжзийклмнопрстуфхцчшщъьюяАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯ",
+
+    'ttf_file'         => './AHGBold.ttf',   // TTF file for captcha text
+
+    //'captcha_type' => Securimage::SI_CAPTCHA_WORDS, // Securimage::SI_CAPTCHA_STRING || Securimage:: SI_CAPTCHA_MATHEMATIC || Securimage::SI_CAPTCHA_WORDS
+
+    //'display_value' => 'ABC 123', // Draws custom text on captcha
+
+
+    /**** Code Storage & Database Options ****/
+
+    // true if you *DO NOT* want to use PHP sessions at all, false to use PHP sessions
+    'no_session'       => false,
+
+    // the PHP session name to use (null for default PHP session name)
+    // do not change unless you know what you are doing
+    'session_name'     => null,
+
+    // change to true to store codes in a database
+    'use_database'     => false,
+
+    // database engine to use for storing codes.  must have the PDO extension loaded
+    // Values choices are:
+    // Securimage::SI_DRIVER_MYSQL, Securimage::SI_DRIVER_SQLITE3, Securimage::SI_DRIVER_PGSQL
+    'database_driver'  => Securimage::SI_DRIVER_MYSQL,
+
+    'database_host'    => 'localhost',     // database server host to connect to
+    'database_user'    => 'root',          // database user to connect as
+    'database_pass'    => '',              // database user password
+    'database_name'    => 'securimage',    // name of database to select (you must create this first or use an existing database)
+    'database_table'   => 'captcha_codes', // database table for storing codes, will be created automatically
+
+    // Securimage will automatically create the database table if it is not found
+    // change to true for performance reasons once database table is up and running
+    'skip_table_check' => false,
+
+    /**** Audio Options ****/
+
+    //'audio_path'       => __DIR__ . '/audio/en/',
+    //'audio_use_noise'  => true,
+    //'audio_noise_path' => __DIR__ . '/audio/noise/',
+    //'degrade_audio' => true,
+);
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/database/.htaccess b/wp-content/plugins/wp2pgpmail/phpcaptcha/database/.htaccess
index 8d2f25636ddd2da5618493bc74e617b0f59bdfc3..4fdb24a5e5045a0670e571f76fa5f55523870b80 100644
--- a/wp-content/plugins/wp2pgpmail/phpcaptcha/database/.htaccess
+++ b/wp-content/plugins/wp2pgpmail/phpcaptcha/database/.htaccess
@@ -1 +1,11 @@
-deny from all
+# Deny access to this folder
+
+# Apache 2.4
+<IfModule mod_authz_core.c>
+	Require all denied
+</IfModule>
+
+# Apache 2.2
+<IfModule !mod_authz_core.c>
+	Deny from all
+</IfModule>
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/database/securimage.sq3 b/wp-content/plugins/wp2pgpmail/phpcaptcha/database/securimage.sq3
new file mode 100644
index 0000000000000000000000000000000000000000..a3fcbd74a66836c242305fb63fd4d223f7111397
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/database/securimage.sq3 differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/example_form.ajax.php b/wp-content/plugins/wp2pgpmail/phpcaptcha/example_form.ajax.php
new file mode 100644
index 0000000000000000000000000000000000000000..e066f2264b67d7ac362a3391a755bd061bacfc17
--- /dev/null
+++ b/wp-content/plugins/wp2pgpmail/phpcaptcha/example_form.ajax.php
@@ -0,0 +1,205 @@
+<?php
+session_start(); // this MUST be called prior to any output including whitespaces and line breaks!
+
+$GLOBALS['ct_recipient']   = 'YOU@EXAMPLE.COM'; // Change to your email address!
+$GLOBALS['ct_msg_subject'] = 'Securimage Test Contact Form';
+
+$GLOBALS['DEBUG_MODE'] = 1;
+// CHANGE TO 0 TO TURN OFF DEBUG MODE
+// IN DEBUG MODE, ONLY THE CAPTCHA CODE IS VALIDATED, AND NO EMAIL IS SENT
+
+
+// Process the form, if it was submitted
+process_si_contact_form();
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+    <meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
+    <title>Securimage Example Form</title>
+    <style type="text/css">
+    <!--
+        #success_message { border: 1px solid #000; width: 550px; text-align: left; padding: 10px 7px; background: #33ff33; color: #000; font-weight: bold; font-size: 1.2em; border-radius: 4px; -moz-border-radius: 4px; -webkit-border-radius: 4px; }
+        fieldset { width: 90%; }
+        legend { font-size: 24px; }
+        .note { font-size: 18px; }
+        form label { display: block; font-weight: bold; }
+    -->
+    </style>
+    </head>
+<body>
+
+<fieldset>
+<legend>Example Form</legend>
+
+<p class="note">
+  This is an example PHP form that processes user information, checks for errors, and validates the captcha code.<br />
+  This example form also demonstrates how to submit a form to itself to display error messages.
+</p>
+
+<div id="success_message" style="display: none">Your message has been sent!<br />We will contact you as soon as possible.</div>
+
+<form method="post" action="" id="contact_form" onsubmit="return processForm()">
+  <input type="hidden" name="do" value="contact" />
+
+  <p>
+    <strong>Name*:</strong><br />
+    <input type="text" name="ct_name" size="35" value="" />
+  </p>
+
+  <p>
+    <strong>Email*:</strong><br />
+    <input type="text" name="ct_email" size="35" value="" />
+  </p>
+
+  <p>
+    <strong>URL:</strong><br />
+    <input type="text" name="ct_URL" size="35" value="" />
+  </p>
+
+  <p>
+    <strong>Message*:</strong><br />
+    <textarea name="ct_message" rows="12" cols="60"></textarea>
+  </p>
+
+  <p>
+    <?php require_once 'securimage.php'; echo Securimage::getCaptchaHtml(array('input_name' => 'ct_captcha')); ?>
+  </p>
+
+  <p>
+    <br />
+    <input type="submit" value="Submit Message" />
+  </p>
+
+</form>
+</fieldset>
+
+<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
+<script type="text/javascript">
+    $.noConflict();
+
+    function reloadCaptcha()
+    {
+        jQuery('#siimage').prop('src', './securimage_show.php?sid=' + Math.random());
+    }
+
+    function processForm()
+    {
+        jQuery.ajax({
+            url: '<?php echo htmlspecialchars($_SERVER['PHP_SELF'], ENT_QUOTES) ?>',
+            type: 'POST',
+            data: jQuery('#contact_form').serialize(),
+            dataType: 'json'
+        }).done(function(data) {
+            if (data.error === 0) {
+                jQuery('#success_message').show();
+                jQuery('#contact_form')[0].reset();
+                reloadCaptcha();
+                setTimeout("jQuery('#success_message').fadeOut()", 12000);
+            } else {
+                alert("There was an error with your submission.\n\n" + data.message);
+
+                if (data.message.indexOf('Incorrect security code') >= 0) {
+                    jQuery('#captcha_code').val('');
+                }
+            }
+        });
+
+        return false;
+    }
+</script>
+
+</body>
+</html>
+
+<?php
+
+// The form processor PHP code
+function process_si_contact_form()
+{
+    if ($_SERVER['REQUEST_METHOD'] == 'POST' && @$_POST['do'] == 'contact') {
+        // if the form has been submitted
+
+        foreach($_POST as $key => $value) {
+            if (!is_array($key)) {
+                // sanitize the input data
+                if ($key != 'ct_message') $value = strip_tags($value);
+                $_POST[$key] = htmlspecialchars(stripslashes(trim($value)));
+            }
+        }
+
+        $name    = @$_POST['ct_name'];    // name from the form
+        $email   = @$_POST['ct_email'];   // email from the form
+        $URL     = @$_POST['ct_URL'];     // url from the form
+        $message = @$_POST['ct_message']; // the message from the form
+        $captcha = @$_POST['ct_captcha']; // the user's entry for the captcha code
+        $name    = substr($name, 0, 64);  // limit name to 64 characters
+
+        $errors = array();  // initialize empty error array
+
+        if (isset($GLOBALS['DEBUG_MODE']) && $GLOBALS['DEBUG_MODE'] == false) {
+            // only check for errors if the form is not in debug mode
+
+            if (strlen($name) < 3) {
+                // name too short, add error
+                $errors['name_error'] = 'Your name is required';
+            }
+
+            if (strlen($email) == 0) {
+                // no email address given
+                $errors['email_error'] = 'Email address is required';
+            } else if ( !preg_match('/^(?:[\w\d-]+\.?)+@(?:(?:[\w\d]\-?)+\.)+\w{2,4}$/i', $email)) {
+                // invalid email format
+                $errors['email_error'] = 'Email address entered is invalid';
+            }
+
+            if (strlen($message) < 20) {
+                // message length too short
+                $errors['message_error'] = 'Please enter a message';
+            }
+        }
+
+        // Only try to validate the captcha if the form has no errors
+        // This is especially important for ajax calls
+        if (sizeof($errors) == 0) {
+            require_once dirname(__FILE__) . '/securimage.php';
+            $securimage = new Securimage();
+
+            if ($securimage->check($captcha) == false) {
+                $errors['captcha_error'] = 'Incorrect security code entered';
+            }
+        }
+
+        if (sizeof($errors) == 0) {
+            // no errors, send the form
+            $time       = date('r');
+            $message = "A message was submitted from the contact form.  The following information was provided.<br /><br />"
+                     . "Name: $name<br />"
+                     . "Email: $email<br />"
+                     . "URL: $URL<br />"
+                     . "Message:<br />"
+                     . "<pre>$message</pre>"
+                     . "<br /><br />IP Address: {$_SERVER['REMOTE_ADDR']}<br />"
+                     . "Time: $time<br />"
+                     . "Browser: " . htmlspecialchars($_SERVER['HTTP_USER_AGENT']) . "<br />";
+
+            if (isset($GLOBALS['DEBUG_MODE']) && $GLOBALS['DEBUG_MODE'] == false) {
+                // send the message with mail()
+                mail($GLOBALS['ct_recipient'], $GLOBALS['ct_msg_subject'], $message, "From: {$GLOBALS['ct_recipient']}\r\nReply-To: {$email}\r\nContent-type: text/html; charset=ISO-8859-1\r\nMIME-Version: 1.0");
+            }
+
+            $return = array('error' => 0, 'message' => 'OK');
+            die(json_encode($return));
+        } else {
+            $errmsg = '';
+            foreach($errors as $key => $error) {
+                // set up error messages to display with each field
+                $errmsg .= " - {$error}\n";
+            }
+
+            $return = array('error' => 1, 'message' => $errmsg);
+            die(json_encode($return));
+        }
+    } // POST
+} // function process_si_contact_form()
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/example_form.php b/wp-content/plugins/wp2pgpmail/phpcaptcha/example_form.php
new file mode 100644
index 0000000000000000000000000000000000000000..8e726d8e122c1d51e5836050b3cab7fa9888857d
--- /dev/null
+++ b/wp-content/plugins/wp2pgpmail/phpcaptcha/example_form.php
@@ -0,0 +1,232 @@
+<?php
+
+error_reporting(E_ALL);
+ini_set('display_errors', 1);
+
+session_start(); // this MUST be called prior to any output including whitespaces and line breaks!
+
+$GLOBALS['DEBUG_MODE'] = 1;
+// CHANGE TO 0 TO TURN OFF DEBUG MODE
+// IN DEBUG MODE, ONLY THE CAPTCHA CODE IS VALIDATED, AND NO EMAIL IS SENT
+
+$GLOBALS['ct_recipient']   = 'YOU@EXAMPLE.COM'; // Change to your email address!  Make sure DEBUG_MODE above is 0 for mail to send!
+$GLOBALS['ct_msg_subject'] = 'Securimage Test Contact Form';
+
+?>
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+  <title>Securimage Example Form</title>
+  <link rel="stylesheet" href="securimage.css" media="screen">
+  <style type="text/css">
+  <!--
+  div.error { display: block; color: #f00; font-weight: bold; font-size: 1.2em; }
+  span.error { display: block; color: #f00; font-style: italic; }
+  .success { color: #00f; font-weight: bold; font-size: 1.2em; }
+  form label { display: block; font-weight: bold; }
+  fieldset { width: 90%; }
+  legend { font-size: 24px; }
+  .note { font-size: 18px;
+  -->
+  </style>
+</head>
+<body>
+
+<fieldset>
+<legend>Example Form</legend>
+
+<p class="note">
+  This is an example PHP form that processes user information, checks for errors, and validates the captcha code.<br />
+  This example form also demonstrates how to submit a form to itself to display error messages.
+</p>
+
+<?php
+
+process_si_contact_form(); // Process the form, if it was submitted
+
+if (isset($_SESSION['ctform']['error']) &&  $_SESSION['ctform']['error'] == true): /* The last form submission had 1 or more errors */ ?>
+<div class="error">There was a problem with your submission.  Errors are displayed below in red.</div><br>
+<?php elseif (isset($_SESSION['ctform']['success']) && $_SESSION['ctform']['success'] == true): /* form was processed successfully */ ?>
+<div class="success">The captcha was correct and the message has been sent!  The captcha was solved in <?php echo $_SESSION['ctform']['timetosolve'] ?> seconds.</div><br />
+<?php endif; ?>
+
+<form method="post" action="<?php echo htmlspecialchars($_SERVER['REQUEST_URI'] . $_SERVER['QUERY_STRING']) ?>" id="contact_form">
+  <input type="hidden" name="do" value="contact">
+
+  <p>
+    <label for="ct_name">Name*:</label>
+    <?php echo @$_SESSION['ctform']['name_error'] ?>
+    <input type="text" id="ct_name" name="ct_name" size="35" value="<?php echo htmlspecialchars(@$_SESSION['ctform']['ct_name']) ?>">
+  </p>
+
+  <p>
+    <label for="ct_email">Email*:</label>
+    <?php echo @$_SESSION['ctform']['email_error'] ?>
+    <input type="text" id="ct_email" name="ct_email" size="35" value="<?php echo htmlspecialchars(@$_SESSION['ctform']['ct_email']) ?>">
+  </p>
+
+  <p>
+    <label for="ct_URL">URL:</label>
+    <?php echo @$_SESSION['ctform']['URL_error'] ?>
+    <input type="text" id="ct_URL" name="ct_URL" size="35" value="<?php echo htmlspecialchars(@$_SESSION['ctform']['ct_URL']) ?>">
+  </p>
+
+  <p>
+    <label for="ct_message">Message*:</label>
+    <?php echo @$_SESSION['ctform']['message_error'] ?>
+    <textarea id="ct_message" name="ct_message" rows="12" cols="60"><?php echo htmlspecialchars(@$_SESSION['ctform']['ct_message']) ?></textarea>
+  </p>
+
+  <div>
+    <?php
+      // show captcha HTML using Securimage::getCaptchaHtml()
+      require_once 'securimage.php';
+      $options = array();
+      $options['input_name']             = 'ct_captcha'; // change name of input element for form post
+      $options['disable_flash_fallback'] = false; // allow flash fallback
+
+      if (!empty($_SESSION['ctform']['captcha_error'])) {
+        // error html to show in captcha output
+        $options['error_html'] = $_SESSION['ctform']['captcha_error'];
+      }
+
+      echo "<div id='captcha_container_1'>\n";
+      echo Securimage::getCaptchaHtml($options);
+      echo "\n</div>\n";
+
+      /*
+      // To render some or all captcha components individually
+      $options['input_name'] = 'ct_captcha_2';
+      $options['image_id']   = 'ct_captcha_2';
+      $options['input_id']   = 'ct_captcha_2';
+      $options['namespace']  = 'captcha2';
+
+      echo "<br>\n<div id='captcha_container_2'>\n";
+      echo Securimage::getCaptchaHtml($options, Securimage::HTML_IMG);
+
+      echo Securimage::getCaptchaHtml($options, Securimage::HTML_ICON_REFRESH);
+      echo Securimage::getCaptchaHtml($options, Securimage::HTML_AUDIO);
+
+      echo '<div style="clear: both"></div>';
+
+      echo Securimage::getCaptchaHtml($options, Securimage::HTML_INPUT_LABEL);
+      echo Securimage::getCaptchaHtml($options, Securimage::HTML_INPUT);
+      echo "\n</div>";
+      */
+    ?>
+  </div>
+
+  <p>
+    <br>
+    <input type="submit" value="Submit Message">
+  </p>
+
+</form>
+</fieldset>
+
+</body>
+</html>
+
+<?php
+
+// The form processor PHP code
+function process_si_contact_form()
+{
+  $_SESSION['ctform'] = array(); // re-initialize the form session data
+
+  if ($_SERVER['REQUEST_METHOD'] == 'POST' && @$_POST['do'] == 'contact') {
+  	// if the form has been submitted
+
+    foreach($_POST as $key => $value) {
+      if (!is_array($key)) {
+      	// sanitize the input data
+        if ($key != 'ct_message') $value = strip_tags($value);
+        $_POST[$key] = htmlspecialchars(stripslashes(trim($value)));
+      }
+    }
+
+    $name    = @$_POST['ct_name'];    // name from the form
+    $email   = @$_POST['ct_email'];   // email from the form
+    $URL     = @$_POST['ct_URL'];     // url from the form
+    $message = @$_POST['ct_message']; // the message from the form
+    $captcha = @$_POST['ct_captcha']; // the user's entry for the captcha code
+    $name    = substr($name, 0, 64);  // limit name to 64 characters
+
+    $errors = array();  // initialize empty error array
+
+    if (isset($GLOBALS['DEBUG_MODE']) && $GLOBALS['DEBUG_MODE'] == false) {
+      // only check for errors if the form is not in debug mode
+
+      if (strlen($name) < 3) {
+        // name too short, add error
+        $errors['name_error'] = 'Your name is required';
+      }
+
+      if (strlen($email) == 0) {
+        // no email address given
+        $errors['email_error'] = 'Email address is required';
+      } else if ( !preg_match('/^(?:[\w\d-]+\.?)+@(?:(?:[\w\d]\-?)+\.)+\w{2,63}$/i', $email)) {
+        // invalid email format
+        $errors['email_error'] = 'Email address entered is invalid';
+      }
+
+      if (strlen($message) < 20) {
+        // message length too short
+        $errors['message_error'] = 'Your message must be longer than 20 characters';
+      }
+    }
+
+    // Only try to validate the captcha if the form has no errors
+    // This is especially important for ajax calls
+    if (sizeof($errors) == 0) {
+      require_once dirname(__FILE__) . '/securimage.php';
+      $securimage = new Securimage();
+
+      if ($securimage->check($captcha) == false) {
+        $errors['captcha_error'] = 'Incorrect security code entered<br />';
+      }
+    }
+
+    if (sizeof($errors) == 0) {
+      // no errors, send the form
+      $time       = date('r');
+      $message = "A message was submitted from the contact form.  The following information was provided.<br /><br />"
+                    . "<em>Name: $name</em><br />"
+                    . "<em>Email: $email</em><br />"
+                    . "<em>URL: $URL</em><br />"
+                    . "<em>Message:</em><br />"
+                    . "<pre>$message</pre>"
+                    . "<br /><br /><em>IP Address:</em> {$_SERVER['REMOTE_ADDR']}<br />"
+                    . "<em>Time:</em> $time<br />"
+                    . "<em>Browser:</em> " . htmlspecialchars($_SERVER['HTTP_USER_AGENT']) . "<br />";
+
+      $message = wordwrap($message, 70);
+
+      if (isset($GLOBALS['DEBUG_MODE']) && $GLOBALS['DEBUG_MODE'] == false) {
+      	// send the message with mail()
+        mail($GLOBALS['ct_recipient'], $GLOBALS['ct_msg_subject'], $message, "From: {$GLOBALS['ct_recipient']}\r\nReply-To: {$email}\r\nContent-type: text/html; charset=UTF-8\r\nMIME-Version: 1.0");
+      }
+
+      $_SESSION['ctform']['timetosolve'] = $securimage->getTimeToSolve();
+      $_SESSION['ctform']['error'] = false;  // no error with form
+      $_SESSION['ctform']['success'] = true; // message sent
+    } else {
+      // save the entries, this is to re-populate the form
+      $_SESSION['ctform']['ct_name'] = $name;       // save name from the form submission
+      $_SESSION['ctform']['ct_email'] = $email;     // save email
+      $_SESSION['ctform']['ct_URL'] = $URL;         // save URL
+      $_SESSION['ctform']['ct_message'] = $message; // save message
+
+      foreach($errors as $key => $error) {
+      	// set up error messages to display with each field
+        $_SESSION['ctform'][$key] = "<span class=\"error\">$error</span>";
+      }
+
+      $_SESSION['ctform']['error'] = true; // set error floag
+    }
+  } // POST
+}
+
+$_SESSION['ctform']['success'] = false; // clear success value after running
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/images/audio_icon.png b/wp-content/plugins/wp2pgpmail/phpcaptcha/images/audio_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..9922ef1a3e1331e84becddbf85fb0793f0dc9391
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/images/audio_icon.png differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/images/loading.png b/wp-content/plugins/wp2pgpmail/phpcaptcha/images/loading.png
new file mode 100644
index 0000000000000000000000000000000000000000..171156861ba7e4df96818be40dfd24e9dc7e2164
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/images/loading.png differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/images/refresh.png b/wp-content/plugins/wp2pgpmail/phpcaptcha/images/refresh.png
new file mode 100644
index 0000000000000000000000000000000000000000..f5e7d827aaf050fa016d81d72b7444763eb0b6fa
Binary files /dev/null and b/wp-content/plugins/wp2pgpmail/phpcaptcha/images/refresh.png differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage.css b/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage.css
new file mode 100644
index 0000000000000000000000000000000000000000..0cffdb921a6844ee3536ac5c1c1031d6a94104be
--- /dev/null
+++ b/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage.css
@@ -0,0 +1,41 @@
+@CHARSET "UTF-8";
+
+@-webkit-keyframes rotating /* Safari and Chrome */ {
+  from {
+    -ms-transform: rotate(0deg);
+    -moz-transform: rotate(0deg);
+    -webkit-transform: rotate(0deg);
+    -o-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+  to {
+    -ms-transform: rotate(360deg);
+    -moz-transform: rotate(360deg);
+    -webkit-transform: rotate(360deg);
+    -o-transform: rotate(360deg);
+    transform: rotate(360deg);
+  }
+}
+@keyframes rotating {
+  from {
+    -ms-transform: rotate(0deg);
+    -moz-transform: rotate(0deg);
+    -webkit-transform: rotate(0deg);
+    -o-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+  to {
+    -ms-transform: rotate(360deg);
+    -moz-transform: rotate(360deg);
+    -webkit-transform: rotate(360deg);
+    -o-transform: rotate(360deg);
+    transform: rotate(360deg);
+  }
+}
+.rotating {
+  -webkit-animation: rotating 1.5s linear infinite;
+  -moz-animation: rotating 1.5s linear infinite;
+  -ms-animation: rotating 1.5s linear infinite;
+  -o-animation: rotating 1.5s linear infinite;
+  animation: rotating 1.5s linear infinite;
+}
\ No newline at end of file
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage.js b/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage.js
new file mode 100644
index 0000000000000000000000000000000000000000..481e9e69fff04ea70abcc63e8176ae6629f6f08f
--- /dev/null
+++ b/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage.js
@@ -0,0 +1,252 @@
+/*!
+ * Securimage CAPTCHA Audio Library
+ * https://www.phpcaptcha.org/
+ * 
+ * Copyright 2015 phpcaptcha.org
+ * Released under the BSD-3 license
+ * See https://github.com/dapphp/securimage/blob/master/README.md
+ */
+
+var SecurimageAudio = function(options) {
+    this.html5Support    = true;
+    this.flashFallback   = false;
+    this.captchaId       = null;
+    this.playing         = false;
+    this.reload          = false;
+    this.audioElement    = null;
+    this.controlsElement = null;
+    this.playButton      = null;
+    this.playButtonImage = null;
+    this.loadingImage    = null;
+    
+    if (options.audioElement) {
+        this.audioElement = document.getElementById(options.audioElement);
+    }
+    if (options.controlsElement) {
+        this.controlsElement = document.getElementById(options.controlsElement);
+    }
+    
+    this.init();
+}
+
+SecurimageAudio.prototype.init = function() {
+    var ua    = navigator.userAgent.toLowerCase();
+    var ieVer = (ua.indexOf('msie') != -1) ? parseInt(ua.split('msie')[1]) : false;
+    // ie 11+ detection
+    if (!ieVer && null != (ieVer = ua.match(/trident\/.*rv:(\d+\.\d+)/)))
+        ieVer = parseInt(ieVer[1]);
+
+    var objAu = this.audioElement.getElementsByTagName('object');
+    if (objAu.length > 0) {
+        objAu = objAu[0];
+    } else {
+        objAu = null;
+    }
+
+    if (ieVer) {
+        if (ieVer < 9) {
+            // no html5 audio support, hide player controls
+            this.controlsElement.style.display = 'none';
+            this.html5Support = false;
+            return ;
+        } else if ('' == this.audioElement.canPlayType('audio/wav')) {
+            // check for mpeg <source> tag - if not found then fallback to flash
+            var sources    = this.audioElement.getElementsByTagName('source');
+            var mp3support = false;
+            var type;
+            
+            if (objAu) {
+                this.flashFallback = true;
+            }
+
+            for (var i = 0; i < sources.length; ++i) {
+                type = sources[i].attributes["type"].value;
+                if (type.toLowerCase().indexOf('mpeg') >= 0 || type.toLowerCase().indexOf('mp3') >= 0) {
+                    mp3support = true;
+                    break;
+                }
+            }
+
+            if (false == mp3support) {
+                // browser supports <audio> but does not support WAV audio and no flash audio available
+                this.html5Support = false;
+                
+                if (this.flashFallback) {
+                    // ie9+? bug - flash object does not display when moved from within audio tag to other dom node
+                    var newObjAu = document.createElement('object');
+                    var newParams = document.createElement('param');
+                    var oldParams = objAu.getElementsByTagName('param');
+                    this.copyElementAttributes(newObjAu, objAu);
+                    if (oldParams.length > 0) {
+                        this.copyElementAttributes(newParams, oldParams[0]);
+                        newObjAu.appendChild(newParams);
+                    }
+                    objAu.parentNode.removeChild(objAu);
+                    this.audioElement.parentNode.appendChild(newObjAu);
+                }
+
+                this.audioElement.parentNode.removeChild(this.audioElement);
+                this.controlsElement.parentNode.removeChild(this.controlsElement);
+                
+                return ;
+            }
+        }
+    }
+
+    this.audioElement.addEventListener('playing', this.updateControls.bind(this), false);
+    this.audioElement.addEventListener('ended',   this.audioStopped.bind(this), false);
+
+    // find the element used as the play button and register click event to play/stop audio
+    var children = this.controlsElement.getElementsByTagName('*');
+    for (var i = 0; i < children.length; ++i) {
+        var el = children[i];
+        if (undefined != el.className) {
+            if (el.className.indexOf('play_button') >= 0) {
+                this.playButton = el;
+                el.addEventListener('click', this.play.bind(this), false);
+            } else if (el.className.indexOf('play_image') >= 0) {
+                this.playButtonImage = el;
+            } else if (el.className.indexOf('loading_image') >= 0) {
+                this.loadingImage = el;
+            }
+        }
+    }
+
+    if (objAu) {
+        // remove flash object from DOM
+        objAu.parentNode.removeChild(objAu);
+    }
+}
+
+SecurimageAudio.prototype.play = function(evt) {
+    if (null != this.playButton) {
+        this.playButton.blur();
+    }
+
+    if (this.reload) {
+        this.replaceElements();
+        this.reload = false;
+    }
+
+    try {
+        if (!this.playing) {
+            if (this.playButtonImage != null) {
+                this.playButtonImage.style.display = 'none';
+            }
+            if (this.loadingImage != null) {
+                this.loadingImage.style.display = '';
+            }
+            //TODO: FIX, most likely browser doesn't support audio type
+            this.audioElement.onerror = this.audioError;
+            try {
+                this.audioElement.play();
+            } catch(ex) {
+                alert('Audio error: ' + ex);
+            }
+        } else {
+            this.audioElement.pause();
+            if (this.loadingImage != null) {
+                this.loadingImage.style.display = 'none';
+            }
+            if (this.playButtonImage != null) {
+                this.playButtonImage.style.display = '';
+            }
+            this.playing = false;
+        }
+    } catch (ex) {
+        alert('Audio error: ' + ex);
+    }
+    
+    if (undefined !== evt) {
+        evt.preventDefault();
+    }
+    return false;
+}
+
+SecurimageAudio.prototype.refresh = function(captchaId) {
+    if (!this.html5Support) {
+        return;
+    }
+
+    if (undefined !== captchaId) {
+        this.captchaId = captchaId;
+    }    
+
+    this.playing = true;
+    this.reload  = false;
+    this.play(); // stops audio if playing
+    this.reload  = true;
+    
+    return false;
+}
+
+SecurimageAudio.prototype.copyElementAttributes = function(newEl, el) {
+    for (var i = 0, atts = el.attributes, n = atts.length; i < n; ++i) {
+        newEl.setAttribute(atts[i].nodeName, atts[i].value);
+    }
+    
+    return newEl;
+}
+
+SecurimageAudio.prototype.replaceElements = function() {
+    var parent = this.audioElement.parentNode;
+    parent.removeChild(this.audioElement);
+    
+    var newAudioEl = document.createElement('audio');
+    newAudioEl.setAttribute('style', 'display: none;');
+    newAudioEl.setAttribute('preload', 'false');
+    newAudioEl.setAttribute('id', this.audioElement.id);
+
+    for (var c = 0; c < this.audioElement.children.length; ++c) {
+        if (this.audioElement.children[c].tagName.toLowerCase() != 'source') continue;
+        var sourceEl = document.createElement('source');
+        this.copyElementAttributes(sourceEl, this.audioElement.children[c]);
+        var cid = (null !== this.captchaId) ? this.captchaId : (Math.random() + '').replace('0.', '');
+        sourceEl.src = sourceEl.src.replace(/([?|&])id=[a-zA-Z0-9]+/, '$1id=' + cid);
+        newAudioEl.appendChild(sourceEl);
+    }
+
+    this.audioElement = null;
+    this.audioElement = newAudioEl;
+    parent.appendChild(this.audioElement);
+
+    this.audioElement.addEventListener('playing', this.updateControls.bind(this), false);
+    this.audioElement.addEventListener('ended',   this.audioStopped.bind(this), false);
+}
+
+SecurimageAudio.prototype.updateControls = function() {
+    this.playing = true;
+    if (this.loadingImage != null) {
+        this.loadingImage.style.display = 'none';
+    }
+    if (this.playButtonImage != null) {
+        this.playButtonImage.style.display = '';
+    }
+}
+
+SecurimageAudio.prototype.audioStopped = function() {
+    this.playing = false;
+}
+
+SecurimageAudio.prototype.audioError = function(err) {
+    var msg = null;
+    switch(err.target.error.code) {
+        case err.target.error.MEDIA_ERR_ABORTED:
+            break;
+        case err.target.error.MEDIA_ERR_NETWORK:
+            msg = 'A network error caused the audio download to fail.';
+            break;
+        case err.target.error.MEDIA_ERR_DECODE:
+            alert('An error occurred while decoding the audio');
+            break;
+        case err.target.error.MEDIA_ERR_SRC_NOT_SUPPORTED:
+            alert('The audio format is not supported by your browser.');
+            break;
+        default:
+            alert('An unknown error occurred trying to play the audio.');
+            break;
+    }
+    if (msg) {
+        alert('Audio playback error: ' + msg);
+    }
+}
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage.php b/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage.php
index 7a52c65a7c72ea52e5545bb1bd2fbbbee7f70fae..b4f4d2c3bd445aec09f8d32bbda378ca4366d593 100644
--- a/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage.php
+++ b/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage.php
@@ -1,41 +1,173 @@
 <?php
 
+// error_reporting(E_ALL); ini_set('display_errors', 1); // uncomment this line for debugging
+
 /**
- * Project:     Securimage: A PHP class for creating and managing form CAPTCHA images<br />
- * File:        securimage.php<br />
+ * Project:  Securimage: A PHP class dealing with CAPTCHA images, audio, and validation
+ * File:     securimage.php
  *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or any later version.<br /><br />
+ * Copyright (c) 2018, Drew Phillips
+ * All rights reserved.
  *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.<br /><br />
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
  *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA<br /><br />
+ *  - Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  - Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
  *
- * Any modifications to the library should be indicated clearly in the source code
- * to inform users that the changes are not a part of the original software.<br /><br />
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
  *
- * If you found this script useful, please take a quick moment to rate it.<br />
- * http://www.hotscripts.com/rate/49400.html  Thanks.
+ * Any modifications to the library should be indicated clearly in the source code
+ * to inform users that the changes are not a part of the original software.
  *
- * @link http://www.phpcaptcha.org Securimage PHP CAPTCHA
- * @link http://www.phpcaptcha.org/latest.zip Download Latest Version
- * @link http://www.phpcaptcha.org/Securimage_Docs/ Online Documentation
- * @copyright 2009 Drew Phillips
+ * @link https://www.phpcaptcha.org Securimage Homepage
+ * @link https://www.phpcaptcha.org/latest.zip Download Latest Version
+ * @link https://github.com/dapphp/securimage GitHub page
+ * @link https://www.phpcaptcha.org/Securimage_Docs/ Online Documentation
+ * @copyright 2018 Drew Phillips
  * @author Drew Phillips <drew@drew-phillips.com>
- * @version 2.0.1 BETA (December 6th, 2009)
+ * @version 3.6.7 (March 2018)
  * @package Securimage
  *
  */
 
 /**
+
  ChangeLog
+ 3.6.7
+ - Merge changes from 4.0.1-nextgen
+ - Increase captcha difficulty
+ - Add setting "use_text_angles". Enable to select a random angle and step value and draw each character at an angle in a step like fashion
+ - Add setting "use_random_spaces". Enable to insert 1-3 spaces between a random group of letters some of the time
+ - Add setting "use_random_baseline". Enable to draw letters at a random height instead of centered.  Each character's baseline is a step up or down from the previous (not totally random)
+ - Add setting "use_random_boxes". Enable to draw a bounding box around one or more characters at random
+ - Improve performance of captcha generation when using distortion (perturbation) and noise (noise_level)
+ - Enable image anti-aliasing
+ - Make all text functions multibyte safe when using UTF-8 or other encodings for charsets and wordlists (using mbstring)
+
+ 3.6.6
+ - Not critical: Fix potential HTML injection in example form via HTTP_USER_AGENT (CVE-2017-14077)
+
+ 3.6.5
+ - Fix regex in replaceElements in securimage.js
+ - Update examples
+ - Exclude certain examples from Git autogenerated archives
+
+ 3.6.4
+ - Fix XSS vulnerability in example_form.ajax.php (Discovered by RedTeam. advisory rt-sa-2016-002)
+ - Update example_form.ajax.php to use Securimage::getCaptchaHtml()
+
+ 3.6.3
+ - Add support for multibyte wordlist files
+ - Fix code generation issues with UTF-8 charsets
+ - Add parameter to getCaptchaHtml() method to control display components of captcha HTML
+ - Fix database audio storage issue with multiple namespaces
+
+ 3.6.2
+ - Support HTTP range requests with audio playback (iOS requirement)
+ - Add optional config.inc.php for storing global configuration settings
+
+ 3.6.1
+ - Fix copyElement bug in securimage.js for IE Flash fallback
+
+ 3.6
+ - Implement CAPTCHA audio using HTML5 <audio> with optional Flash fallback
+ - Support MP3 audio using LAME MP3 Encoder (Internet Explorer 9+ does not support WAV format in <audio> tags)
+ - Add getCaptchaHtml() options to support full framework integration (ruifil)
+
+ 3.5.4
+ - Fix email validation code in example form files
+ - Fix backslashes in getCaptchaHtml for img attribute on Windows systems
+
+ 3.5.3
+ - Add options for audio button to getCaptchaHtml(), fix urlencoding of flash parameters that was breaking button
+
+ 3.5.2
+
+ - Add Securimage::getCaptchaHtml() for getting automatically generated captcha html code
+ - Option for using SoX to add effects to captcha audio to make identification by neural networks more difficult
+ - Add setNamespace() method
+ - Add getTimeToSolve() method
+ - Add session_status() check so session still starts if one had previously been opened and closed
+ - Add .htaccess file to audio directory to deny access; update audio files
+ - Option to skip checking of database tables during connection
+ - Add composer.json to package, submit to packagist
+ - Add font_ratio variable to determine size of font (github.com/wilkor)
+ - Add hint if sqlite3 database is not writeable.  Improve database error handling, add example database options to securimage_play.php
+ - Fixed issue regarding database storage and math captcha breaking audio output (github.com/SoftwareAndOutsourcing)
+
+ 3.5.1
+ - Fix XSS vulnerability in example_form.php (discovered by Gjoko Krstic - <gjoko@zeroscience.mk>)
+
+ 3.5
+ - Release new version
+ - MB string support for charlist
+ - Modify audio file path to use language directories
+ - Changed default captcha appearance
+
+ 3.2RC4
+ - Add MySQL, PostgreSQL, and SQLite3 support for database storage
+ - Deprecate "use_sqlite_db" option and remove SQLite2/sqlite_* functions
+ - Add new captcha type that displays 2 dictionary words on one image
+ - Update examples
+
+ 3.2RC3
+ - Fix canSendHeaders() check which was breaking if a PHP startup error was issued
+
+ 3.2RC2
+ - Add error handler (https://github.com/dapphp/securimage/issues/15)
+ - Fix flash examples to use the correct value name for audio parameter
+
+ 3.2RC1
+ - New audio captcha code.  Faster, fully dynamic audio, full WAV support
+   (Paul Voegler, Drew Phillips) <http://voegler.eu/pub/audio>
+ - New Flash audio streaming button.  User defined image and size supported
+ - Additional options for customizing captcha (noise_level, send_headers,
+   no_exit, no_session, display_value
+ - Add captcha ID support.  Uses sqlite and unique captcha IDs to track captchas,
+   no session used
+ - Add static methods for creating and validating captcha by ID
+ - Automatic clearing of old codes from SQLite database
+
+ 3.0.3Beta
+ - Add improved mixing function to WavFile class (Paul Voegler)
+ - Improve performance and security of captcha audio (Paul Voegler, Drew Phillips)
+ - Add option to use random file as background noise in captcha audio
+ - Add new securimage options for audio files
+
+ 3.0.2Beta
+ - Fix issue with session variables when upgrading from 2.0 - 3.0
+ - Improve audio captcha, switch to use WavFile class, make mathematical captcha audio work
+
+ 3.0.1
+ - Bugfix: removed use of deprecated variable in addSignature method that would cause errors with display_errors on
+
+ 3.0
+ - Rewrite class using PHP5 OOP
+ - Remove support for GD fonts, require FreeType
+ - Remove support for multi-color codes
+ - Add option to make codes case-sensitive
+ - Add namespaces to support multiple captchas on a single page or page specific captchas
+ - Add option to show simple math problems instead of codes
+ - Remove support for mp3 files due to vulnerability in decoding mp3 audio files
+ - Create new flash file to stream wav files instead of mp3
+ - Changed to BSD license
+
+ 2.0.2
+ - Fix pathing to make integration into libraries easier (Nathan Phillip Brink ohnobinki@ohnopublishing.net)
 
  2.0.1
  - Add support for browsers with cookies disabled (requires php5, sqlite) maps users to md5 hashed ip addresses and md5 hashed codes for security
@@ -54,7 +186,7 @@
  - Flash button to stream mp3 audio (Douglas Walsh www.douglaswalsh.net)
  - Audio output is mp3 format by default
  - Change font to AlteHaasGrotesk by yann le coroller
- - Some code cleanup 
+ - Some code cleanup
 
  1.0.4 (unreleased)
  - Ability to output audible codes in mp3 format to stream from flash
@@ -80,1515 +212,3559 @@
 
  */
 
-/**
- * Output images in JPEG format
- */
-if (!defined('SI_IMAGE_JPEG'))
-  define('SI_IMAGE_JPEG', 1);
-/**
- * Output images in PNG format
- */
-if (!defined('SI_IMAGE_PNG'))
-  define('SI_IMAGE_PNG',  2);
-/**
- * Output images in GIF format (not recommended)
- * Must have GD >= 2.0.28!
- */
-if (!defined('SI_IMAGE_GIF'))
-  define('SI_IMAGE_GIF',  3);
 
 /**
  * Securimage CAPTCHA Class.
  *
+ * A class for creating and validating secure CAPTCHA images and audio.
+ *
+ * The class contains many options regarding appearance, security, storage of
+ * captcha data and image/audio generation options.
+ *
  * @package    Securimage
  * @subpackage classes
+ * @author     Drew Phillips <drew@drew-phillips.com>
  *
  */
-class Securimage {
-
-	/**
-	 * The desired width of the CAPTCHA image.
-	 *
-	 * @var int
-	 */
-	var $image_width;
-
-	/**
-	 * The desired width of the CAPTCHA image.
-	 *
-	 * @var int
-	 */
-	var $image_height;
-
-	/**
-	 * The image format for output.<br />
-	 * Valid options: SI_IMAGE_PNG, SI_IMAGE_JPG, SI_IMAGE_GIF
-	 *
-	 * @var int
-	 */
-	var $image_type;
-
-	/**
-	 * The length of the code to generate.
-	 *
-	 * @var int
-	 */
-	var $code_length;
-
-	/**
-	 * The character set for individual characters in the image.<br />
-	 * Letters are converted to uppercase.<br />
-	 * The font must support the letters or there may be problematic substitutions.
-	 *
-	 * @var string
-	 */
-	var $charset;
-
-	/**
-	 * Create codes using this word list
-	 *
-	 * @var string  The path to the word list to use for creating CAPTCHA codes
-	 */
-	var $wordlist_file;
-
-	/**
-	 * Use wordlist of not
-	 *
-	 * @var bool true to use wordlist file, false to use random code
-	 */
-	var $use_wordlist = false;
-
-	/**
-	 * Note: Use of GD fonts is not recommended as many distortion features are not available<br />
-	 * The GD font to use.<br />
-	 * Internal gd fonts can be loaded by their number.<br />
-	 * Alternatively, a file path can be given and the font will be loaded from file.
-	 *
-	 * @var mixed
-	 */
-	var $gd_font_file;
-
-	/**
-	 * The approximate size of the font in pixels.<br />
-	 * This does not control the size of the font because that is determined by the GD font itself.<br />
-	 * This is used to aid the calculations of positioning used by this class.<br />
-	 *
-	 * @var int
-	 */
-	var $gd_font_size;
-
-	/**
-	 * Use a gd font instead of TTF
-	 *
-	 * @var bool true for gd font, false for TTF
-	 */
-	var $use_gd_font;
-
-	// Note: These font options below do not apply if you set $use_gd_font to true with the exception of $text_color
-
-	/**
-	 * The path to the TTF font file to load.
-	 *
-	 * @var string
-	 */
-	var $ttf_file;
-
-	/**
-	 * How much to distort image, higher = more distortion.<br />
-	 * Distortion is only available when using TTF fonts.<br />
-	 *
-	 * @var float
-	 */
-	var $perturbation;
-
-	/**
-	 * The minimum angle in degrees, with 0 degrees being left-to-right reading text.<br />
-	 * Higher values represent a counter-clockwise rotation.<br />
-	 * For example, a value of 90 would result in bottom-to-top reading text.<br />
-	 * This value along with maximum angle distance do not need to be very high with perturbation
-	 *
-	 * @var int
-	 */
-	var $text_angle_minimum;
-
-	/**
-	 * The minimum angle in degrees, with 0 degrees being left-to-right reading text.<br />
-	 * Higher values represent a counter-clockwise rotation.<br />
-	 * For example, a value of 90 would result in bottom-to-top reading text.
-	 *
-	 * @var int
-	 */
-	var $text_angle_maximum;
-
-	/**
-	 * The X-Position on the image where letter drawing will begin.<br />
-	 * This value is in pixels from the left side of the image.
-	 *
-	 * @var int
-	 * @deprecated 2.0
-	 */
-	var $text_x_start;
-
-	/**
-	 * The background color for the image as a Securimage_Color.<br />
-	 *
-	 * @var Securimage_Color
-	 */
-	var $image_bg_color;
-
-	/**
-	 * Scan this directory for gif, jpg, and png files to use as background images.<br />
-	 * A random image file will be picked each time.<br />
-	 * Change from null to the full path to your directory.<br />
-	 * i.e. var $background_directory = $_SERVER['DOCUMENT_ROOT'] . '/securimage/backgrounds';
-	 * Make sure not to pass a background image to the show function, otherwise this directive is ignored.
-	 *
-	 * @var string
-	 */
-	var $background_directory = null; //'./backgrounds';
-
-	/**
-	 * The text color to use for drawing characters as a Securimage_Color.<br />
-	 * This value is ignored if $use_multi_text is set to true.<br />
-	 * Make sure this contrasts well with the background color or image.<br />
-	 *
-	 * @see Securimage::$use_multi_text
-	 * @var Securimage_Color
-	 */
-	var $text_color;
-
-	/**
-	 * Set to true to use multiple colors for each character.
-	 *
-	 * @see Securimage::$multi_text_color
-	 * @var boolean
-	 */
-	var $use_multi_text;
-
-	/**
-	 * Array of Securimage_Colors which will be randomly selected for each letter.<br />
-	 *
-	 * @var array
-	 */
-	var $multi_text_color;
-
-	/**
-	 * Set to true to make the characters appear transparent.
-	 *
-	 * @see Securimage::$text_transparency_percentage
-	 * @var boolean
-	 */
-	var $use_transparent_text;
-
-	/**
-	 * The percentage of transparency, 0 to 100.<br />
-	 * A value of 0 is completely opaque, 100 is completely transparent (invisble)
-	 *
-	 * @see Securimage::$use_transparent_text
-	 * @var int
-	 */
-	var $text_transparency_percentage;
-
-
-	// Line options
-	/**
-	* Draw vertical and horizontal lines on the image.
-	*
-	* @see Securimage::$line_color
-	* @see Securimage::$draw_lines_over_text
-	* @var boolean
-	*/
-	var $num_lines;
-
-	/**
-	 * Color of lines drawn over text
-	 *
-	 * @var string
-	 */
-	var $line_color;
-
-	/**
-	 * Draw the lines over the text.<br />
-	 * If fales lines will be drawn before putting the text on the image.
-	 *
-	 * @var boolean
-	 */
-	var $draw_lines_over_text;
-
-	/**
-	 * Text to write at the bottom corner of captcha image
-	 * 
-	 * @since 2.0
-	 * @var string Signature text
-	 */
-	var $image_signature;
-	
-	/**
-	 * Color to use for writing signature text
-	 * 
-	 * @since 2.0
-	 * @var Securimage_Color
-	 */
-	var $signature_color;
-
-	/**
-	 * Full path to the WAV files to use to make the audio files, include trailing /.<br />
-	 * Name Files  [A-Z0-9].wav
-	 *
-	 * @since 1.0.1
-	 * @var string
-	 */
-	var $audio_path;
-
-	/**
-	 * Type of audio file to generate (mp3 or wav)
-	 *
-	 * @var string
-	 */
-	var $audio_format;
-
-	/**
-	 * The session name to use if not the default.  Blank for none
-	 *
-	 * @see http://php.net/session_name
-	 * @since 2.0
-	 * @var string
-	 */
-	var $session_name = '';
-	
-	/**
-	 * The amount of time in seconds that a code remains valid.<br />
-	 * Any code older than this number will be considered invalid even if entered correctly.<br />
-	 * Any non-numeric or value less than 1 disables this functionality.
-	 * 
-	 * @var int
-	 */
-	var $expiry_time;
-	
-	/**
-	 * Path to the file to use for storing codes for users.<br />
-	 * THIS FILE MUST ABSOLUTELY NOT BE ACCESSIBLE FROM A WEB BROWSER!!<br />
-	 * Put this file in a directory below the web root or one that is restricted (i.e. an apache .htaccess file with deny from all)<br />
-	 * If you cannot meet those requirements your forms may not be completely protected.<br />
-	 * You could obscure the database file name but this is also not recommended.
-	 * 
-	 * @var string
-	 */
-	var $sqlite_database;
-	
-	/**
-	 * Use an SQLite database for storing codes as a backup to sessions.<br />
-	 * Note: Sessions will still be used 
-	 */
-	var $use_sqlite_db;
-
-
-	//END USER CONFIGURATION
-	//There should be no need to edit below unless you really know what you are doing.
-
-	/**
-	 * The gd image resource.
-	 *
-	 * @access private
-	 * @var resource
-	 */
-	var $im;
-
-	/**
-	 * Temporary image for rendering
-	 *
-	 * @access private
-	 * @var resource
-	 */
-	var $tmpimg;
-
-	/**
-	 * Internal scale factor for anti-alias @hkcaptcha
-	 *
-	 * @access private
-	 * @since 2.0
-	 * @var int
-	 */
-	var $iscale; // internal scale factor for anti-alias @hkcaptcha
-
-	/**
-	 * The background image resource
-	 *
-	 * @access private
-	 * @var resource
-	 */
-	var $bgimg;
-
-	/**
-	 * The code generated by the script
-	 *
-	 * @access private
-	 * @var string
-	 */
-	var $code;
-
-	/**
-	 * The code that was entered by the user
-	 *
-	 * @access private
-	 * @var string
-	 */
-	var $code_entered;
-
-	/**
-	 * Whether or not the correct code was entered
-	 *
-	 * @access private
-	 * @var boolean
-	 */
-	var $correct_code;
-	
-	/**
-	 * Handle to SQLite database
-	 *
-	 * @access private
-	 * @var resource
-	 */
-	var $sqlite_handle;
-	
-	/**
-	 * Color resource for image line color
-	 * 
-	 * @access private
-	 * @var int
-	 */
-	var $gdlinecolor;
-	
-	/**
-	 * Array of colors for multi colored codes
-	 * 
-	 * @access private
-	 * @var array
-	 */
-	var $gdmulticolor;
-	
-	/**
-	 * Color resource for image font color
-	 * 
-	 * @access private
-	 * @var int
-	 */
-	var $gdtextcolor;
-	
-	/**
-	 * Color resource for image signature color
-	 * 
-	 * @access private
-	 * @var int
-	 */
-	var $gdsignaturecolor;
-	
-	/**
-	 * Color resource for image background color
-	 * 
-	 * @access private
-	 * @var int
-	 */
-	var $gdbgcolor;
-	
-    public function __construct()
+class Securimage
+{
+    // All of the public variables below are securimage options
+    // They can be passed as an array to the Securimage constructor, set below,
+    // or set from securimage_show.php and securimage_play.php
+
+    /**
+     * Constant for rendering captcha as a JPEG image
+     * @var int
+     */
+    const SI_IMAGE_JPEG = 1;
+
+    /**
+     * Constant for rendering captcha as a PNG image (default)
+     * @var int
+     */
+
+    const SI_IMAGE_PNG  = 2;
+    /**
+     * Constant for rendering captcha as a GIF image
+     * @var int
+     */
+    const SI_IMAGE_GIF  = 3;
+
+    /**
+     * Constant for generating a normal alphanumeric captcha based on the
+     * character set
+     *
+     * @see Securimage::$charset charset property
+     * @var int
+     */
+    const SI_CAPTCHA_STRING     = 0;
+
+    /**
+     * Constant for generating a captcha consisting of a simple math problem
+     *
+     * @var int
+     */
+    const SI_CAPTCHA_MATHEMATIC = 1;
+
+    /**
+     * Constant for generating a word based captcha using 2 words from a list
+     *
+     * @var int
+     */
+    const SI_CAPTCHA_WORDS      = 2;
+
+    /**
+     * MySQL option identifier for database storage option
+     *
+     * @var string
+     */
+    const SI_DRIVER_MYSQL   = 'mysql';
+
+    /**
+     * PostgreSQL option identifier for database storage option
+     *
+     * @var string
+     */
+    const SI_DRIVER_PGSQL   = 'pgsql';
+
+    /**
+     * SQLite option identifier for database storage option
+     *
+     * @var string
+     */
+    const SI_DRIVER_SQLITE3 = 'sqlite';
+
+    /**
+     * getCaptchaHtml() display constant for HTML Captcha Image
+     *
+     * @var integer
+     */
+    const HTML_IMG   = 1;
+
+    /**
+     * getCaptchaHtml() display constant for HTML5 Audio code
+     *
+     * @var integer
+     */
+    const HTML_AUDIO = 2;
+
+    /**
+     * getCaptchaHtml() display constant for Captcha Input text box
+     *
+     * @var integer
+     */
+    const HTML_INPUT = 4;
+
+    /**
+     * getCaptchaHtml() display constant for Captcha Text HTML label
+     *
+     * @var integer
+     */
+    const HTML_INPUT_LABEL = 8;
+
+    /**
+     * getCaptchaHtml() display constant for HTML Refresh button
+     *
+     * @var integer
+     */
+    const HTML_ICON_REFRESH = 16;
+
+    /**
+     * getCaptchaHtml() display constant for all HTML elements (default)
+     *
+     * @var integer
+     */
+    const HTML_ALL = 0xffffffff;
+
+    /*%*********************************************************************%*/
+    // Properties
+
+    /**
+     * The width of the captcha image
+     * @var int
+     */
+    public $image_width = 215;
+
+    /**
+     * The height of the captcha image
+     * @var int
+     */
+    public $image_height = 80;
+
+    /**
+     * Font size is calculated by image height and this ratio.  Leave blank for
+     * default ratio of 0.4.
+     *
+     * Valid range: 0.1 - 0.99.
+     *
+     * Depending on image_width, values > 0.6 are probably too large and
+     * values < 0.3 are too small.
+     *
+     * @var float
+     */
+    public $font_ratio;
+
+    /**
+     * The type of the image, default = png
+     *
+     * @see Securimage::SI_IMAGE_PNG SI_IMAGE_PNG
+     * @see Securimage::SI_IMAGE_JPEG SI_IMAGE_JPEG
+     * @see Securimage::SI_IMAGE_GIF SI_IMAGE_GIF
+     * @var int
+     */
+    public $image_type   = self::SI_IMAGE_PNG;
+
+    /**
+     * The background color of the captcha
+     * @var Securimage_Color|string
+     */
+    public $image_bg_color = '#ffffff';
+
+    /**
+     * The color of the captcha text
+     * @var Securimage_Color|string
+     */
+    public $text_color     = '#707070';
+
+    /**
+     * The color of the lines over the captcha
+     * @var Securimage_Color|string
+     */
+    public $line_color     = '#707070';
+
+    /**
+     * The color of the noise that is drawn
+     * @var Securimage_Color|string
+     */
+    public $noise_color    = '#707070';
+
+    /**
+     * How transparent to make the text.
+     *
+     * 0 = completely opaque, 100 = invisible
+     *
+     * @var int
+     */
+    public $text_transparency_percentage = 20;
+
+    /**
+     * Whether or not to draw the text transparently.
+     *
+     * true = use transparency, false = no transparency
+     *
+     * @var bool
+     */
+    public $use_transparent_text         = true;
+
+    /**
+     * The length of the captcha code
+     * @var int
+     */
+    public $code_length    = 6;
+
+    /**
+     * Display random spaces in the captcha text on the image
+     *
+     * @var bool true to insert random spacing between groups of letters
+     */
+    public $use_random_spaces  = false;
+
+    /**
+     * Draw each character at an angle with random starting angle and increase/decrease per character
+     * @var bool true to use random angles, false to draw each character normally
+     */
+    public $use_text_angles = false;
+
+    /**
+     * Instead of centering text vertically in the image, the baseline of each character is
+     * randomized in such a way that the next character is drawn slightly higher or lower than
+     * the previous in a step-like fashion.
+     *
+     * @var bool true to use random baselines, false to center text in image
+     */
+    public $use_random_baseline = false;
+
+    /**
+     * Draw a bounding box around some characters at random.  20% of the time, random boxes
+     * may be drawn around 0 or more characters on the image.
+     *
+     * @var bool  true to randomly draw boxes around letters, false not to
+     */
+    public $use_random_boxes = false;
+
+    /**
+     * Whether the captcha should be case sensitive or not.
+     *
+     * Not recommended, use only for maximum protection.
+     *
+     * @var bool
+     */
+    public $case_sensitive = false;
+
+    /**
+     * The character set to use for generating the captcha code
+     * @var string
+     */
+    public $charset        = 'abcdefghijkmnopqrstuvwxzyABCDEFGHJKLMNPQRSTUVWXZY0123456789';
+
+    /**
+     * How long in seconds a captcha remains valid, after this time it will be
+     * considered incorrect.
+     *
+     * @var int
+     */
+    public $expiry_time    = 900;
+
+    /**
+     * The session name securimage should use.
+     *
+     * Only use if your application uses a custom session name (e.g. Joomla).
+     * It is recommended to set this value here so it is used by all securimage
+     * scripts (i.e. securimage_show.php)
+     *
+     * @var string
+     */
+    public $session_name   = null;
+
+    /**
+     * true to use the wordlist file, false to generate random captcha codes
+     * @var bool
+     */
+    public $use_wordlist   = false;
+
+    /**
+     * The level of distortion.
+     *
+     * 0.75 = normal, 1.0 = very high distortion
+     *
+     * @var double
+     */
+    public $perturbation = 0.85;
+
+    /**
+     * How many lines to draw over the captcha code to increase security
+     * @var int
+     */
+    public $num_lines    = 5;
+
+    /**
+     * The level of noise (random dots) to place on the image, 0-10
+     * @var int
+     */
+    public $noise_level  = 2;
+
+    /**
+     * The signature text to draw on the bottom corner of the image
+     * @var string
+     */
+    public $image_signature = '';
+
+    /**
+     * The color of the signature text
+     * @var Securimage_Color|string
+     */
+    public $signature_color = '#707070';
+
+    /**
+     * The path to the ttf font file to use for the signature text.
+     * Defaults to $ttf_file (AHGBold.ttf)
+     *
+     * @see Securimage::$ttf_file
+     * @var string
+     */
+    public $signature_font;
+
+    /**
+     * No longer used.
+     *
+     * Use an SQLite database to store data (for users that do not support cookies)
+     *
+     * @var bool
+     * @see Securimage::$database_driver database_driver property
+     * @deprecated 3.2RC4
+     */
+    public $use_sqlite_db = false;
+
+    /**
+     * Use a database backend for code storage.
+     * Provides a fallback to users with cookies disabled.
+     * Required when using captcha IDs.
+     *
+     * @see Securimage::$database_driver
+     * @var bool
+     */
+    public $use_database = false;
+
+    /**
+     * Whether or not to skip checking if Securimage tables exist when using a
+     * database.
+     *
+     * Turn this to true once database functionality is working to improve
+     * performance.
+     *
+     * @var bool true to not check if captcha_codes tables are set up, false
+     * to check (and create if necessary)
+     */
+    public $skip_table_check = false;
+
+    /**
+     * Database driver to use for database support.
+     * Allowable values: *mysql*, *pgsql*, *sqlite*.
+     * Default: sqlite
+     *
+     * @var string
+     */
+    public $database_driver = self::SI_DRIVER_SQLITE3;
+
+    /**
+     * Database host to connect to when using mysql or postgres
+     *
+     * On Linux use "localhost" for Unix domain socket, otherwise uses TCP/IP
+     *
+     * Does not apply to SQLite
+     *
+     * @var string
+     */
+    public $database_host   = 'localhost';
+
+    /**
+     * Database username for connection (mysql, postgres only)
+     * Default is an empty string
+     *
+     * @var string
+     */
+    public $database_user   = '';
+
+    /**
+     * Database password for connection (mysql, postgres only)
+     * Default is empty string
+     *
+     * @var string
+     */
+    public $database_pass   = '';
+
+    /**
+     * Name of the database to select (mysql, postgres only)
+     *
+     * @see Securimage::$database_file for SQLite
+     * @var string
+     */
+    public $database_name   = '';
+
+    /**
+     * Database table where captcha codes are stored
+     *
+     * Note: Securimage will attempt to create this table for you if it does
+     * not exist.  If the table cannot be created, an E_USER_WARNING is emitted
+     *
+     * @var string
+     */
+    public $database_table  = 'captcha_codes';
+
+    /**
+     * Fully qualified path to the database file when using SQLite3.
+     *
+     * This value is only used when $database_driver == sqlite and does
+     * not apply when no database is used, or when using MySQL or PostgreSQL.
+     *
+     * On *nix, file must have permissions of 0666.
+     *
+     * **Make sure the directory containing this file is NOT web accessible**
+     *
+     * @var string
+     */
+    public $database_file;
+
+    /**
+     * The type of captcha to create.
+     *
+     * Either alphanumeric based on *charset*, a simple math problem, or an
+     * image consisting of 2 words from the word list.
+     *
+     * @see Securimage::SI_CAPTCHA_STRING SI_CAPTCHA_STRING
+     * @see Securimage::SI_CAPTCHA_MATHEMATIC SI_CAPTCHA_MATHEMATIC
+     * @see Securimage::SI_CAPTCHA_WORDS SI_CAPTCHA_WORDS
+     * @see Securimage::$charset charset property
+     * @see Securimage::$wordlist_file wordlist_file property
+     * @var int
+     */
+    public $captcha_type  = self::SI_CAPTCHA_STRING; // or self::SI_CAPTCHA_MATHEMATIC, or self::SI_CAPTCHA_WORDS;
+
+    /**
+     * The captcha namespace used for having multiple captchas on a page or
+     * to separate captchas from differen forms on your site.
+     * Example:
+     *
+     *     <?php
+     *     // use <img src="securimage_show.php?namespace=contact_form">
+     *     // or manually in securimage_show.php
+     *     $img->setNamespace('contact_form');
+     *
+     *     // in form validator
+     *     $img->setNamespace('contact_form');
+     *     if ($img->check($code) == true) {
+     *         echo "Valid!";
+     *     }
+     *
+     * @var string
+     */
+    public $namespace;
+
+    /**
+     * The TTF font file to use to draw the captcha code.
+     *
+     * Leave blank for default font AHGBold.ttf
+     *
+     * @var string
+     */
+    public $ttf_file;
+
+    /**
+     * The path to the wordlist file to use.
+     *
+     * Leave blank for default words/words.txt
+     *
+     * @var string
+     */
+    public $wordlist_file;
+
+    /**
+     * Character encoding of the wordlist file.
+     * Requires PHP Multibyte String (mbstring) support.
+     * Allows word list to contain characters other than US-ASCII (requires compatible TTF font).
+     *
+     * @var string The character encoding (e.g. UTF-8, UTF-7, EUC-JP, GB2312)
+     * @see http://php.net/manual/en/mbstring.supported-encodings.php
+     * @since 3.6.3
+     */
+    public $wordlist_file_encoding = null;
+
+    /**
+     * The directory to scan for background images, if set a random background
+     * will be chosen from this folder
+     *
+     * @var string
+     */
+    public $background_directory;
+
+    /**
+     * No longer used
+     *
+     * The path to the SQLite database file to use
+     *
+     * @deprecated 3.2RC4
+     * @see Securimage::$database_file database_file property
+     * @var string
+     */
+    public $sqlite_database;
+
+    /**
+     * The path to the audio files to be used for audio captchas.
+     *
+     * Can also be set in securimage_play.php
+     *
+     * Example:
+     *
+     *     $img->audio_path = '/home/yoursite/public_html/securimage/audio/en/';
+     *
+     * @var string
+     */
+    public $audio_path;
+
+    /**
+     * Use SoX (The Swiss Army knife of audio manipulation) for audio effects
+     * and processing.
+     *
+     * Using SoX should make it more difficult for bots to solve audio captchas
+     *
+     * @see Securimage::$sox_binary_path sox_binary_path property
+     * @var bool true to use SoX, false to use PHP
+     */
+    public $audio_use_sox = false;
+
+    /**
+     * The path to the SoX binary on your system
+     *
+     * @var string
+     */
+    public $sox_binary_path = '/usr/bin/sox';
+
+    /**
+     * The path to the lame (mp3 encoder) binary on your system
+     * Static so that Securimage::getCaptchaHtml() has access to this value.
+     *
+     * @since 3.6
+     * @var string
+     */
+    public static $lame_binary_path = '/usr/bin/lame';
+
+    /**
+     * The path to the directory containing audio files that will be selected
+     * randomly and mixed with the captcha audio.
+     *
+     * @var string
+     */
+    public $audio_noise_path;
+
+    /**
+     * Whether or not to mix background noise files into captcha audio
+     *
+     * Mixing random background audio with noise can help improve security of
+     * audio captcha.
+     *
+     * Default: securimage/audio/noise
+     *
+     * @since 3.0.3
+     * @see Securimage::$audio_noise_path audio_noise_path property
+     * @var bool true = mix, false = no
+     */
+    public $audio_use_noise;
+
+    /**
+     * The method and threshold (or gain factor) used to normalize the mixing
+     * with background noise.
+     *
+     * See http://www.voegler.eu/pub/audio/ for more information.
+     *
+     * Default: 0.6
+     *
+     * Valid:
+     *     >= 1
+     *     Normalize by multiplying by the threshold (boost - positive gain).
+     *     A value of 1 in effect means no normalization (and results in clipping).
+     *
+     *     <= -1
+     *     Normalize by dividing by the the absolute value of threshold (attenuate - negative gain).
+     *     A factor of 2 (-2) is about 6dB reduction in volume.
+     *
+     *     [0, 1)  (open inverval - not including 1)
+     *     The threshold above which amplitudes are comressed logarithmically.
+     *     e.g. 0.6 to leave amplitudes up to 60% "as is" and compressabove.
+     *
+     *     (-1, 0) (open inverval - not including -1 and 0)
+     *     The threshold above which amplitudes are comressed linearly.
+     *     e.g. -0.6 to leave amplitudes up to 60% "as is" and compress above.
+     *
+     * @since 3.0.4
+     * @var float
+     */
+    public $audio_mix_normalization = 0.8;
+
+    /**
+     * Whether or not to degrade audio by introducing random noise.
+     *
+     * Current research shows this may not increase the security of audible
+     * captchas.
+     *
+     * Default: true
+     *
+     * @since 3.0.3
+     * @var bool
+     */
+    public $degrade_audio;
+
+    /**
+     * Minimum delay to insert between captcha audio letters in milliseconds
+     *
+     * @since 3.0.3
+     * @var float
+     */
+    public $audio_gap_min = 0;
+
+    /**
+     * Maximum delay to insert between captcha audio letters in milliseconds
+     *
+     * @since 3.0.3
+     * @var float
+     */
+    public $audio_gap_max = 3000;
+
+    /**
+     * The file path for logging errors from audio (default __DIR__)
+     *
+     * @var string|null
+     */
+    public $log_path = null;
+
+    /**
+     * The name of the log file for logging audio errors
+     *
+     * @var string|null (defualt si_error.log)
+     */
+    public $log_file = null;
+
+    /**
+     * Captcha ID if using static captcha
+     * @var string Unique captcha id
+     */
+    protected static $_captchaId = null;
+
+    /**
+     * The GD image resource of the captcha image
+     *
+     * @var resource
+     */
+    protected $im;
+
+    /**
+     * A temporary GD image resource of the captcha image for distortion
+     *
+     * @var resource
+     */
+    protected $tmpimg;
+
+    /**
+     * The background image GD resource
+     * @var string
+     */
+    protected $bgimg;
+
+    /**
+     * Scale factor for magnification of distorted captcha image
+     *
+     * @var int
+     */
+    protected $iscale = 2;
+
+    /**
+     * Absolute path to securimage directory.
+     *
+     * This is calculated at runtime
+     *
+     * @var string
+     */
+    public $securimage_path = null;
+
+    /**
+     * The captcha challenge value.
+     *
+     * Either the case-sensitive/insensitive word captcha, or the solution to
+     * the math captcha.
+     *
+     * @var string|bool Captcha challenge value
+     */
+    protected $code;
+
+    /**
+     * The display value of the captcha to draw on the image
+     *
+     * Either the word captcha or the math equation to present to the user
+     *
+     * @var string Captcha display value to draw on the image
+     */
+    protected $code_display;
+
+    /**
+     * Alternate text to draw as the captcha image text
+     *
+     * A value that can be passed to the constructor that can be used to
+     * generate a captcha image with a given value.
+     *
+     * This value does not get stored in the session or database and is only
+     * used when calling Securimage::show().
+     *
+     * If a display_value was passed to the constructor and the captcha image
+     * is generated, the display_value will be used as the string to draw on
+     * the captcha image.
+     *
+     * Used only if captcha codes are generated and managed by a 3rd party
+     * app/library
+     *
+     * @var string Captcha code value to display on the image
+     */
+    public $display_value;
+
+    /**
+     * Captcha code supplied by user [set from Securimage::check()]
+     *
+     * @var string
+     */
+    protected $captcha_code;
+
+    /**
+     * Time (in seconds) that the captcha was solved in (correctly or incorrectly).
+     *
+     * This is from the time of code creation, to when validation was attempted.
+     *
+     * @var int
+     */
+    protected $_timeToSolve = 0;
+
+    /**
+     * Flag that can be specified telling securimage not to call exit after
+     * generating a captcha image or audio file
+     *
+     * @var bool If true, script will not terminate; if false script will terminate (default)
+     */
+    protected $no_exit;
+
+    /**
+     * Flag indicating whether or not a PHP session should be started and used
+     *
+     * @var bool If true, no session will be started; if false, session will be started and used to store data (default)
+     */
+    protected $no_session;
+
+    /**
+     * Flag indicating whether or not HTTP headers will be sent when outputting
+     * captcha image/audio
+     *
+     * @var bool If true (default) headers will be sent, if false, no headers are sent
+     */
+    protected $send_headers;
+
+    /**
+     * PDO connection when a database is used
+     *
+     * @var PDO|bool
+     */
+    protected $pdo_conn;
+
+    /**
+     * The GD color for the background color
+     *
+     * @var int
+     */
+    protected $gdbgcolor;
+
+    /**
+     * The GD color for the text color
+     *
+     * @var int
+     */
+    protected $gdtextcolor;
+
+    /**
+     * The GD color for the line color
+     *
+     * @var int
+     */
+    protected $gdlinecolor;
+
+    /**
+     * The GD color for the signature text color
+     *
+     * @var int
+     */
+    protected $gdsignaturecolor;
+
+    /**
+     * Create a new securimage object, pass options to set in the constructor.
+     *
+     * The object can then be used to display a captcha, play an audible captcha, or validate a submission.
+     *
+     * @param array $options  Options to initialize the class.  May be any class property.
+     *
+     *     $options = array(
+     *         'text_color' => new Securimage_Color('#013020'),
+     *         'code_length' => 5,
+     *         'num_lines' => 5,
+     *         'noise_level' => 3,
+     *         'font_file' => Securimage::getPath() . '/custom.ttf'
+     *     );
+     *
+     *     $img = new Securimage($options);
+     *
+     */
+    public function __construct($options = array())
+    {
+        $this->securimage_path = dirname(__FILE__);
+
+        if (!is_array($options)) {
+            trigger_error(
+                    '$options passed to Securimage::__construct() must be an array.  ' .
+                    gettype($options) . ' given',
+                    E_USER_WARNING
+            );
+            $options = array();
+        }
+
+        if (function_exists('mb_internal_encoding')) {
+            mb_internal_encoding('UTF-8');
+        }
+
+        // check for and load settings from custom config file
+        $config_file = null;
+
+        if (file_exists(dirname(__FILE__) . '/config.inc.php')) {
+            $config_file = dirname(__FILE__) . '/config.inc.php';
+        }
+        if (isset($options['config_file']) && file_exists($options['config_file'])) {
+            $config_file = $options['config_file'];
+        }
+
+        if ($config_file) {
+            $settings = include $config_file;
+
+            if (is_array($settings)) {
+                $options = array_merge($settings, $options);
+            }
+        }
+
+        if (is_array($options) && sizeof($options) > 0) {
+            foreach($options as $prop => $val) {
+                if ($prop == 'captchaId') {
+                    Securimage::$_captchaId = $val;
+                    $this->use_database     = true;
+                } else if ($prop == 'use_sqlite_db') {
+                    trigger_error("The use_sqlite_db option is deprecated, use 'use_database' instead", E_USER_NOTICE);
+                } else {
+                    $this->$prop = $val;
+                }
+            }
+        }
+
+        $this->image_bg_color  = $this->initColor($this->image_bg_color,  '#ffffff');
+        $this->text_color      = $this->initColor($this->text_color,      '#616161');
+        $this->line_color      = $this->initColor($this->line_color,      '#616161');
+        $this->noise_color     = $this->initColor($this->noise_color,     '#616161');
+        $this->signature_color = $this->initColor($this->signature_color, '#616161');
+
+        if (is_null($this->ttf_file)) {
+            $this->ttf_file = $this->securimage_path . '/AHGBold.ttf';
+        }
+
+        $this->signature_font = $this->ttf_file;
+
+        if (is_null($this->wordlist_file)) {
+            $this->wordlist_file = $this->securimage_path . '/words/words.txt';
+        }
+
+        if (is_null($this->database_file)) {
+            $this->database_file = $this->securimage_path . '/database/securimage.sq3';
+        }
+
+        if (is_null($this->audio_path)) {
+            $this->audio_path = $this->securimage_path . '/audio/en/';
+        }
+
+        if (is_null($this->audio_noise_path)) {
+            $this->audio_noise_path = $this->securimage_path . '/audio/noise/';
+        }
+
+        if (is_null($this->audio_use_noise)) {
+            $this->audio_use_noise = true;
+        }
+
+        if (is_null($this->degrade_audio)) {
+            $this->degrade_audio = true;
+        }
+
+        if (is_null($this->code_length) || (int)$this->code_length < 1) {
+            $this->code_length = 6;
+        }
+
+        if (is_null($this->perturbation) || !is_numeric($this->perturbation)) {
+            $this->perturbation = 0.75;
+        }
+
+        if (is_null($this->namespace) || !is_string($this->namespace)) {
+            $this->namespace = 'default';
+        }
+
+        if (is_null($this->no_exit)) {
+            $this->no_exit = false;
+        }
+
+        if (is_null($this->no_session)) {
+            $this->no_session = false;
+        }
+
+        if (is_null($this->send_headers)) {
+            $this->send_headers = true;
+        }
+
+        if (is_null($this->log_path)) {
+            $this->log_path = __DIR__;
+        }
+
+        if (is_null($this->log_file)) {
+            $this->log_file = 'securimage.error_log';
+        }
+
+        if ($this->no_session != true) {
+            // Initialize session or attach to existing
+            if ( session_id() == '' || (function_exists('session_status') && PHP_SESSION_NONE == session_status()) ) { // no session has been started yet (or it was previousy closed), which is needed for validation
+                if (!is_null($this->session_name) && trim($this->session_name) != '') {
+                    session_name(trim($this->session_name)); // set session name if provided
+                }
+                session_start();
+            }
+        }
+    }
+
+    /**
+     * Return the absolute path to the Securimage directory.
+     *
+     * @return string The path to the securimage base directory
+     */
+    public static function getPath()
+    {
+        return dirname(__FILE__);
+    }
+
+    /**
+     * Generate a new captcha ID or retrieve the current ID (if exists).
+     *
+     * @param bool $new If true, generates a new challenge and returns and ID.  If false, the existing captcha ID is returned, or null if none exists.
+     * @param array $options Additional options to be passed to Securimage.
+     *   $options must include database settings if they are not set directly in securimage.php
+     *
+     * @return null|string Returns null if no captcha id set and new was false, or the captcha ID
+     */
+    public static function getCaptchaId($new = true, array $options = array())
+    {
+        if (is_null($new) || (bool)$new == true) {
+            $id = sha1(uniqid($_SERVER['REMOTE_ADDR'], true));
+            $opts = array('no_session'    => true,
+                          'use_database'  => true);
+            if (sizeof($options) > 0) $opts = array_merge($options, $opts);
+            $si = new self($opts);
+            Securimage::$_captchaId = $id;
+            $si->createCode();
+
+            return $id;
+        } else {
+            return Securimage::$_captchaId;
+        }
+    }
+
+    /**
+     * Validate a captcha code input against a captcha ID
+     *
+     * @param string $id       The captcha ID to check
+     * @param string $value    The captcha value supplied by the user
+     * @param array  $options  Array of options to construct Securimage with.
+     *   Options must include database options if they are not set in securimage.php
+     *
+     * @see Securimage::$database_driver
+     * @return bool true if the code was valid for the given captcha ID, false if not or if database failed to open
+     */
+    public static function checkByCaptchaId($id, $value, array $options = array())
+    {
+        $opts = array('captchaId'    => $id,
+                      'no_session'   => true,
+                      'use_database' => true);
+
+        if (sizeof($options) > 0) $opts = array_merge($options, $opts);
+
+        $si = new self($opts);
+
+        if ($si->openDatabase()) {
+            $code = $si->getCodeFromDatabase();
+
+            if (is_array($code)) {
+                $si->code         = $code['code'];
+                $si->code_display = $code['code_disp'];
+            }
+
+            if ($si->check($value)) {
+                $si->clearCodeFromDatabase();
+
+                return true;
+            } else {
+                return false;
+            }
+        } else {
+            return false;
+        }
+    }
+
+
+    /**
+     * Generates a new challenge and serves a captcha image.
+     *
+     * Appropriate headers will be sent to the browser unless the *send_headers* option is false.
+     *
+     * @param string $background_image The absolute or relative path to the background image to use as the background of the captcha image.
+     *
+     *     $img = new Securimage();
+     *     $img->code_length = 6;
+     *     $img->num_lines   = 5;
+     *     $img->noise_level = 5;
+     *
+     *     $img->show(); // sends the image and appropriate headers to browser
+     *     exit;
+     */
+    public function show($background_image = '')
+    {
+        set_error_handler(array(&$this, 'errorHandler'));
+
+        if($background_image != '' && is_readable($background_image)) {
+            $this->bgimg = $background_image;
+        }
+
+        $this->doImage();
+    }
+
+    /**
+     * Checks a given code against the correct value from the session and/or database.
+     *
+     * @param string $code  The captcha code to check
+     *
+     *     $code = $_POST['code'];
+     *     $img  = new Securimage();
+     *     if ($img->check($code) == true) {
+     *         $captcha_valid = true;
+     *     } else {
+     *         $captcha_valid = false;
+     *     }
+     *
+     * @return bool true if the given code was correct, false if not.
+     */
+    public function check($code)
+    {
+        if (!is_string($code)) {
+            trigger_error("The \$code parameter passed to Securimage::check() must be a string, " . gettype($code) . " given", E_USER_NOTICE);
+            $code = '';
+        }
+
+        $this->code_entered = $code;
+        $this->validate();
+        return $this->correct_code;
+    }
+
+    /**
+     * Returns HTML code for displaying the captcha image, audio button, and form text input.
+     *
+     * Options can be specified to modify the output of the HTML.  Accepted options:
+     *
+     *     'securimage_path':
+     *         Optional: The URI to where securimage is installed (e.g. /securimage)
+     *     'show_image_url':
+     *         Path to the securimage_show.php script (useful when integrating with a framework or moving outside the securimage directory)
+     *         This will be passed as a urlencoded string to the <img> tag for outputting the captcha image
+     *     'audio_play_url':
+     *         Same as show_image_url, except this indicates the URL of the audio playback script
+     *     'image_id':
+     *          A string that sets the "id" attribute of the captcha image (default: captcha_image)
+     *     'image_alt_text':
+     *         The alt text of the captcha image (default: CAPTCHA Image)
+     *     'show_audio_button':
+     *         true/false  Whether or not to show the audio button (default: true)
+     *     'disable_flash_fallback':)
+     *         Allow only HTML5 audio and disable Flash fallback
+     *     'show_refresh_button':
+     *         true/false  Whether or not to show a button to refresh the image (default: true)
+     *     'audio_icon_url':
+     *         URL to the image used for showing the HTML5 audio icon
+     *     'icon_size':
+     *         Size (for both height & width) in pixels of the audio and refresh buttons
+     *     'show_text_input':
+     *         true/false  Whether or not to show the text input for the captcha (default: true)
+     *     'refresh_alt_text':
+     *         Alt text for the refresh image (default: Refresh Image)
+     *     'refresh_title_text':
+     *         Title text for the refresh image link (default: Refresh Image)
+     *     'input_id':
+     *         A string that sets the "id" attribute of the captcha text input (default: captcha_code)
+     *     'input_name':
+     *         A string that sets the "name" attribute of the captcha text input (default: same as input_id)
+     *     'input_text':
+     *         A string that sets the text of the label for the captcha text input (default: Type the text:)
+     *     'input_attributes':
+     *         An array of additional HTML tag attributes to pass to the text input tag (default: empty)
+     *     'image_attributes':
+     *         An array of additional HTML tag attributes to pass to the captcha image tag (default: empty)
+     *     'error_html':
+     *         Optional HTML markup to be shown above the text input field
+     *     'namespace':
+     *         The optional captcha namespace to use for showing the image and playing back the audio. Namespaces are for using multiple captchas on the same page.
+     *
+     * @param array $options Array of options for modifying the HTML code.
+     * @param int   $parts Securiage::HTML_* constant controlling what component of the captcha HTML to display
+     *
+     * @return string  The generated HTML code for displaying the captcha
+     */
+    public static function getCaptchaHtml($options = array(), $parts = Securimage::HTML_ALL)
+    {
+        static $javascript_init = false;
+
+        if (!isset($options['securimage_path'])) {
+            $docroot = (isset($_SERVER['DOCUMENT_ROOT'])) ? $_SERVER['DOCUMENT_ROOT'] : substr($_SERVER['SCRIPT_FILENAME'], 0, -strlen($_SERVER['SCRIPT_NAME']));
+            $docroot = realpath($docroot);
+            $sipath  = dirname(__FILE__);
+            $securimage_path = str_replace($docroot, '', $sipath);
+        } else {
+            $securimage_path = $options['securimage_path'];
+        }
+
+        $show_image_url    = (isset($options['show_image_url'])) ? $options['show_image_url'] : null;
+        $image_id          = (isset($options['image_id'])) ? $options['image_id'] : 'captcha_image';
+        $image_alt         = (isset($options['image_alt_text'])) ? $options['image_alt_text'] : 'CAPTCHA Image';
+        $show_audio_btn    = (isset($options['show_audio_button'])) ? (bool)$options['show_audio_button'] : true;
+        $disable_flash_fbk = (isset($options['disable_flash_fallback'])) ? (bool)$options['disable_flash_fallback'] : false;
+        $show_refresh_btn  = (isset($options['show_refresh_button'])) ? (bool)$options['show_refresh_button'] : true;
+        $refresh_icon_url  = (isset($options['refresh_icon_url'])) ? $options['refresh_icon_url'] : null;
+        $audio_but_bg_col  = (isset($options['audio_button_bgcol'])) ? $options['audio_button_bgcol'] : '#ffffff';
+        $audio_icon_url    = (isset($options['audio_icon_url'])) ? $options['audio_icon_url'] : null;
+        $loading_icon_url  = (isset($options['loading_icon_url'])) ? $options['loading_icon_url'] : null;
+        $icon_size         = (isset($options['icon_size'])) ? $options['icon_size'] : 32;
+        $audio_play_url    = (isset($options['audio_play_url'])) ? $options['audio_play_url'] : null;
+        $audio_swf_url     = (isset($options['audio_swf_url'])) ? $options['audio_swf_url'] : null;
+        $show_input        = (isset($options['show_text_input'])) ? (bool)$options['show_text_input'] : true;
+        $refresh_alt       = (isset($options['refresh_alt_text'])) ? $options['refresh_alt_text'] : 'Refresh Image';
+        $refresh_title     = (isset($options['refresh_title_text'])) ? $options['refresh_title_text'] : 'Refresh Image';
+        $input_text        = (isset($options['input_text'])) ? $options['input_text'] : 'Type the text:';
+        $input_id          = (isset($options['input_id'])) ? $options['input_id'] : 'captcha_code';
+        $input_name        = (isset($options['input_name'])) ? $options['input_name'] :  $input_id;
+        $input_attrs       = (isset($options['input_attributes'])) ? $options['input_attributes'] : array();
+        $image_attrs       = (isset($options['image_attributes'])) ? $options['image_attributes'] : array();
+        $error_html        = (isset($options['error_html'])) ? $options['error_html'] : null;
+        $namespace         = (isset($options['namespace'])) ? $options['namespace'] : '';
+
+        $rand              = md5(uniqid($_SERVER['REMOTE_PORT'], true));
+        $securimage_path   = rtrim($securimage_path, '/\\');
+        $securimage_path   = str_replace('\\', '/', $securimage_path);
+
+        $image_attr = '';
+        if (!is_array($image_attrs)) $image_attrs = array();
+        if (!isset($image_attrs['style'])) $image_attrs['style'] = 'float: left; padding-right: 5px';
+        $image_attrs['id']  = $image_id;
+
+        $show_path = $securimage_path . '/securimage_show.php?';
+        if ($show_image_url) {
+            if (parse_url($show_image_url, PHP_URL_QUERY)) {
+                $show_path = "{$show_image_url}&";
+            } else {
+                $show_path = "{$show_image_url}?";
+            }
+        }
+        if (!empty($namespace)) {
+            $show_path .= sprintf('namespace=%s&amp;', $namespace);
+        }
+        $image_attrs['src'] = $show_path . $rand;
+
+        $image_attrs['alt'] = $image_alt;
+
+        foreach($image_attrs as $name => $val) {
+            $image_attr .= sprintf('%s="%s" ', $name, htmlspecialchars($val));
+        }
+
+        $swf_path  = $securimage_path . '/securimage_play.swf';
+        $play_path = $securimage_path . '/securimage_play.php?';
+        $icon_path = $securimage_path . '/images/audio_icon.png';
+        $load_path = $securimage_path . '/images/loading.png';
+        $js_path   = $securimage_path . '/securimage.js';
+
+        if (!empty($audio_icon_url)) {
+            $icon_path = $audio_icon_url;
+        }
+
+        if (!empty($loading_icon_url)) {
+            $load_path = $loading_icon_url;
+        }
+
+        if (!empty($audio_play_url)) {
+            if (parse_url($audio_play_url, PHP_URL_QUERY)) {
+                $play_path = "{$audio_play_url}&";
+            } else {
+                $play_path = "{$audio_play_url}?";
+            }
+        }
+
+        if (!empty($namespace)) {
+            $play_path .= sprintf('namespace=%s&amp;', $namespace);
+        }
+
+        if (!empty($audio_swf_url)) {
+            $swf_path = $audio_swf_url;
+        }
+
+        $audio_obj = $image_id . '_audioObj';
+        $html      = '';
+
+        if ( ($parts & Securimage::HTML_IMG) > 0) {
+            $html .= sprintf('<img %s/>', $image_attr);
+        }
+
+        if ( ($parts & Securimage::HTML_AUDIO) > 0 && $show_audio_btn) {
+            // html5 audio
+            $html .= sprintf('<div id="%s_audio_div">', $image_id) . "\n" .
+                     sprintf('<audio id="%s_audio" preload="none" style="display: none">', $image_id) . "\n";
+
+            // check for existence and executability of LAME binary
+            // prefer mp3 over wav by sourcing it first, if available
+            if (is_executable(Securimage::$lame_binary_path)) {
+                $html .= sprintf('<source id="%s_source_mp3" src="%sid=%s&amp;format=mp3" type="audio/mpeg">', $image_id, $play_path, uniqid()) . "\n";
+            }
+
+            // output wav source
+            $html .= sprintf('<source id="%s_source_wav" src="%sid=%s" type="audio/wav">', $image_id, $play_path, uniqid()) . "\n";
+
+            // flash audio button
+            if (!$disable_flash_fbk) {
+                $html .= sprintf('<object type="application/x-shockwave-flash" data="%s?bgcol=%s&amp;icon_file=%s&amp;audio_file=%s" height="%d" width="%d">',
+                        htmlspecialchars($swf_path),
+                        urlencode($audio_but_bg_col),
+                        urlencode($icon_path),
+                        urlencode(html_entity_decode($play_path)),
+                        $icon_size, $icon_size
+                );
+
+                $html .= sprintf('<param name="movie" value="%s?bgcol=%s&amp;icon_file=%s&amp;audio_file=%s">',
+                        htmlspecialchars($swf_path),
+                        urlencode($audio_but_bg_col),
+                        urlencode($icon_path),
+                        urlencode(html_entity_decode($play_path))
+                );
+
+                $html .= '</object><br />';
+            }
+
+            // html5 audio close
+            $html .= "</audio>\n</div>\n";
+
+            // html5 audio controls
+            $html .= sprintf('<div id="%s_audio_controls">', $image_id) . "\n" .
+                     sprintf('<a tabindex="-1" class="captcha_play_button" href="%sid=%s" onclick="return false">',
+                             $play_path, uniqid()
+                     ) . "\n" .
+                     sprintf('<img class="captcha_play_image" height="%d" width="%d" src="%s" alt="Play CAPTCHA Audio" style="border: 0px">', $icon_size, $icon_size, htmlspecialchars($icon_path)) . "\n" .
+                     sprintf('<img class="captcha_loading_image rotating" height="%d" width="%d" src="%s" alt="Loading audio" style="display: none">', $icon_size, $icon_size, htmlspecialchars($load_path)) . "\n" .
+                     "</a>\n<noscript>Enable Javascript for audio controls</noscript>\n" .
+                     "</div>\n";
+
+            // html5 javascript
+            if (!$javascript_init) {
+                $html .= sprintf('<script type="text/javascript" src="%s"></script>', $js_path) . "\n";
+                $javascript_init = true;
+            }
+            $html .= '<script type="text/javascript">' .
+                     "$audio_obj = new SecurimageAudio({ audioElement: '{$image_id}_audio', controlsElement: '{$image_id}_audio_controls' });" .
+                     "</script>\n";
+        }
+
+        if ( ($parts & Securimage::HTML_ICON_REFRESH) > 0 && $show_refresh_btn) {
+            $icon_path = $securimage_path . '/images/refresh.png';
+            if ($refresh_icon_url) {
+                $icon_path = $refresh_icon_url;
+            }
+            $img_tag = sprintf('<img height="%d" width="%d" src="%s" alt="%s" onclick="this.blur()" style="border: 0px; vertical-align: bottom">',
+                               $icon_size, $icon_size, htmlspecialchars($icon_path), htmlspecialchars($refresh_alt));
+
+            $html .= sprintf('<a tabindex="-1" style="border: 0" href="#" title="%s" onclick="%sdocument.getElementById(\'%s\').src = \'%s\' + Math.random(); this.blur(); return false">%s</a><br>',
+                    htmlspecialchars($refresh_title),
+                    ($audio_obj) ? "if (typeof window.{$audio_obj} !== 'undefined') {$audio_obj}.refresh(); " : '',
+                    $image_id,
+                    $show_path,
+                    $img_tag
+            );
+        }
+
+        if ($parts == Securimage::HTML_ALL) {
+            $html .= '<div style="clear: both"></div>';
+        }
+
+        if ( ($parts & Securimage::HTML_INPUT_LABEL) > 0 && $show_input) {
+            $html .= sprintf('<label for="%s">%s</label> ',
+                    htmlspecialchars($input_id),
+                    htmlspecialchars($input_text));
+
+            if (!empty($error_html)) {
+                $html .= $error_html;
+            }
+        }
+
+        if ( ($parts & Securimage::HTML_INPUT) > 0 && $show_input) {
+            $input_attr = '';
+            if (!is_array($input_attrs)) $input_attrs = array();
+            $input_attrs['type'] = 'text';
+            $input_attrs['name'] = $input_name;
+            $input_attrs['id']   = $input_id;
+            $input_attrs['autocomplete'] = 'off';
+
+            foreach($input_attrs as $name => $val) {
+                $input_attr .= sprintf('%s="%s" ', $name, htmlspecialchars($val));
+            }
+
+            $html .= sprintf('<input %s>', $input_attr);
+        }
+
+        return $html;
+    }
+
+    /**
+     * Get the time in seconds that it took to solve the captcha.
+     *
+     * @return int The time in seconds from when the code was created, to when it was solved
+     */
+    public function getTimeToSolve()
+    {
+        return $this->_timeToSolve;
+    }
+
+    /**
+     * Set the namespace for the captcha being stored in the session or database.
+     *
+     * Namespaces are useful when multiple captchas need to be displayed on a single page.
+     *
+     * @param string $namespace  Namespace value, String consisting of characters "a-zA-Z0-9_-"
+     */
+    public function setNamespace($namespace)
+    {
+        $namespace = preg_replace('/[^a-z0-9-_]/i', '', $namespace);
+        $namespace = substr($namespace, 0, 64);
+
+        if (!empty($namespace)) {
+            $this->namespace = $namespace;
+        } else {
+            $this->namespace = 'default';
+        }
+    }
+
+    /**
+     * Generate an audible captcha in WAV format and send it to the browser with appropriate headers.
+     * Example:
+     *
+     *     $img = new Securimage();
+     *     $img->outputAudioFile(); // outputs a wav file to the browser
+     *     exit;
+     *
+     * @param string $format
+     */
+    public function outputAudioFile($format = null)
+    {
+        set_error_handler(array(&$this, 'errorHandler'));
+
+        if (isset($_SERVER['HTTP_RANGE'])) {
+            $range   = true;
+            $rangeId = (isset($_SERVER['HTTP_X_PLAYBACK_SESSION_ID'])) ?
+                       'ID' . $_SERVER['HTTP_X_PLAYBACK_SESSION_ID']   :
+                       'ID' . md5($_SERVER['REQUEST_URI']);
+            $uniq    = $rangeId;
+        } else {
+            $uniq = md5(uniqid(microtime()));
+        }
+
+        try {
+            if (!($audio = $this->getAudioData())) {
+                // if previously generated audio not found for current captcha
+                require_once dirname(__FILE__) . '/WavFile.php';
+                $audio = $this->getAudibleCode();
+
+                if (strtolower($format) == 'mp3') {
+                    $audio = $this->wavToMp3($audio);
+                }
+
+                $this->saveAudioData($audio);
+            }
+        } catch (Exception $ex) {
+            $log_file = rtrim($this->log_path, '/\\ ') . DIRECTORY_SEPARATOR . $this->log_file;
+
+            if (($fp = fopen($log_file, 'a+')) !== false) {
+                fwrite($fp, date('Y-m-d H:i:s') . ': Securimage audio error "' . $ex->getMessage() . '"' . "\n");
+                fclose($fp);
+            }
+
+            $audio = $this->audioError();
+        }
+
+        if ($this->no_session != true) {
+            // close session to make it available to other requests in the event
+            // streaming the audio takes sevaral seconds or more
+            session_write_close();
+        }
+
+        if ($this->canSendHeaders() || $this->send_headers == false) {
+            if ($this->send_headers) {
+                if ($format == 'mp3') {
+                    $ext  = 'mp3';
+                    $type = 'audio/mpeg';
+                } else {
+                    $ext  = 'wav';
+                    $type = 'audio/wav';
+                }
+
+                header('Accept-Ranges: bytes');
+                header("Content-Disposition: attachment; filename=\"securimage_audio-{$uniq}.{$ext}\"");
+                header('Cache-Control: no-store, no-cache, must-revalidate');
+                header('Expires: Sun, 1 Jan 2000 12:00:00 GMT');
+                header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT');
+                header('Content-type: ' . $type);
+            }
+
+            $this->rangeDownload($audio);
+        } else {
+            echo '<hr /><strong>'
+                .'Failed to generate audio file, content has already been '
+                .'output.<br />This is most likely due to misconfiguration or '
+                .'a PHP error was sent to the browser.</strong>';
+        }
+
+        restore_error_handler();
+
+        if (!$this->no_exit) exit;
+    }
+
+    /**
+     * Output audio data with http range support.  Typically this shouldn't be
+     * called directly unless being used with a custom implentation.  Use
+     * Securimage::outputAudioFile instead.
+     *
+     * @param string $audio Raw wav or mp3 audio file content
+     */
+    public function rangeDownload($audio)
+    {
+        /* Congratulations Firefox Android/Linux/Windows for being the most
+         * sensible browser of all when streaming HTML5 audio!
+         *
+         * Chrome on Android and iOS on iPad/iPhone both make extra HTTP requests
+         * for the audio whether on WiFi or the mobile network resulting in
+         * multiple downloads of the audio file and wasted bandwidth.
+         *
+         * If I'm doing something wrong in this code or anyone knows why, I'd
+         * love to hear from you.
+         */
+        $audioLength = $size = strlen($audio);
+
+        if (isset($_SERVER['HTTP_RANGE'])) {
+            list( , $range) = explode('=', $_SERVER['HTTP_RANGE']); // bytes=byte-range-set
+            $range = trim($range);
+
+            if (strpos($range, ',') !== false) {
+                // eventually, we should handle requests with multiple ranges
+                // most likely these types of requests will never be sent
+                header('HTTP/1.1 416 Range Not Satisfiable');
+                echo "<h1>Range Not Satisfiable</h1>";
+                exit;
+            } else if (preg_match('/(\d+)-(\d+)/', $range, $match)) {
+                // bytes n - m
+                $range = array(intval($match[1]), intval($match[2]));
+            } else if (preg_match('/(\d+)-$/', $range, $match)) {
+                // bytes n - last byte of file
+                $range = array(intval($match[1]), null);
+            } else if (preg_match('/-(\d+)/', $range, $match)) {
+                // final n bytes of file
+                $range = array($size - intval($match[1]), $size - 1);
+            }
+
+            if ($range[1] === null) $range[1] = $size - 1;
+            $length = $range[1] - $range[0] + 1;
+            $audio = substr($audio, $range[0], $length);
+            $audioLength = strlen($audio);
+
+            header('HTTP/1.1 206 Partial Content');
+            header("Content-Range: bytes {$range[0]}-{$range[1]}/{$size}");
+
+            if ($range[0] < 0 ||$range[1] >= $size || $range[0] >= $size || $range[0] > $range[1]) {
+                header('HTTP/1.1 416 Range Not Satisfiable');
+                echo "<h1>Range Not Satisfiable</h1>";
+                exit;
+            }
+        }
+
+        header('Content-Length: ' . $audioLength);
+
+        echo $audio;
+    }
+
+    /**
+     * Return the code from the session or database (if configured).  If none exists or was found, an empty string is returned.
+     *
+     * @param bool $array  true to receive an array containing the code and properties, false to receive just the code.
+     * @param bool $returnExisting If true, and the class property *code* is set, it will be returned instead of getting the code from the session or database.
+     * @return array|string Return is an array if $array = true, otherwise a string containing the code
+     */
+    public function getCode($array = false, $returnExisting = false)
+    {
+        $code = array();
+        $time = 0;
+        $disp = 'error';
+
+        if ($returnExisting && strlen($this->code) > 0) {
+            if ($array) {
+                return array(
+                    'code'         => $this->code,
+                    'display'      => $this->code_display,
+                    'code_display' => $this->code_display,
+                    'time'         => 0);
+            } else {
+                return $this->code;
+            }
+        }
+
+        if ($this->no_session != true) {
+            if (isset($_SESSION['securimage_code_value'][$this->namespace]) &&
+                    trim($_SESSION['securimage_code_value'][$this->namespace]) != '') {
+                if ($this->isCodeExpired(
+                        $_SESSION['securimage_code_ctime'][$this->namespace]) == false) {
+                    $code['code'] = $_SESSION['securimage_code_value'][$this->namespace];
+                    $code['time'] = $_SESSION['securimage_code_ctime'][$this->namespace];
+                    $code['display'] = $_SESSION['securimage_code_disp'] [$this->namespace];
+                }
+            }
+        }
+
+        if (empty($code) && $this->use_database) {
+            // no code in session - may mean user has cookies turned off
+            $this->openDatabase();
+            $code = $this->getCodeFromDatabase();
+
+            if (!empty($code)) {
+                $code['display'] = $code['code_disp'];
+                unset($code['code_disp']);
+            }
+        } else { /* no code stored in session or sqlite database, validation will fail */ }
+
+        if ($array == true) {
+            return $code;
+        } else {
+            return $code['code'];
+        }
+    }
+
+    /**
+     * The main image drawing routing, responsible for constructing the entire image and serving it
+     */
+    protected function doImage()
+    {
+        if($this->use_transparent_text == true || $this->bgimg != '' || function_exists('imagecreatetruecolor')) {
+            $imagecreate = 'imagecreatetruecolor';
+        } else {
+            $imagecreate = 'imagecreate';
+        }
+
+        $this->im = $imagecreate($this->image_width, $this->image_height);
+
+        if (function_exists('imageantialias')) {
+            imageantialias($this->im, true);
+        }
+
+        $this->allocateColors();
+
+        if ($this->perturbation > 0) {
+            $this->tmpimg = $imagecreate($this->image_width * $this->iscale, $this->image_height * $this->iscale);
+            imagepalettecopy($this->tmpimg, $this->im);
+        } else {
+            $this->iscale = 1;
+        }
+
+        $this->setBackground();
+
+        $code = '';
+
+        if ($this->getCaptchaId(false) !== null) {
+            // a captcha Id was supplied
+
+            // check to see if a display_value for the captcha image was set
+            if (is_string($this->display_value) && strlen($this->display_value) > 0) {
+                $this->code_display = $this->display_value;
+                $this->code         = ($this->case_sensitive) ?
+                                       $this->display_value   :
+                                       strtolower($this->display_value);
+                $code = $this->code;
+            } elseif ($this->openDatabase()) {
+                // no display_value, check the database for existing captchaId
+                $code = $this->getCodeFromDatabase();
+
+                // got back a result from the database with a valid code for captchaId
+                if (is_array($code)) {
+                    $this->code         = $code['code'];
+                    $this->code_display = $code['code_disp'];
+                    $code = $code['code'];
+                }
+            }
+        }
+
+        if ($code == '') {
+            // if the code was not set using display_value or was not found in
+            // the database, create a new code
+            $this->createCode();
+        }
+
+        if ($this->noise_level > 0) {
+            $this->drawNoise();
+        }
+
+        $this->drawWord();
+
+        if ($this->perturbation > 0 && is_readable($this->ttf_file)) {
+            $this->distortedCopy();
+        }
+
+        if ($this->num_lines > 0) {
+            $this->drawLines();
+        }
+
+        if (trim($this->image_signature) != '') {
+            $this->addSignature();
+        }
+
+        $this->output();
+    }
+
+    /**
+     * Allocate the colors to be used for the image
+     */
+    protected function allocateColors()
+    {
+        // allocate bg color first for imagecreate
+        $this->gdbgcolor = imagecolorallocate($this->im,
+                                              $this->image_bg_color->r,
+                                              $this->image_bg_color->g,
+                                              $this->image_bg_color->b);
+
+        $alpha = intval($this->text_transparency_percentage / 100 * 127);
+
+        if ($this->use_transparent_text == true) {
+            $this->gdtextcolor = imagecolorallocatealpha($this->im,
+                                                         $this->text_color->r,
+                                                         $this->text_color->g,
+                                                         $this->text_color->b,
+                                                         $alpha);
+            $this->gdlinecolor = imagecolorallocatealpha($this->im,
+                                                         $this->line_color->r,
+                                                         $this->line_color->g,
+                                                         $this->line_color->b,
+                                                         $alpha);
+            $this->gdnoisecolor = imagecolorallocatealpha($this->im,
+                                                          $this->noise_color->r,
+                                                          $this->noise_color->g,
+                                                          $this->noise_color->b,
+                                                          $alpha);
+        } else {
+            $this->gdtextcolor = imagecolorallocate($this->im,
+                                                    $this->text_color->r,
+                                                    $this->text_color->g,
+                                                    $this->text_color->b);
+            $this->gdlinecolor = imagecolorallocate($this->im,
+                                                    $this->line_color->r,
+                                                    $this->line_color->g,
+                                                    $this->line_color->b);
+            $this->gdnoisecolor = imagecolorallocate($this->im,
+                                                          $this->noise_color->r,
+                                                          $this->noise_color->g,
+                                                          $this->noise_color->b);
+        }
+
+        $this->gdsignaturecolor = imagecolorallocate($this->im,
+                                                     $this->signature_color->r,
+                                                     $this->signature_color->g,
+                                                     $this->signature_color->b);
+
+    }
+
+    /**
+     * The the background color, or background image to be used
+     */
+    protected function setBackground()
+    {
+        // set background color of image by drawing a rectangle since imagecreatetruecolor doesn't set a bg color
+        imagefilledrectangle($this->im, 0, 0,
+                             $this->image_width, $this->image_height,
+                             $this->gdbgcolor);
+
+        if ($this->perturbation > 0) {
+            imagefilledrectangle($this->tmpimg, 0, 0,
+                                 $this->image_width * $this->iscale, $this->image_height * $this->iscale,
+                                 $this->gdbgcolor);
+        }
+
+        if ($this->bgimg == '') {
+            if ($this->background_directory != null &&
+                is_dir($this->background_directory) &&
+                is_readable($this->background_directory))
+            {
+                $img = $this->getBackgroundFromDirectory();
+                if ($img != false) {
+                    $this->bgimg = $img;
+                }
+            }
+        }
+
+        if ($this->bgimg == '') {
+            return;
+        }
+
+        $dat = @getimagesize($this->bgimg);
+        if($dat == false) {
+            return;
+        }
+
+        switch($dat[2]) {
+            case 1:  $newim = @imagecreatefromgif($this->bgimg); break;
+            case 2:  $newim = @imagecreatefromjpeg($this->bgimg); break;
+            case 3:  $newim = @imagecreatefrompng($this->bgimg); break;
+            default: return;
+        }
+
+        if(!$newim) return;
+
+        imagecopyresized($this->im, $newim, 0, 0, 0, 0,
+                         $this->image_width, $this->image_height,
+                         imagesx($newim), imagesy($newim));
+    }
+
+    /**
+     * Scan the directory for a background image to use
+     * @return string|bool
+     */
+    protected function getBackgroundFromDirectory()
+    {
+        $images = array();
+
+        if ( ($dh = opendir($this->background_directory)) !== false) {
+            while (($file = readdir($dh)) !== false) {
+                if (preg_match('/(jpg|gif|png)$/i', $file)) $images[] = $file;
+            }
+
+            closedir($dh);
+
+            if (sizeof($images) > 0) {
+                return rtrim($this->background_directory, '/') . '/' . $images[mt_rand(0, sizeof($images)-1)];
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * This method generates a new captcha code.
+     *
+     * Generates a random captcha code based on *charset*, math problem, or captcha from the wordlist and saves the value to the session and/or database.
+     */
+    public function createCode()
+    {
+        $this->code = false;
+
+        switch($this->captcha_type) {
+            case self::SI_CAPTCHA_MATHEMATIC:
+            {
+                do {
+                    $signs = array('+', '-', 'x');
+                    $left  = mt_rand(1, 10);
+                    $right = mt_rand(1, 5);
+                    $sign  = $signs[mt_rand(0, 2)];
+
+                    switch($sign) {
+                        case 'x': $c = $left * $right; break;
+                        case '-': $c = $left - $right; break;
+                        default:  $c = $left + $right; break;
+                    }
+                } while ($c <= 0); // no negative #'s or 0
+
+                $this->code         = "$c";
+                $this->code_display = "$left $sign $right";
+                break;
+            }
+
+            case self::SI_CAPTCHA_WORDS:
+                $words = $this->readCodeFromFile(2);
+                $this->code = implode(' ', $words);
+                $this->code_display = $this->code;
+                break;
+
+            default:
+            {
+                if ($this->use_wordlist && is_readable($this->wordlist_file)) {
+                    $this->code = $this->readCodeFromFile();
+                }
+
+                if ($this->code == false) {
+                    $this->code = $this->generateCode($this->code_length);
+                }
+
+                $this->code_display = $this->code;
+                $this->code         = ($this->case_sensitive) ? $this->code : strtolower($this->code);
+            } // default
+        }
+
+        $this->saveData();
+    }
+
+    /**
+     * Draws the captcha code on the image
+     */
+    protected function drawWord()
+    {
+        $ratio = ($this->font_ratio) ? $this->font_ratio : 0.4;
+
+        if ((float)$ratio < 0.1 || (float)$ratio >= 1) {
+            $ratio = 0.4;
+        }
+
+        if (!is_readable($this->ttfFile())) {
+            // this will not catch missing fonts after the first!
+            $this->perturbation = 0;
+            imagestring($this->im, 4, 10, ($this->image_height / 2) - 5, 'Failed to load TTF font file!', $this->gdtextcolor);
+
+            return ;
+        }
+
+        if ($this->perturbation > 0) {
+            $width     = $this->image_width * $this->iscale;
+            $height    = $this->image_height * $this->iscale;
+            $font_size = $height * $ratio;
+            $im        = &$this->tmpimg;
+            $scale     = $this->iscale;
+        } else {
+            $height    = $this->image_height;
+            $width     = $this->image_width;
+            $font_size = $this->image_height * $ratio;
+            $im        = &$this->im;
+            $scale     = 1;
+        }
+
+        $captcha_text = $this->code_display;
+
+        if ($this->use_random_spaces && $this->strpos($captcha_text, ' ') === false) {
+            if (mt_rand(1, 100) % 5 > 0) { // ~20% chance no spacing added
+                $index  = mt_rand(1, $this->strlen($captcha_text) -1);
+                $spaces = mt_rand(1, 3);
+
+                // in general, we want all characters drawn close together to
+                // prevent easy segmentation by solvers, but this adds random
+                // spacing between two groups to make character positioning
+                // less normalized.
+
+                $captcha_text = sprintf(
+                    '%s%s%s',
+                    $this->substr($captcha_text, 0, $index),
+                    str_repeat(' ', $spaces),
+                    $this->substr($captcha_text, $index)
+                );
+            }
+        }
+
+        $fonts    = array();  // list of fonts corresponding to each char $i
+        $angles   = array();  // angles corresponding to each char $i
+        $distance = array();  // distance from current char $i to previous char
+        $dims     = array();  // dimensions of each individual char $i
+        $txtWid   = 0;        // width of the entire text string, including spaces and distances
+
+        // Character positioning and angle
+
+        $angle0 = mt_rand(10, 20);
+        $angleN = mt_rand(-20, 10);
+
+        if ($this->use_text_angles == false) {
+            $angle0 = $angleN = $step = 0;
+        }
+
+        if (mt_rand(0, 99) % 2 == 0) {
+            $angle0 = -$angle0;
+        }
+        if (mt_rand(0, 99) % 2 == 1) {
+            $angleN = -$angleN;
+        }
+
+        $step   = abs($angle0 - $angleN) / ($this->strlen($captcha_text) - 1);
+        $step   = ($angle0 > $angleN) ? -$step : $step;
+        $angle  = $angle0;
+
+        for ($c = 0; $c < $this->strlen($captcha_text); ++$c) {
+            $font     = $this->ttfFile(); // select random font from list for this character
+            $fonts[]  = $font;
+            $angles[] = $angle;  // the angle of this character
+            $dist     = mt_rand(-2, 0) * $scale; // random distance between this and next character
+            $distance[] = $dist;
+            $char     = $this->substr($captcha_text, $c, 1); // the character to draw for this sequence
+
+            $dim = $this->getCharacterDimensions($char, $font_size, $angle, $font); // calculate dimensions of this character
+
+            $dim[0] += $dist;   // add the distance to the dimension (negative to bring them closer)
+            $txtWid += $dim[0]; // increment width based on character width
+
+            $dims[] = $dim;
+
+            $angle += $step; // next angle
+
+            if ($angle > 20) {
+                $angle = 20;
+                $step  = $step * -1;
+            } elseif ($angle < -20) {
+                $angle = -20;
+                $step  = -1 * $step;
+            }
+        }
+
+        $nextYPos = function($y, $i, $step) use ($height, $scale, $dims) {
+            static $dir = 1;
+
+            if ($y + $step + $dims[$i][2] + (10 * $scale) > $height) {
+                $dir = 0;
+            } elseif ($y - $step - $dims[$i][2] < $dims[$i][1] + $dims[$i][2] + (5 * $scale)) {
+                $dir = 1;
+            }
+
+            if ($dir) {
+                $y += $step;
+            } else {
+                $y -= $step;
+            }
+
+            return $y;
+        };
+
+        $cx = floor($width / 2 - ($txtWid / 2));
+        $x  = mt_rand(5 * $scale, max($cx * 2 - (5 * $scale), 5 * $scale));
+
+        if ($this->use_random_baseline) {
+            $y = mt_rand($dims[0][1], $height - 10);
+        } else {
+            $y = ($height / 2 + $dims[0][1] / 2 - $dims[0][2]);
+        }
+
+        $st = $scale * mt_rand(5, 10);
+
+        for ($c = 0; $c < $this->strlen($captcha_text); ++$c) {
+            $font  = $fonts[$c];
+            $char  = $this->substr($captcha_text, $c, 1);
+            $angle = $angles[$c];
+            $dim   = $dims[$c];
+
+            if ($this->use_random_baseline) {
+                $y = $nextYPos($y, $c, $st);
+            }
+
+            imagettftext(
+                $im,
+                $font_size,
+                $angle,
+                (int)$x,
+                (int)$y,
+                $this->gdtextcolor,
+                $font,
+                $char
+            );
+
+            if ($this->use_random_boxes && strlen(trim($char)) && mt_rand(1,100) % 5 == 0) {
+                imagesetthickness($im, 3);
+                imagerectangle($im, $x, $y - $dim[1] + $dim[2], $x + $dim[0], $y + $dim[2], $this->gdtextcolor);
+            }
+
+            if ($c == ' ') {
+                $x += $dim[0];
+            } else {
+                $x += $dim[0] + $distance[$c];
+            }
+        }
+
+        // DEBUG
+        //$this->im = $im;
+        //$this->output();
+    }
+
+    /**
+     * Get the width and height (in points) of a character for a given font,
+     * angle, and size.
+     *
+     * @param string $char The character to get dimensions for
+     * @param number $size The font size, in points
+     * @param number $angle The angle of the text
+     * @return number[] A 3-element array representing the width, height and baseline of the text
+     */
+    protected function getCharacterDimensions($char, $size, $angle, $font)
+    {
+        $box = imagettfbbox($size, $angle, $font, $char);
+
+        return array($box[2] - $box[0], max($box[1] - $box[7], $box[5] - $box[3]), $box[1]);
+    }
+
+    /**
+     * Copies the captcha image to the final image with distortion applied
+     */
+    protected function distortedCopy()
+    {
+        $numpoles = 3;       // distortion factor
+        $px       = array(); // x coordinates of poles
+        $py       = array(); // y coordinates of poles
+        $rad      = array(); // radius of distortion from pole
+        $amp      = array(); // amplitude
+        $x        = ($this->image_width / 4); // lowest x coordinate of a pole
+        $maxX     = $this->image_width - $x;  // maximum x coordinate of a pole
+        $dx       = mt_rand($x / 10, $x);     // horizontal distance between poles
+        $y        = mt_rand(20, $this->image_height - 20);  // random y coord
+        $dy       = mt_rand(20, $this->image_height * 0.7); // y distance
+        $minY     = 20;                                     // minimum y coordinate
+        $maxY     = $this->image_height - 20;               // maximum y cooddinate
+
+        // make array of poles AKA attractor points
+        for ($i = 0; $i < $numpoles; ++ $i) {
+            $px[$i]  = ($x + ($dx * $i)) % $maxX;
+            $py[$i]  = ($y + ($dy * $i)) % $maxY + $minY;
+            $rad[$i] = mt_rand($this->image_height * 0.4, $this->image_height * 0.8);
+            $tmp     = ((- $this->frand()) * 0.15) - .15;
+            $amp[$i] = $this->perturbation * $tmp;
+        }
+
+        $bgCol   = imagecolorat($this->tmpimg, 0, 0);
+        $width2  = $this->iscale * $this->image_width;
+        $height2 = $this->iscale * $this->image_height;
+        imagepalettecopy($this->im, $this->tmpimg); // copy palette to final image so text colors come across
+
+        // loop over $img pixels, take pixels from $tmpimg with distortion field
+        for ($ix = 0; $ix < $this->image_width; ++ $ix) {
+            for ($iy = 0; $iy < $this->image_height; ++ $iy) {
+                $x = $ix;
+                $y = $iy;
+                for ($i = 0; $i < $numpoles; ++ $i) {
+                    $dx = $ix - $px[$i];
+                    $dy = $iy - $py[$i];
+                    if ($dx == 0 && $dy == 0) {
+                        continue;
+                    }
+                    $r = sqrt($dx * $dx + $dy * $dy);
+                    if ($r > $rad[$i]) {
+                        continue;
+                    }
+                    $rscale = $amp[$i] * sin(3.14 * $r / $rad[$i]);
+                    $x += $dx * $rscale;
+                    $y += $dy * $rscale;
+                }
+                $c = $bgCol;
+                $x *= $this->iscale;
+                $y *= $this->iscale;
+                if ($x >= 0 && $x < $width2 && $y >= 0 && $y < $height2) {
+                    $c = imagecolorat($this->tmpimg, $x, $y);
+                }
+                if ($c != $bgCol) { // only copy pixels of letters to preserve any background image
+                    imagesetpixel($this->im, $ix, $iy, $c);
+                }
+            }
+        }
+    }
+
+    /**
+     * Draws distorted lines on the image
+     */
+    protected function drawLines()
+    {
+        for ($line = 0; $line < $this->num_lines; ++ $line) {
+            $x = $this->image_width * (1 + $line) / ($this->num_lines + 1);
+            $x += (0.5 - $this->frand()) * $this->image_width / $this->num_lines;
+            $y = mt_rand($this->image_height * 0.1, $this->image_height * 0.9);
+
+            $theta = ($this->frand() - 0.5) * M_PI * 0.33;
+            $w = $this->image_width;
+            $len = mt_rand($w * 0.4, $w * 0.7);
+            $lwid = mt_rand(0, 2);
+
+            $k = $this->frand() * 0.6 + 0.2;
+            $k = $k * $k * 0.5;
+            $phi = $this->frand() * 6.28;
+            $step = 0.5;
+            $dx = $step * cos($theta);
+            $dy = $step * sin($theta);
+            $n = $len / $step;
+            $amp = 1.5 * $this->frand() / ($k + 5.0 / $len);
+            $x0 = $x - 0.5 * $len * cos($theta);
+            $y0 = $y - 0.5 * $len * sin($theta);
+
+            $ldx = round(- $dy * $lwid);
+            $ldy = round($dx * $lwid);
+
+            for ($i = 0; $i < $n; ++ $i) {
+                $x = $x0 + $i * $dx + $amp * $dy * sin($k * $i * $step + $phi);
+                $y = $y0 + $i * $dy - $amp * $dx * sin($k * $i * $step + $phi);
+                imagefilledrectangle($this->im, $x, $y, $x + $lwid, $y + $lwid, $this->gdlinecolor);
+            }
+        }
+    }
+
+    /**
+     * Draws random noise on the image
+     */
+    protected function drawNoise()
+    {
+        if ($this->noise_level > 10) {
+            $noise_level = 10;
+        } else {
+            $noise_level = $this->noise_level;
+        }
+
+        $t0 = microtime(true);
+        $noise_level *= M_LOG2E;
+
+        for ($x = 1; $x < $this->image_width; $x += 20) {
+            for ($y = 1; $y < $this->image_height; $y += 20) {
+                for ($i = 0; $i < $noise_level; ++$i) {
+                    $x1 = mt_rand($x, $x + 20);
+                    $y1 = mt_rand($y, $y + 20);
+                    $size = mt_rand(1, 3);
+
+                    if ($x1 - $size <= 0 && $y1 - $size <= 0) continue; // dont cover 0,0 since it is used by imagedistortedcopy
+                    imagefilledarc($this->im, $x1, $y1, $size, $size, 0, mt_rand(180,360), $this->gdlinecolor, IMG_ARC_PIE);
+                }
+            }
+        }
+
+        $t = microtime(true) - $t0;
+
+        /*
+        // DEBUG
+        imagestring($this->im, 5, 25, 30, "$t", $this->gdnoisecolor);
+        header('content-type: image/png');
+        imagepng($this->im);
+        exit;
+        */
+    }
+
+    /**
+    * Print signature text on image
+    */
+    protected function addSignature()
+    {
+        $bbox = imagettfbbox(10, 0, $this->signature_font, $this->image_signature);
+        $textlen = $bbox[2] - $bbox[0];
+        $x = $this->image_width - $textlen - 5;
+        $y = $this->image_height - 3;
+
+        imagettftext($this->im, 10, 0, $x, $y, $this->gdsignaturecolor, $this->signature_font, $this->image_signature);
+    }
+
+    /**
+     * Sends the appropriate image and cache headers and outputs image to the browser
+     */
+    protected function output()
+    {
+        if ($this->canSendHeaders() || $this->send_headers == false) {
+            if ($this->send_headers) {
+                // only send the content-type headers if no headers have been output
+                // this will ease debugging on misconfigured servers where warnings
+                // may have been output which break the image and prevent easily viewing
+                // source to see the error.
+                header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
+                header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT");
+                header("Cache-Control: no-store, no-cache, must-revalidate");
+                header("Cache-Control: post-check=0, pre-check=0", false);
+                header("Pragma: no-cache");
+            }
+
+            switch ($this->image_type) {
+                case self::SI_IMAGE_JPEG:
+                    if ($this->send_headers) header("Content-Type: image/jpeg");
+                    imagejpeg($this->im, null, 90);
+                    break;
+                case self::SI_IMAGE_GIF:
+                    if ($this->send_headers) header("Content-Type: image/gif");
+                    imagegif($this->im);
+                    break;
+                default:
+                    if ($this->send_headers) header("Content-Type: image/png");
+                    imagepng($this->im);
+                    break;
+            }
+        } else {
+            echo '<hr /><strong>'
+                .'Failed to generate captcha image, content has already been '
+                .'output.<br />This is most likely due to misconfiguration or '
+                .'a PHP error was sent to the browser.</strong>';
+        }
+
+        imagedestroy($this->im);
+        restore_error_handler();
+
+        if (!$this->no_exit) exit;
+    }
+
+    /**
+     * Generates an audio captcha in WAV format
+     *
+     * @return string The audio representation of the captcha in Wav format
+     */
+    protected function getAudibleCode()
+    {
+        $letters = array();
+        $code    = $this->getCode(true, true);
+
+        if (empty($code) || empty($code['code'])) {
+            if (strlen($this->display_value) > 0) {
+                $code = array('code' => $this->display_value, 'display' => $this->display_value);
+            } else {
+                $this->createCode();
+                $code = $this->getCode(true);
+            }
+        }
+
+        if (empty($code)) {
+            $error = 'Failed to get audible code (are database settings correct?).  Check the error log for details';
+            trigger_error($error, E_USER_WARNING);
+            throw new \Exception($error);
+        }
+
+        if (preg_match('/(\d+) (\+|-|x) (\d+)/i', $code['display'], $eq)) {
+            $math = true;
+
+            $left  = $eq[1];
+            $sign  = str_replace(array('+', '-', 'x'), array('plus', 'minus', 'times'), $eq[2]);
+            $right = $eq[3];
+
+            $letters = array($left, $sign, $right);
+        } else {
+            $math = false;
+
+            $length = $this->strlen($code['display']);
+
+            for($i = 0; $i < $length; ++$i) {
+                $letter    = $this->substr($code['display'], $i, 1);
+                $letters[] = $letter;
+            }
+        }
+
+        try {
+            return $this->generateWAV($letters);
+        } catch(\Exception $ex) {
+            throw $ex;
+        }
+    }
+
+    /**
+     * Gets a captcha code from a file containing a list of words.
+     *
+     * Seek to a random offset in the file and reads a block of data and returns a line from the file.
+     *
+     * @param int $numWords Number of words (lines) to read from the file
+     * @return string|array|bool  Returns a string if only one word is to be read, or an array of words
+     */
+    protected function readCodeFromFile($numWords = 1)
+    {
+        $strtolower_func = 'strtolower';
+        $mb_support      = false;
+
+        if (!empty($this->wordlist_file_encoding)) {
+            if (!extension_loaded('mbstring')) {
+                trigger_error("wordlist_file_encoding option set, but PHP does not have mbstring support", E_USER_WARNING);
+                return false;
+            }
+
+            // emits PHP warning if not supported
+            $mb_support = mb_internal_encoding($this->wordlist_file_encoding);
+
+            if (!$mb_support) {
+                return false;
+            }
+
+            $strtolower_func = 'mb_strtolower';
+        }
+
+        $fp = fopen($this->wordlist_file, 'rb');
+        if (!$fp) return false;
+
+        $fsize = filesize($this->wordlist_file);
+        if ($fsize < 128) return false; // too small of a list to be effective
+
+        if ((int)$numWords < 1 || (int)$numWords > 5) $numWords = 1;
+
+        $words = array();
+        $i = 0;
+        do {
+            fseek($fp, mt_rand(0, $fsize - 128), SEEK_SET); // seek to a random position of file from 0 to filesize-128
+            $data = fread($fp, 128); // read a chunk from our random position
+
+            if ($mb_support !== false) {
+                $data = mb_ereg_replace("\r?\n", "\n", $data);
+            } else {
+                $data = preg_replace("/\r?\n/", "\n", $data);
+            }
+
+            $start = @$this->strpos($data, "\n", mt_rand(0, 56)) + 1; // random start position
+            $end   = @$this->strpos($data, "\n", $start);          // find end of word
+
+            if ($start === false) {
+                // picked start position at end of file
+                continue;
+            } else if ($end === false) {
+                $end = $this->strlen($data);
+            }
+
+            $word = $strtolower_func($this->substr($data, $start, $end - $start)); // return a line of the file
+
+            if ($mb_support) {
+                // convert to UTF-8 for imagettftext
+                $word = mb_convert_encoding($word, 'UTF-8', $this->wordlist_file_encoding);
+            }
+
+            $words[] = $word;
+        } while (++$i < $numWords);
+
+        fclose($fp);
+
+        if ($numWords < 2) {
+            return $words[0];
+        } else {
+            return $words;
+        }
+    }
+
+    /**
+     * Generates a random captcha code from the set character set
+     *
+     * @see Securimage::$charset  Charset option
+     * @return string A randomly generated CAPTCHA code
+     */
+    protected function generateCode()
+    {
+        $code = '';
+
+        for($i = 1, $cslen = $this->strlen($this->charset); $i <= $this->code_length; ++$i) {
+            $code .= $this->substr($this->charset, mt_rand(0, $cslen - 1), 1);
+        }
+
+        return $code;
+    }
+
+    /**
+     * Validate a code supplied by the user
+     *
+     * Checks the entered code against the value stored in the session and/or database (if configured).  Handles case sensitivity.
+     * Also removes the code from session/database if the code was entered correctly to prevent re-use attack.
+     *
+     * This function does not return a value.
+     *
+     * @see Securimage::$correct_code 'correct_code' property
+     */
+    protected function validate()
+    {
+        if (!is_string($this->code) || strlen($this->code) == 0) {
+            $code = $this->getCode(true);
+            // returns stored code, or an empty string if no stored code was found
+            // checks the session and database if enabled
+        } else {
+            $code = $this->code;
+        }
+
+        if (is_array($code)) {
+            if (!empty($code)) {
+                $ctime = $code['time'];
+                $code  = $code['code'];
+
+                $this->_timeToSolve = time() - $ctime;
+            } else {
+                $code = '';
+            }
+        }
+
+        if ($this->case_sensitive == false && preg_match('/[A-Z]/', $code)) {
+            // case sensitive was set from securimage_show.php but not in class
+            // the code saved in the session has capitals so set case sensitive to true
+            $this->case_sensitive = true;
+        }
+
+        $code_entered = trim( (($this->case_sensitive) ? $this->code_entered
+                                                       : strtolower($this->code_entered))
+                        );
+        $this->correct_code = false;
+
+        if ($code != '') {
+            if (strpos($code, ' ') !== false) {
+                // for multi word captchas, remove more than once space from input
+                $code_entered = preg_replace('/\s+/', ' ', $code_entered);
+                $code_entered = strtolower($code_entered);
+            }
+
+            if ((string)$code === (string)$code_entered) {
+                $this->correct_code = true;
+                if ($this->no_session != true) {
+                    $_SESSION['securimage_code_disp'] [$this->namespace] = '';
+                    $_SESSION['securimage_code_value'][$this->namespace] = '';
+                    $_SESSION['securimage_code_ctime'][$this->namespace] = '';
+                    $_SESSION['securimage_code_audio'][$this->namespace] = '';
+                }
+                $this->clearCodeFromDatabase();
+            }
+        }
+    }
+
+    /**
+     * Save CAPTCHA data to session and database (if configured)
+     */
+    protected function saveData()
     {
-        // Constructor's functionality here, if you have any.
+        if ($this->no_session != true) {
+            if (isset($_SESSION['securimage_code_value']) && is_scalar($_SESSION['securimage_code_value'])) {
+                // fix for migration from v2 - v3
+                unset($_SESSION['securimage_code_value']);
+                unset($_SESSION['securimage_code_ctime']);
+            }
+
+            $_SESSION['securimage_code_disp'] [$this->namespace] = $this->code_display;
+            $_SESSION['securimage_code_value'][$this->namespace] = $this->code;
+            $_SESSION['securimage_code_ctime'][$this->namespace] = time();
+            $_SESSION['securimage_code_audio'][$this->namespace] = null; // clear previous audio, if set
+        }
+
+        if ($this->use_database) {
+            $this->saveCodeToDatabase();
+        }
     }
-	/**
-	 * Class constructor.<br />
-	 * Because the class uses sessions, this will attempt to start a session if there is no previous one.<br />
-	 * If you do not start a session before calling the class, the constructor must be called before any
-	 * output is sent to the browser.
-	 *
-	 * <code>
-	 *   $securimage = new Securimage();
-	 * </code>
-	 *
-	 */
-	function Securimage()
-	{
-		// Initialize session or attach to existing
-		if ( session_id() == '' ) { // no session has been started yet, which is needed for validation
-			if (trim($this->session_name) != '') {
-				session_name($this->session_name); // set session name if provided
-			}
-			session_start();
-		}
-
-		// Set Default Values
-		$this->image_width   = 230;
-		$this->image_height  = 80;
-		$this->image_type    = SI_IMAGE_PNG;
-
-		$this->code_length   = 6;
-		$this->charset       = 'ABCDEFGHKLMNPRSTUVWYZabcdefghklmnprstuvwyz23456789';
-		$this->wordlist_file = './words/words.txt';
-		$this->use_wordlist  = false;
-
-		$this->gd_font_file  = 'gdfonts/automatic.gdf';
-		$this->use_gd_font   = false;
-		$this->gd_font_size  = 24;
-		$this->text_x_start  = 15;
-
-		$this->ttf_file      = './AHGBold.ttf';
-
-		$this->perturbation       = 0.75;
-		$this->iscale             = 5;
-		$this->text_angle_minimum = 0;
-		$this->text_angle_maximum = 0;
-
-		$this->image_bg_color   = new Securimage_Color(0xff, 0xff, 0xff);
-    $this->text_color       = new Securimage_Color(0x3d, 0x3d, 0x3d);
-		$this->multi_text_color = array(new Securimage_Color(0x0, 0x20, 0xCC),
-																		new Securimage_Color(0x0, 0x30, 0xEE),
-																		new Securimage_color(0x0, 0x40, 0xCC),
-																		new Securimage_Color(0x0, 0x50, 0xEE),
-																		new Securimage_Color(0x0, 0x60, 0xCC));
-		$this->use_multi_text   = false;
-
-		$this->use_transparent_text         = false;
-		$this->text_transparency_percentage = 30;
-
-		$this->num_lines            = 10;
-		$this->line_color           = new Securimage_Color(0x3d, 0x3d, 0x3d);
-		$this->draw_lines_over_text = true;
-
-		$this->image_signature = '';
-		$this->signature_color = new Securimage_Color(0x20, 0x50, 0xCC);
-		$this->signature_font  = './AHGBold.ttf';
-
-		$this->audio_path   = './audio/';
-		$this->audio_format = 'mp3';
-		$this->session_name = '';
-		$this->expiry_time  = 900;
-		
-		$this->sqlite_database = 'database/securimage.sqlite';
-		$this->use_sqlite_db   = false;
-		
-		$this->sqlite_handle = false;
-		self::__construct();
-	}
-
-	/**
-	 * Generate a code and output the image to the browser.
-	 *
-	 * <code>
-	 *   <?php
-	 *   include 'securimage.php';
-	 *   $securimage = new Securimage();
-	 *   $securimage->show('bg.jpg');
-	 *   ?>
-	 * </code>
-	 *
-	 * @param string $background_image  The path to an image to use as the background for the CAPTCHA
-	 */
-	function show($background_image = "")
-	{
-		if($background_image != "" && is_readable($background_image)) {
-			$this->bgimg = $background_image;
-		}
-
-		$this->doImage();
-	}
-
-	/**
-	 * Validate the code entered by the user.
-	 *
-	 * <code>
-	 *   $code = $_POST['code'];
-	 *   if ($securimage->check($code) == false) {
-	 *     die("Sorry, the code entered did not match.");
-	 *   } else {
-	 *     $valid = true;
-	 *   }
-	 * </code>
-	 * @param string $code  The code the user entered
-	 * @return boolean  true if the code was correct, false if not
-	 */
-	function check($code)
-	{
-		$this->code_entered = $code;
-		$this->validate();
-		return $this->correct_code;
-	}
-
-	/**
-	 * Output audio file with HTTP headers to browser
-	 * 
-	 * <code>
-	 *   $sound = new Securimage();
-	 *   $sound->audio_format = 'mp3';
-	 *   $sound->outputAudioFile();
-	 * </code>
-	 * 
-	 * @since 2.0
-	 */
-	function outputAudioFile()
-	{
-		if (strtolower($this->audio_format) == 'wav') {
-			header('Content-type: audio/x-wav');
-			$ext = 'wav';
-		} else {
-			header('Content-type: audio/mpeg'); // default to mp3
-			$ext = 'mp3';
-		}
-
-		header("Content-Disposition: attachment; filename=\"securimage_audio.{$ext}\"");
-		header('Cache-Control: no-store, no-cache, must-revalidate');
-		header('Expires: Sun, 1 Jan 2000 12:00:00 GMT');
-		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT');
-
-		$audio = $this->getAudibleCode($ext);
-
-		header('Content-Length: ' . strlen($audio));
-
-		echo $audio;
-		exit;
-	}
-
-	/**
-	 * Generate and output the image
-	 *
-	 * @access private
-	 *
-	 */
-	function doImage()
-	{
-		if ($this->use_gd_font == true) {
-			$this->iscale = 1;
-		}
-		if($this->use_transparent_text == true || $this->bgimg != "") {
-			$this->im     = imagecreatetruecolor($this->image_width, $this->image_height);
-			$this->tmpimg = imagecreatetruecolor($this->image_width * $this->iscale, $this->image_height * $this->iscale);
-
-		} else { //no transparency
-			$this->im     = imagecreate($this->image_width, $this->image_height);
-			$this->tmpimg = imagecreate($this->image_width * $this->iscale, $this->image_height * $this->iscale);
-		}
-		
-		$this->allocateColors();
-		imagepalettecopy($this->tmpimg, $this->im);
-
-		$this->setBackground();
-
-		$this->createCode();
-
-		if (!$this->draw_lines_over_text && $this->num_lines > 0) $this->drawLines();
-
-		$this->drawWord();
-		if ($this->use_gd_font == false && is_readable($this->ttf_file)) $this->distortedCopy();
-
-		if ($this->draw_lines_over_text && $this->num_lines > 0) $this->drawLines();
-
-		if (trim($this->image_signature) != '')	$this->addSignature();
-
-		$this->output();
-
-	}
-	
-	/**
-	 * Allocate all colors that will be used in the CAPTCHA image
-	 * 
-	 * @since 2.0.1
-	 * @access private
-	 */
-	function allocateColors()
-	{
-		// allocate bg color first for imagecreate
-		$this->gdbgcolor = imagecolorallocate($this->im, $this->image_bg_color->r, $this->image_bg_color->g, $this->image_bg_color->b);
-		
-		$alpha = intval($this->text_transparency_percentage / 100 * 127);
-		
-		if ($this->use_transparent_text == true) {
-      $this->gdtextcolor = imagecolorallocatealpha($this->im, $this->text_color->r, $this->text_color->g, $this->text_color->b, $alpha);
-      $this->gdlinecolor = imagecolorallocatealpha($this->im, $this->line_color->r, $this->line_color->g, $this->line_color->b, $alpha);
-		} else {
-			$this->gdtextcolor = imagecolorallocate($this->im, $this->text_color->r, $this->text_color->g, $this->text_color->b);
-      $this->gdlinecolor = imagecolorallocate($this->im, $this->line_color->r, $this->line_color->g, $this->line_color->b);
-		}
-    
-    $this->gdsignaturecolor = imagecolorallocate($this->im, $this->signature_color->r, $this->signature_color->g, $this->signature_color->b);
-    
-    if ($this->use_multi_text == true) {
-    	$this->gdmulticolor = array();
-    	
-    	foreach($this->multi_text_color as $color) {
-    		if ($this->use_transparent_text == true) {
-    		  $this->gdmulticolor[] = imagecolorallocatealpha($this->im, $color->r, $color->g, $color->b, $alpha);
-    		} else {
-    			$this->gdmulticolor[] = imagecolorallocate($this->im, $color->r, $color->g, $color->b);
-    		}
-    	}
+
+    /**
+     * Save audio data to session and/or the configured database
+     *
+     * @param string $data The CAPTCHA audio data
+     */
+    protected function saveAudioData($data)
+    {
+        if ($this->no_session != true) {
+            $_SESSION['securimage_code_audio'][$this->namespace] = $data;
+        }
+
+        if ($this->use_database) {
+            $this->saveAudioToDatabase($data);
+        }
     }
-	}
-
-	/**
-	 * Set the background of the CAPTCHA image
-	 *
-	 * @access private
-	 *
-	 */
-	function setBackground()
-	{
-		imagefilledrectangle($this->im, 0, 0, $this->image_width * $this->iscale, $this->image_height * $this->iscale, $this->gdbgcolor);
-    imagefilledrectangle($this->tmpimg, 0, 0, $this->image_width * $this->iscale, $this->image_height * $this->iscale, $this->gdbgcolor);
-    
-		if ($this->bgimg == '') {
-			if ($this->background_directory != null && is_dir($this->background_directory) && is_readable($this->background_directory)) {
-				$img = $this->getBackgroundFromDirectory();
-				if ($img != false) {
-					$this->bgimg = $img;
-				}
-			}
-		}
-
-		$dat = @getimagesize($this->bgimg);
-		if($dat == false) { 
-			return;
-		}
-
-		switch($dat[2]) {
-			case 1:  $newim = @imagecreatefromgif($this->bgimg); break;
-			case 2:  $newim = @imagecreatefromjpeg($this->bgimg); break;
-			case 3:  $newim = @imagecreatefrompng($this->bgimg); break;
-			case 15: $newim = @imagecreatefromwbmp($this->bgimg); break;
-			case 16: $newim = @imagecreatefromxbm($this->bgimg); break;
-			default: return;
-		}
-
-		if(!$newim) return;
-
-		imagecopyresized($this->im, $newim, 0, 0, 0, 0, $this->image_width, $this->image_height, imagesx($newim), imagesy($newim));
-	}
-
-	/**
-	 * Return the full path to a random gif, jpg, or png from the background directory.
-	 *
-	 * @access private
-	 * @see Securimage::$background_directory
-	 * @return mixed  false if none found, string $path if found
-	 */
-	function getBackgroundFromDirectory()
-	{
-		$images = array();
-
-		if ($dh = opendir($this->background_directory)) {
-			while (($file = readdir($dh)) !== false) {
-				if (preg_match('/(jpg|gif|png)$/i', $file)) $images[] = $file;
-			}
-
-			closedir($dh);
-
-			if (sizeof($images) > 0) {
-				return rtrim($this->background_directory, '/') . '/' . $images[rand(0, sizeof($images)-1)];
-			}
-		}
-
-		return false;
-	}
-
-	/**
-	 * Draw random curvy lines over the image<br />
-	 * Modified code from HKCaptcha
-	 *
-	 * @since 2.0
-	 * @access private
-	 *
-	 */
-	function drawLines()
-	{
-		for ($line = 0; $line < $this->num_lines; ++$line) {
-			$x = $this->image_width * (1 + $line) / ($this->num_lines + 1);
-			$x += (0.5 - $this->frand()) * $this->image_width / $this->num_lines;
-			$y = rand($this->image_height * 0.1, $this->image_height * 0.9);
-			 
-			$theta = ($this->frand()-0.5) * M_PI * 0.7;
-			$w = $this->image_width;
-			$len = rand($w * 0.4, $w * 0.7);
-			$lwid = rand(0, 2);
-			 
-			$k = $this->frand() * 0.6 + 0.2;
-			$k = $k * $k * 0.5;
-			$phi = $this->frand() * 6.28;
-			$step = 0.5;
-			$dx = $step * cos($theta);
-			$dy = $step * sin($theta);
-			$n = $len / $step;
-			$amp = 1.5 * $this->frand() / ($k + 5.0 / $len);
-			$x0 = $x - 0.5 * $len * cos($theta);
-			$y0 = $y - 0.5 * $len * sin($theta);
-			 
-			$ldx = round(-$dy * $lwid);
-			$ldy = round($dx * $lwid);
-			 
-			for ($i = 0; $i < $n; ++$i) {
-				$x = $x0 + $i * $dx + $amp * $dy * sin($k * $i * $step + $phi);
-				$y = $y0 + $i * $dy - $amp * $dx * sin($k * $i * $step + $phi);
-				imagefilledrectangle($this->im, $x, $y, $x + $lwid, $y + $lwid, $this->gdlinecolor);
-			}
-		}
-	}
-
-	/**
-	 * Draw the CAPTCHA code over the image
-	 *
-	 * @access private
-	 *
-	 */
-	function drawWord()
-	{
-		$width2 = $this->image_width * $this->iscale;
-		$height2 = $this->image_height * $this->iscale;
-		 
-		if ($this->use_gd_font == true || !is_readable($this->ttf_file)) {
-			if (!is_int($this->gd_font_file)) { //is a file name
-				$font = @imageloadfont($this->gd_font_file);
-				if ($font == false) {
-					trigger_error("Failed to load GD Font file {$this->gd_font_file} ", E_USER_WARNING);
-					return;
-				}
-			} else { //gd font identifier
-				$font = $this->gd_font_file;
-			}
-
-			imagestring($this->im, $font, $this->text_x_start, ($this->image_height / 2) - ($this->gd_font_size / 2), $this->code, $this->gdtextcolor);
-		} else { //ttf font
-			$font_size = $height2 * .35;
-			$bb = imagettfbbox($font_size, 0, $this->ttf_file, $this->code);
-			$tx = $bb[4] - $bb[0];
-			$ty = $bb[5] - $bb[1];
-			$x  = floor($width2 / 2 - $tx / 2 - $bb[0]);
-			$y  = round($height2 / 2 - $ty / 2 - $bb[1]);
-
-			$strlen = strlen($this->code);
-			if (!is_array($this->multi_text_color)) $this->use_multi_text = false;
-
-
-			if ($this->use_multi_text == false && $this->text_angle_minimum == 0 && $this->text_angle_maximum == 0) { // no angled or multi-color characters
-				imagettftext($this->tmpimg, $font_size, 0, $x, $y, $this->gdtextcolor, $this->ttf_file, $this->code);
-			} else {
-				for($i = 0; $i < $strlen; ++$i) {
-					$angle = rand($this->text_angle_minimum, $this->text_angle_maximum);
-					$y = rand($y - 5, $y + 5);
-					if ($this->use_multi_text == true) {
-						$font_color = $this->gdmulticolor[rand(0, sizeof($this->gdmulticolor) - 1)];
-					} else {
-						$font_color = $this->gdtextcolor;
-					}
-					
-					$ch = $this->code{$i};
-					 
-					imagettftext($this->tmpimg, $font_size, $angle, $x, $y, $font_color, $this->ttf_file, $ch);
-					 
-					// estimate character widths to increment $x without creating spaces that are too large or too small
-					// these are best estimates to align text but may vary between fonts
-					// for optimal character widths, do not use multiple text colors or character angles and the complete string will be written by imagettftext
-					if (strpos('abcdeghknopqsuvxyz', $ch) !== false) {
-						$min_x = $font_size - ($this->iscale * 6);
-						$max_x = $font_size - ($this->iscale * 6);
-					} else if (strpos('ilI1', $ch) !== false) {
-						$min_x = $font_size / 5;
-						$max_x = $font_size / 3;
-					} else if (strpos('fjrt', $ch) !== false) {
-						$min_x = $font_size - ($this->iscale * 12);
-						$max_x = $font_size - ($this->iscale * 12);
-					} else if ($ch == 'wm') {
-						$min_x = $font_size;
-						$max_x = $font_size + ($this->iscale * 3);
-					} else { // numbers, capitals or unicode
-						$min_x = $font_size + ($this->iscale * 2);
-						$max_x = $font_size + ($this->iscale * 5);
-					}
-					 
-					$x += rand($min_x, $max_x);
-				} //for loop
-			} // angled or multi-color
-		} //else ttf font
-		//$this->im = $this->tmpimg;
-		//$this->output();
-	} //function
-
-	/**
-	 * Warp text from temporary image onto final image.<br />
-	 * Modified for securimage
-	 *
-	 * @access private
-	 * @since 2.0
-	 * @author Han-Kwang Nienhuys modified
-	 * @copyright Han-Kwang Neinhuys
-	 *
-	 */
-	function distortedCopy()
-	{
-		$numpoles = 3; // distortion factor
-		 
-		// make array of poles AKA attractor points
-		for ($i = 0; $i < $numpoles; ++$i) {
-			$px[$i]  = rand($this->image_width * 0.3, $this->image_width * 0.7);
-			$py[$i]  = rand($this->image_height * 0.3, $this->image_height * 0.7);
-			$rad[$i] = rand($this->image_width * 0.4, $this->image_width * 0.7);
-			$tmp     = -$this->frand() * 0.15 - 0.15;
-			$amp[$i] = $this->perturbation * $tmp;
-		}
-		 
-		$bgCol   = imagecolorat($this->tmpimg, 0, 0);
-		$width2  = $this->iscale * $this->image_width;
-		$height2 = $this->iscale * $this->image_height;
-		 
-		imagepalettecopy($this->im, $this->tmpimg); // copy palette to final image so text colors come across
-		 
-		// loop over $img pixels, take pixels from $tmpimg with distortion field
-		for ($ix = 0; $ix < $this->image_width; ++$ix) {
-			for ($iy = 0; $iy < $this->image_height; ++$iy) {
-				$x = $ix;
-				$y = $iy;
-					
-				for ($i = 0; $i < $numpoles; ++$i) {
-					$dx = $ix - $px[$i];
-					$dy = $iy - $py[$i];
-					if ($dx == 0 && $dy == 0) continue;
-
-					$r = sqrt($dx * $dx + $dy * $dy);
-					if ($r > $rad[$i]) continue;
-
-					$rscale = $amp[$i] * sin(3.14 * $r / $rad[$i]);
-					$x += $dx * $rscale;
-					$y += $dy * $rscale;
-				}
-					
-				$c = $bgCol;
-				$x *= $this->iscale;
-				$y *= $this->iscale;
-
-				if ($x >= 0 && $x < $width2 && $y >= 0 && $y < $height2) {
-					$c = imagecolorat($this->tmpimg, $x, $y);
-				}
-
-				if ($c != $bgCol) { // only copy pixels of letters to preserve any background image
-					imagesetpixel($this->im, $ix, $iy, $c);
-				}
-			}
-		}
-	}
-
-	/**
-	 * Create a code and save to the session
-	 *
-	 * @access private
-	 * @since 1.0.1
-	 *
-	 */
-	function createCode()
-	{
-		$this->code = false;
-
-		if ($this->use_wordlist && is_readable($this->wordlist_file)) {
-			$this->code = $this->readCodeFromFile();
-		}
-
-		if ($this->code == false) {
-			$this->code = $this->generateCode($this->code_length);
-		}
-		
-		$this->saveData();
-	}
-
-	/**
-	 * Generate a code
-	 *
-	 * @access private
-	 * @param int $len  The code length
-	 * @return string
-	 */
-	function generateCode($len)
-	{
-		$code = '';
-
-		for($i = 1, $cslen = strlen($this->charset); $i <= $len; ++$i) {
-			$code .= $this->charset{rand(0, $cslen - 1)};
-		}
-		return $code;
-	}
-
-	/**
-	 * Reads a word list file to get a code
-	 *
-	 * @access private
-	 * @since 1.0.2
-	 * @return mixed  false on failure, a word on success
-	 */
-	function readCodeFromFile()
-	{
-		$fp = @fopen($this->wordlist_file, 'rb');
-		if (!$fp) return false;
-
-		$fsize = filesize($this->wordlist_file);
-		if ($fsize < 32) return false; // too small of a list to be effective
-
-		if ($fsize < 128) {
-			$max = $fsize; // still pretty small but changes the range of seeking
-		} else {
-			$max = 128;
-		}
-
-		fseek($fp, rand(0, $fsize - $max), SEEK_SET);
-		$data = fread($fp, 128); // read a random 128 bytes from file
-		fclose($fp);
-		$data = preg_replace("/\r?\n/", "\n", $data);
-
-		$start = strpos($data, "\n", rand(0, 100)) + 1; // random start position
-		$end   = strpos($data, "\n", $start);           // find end of word
-
-		return strtolower(substr($data, $start, $end - $start)); // return substring in 128 bytes
-	}
-
-	/**
-	 * Output image to the browser
-	 *
-	 * @access private
-	 *
-	 */
-	function output()
-	{
-		header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
-		header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT");
-		header("Cache-Control: no-store, no-cache, must-revalidate");
-		header("Cache-Control: post-check=0, pre-check=0", false);
-		header("Pragma: no-cache");
-
-		switch($this->image_type)
-		{
-			case SI_IMAGE_JPEG:
-				header("Content-Type: image/jpeg");
-				imagejpeg($this->im, null, 90);
-				break;
-
-			case SI_IMAGE_GIF:
-				header("Content-Type: image/gif");
-				imagegif($this->im);
-				break;
-
-			default:
-				header("Content-Type: image/png");
-				imagepng($this->im);
-				break;
-		}
-
-		imagedestroy($this->im);
-		exit;
-	}
-
-	/**
-	 * Get WAV or MP3 file data of the spoken code.<br />
-	 * This is appropriate for output to the browser as audio/x-wav or audio/mpeg
-	 *
-	 * @since 1.0.1
-	 * @return string  WAV or MP3 data
-	 *
-	 */
-	function getAudibleCode($format = 'wav')
-	{
-		$letters = array();
-		$code    = $this->getCode();
-
-		if ($code == '') {
-			$this->createCode();
-			$code = $this->getCode();
-		}
-
-		for($i = 0; $i < strlen($code); ++$i) {
-			$letters[] = $code{$i};
-		}
-
-		if ($format == 'mp3') {
-			return $this->generateMP3($letters);
-		} else {
-			return $this->generateWAV($letters);
-		}
-	}
-
-	/**
-	 * Set the path to the audio directory.<br />
-	 *
-	 * @since 1.0.4
-	 * @return bool true if the directory exists and is readble, false if not
-	 */
-	function setAudioPath($audio_directory)
-	{
-		if (is_dir($audio_directory) && is_readable($audio_directory)) {
-			$this->audio_path = $audio_directory;
-			return true;
-		} else {
-			return false;
-		}
-	}
-
-	/**
-	 * Save the code in the session
-	 *
-	 * @access private
-	 *
-	 */
-	function saveData()
-	{
-		$_SESSION['securimage_code_value'] = strtolower($this->code);
-		$_SESSION['securimage_code_ctime'] = time();
-		
-		$this->saveCodeToDatabase();
-	}
-
-	/**
-	 * Validate the code to the user code
-	 *
-	 * @access private
-	 *
-	 */
-	function validate()
-	{
-		// retrieve code from session, if no code exists check sqlite database if supported.
-		
-		if (isset($_SESSION['securimage_code_value']) && trim($_SESSION['securimage_code_value']) != '') {
-			if ($this->isCodeExpired($_SESSION['securimage_code_ctime']) == false) { 
-			  $code = $_SESSION['securimage_code_value'];
-			}
-		} else if ($this->use_sqlite_db == true && function_exists('sqlite_open')) { // no code in session - may mean user has cookies turned off
-			$this->openDatabase();
-			$code = $this->getCodeFromDatabase();
-		} else {
-			// session code invalid or non-existant and code not found in sqlite db or sqlite is not available
-			$code = '';
-		}
-		
-		$code               = trim(strtolower($code));
-		$code_entered       = trim(strtolower($this->code_entered));
-		$this->correct_code = false;
-		
-		if ($code != '') {
-			if ($code == $code_entered) {
-			  $this->correct_code = true;
-			  $_SESSION['securimage_code_value'] = '';
-			  $_SESSION['securimage_code_ctime'] = '';
-			  $this->clearCodeFromDatabase();
-		  }
-		}
-	}
-
-	/**
-	 * Get the captcha code
-	 *
-	 * @since 1.0.1
-	 * @return string
-	 */
-	function getCode()
-	{
-		if (isset($_SESSION['securimage_code_value']) && !empty($_SESSION['securimage_code_value'])) {
-			return strtolower($_SESSION['securimage_code_value']);
-		} else {
-			if ($this->sqlite_handle == false) $this->openDatabase();
-			
-			return $this->getCodeFromDatabase(); // attempt to get from database, returns empty string if sqlite is not available or disabled
-		}
-	}
-
-	/**
-	 * Check if the user entered code was correct
-	 *
-	 * @access private
-	 * @return boolean
-	 */
-	function checkCode()
-	{
-		return $this->correct_code;
-	}
-
-	/**
-	 * Generate a wav file by concatenating individual files
-	 *
-	 * @since 1.0.1
-	 * @access private
-	 * @param array $letters  Array of letters to build a file from
-	 * @return string  WAV file data
-	 */
-	function generateWAV($letters)
-	{
-		$data_len    = 0;
-		$files       = array();
-		$out_data    = '';
-
-		foreach ($letters as $letter) {
-			$filename = $this->audio_path . strtoupper($letter) . '.wav';
-
-			$fp = fopen($filename, 'rb');
-
-			$file = array();
-
-			$data = fread($fp, filesize($filename)); // read file in
-
-			$header = substr($data, 0, 36);
-			$body   = substr($data, 44);
-
-
-			$data = unpack('NChunkID/VChunkSize/NFormat/NSubChunk1ID/VSubChunk1Size/vAudioFormat/vNumChannels/VSampleRate/VByteRate/vBlockAlign/vBitsPerSample', $header);
-
-			$file['sub_chunk1_id']   = $data['SubChunk1ID'];
-			$file['bits_per_sample'] = $data['BitsPerSample'];
-			$file['channels']        = $data['NumChannels'];
-			$file['format']          = $data['AudioFormat'];
-			$file['sample_rate']     = $data['SampleRate'];
-			$file['size']            = $data['ChunkSize'] + 8;
-			$file['data']            = $body;
-
-			if ( ($p = strpos($file['data'], 'LIST')) !== false) {
-				// If the LIST data is not at the end of the file, this will probably break your sound file
-				$info         = substr($file['data'], $p + 4, 8);
-				$data         = unpack('Vlength/Vjunk', $info);
-				$file['data'] = substr($file['data'], 0, $p);
-				$file['size'] = $file['size'] - (strlen($file['data']) - $p);
-			}
-
-			$files[] = $file;
-			$data    = null;
-			$header  = null;
-			$body    = null;
-
-			$data_len += strlen($file['data']);
-
-			fclose($fp);
-		}
-
-		$out_data = '';
-		for($i = 0; $i < sizeof($files); ++$i) {
-			if ($i == 0) { // output header
-				$out_data .= pack('C4VC8', ord('R'), ord('I'), ord('F'), ord('F'), $data_len + 36, ord('W'), ord('A'), ord('V'), ord('E'), ord('f'), ord('m'), ord('t'), ord(' '));
-
-				$out_data .= pack('VvvVVvv',
-				16,
-				$files[$i]['format'],
-				$files[$i]['channels'],
-				$files[$i]['sample_rate'],
-				$files[$i]['sample_rate'] * (($files[$i]['bits_per_sample'] * $files[$i]['channels']) / 8),
-				($files[$i]['bits_per_sample'] * $files[$i]['channels']) / 8,
-				$files[$i]['bits_per_sample'] );
-
-				$out_data .= pack('C4', ord('d'), ord('a'), ord('t'), ord('a'));
-
-				$out_data .= pack('V', $data_len);
-			}
-
-			$out_data .= $files[$i]['data'];
-		}
-
-		$this->scrambleAudioData($out_data, 'wav');
-		return $out_data;
-	}
-
-	/**
-	 * Randomly modify the audio data to scramble sound and prevent binary recognition.<br />
-	 * Take care not to "break" the audio file by leaving the header data intact.
-	 *
-	 * @since 2.0
-	 * @access private
-	 * @param $data Sound data in mp3 of wav format
-	 */
-	function scrambleAudioData(&$data, $format)
-	{
-		if ($format == 'wav') {
-			$start = strpos($data, 'data') + 4; // look for "data" indicator
-			if ($start === false) $start = 44;  // if not found assume 44 byte header
-		} else { // mp3
-			$start = 4; // 4 byte (32 bit) frame header
-		}
-		 
-		$start  += rand(1, 64); // randomize starting offset
-		$datalen = strlen($data) - $start - 256; // leave last 256 bytes unchanged
-		 
-		for ($i = $start; $i < $datalen; $i += 64) {
-			$ch = ord($data{$i});
-			if ($ch < 9 || $ch > 119) continue;
-
-			$data{$i} = chr($ch + rand(-8, 8));
-		}
-	}
-
-	/**
-	 * Generate an mp3 file by concatenating individual files
-	 * @since 1.0.4
-	 * @access private
-	 * @param array $letters  Array of letters to build a file from
-	 * @return string  MP3 file data
-	 */
-	function generateMP3($letters)
-	{
-		$data_len    = 0;
-		$files       = array();
-		$out_data    = '';
-
-		foreach ($letters as $letter) {
-			$filename = $this->audio_path . strtoupper($letter) . '.mp3';
-
-			$fp   = fopen($filename, 'rb');
-			$data = fread($fp, filesize($filename)); // read file in
-
-			$this->scrambleAudioData($data, 'mp3');
-			$out_data .= $data;
-
-			fclose($fp);
-		}
-
-
-		return $out_data;
-	}
-
-	/**
-	 * Generate random number less than 1
-	 * @since 2.0
-	 * @access private
-	 * @return float
-	 */
-	function frand()
-	{
-		return 0.0001*rand(0,9999);
-	}
-
-	/**
-	 * Print signature text on image
-	 *
-	 * @since 2.0
-	 * @access private
-	 *
-	 */
-	function addSignature()
-	{
-		if ($this->use_gd_font) {
-			imagestring($this->im, 5, $this->image_width - (strlen($this->image_signature) * 10), $this->image_height - 20, $this->image_signature, $this->gdsignaturecolor);
-		} else {
-			 
-			$bbox = imagettfbbox(10, 0, $this->signature_font, $this->image_signature);
-			$textlen = $bbox[2] - $bbox[0];
-			$x = $this->image_width - $textlen - 5;
-			$y = $this->image_height - 3;
-			 
-			imagettftext($this->im, 10, 0, $x, $y, $this->gdsignaturecolor, $this->signature_font, $this->image_signature);
-		}
-	}
-	
-	/**
-	 * Get hashed IP address of remote user
-	 * 
-	 * @access private
-	 * @since 2.0.1
-	 * @return string
-	 */
-	function getIPHash()
-	{
-		return strtolower(md5($_SERVER['REMOTE_ADDR']));
-	}
-	
-	/**
-	 * Open SQLite database
-	 * 
-	 * @access private
-	 * @since 2.0.1
-	 * @return bool true if database was opened successfully
-	 */
-	function openDatabase()
-	{
-		$this->sqlite_handle = false;
-		
-		if ($this->use_sqlite_db && function_exists('sqlite_open')) {
-			$this->sqlite_handle = sqlite_open($this->sqlite_database, 0666, $error);
-			
-			if ($this->sqlite_handle !== false) {
-				$res = sqlite_query($this->sqlite_handle, "PRAGMA table_info(codes)");
-				if (sqlite_num_rows($res) == 0) {
-				  sqlite_query($this->sqlite_handle, "CREATE TABLE codes (iphash VARCHAR(32) PRIMARY KEY, code VARCHAR(32) NOT NULL, created INTEGER)");
-				}
-			}
-			
-			return $this->sqlite_handle != false;
-		}
-		
-		return $this->sqlite_handle;
-	}
-	
-	/**
-	 * Save captcha code to sqlite database
-	 * 
-	 * @access private
-	 * @since 2.0.1
-	 * @return bool true if code was saved, false if not
-	 */
-	function saveCodeToDatabase()
-	{
-		$success = false;
-		
-		$this->openDatabase();
-		
-		if ($this->use_sqlite_db && $this->sqlite_handle !== false) {
-			$ip = $this->getIPHash();
-			$time = time();
-			$code = $_SESSION['securimage_code_value']; // hash code for security - if cookies are disabled the session still exists at this point
-			$success = sqlite_query($this->sqlite_handle, "INSERT OR REPLACE INTO codes(iphash, code, created) VALUES('$ip', '$code', $time)");
-		}
-		
-		return $success !== false;
-	}
-	
-	/**
-	 * Get stored captcha code from sqlite database based on ip address hash
-	 * 
-	 * @access private
-	 * @since 2.0.1
-	 * @return string captcha code
-	 */
-	function getCodeFromDatabase()
-	{
-    $code = '';
-
-    if ($this->use_sqlite_db && $this->sqlite_handle !== false) {
-    	$ip = $this->getIPHash();
-    	
-    	$res = sqlite_query($this->sqlite_handle, "SELECT * FROM codes WHERE iphash = '$ip'");
-    	if ($res && sqlite_num_rows($res) > 0) {
-    		$res = sqlite_fetch_array($res);
-    		
-    		if ($this->isCodeExpired($res['created']) == false) {
-    			$code = $res['code'];
-    		}
-    	}
+
+    /**
+     * Gets audio file contents from the session or database
+     *
+     * @return string|boolean Audio contents on success, or false if no audio found in session or DB
+     */
+    protected function getAudioData()
+    {
+        if ($this->no_session != true) {
+            if (isset($_SESSION['securimage_code_audio'][$this->namespace])) {
+                return $_SESSION['securimage_code_audio'][$this->namespace];
+            }
+        }
+
+        if ($this->use_database) {
+            $this->openDatabase();
+            $code = $this->getCodeFromDatabase();
+
+            if (!empty($code['audio_data'])) {
+                return $code['audio_data'];
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Saves the CAPTCHA data to the configured database.
+     */
+    protected function saveCodeToDatabase()
+    {
+        $success = false;
+        $this->openDatabase();
+
+        if ($this->use_database && $this->pdo_conn) {
+            $id = $this->getCaptchaId(false);
+            $ip = $_SERVER['REMOTE_ADDR'];
+
+            if (empty($id)) {
+                $id = $ip;
+            }
+
+            $time      = time();
+            $code      = $this->code;
+            $code_disp = $this->code_display;
+
+            // This is somewhat expensive in PDO Sqlite3 (when there is something to delete)
+            // Clears previous captcha for this client from database so we can do a straight insert
+            // without having to do INSERT ... ON DUPLICATE KEY or a find/update
+            $this->clearCodeFromDatabase();
+
+            $query = "INSERT INTO {$this->database_table} ("
+                    ."id, code, code_display, namespace, created) "
+                    ."VALUES(?, ?, ?, ?, ?)";
+
+            $stmt    = $this->pdo_conn->prepare($query);
+            $success = $stmt->execute(array($id, $code, $code_disp, $this->namespace, $time));
+
+            if (!$success) {
+                $err   = $stmt->errorInfo();
+                $error = "Failed to insert code into database. {$err[1]}: {$err[2]}.";
+
+                if ($this->database_driver == self::SI_DRIVER_SQLITE3) {
+                    $err14 = ($err[1] == 14);
+                    if ($err14) $error .= sprintf(" Ensure database directory and file are writeable by user '%s' (%d).",
+                                                   get_current_user(), getmyuid());
+                }
+
+                trigger_error($error, E_USER_WARNING);
+            }
+        }
+
+        return $success !== false;
+    }
+
+    /**
+     * Saves CAPTCHA audio to the configured database
+     *
+     * @param string $data Audio data
+     * @return boolean true on success, false on failure
+     */
+    protected function saveAudioToDatabase($data)
+    {
+        $success = false;
+        $this->openDatabase();
+
+        if ($this->use_database && $this->pdo_conn) {
+            $id = $this->getCaptchaId(false);
+            $ip = $_SERVER['REMOTE_ADDR'];
+            $ns = $this->namespace;
+
+            if (empty($id)) {
+                $id = $ip;
+            }
+
+            $query = "UPDATE {$this->database_table} SET audio_data = :audioData WHERE id = :id AND namespace = :namespace";
+            $stmt  = $this->pdo_conn->prepare($query);
+            $stmt->bindParam(':audioData', $data, PDO::PARAM_LOB);
+            $stmt->bindParam(':id', $id);
+            $stmt->bindParam(':namespace', $ns);
+            $success = $stmt->execute();
+        }
+
+        return $success !== false;
+    }
+
+    /**
+     * Opens a connection to the configured database.
+     *
+     * @see Securimage::$use_database Use database
+     * @see Securimage::$database_driver Database driver
+     * @see Securimage::$pdo_conn pdo_conn
+     * @return bool true if the database connection was successful, false if not
+     */
+    protected function openDatabase()
+    {
+        $this->pdo_conn = false;
+
+        if ($this->use_database) {
+            $pdo_extension = 'PDO_' . strtoupper($this->database_driver);
+
+            if (!extension_loaded($pdo_extension)) {
+                trigger_error("Database support is turned on in Securimage, but the chosen extension $pdo_extension is not loaded in PHP.", E_USER_WARNING);
+                return false;
+            }
+        }
+
+        if ($this->database_driver == self::SI_DRIVER_SQLITE3) {
+            if (!file_exists($this->database_file)) {
+                $fp = fopen($this->database_file, 'w+');
+                if (!$fp) {
+                    $err = error_get_last();
+                    trigger_error("Securimage failed to create SQLite3 database file '{$this->database_file}'. Reason: {$err['message']}", E_USER_WARNING);
+                    return false;
+                }
+                fclose($fp);
+                chmod($this->database_file, 0666);
+            } else if (!is_writeable($this->database_file)) {
+                trigger_error("Securimage does not have read/write access to database file '{$this->database_file}. Make sure permissions are 0666 and writeable by user '" . get_current_user() . "'", E_USER_WARNING);
+                return false;
+            }
+        }
+
+        try {
+            $dsn = $this->getDsn();
+
+            $options = array();
+            $this->pdo_conn = new PDO($dsn, $this->database_user, $this->database_pass, $options);
+        } catch (PDOException $pdoex) {
+            trigger_error("Database connection failed: " . $pdoex->getMessage(), E_USER_WARNING);
+            return false;
+        } catch (Exception $ex) {
+            trigger_error($ex->getMessage(), E_USER_WARNING);
+            return false;
+        }
+
+        try {
+            if (!$this->skip_table_check && !$this->checkTablesExist()) {
+                // create tables...
+                $this->createDatabaseTables();
+            }
+        } catch (Exception $ex) {
+            trigger_error($ex->getMessage(), E_USER_WARNING);
+            $this->pdo_conn = false;
+            return false;
+        }
+
+        if (mt_rand(0, 100) / 100.0 == 1.0) {
+            $this->purgeOldCodesFromDatabase();
+        }
+
+        return $this->pdo_conn;
+    }
+
+    /**
+     * Get the PDO DSN string for connecting to the database
+     *
+     * @see Securimage::$database_driver Database driver
+     * @throws Exception  If database specific options are not configured
+     * @return string     The DSN for connecting to the database
+     */
+    protected function getDsn()
+    {
+        $dsn = sprintf('%s:', $this->database_driver);
+
+        switch($this->database_driver) {
+            case self::SI_DRIVER_SQLITE3:
+                $dsn .= $this->database_file;
+                break;
+
+            case self::SI_DRIVER_MYSQL:
+            case self::SI_DRIVER_PGSQL:
+                if (empty($this->database_host)) {
+                    throw new Exception('Securimage::database_host is not set');
+                } else if (empty($this->database_name)) {
+                    throw new Exception('Securimage::database_name is not set');
+                }
+
+                $dsn .= sprintf('host=%s;dbname=%s',
+                                $this->database_host,
+                                $this->database_name);
+                break;
+
+        }
+
+        return $dsn;
     }
-    
-    return $code;
-	}
-	
-	/**
-	 * Delete a code from the database by ip address hash
-	 * 
-	 * @access private
-	 * @since 2.0.1
-	 */
-	function clearCodeFromDatabase()
-	{
-		if ($this->sqlite_handle !== false) {
-			$ip = $this->getIPHash();
-			
-			sqlite_query($this->sqlite_handle, "DELETE FROM codes WHERE iphash = '$ip'");
-		}
-	}
-	
-	/**
-	 * Purge codes over a day old from database
-	 * 
-	 * @access private
-	 * @since 2.0.1
-	 */
-	function purgeOldCodesFromDatabase()
-	{
-		if ($this->use_sqlite_db && $this->sqlite_handle !== false) {
-			$now   = time();
-			$limit = (!is_numeric($this->expiry_time) || $this->expiry_time < 1) ? 86400 : $this->expiry_time;
-			
-			sqlite_query($this->sqlite_handle, "DELETE FROM codes WHERE $now - created > $limit");
-		}
-	}
-	
-	/**
-	 * Check a code to see if it is expired based on creation time
-	 * 
-	 * @access private
-	 * @since 2.0.1
-	 * @param $creation_time unix timestamp of code creation time
-	 * @return bool true if code has expired, false if not
-	 */
-	function isCodeExpired($creation_time)
-	{
-		$expired = true;
-		
-		if (!is_numeric($this->expiry_time) || $this->expiry_time < 1) {
-			$expired = false;
-		} else if (time() - $creation_time < $this->expiry_time) {
-			$expired = false;
-		}
-		
-		return $expired;
-	}
-	
-} /* class Securimage */
+
+    /**
+     * Checks if the necessary database tables for storing captcha codes exist
+     *
+     * @throws Exception If the table check failed for some reason
+     * @return boolean true if the database do exist, false if not
+     */
+    protected function checkTablesExist()
+    {
+        $table = $this->pdo_conn->quote($this->database_table);
+
+        switch($this->database_driver) {
+            case self::SI_DRIVER_SQLITE3:
+                // query row count for sqlite, PRAGMA queries seem to return no
+                // rowCount using PDO even if there are rows returned
+                $query = "SELECT COUNT(id) FROM $table";
+                break;
+
+            case self::SI_DRIVER_MYSQL:
+                $query = "SHOW TABLES LIKE $table";
+                break;
+
+            case self::SI_DRIVER_PGSQL:
+                $query = "SELECT * FROM information_schema.columns WHERE table_name = $table;";
+                break;
+        }
+
+        $result = $this->pdo_conn->query($query);
+
+        if (!$result) {
+            $err = $this->pdo_conn->errorInfo();
+
+            if ($this->database_driver == self::SI_DRIVER_SQLITE3 &&
+                $err[1] === 1 && strpos($err[2], 'no such table') !== false)
+            {
+                return false;
+            }
+
+            throw new Exception("Failed to check tables: {$err[0]} - {$err[1]}: {$err[2]}");
+        } else if ($this->database_driver == self::SI_DRIVER_SQLITE3) {
+            // successful here regardless of row count for sqlite
+            return true;
+        } else if ($result->rowCount() == 0) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * Create the necessary databaes table for storing captcha codes.
+     *
+     * Based on the database adapter used, the tables will created in the existing connection.
+     *
+     * @see Securimage::$database_driver Database driver
+     * @return boolean true if the tables were created, false if not
+     */
+    protected function createDatabaseTables()
+    {
+        $queries = array();
+
+        switch($this->database_driver) {
+            case self::SI_DRIVER_SQLITE3:
+                $queries[] = "CREATE TABLE \"{$this->database_table}\" (
+                                id VARCHAR(40),
+                                namespace VARCHAR(32) NOT NULL,
+                                code VARCHAR(32) NOT NULL,
+                                code_display VARCHAR(32) NOT NULL,
+                                created INTEGER NOT NULL,
+                                audio_data BLOB NULL,
+                                PRIMARY KEY(id, namespace)
+                              )";
+
+                $queries[] = "CREATE INDEX ndx_created ON {$this->database_table} (created)";
+                break;
+
+            case self::SI_DRIVER_MYSQL:
+                $queries[] = "CREATE TABLE `{$this->database_table}` (
+                                `id` VARCHAR(40) NOT NULL,
+                                `namespace` VARCHAR(32) NOT NULL,
+                                `code` VARCHAR(32) NOT NULL,
+                                `code_display` VARCHAR(32) NOT NULL,
+                                `created` INT NOT NULL,
+                                `audio_data` MEDIUMBLOB NULL,
+                                PRIMARY KEY(id, namespace),
+                                INDEX(created)
+                              )";
+                break;
+
+            case self::SI_DRIVER_PGSQL:
+                $queries[] = "CREATE TABLE {$this->database_table} (
+                                id character varying(40) NOT NULL,
+                                namespace character varying(32) NOT NULL,
+                                code character varying(32) NOT NULL,
+                                code_display character varying(32) NOT NULL,
+                                created integer NOT NULL,
+                                audio_data bytea NULL,
+                                CONSTRAINT pkey_id_namespace PRIMARY KEY (id, namespace)
+                              )";
+
+                $queries[] = "CREATE INDEX ndx_created ON {$this->database_table} (created);";
+                break;
+        }
+
+        $this->pdo_conn->beginTransaction();
+
+        foreach($queries as $query) {
+            $result = $this->pdo_conn->query($query);
+
+            if (!$result) {
+                $err = $this->pdo_conn->errorInfo();
+                trigger_error("Failed to create table.  {$err[1]}: {$err[2]}", E_USER_WARNING);
+                $this->pdo_conn->rollBack();
+                $this->pdo_conn = false;
+                return false;
+            }
+        }
+
+        $this->pdo_conn->commit();
+
+        return true;
+    }
+
+    /**
+     * Retrieves a stored code from the database for based on the captchaId or
+     * IP address if captcha ID not used.
+     *
+     * @return string|array Empty string if no code was found or has expired,
+     * otherwise returns array of code information.
+     */
+    protected function getCodeFromDatabase()
+    {
+        $code = '';
+
+        if ($this->use_database == true && $this->pdo_conn) {
+            if (Securimage::$_captchaId !== null) {
+                $query  = "SELECT * FROM {$this->database_table} WHERE id = ?";
+                $stmt   = $this->pdo_conn->prepare($query);
+                $result = $stmt->execute(array(Securimage::$_captchaId));
+            } else {
+                $ip = $_SERVER['REMOTE_ADDR'];
+                $ns = $this->namespace;
+
+                // ip is stored in id column when no captchaId
+                $query  = "SELECT * FROM {$this->database_table} WHERE id = ? AND namespace = ?";
+                $stmt   = $this->pdo_conn->prepare($query);
+                $result = $stmt->execute(array($ip, $ns));
+            }
+
+            if (!$result) {
+                $err = $this->pdo_conn->errorInfo();
+                trigger_error("Failed to select code from database.  {$err[0]}: {$err[1]}", E_USER_WARNING);
+            } else {
+                if ( ($row = $stmt->fetch()) !== false ) {
+                    if (false == $this->isCodeExpired($row['created'])) {
+                        if ($this->database_driver == self::SI_DRIVER_PGSQL && is_resource($row['audio_data'])) {
+                            // pg bytea data returned as stream resource
+                            $data = '';
+                            while (!feof($row['audio_data'])) {
+                                $data .= fgets($row['audio_data']);
+                            }
+                            $row['audio_data'] = $data;
+                        }
+                        $code = array(
+                            'code'      => $row['code'],
+                            'code_disp' => $row['code_display'],
+                            'time'      => $row['created'],
+                            'audio_data' => $row['audio_data'],
+                        );
+                    }
+                }
+            }
+        }
+
+        return $code;
+    }
+
+    /**
+     * Remove a stored code from the database based on captchaId or IP address.
+     */
+    protected function clearCodeFromDatabase()
+    {
+        if ($this->pdo_conn) {
+            $ip = $_SERVER['REMOTE_ADDR'];
+            $ns = $this->pdo_conn->quote($this->namespace);
+            $id = Securimage::$_captchaId;
+
+            if (empty($id)) {
+                $id = $ip; // if no captchaId set, IP address is captchaId.
+            }
+
+            $id = $this->pdo_conn->quote($id);
+
+            $query = sprintf("DELETE FROM %s WHERE id = %s AND namespace = %s",
+                             $this->database_table, $id, $ns);
+
+            $result = $this->pdo_conn->query($query);
+            if (!$result) {
+                trigger_error("Failed to delete code from database.", E_USER_WARNING);
+            }
+        }
+    }
+
+    /**
+     * Deletes old (expired) codes from the database
+     */
+    protected function purgeOldCodesFromDatabase()
+    {
+        if ($this->use_database && $this->pdo_conn) {
+            $now   = time();
+            $limit = (!is_numeric($this->expiry_time) || $this->expiry_time < 1) ? 86400 : $this->expiry_time;
+
+            $query = sprintf("DELETE FROM %s WHERE %s - created > %s",
+                             $this->database_table,
+                             $now,
+                             $this->pdo_conn->quote("$limit", PDO::PARAM_INT));
+
+            $result = $this->pdo_conn->query($query);
+        }
+    }
+
+    /**
+     * Checks to see if the captcha code has expired and can no longer be used.
+     *
+     * @see Securimage::$expiry_time expiry_time
+     * @param int $creation_time  The Unix timestamp of when the captcha code was created
+     * @return bool true if the code is expired, false if it is still valid
+     */
+    protected function isCodeExpired($creation_time)
+    {
+        $expired = true;
+
+        if (!is_numeric($this->expiry_time) || $this->expiry_time < 1) {
+            $expired = false;
+        } else if (time() - $creation_time < $this->expiry_time) {
+            $expired = false;
+        }
+
+        return $expired;
+    }
+
+    /**
+     * Generate a wav file given the $letters in the code
+     *
+     * @param array $letters  The letters making up the captcha
+     * @return string The audio content in WAV format
+     */
+    protected function generateWAV($letters)
+    {
+        $wavCaptcha = new WavFile();
+        $first      = true;     // reading first wav file
+
+        if ($this->audio_use_sox && !is_executable($this->sox_binary_path)) {
+            throw new Exception("Path to SoX binary is incorrect or not executable");
+        }
+
+        foreach ($letters as $letter) {
+            $letter = strtoupper($letter);
+
+            try {
+                $letter_file = realpath($this->audio_path) . DIRECTORY_SEPARATOR . $letter . '.wav';
+
+                if ($this->audio_use_sox) {
+                    $sox_cmd = sprintf("%s %s -t wav - %s",
+                                       $this->sox_binary_path,
+                                       $letter_file,
+                                       $this->getSoxEffectChain());
+
+                    $data = `$sox_cmd`;
+
+                    $l = new WavFile();
+                    $l->setIgnoreChunkSizes(true);
+                    $l->setWavData($data);
+                } else {
+                    $l = new WavFile($letter_file);
+                }
+
+                if ($first) {
+                    // set sample rate, bits/sample, and # of channels for file based on first letter
+                    $wavCaptcha->setSampleRate($l->getSampleRate())
+                               ->setBitsPerSample($l->getBitsPerSample())
+                               ->setNumChannels($l->getNumChannels());
+                    $first = false;
+                }
+
+                // append letter to the captcha audio
+                $wavCaptcha->appendWav($l);
+
+                // random length of silence between $audio_gap_min and $audio_gap_max
+                if ($this->audio_gap_max > 0 && $this->audio_gap_max > $this->audio_gap_min) {
+                    $wavCaptcha->insertSilence( mt_rand($this->audio_gap_min, $this->audio_gap_max) / 1000.0 );
+                }
+            } catch (Exception $ex) {
+                // failed to open file, or the wav file is broken or not supported
+                // 2 wav files were not compatible, different # channels, bits/sample, or sample rate
+                throw new Exception("Error generating audio captcha on letter '$letter': " . $ex->getMessage());
+            }
+        }
+
+        /********* Set up audio filters *****************************/
+        $filters = array();
+
+        if ($this->audio_use_noise == true) {
+            // use background audio - find random file
+            $wavNoise   = false;
+            $randOffset = 0;
+
+            /*
+            // uncomment to try experimental SoX noise generation.
+            // warning: sounds may be considered annoying
+            if ($this->audio_use_sox) {
+                $duration = $wavCaptcha->getDataSize() / ($wavCaptcha->getBitsPerSample() / 8) /
+                            $wavCaptcha->getNumChannels() / $wavCaptcha->getSampleRate();
+                $duration = round($duration, 2);
+                $wavNoise = new WavFile();
+                $wavNoise->setIgnoreChunkSizes(true);
+                $noiseData = $this->getSoxNoiseData($duration,
+                                                    $wavCaptcha->getNumChannels(),
+                                                    $wavCaptcha->getSampleRate(),
+                                                    $wavCaptcha->getBitsPerSample());
+                $wavNoise->setWavData($noiseData, true);
+
+            } else
+            */
+            if ( ($noiseFile = $this->getRandomNoiseFile()) !== false) {
+                try {
+                    $wavNoise = new WavFile($noiseFile, false);
+                } catch(Exception $ex) {
+                    throw $ex;
+                }
+
+                // start at a random offset from the beginning of the wavfile
+                // in order to add more randomness
+
+                $randOffset = 0;
+
+                if ($wavNoise->getNumBlocks() > 2 * $wavCaptcha->getNumBlocks()) {
+                    $randBlock = mt_rand(0, $wavNoise->getNumBlocks() - $wavCaptcha->getNumBlocks());
+                    $wavNoise->readWavData($randBlock * $wavNoise->getBlockAlign(), $wavCaptcha->getNumBlocks() * $wavNoise->getBlockAlign());
+                } else {
+                    $wavNoise->readWavData();
+                    $randOffset = mt_rand(0, $wavNoise->getNumBlocks() - 1);
+                }
+            }
+
+            if ($wavNoise !== false) {
+                $mixOpts = array('wav'  => $wavNoise,
+                                 'loop' => true,
+                                 'blockOffset' => $randOffset);
+
+                $filters[WavFile::FILTER_MIX]       = $mixOpts;
+                $filters[WavFile::FILTER_NORMALIZE] = $this->audio_mix_normalization;
+            }
+        }
+
+        if ($this->degrade_audio == true) {
+            // add random noise.
+            // any noise level below 95% is intensely distorted and not pleasant to the ear
+            $filters[WavFile::FILTER_DEGRADE] = mt_rand(95, 98) / 100.0;
+        }
+
+        if (!empty($filters)) {
+            $wavCaptcha->filter($filters);  // apply filters to captcha audio
+        }
+
+        return $wavCaptcha->__toString();
+    }
+
+    /**
+     * Gets and returns the path to a random noise file from the audio noise directory.
+     *
+     * @return bool|string  false if a file could not be found, or a string containing the path to the file.
+     */
+    public function getRandomNoiseFile()
+    {
+        $return = false;
+
+        if ( ($dh = opendir($this->audio_noise_path)) !== false ) {
+            $list = array();
+
+            while ( ($file = readdir($dh)) !== false ) {
+                if ($file == '.' || $file == '..') continue;
+                if (strtolower(substr($file, -4)) != '.wav') continue;
+
+                $list[] = $file;
+            }
+
+            closedir($dh);
+
+            if (sizeof($list) > 0) {
+                $file   = $list[array_rand($list, 1)];
+                $return = $this->audio_noise_path . DIRECTORY_SEPARATOR . $file;
+
+                if (!is_readable($return)) $return = false;
+            }
+        }
+
+        return $return;
+    }
+
+    /**
+     * Get a random effect or chain of effects to apply to a segment of the
+     * audio file.
+     *
+     * These effects should increase the randomness of the audio for
+     * a particular letter/number by modulating the signal.  The SoX effects
+     * used are *bend*, *chorus*, *overdrive*, *pitch*, *reverb*, *tempo*, and
+     * *tremolo*.
+     *
+     * For each effect selected, random parameters are supplied to the effect.
+     *
+     * @param int $numEffects  How many effects to chain together
+     * @return string  A string of valid SoX effects and their respective options.
+     */
+    protected function getSoxEffectChain($numEffects = 2)
+    {
+        $effectsList = array('bend', 'chorus', 'overdrive', 'pitch', 'reverb', 'tempo', 'tremolo');
+        $effects     = array_rand($effectsList, $numEffects);
+        $outEffects  = array();
+
+        if (!is_array($effects)) $effects = array($effects);
+
+        foreach($effects as $effect) {
+            $effect = $effectsList[$effect];
+
+            switch($effect)
+            {
+                case 'bend':
+                    $delay = mt_rand(0, 15) / 100.0;
+                    $cents = mt_rand(-120, 120);
+                    $dur   = mt_rand(75, 400) / 100.0;
+                    $outEffects[] = "$effect $delay,$cents,$dur";
+                    break;
+
+                case 'chorus':
+                    $gainIn  = mt_rand(75, 90) / 100.0;
+                    $gainOut = mt_rand(70, 95) / 100.0;
+                    $chorStr = "$effect $gainIn $gainOut";
+
+                    for ($i = 0; $i < mt_rand(2, 3); ++$i) {
+                        $delay = mt_rand(20, 100);
+                        $decay = mt_rand(10, 100) / 100.0;
+                        $speed = mt_rand(20, 50) / 100.0;
+                        $depth = mt_rand(150, 250) / 100.0;
+
+                        $chorStr .= " $delay $decay $speed $depth -s";
+                    }
+
+                    $outEffects[] = $chorStr;
+                    break;
+
+                case 'overdrive':
+                    $gain = mt_rand(5, 25);
+                    $color = mt_rand(20, 70);
+                    $outEffects[] = "$effect $gain $color";
+                    break;
+
+                case 'pitch':
+                    $cents = mt_rand(-300, 300);
+                    $outEffects[] = "$effect $cents";
+                    break;
+
+                case 'reverb':
+                    $reverberance = mt_rand(20, 80);
+                    $damping      = mt_rand(10, 80);
+                    $scale        = mt_rand(85, 100);
+                    $depth        = mt_rand(90, 100);
+                    $predelay     = mt_rand(0, 5);
+                    $outEffects[] = "$effect $reverberance $damping $scale $depth $predelay";
+                    break;
+
+                case 'tempo':
+                    $factor = mt_rand(65, 135) / 100.0;
+                    $outEffects[] = "$effect -s $factor";
+                    break;
+
+                case 'tremolo':
+                    $hz    = mt_rand(10, 30);
+                    $depth = mt_rand(40, 85);
+                    $outEffects[] = "$effect $hz $depth";
+                    break;
+            }
+        }
+
+        return implode(' ', $outEffects);
+    }
+
+    /**
+     * This function is not yet used.
+     *
+     * Generate random background noise from sweeping oscillators
+     *
+     * @param float $duration  How long in seconds the generated sound will be
+     * @param int $numChannels Number of channels in output wav
+     * @param int $sampleRate  Sample rate of output wav
+     * @param int $bitRate     Bits per sample (8, 16, 24)
+     * @return string          Audio data in wav format
+     */
+    protected function getSoxNoiseData($duration, $numChannels, $sampleRate, $bitRate)
+    {
+        $shapes = array('sine', 'square', 'triangle', 'sawtooth', 'trapezium');
+        $steps  = array(':', '+', '/', '-');
+        $selShapes = array_rand($shapes, 2);
+        $selSteps  = array_rand($steps, 2);
+        $sweep0    = array();
+        $sweep0[0] = mt_rand(100, 700);
+        $sweep0[1] = mt_rand(1500, 2500);
+        $sweep1    = array();
+        $sweep1[0] = mt_rand(500, 1000);
+        $sweep1[1] = mt_rand(1200, 2000);
+
+        if (mt_rand(0, 10) % 2 == 0)
+            $sweep0 = array_reverse($sweep0);
+
+        if (mt_rand(0, 10) % 2 == 0)
+            $sweep1 = array_reverse($sweep1);
+
+        $cmd = sprintf("%s -c %d -r %d -b %d -n -t wav - synth noise create vol 0.3 synth %.2f %s mix %d%s%d vol 0.3 synth %.2f %s fmod %d%s%d vol 0.3",
+                       $this->sox_binary_path,
+                       $numChannels,
+                       $sampleRate,
+                       $bitRate,
+                       $duration,
+                       $shapes[$selShapes[0]],
+                       $sweep0[0],
+                       $steps[$selSteps[0]],
+                       $sweep0[1],
+                       $duration,
+                       $shapes[$selShapes[1]],
+                       $sweep1[0],
+                       $steps[$selSteps[1]],
+                       $sweep1[1]
+                       );
+        $data = `$cmd`;
+
+        return $data;
+    }
+
+    /**
+     * Convert WAV data to MP3 using the Lame MP3 encoder binary
+     *
+     * @param string $data  Contents of the WAV file to convert
+     * @return string       MP3 file data
+     */
+    protected function wavToMp3($data)
+    {
+        if (!file_exists(self::$lame_binary_path) || !is_executable(self::$lame_binary_path)) {
+            throw new Exception('Lame binary "' . $this->lame_binary_path . '" does not exist or is not executable');
+        }
+
+        // size of wav data input
+        $size = strlen($data);
+
+        // file descriptors for reading and writing to the Lame process
+        $descriptors = array(
+                0 => array('pipe', 'r'), // stdin
+                1 => array('pipe', 'w'), // stdout
+                2 => array('pipe', 'a'), // stderr
+        );
+
+        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
+            // workaround for Windows conversion
+            // writing to STDIN seems to hang indefinitely after writing approximately 0xC400 bytes
+            $wavinput = tempnam(sys_get_temp_dir(), 'wav');
+            if (!$wavinput) {
+                throw new Exception('Failed to create temporary file for WAV to MP3 conversion');
+            }
+            file_put_contents($wavinput, $data);
+            $size = 0;
+        } else {
+            $wavinput = '-'; // stdin
+        }
+
+        // Mono, variable bit rate, 32 kHz sampling rate, read WAV from stdin, write MP3 to stdout
+        $cmd  = sprintf("%s -m m -v -b 32 %s -", self::$lame_binary_path, $wavinput);
+        $proc = proc_open($cmd, $descriptors, $pipes);
+
+        if (!is_resource($proc)) {
+            throw new Exception('Failed to open process for MP3 encoding');
+        }
+
+        stream_set_blocking($pipes[0], 0); // set stdin to be non-blocking
+
+        for ($written = 0; $written < $size; $written += $len) {
+            // write to stdin until all WAV data is written
+            $len = fwrite($pipes[0], substr($data, $written, 0x20000));
+
+            if ($len === 0) {
+                // fwrite wrote no data, make sure process is still alive, otherwise wait for it to process
+                $status = proc_get_status($proc);
+                if ($status['running'] === false) break;
+                usleep(25000);
+            } else if ($written < $size) {
+                // couldn't write all data, small pause and try again
+                usleep(10000);
+            } else if ($len === false) {
+                // fwrite failed, should not happen
+                break;
+            }
+        }
+
+        fclose($pipes[0]);
+
+        $data = stream_get_contents($pipes[1]);
+        $err  = trim(stream_get_contents($pipes[2]));
+
+        fclose($pipes[1]);
+        fclose($pipes[2]);
+
+        $return = proc_close($proc);
+
+        if ($wavinput != '-') unlink($wavinput); // delete temp file on Windows
+
+        if ($return !== 0) {
+            throw new Exception("Failed to convert WAV to MP3.  Shell returned ({$return}): {$err}");
+        } else if ($written < $size) {
+            throw new Exception('Failed to convert WAV to MP3.  Failed to write all data to encoder');
+        }
+
+        return $data;
+    }
+
+    /**
+     * Return a wav file saying there was an error generating file
+     *
+     * @return string The binary audio contents
+     */
+    protected function audioError()
+    {
+        return @file_get_contents(dirname(__FILE__) . '/audio/en/error.wav');
+    }
+
+    /**
+     * Checks to see if headers can be sent and if any error has been output
+     * to the browser
+     *
+     * @return bool true if it is safe to send headers, false if not
+     */
+    protected function canSendHeaders()
+    {
+        if (headers_sent()) {
+            // output has been flushed and headers have already been sent
+            return false;
+        } else if (strlen((string)ob_get_contents()) > 0) {
+            // headers haven't been sent, but there is data in the buffer that will break image and audio data
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Return a random float between 0 and 0.9999
+     *
+     * @return float Random float between 0 and 0.9999
+     */
+    protected function frand()
+    {
+        return 0.0001 * mt_rand(0,9999);
+    }
+
+    protected function strlen($string)
+    {
+        $strlen= 'strlen';
+
+        if (function_exists('mb_strlen')) {
+            $strlen= 'mb_strlen';
+        }
+
+        return $strlen($string);
+    }
+
+    protected function substr($string, $start, $length = null)
+    {
+        $substr= 'substr';
+
+        if (function_exists('mb_substr')) {
+            $substr = 'mb_substr';
+        }
+
+        if ($length === null) {
+            return $substr($string, $start);
+        } else {
+            return $substr($string, $start, $length);
+        }
+    }
+
+    protected function strpos($haystack, $needle, $offset = 0)
+    {
+        $strpos = 'strpos';
+
+        if (function_exists('mb_strpos')) {
+            $strpos = 'mb_strpos';
+        }
+
+        return $strpos($haystack, $needle, $offset);
+    }
+
+    /**
+     * Convert an html color code to a Securimage_Color
+     * @param string $color
+     * @param Securimage_Color|string $default The defalt color to use if $color is invalid
+     */
+    protected function initColor($color, $default)
+    {
+        if ($color == null) {
+            return new Securimage_Color($default);
+        } else if (is_string($color)) {
+            try {
+                return new Securimage_Color($color);
+            } catch(Exception $e) {
+                return new Securimage_Color($default);
+            }
+        } else if (is_array($color) && sizeof($color) == 3) {
+            return new Securimage_Color($color[0], $color[1], $color[2]);
+        } else {
+            return new Securimage_Color($default);
+        }
+    }
+
+    protected function ttfFile()
+    {
+        if (is_string($this->ttf_file)) {
+            return $this->ttf_file;
+        } elseif (is_array($this->ttf_file)) {
+            return $this->ttf_file[mt_rand(0, sizeof($this->ttf_file)-1)];
+        } else {
+            throw new \Exception('ttf_file is not a string or array');
+        }
+    }
+
+    /**
+     * The error handling function used when outputting captcha image or audio.
+     *
+     * This error handler helps determine if any errors raised would
+     * prevent captcha image or audio from displaying.  If they have
+     * no effect on the output buffer or headers, true is returned so
+     * the script can continue processing.
+     *
+     * See https://github.com/dapphp/securimage/issues/15
+     *
+     * @param int $errno  PHP error number
+     * @param string $errstr  String description of the error
+     * @param string $errfile  File error occurred in
+     * @param int $errline  Line the error occurred on in file
+     * @param array $errcontext  Additional context information
+     * @return boolean true if the error was handled, false if PHP should handle the error
+     */
+    public function errorHandler($errno, $errstr, $errfile = '', $errline = 0, $errcontext = array())
+    {
+        // get the current error reporting level
+        $level = error_reporting();
+
+        // if error was supressed or $errno not set in current error level
+        if ($level == 0 || ($level & $errno) == 0) {
+            return true;
+        }
+
+        return false;
+    }
+}
 
 
 /**
  * Color object for Securimage CAPTCHA
  *
- * @since 2.0
+* @since 2.0
  * @package Securimage
  * @subpackage classes
  *
  */
-class Securimage_Color {
-	/**
-	 * Red component: 0-255
-	 *
-	 * @var int
-	 */
-	var $r;
-	/**
-	 * Green component: 0-255
-	 *
-	 * @var int
-	 */
-	var $g;
-	/**
-	 * Blue component: 0-255
-	 *
-	 * @var int
-	 */
-	var $b;
-	
-	public function __construct()
+class Securimage_Color
+{
+    /**
+     * Red value (0-255)
+     * @var int
+     */
+    public $r;
+
+    /**
+     * Gree value (0-255)
+     * @var int
+     */
+    public $g;
+
+    /**
+     * Blue value (0-255)
+     * @var int
+     */
+    public $b;
+
+    /**
+     * Create a new Securimage_Color object.
+     *
+     * Constructor expects 1 or 3 arguments.
+     *
+     * When passing a single argument, specify the color using HTML hex format.
+     *
+     * When passing 3 arguments, specify each RGB component (from 0-255)
+     * individually.
+     *
+     * Examples:
+     *
+     *     $color = new Securimage_Color('#0080FF');
+     *     $color = new Securimage_Color(0, 128, 255);
+     *
+     * @param string $color  The html color code to use
+     * @throws Exception  If any color value is not valid
+     */
+    public function __construct($color = '#ffffff')
+    {
+        $args = func_get_args();
+
+        if (sizeof($args) == 0) {
+            $this->r = 255;
+            $this->g = 255;
+            $this->b = 255;
+        } else if (sizeof($args) == 1) {
+            // set based on html code
+            if (substr($color, 0, 1) == '#') {
+                $color = substr($color, 1);
+            }
+
+            if (strlen($color) != 3 && strlen($color) != 6) {
+                throw new InvalidArgumentException(
+                  'Invalid HTML color code passed to Securimage_Color'
+                );
+            }
+
+            $this->constructHTML($color);
+        } else if (sizeof($args) == 3) {
+            $this->constructRGB($args[0], $args[1], $args[2]);
+        } else {
+            throw new InvalidArgumentException(
+              'Securimage_Color constructor expects 0, 1 or 3 arguments; ' . sizeof($args) . ' given'
+            );
+        }
+    }
+
+    public function toLongColor()
+    {
+        return ($this->r << 16) + ($this->g << 8) + $this->b;
+    }
+
+    public function fromLongColor($color)
+    {
+        $this->r = ($color >> 16) & 0xff;
+        $this->g = ($color >>  8) & 0xff;
+        $this->b =  $color        & 0xff;
+
+        return $this;
+    }
+
+    /**
+     * Construct from an rgb triplet
+     *
+     * @param int $red The red component, 0-255
+     * @param int $green The green component, 0-255
+     * @param int $blue The blue component, 0-255
+     */
+    protected function constructRGB($red, $green, $blue)
     {
-        // Constructor's functionality here, if you have any.
+        if ($red < 0)     $red   = 0;
+        if ($red > 255)   $red   = 255;
+        if ($green < 0)   $green = 0;
+        if ($green > 255) $green = 255;
+        if ($blue < 0)    $blue  = 0;
+        if ($blue > 255)  $blue  = 255;
+
+        $this->r = $red;
+        $this->g = $green;
+        $this->b = $blue;
     }
 
-	/**
-	 * Create a new Securimage_Color object.<br />
-	 * Specify the red, green, and blue components using their HTML hex code equivalent.<br />
-	 * Example: The code for the HTML color #4A203C is:<br />
-	 * $color = new Securimage_Color(0x4A, 0x20, 0x3C);
-	 *
-	 * @param $red Red component 0-255
-	 * @param $green Green component 0-255
-	 * @param $blue Blue component 0-255
-	 */
-	function Securimage_Color($red, $green = null, $blue = null)
-	{
-		if ($green == null && $blue == null && preg_match('/^#[a-f0-9]{3,6}$/i', $red)) {
-			$col = substr($red, 1);
-			if (strlen($col) == 3) {
-				$red   = str_repeat(substr($col, 0, 1), 2);
-				$green = str_repeat(substr($col, 1, 1), 2);
-				$blue  = str_repeat(substr($col, 2, 1), 2);
-			} else {
-				$red   = substr($col, 0, 2);
-				$green = substr($col, 2, 2);
-				$blue  = substr($col, 4, 2); 
-			}
-			
-			$red   = hexdec($red);
-			$green = hexdec($green);
-			$blue  = hexdec($blue);
-		} else {
-			if ($red < 0) $red       = 0;
-			if ($red > 255) $red     = 255;
-			if ($green < 0) $green   = 0;
-			if ($green > 255) $green = 255;
-			if ($blue < 0) $blue     = 0;
-			if ($blue > 255) $blue   = 255;
-		}
-
-		$this->r = $red;
-		$this->g = $green;
-		$this->b = $blue;
-		self::__construct();
-	}
+    /**
+     * Construct from an html hex color code
+     *
+     * @param string $color
+     */
+    protected function constructHTML($color)
+    {
+        if (strlen($color) == 3) {
+            $red   = str_repeat(substr($color, 0, 1), 2);
+            $green = str_repeat(substr($color, 1, 1), 2);
+            $blue  = str_repeat(substr($color, 2, 1), 2);
+        } else {
+            $red   = substr($color, 0, 2);
+            $green = substr($color, 2, 2);
+            $blue  = substr($color, 4, 2);
+        }
+
+        $this->r = hexdec($red);
+        $this->g = hexdec($green);
+        $this->b = hexdec($blue);
+    }
 }
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage_play.php b/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage_play.php
index 1f369bcb34ff827f995a301e9a8ce563bad1b73a..b028c2b98ed5c56040a170c4a7122c8cc91115bd 100644
--- a/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage_play.php
+++ b/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage_play.php
@@ -27,17 +27,44 @@
  * @link http://www.phpcaptcha.org Securimage PHP CAPTCHA
  * @link http://www.phpcaptcha.org/latest.zip Download Latest Version
  * @link http://www.phpcaptcha.org/Securimage_Docs/ Online Documentation
- * @copyright 2009 Drew Phillips
- * @author drew010 <drew@drew-phillips.com>
- * @version 2.0.1 BETA (December 6th, 2009)
+ * @copyright 2012 Drew Phillips
+ * @author Drew Phillips <drew@drew-phillips.com>
+ * @version 3.6.6 (Nov 20 2017)
  * @package Securimage
  *
  */
 
-include 'securimage.php';
+require_once dirname(__FILE__) . '/securimage.php';
 
-$img    = new Securimage();
-$img->audio_format = (isset($_GET['format']) && in_array(strtolower($_GET['format']), array('mp3', 'wav')) ? strtolower($_GET['format']) : 'mp3');
-//$img->setAudioPath('/path/to/securimage/audio/');
+// if using database, adjust these options as necessary and change $img = new Securimage(); to $img = new Securimage($options);
+// see test.mysql.php or test.sqlite.php for examples
+$options = array(
+    'use_database'    => true,
+    'database_name'   => '',
+    'database_user'   => '',
+    'database_driver' => Securimage::SI_DRIVER_MYSQL
+);
 
-$img->outputAudioFile();
+$img = new Securimage();
+
+// Other audio settings
+//$img->audio_use_sox   = true;
+//$img->audio_use_noise = true;
+//$img->degrade_audio   = false;
+//$img->sox_binary_path = 'sox';
+//Securimage::$lame_binary_path = '/usr/bin/lame'; // for mp3 audio support
+
+// To use an alternate language, uncomment the following and download the files from phpcaptcha.org
+// $img->audio_path = $img->securimage_path . '/audio/es/';
+
+// If you have more than one captcha on a page, one must use a custom namespace
+// $img->namespace = 'form2';
+
+// set namespace if supplied to script via HTTP GET
+if (!empty($_GET['namespace'])) $img->setNamespace($_GET['namespace']);
+
+
+// mp3 or wav format
+$format = (isset($_GET['format']) && strtolower($_GET['format']) == 'mp3') ? 'mp3' : null;
+
+$img->outputAudioFile($format);
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage_play.swf b/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage_play.swf
index d1718b7355c4e8d6a03d2caab2224e9a3c862b24..e749bc50cdc7aec800b8fb6d5552e28d2d600851 100644
Binary files a/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage_play.swf and b/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage_play.swf differ
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage_show.php b/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage_show.php
index cbb57343d0fea09cd353052aad088353a346f559..f352f76fc5c2ed335f5cb6309a7e92c8ead06d6a 100644
--- a/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage_show.php
+++ b/wp-content/plugins/wp2pgpmail/phpcaptcha/securimage_show.php
@@ -4,19 +4,29 @@
  * Project:     Securimage: A PHP class for creating and managing form CAPTCHA images<br />
  * File:        securimage_show.php<br />
  *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or any later version.<br /><br />
+ * Copyright (c) 2013, Drew Phillips
+ * All rights reserved.
  *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.<br /><br />
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
  *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA<br /><br />
+ *  - Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  - Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
  *
  * Any modifications to the library should be indicated clearly in the source code
  * to inform users that the changes are not a part of the original software.<br /><br />
@@ -27,36 +37,43 @@
  * @link http://www.phpcaptcha.org Securimage PHP CAPTCHA
  * @link http://www.phpcaptcha.org/latest.zip Download Latest Version
  * @link http://www.phpcaptcha.org/Securimage_Docs/ Online Documentation
- * @copyright 2009 Drew Phillips
- * @author drew010 <drew@drew-phillips.com>
- * @version 2.0.1 BETA (December 6th, 2009)
+ * @copyright 2013 Drew Phillips
+ * @author Drew Phillips <drew@drew-phillips.com>
+ * @version 3.6.6 (Nov 20 2017)
  * @package Securimage
  *
  */
 
-include 'securimage.php';
+// Remove the "//" from the following line for debugging problems
+// error_reporting(E_ALL); ini_set('display_errors', 1);
+
+require_once dirname(__FILE__) . '/securimage.php';
 
-$img = new securimage();
+$img = new Securimage();
 
-// Change some settings
+// You can customize the image by making changes below, some examples are included - remove the "//" to uncomment
 
-//$img->image_width = 275;
-//$img->image_height = 90;
-//$img->perturbation = 0.9; // 1.0 = high distortion, higher numbers = more distortion
-//$img->image_bg_color = new Securimage_Color("#0099CC");
-//$img->text_color = new Securimage_Color("#EAEAEA");
-//$img->text_transparency_percentage = 65; // 100 = completely transparent
-//$img->num_lines = 8;
-//$img->line_color = new Securimage_Color("#0000CC");
-//$img->signature_color = new Securimage_Color(rand(0, 64), rand(64, 128), rand(128, 255));
-//$img->image_type = SI_IMAGE_PNG;
+//$img->ttf_file        = './Quiff.ttf';
+//$img->captcha_type    = Securimage::SI_CAPTCHA_MATHEMATIC; // show a simple math problem instead of text
+//$img->case_sensitive  = true;                              // true to use case sensitve codes - not recommended
+//$img->image_height    = 90;                                // height in pixels of the image
+//$img->image_width     = $img->image_height * M_E;          // a good formula for image size based on the height
+//$img->perturbation    = .75;                               // 1.0 = high distortion, higher numbers = more distortion
+//$img->image_bg_color  = new Securimage_Color("#0099CC");   // image background color
+//$img->text_color      = new Securimage_Color("#EAEAEA");   // captcha text color
+//$img->num_lines       = 8;                                 // how many lines to draw over the image
+//$img->line_color      = new Securimage_Color("#0000CC");   // color of lines over the image
+//$img->image_type      = SI_IMAGE_JPEG;                     // render as a jpeg image
+//$img->signature_color = new Securimage_Color(rand(0, 64),
+//                                             rand(64, 128),
+//                                             rand(128, 255));  // random signature color
 
+// see securimage.php for more options that can be set
 
-//$img->show(); // alternate use:  $img->show('/path/to/background_image.jpg');
+// set namespace if supplied to script via HTTP GET
+if (!empty($_GET['namespace'])) $img->setNamespace($_GET['namespace']);
 
 
-$img->text_color = new Securimage_Color("#EAEAEA");
-$img->num_lines = 0;
-$img->code_length = 4;
-$img->draw_lines_over_text = false;
-$img->show('backgrounds/bg6.png');
+$img->show();  // outputs the image and content headers to the browser
+// alternate use:
+// $img->show('/path/to/background_image.jpg');
diff --git a/wp-content/plugins/wp2pgpmail/phpcaptcha/words/words.txt b/wp-content/plugins/wp2pgpmail/phpcaptcha/words/words.txt
index 308d48fb5ae016fc323eaa7b4298a828b41a68c8..9a444ce37093e6bc1de4b34f846d9d8cee42347f 100644
--- a/wp-content/plugins/wp2pgpmail/phpcaptcha/words/words.txt
+++ b/wp-content/plugins/wp2pgpmail/phpcaptcha/words/words.txt
@@ -511,7 +511,6 @@ anural
 anuran
 anuria
 anuric
-anuses
 anvils
 anyhow
 anyone
@@ -690,20 +689,6 @@ aspics
 aspire
 aspish
 asrama
-assail
-assais
-assays
-assent
-assert
-assess
-assets
-assign
-assist
-assize
-assoil
-assort
-assume
-assure
 astern
 asters
 asthma
@@ -914,12 +899,9 @@ baling
 balked
 balker
 ballad
-balled
-baller
 ballet
 ballon
 ballot
-ballsy
 balsam
 balsas
 bamboo
@@ -1346,7 +1328,6 @@ bisque
 bister
 bistre
 bistro
-bitchy
 biters
 biting
 bitmap
@@ -1485,8 +1466,6 @@ bolete
 boleti
 bolide
 bolled
-bollix
-bollox
 bolshy
 bolson
 bolted
@@ -1501,14 +1480,11 @@ bonbon
 bonded
 bonder
 bonduc
-boners
-bonged
 bongos
 bonier
 boning
 bonita
 bonito
-bonked
 bonnes
 bonnet
 bonnie
@@ -1565,8 +1541,6 @@ borzoi
 boshes
 bosker
 bosket
-bosoms
-bosomy
 bosons
 bosque
 bossed
@@ -1821,8 +1795,6 @@ bulled
 bullet
 bumble
 bumkin
-bummed
-bummer
 bumped
 bumper
 bumphs
@@ -3660,10 +3632,6 @@ dicast
 dicers
 dicier
 dicing
-dicked
-dicker
-dickey
-dickie
 dicots
 dictum
 didact
@@ -3840,7 +3808,6 @@ doming
 domino
 donate
 donees
-dongas
 dongle
 donjon
 donkey
@@ -3900,7 +3867,6 @@ dottle
 double
 doubly
 doubts
-douche
 doughs
 dought
 doughy
@@ -4852,7 +4818,6 @@ fiscal
 fished
 fisher
 fishes
-fisted
 fistic
 fitchy
 fitful
@@ -4888,7 +4853,6 @@ flamen
 flamer
 flames
 flanes
-flange
 flanks
 flappy
 flared
@@ -5226,7 +5190,6 @@ fuming
 fumuli
 funded
 funder
-fundic
 fundus
 funest
 fungal
@@ -5431,9 +5394,6 @@ gawped
 gawper
 gawsie
 gayals
-gaydar
-gayest
-gayety
 gazabo
 gazars
 gazebo
@@ -5678,20 +5638,15 @@ goblet
 goblin
 goboes
 gobony
-goddam
-godded
 godets
 godown
 godson
-godwit
 gofers
 goffer
 goggle
 goggly
 goglet
 goings
-goiter
-goitre
 golden
 golder
 golems
@@ -5851,7 +5806,6 @@ groans
 groats
 grocer
 groggy
-groins
 grooms
 groove
 groovy
@@ -5975,7 +5929,6 @@ haboob
 haceks
 hacked
 hackee
-hacker
 hackie
 hackle
 hackly
@@ -6058,7 +6011,6 @@ handed
 hander
 handle
 hangar
-hanged
 hanger
 hangul
 hangup
@@ -6241,7 +6193,6 @@ hermit
 hernia
 heroes
 heroic
-heroin
 herons
 herpes
 hetero
@@ -6294,7 +6245,6 @@ hinted
 hinter
 hipped
 hipper
-hippie
 hippos
 hirees
 hirers
@@ -6370,9 +6320,6 @@ homage
 hombre
 homely
 homers
-homeys
-homier
-homies
 homily
 homing
 hominy
@@ -6383,25 +6330,18 @@ hondas
 hondle
 honers
 honest
-honeys
 honied
 honing
 honked
 honker
-honkey
-honkie
 honors
 honour
 hooded
-hoodie
 hoodoo
 hooeys
 hoofed
 hoofer
-hookah
-hookas
 hooked
-hooker
 hookey
 hookup
 hoolie
@@ -6926,8 +6866,6 @@ jerboa
 jereed
 jerids
 jerked
-jerker
-jerkin
 jerrid
 jersey
 jessed
@@ -6943,7 +6881,6 @@ jetted
 jetton
 jetway
 jewels
-jewing
 jezail
 jibbed
 jibber
@@ -6970,8 +6907,6 @@ jinked
 jinker
 jinnee
 jinnis
-jinxed
-jinxes
 jitney
 jitter
 jivers
@@ -7057,7 +6992,6 @@ junior
 junked
 junker
 junket
-junkie
 juntas
 juntos
 jupons
@@ -7371,7 +7305,6 @@ kythes
 laager
 labara
 labels
-labial
 labile
 labium
 labors
@@ -7629,7 +7562,6 @@ leones
 lepers
 leptin
 lepton
-lesbos
 lesion
 lessee
 lessen
@@ -7880,8 +7812,6 @@ looked
 looker
 lookup
 loomed
-looney
-loonie
 looped
 looper
 loosed
@@ -8049,8 +7979,6 @@ madcap
 madded
 madden
 madder
-madman
-madmen
 madras
 madres
 madtom
@@ -8467,9 +8395,6 @@ mimics
 miming
 mimosa
 minced
-mincer
-minces
-minded
 minder
 miners
 mingle
@@ -8645,7 +8570,6 @@ morgan
 morgen
 morgue
 morion
-morons
 morose
 morpho
 morphs
@@ -8989,7 +8913,6 @@ nielli
 niello
 nieves
 niffer
-nigger
 niggle
 niggly
 nighed
@@ -9012,7 +8935,6 @@ ninths
 niobic
 nipped
 nipper
-nipple
 niseis
 niters
 nitery
@@ -9720,7 +9642,6 @@ pecans
 pechan
 peched
 pecked
-pecker
 pecten
 pectic
 pectin
@@ -9762,15 +9683,11 @@ pencel
 pencil
 pended
 pengos
-penial
-penile
 penman
 penmen
 pennae
 penned
 penner
-pennia
-pennis
 pennon
 pensee
 pensil
@@ -9831,8 +9748,6 @@ pettle
 pewees
 pewits
 pewter
-peyote
-peyotl
 phages
 pharos
 phased
@@ -10181,16 +10096,6 @@ ponies
 pontes
 pontil
 ponton
-poodle
-poohed
-pooing
-pooled
-pooler
-pooped
-poorer
-pooris
-poorly
-pooves
 popery
 popgun
 popish
@@ -10504,12 +10409,9 @@ purses
 pursue
 purvey
 pushed
-pusher
 pushes
 pushup
 pusley
-pusses
-pussly
 putlog
 putoff
 putons
@@ -10861,7 +10763,6 @@ reckon
 reclad
 recoal
 recoat
-recock
 recode
 recoil
 recoin
@@ -10875,8 +10776,6 @@ recoup
 rectal
 rector
 rectos
-rectum
-rectus
 recurs
 recuse
 recuts
@@ -10923,9 +10822,6 @@ reechy
 reeded
 reedit
 reefed
-reefer
-reeked
-reeker
 reeled
 reeler
 reemit
@@ -11324,9 +11220,6 @@ rilles
 rillet
 rimers
 rimier
-riming
-rimmed
-rimmer
 rimose
 rimous
 rimple
@@ -11407,8 +11300,6 @@ romaji
 romano
 romans
 romeos
-romped
-romper
 rondel
 rondos
 ronion
@@ -11453,10 +11344,6 @@ rotate
 rotche
 rotgut
 rotors
-rotted
-rotten
-rotter
-rottes
 rotund
 rouble
 rouche
@@ -11595,8 +11482,6 @@ saddhu
 saddle
 sadhes
 sadhus
-sadism
-sadist
 safari
 safely
 safest
@@ -11807,8 +11692,6 @@ schema
 scheme
 schism
 schist
-schizo
-schizy
 schlep
 schlub
 schmoe
@@ -11887,7 +11770,6 @@ sculks
 sculls
 sculps
 sculpt
-scummy
 scurfs
 scurfy
 scurry
@@ -11895,7 +11777,6 @@ scurvy
 scutch
 scutes
 scutum
-scuzzy
 scyphi
 scythe
 seabag
@@ -11905,7 +11786,6 @@ sealed
 sealer
 seaman
 seamed
-seamen
 seamer
 seance
 search
@@ -12557,7 +12437,6 @@ smudge
 smudgy
 smugly
 smutch
-smutty
 snacks
 snafus
 snaggy
@@ -12853,7 +12732,6 @@ splays
 spleen
 splent
 splice
-spliff
 spline
 splint
 splits
@@ -12910,9 +12788,6 @@ spryly
 spuing
 spumed
 spumes
-spunks
-spunky
-spurge
 spurns
 spurry
 spying
@@ -13045,10 +12920,6 @@ stilts
 stimes
 stingo
 stings
-stingy
-stinko
-stinks
-stinky
 stints
 stiped
 stipel
@@ -13083,7 +12954,6 @@ stones
 stoney
 stooge
 stooks
-stools
 stoops
 stoped
 stoper
@@ -13674,8 +13544,6 @@ testae
 tested
 testee
 tester
-testes
-testis
 teston
 tetany
 tetchy
@@ -13773,7 +13641,6 @@ throve
 thrown
 throws
 thrums
-thrush
 thrust
 thujas
 thulia
@@ -13900,8 +13767,6 @@ toiles
 toited
 tokays
 tokens
-tokers
-toking
 tolane
 tolans
 tolars
@@ -13983,7 +13848,6 @@ tortes
 torula
 toshes
 tossed
-tosser
 tosses
 tossup
 totals
@@ -14237,9 +14101,7 @@ tushes
 tushie
 tusked
 tusker
-tussah
 tussal
-tussar
 tusseh
 tusser
 tusses
@@ -14256,8 +14118,6 @@ tuxedo
 tuyere
 tuyers
 twains
-twangs
-twangy
 twanky
 tweaks
 tweaky
@@ -14628,14 +14488,10 @@ uredos
 ureide
 uremia
 uremic
-ureter
-uretic
 urgent
 urgers
 urging
 urials
-urinal
-urines
 uropod
 urping
 ursids
@@ -14938,17 +14794,8 @@ voyage
 voyeur
 vrooms
 vrouws
-vulgar
-vulgus
-vulvae
-vulval
-vulvar
-vulvas
 wabble
 wabbly
-wacker
-wackes
-wackos
 wadded
 wadder
 waddie
@@ -15017,9 +14864,6 @@ wampum
 wampus
 wander
 wandle
-wangan
-wangle
-wangun
 wanier
 waning
 wanion
@@ -15057,8 +14901,6 @@ washed
 washer
 washes
 washup
-wasted
-waster
 wastes
 wastry
 watape
@@ -15127,8 +14969,6 @@ weevil
 weewee
 weighs
 weight
-weiner
-weirdo
 weirds
 weirdy
 welded
@@ -15144,8 +14984,6 @@ weskit
 wester
 wether
 wetted
-wetter
-whacko
 whacks
 whacky
 whaled
@@ -15209,8 +15047,6 @@ whomso
 whoofs
 whoops
 whoosh
-whored
-whores
 whorls
 whorts
 whosis
@@ -15618,4 +15454,4 @@ zygoid
 zygoma
 zygose
 zygote
-zymase
\ No newline at end of file
+zymase
diff --git a/wp-content/plugins/wp2pgpmail/readme.txt b/wp-content/plugins/wp2pgpmail/readme.txt
index fe8fc5abcc5e0f08a0e23aab0d06f8c58bb846d5..84dfdf5e376d2d4d1cc6249c67a6a8fbe8940f6d 100644
--- a/wp-content/plugins/wp2pgpmail/readme.txt
+++ b/wp-content/plugins/wp2pgpmail/readme.txt
@@ -3,8 +3,8 @@ Contributors: wp2pgpmail
 Donate link: http://wp2pgpmail.com
 Tags: PGP, mail, contact form, encrypt, crypt, privacy, encode, secure, encryption, GnuPG, GPG
 Requires at least: 2.9.2
-Tested up to: 5.0
-Stable tag: 1.25
+Tested up to: 5.1
+Stable tag: 1.26
 Requires PHP: 5.6
 License: GPLv2
 License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -84,6 +84,9 @@ If you want to translate the Pro Edition, please [contact us !](https://wp2pgpma
 Screenshots are available on the [wp2pgpmail plugin website](https://wp2pgpmail.com/screenshots/).
 
 == Changelog ==
+= 1.26 =
+* Updating phpcaptcha Securimage to 3.6.7
+
 = 1.25 =
 * Testing plugin for WordPress 5.0
 
diff --git a/wp-content/plugins/wp2pgpmail/wp2pgpmail.php b/wp-content/plugins/wp2pgpmail/wp2pgpmail.php
index 321f668e7910f108ea5536405f3c22eb124e022a..93deb2f1ab083c127b7d919ec4fbfb3b105ba06a 100644
--- a/wp-content/plugins/wp2pgpmail/wp2pgpmail.php
+++ b/wp-content/plugins/wp2pgpmail/wp2pgpmail.php
@@ -3,7 +3,7 @@
 Plugin Name: wp2pgpmail
 Plugin URI: http://wp2pgpmail.com
 Description: A simple PGP Mail Form Plugin for WordPress
-Version: 1.25
+Version: 1.26
 Author: wp2pgpmail
 Author URI: http://wp2pgpmail.com
 License: