/*
   +----------------------------------------------------------------------+
   | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2008 The PHP Group                                |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
   | available through the world-wide-web at the following url:           |
   | http://www.php.net/license/3_01.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: Uwe Steinmann <Uwe.Steinmann@fernuni-hagen.de>              |
   |          Hartmut Holzgraefe <hholzgra@php.net>                       |
   +----------------------------------------------------------------------+
 */

/* $Id: fdf.c,v 1.89.2.2.2.10 2007/12/31 07:20:06 sebastian Exp $ */

/* FdfTk lib 2.0 is a Complete C/C++ FDF Toolkit available from
   http://beta1.adobe.com/ada/acrosdk/forms.html. */

/* Note that there is no code from the FdfTk lib in this file */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_open_temporary_file.h"

#if HAVE_FDFLIB

#include "SAPI.h"
#include "ext/standard/info.h"
#include "php_open_temporary_file.h"
#include "php_variables.h"
#include "php_fdf.h"

#ifndef S_ISDIR
#define S_ISDIR(m) (((m) & _S_IFDIR) == _S_IFDIR)
#endif

static int le_fdf;

SAPI_POST_HANDLER_FUNC(fdf_post_handler);

/* {{{ arginfo */
static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_open, 0)
	ZEND_ARG_INFO(0, filename)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_open_string, 0)
	ZEND_ARG_INFO(0, fdf_data)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_create, 0)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_close, 0)
	ZEND_ARG_INFO(0, fdfdoc)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO_EX(arginfo_fdf_get_value, 0, 0, 2)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, fieldname)
	ZEND_ARG_INFO(0, which)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO_EX(arginfo_fdf_set_value, 0, 0, 3)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, fieldname)
	ZEND_ARG_INFO(0, value)
	ZEND_ARG_INFO(0, isname)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO_EX(arginfo_fdf_next_field_name, 0, 0, 1)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, fieldname)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_set_ap, 0)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, fieldname)
	ZEND_ARG_INFO(0, face)
	ZEND_ARG_INFO(0, filename)
	ZEND_ARG_INFO(0, pagenr)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_get_ap, 0)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, fieldname)
	ZEND_ARG_INFO(0, face)
	ZEND_ARG_INFO(0, filename)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_get_encoding, 0)
	ZEND_ARG_INFO(0, fdfdoc)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_set_status, 0)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, status)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_get_status, 0)
	ZEND_ARG_INFO(0, fdfdoc)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO_EX(arginfo_fdf_set_file, 0, 0, 2)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, filename)
	ZEND_ARG_INFO(0, target_frame)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_get_file, 0)
	ZEND_ARG_INFO(0, fdfdoc)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO_EX(arginfo_fdf_save, 0, 0, 1)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, filename)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_save_string, 0)
	ZEND_ARG_INFO(0, fdfdoc)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_add_template, 0)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, newpage)
	ZEND_ARG_INFO(0, filename)
	ZEND_ARG_INFO(0, template)
	ZEND_ARG_INFO(0, rename)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_set_flags, 0)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, fieldname)
	ZEND_ARG_INFO(0, whichflags)
	ZEND_ARG_INFO(0, newflags)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_get_flags, 0)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, fieldname)
	ZEND_ARG_INFO(0, whichflags)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_set_opt, 0)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, fieldname)
	ZEND_ARG_INFO(0, element)
	ZEND_ARG_INFO(0, value)
	ZEND_ARG_INFO(0, name)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO_EX(arginfo_fdf_get_opt, 0, 0, 2)
	ZEND_ARG_INFO(0, fdfdof)
	ZEND_ARG_INFO(0, fieldname)
	ZEND_ARG_INFO(0, element)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_set_submit_form_action, 0)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, fieldname)
	ZEND_ARG_INFO(0, whichtrigger)
	ZEND_ARG_INFO(0, url)
	ZEND_ARG_INFO(0, flags)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_set_javascript_action, 0)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, fieldname)
	ZEND_ARG_INFO(0, whichtrigger)
	ZEND_ARG_INFO(0, script)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_set_encoding, 0)
	ZEND_ARG_INFO(0, fdf_document)
	ZEND_ARG_INFO(0, encoding)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_errno, 0)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_error, 0)
	ZEND_ARG_INFO(0, errno)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO_EX(arginfo_fdf_get_version, 0, 0, 0)
	ZEND_ARG_INFO(0, fdfdoc)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_set_version, 0)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, version)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_add_doc_javascript, 0)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, scriptname)
	ZEND_ARG_INFO(0, script)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_set_on_import_javascript, 0)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, script)
	ZEND_ARG_INFO(0, before_data_import)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_set_target_frame, 0)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, target)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_remove_item, 0)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, fieldname)
	ZEND_ARG_INFO(0, item)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_get_attachment, 0)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, fieldname)
	ZEND_ARG_INFO(0, savepath)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO_EX(arginfo_fdf_enum_values, 0, 0, 2)
	ZEND_ARG_INFO(0, fdfdoc)
	ZEND_ARG_INFO(0, function)
	ZEND_ARG_INFO(0, userdata)
ZEND_END_ARG_INFO()

static
ZEND_BEGIN_ARG_INFO(arginfo_fdf_header, 0)
ZEND_END_ARG_INFO()

/* }}} */

/* {{{ fdf_functions[]
 */
zend_function_entry fdf_functions[] = {
	PHP_FE(fdf_add_template,						arginfo_fdf_add_template)
	PHP_FE(fdf_close,								arginfo_fdf_close)
	PHP_FE(fdf_create,								arginfo_fdf_create)
	PHP_FE(fdf_enum_values,                         arginfo_fdf_enum_values)
	PHP_FE(fdf_errno,        						arginfo_fdf_errno)
	PHP_FE(fdf_error,        						arginfo_fdf_error)
	PHP_FE(fdf_get_ap,								arginfo_fdf_get_ap)
	PHP_FE(fdf_get_encoding,						arginfo_fdf_get_encoding)
	PHP_FE(fdf_get_file,							arginfo_fdf_get_file)
	PHP_FE(fdf_get_flags,							arginfo_fdf_get_flags)
	PHP_FE(fdf_get_opt,								arginfo_fdf_get_opt)
	PHP_FE(fdf_get_status,							arginfo_fdf_get_status)
	PHP_FE(fdf_get_value,							arginfo_fdf_get_value)
	PHP_FE(fdf_get_version,                         arginfo_fdf_get_version)
	PHP_FE(fdf_next_field_name,						arginfo_fdf_next_field_name)
	PHP_FE(fdf_open,								arginfo_fdf_open)
	PHP_FE(fdf_open_string,							arginfo_fdf_open_string)
	PHP_FE(fdf_remove_item,                         arginfo_fdf_remove_item)
	PHP_FE(fdf_save,								arginfo_fdf_save)
	PHP_FE(fdf_save_string,	        				arginfo_fdf_save_string)
	PHP_FE(fdf_set_ap,								arginfo_fdf_set_ap)
	PHP_FE(fdf_set_encoding,						arginfo_fdf_set_encoding)
	PHP_FE(fdf_set_file,							arginfo_fdf_set_file)
	PHP_FE(fdf_set_flags,							arginfo_fdf_set_flags)
	PHP_FE(fdf_set_javascript_action,				arginfo_fdf_set_javascript_action)
	PHP_FE(fdf_set_opt,								arginfo_fdf_set_opt)
	PHP_FE(fdf_set_status,							arginfo_fdf_set_status)
	PHP_FE(fdf_set_submit_form_action,				arginfo_fdf_set_submit_form_action)
	PHP_FE(fdf_set_value,							arginfo_fdf_set_value)
	PHP_FE(fdf_header,                              arginfo_fdf_header)
#ifdef HAVE_FDFTK_5
	PHP_FE(fdf_add_doc_javascript,                  arginfo_fdf_add_doc_javascript)
	PHP_FE(fdf_get_attachment,                      arginfo_fdf_get_attachment)
	PHP_FE(fdf_set_on_import_javascript,            arginfo_fdf_set_on_import_javascript)
	PHP_FE(fdf_set_target_frame,                    arginfo_fdf_set_target_frame)
	PHP_FE(fdf_set_version,                         arginfo_fdf_set_version)
#endif
	{NULL, NULL, NULL}
};
/* }}} */

