php-src/ext/pfpro/pfpro.c

596 lines
13 KiB
C

/*
+----------------------------------------------------------------------+
| PHP version 4.0 |
+----------------------------------------------------------------------+
| Copyright (c) 1997, 1998, 1999, 2000 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 2.02 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available at through the world-wide-web at |
| http://www.php.net/license/2_02.txt. |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: David Croft <david@infotrek.co.uk>, |
| John Donagher <john@webmeta.com> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
/* Note: I've left the globals and ini-file handling stuff in
as I've yet to decide whether this is a good way to place "default"
stuff like hosts and proxies */
/* Status: Working. */
#include "php.h"
#include "php_ini.h"
#include "php_pfpro.h"
#include "pfpro.h"
#if HAVE_PFPRO
#ifdef ZTS
int pfpro_globals_id;
#else
php_pfpro_globals pfpro_globals;
#endif
/* Function table */
function_entry pfpro_functions[] = {
PHP_FE(pfpro_version, NULL)
PHP_FE(pfpro_init, NULL)
PHP_FE(pfpro_cleanup, NULL)
PHP_FE(pfpro_process_raw, NULL)
PHP_FE(pfpro_process, NULL)
{NULL, NULL, NULL}
};
zend_module_entry pfpro_module_entry = {
"pfpro",
pfpro_functions,
PHP_MINIT(pfpro),
PHP_MSHUTDOWN(pfpro),
PHP_RINIT(pfpro), /* request start */
PHP_RSHUTDOWN(pfpro), /* request end */
PHP_MINFO(pfpro),
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_PFPRO
ZEND_GET_MODULE(pfpro)
#endif
/*
PHP_INI_BEGIN()
PHP_INI_END()
*/
PHP_MINIT_FUNCTION(pfpro)
{
/*
REGISTER_INI_ENTRIES();
*/
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(pfpro)
{
/*
UNREGISTER_INI_ENTRIES();
*/
return SUCCESS;
}
PHP_RINIT_FUNCTION(pfpro)
{
return SUCCESS;
}
PHP_RSHUTDOWN_FUNCTION(pfpro)
{
return SUCCESS;
}
PHP_MINFO_FUNCTION(pfpro)
{
php_info_print_table_start();
php_info_print_table_header(2, "Verisign Payflow Pro support", "enabled");
php_info_print_table_end();
/*
DISPLAY_INI_ENTRIES();
*/
}
/* {{{ proto string pfpro_version()
Returns the version of the Payflow Pro library */
PHP_FUNCTION(pfpro_version)
{
if (ZEND_NUM_ARGS() != 0) {
WRONG_PARAM_COUNT;
}
RETURN_STRING(PNVersion(), 1);
}
/* }}} */
/* {{{ proto void pfpro_init()
Initialises the Payflow Pro library */
PHP_FUNCTION(pfpro_init)
{
if (ZEND_NUM_ARGS() != 0) {
WRONG_PARAM_COUNT;
}
PNInit();
RETURN_TRUE;
}
/* }}} */
/* {{{ proto void pfpro_cleanup()
Shuts down the Payflow Pro library */
PHP_FUNCTION(pfpro_cleanup)
{
if (ZEND_NUM_ARGS() != 0) {
WRONG_PARAM_COUNT;
}
PNCleanup();
RETURN_TRUE;
}
/* }}} */
/* {{{ proto string pfpro_process_raw(string parmlist [, string hostaddress [, int port, [, int timeout [, string proxyAddress [, int proxyPort [, string proxyLogon [, string proxyPassword]]]]]]])
Raw Payflow Pro transaction processing */
PHP_FUNCTION(pfpro_process_raw)
{
pval ***args;
char *parmlist;
char *address = NULL;
int port = 443;
int timeout = 30;
char *proxyAddress = NULL;
int proxyPort = 0;
char *proxyLogon = NULL;
char *proxyPassword = NULL;
int freeaddress = 0;
/* No, I don't like that Signio tell you to use a
fixed length buffer either */
char response[512] = "";
if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 8) {
WRONG_PARAM_COUNT;
}
args = (pval ***) emalloc(sizeof(pval **) * ZEND_NUM_ARGS());
if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
php_error(E_WARNING, "Unable to read parameters in pfpro_process_raw()");
efree(args);
RETURN_FALSE;
}
switch (ZEND_NUM_ARGS()) {
case 8:
convert_to_string_ex(args[7]);
proxyPassword = (*args[7])->value.str.val;
/* fall through */
case 7:
convert_to_string_ex(args[6]);
proxyLogon = (*args[6])->value.str.val;
/* fall through */
case 6:
convert_to_long_ex(args[5]);
proxyPort = (*args[5])->value.lval;
/* fall through */
case 5:
convert_to_string_ex(args[4]);
proxyAddress = (*args[4])->value.str.val;
/* fall through */
case 4:
convert_to_long_ex(args[3]);
timeout = (*args[3])->value.lval;
/* fall through */
case 3:
convert_to_long_ex(args[2]);
port = (*args[2])->value.lval;
/* fall through */
case 2:
convert_to_string_ex(args[1]);
address = (*args[1])->value.str.val;
}
convert_to_string_ex(args[0]);
parmlist = (*args[0])->value.str.val;
efree(args);
/* Default to signio's test server */
if (address == NULL) {
address = estrdup("test.signio.com");
freeaddress = 1;
}
#if 0
printf("Address: >%s<\n", address);
printf("Port: >%d<\n", port);
printf("Parmlist: >%s<\n", parmlist);
printf("Timeout: >%d<\n", timeout);
printf("Proxy address: >%s<\n", proxyAddress);
printf("Proxy port: >%d<\n", proxyPort);
printf("Proxy logon: >%s<\n", proxyLogon);
printf("Proxy password: >%s<\n", proxyPassword);
#endif
/* Blank the response buffer */
memset(response, 0, sizeof(response));
/* Perform the transaction */
ProcessPNTransaction(address, port, proxyAddress, proxyPort, proxyLogon, proxyPassword, parmlist, strlen(parmlist), timeout, response);
if (freeaddress) {
efree(address);
}
RETURN_STRING(response, 1);
}
/* }}} */
/* {{{ proto array pfpro_process(array parmlist [, string hostaddress [, int port, [, int timeout [, string proxyAddress [, int proxyPort [, string proxyLogon [, string proxyPassword]]]]]]])
Payflow Pro transaction processing using arrays */
PHP_FUNCTION(pfpro_process)
{
pval ***args;
HashTable *target_hash;
ulong num_key;
char *string_key;
zval **entry;
int pass;
char *address = NULL;
int port = 443;
int timeout = 30;
char *proxyAddress = NULL;
int proxyPort = 0;
char *proxyLogon = NULL;
char *proxyPassword = NULL;
int freeaddress = 0;
char *parmlist = NULL;
int parmlength = 0;
/* No, I don't like that Signio tell you to use a
fixed length buffer either */
char response[512] = "";
char tmpbuf[128];
char *rsppos, *valpos;
char buf[128], sbuf[128];
char *p1, *p2, *p_end, /* Pointers for string manipulation */
*sp1, *sp2, *sp_end,
*pdelim1="&", *pdelim2="=";
if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 8) {
WRONG_PARAM_COUNT;
}
args = (pval ***) emalloc(sizeof(pval **) * ZEND_NUM_ARGS());
if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
php_error(E_ERROR, "Unable to read parameters in pfpro_process()");
efree(args);
RETURN_FALSE;
}
if ((*args[0])->type != IS_ARRAY) {
php_error(E_ERROR, "First parameter to pfpro_process() must be an array");
efree(args);
RETURN_FALSE;
}
switch (ZEND_NUM_ARGS()) {
case 8:
convert_to_string_ex(args[7]);
proxyPassword = (*args[7])->value.str.val;
/* fall through */
case 7:
convert_to_string_ex(args[6]);
proxyLogon = (*args[6])->value.str.val;
/* fall through */
case 6:
convert_to_long_ex(args[5]);
proxyPort = (*args[5])->value.lval;
/* fall through */
case 5:
convert_to_string_ex(args[4]);
proxyAddress = (*args[4])->value.str.val;
/* fall through */
case 4:
convert_to_long_ex(args[3]);
timeout = (*args[3])->value.lval;
/* fall through */
case 3:
convert_to_long_ex(args[2]);
port = (*args[2])->value.lval;
/* fall through */
case 2:
convert_to_string_ex(args[1]);
address = (*args[1])->value.str.val;
}
/* Concatenate the passed array as specified by signio.
Basically it's all key=value&key=value, the only exception
being if the value contains = or &, in which case we also
encode the length, e.g. key[5]=bl&ah */
target_hash = HASH_OF(*args[0]);
for (pass = 0; pass <= 1; pass ++) {
parmlength = 0;
/* we go around the array twice. the first time to calculate
the string length, the second time to actually store it */
zend_hash_internal_pointer_reset(target_hash);
while (zend_hash_get_current_data(target_hash, (void **)&entry) == SUCCESS) {
if (parmlength > 0) {
if (pass == 1)
strcpy(parmlist + parmlength, "&");
parmlength += 1;
}
/* John I don't see any need to put the key in a zval
if it's not used as such and is pulled straight out */
switch (zend_hash_get_current_key(target_hash, &string_key, &num_key)) {
case HASH_KEY_IS_STRING:
if (pass == 1)
strcpy(parmlist + parmlength, string_key);
parmlength += strlen(string_key);
efree(string_key);
break;
case HASH_KEY_IS_LONG:
sprintf(tmpbuf, "%d", num_key);
if (pass == 1)
strcpy(parmlist + parmlength, tmpbuf);
parmlength += strlen(tmpbuf);
break;
default:
php_error(E_ERROR, "pfpro_process() array keys must be strings or integers");
efree(args);
RETURN_FALSE;
}
switch ((*entry)->type) {
case IS_STRING:
if (strchr((*entry)->value.str.val, '&')
|| strchr((*entry)->value.str.val, '=')) {
sprintf(tmpbuf, "[%d]=", (*entry)->value.str.len);
if (pass == 1)
strcpy(parmlist + parmlength, tmpbuf);
parmlength += strlen(tmpbuf);
}
else {
if (pass == 1)
strcpy(parmlist + parmlength, "=");
parmlength += 1;
}
if (pass == 1)
strcpy(parmlist + parmlength, (*entry)->value.str.val);
parmlength += (*entry)->value.str.len;
break;
case IS_LONG:
sprintf(tmpbuf, "=%d", (*entry)->value.lval);
if (pass == 1)
strcpy(parmlist + parmlength, tmpbuf);
parmlength += strlen(tmpbuf);
break;
case IS_DOUBLE:
sprintf(tmpbuf, "=%.2f", (*entry)->value.dval);
if (pass == 1)
strcpy(parmlist + parmlength, tmpbuf);
parmlength += strlen(tmpbuf);
break;
default:
php_error(E_ERROR, "pfpro_process() array values must be strings, ints or floats");
efree(args);
RETURN_FALSE;
}
zend_hash_move_forward(target_hash);
}
if (pass == 0) {
parmlist = emalloc(parmlength + 1);
}
}
efree(args);
/* Default to signio's test server */
if (address == NULL) {
/* is it safe to just do address = "test.signio.com"; here? */
address = estrdup("test.signio.com");
freeaddress = 1;
}
#if 0
printf("Address: >%s<\n", address);
printf("Port: >%d<\n", port);
printf("Parmlist: >%s<\n", parmlist);
printf("Timeout: >%d<\n", timeout);
printf("Proxy address: >%s<\n", proxyAddress);
printf("Proxy port: >%d<\n", proxyPort);
printf("Proxy logon: >%s<\n", proxyLogon);
printf("Proxy password: >%s<\n", proxyPassword);
#endif
/* Allocate the array for the response now - so we catch any errors
from this BEFORE we knock it off to the bank */
if (array_init(return_value) == FAILURE) {
php_error(E_ERROR, "pfpro_process() unable to create array");
RETURN_FALSE;
}
/* Blank the response buffer */
memset(response, 0, sizeof(response));
/* Perform the transaction */
ProcessPNTransaction(address, port, proxyAddress, proxyPort, proxyLogon, proxyPassword, parmlist, parmlength, timeout, response);
if (freeaddress) {
efree(address);
}
#if 0
/* Decode the response back into a PHP array */
rsppos = strtok(response, "&");
do {
valpos = strchr(rsppos, '=');
if (valpos) {
strncpy(tmpbuf, rsppos, valpos - rsppos);
tmpbuf[valpos - rsppos] = 0;
add_assoc_string(return_value, tmpbuf, valpos + 1, 1);
}
} while (rsppos = strtok(NULL, "&"));
#else
/* This final chunk of code is to walk the string returned by Signio
and build a string array to return to the user */
/* John, I suspect this code will fall over if there are less than
3 items in the response string -- david */
/* Clean our str[n]cpy buffers */
memset(buf, 0, sizeof(buf));
memset(sbuf, 0, sizeof(sbuf));
p_end = response + strlen(response);
p1 = response;
p2 = (char*)php_memnstr(response, pdelim1, 1, p_end);
sp1 = (char*)php_memnstr(response, pdelim2, 1, p2);
strncpy(buf, p1, sp1-p1);
sp1++;
strncpy(sbuf, sp1, p2-sp1);
add_assoc_string(return_value, &buf[0], &sbuf[0], 1);
do {
memset(buf, 0, sizeof(buf));
memset(sbuf, 0, sizeof(sbuf));
p1 = p2+1;
if ((sp2 = (char*)php_memnstr(p1, pdelim1, 1, p_end)) != NULL) {
sp1 = (char*)php_memnstr(p1, pdelim2, 1, sp2);
strncpy(buf, p1, sp1-p1);
sp1++;
strncpy(sbuf, sp1, sp2-sp1);
add_assoc_string(return_value, &buf[0], &sbuf[0], 1);
}
} while ((p2 = (char*)php_memnstr(p1, pdelim1, 1, p_end)) != NULL);
if (p1 <= p_end) {
memset(buf, 0, sizeof(buf));
memset(sbuf, 0, sizeof(sbuf));
sp1 = (char*)php_memnstr(p1, pdelim2, 1, p_end);
strncpy(buf, p1, sp1-p1);
sp1++;
strncpy(sbuf, sp1, p_end-sp1);
add_assoc_string(return_value, &buf[0], &sbuf[0], 1);
}
#endif
}
/* }}} */
#endif /* HAVE_PFPRO */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/