mirror of
https://github.com/php/php-src.git
synced 2024-09-25 03:47:26 +00:00
new credit card processing module for TrustCommerce
http://trustcommerce.com/tclink.html
This commit is contained in:
parent
60ad61def7
commit
ee250fb4d6
65
ext/tclink/README
Normal file
65
ext/tclink/README
Normal file
@ -0,0 +1,65 @@
|
||||
|
||||
|
||||
TCLink v3.3
|
||||
PHP Implementation
|
||||
copyright (C) TrustCommerce 2002
|
||||
http://www.trustcommerce.com
|
||||
developer@trustcommerce.com
|
||||
|
||||
July 17, 2002
|
||||
|
||||
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 using Python on Win32, do not use this client. Please
|
||||
download the COM object from the Vault website instead.
|
||||
|
||||
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. COMPILATION
|
||||
|
||||
If you would like to recompile PHP with support for TCLink, be sure
|
||||
to run "./configure --with-openssl" first, as TCLink relies on OpenSSL.
|
||||
Note that if your PHP installation already includes TCLink, there is
|
||||
no need to do any compilation.
|
||||
|
||||
|
||||
III. 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/php4) if it is not already installed there by
|
||||
default. 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.
|
||||
|
||||
|
||||
IV. 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.
|
||||
|
||||
A note to users of TCLink PHP prior to 3.3: the API has changed from
|
||||
the C-style interface, to a single tclink_send() function which accepts
|
||||
a hash (associative array) of input parameters, and returns a hash with
|
||||
the output parameters. Please see the examples to update your code.
|
6
ext/tclink/config.m4
Normal file
6
ext/tclink/config.m4
Normal file
@ -0,0 +1,6 @@
|
||||
if test "$PHP_TCLINK" != "no"; then
|
||||
PHP_NEW_EXTENSION(tclink, tclink.c, $ext_shared)
|
||||
TCLINK_SHARED_LIBADD="-lcrypto -lssl"
|
||||
PHP_SUBST(TCLINK_SHARED_LIBADD)
|
||||
AC_DEFINE(HAVE_TCLINK_EXT,1,[ ])
|
||||
fi
|
1039
ext/tclink/doc/TCDevGuide.html
Normal file
1039
ext/tclink/doc/TCDevGuide.html
Normal file
File diff suppressed because it is too large
Load Diff
1459
ext/tclink/doc/TCDevGuide.txt
Normal file
1459
ext/tclink/doc/TCDevGuide.txt
Normal file
File diff suppressed because it is too large
Load Diff
123
ext/tclink/php_tclink.c
Normal file
123
ext/tclink/php_tclink.c
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* TCLink PHP Module
|
||||
*
|
||||
* TCLink Copyright (c) 2002 TrustCommerce.
|
||||
* http://www.trustcommerce.com
|
||||
* developer@trustcommerce.com
|
||||
* (626) 744-7700
|
||||
*/
|
||||
|
||||
#include "php.h"
|
||||
#include "php_config.h"
|
||||
#include "php_tclink.h"
|
||||
|
||||
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), STANDARD_MODULE_PROPERTIES
|
||||
};
|
||||
|
||||
#define PHP_TCLINK_DEFAULT_BUFFER_SIZE 8196
|
||||
|
||||
#ifdef COMPILE_DL_TCLINK
|
||||
ZEND_GET_MODULE(php_tclink)
|
||||
|
||||
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)
|
||||
{
|
||||
pval **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, ¶ms) == 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 */
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ proto string tclink_getversion()
|
||||
returns the API version information */
|
||||
PHP_FUNCTION(tclink_getversion)
|
||||
{
|
||||
char version[1024];
|
||||
|
||||
TCLinkGetVersion(version);
|
||||
RETURN_STRING(version, 1);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
#endif
|
68
ext/tclink/php_tclink.h
Normal file
68
ext/tclink/php_tclink.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* TCLink PHP Module
|
||||
*
|
||||
* TCLink Copyright (c) 2002 TrustCommerce.
|
||||
* http://www.trustcommerce.com
|
||||
* developer@trustcommerce.com
|
||||
* (626) 744-7700
|
||||
*/
|
||||
|
||||
#ifndef PHP_TCLINK_H
|
||||
#define PHP_TCLINK_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);
|
||||
|
||||
#if HAVE_TCLINK
|
||||
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
|
||||
|
||||
#define phpext_tclink_ptr tclink_module_ptr
|
||||
|
||||
#endif /* PHP_TCLINK_H */
|
101
ext/tclink/tcexample.php
Normal file
101
ext/tclink/tcexample.php
Normal file
@ -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.
|
||||
|
||||
if (!$custid) $custid = "TestMerchant";
|
||||
if (!$password) $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="<?= $custid ?>"> </td></tr>
|
||||
<tr><th align=right> Password: </td><td> <input type="text" name="password" value="<?= $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 ($Action == 'Process')
|
||||
{
|
||||
$tclink['custid'] = $custid;
|
||||
$tclink['password'] = $password;
|
||||
$tclink['action'] = $action;
|
||||
if (is_numeric($amount))
|
||||
$tclink['amount'] = $amount;
|
||||
|
||||
if ($action == 'sale' || $action == 'preauth')
|
||||
{
|
||||
$tclink['name'] = $name;
|
||||
$tclink['cc'] = $cc;
|
||||
$tclink['exp'] = $mm . $yy;
|
||||
}
|
||||
else if ($action == 'credit' || $action == 'postauth')
|
||||
{
|
||||
$tclink['transid'] = $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", $tclink['status']);
|
||||
switch($tclink['status'])
|
||||
{
|
||||
case 'accepted':
|
||||
case 'approved':
|
||||
break;
|
||||
|
||||
case 'decline':
|
||||
case 'rejected':
|
||||
printf("<tr><th>Decline Type:</th><td>%s</td></tr>\n", $tclink['declinetype']);
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
printf("<tr><th>Error Type</th><td>%s</td></tr>\n", $tclink['errortype']);
|
||||
break;
|
||||
|
||||
case 'baddata':
|
||||
printf("<tr><th>Offenders:</th><td>%s</td></tr>\n", $tclink['$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", $key, $value);
|
||||
}
|
||||
?>
|
||||
|
||||
</table>
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
877
ext/tclink/tclink.c
Normal file
877
ext/tclink/tclink.c
Normal file
@ -0,0 +1,877 @@
|
||||
/* tclink.c - Library code for the TCLink client API.
|
||||
*
|
||||
* TCLink Copyright (c) 2002 TrustCommerce.
|
||||
* http://www.trustcommerce.com
|
||||
* developer@trustcommerce.com
|
||||
* (626) 744-7700
|
||||
*/
|
||||
|
||||
#include "php_tclink.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/poll.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#define DEFAULT_HOST "gateway.trustcommerce.com"
|
||||
#define TIMEOUT 40 /* seconds */
|
||||
|
||||
#define TC_BUFF_MAX 16000
|
||||
#define TC_LINE_MAX ((PARAM_MAX_LEN * 2) + 2)
|
||||
|
||||
char *tclink_version = "3.3-PHP";
|
||||
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 */
|
||||
X509 *tc_cert;
|
||||
SSL_METHOD *meth;
|
||||
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;
|
||||
|
||||
} TCLinkCon;
|
||||
|
||||
/* The TrustCommerce certificate. */
|
||||
unsigned char cert_data[540]={
|
||||
0x30,0x82,0x02,0x18,0x30,0x82,0x01,0x81,0x02,0x01,0x02,0x30,0x0D,0x06,0x09,0x2A,
|
||||
0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x04,0x05,0x00,0x30,0x55,0x31,0x0B,0x30,0x09,
|
||||
0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x14,0x30,0x12,0x06,0x03,0x55,
|
||||
0x04,0x08,0x13,0x0B,0x4C,0x6F,0x73,0x20,0x41,0x6E,0x67,0x65,0x6C,0x65,0x73,0x31,
|
||||
0x17,0x30,0x15,0x06,0x03,0x55,0x04,0x0A,0x13,0x0E,0x54,0x72,0x75,0x73,0x74,0x20,
|
||||
0x43,0x6F,0x6D,0x6D,0x65,0x72,0x63,0x65,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04,
|
||||
0x03,0x13,0x0E,0x50,0x43,0x41,0x20,0x28,0x31,0x30,0x32,0x34,0x20,0x62,0x69,0x74,
|
||||
0x29,0x30,0x1E,0x17,0x0D,0x30,0x30,0x30,0x34,0x32,0x39,0x30,0x35,0x30,0x39,0x30,
|
||||
0x34,0x5A,0x17,0x0D,0x30,0x34,0x30,0x34,0x32,0x39,0x30,0x35,0x30,0x39,0x30,0x34,
|
||||
0x5A,0x30,0x54,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,
|
||||
0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x08,0x13,0x0B,0x4C,0x6F,0x73,0x20,0x41,
|
||||
0x6E,0x67,0x65,0x6C,0x65,0x73,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04,0x0A,0x13,
|
||||
0x0E,0x54,0x72,0x75,0x73,0x74,0x20,0x43,0x6F,0x6D,0x6D,0x65,0x72,0x63,0x65,0x31,
|
||||
0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x03,0x13,0x0D,0x43,0x41,0x20,0x28,0x31,0x30,
|
||||
0x32,0x34,0x20,0x62,0x69,0x74,0x29,0x30,0x81,0x9F,0x30,0x0D,0x06,0x09,0x2A,0x86,
|
||||
0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x81,0x8D,0x00,0x30,0x81,0x89,
|
||||
0x02,0x81,0x81,0x00,0xBD,0x7C,0x7B,0x6F,0x77,0x46,0xE3,0x0F,0xF8,0x50,0x89,0x06,
|
||||
0xFC,0x54,0x5A,0x59,0x30,0x55,0xC6,0x00,0x34,0x6F,0x6B,0x64,0x8E,0x11,0x3C,0xDD,
|
||||
0xA9,0x0D,0xC5,0xE1,0x1C,0x49,0xF7,0x0A,0x6B,0x3E,0xAA,0x98,0xA4,0xA2,0x8D,0xEF,
|
||||
0x9A,0xCB,0xA2,0x40,0x87,0x3B,0x4B,0x13,0x73,0xE6,0x6C,0x39,0x1C,0x48,0xBE,0x6C,
|
||||
0x1C,0x78,0x0F,0x8E,0x40,0x27,0xAD,0x61,0x0E,0x5E,0x1F,0x94,0xD7,0xAB,0x61,0x3C,
|
||||
0xB1,0xF4,0xC9,0xE2,0x0D,0x05,0x83,0xE8,0x75,0xAB,0x64,0x12,0x39,0xAB,0xEF,0x79,
|
||||
0x53,0x49,0x48,0xA0,0x9C,0x55,0xD2,0xE3,0xD0,0x25,0x94,0x78,0x69,0x03,0x95,0xBA,
|
||||
0x68,0xC6,0x35,0xFB,0x54,0x3D,0x05,0x6D,0xAD,0x50,0x5F,0xE7,0x63,0xB9,0x4A,0x28,
|
||||
0xB0,0xB3,0xE2,0x07,0x02,0x03,0x01,0x00,0x01,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,
|
||||
0x86,0xF7,0x0D,0x01,0x01,0x04,0x05,0x00,0x03,0x81,0x81,0x00,0x34,0xED,0xF6,0x9D,
|
||||
0x32,0x51,0xCA,0x22,0xD6,0x8C,0x81,0x6F,0x64,0x41,0x7D,0xC0,0xD8,0x66,0xB7,0x2C,
|
||||
0x65,0x54,0x8A,0xE4,0x21,0x6A,0xF2,0x0F,0x3F,0xD0,0x85,0xDC,0x15,0xC1,0x5C,0x72,
|
||||
0x9A,0x0F,0x00,0x8D,0x38,0x59,0x70,0x7A,0x9C,0x40,0xA6,0x9A,0x2D,0x0E,0xA0,0x31,
|
||||
0x61,0x2E,0xA2,0x77,0x11,0xBB,0x20,0xF8,0xF9,0x28,0x10,0x1E,0x12,0xB1,0x9D,0x29,
|
||||
0xF7,0x86,0x12,0x05,0x83,0x83,0xE3,0xC3,0x82,0x65,0x97,0xE9,0xC2,0x5B,0x09,0x11,
|
||||
0x1D,0xF1,0x01,0x37,0x20,0x2E,0xC5,0x69,0x9C,0xED,0xE3,0xC1,0x29,0x1B,0x3D,0x47,
|
||||
0x72,0xED,0xA1,0x7B,0xE4,0x8B,0x2B,0x18,0x39,0xEA,0xDE,0x54,0x69,0xE7,0x35,0xDB,
|
||||
0x8F,0xFB,0x34,0xC7,0xF7,0xB3,0x6A,0x9A,0xE5,0x27,0xA4,0x0F};
|
||||
|
||||
|
||||
/*************************************
|
||||
* Internal functions, not exported. *
|
||||
*************************************/
|
||||
|
||||
/* Random number from min to max. */
|
||||
static int number(int min, int max)
|
||||
{
|
||||
return (rand() % (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
/* 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));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
fcntl(sd, F_SETFL, O_NONBLOCK);
|
||||
|
||||
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, n;
|
||||
struct pollfd pfd;
|
||||
X509 *server_cert;
|
||||
time_t start, remaining;
|
||||
|
||||
/* check if socket has connected successfully */
|
||||
int val;
|
||||
int /*socklen_t*/ size = 4;
|
||||
getsockopt(sd, SOL_SOCKET, SO_ERROR, &val, &size);
|
||||
if (val != 0)
|
||||
return 0;
|
||||
|
||||
c->ssl = SSL_new(c->ctx);
|
||||
if (!c->ssl)
|
||||
return 0;
|
||||
|
||||
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)
|
||||
{
|
||||
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:
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
/* no error, just wait for more data */
|
||||
pfd.fd = sd; pfd.events = POLLOUT|POLLIN; pfd.revents = 0;
|
||||
while ((n = poll(&pfd, 1, remaining * 1000)) < 0)
|
||||
if (errno != EINTR) {
|
||||
is_error = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!n || !(pfd.revents & (POLLOUT|POLLIN)))
|
||||
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) {
|
||||
SSL_free(c->ssl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fcntl(sd, F_SETFL, 0); /* make the socket blocking again */
|
||||
|
||||
/* verify that server certificate is authentic */
|
||||
server_cert = SSL_get_peer_certificate(c->ssl);
|
||||
if (!server_cert || (X509_cmp(server_cert, c->tc_cert) != 0)) {
|
||||
SSL_free(c->ssl);
|
||||
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;
|
||||
|
||||
unsigned char *cert_data_ptr = cert_data;
|
||||
|
||||
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("204.212.133.5");
|
||||
addr[1] = inet_addr("216.34.194.254");
|
||||
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 = SSLv3_client_method();
|
||||
}
|
||||
|
||||
if (!c->ctx)
|
||||
{
|
||||
c->ctx = SSL_CTX_new(c->meth);
|
||||
if (!c->ctx) return 0;
|
||||
}
|
||||
|
||||
/* create the valid certificate */
|
||||
if (c->tc_cert == NULL) {
|
||||
c->tc_cert = d2i_X509(NULL, &cert_data_ptr, 540);
|
||||
if (!c->tc_cert) 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
if (select(c->sd + 1, &read, NULL, &error, &tv) < 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)
|
||||
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) SSL_shutdown(c->ssl);
|
||||
|
||||
if (c->sd >= 0) {
|
||||
close(c->sd);
|
||||
c->sd = -1;
|
||||
}
|
||||
|
||||
if (c->ssl) {
|
||||
SSL_free(c->ssl);
|
||||
c->ssl = NULL;
|
||||
}
|
||||
|
||||
if (c->ctx) {
|
||||
SSL_CTX_free(c->ctx);
|
||||
c->ctx = NULL;
|
||||
}
|
||||
|
||||
/* We DON'T free c->meth or c->tc_cert here, because they can be *
|
||||
* reused by other transactions run on this same TCLinkHandle. */
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**********************************************
|
||||
* API functions exported to the user client. *
|
||||
**********************************************/
|
||||
|
||||
TCLinkHandle TCLinkCreate()
|
||||
{
|
||||
TCLinkCon *c = (TCLinkCon *)malloc(sizeof(TCLinkCon));
|
||||
|
||||
c->ip = NULL;
|
||||
c->num_ips = 0;
|
||||
c->sd = -1;
|
||||
|
||||
c->tc_cert = NULL;
|
||||
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;
|
||||
|
||||
return (TCLinkHandle)c;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (state != 1)
|
||||
state = -1;
|
||||
else
|
||||
state = 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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->tc_cert)
|
||||
X509_free(c->tc_cert);
|
||||
|
||||
free(c);
|
||||
}
|
||||
|
||||
char *TCLinkGetVersion(char *buf)
|
||||
{
|
||||
strcpy(buf, tclink_version);
|
||||
return buf;
|
||||
}
|
||||
|
45
ext/tclink/tctest.php
Normal file
45
ext/tclink/tctest.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?
|
||||
// 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"))
|
||||
{
|
||||
$extfname = getcwd() . "/modules/tclink.so";
|
||||
echo "TCLink extension not loaded, attempting to load manually from $extfname...";
|
||||
|
||||
// The excessive "../" bit allows us to back up out of the extension dir
|
||||
// so that we can access the current working directory. It is only done
|
||||
// this way so that tctest.php may work out of the box on any system, you
|
||||
// can get rid of this if you do a global install of TCLink.
|
||||
$extfname = "../../../../../../../../../../../.." . $extfname;
|
||||
if (!dl($extfname)) {
|
||||
echo "FAILED!\nAborting this test script, please check to make sure the module is properly built.\n";
|
||||
exit(1);
|
||||
}
|
||||
echo "success.\n";
|
||||
}
|
||||
|
||||
print "\nTCLink version " . tclink_getversion() . "\n";
|
||||
print "Sending transaction...";
|
||||
|
||||
// Build a hash containing our parameters
|
||||
$params{'custid'} = 'TestMerchant';
|
||||
$params{'password'} = 'password';
|
||||
$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";
|
||||
?>
|
||||
|
Loading…
Reference in New Issue
Block a user