ZEND_DECLARE_MODULE_GLOBALS(fdf)
static PHP_GINIT_FUNCTION(fdf);

zend_module_entry fdf_module_entry = {
    STANDARD_MODULE_HEADER,
	"fdf", 
	fdf_functions, 
	PHP_MINIT(fdf), 
	PHP_MSHUTDOWN(fdf), 
	PHP_RINIT(fdf), 
	NULL,
	PHP_MINFO(fdf), 
    NO_VERSION_YET,
    PHP_MODULE_GLOBALS(fdf),
    PHP_GINIT(fdf),
    NULL,
    NULL,
	STANDARD_MODULE_PROPERTIES_EX
};

#ifdef COMPILE_DL_FDF
ZEND_GET_MODULE(fdf)
#endif

#define FDF_SUCCESS do { FDF_G(error)=FDFErcOK; RETURN_TRUE;} while(0)
#define FDF_FAILURE(err)  do { FDF_G(error)=err; RETURN_FALSE;} while(0)

static void phpi_FDFClose(zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
	FDFDoc fdf = (FDFDoc)rsrc->ptr;

	(void) FDFClose(fdf);
}

#define FDF_POST_CONTENT_TYPE	"application/vnd.fdf"

static sapi_post_entry php_fdf_post_entry =	{
	FDF_POST_CONTENT_TYPE,
	sizeof(FDF_POST_CONTENT_TYPE)-1,
	sapi_read_standard_form_data,
	fdf_post_handler
};

static PHP_GINIT_FUNCTION(fdf)
{
	memset(fdf_globals, 0, sizeof(*fdf_globals));
}



/* {{{ PHP_MINIT_FUNCTION
 */
PHP_MINIT_FUNCTION(fdf)
{
	le_fdf = zend_register_list_destructors_ex(phpi_FDFClose, NULL, "fdf", module_number);

 	/* add handler for Acrobat FDF form post requests */
	sapi_register_post_entry(&php_fdf_post_entry TSRMLS_CC);


	/* Constants used by fdf_set_opt() */ 
	REGISTER_LONG_CONSTANT("FDFValue", FDFValue, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFStatus", FDFStatus, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFFile", FDFFile, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFID", FDFID, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFFf", FDFFf, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFSetFf", FDFSetFf, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFClearFf", FDFClearFf, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFFlags", FDFFlags, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFSetF", FDFSetF, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFClrF", FDFClrF, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFAP", FDFAP, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFAS", FDFAS, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFAction", FDFAction, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFAA", FDFAA, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFAPRef", FDFAPRef, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFIF", FDFIF, CONST_CS | CONST_PERSISTENT);

	/* Constants used by fdf_set_javascript_action() */
	REGISTER_LONG_CONSTANT("FDFEnter", FDFEnter, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFExit", FDFExit, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFDown", FDFDown, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFUp", FDFUp, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFFormat", FDFFormat, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFValidate", FDFValidate, CONST_CS | CONST_PERSISTENT);

	REGISTER_LONG_CONSTANT("FDFKeystroke", FDFKeystroke, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFCalculate", FDFCalculate, CONST_CS | CONST_PERSISTENT);

	/* Constants used by fdf_(set|get)_ap */
	REGISTER_LONG_CONSTANT("FDFNormalAP", 1, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFRolloverAP", 2, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("FDFDownAP", 3, CONST_CS | CONST_PERSISTENT);

#ifdef PHP_WIN32
	return SUCCESS;
#else
	return (FDFInitialize() == FDFErcOK) ? SUCCESS : FAILURE;
#endif
}
/* }}} */

/* {{{ RINIT */
PHP_RINIT_FUNCTION(fdf)
{
	FDF_G(error) = FDFErcOK;
	return SUCCESS;
}
/* }}} */

/* {{{ PHP_MINFO_FUNCTION
 */
PHP_MINFO_FUNCTION(fdf)
{
	/* need to use a PHPAPI function here because it is external module in windows */
	php_info_print_table_start();
	php_info_print_table_row(2, "FDF Support", "enabled");
	php_info_print_table_row(2, "FdfTk Version", FDFGetVersion() );
	php_info_print_table_end();
}
/* }}} */

/* {{{ PHP_MSHUTDOWN_FUNCTION
 */
PHP_MSHUTDOWN_FUNCTION(fdf)
{
	/* remove handler for Acrobat FDF form post requests */
	sapi_unregister_post_entry(&php_fdf_post_entry TSRMLS_CC); 

#ifdef PHP_WIN32
	return SUCCESS;
#else
	return (FDFFinalize() == FDFErcOK) ? SUCCESS : FAILURE;
#endif
}
/* }}} */

/* {{{ proto resource fdf_open(string filename)
   Opens a new FDF document */
PHP_FUNCTION(fdf_open) 
{
	zval **file;
	FDFDoc fdf;
	FDFErc err;

	if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &file) == FAILURE) {
		WRONG_PARAM_COUNT;
	}

	convert_to_string_ex(file);

	if (php_check_open_basedir(Z_STRVAL_PP(file) TSRMLS_CC) || (PG(safe_mode) && !php_checkuid(Z_STRVAL_PP(file), "wb+", CHECKUID_CHECK_MODE_PARAM))) {
		RETURN_FALSE;
	}

	err = FDFOpen(Z_STRVAL_PP(file), 0, &fdf);

	if(err != FDFErcOK || !fdf) {
		if(err == FDFErcOK) err= FDFErcInternalError;
		FDF_FAILURE(err);
	}

	ZEND_REGISTER_RESOURCE(return_value, fdf, le_fdf);
} 
/* }}} */

/* {{{ proto resource fdf_open_string(string fdf_data)
   Opens a new FDF document from string */
PHP_FUNCTION(fdf_open_string) 
{
	char *fdf_data;
	int fdf_data_len;
	FDFDoc fdf;
	FDFErc err;
	char *temp_filename;
	FILE *fp;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
							 &fdf_data, &fdf_data_len) 
	   == FAILURE) {
		return;
	}

	fp = php_open_temporary_file(PG(upload_tmp_dir), "php", &temp_filename TSRMLS_CC);
	if(!fp) {
		RETURN_FALSE;
	}
	fwrite(fdf_data, fdf_data_len, 1, fp);
	fclose(fp);

	err = FDFOpen(temp_filename, 0, &fdf);
	unlink(temp_filename);
	efree(temp_filename);

	if(err != FDFErcOK || !fdf) {
		if(err == FDFErcOK) err= FDFErcInternalError;
		FDF_FAILURE(err);
	}

	ZEND_REGISTER_RESOURCE(return_value, fdf, le_fdf);
} 
/* }}} */

