4.2.1 that we had on our wiki
authorIan Kelling <iank@fsf.org>
Tue, 13 Oct 2020 21:15:12 +0000 (17:15 -0400)
committerIan Kelling <iank@fsf.org>
Tue, 13 Oct 2020 21:15:12 +0000 (17:15 -0400)
13 files changed:
LICENSE [new file with mode: 0644]
Makefile.in [new file with mode: 0644]
README [new file with mode: 0644]
config.m4 [new file with mode: 0644]
curltest.php [new file with mode: 0644]
mem.c [new file with mode: 0644]
php_tclink.c [new file with mode: 0644]
php_tclink.h [new file with mode: 0644]
tcexample.php [new file with mode: 0644]
tclink.c [new file with mode: 0644]
tclink.h [new file with mode: 0644]
tctest.php [new file with mode: 0644]
validate.c [new file with mode: 0644]

diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..b1e3f5a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,504 @@
+                 GNU LESSER GENERAL PUBLIC LICENSE
+                      Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  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.
+\f
+                 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.
+\f
+  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.
+\f
+  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.
+\f
+  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.
+\f
+  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.
+\f
+  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.
+\f
+  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
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/Makefile.in b/Makefile.in
new file mode 100644 (file)
index 0000000..a137c97
--- /dev/null
@@ -0,0 +1,10 @@
+# $Id: Makefile.in,v 1.5.10.1 2015-10-15 23:20:29 mlai Exp $
+
+LTLIBRARY_NAME        = libtclink.la
+LTLIBRARY_SOURCES     = tclink.c php_tclink.c mem.c validate.c
+LTLIBRARY_SHARED_NAME = tclink.la
+LTLIBRARY_SHARED_LIBADD  = $(TCLINK_SHARED_LIBADD)
+
+CFLAGS =       @CFLAGS@ @DEFS@ 
+
+include $(top_srcdir)/build/dynlib.mk
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..e4f323c
--- /dev/null
+++ b/README
@@ -0,0 +1,110 @@
+Some countries have regulations on the use of cryptographic libraries
+like the ones embedded in TCLink. It may be unlawful to download TCLink
+in these countries.
+
+
+                                TCLink v4.2.1
+                             PHP Implementation
+                       copyright (C) TrustCommerce 2016
+                         http://www.trustcommerce.com
+                         techsupport@trustcommerce.com
+
+                              September 13, 2016
+
+I. DESCRIPTION
+
+  TCLink is a thin client library to allow your e-commerce servers to
+connect to the TrustCommerce payment gateway easily and consistently.
+The protocol (which is the same across all platforms and languages) is
+well-documented in the Web Developer's Guide, so please consult it for
+any questions you may have about the protocol syntax itself.
+
+  If you are on a Windows environment, do not use this client.  Please
+download the COM object and use the COM functions available for Windows
+to access the TCLink object.  See here for more information:
+       http://www.php.net/manual/en/ref.com.php
+
+  TCLink was originally ported to PHP by Andrew Barnett of Data Clarity.
+Thanks go to both him (for the code) and his business (for their support
+of open source).
+
+
+II. LICENSE
+
+  TCLink for PHP is released under the GNU LGPL.  Please read LICENSE
+for details.
+
+
+III. REQUIREMENTS
+
+  You must be running PHP 4.0.4pl1 or higher.
+
+  You need to have the OpenSSL development libraries installed.  
+It is recommended you use the most recent version provided by the
+vendor for PCI reasons.
+
+
+  Besides the normal PHP install, you'll need the php-devel package,
+which contains files needed for building PHP modules.  You can tell
+if this package is installed if the binary "phpize" is in your path.
+
+IV. BUILDING
+
+  At the root directory of this archive, execute the following:
+
+    phpize
+  Afterwards, type the following commands:
+
+    ./configure --with-ssl
+    make
+
+  Note 1:  On some systems, the SSL library may be in a different place, such as /usr/lib64.
+  In that case, type the following commands instead:
+
+    ./configure --with-ssl=/usr/lib64
+    make
+    
+  If you are not sure where your SSL library is installed, contact your system administrator or hosting provider
+  for more information.
+  
+  If the module builds without errors and you have access to PHP at the command line, 
+  you can test it with the following command:
+
+    php -d extension=modules/tclink.so tctest.php
+
+  This script will run a test transaction and print the results.
+
+
+V. INSTALLATION
+
+  If you have root access to the machine, you will probably want to
+install TCLink as a global extension.  You can do this by copying the
+module library (modules/tclink.so) to your PHP extensions directory
+(typically /usr/lib/php5).  Your extensions directory is defined by the
+"extension_dir" directive in php.ini.
+
+  Edit php.ini (typically found in /etc) and add the following line:
+
+    extension=tclink.so
+
+  Restart your webserver, and all PHP scripts will have access to TCLink.
+
+  If you can't or don't want to install the module system wide, you can
+still load it manually in each script that needs to access it through
+the dl() call.  See the top of tctest.php for an example.
+
+
+VI. USAGE
+
+  The tctest.php script shows a simple example of running transactions
+through the TCLink API.  A more complex example is contained in
+tcexample.php.  For further information, please consult the TC
+Developer's Guide, located in the doc subdirectory.
+
+  The curltest.php script shows a simple example of running transactions
+through the HTTPS post, as describd in the Developer's Guide.  It is 
+provided here in as a conveinence to users who are unable to install
+TCLink and have Curl available in their installation.  
+
diff --git a/config.m4 b/config.m4
new file mode 100644 (file)
index 0000000..fc9447e
--- /dev/null
+++ b/config.m4
@@ -0,0 +1,95 @@
+dnl $Id: config.m4,v 2.00 2001/07/26
+AC_DEFUN(SSL_LIB_CHK,[if ((test -r $i$1/libssl.so) || (test -r $i$1/libssl.dylib) || test -n "`ls $i$1/libssl.so* 2>/dev/null`"); then SSL_DIR=$i;])
+
+PHP_ARG_WITH(tclink,for TCLink support,
+[  --with-tclink[=DIR]    Include TCLink support.  DIR is the TCLink
+                          base install directory, defaults to ../C.
+                          Set DIR to "shared" to build as a dl, or "shared,DIR" 
+                          to build as a dl and still specify DIR.])
+
+PHP_ARG_WITH(ssl,for TCLink SSL  support,
+[  --with-ssl[=DIR]       Include TCLink SSL support.  DIR is the SSL
+                          library directory, which defaults to /usr/lib.])
+
+PHP_ARG_WITH(ca-path,for CA path,
+[  --with-ca-path[=DIR]     ca-bundle file or CA directory])
+
+if test "$PHP_TCLINK" != "no"; then
+  AC_DEFINE(HAVE_TCLINK,1,[ ])
+  PHP_EXTENSION(tclink,$ext_shared)
+fi
+
+dnl Need to have a way to sepcify the include dir as well
+if test "$PHP_SSL" != "no"; then
+  for i in /lib /usr/local/lib /usr/lib $PHP_SSL; do
+      SSL_LIB_CHK(/)
+    fi
+  done
+  if test -z "$SSL_DIR"; then
+    AC_MSG_ERROR(Cannot find libssl.so. Please specify the installation path for SSL)
+  fi
+
+  AC_DEFINE(HAVE_SSL,1,[ ])
+
+fi
+
+if test "$PHP_CA_PATH" != "no"; then
+       if test "$PHP_CA_PATH" != "yes"; then
+               AC_MSG_NOTICE([User specified CA path: $PHP_CA_PATH])
+               TCLINK_CA_PATH="$PHP_CA_PATH"
+       fi
+fi
+if test -z "$TCLINK_CA_PATH"; then
+    OPENSSL_CA_PATH=`openssl version -d | sed 's/[[^"]]*"\(.*\)"/\1/'`    # grab whatever between the double quotes 
+    if test -n "$OPENSSL_CA_PATH"; then
+        OPENSSL_CA_PATH="$OPENSSL_CA_PATH/certs"
+        AC_MSG_NOTICE([Openssl CA path: $OPENSSL_CA_PATH])
+        
+        for item in \
+            "$OPENSSL_CA_PATH/ca-bundle.crt" \
+            "$OPENSSL_CA_PATH/ca-certificates.crt" \
+            "$OPENSSL_CA_PATH/ca-bundle.trust.crt" \
+            "$OPENSSL_CA_PATH/tls-ca-bundle.pem" \
+            "$OPENSSL_CA_PATH"
+        do
+                       AC_MSG_NOTICE([Checking '$item'])
+            if test -f $item || test -d $item; then
+                AC_MSG_NOTICE([Found '$item'])
+                TCLINK_CA_PATH=$item
+                break
+            fi
+        done
+    fi  
+fi
+if test -z "$TCLINK_CA_PATH"; then
+       AC_MSG_NOTICE([Search from known locations])
+       for item in \
+            "/etc/pki/tls/certs/ca-bundle.crt" \
+            "/etc/ssl/certs/ca-certificates.crt" \
+            "/etc/pki/tls/certs/ca-bundle.trust.crt" \
+            "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem" \
+            "/etc/ssl/certs"
+        do
+            AC_MSG_NOTICE([Checking '$item'])
+                       if test -f $item || test -d $item; then
+                AC_MSG_NOTICE([Found '$item'])
+                TCLINK_CA_PATH=$item
+                break
+            fi 
+        done 
+fi
+if test -z "$TCLINK_CA_PATH"; then
+    AC_MSG_ERROR([Cannot determine CA path. Please use --with-ca-path=<path> to specify ca-bundle file or CA directory.])
+fi
+
+TCLINK_SHARED_LIBADD="-lssl -lcrypto -L$SSL_DIR";
+CFLAGS="-I$SSL_DIR/../include"
+TCLINK_VERSION="4.2.1-PHP-`uname -sm | tr ' ' -`"
+AC_DEFINE_UNQUOTED([TCLINK_VERSION], "$TCLINK_VERSION", [TCLink version string.])
+
+AC_MSG_NOTICE([CA path: $TCLINK_CA_PATH])
+AC_DEFINE_UNQUOTED([TCLINK_CA_PATH], "$TCLINK_CA_PATH", [CA path])
+
+PHP_SUBST(TCLINK_INCLUDE)
+PHP_SUBST(TCLINK_SHARED_LIBADD)
+
diff --git a/curltest.php b/curltest.php
new file mode 100644 (file)
index 0000000..de2044b
--- /dev/null
@@ -0,0 +1,47 @@
+<?
+       // Sends the transaction to us and processes the response into a
+       // standard PHP array.
+       function curl_tc_send($fields_to_send)
+       {
+               $post_string = '';
+               $use_amp = 0;
+               foreach ($fields_to_send as $key => $value)
+               {
+                       if ($use_amp) $post_string .= '&';
+                       $post_string .= "$key=$value";
+                       $use_amp = 1;
+               }
+               $curl_object = curl_init('https://vault.trustcommerce.com/trans/');
+               curl_setopt($curl_object, CURLOPT_RETURNTRANSFER, 1);
+               curl_setopt($curl_object, CURLOPT_POST, 1);
+               curl_setopt($curl_object, CURLOPT_POSTFIELDS, $post_string);
+       
+               $unformatted_results = curl_exec($curl_object);
+               curl_close($curl_object);
+               $result_array = explode("\n", $unformatted_results);
+               $tclink_results = array();
+               foreach ($result_array as $key => $value)
+               {
+                       $key_pair = explode('=', $value);
+                       if (count($key_pair) == 2)
+                       {
+                               $tclink_results[$key_pair[0]] = $key_pair[1];
+                       }
+               }
+               return $tclink_results;
+       }
+       
+       $transaction = array(
+               'custid' => 'XXXX',
+               'password' => 'PPPP',
+               'action' => 'preauth',
+               'amount' => '100',
+               'cc' => '4111111111111111',
+               'exp' => '1213',
+               'address1' => '123 Anywhere St',
+               'zip' => '92606'
+       );
+       
+       print_r(curl_tc_send($transaction));
+       
+?>
diff --git a/mem.c b/mem.c
new file mode 100644 (file)
index 0000000..95c6be5
--- /dev/null
+++ b/mem.c
@@ -0,0 +1,166 @@
+/* originally from crypto/x509/by_file.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+/* Added code to use a memory object as opposed to a file for lookup purposes. */
+
+#include <string.h>
+
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+#include <openssl/x509_vfy.h>
+
+static int by_mem_ctrl(X509_LOOKUP *, int, const char *, long, char **);
+static int X509_load_cert_crl_mem(X509_LOOKUP *ctx, const char *str, int type);
+
+X509_LOOKUP_METHOD x509_mem_lookup =
+{
+       "Memory from user provided string",
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       by_mem_ctrl,
+       NULL,
+       NULL,
+       NULL,
+       NULL
+};
+
+static X509_LOOKUP_METHOD * X509_LOOKUP_mem()
+{
+       return (&x509_mem_lookup);
+}
+
+static int by_mem_ctrl(X509_LOOKUP *ctx, int cmd, const char * str, long argl, char ** ret)
+{
+       int status = 0;
+       if (cmd == X509_L_FILE_LOAD)
+       {
+               if (argl == X509_FILETYPE_PEM)
+                       return X509_load_cert_crl_mem(ctx, str, X509_FILETYPE_PEM) != 0;
+       }
+       return status;
+}
+
+static int X509_load_cert_crl_mem(X509_LOOKUP *ctx, const char *str, int type)
+{
+       BIO *in = NULL;
+       int status = 0;
+       int count = 0;
+       int i;
+       X509 *x = NULL;
+
+       if (str == NULL) return 1;
+       in = BIO_new(BIO_s_mem());
+       if ((in == NULL) || (BIO_write(in, str, strlen(str)) != strlen(str)) || type != X509_FILETYPE_PEM)
+       {
+               /* this error isn't the same as the real file one, but it'll serve the same purpose */
+               X509err(X509_F_X509_LOAD_CERT_FILE,ERR_R_SYS_LIB);
+               goto err;
+       }
+
+       for (;;)
+       {
+               x=PEM_read_bio_X509_AUX(in,NULL,NULL,NULL);
+               if (x == NULL)
+               {
+                       if ((ERR_GET_REASON(ERR_peek_last_error()) ==
+                               PEM_R_NO_START_LINE) && (count > 0))
+                       {
+                               ERR_clear_error();
+                               break;
+                       }
+                       else
+                       {
+                               X509err(X509_F_X509_LOAD_CERT_FILE,
+                               ERR_R_PEM_LIB);
+                               goto err;
+                       }
+               }
+               i=X509_STORE_add_cert(ctx->store_ctx,x);
+               if (!i) goto err;
+               count++;
+               X509_free(x);
+               x=NULL;
+       }
+       status=count;
+
+err:
+       if (x != NULL) X509_free(x);
+       if (in != NULL) BIO_free(in);
+       return status;
+
+}
+
+static int X509_STORE_load_mem(X509_STORE *ctx, const char *str)
+{
+       X509_LOOKUP *lookup;
+       if (!str) return 1;
+       
+       lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_mem());
+       if (lookup == NULL) return 0;
+       if (X509_LOOKUP_ctrl(lookup,X509_L_FILE_LOAD,str,X509_FILETYPE_PEM, NULL) != 1)
+               return 0;
+       return 1;
+}
+int SSL_CTX_load_verify_locations_mem(SSL_CTX * ctx, const char *str)
+{
+       return X509_STORE_load_mem(ctx->cert_store, str);
+}
diff --git a/php_tclink.c b/php_tclink.c
new file mode 100644 (file)
index 0000000..08756f6
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * TCLink PHP Module
+ *
+ * TCLink Copyright (c) 2010 TrustCommerce.
+ * http://www.trustcommerce.com
+ * techsupport@trustcommerce.com
+ * (949) 387-3747
+ *
+ * PHP Port Copyright (c) 2000
+ * Andrew Barnett <andrew@dataclarity.com>
+ * 2000-11-21
+ *
+ * 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 (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "php.h"
+#include "php_config.h"
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_tclink.h"
+
+#if PHP_API_VERSION >= 20151012
+#include "ext/standard/info.h"
+#endif
+
+#if HAVE_TCLINK
+
+zend_function_entry php_tclink_functions[] = {
+       PHP_FE(tclink_send, NULL)
+       PHP_FE(tclink_getversion, NULL)
+       {NULL, NULL, NULL}
+};
+
+zend_module_entry php_tclink_module_entry = {
+#ifdef STANDARD_MODULE_HEADER
+       STANDARD_MODULE_HEADER,
+#endif
+       "tclink", 
+       php_tclink_functions, 
+       NULL, 
+       NULL, 
+       NULL, 
+       NULL, 
+       PHP_MINFO(tclink), 
+       NO_VERSION_YET,
+#if PHP_API_VERSION < 20090626
+       STANDARD_MODULE_PROPERTIES
+#else
+       0,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       STANDARD_MODULE_PROPERTIES_EX
+#endif
+};
+
+#define PHP_TCLINK_DEFAULT_BUFFER_SIZE 8196
+
+#ifdef COMPILE_DL_TCLINK
+ZEND_GET_MODULE(php_tclink)
+#endif
+
+PHP_MINFO_FUNCTION(tclink)
+{
+       char *tmp = (char *)malloc(1024);
+       php_info_print_table_start();
+       if(tmp == NULL) {
+               php_info_print_table_row(2, "TCLink PHP Module", "enabled");
+       } else {
+               php_info_print_table_row(2, "TCLink PHP Module", TCLinkGetVersion(tmp));
+               free(tmp);
+       }
+       php_info_print_table_end();
+}
+
+/* {{{ proto void tclink_send(array params)
+   Send the transaction in for processing. */
+PHP_FUNCTION(tclink_send)
+{
+/* start with a clean break using PHP7 APIs */
+#if PHP_API_VERSION >= 20151012
+       zval *arr;
+       TCLinkHandle tclink;
+       HashTable *hash;
+       zval *zvalue;
+       zend_string * zkey;
+       char buf[4096];
+       char *key, *value, *next_key;
+
+       if (ZEND_NUM_ARGS() != 1)
+       {
+               WRONG_PARAM_COUNT;
+       }
+
+       /* initialize it here so that it always has a return value is always something */
+       array_init(return_value);
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) == FAILURE) {
+               return;
+       }
+
+       tclink = TCLinkCreate();
+       hash = HASH_OF(arr);
+
+       ZEND_HASH_FOREACH_STR_KEY_VAL(hash, zkey, zvalue)
+       /* there aren't any numeric keys in tclink parameters */
+       if (ZSTR_VAL(zkey))
+       {
+               /* for converting the numeric values in case they are provided */
+               if (Z_TYPE_P(zvalue) != IS_STRING)
+               {
+                       convert_to_string(zvalue);
+               }
+               TCLinkPushParam(tclink, ZSTR_VAL(zkey), Z_STRVAL_P(zvalue));
+       }
+       ZEND_HASH_FOREACH_END();
+
+       /* send the transaction */
+       TCLinkSend(tclink);
+
+       /* pull out the parameters and put them in a hash */
+       TCLinkGetEntireResponse(tclink, buf, sizeof(buf));
+
+       key = value = buf;
+       while (key && (value = strchr(key, '=')))
+       {
+               *value++ = 0;
+               next_key = strchr(value, '\n');
+               if (next_key) *next_key++ = 0;
+
+               add_assoc_string(return_value, key, value);
+
+               key = next_key;
+       }
+
+       TCLinkDestroy(tclink);
+/* for older PHP 4 / PHP 5 clients */
+#else
+       zval **params, **zvalue;
+       HashTable *hash;
+       char *key, *value, *next_key;
+
+       TCLinkHandle h;
+       char buf[4096];
+
+       /* check parameters */
+       if((ZEND_NUM_ARGS() != 1) ||
+          (zend_get_parameters_ex(1, &params) == FAILURE)) {
+               WRONG_PARAM_COUNT;
+       }
+
+       convert_to_array_ex(params);
+
+       h = TCLinkCreate();
+
+       /* grab the hash and stuff each parameter set into TCLink */
+       hash = HASH_OF(*params);
+       zend_hash_internal_pointer_reset(hash);
+       while (zend_hash_get_current_data(hash, (void **)&zvalue) == SUCCESS)
+       {
+               /* The Zend API added an extra parameter between 4.04 (sometime in
+                * 1999) and 4.06 (in early 2001).  Assume that anything prior to
+                * 1/1/2001 is the older version. */
+#if PHP_API_VERSION < 20000101
+               zend_hash_get_current_key(hash, &key, NULL);
+#else
+               zend_hash_get_current_key(hash, &key, NULL, 0);
+#endif
+               convert_to_string_ex(zvalue);
+               value = Z_STRVAL_PP(zvalue);
+               TCLinkPushParam(h, key, value);
+               zend_hash_move_forward(hash);
+       }
+
+       /* send the transaction */
+       TCLinkSend(h);
+
+       /* pull out the parameters and put them in a hash */
+       TCLinkGetEntireResponse(h, buf, sizeof(buf));
+
+       array_init(return_value);
+
+       key = value = buf;
+       while (key && (value = strchr(key, '=')))
+       {
+               *value++ = 0;
+               next_key = strchr(value, '\n');
+               if (next_key) *next_key++ = 0;
+
+               add_assoc_string(return_value, key, value, 1);
+
+               key = next_key;
+       }
+
+       TCLinkDestroy(h);
+
+       /* return_value is returned automatically, we need not explictly call a
+          return macro */
+#endif
+}
+/* }}} */
+
+/* {{{ proto string tclink_getversion()
+       returns the API version information */
+PHP_FUNCTION(tclink_getversion)
+{
+       char version[1024];
+
+       TCLinkGetVersion(version);
+
+#if PHP_API_VERSION >= 20151012
+       char *str = estrdup(version);
+       RETURN_STRING(str);
+#else
+       RETURN_STRING(version, 1);
+#endif
+}
+/* }}} */
+
+#endif
diff --git a/php_tclink.h b/php_tclink.h
new file mode 100644 (file)
index 0000000..b5efd2a
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * TCLink PHP Module
+ *
+ * TCLink Copyright (c) 2002 TrustCommerce.
+ * http://www.trustcommerce.com
+ * developer@trustcommerce.com
+ * (626) 744-7700
+ *
+ * PHP Port Copyright (c) 2000
+ * Andrew Barnett <andrew@dataclarity.com>
+ * 2000-11-21
+ *
+ * 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 (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef PHP_TCLINK_H
+#define PHP_TCLINK_H
+
+#if HAVE_TCLINK
+
+#include <tclink.h>
+
+extern zend_module_entry php_tclink_module_entry;
+#define tclink_module_ptr &php_tclink_module_entry
+
+PHP_MINFO_FUNCTION(tclink);
+
+PHP_FUNCTION(tclink_send);
+PHP_FUNCTION(tclink_getversion);
+
+#else
+#define tclink_module_ptr NULL
+#endif /* HAVE_TCLINK */
+
+#define phpext_tclink_ptr tclink_module_ptr
+
+#endif /* PHP_TCLINK_H */
diff --git a/tcexample.php b/tcexample.php
new file mode 100644 (file)
index 0000000..88ebb45
--- /dev/null
@@ -0,0 +1,101 @@
+<?
+       // This example assumes that tclink.so has already been loaded.
+       // Normally this is done by adding "extension=tclink.so" to your
+       // php.ini, but you may also make a manual call to dl("tclink")
+       // as well.  See tctest.php for example code.
+
+       $custid = (!empty($_REQUEST['custid']) ? $_REQUEST['custid'] : "TestMerchant");
+       $password = (!empty($_REQUEST['password']) ? $_REQUEST['password'] : "password");
+?>
+<html>
+<head><title>TCLink PHP Example</title></head>
+<body bgcolor=white text=black>
+
+       <form method="post" action="<?= $PHP_SELF ?>">
+
+       <table cellspacing=1 cellpadding=3>
+       <tr bgcolor=blue><th colspan=2 align=center>
+               <font color=white>TrustCommerce PHP Example - TCLink ver. <?= tclink_getversion() ?>)</font>
+       </th></tr>
+
+       <tr><th align=right> CustID: </td><td> <input type="text" name="custid" value="<?= htmlspecialchars($custid) ?>"> </td></tr>
+       <tr><th align=right> Password: </td><td> <input type="text" name="password" value="<?= htmlspecialchars($password) ?>"> </td></tr>
+       <tr><th align=right> Action: </td><td> <select name="action">
+               <option value="sale">Sale</option>
+               <option value="preauth">Pre-Authorization</option>
+               <option value="postauth">Post-Authorization</option>
+               <option value="credit">Credit</option>
+       </select> </td></tr>
+       <tr><th align=right> Amount (in cents):</td><td> <input type="text" name="amount"> </td></tr>
+       <tr bgcolor=lightgray><td colspan=2 align=center> Sales and Pre-Authorizations Only: </td></tr>
+       <tr><th align=right> Card Number: </td><td> <input type="text" name="cc" size="16" maxlength="16"> </td></tr>
+       <tr><th align=right> Expiration: </td>
+           <td><select name="mm"><? for ($i = 1; $i <= 12; $i++) { ?><option value="<?=sprintf("%02d", $i);?>"><?=sprintf("%02d", $i);?></option><? } ?></select>
+               <select name="yy"><? for($i = (strftime("%Y")); $i <= (strftime("%Y") + 10); $i++) { ?><option value="<?=substr(sprintf("%04d", $i),2,2);?>"><?=$i;?></option><? } ?></select><br>
+           </td></tr>
+       <tr><th align=right> Cardholder Name: </td><td> <input type="text" name="name"> </td></tr>
+       <tr bgcolor=lightgray><td colspan=2 align=center> Credits and Post-Authorizations Only: </td></tr>
+       <tr><th align=right> Transaction ID: </td><td> <input type="text" name="transid" size="14" maxlength="14"> </td></tr>
+       <tr><td colspan=2 align=center> <input type="submit" name="Action" value="Process"> </td></tr>
+
+       <?
+               if ($_REQUEST['Action'] == 'Process')
+               {
+                       $tclink['custid'] = $custid;
+                       $tclink['password'] = $password;
+                       $tclink['action'] = $_REQUEST['action'];
+                       if (is_numeric($_REQUEST['amount']))
+                               $tclink['amount'] = $_REQUEST['amount'];
+
+                       if ($_REQUEST['action'] == 'sale' || $_REQUEST['action'] == 'preauth')
+                       {
+                               $tclink['name'] = $_REQUEST['name'];
+                               $tclink['cc'] = $_REQUEST['cc'];
+                               $tclink['exp'] = $_REQUEST['mm'] . $_REQUEST['yy'];
+                       }
+                       else if ($_REQUEST['action'] == 'credit' || $_REQUEST['action'] == 'postauth')
+                       {
+                               $tclink['transid'] = $_REQUEST['transid'];
+                       }
+
+                       $result = tclink_send($tclink);
+
+                       print "<tr><td colspan=2><hr></td></tr>";
+                       print "<tr bgcolor=blue><th colspan=2 align=center><font color=white>Transaction Results:</font></td></tr>";
+
+                       if ($result['transid'])
+                               printf("<tr><th>Transaction ID:</th><td>%s</td></tr>\n", $result['transid']);
+
+                       printf("<tr><th>Status:</td><td>%s</td></tr>\n", $result['status']);
+                       switch($result['status'])
+                       {
+                               case 'accepted':
+                               case 'approved':
+                                       break;
+
+                               case 'decline':
+                               case 'rejected':
+                                       printf("<tr><th>Decline Type:</th><td>%s</td></tr>\n", $result['declinetype']);
+                                       break;
+
+                               case 'error':
+                                       printf("<tr><th>Error Type</th><td>%s</td></tr>\n", $result['errortype']);
+                                       break;
+
+                               case 'baddata':
+                                       printf("<tr><th>Offenders:</th><td>%s</td></tr>\n", htmlspecialchars($result['$offenders']));
+                                       break;
+                       }
+
+                       print "<tr bgcolor=lightgray><td colspan=2 align=center>All Results:</td></tr>";
+
+                       while(list($key, $value) = each($result))
+                               printf("<tr><th>%s</th><td>%s</td></tr>\n", htmlspecialchars($key), htmlspecialchars($value));
+               }
+       ?>
+
+</table>
+</form>
+
+</body>
+</html>
diff --git a/tclink.c b/tclink.c
new file mode 100644 (file)
index 0000000..9510798
--- /dev/null
+++ b/tclink.c
@@ -0,0 +1,1017 @@
+/* tclink.c - Library code for the TCLink client API.
+ *
+ * TCLink Copyright (c) 2013 TrustCommerce.
+ * http://www.trustcommerce.com
+ * techsupport@trustcommerce.com
+ * (949) 387-3747
+ *
+ * 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 (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "tclink.h"
+
+#include <stdio.h>
+#include <memory.h>
+#include <errno.h>
+#include <string.h>
+
+#ifdef WIN32
+#include <io.h>
+#include <winsock2.h>
+#else
+#include <strings.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#endif
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#define OPENSSL_NO_KRB5 1
+
+#include <openssl/crypto.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+#ifdef WIN32
+#define strcasecmp(x,y)        stricmp(x,y)
+#else
+#define closesocket(x) close(x)
+#endif
+
+#define DEFAULT_HOST    "pgw1.trustcommerce.com"
+
+/* changed from forty second to one hundred second to reflect more complicated transaction processing logic */
+#define TIMEOUT         100     /* seconds */
+#define TC_BUFF_MAX     16000
+#define TC_LINE_MAX     ((PARAM_MAX_LEN * 2) + 2)
+
+char *tclink_version    = TCLINK_VERSION;  /* TCLINK_VERSION is defined in Makefile */
+char *tclink_host       = DEFAULT_HOST;
+int tclink_port         = 443;
+
+/*************************************************/
+/* Data structures used only within this module. */
+/*************************************************/
+
+/* Variables used for transaction data. */
+
+typedef struct param_data
+{
+       char *name;
+       char *value;
+       struct param_data *next;
+} param;
+
+typedef struct _TCLinkCon
+{
+       /* Connection data */
+       int *ip;
+       int num_ips;
+       int sd;
+
+       /* SSL encryption */
+       const SSL_METHOD *meth;
+       long ctx_options;
+       SSL_CTX *ctx;
+       SSL *ssl;
+
+       /* Transaction parameters, sent and received */
+       param *send_param_list, *send_param_tail;
+       param *recv_param_list;
+
+       /* Connection status */
+       int is_error;
+       int pass;
+       time_t start_time;
+       int dns;
+
+       char * trusted_ca_pem;
+       int (*validate_cert)(int, void *);
+       int full_ssl_close;
+
+} TCLinkCon;
+
+/*************************************
+ * Internal functions, not exported. *
+ *************************************/
+
+/* Random number from min to max. */
+static int number(int min, int max)
+{
+       return (rand() % (max - min + 1)) + min;
+}
+
+/* Check if path points to a regular file */
+int is_regular_file(const char* path)
+{
+       struct stat st;
+       stat(path, &st);
+       return S_ISREG(st.st_mode);
+}
+
+/* Safe string copy and append functions. */
+#define SAFE_COPY(d, s)    safe_copy((d), (s), sizeof(d));
+#define SAFE_APPEND(d, s)  safe_append((d), (s), sizeof(d));
+
+static void safe_copy(char *dst, const char *src, int size)
+{
+  int len = strlen(src);
+  if (len < size)
+    strcpy(dst, src);
+  else {
+    strncpy(dst, src, size - 1);
+    dst[size-1] = 0;
+  }
+}
+
+static void safe_append(char *dst, const char *src, int size)
+{
+  int dlen = strlen(dst);
+  int slen = strlen(src);
+       int avail = size - dlen;
+       if (avail < 1)
+               return;
+
+  if (slen < avail)
+    strcpy(dst+dlen, src);
+  else {
+    strncpy(dst+dlen, src, avail - 1);
+    dst[size-1] = 0;
+  }
+}
+
+/* Add a parameter-value pair to the recieved list. */
+static void AddRecvParam(TCLinkCon *c, const char *name, const char *value)
+{
+       param *p;
+
+       if (name[0] == 0 || value[0] == 0)
+               return;
+
+       p = (param *)malloc(sizeof(param));
+       p->name = strdup(name);
+       p->value = strdup(value);
+       p->next = c->recv_param_list;
+       c->recv_param_list = p;
+}
+
+/* Add a string to the received list. */
+static int AddRecvString(TCLinkCon *c, char *string)
+{
+       char *ptr = strchr(string, '=');
+       if (ptr == NULL)
+               return 0;
+
+       *ptr = 0;
+       AddRecvParam(c, string, ptr+1);
+
+       return 1;
+}
+
+/* Deallocate the send list. */
+static void ClearSendList(TCLinkCon *c)
+{
+       param *p, *next;
+       for (p = c->send_param_list; p; p = next)
+       {
+               next = p->next;
+               free(p->name);
+               free(p->value);
+               free(p);
+       }
+
+       c->send_param_list = c->send_param_tail = NULL;
+}
+
+/* Deallocate the recv list. */
+static void ClearRecvList(TCLinkCon *c)
+{
+       param *p, *next;
+       for (p = c->recv_param_list; p; p = next)
+       {
+               next = p->next;
+               free(p->name);
+               free(p->value);
+               free(p);
+       }
+
+       c->recv_param_list = NULL;
+}
+
+/* Open a socket to the host_ip specified.  Returns the socket's file
+ * descriptor on success (the open attempt is underway) or -1 for failure
+ * (should never happen in practice).  Note that this function DOES NOT block
+ * and wait for the connection; you'll need to select() on the socket later to see
+ * if it opened successfully.
+ */
+static int BeginConnection(TCLinkCon *c, int host_ip)
+{
+       struct sockaddr_in sa;
+       int sd;
+
+       sd = socket(AF_INET, SOCK_STREAM, 0);
+       if (sd < 0)
+               return -1;
+
+#ifdef WIN32
+       u_long param = 1;
+       ioctlsocket(sd, FIONBIO, &param);
+#else
+       fcntl(sd, F_SETFL, O_NONBLOCK);
+#endif
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sin_family = AF_INET;
+       sa.sin_addr.s_addr = host_ip;
+       sa.sin_port = htons(tclink_port);
+
+       connect(sd, (struct sockaddr *) &sa, sizeof(sa));
+
+       return sd;
+}
+
+/* This function is called on a socket file descriptor once the connection has been
+ * established and we're ready to negotiate SSL.  If the SSL handshake fails for some
+ * reason (such as the host on the other end not using SSL), it will return 0 for
+ * failure.  Success returns 1.
+ */
+static int FinishConnection(TCLinkCon *c, int sd)
+{
+       int ssl_connected, is_error, errcode, res;
+       X509 *server_cert;
+       time_t start, remaining;
+       fd_set in, out, err;
+       struct timeval tv;
+
+       /* check if socket has connected successfully */
+       int val;
+       int /*socklen_t*/ size = 4;
+       getsockopt(sd, SOL_SOCKET, SO_ERROR, (char*)&val, &size);
+       if (val != 0)
+               return 0;
+
+       SSL_clear(c->ssl);
+
+       SSL_set_fd(c->ssl, sd);
+
+       ssl_connected = 0;
+       is_error = 0;
+       start = time(0);
+
+       while (!ssl_connected && !is_error)
+       {
+
+               remaining = 5 - (time(0) - start);
+               if (remaining <= 0) {
+                       is_error = 1;
+                       break;
+               }
+
+               res = SSL_connect(c->ssl);
+
+               ssl_connected = ((res == 1) && SSL_is_init_finished(c->ssl));
+
+               if (!ssl_connected)
+               {
+                       FD_ZERO(&in); FD_SET((unsigned)sd, &in);
+                       FD_ZERO(&out); FD_SET((unsigned)sd, &out);
+                       FD_ZERO(&err); FD_SET((unsigned)sd, &err);
+                       /* the documentation does not suggest that both error types occur at the same time so
+                        * the retry logic will consume all the outstanding events
+                        * we do not actually use oob data, but if it is sent, it is treated as an error all the
+                        * same
+                        */
+                       errcode = SSL_get_error(c->ssl, res);
+                       switch (errcode)
+                       {
+                               case SSL_ERROR_NONE:
+                                       /* no error, we should have a connection, check again */
+                                       break;
+
+                               case SSL_ERROR_WANT_READ:
+                                       /* no error, just wait for more data */
+                                       tv.tv_sec = remaining; tv.tv_usec = 0;
+                                       /* posix-2001 says the function will modify the appropriate descriptors */
+                                       if (select(sd+1, &in, NULL, &err, &tv) < 0 ||
+                                               FD_ISSET((unsigned)sd, &err)
+                                               )
+                                               is_error = 1;
+                                       break;
+                               case SSL_ERROR_WANT_WRITE:
+                                       /* no error, just wait for more data */
+                                       tv.tv_sec = remaining; tv.tv_usec = 0;
+                                       if (select(sd+1, NULL, &out, &err, &tv) < 0 ||
+                                               FD_ISSET((unsigned)sd, &err)
+                                               )
+                                               is_error = 1;
+                                       break;
+                               case SSL_ERROR_ZERO_RETURN: /* peer closed the connection */
+                               case SSL_ERROR_SSL:         /* error in SSL handshake */
+                               default:
+                                       is_error = 1;
+                       }
+               }
+       }
+
+       if (is_error) {
+               return 0;
+       }
+   
+       
+#ifdef WIN32
+       u_long param = 0;
+       ioctlsocket(sd, FIONBIO, &param);           // make the socket blocking again 
+#else
+       fcntl(sd, F_SETFL, 0);           /* make the socket blocking again */
+#endif
+       
+       /* verify that server certificate is authentic */
+       server_cert = SSL_get_peer_certificate(c->ssl);
+       if (!server_cert) {
+               return 0;
+       }
+       if (c->validate_cert && c->validate_cert(0, server_cert) != 0)
+       {
+               X509_free(server_cert);
+               return 0;
+       }
+       X509_free(server_cert);
+
+       return 1;
+}
+
+/* This function should be called on list of socket file descriptors (sd) to determine
+ * if any have opened successfully.  If so, it will return which one (index into
+ * the array).  Otherwise it returns -1 if none have successfully opened.
+ * This function will block for a maximum of 3 seconds.
+ * As this function calls FinishConnection(), you shouldn't need to do anything special
+ * after it returns success - the socket is set up and ready for use.
+ */
+static int CheckConnection(TCLinkCon *c, int *sd, int num_sd)
+{
+       fd_set wr_set, err_set;
+       struct timeval tv;
+       int max_sd = -1, i;
+
+       tv.tv_sec = 3;        /* wait 3 seconds for soc->mething to happen */
+       tv.tv_usec = 0;
+
+       /* build the fd_sets used for select() */
+       FD_ZERO(&wr_set);
+       FD_ZERO(&err_set);
+       for (i = 0; i < num_sd; i++)
+       {
+               if (sd[i] < 0) continue;
+               FD_SET(sd[i], &wr_set);
+               FD_SET(sd[i], &err_set);
+               if (sd[i] > max_sd)
+                       max_sd = sd[i];
+       }
+
+       /* run the select and see what we have waiting for us */
+       if (select(max_sd + 1, NULL, &wr_set, &err_set, &tv) < 1)
+               return -1;     /* I hope this never happens */
+
+       for (i = 0; i < num_sd; i++)
+               if (sd[i] >= 0)
+               {
+                       if (FD_ISSET(sd[i], &err_set))
+                       {
+                               /* error - close the socket and mark it defunct */
+                               close(sd[i]);
+                               sd[i] = -1;
+                       }
+                       else if (FD_ISSET(sd[i], &wr_set))
+                       {
+                               /* socket has opened! try to negotiate SSL */
+                               if (FinishConnection(c, sd[i])) {
+                                       /* socket is ready to go, so return success */
+                                       c->sd = sd[i];
+                                       return i;
+                               }
+                               else {
+                                       /* SSL handshake had errors, close the socket and mark it defunct */
+                                       close(sd[i]);
+                                       sd[i] = -1;
+                               }
+                       }
+               }
+
+       /* if we get here, nothing much interesting happened during those 3 seconds */
+       return -1;
+}
+
+void do_SSL_randomize()
+{
+       enum { RAND_VALS = 32 };
+       int randbuf[RAND_VALS];
+       char fname[512];
+       int use_rand_file;
+       time_t t;
+       int i, c;
+
+       /* if they have a /dev/urandom we can skip this function */
+       if (RAND_status() != 0)
+               return;
+
+       t = time(0);
+       RAND_seed((char *)&t, sizeof(time_t));
+
+       /* have they specified a random file with RANDFILE environment variable? */
+       use_rand_file = RAND_file_name(fname, sizeof(fname)) ? 1 : 0;
+       if (use_rand_file)
+               RAND_load_file(fname, 4096);
+
+       /* stuff it with packets of random numbers until it is satisfied */
+       for (i = 0; i < 256 && RAND_status() == 0; i++)
+       {
+               for (c = 0; c < RAND_VALS; c++)
+                       randbuf[c] = rand();
+               RAND_seed((char *)randbuf, sizeof(int) * RAND_VALS);
+       }
+}
+
+/* Open a connection to one of the TrustCommerce gateway servers. */
+static int Connect(TCLinkCon *c, int host_hash)
+{
+       struct hostent default_he;
+       char *addr_list[3]; int addr[2];
+       struct hostent *he;
+       unsigned int **gw;
+
+       enum { MAX_HOSTS = 32 };
+       time_t last_connect[MAX_HOSTS];
+       int sd[MAX_HOSTS];
+       int num_sd = 0;
+       int host;
+
+       int i, j, sort, sort_val;
+
+       c->sd = -1;
+       c->is_error = 0;
+
+       srand(time(0));
+
+       /* These are used as BACKUP ONLY if the DNS if offline. */
+       addr[0] = inet_addr("207.38.46.42");
+       addr[1] = inet_addr("208.42.227.151");
+       addr_list[0] = (char *)&addr[0];
+       addr_list[1] = (char *)&addr[1];
+       addr_list[2] = 0;
+       default_he.h_addr_list = addr_list;
+
+       /* determine IP addresses of gateway */
+       if (!c->ip) 
+       {
+               he = gethostbyname(tclink_host);
+               if (he)
+                       c->dns = 1;
+               else {
+                       /* fall back to hardcoded IPs in an emergency */
+                       c->dns = 0;
+                       he = &default_he;
+               }
+
+               for (c->num_ips = 0; he->h_addr_list[c->num_ips]; c->num_ips++)
+                       ;
+
+               c->ip = (int *)malloc(c->num_ips * sizeof(int));
+               gw = (int unsigned **)he->h_addr_list;
+
+               /* sort the IP address list before storing it */
+               for (i = 0; i < c->num_ips; i++)
+               {
+                       sort = 0; sort_val = *gw[0];
+                       for (j = 1; j < c->num_ips; j++)
+                               if (*gw[j] > sort_val)
+                               {
+                                       sort = j;
+                                       sort_val = *gw[j];
+                               }
+
+                       c->ip[i] = sort_val;
+                       *gw[sort] = 0;
+               }
+       }
+
+       /* do some SSL setup */
+       if (!c->meth)
+       {
+               do_SSL_randomize();        /* handle systems without /dev/urandom */
+               SSLeay_add_ssl_algorithms();
+               c->meth = SSLv23_client_method();
+               c->ctx_options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;     // Disable all known SSL versions
+       }
+
+       if (!c->ctx)
+       {
+               int val;
+
+               c->ctx = SSL_CTX_new(c->meth);
+               if (!c->ctx) return 0;
+               /* set options */
+               if (c->ctx_options)
+                       SSL_CTX_set_options(c->ctx, c->ctx_options);
+
+               if (!c->trusted_ca_pem)
+               {
+                       int is_file = is_regular_file(TCLINK_CA_PATH);
+                       val = SSL_CTX_load_verify_locations(c->ctx, is_file?TCLINK_CA_PATH:NULL, is_file?NULL:TCLINK_CA_PATH);
+               }
+               else
+               {
+                       extern int SSL_CTX_load_verify_locations_mem(SSL_CTX*, const char *);
+                       val = SSL_CTX_load_verify_locations_mem(c->ctx, c->trusted_ca_pem);
+               }
+
+               if (!val) return 0;     // failed to populate cert store
+
+               /* turn on certificate chain validation */
+               SSL_CTX_set_verify(c->ctx, SSL_VERIFY_PEER, NULL);
+       }
+
+       if (!c->ssl)
+       {
+               c->ssl = SSL_new(c->ctx);
+               if (!c->ssl)
+               {
+                       SSL_CTX_free(c->ctx);
+                       return 0;
+               }
+       }
+
+       /* This loop works as follows:
+        * Grab the first host.  Try to open a connection to it.  If there was an
+        * error (host down or unreachable) go to the next one.  If nothing has happened
+        * after 3 seconds, open a second socket (the first one is still open!) and try
+        * with the next fail-over host.  Continue to do this for a maximum of MAX_HOSTS
+        * sockets, or until our TIMEOUT value runs out.  We also keep track of how recently
+        * we tried to connect to a given host, so that we avoid saturating the machines
+        * in a heavy-load situation (which could be caused by anything from heavy internet
+        * lag between the local host and the TrustCommerce servers, to heavy load on the
+        * servers themselves due to half a million people trying to run credit card
+        * transactions in the same half second - unlikely, but certainly possible.)
+        */
+       c->start_time = time(0);
+       c->pass = 1;
+       memset(last_connect, 0, MAX_HOSTS * sizeof(time_t));
+       host = host_hash % c->num_ips;
+
+       for ( ; time(0) < (c->start_time + TIMEOUT); c->pass++)
+       {
+               /* retry the first host at least once */
+               if (c->pass > 2) host += 1;
+               if (host >= c->num_ips) host = 0;
+
+               /* only connect if we haven't tried this host before, or it's been a little
+                * while (note random modifier to help stagger network traffic) */
+               if (last_connect[host] == 0 ||
+                   (time(0) - last_connect[host]) >= number(TIMEOUT / 4, TIMEOUT))
+               {
+                       if (num_sd < MAX_HOSTS)
+                       {
+                               /* fire up a new connection to this host */
+                               if (c->pass != 1)
+                                       last_connect[host] = time(0);
+
+                               sd[num_sd] = BeginConnection(c, c->ip[host]);
+                               if (sd[num_sd] >= 0)
+                                       num_sd++;
+                       }
+               }
+
+               /* scan all current sockets and see if we've made a successful connection
+                * somewhere.  note that this also includes SSL and all that sort of fun,
+                * so once it returns success, we're all done. */
+               if (num_sd > 0)
+               {
+                       if (CheckConnection(c, sd, num_sd) >= 0)
+                       {
+                               /* Success: close all other file handles and return */
+                               for (i = 0; i < num_sd; i++)
+                                       if (sd[i] >= 0 && sd[i] != c->sd)
+                                               close(sd[i]);
+
+                               return 1;
+                       }
+               }
+
+               usleep(1000);   // sleep for 1 millisecond
+       }
+
+       return 0;
+}
+
+/* Send a chunk of data through a connection previously opened with Connect(). */
+static int Send(TCLinkCon *c, const char *string)
+{
+       if (SSL_write(c->ssl, string, strlen(string)) < 0)
+               return 0;
+
+       return 1;
+}
+
+/* Peel a line off the current input.  Note that this DOESN'T necessarily wait for all
+ * input to come in, only up to a "\n".  -1 is returned for a network error, otherwise
+ * it returns the length of the line read.  If there is not a complete line pending
+ * for read this will block until there is, or an error occurs.
+ */
+static int ReadLine(TCLinkCon *c, char *buffer, char *destbuf)
+{
+       struct timeval tv;
+       fd_set read;
+       fd_set error;
+       int sel;
+
+       while (1)      /* we wait for a line to come in or an error to occur */
+       {
+               char *eol = strchr(buffer, '\n');
+               if (eol != NULL)
+               {
+                       /* peel off the line and return it */
+                       *eol++ = 0;
+                       safe_copy(destbuf, buffer, TC_LINE_MAX);
+                       memmove(buffer, eol, strlen(eol)+1);
+                       return strlen(destbuf);
+               }
+               else
+               {       
+                       if (c->is_error == 1)
+                               return -1;
+
+                       /* do socket work to grab the most recent chunk of incoming data */
+                       FD_ZERO(&read);   FD_SET(c->sd, &read);
+                       FD_ZERO(&error);  FD_SET(c->sd, &error);
+                       tv.tv_sec = TIMEOUT;
+                       tv.tv_usec = 0;
+
+                       sel = select(c->sd+1, &read, NULL, &error, &tv);
+                       if (sel < 1)
+                               c->is_error = 1;
+                       else if (FD_ISSET(c->sd, &error))
+                               c->is_error = 1;
+                       else if (FD_ISSET(c->sd, &read))
+                       {
+                               int buffer_end = strlen(buffer);
+                               int size = SSL_read(c->ssl, buffer + buffer_end, TC_BUFF_MAX-1 - buffer_end);
+                               if (size == 0)
+                               {
+                                       int error_type = SSL_get_error(c->ssl, size);
+                                       switch (error_type)
+                                       {
+                                               /* this would never happen in practice */
+                                               case SSL_ERROR_NONE: 
+                                               /* this wouldn't happen either because the ssl transport is blocking */
+                                               case SSL_ERROR_WANT_READ:
+                                               case SSL_ERROR_WANT_WRITE:
+                                                       buffer[buffer_end] = 0;
+                                                       break;
+
+                                               /* these others should not really happen but if they do, we bail */
+                                               /* we would never get any more data and it looks like the callee is expecting something */
+                                               case SSL_ERROR_ZERO_RETURN:
+                                               case SSL_ERROR_WANT_CONNECT:
+                                               case SSL_ERROR_WANT_ACCEPT:
+                                               case SSL_ERROR_SYSCALL: 
+                                               case SSL_ERROR_WANT_X509_LOOKUP:
+                                               case SSL_ERROR_SSL:
+                                               default:
+                                                       c->is_error = 1;
+                                                       break;
+                                       }
+                               }
+                               else if (size < 0)
+                                       c->is_error = 1;
+                               else
+                                       buffer[buffer_end + size] = 0;
+                       }
+               }
+       }
+}
+
+/* Closes a connection opened with Connect() and frees memory associated with it.
+ * You ONLY need to Close() connections which opened successfully; those that don't
+ * clean up after themselves before Connect() returns.
+ */
+static int Close(TCLinkCon *c)
+{
+       if (c->ssl) 
+       {
+               /* The full shutdown presented here is more for completeness than necessity; at this point in the
+                * application, we have already received the end trailer (or bust) which is generally accompanied by
+                * a close notify message.  If the software chooses to respond to the close notify (per TLS specification)
+                * this would result in at least reading the incoming close notify and issuing our own.  Because this entails
+                * an additional round trip that is not needed (the transaction is done after the accompanying END), there
+                * does not appear to be a benefit to it at all.  By default though, this configuration is enabled and
+                * can be disabled by the integrator for performance reasons.
+                */
+               if (c->full_ssl_close)
+               {
+                       int status = SSL_shutdown(c->ssl);
+                       if (status == 0) status = SSL_shutdown(c->ssl);
+               }
+               else
+                       SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
+       }
+
+       if (c->sd >= 0) {
+               close(c->sd);
+               c->sd = -1;
+       }
+
+       if (c->trusted_ca_pem) {
+               free(c->trusted_ca_pem);
+               c->trusted_ca_pem = NULL;
+       }
+
+       return 1;
+}
+
+static void stuff_string(char *buf, int *len, int size, const char *add)
+{
+       int newlen = strlen(add);
+       if ((*len + newlen) >= size)
+               newlen = size - *len - 1;
+       if (newlen < 1) return;
+       strncpy(buf + *len, add, newlen);
+       *len += newlen;
+       buf[*len] = 0;
+}
+
+/**********************************************
+ * API functions exported to the user client. *
+ **********************************************/
+
+TCLinkHandle TCLinkCreate()
+{
+       extern int TCLinkDefaultValidate(int, void *);
+
+       TCLinkCon *c = (TCLinkCon *)malloc(sizeof(TCLinkCon));
+
+       c->ip = NULL;
+       c->num_ips = 0;
+       c->sd = -1;
+
+       c->meth = NULL;
+       c->ctx = NULL;
+       c->ssl = NULL;
+
+       c->send_param_list = NULL;
+       c->send_param_tail = NULL;
+       c->recv_param_list = NULL;
+
+       c->is_error = 0;
+       c->pass = 0;
+       c->start_time = 0;
+       c->dns = -1;
+
+       c->trusted_ca_pem = NULL;
+       c->validate_cert = TCLinkDefaultValidate;
+       c->full_ssl_close = 1;
+
+       return (TCLinkHandle)c;
+}
+
+int TCLinkSetFullClose(TCLinkHandle handle, int full_ssl_close)
+{
+       TCLinkCon *c = (TCLinkCon *)handle;
+       int swap = c->full_ssl_close;
+       c->full_ssl_close = full_ssl_close ? 1 : 0;
+       return swap;
+}
+
+void TCLinkSetTrustedCABundle(TCLinkHandle handle, const char *str, int len)
+{      
+       TCLinkCon *c = (TCLinkCon *)handle;
+       
+       if (c->trusted_ca_pem)
+                       free(c->trusted_ca_pem);
+       
+       if (str == NULL)
+       {
+               c->trusted_ca_pem = NULL;
+               return;
+       }
+       
+       c->trusted_ca_pem = malloc(len+1);
+       strncpy(c->trusted_ca_pem,str,len);
+       c->trusted_ca_pem[len] = 0;
+}
+
+void TCLinkSetValidateCallback(TCLinkHandle handle, int (*validate_cert)(int, void *))
+{
+       TCLinkCon *c = (TCLinkCon *)handle;
+       if (validate_cert == NULL)
+       {
+               extern int TCLinkDefaultValidate(int, void *);
+               c->validate_cert = TCLinkDefaultValidate;
+       }
+       else
+               c->validate_cert = validate_cert;
+}
+void TCLinkPushParam(TCLinkHandle handle, const char *name, const char *value)
+{
+       param *p;
+       char *ch;
+
+       TCLinkCon *c = (TCLinkCon *)handle;
+
+       if (name && value)
+       {
+               p = (param *)malloc(sizeof(param));
+               p->name = strdup(name);
+               p->value = strdup(value);
+               p->next = NULL;
+               if (c->send_param_tail)
+                       c->send_param_tail->next = p;
+               else
+                       c->send_param_list = p;
+               c->send_param_tail = p;
+
+               /* remove newlines and equals signs from the parameter name */
+               for (ch = p->name; *ch; ch++)
+                       if (*ch == '=' || *ch == '\n') *ch = ' ';
+
+               /* remove newlines from the value */
+               for (ch = p->value; *ch; ch++)
+                       if (*ch == '\n') *ch = ' ';
+       }
+}
+
+void TCLinkSend(TCLinkHandle handle)
+{
+       param *p, *next;
+       char buf[TC_BUFF_MAX], destbuf[TC_LINE_MAX];
+       char buf2[1024];
+       int host_hash = 1;
+       int retval = 0;
+
+       TCLinkCon *c = (TCLinkCon *)handle;
+
+       ClearRecvList(c);
+
+       /* build most of the string we will send to the processor */
+       sprintf(buf, "BEGIN\nversion=%s\n", tclink_version);
+
+       for (p = c->send_param_list; p; p = next)
+       {
+               next = p->next;
+               SAFE_COPY(buf2, p->name);
+               SAFE_APPEND(buf2, "=");
+               SAFE_APPEND(buf2, p->value);
+               SAFE_APPEND(buf2, "\n");
+               SAFE_APPEND(buf, buf2);
+               if (!strcasecmp(p->name, "custid")) {
+                       host_hash = atoi(p->value);
+                       host_hash = (host_hash / 100) + (host_hash % 100);
+               }
+               free(p->name);
+               free(p->value);
+               free(p);
+       }
+
+       c->send_param_list = c->send_param_tail = NULL;
+
+       /* try to make the connection */
+       if (!Connect(c, host_hash))
+       {
+               Close(c);  /* clean up any memory Connect() may have left lying around */
+               AddRecvParam(c, "status", "error");
+               AddRecvParam(c, "errortype", "cantconnect");
+               return;
+       }
+
+       /* append some data about the connection */
+       sprintf(buf+strlen(buf), "pass=%d\ntime=%ld\n", c->pass, time(0) - c->start_time);
+       if (c->dns != 1) SAFE_APPEND(buf, "dns=n\n");
+       SAFE_APPEND(buf, "END\n");
+
+       /* send the data */
+       if (Send(c, buf))
+       {
+               int state = 0;
+               buf[0] = destbuf[0] = 0;          /* recycle buf */
+               c->is_error = 0;
+               while (1)
+               {
+                       int len = ReadLine(c, buf, destbuf);
+                       if (len == 0) continue;
+                       if (len < 0) break;
+                       if (strcasecmp(destbuf, "BEGIN") == 0)
+                       {
+                               if (state != 0)
+                                       { state = -1; break; }
+                               state = 1;
+                       }
+                       else if (strcasecmp(destbuf, "END") == 0)
+                       {
+                               state = (state != 1) ? -1 : 2;
+                               break;
+                       }
+                       else
+                       {
+                               if (state != 1 || !AddRecvString(c, destbuf))
+                                       { state = -1; break; }
+                       }
+               }
+               if (state == 2)
+                       retval = 1;
+       }
+
+       Close(c);
+
+       if (!retval)
+       {
+               ClearRecvList(c);
+               AddRecvParam(c, "status", "error");
+               AddRecvParam(c, "errortype", "linkfailure");
+       }
+}
+char *TCLinkGetResponse(TCLinkHandle handle, const char *name, char *value)
+{
+       param *p;
+       TCLinkCon *c = (TCLinkCon *)handle;
+
+       for (p = c->recv_param_list; p; p = p->next)
+               if (strcasecmp(name, p->name) == 0)
+               {
+                       safe_copy(value, p->value, PARAM_MAX_LEN);
+                       return value;
+               }
+
+       return NULL;
+}
+
+char *TCLinkGetEntireResponse(TCLinkHandle handle, char *buf, int size)
+{
+       param *p;
+       int len = 0;
+       TCLinkCon *c = (TCLinkCon *)handle;
+
+       for (p = c->recv_param_list; p; p = p->next) {
+               stuff_string(buf, &len, size, p->name);
+               stuff_string(buf, &len, size, "=");
+               stuff_string(buf, &len, size, p->value);
+               stuff_string(buf, &len, size, "\n");
+       }
+
+       return buf;
+}
+
+void TCLinkDestroy(TCLinkHandle handle)
+{
+       TCLinkCon *c = (TCLinkCon *)handle;
+       if (!c) return;
+
+       ClearSendList(c);
+       ClearRecvList(c);
+       Close(c);
+
+       if (c->ip)
+               free(c->ip);
+
+       if (c->ssl) {
+               SSL_free(c->ssl);
+               c->ssl = NULL;
+       }
+
+       if (c->ctx) {
+               SSL_CTX_free(c->ctx);
+               c->ctx = NULL;
+       }
+
+       free(c);
+}
+
+char *TCLinkGetVersion(char *buf)
+{
+       strcpy(buf, tclink_version);
+       return buf;
+}
+
diff --git a/tclink.h b/tclink.h
new file mode 100644 (file)
index 0000000..83862ef
--- /dev/null
+++ b/tclink.h
@@ -0,0 +1,83 @@
+/* tclink.h - Header file for TCLink library.
+ *
+ * TCLink Copyright (c) 2013 TrustCommerce.
+ * http://www.trustcommerce.com
+ * techsupport@trustcommerce.com
+ * (949) 387-3747
+ *
+ * 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 (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _TCLINK_H
+#define _TCLINK_H
+
+#include "config.h"
+
+/* Handle passed to all TCLink functions.  A unique handle must be created
+ * for each concurrent thread, but the same handle can be shared by transactions
+ * occurring one after another (such as a for loop).
+ */
+#define TCLinkHandle void *
+
+/* Parameter names and values cannot exceed this size.
+ */
+#define PARAM_MAX_LEN 256
+
+/* Create a new TCLinkHandle.
+ */
+TCLinkHandle TCLinkCreate();
+
+/* Add a parameter to be sent to the server.
+ */
+void TCLinkPushParam(TCLinkHandle handle, const char *name, const char *value);
+
+/* Flush the parameters to the server.
+ */
+void TCLinkSend(TCLinkHandle handle);
+
+/* Look up a response value from the server.
+ * Returns NULL if no such parameter, or stores the value in 'value' and
+ * returns a pointer to value.  value should be at least PARAM_MAX_LEN in size.
+ */
+char *TCLinkGetResponse(TCLinkHandle handle, const char *name, char *value);
+
+/* Get all response values from the server in one giant string.
+ * Stores the string into buf and returns a pointer to it.  Size should be
+ * sizeof(buf), which will limit the string so that no buffer overruns occur.
+ */
+char *TCLinkGetEntireResponse(TCLinkHandle handle, char *buf, int size);
+
+/* Destory a handle, ending that transaction and freeing the memory associated with it. */
+void TCLinkDestroy(TCLinkHandle handle);
+
+/* Store version string into buf.  Returns a pointer to buf. */
+char *TCLinkGetVersion(char *buf);
+
+
+/* The API methods below are subject to change. */
+
+/* Enables (1) or Disables (0) the full SSL close_notify sequence.  By default, this is set to 1.*/
+int TCLinkSetFullClose(TCLinkHandle handle, int full_ssl_close);
+
+/* Provides a method, before the first send call is initiated, of loading a set of trusted CA certificates (PEM format). */
+void TCLinkSetTrustedCABundle(TCLinkHandle handle, const char *str, int len);
+
+/* Provides a method, once the handshake is completed, a means to verify the contents of that certificate independently.  
+ * Note that the certificate may not be set depending on the negotation type (in which case the pointer would be NULL)
+ */
+void TCLinkSetValidateCallback(TCLinkHandle handle, int (*validate_cert)(int, void *));
+
+#endif
+
diff --git a/tctest.php b/tctest.php
new file mode 100644 (file)
index 0000000..1edab30
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+       // The ugly bit of code below loads the TCLink extension if it is not
+       // already.  You can skip all of this if "extension=tclink.so" is in
+       // your php.ini, or you can make a single call to dl("tclink") if
+       // tclink.so is in your PHP extensions path ("extension_dir").
+       if (!extension_loaded("tclink"))
+       {
+        echo "TCLink not loaded.  Try running as `php -d extension=modules/tclink.so tctest.php`\n";
+        exit(1);
+       }
+
+       print "\nTCLink version " . tclink_getversion() . "\n";
+       print "Sending transaction...";
+
+       // Build a hash containing our parameters
+       $params['custid'] = 'XXXX';
+       $params['password'] = 'PPPP';
+       $params['action'] = 'sale';
+       $params['media'] = 'cc';
+       $params['cc'] = '4111111111111111';
+       $params['exp'] = '0110';
+       $params['amount'] = '100';
+       $params['name'] = 'Joe PHP';
+
+       // Send the hash to TrustCommerce for processing
+       $result = tclink_send($params);
+
+       print "done!\n\nTransaction results:\n";
+
+       // Print out all parameters returned
+       while (list($key, $val) = each($result))
+               print "\t$key=$val\n";
+?>
+
diff --git a/validate.c b/validate.c
new file mode 100644 (file)
index 0000000..3c7dcc5
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+COPYRIGHT AND PERMISSION NOTICE
+Copyright (c) 1996 - 2010, Daniel Stenberg, <daniel@haxx.se>.
+All rights reserved.
+Permission to use, copy, modify, and distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright
+notice and this permission notice appear in all copies.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
+NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+Except as contained in this notice, the name of a copyright holder shall not
+be used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization of the copyright holder.
+*/
+/* simplified to a basic host name check */
+#include <string.h>
+#include <openssl/x509_vfy.h>
+#include <openssl/x509v3.h>
+
+#define bool int
+#define false 0
+#define true 1
+/** @fn static bool cert_hostcheck(const char *hostname, char *pattern)
+  * Verifies that the hostname matches against the pattern specified.
+  * Handles wildcard patterns and ignores the distinction between upper and lower case letters.
+  * Note: Ported over from ssluse.c in curl (7.1.16) lib
+  * Note: Explicit pattern match disabled as we do not use that for processing node certificate.
+  * Note: No longer ignores the distinction between upper and lower case letters.  Our certificate is generated with lowercase letters.
+  * @return true if matches, false otherwise
+  * @param hostname The hostname we want to check. e.g: vault.trustcommerce.com
+  * @param pattern The pattern we wish to match against. e.g: *.trustcommerce.com
+  */
+bool cert_hostcheck(const char *pattern, const char *hostname)
+{
+       if (!hostname || !pattern || !*hostname || !*pattern) return false;
+       if (!strcmp(hostname,pattern)) return true;
+       return false;
+}
+/** @fn static bool checkCertificate(X509 *cert, char *host)
+  * Provides validation of the hostname associated with a certificate.
+  * See RFC2818 - Server Identity for an overview of the concept.
+  * This implementation is based off the one found in curl-7.16.1: ssluse.c
+  * but we treat the subjectAltName as a recommendation... so if it fails, 
+  * we will proceed to the CN check.
+  * The rationale for this is that we are not always using HTTP (over SSL)
+  * and its more of a certification generation / CA issue and we want
+  * maximum interoperability (as opposed to strict compliance). 
+  * @param cert The X509 certificate in question.
+  * @param host The hostname or ip we wish to check.
+  * @return true if matches, false otherwise
+  */
+static bool checkCertificate(X509 * cert, const char *host)
+{
+       int i,j;
+       bool matched = false;
+       STACK_OF(GENERAL_NAME) * altnames;
+       unsigned char *nulstr = { '\0' };
+       unsigned char *peer_CN = nulstr;
+       X509_NAME *name;
+       ASN1_STRING * tmp;
+       bool status = false;
+
+       if (!cert || !host) return false;
+
+       altnames = (STACK_OF(GENERAL_NAME) *)(X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL));
+
+       if (altnames != NULL)
+       {
+               int numalts = sk_GENERAL_NAME_num(altnames);
+               for (i=0; (i<numalts) && (matched == false); i++)
+               {
+                       const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i);
+                       const char *altptr = (char *)(ASN1_STRING_data(check->d.ia5));
+                       size_t altlen;
+                       switch (check->type)
+                       {
+                               case GEN_DNS: 
+                                       altlen = ASN1_STRING_length(check->d.ia5);
+                                       if (altlen == strlen(host) && cert_hostcheck(altptr, host))
+                                               matched = true;
+                                       break;
+                               case GEN_IPADD:
+                                       altlen = ASN1_STRING_length(check->d.ia5);
+                                       if (altlen == strlen(host) && !memcmp(altptr, host, altlen))
+                                               matched = true;
+                                       break;
+                       }
+               }
+               GENERAL_NAMES_free(altnames);
+               if (matched != false) return true;
+       }
+       
+       i = j = -1;
+
+
+       name = X509_get_subject_name(cert);
+       if (!name) return false;
+
+
+       // get the last CN found in the subject (supposedly its the most distinguished one)
+       while ((j=X509_NAME_get_index_by_NID(name,NID_commonName,i))>=0)
+               i=j;
+
+       if (i<0) return false;
+
+       tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i));
+       /* workaround for version of openssl < 0.9.7d */
+       if (tmp && ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING)
+       {
+               j = ASN1_STRING_length(tmp);
+               if (j >= 0) {
+                       peer_CN = (unsigned char *)(OPENSSL_malloc(j+1));
+                       if (peer_CN)
+                       {
+                               memcpy(peer_CN, ASN1_STRING_data(tmp), j);
+                               peer_CN[j] = '\0';
+                       }
+               }
+       }
+       else
+       {
+               j = ASN1_STRING_to_UTF8(&peer_CN, tmp);
+       }
+
+       if (peer_CN == nulstr)
+               peer_CN = NULL;
+
+       if (peer_CN == NULL)
+               return false; // the cn isnt missing in virtually all cases
+       else if(!cert_hostcheck((char *)(peer_CN), host))
+               status = false;
+       else 
+               status = true;
+
+       if (peer_CN)
+               OPENSSL_free(peer_CN);
+       return status;
+}
+
+int TCLinkDefaultValidate(int x, void * cert)
+{
+       if (x != 0 || cert == NULL) return 0;
+       return !checkCertificate((X509 *)cert, "pgw1.trustcommerce.com");
+
+}