/* {{{ proto resource fdf_create(void)
   Creates a new FDF document */
PHP_FUNCTION(fdf_create) 
{
	FDFDoc fdf;
	FDFErc err;

	if (ZEND_NUM_ARGS() != 0) {
		WRONG_PARAM_COUNT;
	}

	err = FDFCreate(&fdf);

	if(err != FDFErcOK || !fdf) {
		if(err == FDFErcOK) err= FDFErcInternalError;
		FDF_FAILURE(err);
	}

	ZEND_REGISTER_RESOURCE(return_value, fdf, le_fdf);
}
/* }}} */

/* {{{ proto void fdf_close(resource fdfdoc)
   Closes the FDF document */
PHP_FUNCTION(fdf_close) 
{
	zval **fdfp;
	FDFDoc fdf;

	if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &fdfp) == FAILURE) {
		WRONG_PARAM_COUNT;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, fdfp, -1, "fdf", le_fdf);
	zend_list_delete(Z_RESVAL_PP(fdfp));
} 
/* }}} */

/* {{{ proto string fdf_get_value(resource fdfdoc, string fieldname [, int which])
   Gets the value of a field as string */
PHP_FUNCTION(fdf_get_value) 
{
	zval *r_fdf;
	char *fieldname;
	int fieldname_len;
	long which = -1;
	FDFDoc fdf;
	FDFErc err;
	ASInt32 nr, size = 256;
	char *buffer;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|l",
							 &r_fdf, &fieldname, &fieldname_len,
							 &which) 
	   == FAILURE) {
		return;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, &r_fdf, -1, "fdf", le_fdf);

	buffer = emalloc(size);
	if(which >= 0) {
#if HAVE_FDFTK_5
		err = FDFGetNthValue(fdf, fieldname, which, buffer, size-2, &nr);
#else
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "the optional 'which' parameter requires FDF toolkit 5.0 or above, it will be ignored for now");
		which = -1;
#endif
	} else {
		err = FDFGetValue(fdf, fieldname, buffer, size-2, &nr);
	}
	if(err == FDFErcBufTooShort && nr > 0 ) {
		buffer = erealloc(buffer, nr+2); 
		if(which >= 0) {
#if HAVE_FDFTK_5
			err = FDFGetNthValue(fdf, fieldname, which, buffer, nr, &nr);
#endif
		} else {
			err = FDFGetValue(fdf, fieldname, buffer, nr, &nr);
		} 
#if HAVE_FDFTK_5
	} else if((err == FDFErcValueIsArray) && (which == -1)) {
		array_init(return_value);
		which = 0;
		do {
			err = FDFGetNthValue(fdf, fieldname, which, buffer, size-2, &nr); 
			if(err == FDFErcBufTooShort && nr > 0 ) {
				buffer = erealloc(buffer, nr+2); 
				err = FDFGetNthValue(fdf, fieldname, which, buffer, nr, &nr);
			} 
			if (err == FDFErcOK) {
				add_next_index_string(return_value, buffer, 1);
			} 
			which++;
		} while (err == FDFErcOK);
		efree(buffer); 
		buffer = NULL;
#endif
	}

	if((err != FDFErcOK) && (err != FDFErcNoValue)) {
		if(buffer) efree(buffer);
		FDF_FAILURE(err);
	}

	if(buffer) {
		RETVAL_STRING(buffer, 1);
		efree(buffer);
	}

	return;
}
/* }}} */

/* {{{ proto bool fdf_set_value(resource fdfdoc, string fieldname, mixed value [, int isname])
   Sets the value of a field */
PHP_FUNCTION(fdf_set_value) 
{
	zval **fdfp, **fieldname, **value, **dummy;
	FDFDoc fdf;
	FDFErc err;

	switch(ZEND_NUM_ARGS()) {
	case 3:
		if (zend_get_parameters_ex(3, &fdfp, &fieldname, &value) == FAILURE) {
			WRONG_PARAM_COUNT;
		}
		break;
	case 4:
		if (zend_get_parameters_ex(4, &fdfp, &fieldname, &value, &dummy) == FAILURE) {
			WRONG_PARAM_COUNT;
		}
		break;
	default:
		WRONG_PARAM_COUNT;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, fdfp, -1, "fdf", le_fdf);

	convert_to_string_ex(fieldname);

	if (Z_TYPE_PP(value) == IS_ARRAY) {
#ifdef HAVE_FDFTK_5
		ASInt32 nValues = zend_hash_num_elements(Z_ARRVAL_PP(value));
		char **newValues = ecalloc(nValues, sizeof(char *)), **next;
		HashPosition   pos;
		zval         **tmp;

		next = newValues;
		zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(value), &pos);
		while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(value), 
											 (void **) &tmp,
											 &pos) == SUCCESS) {
			convert_to_string_ex(tmp);
			*next++ = estrdup(Z_STRVAL_PP(tmp));
			zend_hash_move_forward_ex(Z_ARRVAL_PP(value), &pos);
		}

		err = FDFSetValues(fdf, Z_STRVAL_PP(fieldname), nValues, (const char **)newValues);
		
		for(next = newValues; nValues; nValues--) {
			efree(*next++);
		}
		efree(newValues);
#else
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "setting array values is only possible with FDF toolkit 5.0 and above");
		RETURN_FALSE;
#endif
	} else {
		convert_to_string_ex(value);
		
		err = FDFSetValue(fdf, Z_STRVAL_PP(fieldname), Z_STRVAL_PP(value), (ASBool)0 /*dummy*/);
	}
	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}
	FDF_SUCCESS;
}
/* }}} */

/* {{{ proto string fdf_next_field_name(resource fdfdoc [, string fieldname])
   Gets the name of the next field name or the first field name */
PHP_FUNCTION(fdf_next_field_name) 
{
	zval **fdfp, **field;
	int argc=ZEND_NUM_ARGS();
	ASInt32 length=256, nr;
	char *buffer=NULL, *fieldname=NULL;
	FDFDoc fdf;
	FDFErc err;

	if (argc > 2 || argc < 1 || zend_get_parameters_ex(argc, &fdfp, &field) == FAILURE) {
		WRONG_PARAM_COUNT;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, fdfp, -1, "fdf", le_fdf);

	if(argc == 2) {
		convert_to_string_ex(field);
		fieldname = Z_STRVAL_PP(field);
	}

	buffer = emalloc(length);
	err = FDFNextFieldName(fdf, fieldname, buffer, length-1, &nr);

	if(err == FDFErcBufTooShort && nr > 0 ) {
		buffer = erealloc(buffer, nr+1); 
		err = FDFNextFieldName(fdf, fieldname, buffer, length-1, &nr);
	} 

	if(err != FDFErcOK) {
		efree(buffer);
		FDF_FAILURE(err);
	} 

	RETVAL_STRING(buffer, 1);
	efree(buffer);
}
/* }}} */

/* {{{ proto bool fdf_set_ap(resource fdfdoc, string fieldname, int face, string filename, int pagenr)
   Sets the appearence of a field */
PHP_FUNCTION(fdf_set_ap) 
{
	zval **fdfp, **fieldname, **face, **filename, **pagenr;
	FDFDoc fdf;
	FDFErc err;
	FDFAppFace facenr;

	if (ZEND_NUM_ARGS() != 5 || zend_get_parameters_ex(5, &fdfp, &fieldname, &face, &filename, &pagenr) == FAILURE) {
		WRONG_PARAM_COUNT;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, fdfp, -1, "fdf", le_fdf);

	convert_to_string_ex(fieldname);
	convert_to_long_ex(face);
	convert_to_string_ex(filename);

	if (php_check_open_basedir(Z_STRVAL_PP(filename) TSRMLS_CC) || (PG(safe_mode) && !php_checkuid(Z_STRVAL_PP(filename), "wb+", CHECKUID_CHECK_MODE_PARAM))) {
		RETURN_FALSE;
	}

	convert_to_long_ex(pagenr);

	switch(Z_LVAL_PP(face)) {
		case 1:
			facenr = FDFNormalAP;
			break;
		case 2:
			facenr = FDFRolloverAP;
			break;
		case 3:
			facenr = FDFDownAP;
			break;
		default:
			facenr = FDFNormalAP;
			break;
	}

	err = FDFSetAP(fdf, Z_STRVAL_PP(fieldname), facenr, NULL, Z_STRVAL_PP(filename), (ASInt32) Z_LVAL_PP(pagenr));

	/* This should be made more intelligent, ie. use switch() with the 
	   possible errors this function can return. Or create global error handler function.
	 */
	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}

	FDF_SUCCESS;

}
/* }}} */

/* {{{ proto bool fdf_get_ap(resource fdfdoc, string fieldname, int face, string filename) 
	 Gets the appearance of a field and creates a PDF document out of it. */
PHP_FUNCTION(fdf_get_ap) {
	zval *r_fdf;
	char *fieldname, *filename;
	int fieldname_len, filename_len;
	long face;
	FDFDoc fdf;
	FDFErc err;
	FDFAppFace facenr;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsls",
							 &r_fdf, &fieldname, &fieldname_len,
							 &face, &filename, &filename_len) 
	   == FAILURE) {
		return;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, &r_fdf, -1, "fdf", le_fdf);

	if (php_check_open_basedir(filename TSRMLS_CC) || (PG(safe_mode) && !php_checkuid(filename, "wb+", CHECKUID_CHECK_MODE_PARAM))) {
		RETURN_FALSE;
	}

	switch(face) {
		case 1:
			facenr = FDFNormalAP;
			break;
		case 2:
			facenr = FDFRolloverAP;
			break;
		case 3:
			facenr = FDFDownAP;
			break;
		default:
			facenr = FDFNormalAP;
			break;
	}

	err = FDFGetAP(fdf, fieldname, facenr, filename);

	/* This should be made more intelligent, ie. use switch() with the 
	   possible errors this function can return. Or create global error handler function.
	 */
	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}

	FDF_SUCCESS;

	
}
/* }}} */

/* {{{ proto string fdf_get_encoding(resource fdf) 
   Gets FDF file encoding scheme */
PHP_FUNCTION(fdf_get_encoding) {
	zval *r_fdf;
	FDFDoc fdf;
	FDFErc err;
	char buffer[32];
	ASInt32 len;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
							 &r_fdf) == FAILURE) {
		return;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, &r_fdf, -1, "fdf", le_fdf);

	err = FDFGetEncoding(fdf, buffer, 32, &len);

	/* This should be made more intelligent, ie. use switch() with the 
	   possible errors this function can return. Or create global error handler function.
	 */
	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}
	
	FDF_G(error) = FDFErcOK;
	RETURN_STRINGL(buffer, (size_t)len, 1);
}
/* }}} */

/* {{{ proto bool fdf_set_status(resource fdfdoc, string status)
   Sets the value of /Status key */
PHP_FUNCTION(fdf_set_status) 
{
	zval **fdfp, **status;
	FDFDoc fdf;
	FDFErc err;

	if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &fdfp, &status) == FAILURE) {
		WRONG_PARAM_COUNT;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, fdfp, -1, "fdf", le_fdf);

	convert_to_string_ex(status);

	err = FDFSetStatus(fdf, Z_STRVAL_PP(status));
	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}

	FDF_SUCCESS;
}
/* }}} */

/* {{{ proto string fdf_get_status(resource fdfdoc)
   Gets the value of /Status key */
PHP_FUNCTION(fdf_get_status) 
{
	zval **fdfp;
	ASInt32 nr, size = 256;
	char *buf;
	FDFDoc fdf;
	FDFErc err;

	if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &fdfp) == FAILURE) {
		WRONG_PARAM_COUNT;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, fdfp, -1, "fdf", le_fdf);

	buf = emalloc(size);
	err = FDFGetStatus(fdf, buf, size-1,  &nr);

	if(err == FDFErcBufTooShort && nr > 0 ) {
		buf = erealloc(buf, nr+1); 
		err = FDFGetStatus(fdf, buf, size-1,  &nr);
	}
	
	if(err != FDFErcOK) {
		efree(buf);
		FDF_FAILURE(err);
	}

	RETVAL_STRING(buf, 1);
	efree(buf);
}
/* }}} */

/* {{{ proto bool fdf_set_file(resource fdfdoc, string filename [, string target_frame])
   Sets the value of /F key */
PHP_FUNCTION(fdf_set_file) 
{
	zval *r_fdf;
	char *filename, *target_frame= NULL;
	int filename_len, target_frame_len;
	FDFDoc fdf;
	FDFErc err;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|s", &r_fdf, 
							 &filename, &filename_len,
							 &target_frame, &target_frame_len)
	   == FAILURE) {
		return;
	}

	if (php_check_open_basedir(filename TSRMLS_CC) || (PG(safe_mode) && !php_checkuid(filename, "wb+", CHECKUID_CHECK_MODE_PARAM))) {
		RETURN_FALSE;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, &r_fdf, -1, "fdf", le_fdf);

	err = FDFSetFile(fdf, filename);
	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}
	if(target_frame) {
#ifdef HAVE_FDFTK_5
		err = FDFSetTargetFrame(fdf, target_frame);
		if(err != FDFErcOK) {
			FDF_FAILURE(err);
		}
#else
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "setting the target frame is only possible with FDF toolkit 5.0 and above, ignoring it for now");
#endif
	}

	FDF_SUCCESS;
}
/* }}} */

/* {{{ proto string fdf_get_file(resource fdfdoc)
   Gets the value of /F key */
PHP_FUNCTION(fdf_get_file) 
{
	zval **fdfp;
	ASInt32 nr, size = 256;
	char *buf;
	FDFDoc fdf;
	FDFErc err;

	if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &fdfp) == FAILURE) {
		WRONG_PARAM_COUNT;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, fdfp, -1, "fdf", le_fdf);

	buf = emalloc(size);
	err = FDFGetFile(fdf, buf, size-1,  &nr);

	if(err == FDFErcBufTooShort && nr > 0 ) {
		buf = erealloc(buf, nr+1); 
		err = FDFGetFile(fdf, buf, size-1,  &nr);
	}
	
	if(err != FDFErcOK) {
		efree(buf);
		FDF_FAILURE(err);
	}

	RETVAL_STRING(buf, 1);
	efree(buf);
}
/* }}} */

/* {{{ proto bool fdf_save(resource fdfdoc [, string filename])
   Writes out the FDF file */
PHP_FUNCTION(fdf_save) 
{
	zval *r_fdf;
	char *filename = NULL;
	int filename_len;
	FDFDoc fdf;
	FDFErc err;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|s", &r_fdf, &filename, &filename_len)
	   == FAILURE) {
		return;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, &r_fdf, -1, "fdf", le_fdf);

	if(filename) {
		if (php_check_open_basedir(filename TSRMLS_CC) || (PG(safe_mode) && !php_checkuid(filename, "wb+", CHECKUID_CHECK_MODE_PARAM))) {
			RETURN_FALSE;
		}
		err = FDFSave(fdf, filename);	
	} else {
		FILE *fp;
		char *temp_filename;

		fp = php_open_temporary_file(PG(upload_tmp_dir), "php", &temp_filename TSRMLS_CC);
		if(!fp) {
			err = FDFErcFileSysErr;
		} else {
			fclose(fp);
			err = FDFSave(fdf, temp_filename);

			if(err == FDFErcOK) {
				php_stream *stream = php_stream_open_wrapper(temp_filename, "rb", 0, NULL);
				if (stream)	{
					php_stream_passthru(stream);
					php_stream_close(stream);
				} else {
					err = FDFErcFileSysErr;
				}	
			}
		}
		if(temp_filename) {
			unlink(temp_filename);
			efree(temp_filename);
		}
	}

	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}
	
	FDF_SUCCESS;
} 
/* }}} */

/* {{{ proto string fdf_save_string(resource fdfdoc)
   Returns the FDF file as a string */
PHP_FUNCTION(fdf_save_string) 
{
	zval *r_fdf;
	FDFDoc fdf;
	FDFErc err;
	FILE *fp;
	char *temp_filename = NULL;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &r_fdf)
	   == FAILURE) {
		return;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, &r_fdf, -1, "fdf", le_fdf);

	fp = php_open_temporary_file(PG(upload_tmp_dir), "php", &temp_filename TSRMLS_CC);
	if(!fp) {
		err = FDFErcFileSysErr;
	} else {
		fclose(fp);
		err = FDFSave(fdf, temp_filename);

		if(err == FDFErcOK) {
			fp = fopen(temp_filename, "rb");
			if (fp)	{
				struct stat stat;
				char *buf;

				if (fstat(fileno(fp), &stat) == -1) {
					RETVAL_FALSE;
					goto err;
				}
				buf = safe_emalloc(1, stat.st_size, 1);
				fread(buf, stat.st_size, 1, fp);
				buf[stat.st_size] = '\0';
				fclose(fp);

				unlink(temp_filename);
				efree(temp_filename);
				RETURN_STRINGL(buf, stat.st_size, 0);
			} else {
				err = FDFErcFileSysErr;
			}	
		}
	}

	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}
err:
	if(temp_filename) {
		unlink(temp_filename);
		efree(temp_filename);
	}

	return;
} 
/* }}} */

/* {{{ proto bool fdf_add_template(resource fdfdoc, int newpage, string filename, string template, int rename)
   Adds a template into the FDF document */
PHP_FUNCTION(fdf_add_template) 
{
	zval **fdfp, **newpage, **filename, **template, **rename;
	FDFDoc fdf;
	FDFErc err;
	pdfFileSpecRec filespec;

	if (ZEND_NUM_ARGS() != 5 || zend_get_parameters_ex(5, &fdfp, &newpage, &filename, &template, &rename) == FAILURE) {
		WRONG_PARAM_COUNT;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, fdfp, -1, "fdf", le_fdf);

	convert_to_long_ex(newpage);
	convert_to_string_ex(filename);
	convert_to_string_ex(template);
	convert_to_long_ex(rename);

	if (php_check_open_basedir(Z_STRVAL_PP(filename) TSRMLS_CC) || (PG(safe_mode) && !php_checkuid(Z_STRVAL_PP(filename), "wb+", CHECKUID_CHECK_MODE_PARAM))) {
		RETURN_FALSE;
	}

	filespec.FS = NULL;
	filespec.F = Z_STRVAL_PP(filename);
	filespec.Mac = NULL;
	filespec.DOS = NULL;
	filespec.Unix = NULL;
	filespec.ID[0] = NULL;
	filespec.ID[1] = NULL;
	filespec.bVolatile = false;

	err = FDFAddTemplate(fdf, (unsigned short)(Z_LVAL_PP(newpage)), &filespec, Z_STRVAL_PP(template), (unsigned short)(Z_LVAL_PP(rename)));
	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}

	FDF_SUCCESS;
}
/* }}} */

/* {{{ proto bool fdf_set_flags(resource fdfdoc, string fieldname, int whichflags, int newflags)
   Sets flags for a field in the FDF document */
PHP_FUNCTION(fdf_set_flags) 
{
	zval **fdfp, **fieldname, **flags, **newflags;
	FDFDoc fdf;
	FDFErc err;

	if (ZEND_NUM_ARGS() != 4 || zend_get_parameters_ex(4, &fdfp, &fieldname, &flags, &newflags) == FAILURE) {
		WRONG_PARAM_COUNT;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, fdfp, -1, "fdf", le_fdf);

	convert_to_string_ex(fieldname);
	convert_to_long_ex(flags);
	convert_to_long_ex(newflags);	

	err=FDFSetFlags(fdf, Z_STRVAL_PP(fieldname), Z_LVAL_PP(flags), Z_LVAL_PP(newflags));
	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}

	FDF_SUCCESS;
}
/* }}} */

/* {{{ proto int fdf_get_flags(resorce fdfdoc, string fieldname, int whichflags) 
   Gets the flags of a field */
PHP_FUNCTION(fdf_get_flags) {
 	zval *r_fdf;
	char *fieldname;
	int fieldname_len;
	long whichflags;
	FDFDoc fdf;
	FDFErc err;
	ASUns32 flags;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsl",
							 &r_fdf, &fieldname, &fieldname_len,
							 &whichflags) 
	   == FAILURE) {
		return;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, &r_fdf, -1, "fdf", le_fdf);

	err = FDFGetFlags(fdf, fieldname, (FDFItem)whichflags, &flags);

	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}

	RETURN_LONG((long)flags);
}
/* }}} */

/* {{{ proto bool fdf_set_opt(resource fdfdoc, string fieldname, int element, string value, string name)
   Sets a value in the opt array for a field */
PHP_FUNCTION(fdf_set_opt)
{
	zval **fdfp, **fieldname, **element, **value, **name;
	FDFDoc fdf;
	FDFErc err;	

	if (ZEND_NUM_ARGS() != 5 || zend_get_parameters_ex(5, &fdfp, &fieldname, &element, &value, &name) == FAILURE) {
		WRONG_PARAM_COUNT;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, fdfp, -1, "fdf", le_fdf);

	convert_to_string_ex(fieldname);
	convert_to_long_ex(element);
	convert_to_string_ex(value);
	convert_to_string_ex(name);

	err = FDFSetOpt(fdf, Z_STRVAL_PP(fieldname), Z_LVAL_PP(element), Z_STRVAL_PP(value), Z_STRVAL_PP(name));
	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}
	FDF_SUCCESS;
}
/* }}} */

/* {{{ proto mixed fdf_get_opt(resource fdfdof, string fieldname [, int element])
   Gets a value from the opt array of a field */
PHP_FUNCTION(fdf_get_opt) {
 	zval *r_fdf;
	char *fieldname;
	int fieldname_len;
	long element = -1;
	FDFDoc fdf;
	FDFErc err;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|l",
							 &r_fdf, &fieldname, &fieldname_len,
							 &element) 
	   == FAILURE) {
		return;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, &r_fdf, -1, "fdf", le_fdf);

	if(element == -1) {
		ASInt32 elements;
		err = FDFGetOpt(fdf, fieldname, (ASInt32)-1, NULL, NULL, 0, &elements);
		if(err != FDFErcOK) {
			FDF_FAILURE(err);
		}
		RETURN_LONG((long)elements);
	} else {
		ASInt32 bufSize, nRet;
		char *buf1, *buf2;

		bufSize = 1024; 
		buf1 = emalloc(bufSize);
		buf2 = emalloc(bufSize);

		err = FDFGetOpt(fdf, fieldname, (ASInt32)element, buf1, buf2, bufSize, &nRet);
		if(err == FDFErcBufTooShort) {
			efree(buf1);
			efree(buf2);
			buf1 = emalloc(nRet);
			buf2 = emalloc(nRet);
			bufSize = nRet;
			err = FDFGetOpt(fdf, fieldname, (ASInt32)element, buf1, buf2, bufSize, &nRet);
		}
		if(err != FDFErcOK) {
			FDF_FAILURE(err);
		}
		array_init(return_value);
		add_next_index_stringl(return_value, buf1, strlen(buf1), 1);
		add_next_index_stringl(return_value, buf2, strlen(buf2), 1);
		efree(buf1);
		efree(buf2);
	}
}
/* }}} */

/* {{{ proto bool fdf_set_submit_form_action(resource fdfdoc, string fieldname, int whichtrigger, string url, int flags)
   Sets the submit form action for a field */
PHP_FUNCTION(fdf_set_submit_form_action) 
{
	zval **fdfp, **fieldname, **trigger, **url, **flags;
	FDFDoc fdf;
	FDFErc err;	

	if (ZEND_NUM_ARGS() != 5 || zend_get_parameters_ex(5, &fdfp, &fieldname, &trigger, &url, &flags) == FAILURE) {
		WRONG_PARAM_COUNT;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, fdfp, -1, "fdf", le_fdf);

	convert_to_string_ex(fieldname);
	convert_to_long_ex(trigger);
	convert_to_string_ex(url);
	convert_to_long_ex(flags);

	err = FDFSetSubmitFormAction(fdf, Z_STRVAL_PP(fieldname), Z_LVAL_PP(trigger), Z_STRVAL_PP(url), Z_LVAL_PP(flags));
	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}
	FDF_SUCCESS;
}
/* }}} */

/* {{{ proto bool fdf_set_javascript_action(resource fdfdoc, string fieldname, int whichtrigger, string script)
   Sets the javascript action for a field */
PHP_FUNCTION(fdf_set_javascript_action) 
{
	zval **fdfp, **fieldname, **trigger, **script;
	FDFDoc fdf;
	FDFErc err;	

	if (ZEND_NUM_ARGS() != 4 || zend_get_parameters_ex(4, &fdfp, &fieldname, &trigger, &script) == FAILURE) {
		WRONG_PARAM_COUNT;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, fdfp, -1, "fdf", le_fdf);

	convert_to_string_ex(fieldname);
	convert_to_long_ex(trigger);
	convert_to_string_ex(script);
	
	err = FDFSetJavaScriptAction(fdf, Z_STRVAL_PP(fieldname), Z_LVAL_PP(trigger), Z_STRVAL_PP(script));
	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}
	FDF_SUCCESS;
}
/* }}} */

/* {{{ proto bool fdf_set_encoding(resource fdf_document, string encoding)
   Sets FDF encoding (either "Shift-JIS" or "Unicode") */  
PHP_FUNCTION(fdf_set_encoding) 
{
	zval **fdfp, **enc;
	FDFDoc fdf;
	FDFErc err;

	if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &fdfp, &enc) == FAILURE) {
		WRONG_PARAM_COUNT;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, fdfp, -1, "fdf", le_fdf);

	convert_to_string_ex(enc);

	err = FDFSetEncoding(fdf, Z_STRVAL_PP(enc));
    
	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}
	FDF_SUCCESS;
}
/* }}} */

/* {{{ SAPI_POST_HANDLER_FUNC
 * SAPI post handler for FDF forms */
SAPI_POST_HANDLER_FUNC(fdf_post_handler)
{
	FILE *fp;
	FDFDoc theFDF;
	char *name=NULL, *value=NULL, *p, *data;
	int name_len=0, value_len=0;
	char *lastfieldname =NULL;
	char *filename = NULL;
	FDFErc err;
	ASInt32 nBytes;
	zval *array_ptr = (zval *) arg;
	
	fp=php_open_temporary_file(NULL, "fdfdata.", &filename TSRMLS_CC);
	if(!fp) {
		if(filename) efree(filename);
		return;
	}
	fwrite(SG(request_info).post_data, SG(request_info).post_data_length, 1, fp);
	fclose(fp);

	/* Set HTTP_FDF_DATA variable */
	data = estrndup(SG(request_info).post_data, SG(request_info).post_data_length);
	SET_VAR_STRINGL("HTTP_FDF_DATA", data, SG(request_info).post_data_length);

 	err = FDFOpen(filename, 0, &theFDF);

	if(err==FDFErcOK){	
		name = emalloc(name_len=256);
		value= emalloc(value_len=256);
		while (1) {
			err = FDFNextFieldName(theFDF, lastfieldname, name, name_len-1, &nBytes);
			if(err == FDFErcBufTooShort && nBytes >0 ) {
				name = erealloc(name, name_len=(nBytes+1)); 
				err = FDFNextFieldName(theFDF, lastfieldname, name, name_len-1, &nBytes);
			} 
			
			if(err != FDFErcOK || nBytes == 0) break; 
			
			if(lastfieldname) efree(lastfieldname);
			lastfieldname = estrdup(name);		

			err = FDFGetValue(theFDF, name, NULL, 0, &nBytes);			
			if(err != FDFErcOK && err != FDFErcNoValue ) break; 

			if(value_len<nBytes+1) value = erealloc(value, value_len=(nBytes+1));
			
			if(nBytes>0) {
				err = FDFGetValue(theFDF, name, value, value_len-1, &nBytes);
				if(err == FDFErcOK && nBytes != 0) {
					unsigned int new_val_len;

					for(p=value;*p;p++) if(*p=='\r') *p='\n';
					if(lastfieldname) efree(lastfieldname);
					lastfieldname = estrdup(name);

					if (sapi_module.input_filter(PARSE_POST, name, &value, nBytes, &new_val_len TSRMLS_CC)) {
						php_register_variable_safe(name, value, new_val_len, array_ptr TSRMLS_CC);
					}
				} 
			}
		}   
		FDFClose(theFDF);

		if(name)          efree(name);
		if(value)         efree(value);
		if(lastfieldname) efree(lastfieldname);
	} 
	VCWD_UNLINK((const char *)filename);
	efree(filename);
}
/* }}} */

/* {{{ proto int fdf_errno(void) 
   Gets error code for last operation */
PHP_FUNCTION(fdf_errno) {
	RETURN_LONG((long)FDF_G(error));
}
/* }}} */

/* {{{ proto string fdf_error([int errno]) 
   Gets error description for error code */
PHP_FUNCTION(fdf_error) {
	FDFErc err;
	long p_err = -1;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &p_err)
	   == FAILURE) {
		return;
	}

	err = (p_err >= 0) ? (FDFErc)p_err : FDF_G(error);

	switch(err) {
	case FDFErcOK: 
		RETURN_STRING("no error", 1);
	case FDFErcInternalError: 
		RETURN_STRING("An internal FDF Library error occurred", 1);
	case FDFErcBadParameter: 
		RETURN_STRING("One or more of the parameters passed were invalid. ", 1);
	case FDFErcFileSysErr: 
		RETURN_STRING("A file system error occurred or the file was not found", 1);
	case FDFErcBadFDF: 
		RETURN_STRING("The FDF file being opened or parsed was invalid", 1);
	case FDFErcFieldNotFound: 
		RETURN_STRING("The field whose name was passed in the parameter fieldName does not exist in the FDF file", 1);
	case FDFErcNoValue: 
		RETURN_STRING("The field whose value was requested has no value", 1);
	case FDFErcEnumStopped: 
		RETURN_STRING("Enumeration was stopped by FDFEnumValues by returning FALSE", 1);
	case FDFErcCantInsertField: 
		RETURN_STRING("The field whose name was passed in the parameter fieldName cannot be inserted into the FDF file", 1);
	case FDFErcNoOption: 
		RETURN_STRING("The requested element in a fields /Opt key does not exist, or the field has no /Opt key. ", 1);
	case FDFErcNoFlags: 
		RETURN_STRING("The field has no /F or /Ff keys", 1);
	case FDFErcBadPDF:
		RETURN_STRING("The PDF file passed as the parameter to FDFSetAP was invalid, or did not contain the requested page ", 1);
	case FDFErcBufTooShort: 
		RETURN_STRING("The buffer passed as a parameter was too short", 1);
	case FDFErcNoAP: 
		RETURN_STRING("The field has no /AP key", 1);
	case FDFErcIncompatibleFDF: 
		RETURN_STRING("An attempt to mix classic and template-based FDF files was made", 1);
#ifdef HAVE_FDFTK_5
	case FDFErcNoAppendSaves: 
		RETURN_STRING("The FDF does not include a /Difference key", 1);
	case FDFErcValueIsArray: 
		RETURN_STRING("The value of this field is an array. Use FDFGetNthValue. ", 1);
	case FDFErcEmbeddedFDFs: 
		RETURN_STRING("The FDF you passed as a parameter is a container for one or more FDFs embedded within it. Use FDFOpenFromEmbedded to gain access to each embedded FDF", 1);
	case FDFErcNoMoreFDFs: 
		RETURN_STRING("Returned by FDFOpenFromEmbedded when parameter iWhich >= the number of embedded FDFs (including the case when the passed FDF does not contain any embedded FDFs)", 1);
	case FDFErcInvalidPassword: 
		RETURN_STRING("Returned by FDFOpenFromEmbedded when the embedded FDF is encrypted, and you did not provide the correct password", 1);
#endif
	case FDFErcLast: 
		RETURN_STRING("Reserved for future use", 1);
	default: 
		RETURN_STRING("unknown error", 1);
	}
}
/* }}} */

/* {{{ proto string fdf_get_version([resource fdfdoc]) 
   Gets version number for FDF api or file */
PHP_FUNCTION(fdf_get_version) {
	zval *r_fdf = NULL;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &r_fdf)
	   == FAILURE) {
		return;
	}

	if(r_fdf) {
#if HAVE_FDFTK_5
		const char *fdf_version; 
		FDFDoc fdf;

		ZEND_FETCH_RESOURCE(fdf, FDFDoc *, &r_fdf, -1, "fdf", le_fdf);
		fdf_version = FDFGetFDFVersion(fdf);
		RETURN_STRING((char *)fdf_version, 1);
#else
		RETURN_STRING("1.2",1);
#endif
	} else {
		const char *api_version = FDFGetVersion();
		RETURN_STRING((char *)api_version, 1);
	}
}
/* }}} */

#ifdef HAVE_FDFTK_5
/* {{{ proto bool fdf_set_version(resourece fdfdoc, string version)
   Sets FDF version for a file*/
PHP_FUNCTION(fdf_set_version) {
	zval *r_fdf;
	char *version;
	int version_len;
	FDFDoc fdf;
	FDFErc err;
	
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &r_fdf, &version, &version_len)
	   == FAILURE) {
		return;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, &r_fdf, -1, "fdf", le_fdf);

	err = FDFSetFDFVersion(fdf, version);

	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}
	FDF_SUCCESS;
}
/* }}} */

/* {{{ proto bool fdf_add_doc_javascript(resource fdfdoc, string scriptname, string script) 
	 Add javascript code to the fdf file */
PHP_FUNCTION(fdf_add_doc_javascript) {
	zval *r_fdf;
	char *name, *script;
	int name_len, script_len;
	FDFDoc fdf;
	FDFErc err;
	
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &r_fdf, 
							 &name, &name_len,
							 &script, &script_len
							 )
	   == FAILURE) {
		return;
	}
	
	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, &r_fdf, -1, "fdf", le_fdf);
	
	err = FDFAddDocJavaScript(fdf, name, script);
	
	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}
	FDF_SUCCESS;
}
/* }}} */

/* {{{ proto bool fdf_set_on_import_javascript(resource fdfdoc, string script, bool before_data_import)
   Adds javascript code to be executed when Acrobat opens the FDF */
PHP_FUNCTION(fdf_set_on_import_javascript) {
	zval *r_fdf;
	char *script;
	int script_len;
	zend_bool before;
	FDFDoc fdf;
	FDFErc err;
	
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsb", &r_fdf, 
							 &script, &script_len, &before
							 )
	   == FAILURE) {
		return;
	}
	
	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, &r_fdf, -1, "fdf", le_fdf);
	
	err = FDFSetOnImportJavaScript(fdf, script, before);
	
	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}
	FDF_SUCCESS;
} 
/* }}} */

/* {{{ proto bool fdf_set_target_frame(resource fdfdoc, string target)
   Sets target frame for form */
PHP_FUNCTION(fdf_set_target_frame) {
	zval *r_fdf;
	char *target;
	int target_len;
	FDFDoc fdf;
	FDFErc err;
	
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &r_fdf, 
							 &target, &target_len
							 )
	   == FAILURE) {
		return;
	}
	
	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, &r_fdf, -1, "fdf", le_fdf);
	
	err = FDFSetTargetFrame(fdf, target);
	
	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}
	FDF_SUCCESS;
} 
/* }}} */
#endif

/* {{{ proto bool fdf_remove_item(resource fdfdoc, string fieldname, int item)
   Sets target frame for form */
PHP_FUNCTION(fdf_remove_item) {
	zval *r_fdf;
	char *fieldname;
	int fieldname_len;
	long item;
	FDFDoc fdf;
	FDFErc err;
	
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsl", &r_fdf, 
							 &fieldname, &fieldname_len,
							 &item
							 )
	   == FAILURE) {
		return;
	}
	
	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, &r_fdf, -1, "fdf", le_fdf);
	
	err = FDFRemoveItem(fdf, *fieldname ? fieldname : NULL, item);
	
	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}
	FDF_SUCCESS;
} 
/* }}} */

#ifdef HAVE_FDFTK_5
/* {{{ proto array fdf_get_attachment(resource fdfdoc, string fieldname, string savepath)
   Get attached uploaded file */
PHP_FUNCTION(fdf_get_attachment) {
	zval *r_fdf;
	char *fieldname, *savepath;
	int fieldname_len, savepath_len;
	int is_dir=0;
	FDFDoc fdf;
	FDFErc err;
	char pathbuf[MAXPATHLEN], mimebuf[1024];
    struct stat statBuf;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &r_fdf, 
							 &fieldname, &fieldname_len,
							 &savepath, &savepath_len
							 )
	   == FAILURE) {
		return;
	}
	
	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, &r_fdf, -1, "fdf", le_fdf);

	if (php_check_open_basedir(savepath TSRMLS_CC) || (PG(safe_mode) && !php_checkuid(savepath, "wb+", CHECKUID_CHECK_MODE_PARAM))) {
		RETURN_FALSE;
	}

	strlcpy(pathbuf, savepath, sizeof(pathbuf));

	if(0 == stat(pathbuf, &statBuf)) {
		is_dir = S_ISDIR(statBuf.st_mode);
	}

	err = FDFExtractAttachment(fdf, fieldname, pathbuf, sizeof(pathbuf), is_dir, mimebuf, sizeof(mimebuf)); 
	
	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}

	array_init(return_value);
	add_assoc_string(return_value, "path", pathbuf, 1);
    add_assoc_string(return_value, "type", mimebuf, 1);
	stat(pathbuf, &statBuf);
	add_assoc_long(return_value, "size", statBuf.st_size);
}
/* }}} */
#endif 

/* {{{ enum_values_callback */
static ASBool enum_values_callback(char *name, char *value, void *userdata)
{
	zval *retval_ptr, *z_name, *z_value, **args[3];
	long retval = 0;
	int numargs = 2;
	TSRMLS_FETCH();

	MAKE_STD_ZVAL(z_name);
	ZVAL_STRING(z_name, name, 1);
	args[0] = &z_name;

	if (*value) { /* simple value */
		MAKE_STD_ZVAL(z_value);
		ZVAL_STRING(z_value, value, 1);
		args[1] = &z_value;
	} else { /* empty value *might* be an array */
		/* TODO: do it like fdf_get_value (or re-implement yourself?) */
	}
	
	if (userdata) {
		args[2] = (zval **) userdata;
		numargs++;
	}

	if (call_user_function_ex(EG(function_table), 
								NULL, 
								FDF_G(enum_callback), 
								&retval_ptr, 
								numargs, args, 
								0, NULL TSRMLS_CC) == SUCCESS
		&& retval_ptr) {
		
		convert_to_long_ex(&retval_ptr);
		retval = Z_LVAL_P(retval_ptr);
		zval_ptr_dtor(&retval_ptr);
	} else {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "callback failed");
	}

	zval_ptr_dtor(&z_name);
	zval_ptr_dtor(&z_value);

	return (ASBool)retval;
}
/* }}} */

/* {{{ proto bool fdf_enum_values(resource fdfdoc, callback function [, mixed userdata])
   Call a user defined function for each document value */
PHP_FUNCTION(fdf_enum_values) {
	zval *r_fdf;
	zval *callback;
	zval *userdata = NULL;
	FDFDoc fdf;
	FDFErc err;
	char *name;
	char namebuf[1024], valbuf[1024];

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rz|z", &r_fdf, 
							 &callback, &userdata
							 )
	   == FAILURE) {
		return;
	}

	ZEND_FETCH_RESOURCE(fdf, FDFDoc *, &r_fdf, -1, "fdf", le_fdf);

	if (!zend_is_callable(callback, 0, &name)) {
		php_error_docref1(NULL TSRMLS_CC, name, E_WARNING, "Second argument is expected to be a valid callback");
		efree(name);
		RETURN_FALSE;
	}
	efree(name);
	FDF_G(enum_callback) = callback;
	FDF_G(enum_fdf) = fdf;

	err = FDFEnumValues(fdf, enum_values_callback, 
						namebuf, sizeof(namebuf), 
						valbuf, sizeof(valbuf), 
						userdata ? &userdata : NULL, 0); 

	if(err != FDFErcOK) {
		FDF_FAILURE(err);
	}
	FDF_SUCCESS;
}
/* }}} */

/* {{{ proto void fdf_header(void) 
   Set FDF specific HTTP headers */
PHP_FUNCTION(fdf_header) {
	sapi_header_line ctr = {0};
	
	ctr.line = "Content-type: application/vnd.fdf";
	ctr.line_len = strlen(ctr.line);
	ctr.response_code = 200;

	sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC);
} 
/* }}} */

#endif

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: sw=4 ts=4 fdm=marker
 * vim<600: sw=4 ts=4
 */
