/*
  +----------------------------------------------------------------------+
  | PHP Version 4                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2004 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3 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/3.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:    Marco Tabini <marcot@tabini.ca>                          |
  |             Ilia Alshanetsky <ilia@php.net>                          |
  +----------------------------------------------------------------------+

  $Id: pop3.c,v 1.19 2005/07/25 21:47:03 iliaa Exp $ 
*/

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

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "ext/standard/md5.h" /* needed for APOP auth */
#ifndef ZEND_ENGINE_2
#include "ext/standard/url.h"
#include "main/php_network.h"
#endif
#include "php_pop3.h"

#include <sys/types.h>
#ifndef PHP_WIN32
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#endif

/* {{{ Internal macros */

#define POP3_VERSION "1.0.2"
#define POP3_RESOURCE_NAME	"POP3 Library Session"

#define POP3_FETCH_SESSION(session, session_container)	\
	ZEND_FETCH_RESOURCE ( session, php_stream *, &session_container, -1, POP3_RESOURCE_NAME, le_pop3_resource);

#ifdef ZEND_ENGINE_2

#define POP3_REGISTER_OBJECT(_object, _ptr) \
    { \
        pop3_object *obj; \
        obj = (pop3_object*)zend_object_store_get_object(_object TSRMLS_CC); \
        obj->ptr = (void *) _ptr; \
    }

#define POP3_FROM_OBJECT(socket_id, object) \
    { \
	pop3_object *obj = zend_object_store_get_object(object TSRMLS_CC); \
	socket_id = (php_stream *) obj->ptr; \
	if (!socket_id) { \
        	php_error_docref(NULL TSRMLS_CC, E_WARNING, "The pop3 connection was not established."); \
		RETURN_FALSE; \
	} \
    }
        
#define POP3_DECLARE_INIT_OBJECT(object) \
	zval	*object = getThis();

#else /* ZEND_ENGINE_2 */

#define POP3_REGISTER_OBJECT(_object, _ptr) {}
#define POP3_FROM_OBJECT(socket_id, object) {}

#define POP3_DECLARE_INIT_OBJECT(object)
#define object 0

#endif /* ZEND_ENGINE_2 */


/* }}} */


#define PHP_POP3_HEADER		1
#define PHP_POP3_MESSAGE	2
#define PHP_POP3_DELETE		3
#define PHP_POP3_SIZE		4

/* True global resources - no need for thread safety here */

static int le_pop3_resource;

/* {{{ _pop3_receive_response */

static int _pop3_receive_response (php_stream *stream, char *response TSRMLS_DC) /* Returns SUCCESS or FAIL */
{
	char buffer[2048];
	size_t fetch_len;

	if (!php_stream_get_line(stream, buffer, sizeof(buffer) - 1, &fetch_len)) {
		php_error_docref(NULL TSRMLS_CC, E_ERROR, "I/O Error when receiving data from the server");
	}

	/* Check for error */

	if (buffer[0] == '-') {
		char *output = memchr(buffer, ' ', fetch_len);
		/* Find the first space */
		if (!output) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "POP3 Error: Unknown POP3 error");
		} else {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "POP3 Error: %s", output + 1);
		}
		return FAILURE;
	} else {
		if (response) {
			memcpy(response, buffer, fetch_len);
			buffer[fetch_len] = '\0';
		}
		return SUCCESS;
	}
}

/* }}} */

/* _pop3_send_command {{{ */

static int _pop3_send_command (php_stream *stream, char *command, char *response TSRMLS_DC)
{
	if ((!php_stream_write_string(stream, command)) || (!php_stream_write_string(stream, "\r\n"))) {
		php_error_docref(NULL TSRMLS_CC, E_ERROR, "I/O Error when sending data to server");
	}

	return _pop3_receive_response(stream, response TSRMLS_CC);
}

/* }}} */

#ifdef ZEND_ENGINE_2

static zend_object_handlers pop3_object_handlers;

/* {{{ pop3_objects_dtor
 */
static void pop3_objects_dtor(void *object, zend_object_handle handle TSRMLS_DC)
{
	pop3_object *intern = (pop3_object *) object;

	if (intern->zo.properties) {
		zend_hash_destroy(intern->zo.properties);
		FREE_HASHTABLE(intern->zo.properties);
	}

	if (intern->ptr) {
		_pop3_send_command((php_stream *) intern->ptr, "QUIT", NULL TSRMLS_CC);
		php_stream_close((php_stream *) intern->ptr);
	}

	efree(intern);
}
/* }}} */

/* {{{ pop3_objects_new
 */
PHP_POP3_API zend_object_value pop3_objects_new(zend_class_entry *class_type TSRMLS_DC)
{
	zend_object_value retval;
	pop3_object *intern;

	intern = emalloc(sizeof(pop3_object));
	intern->zo.ce = class_type;
#if ZEND_MODULE_API_NO >= 20050922
	intern->zo.guards = NULL;
#else
	intern->zo.in_get = 0;
	intern->zo.in_set = 0;
#endif
	intern->zo.properties = NULL;
	intern->ptr = NULL;

	retval.handle = zend_objects_store_put(intern, pop3_objects_dtor, NULL, NULL TSRMLS_CC);
	retval.handlers = (zend_object_handlers *) &pop3_object_handlers;

	return retval;
}
/* }}} */

#endif /* ZEND_ENGINE_2 */

/* {{{ Forward declarations */

/*
 * Forward declarations
 */

void pop3_resource_destructor (zend_rsrc_list_entry *rsrc TSRMLS_DC);

/* }}} */

/* {{{ pop3_functions[]
 *
 * Every user visible function must have an entry in pop3_functions[].
 */
function_entry pop3_functions[] = {
	PHP_FE(pop3_open,	NULL)
	PHP_FE(pop3_close, NULL)
	PHP_FE(pop3_get_message_count, NULL)
	PHP_FE(pop3_get_account_size, NULL)
	PHP_FE(pop3_get_message_ids, NULL)
	PHP_FE(pop3_get_message_sizes, NULL)
	PHP_FE(pop3_get_message_header, NULL)
	PHP_FE(pop3_get_message_size, NULL)
	PHP_FE(pop3_get_message, NULL)
	PHP_FE(pop3_delete_message, NULL)
	PHP_FE(pop3_undelete, NULL)
	{NULL, NULL, NULL}
};
/* }}} */

#define POP3_ME_MAPPING(name, func_name, arg_types) \
        ZEND_NAMED_FE(name, ZEND_FN(func_name), arg_types)

/* {{{ pop3_class_functions
 */
function_entry pop3_class_functions[] = {
	POP3_ME_MAPPING(pop3, pop3_open, NULL)
	POP3_ME_MAPPING(get_message_count, pop3_get_message_count, NULL)
	POP3_ME_MAPPING(get_account_size, pop3_get_account_size, NULL)
	POP3_ME_MAPPING(get_message_ids, pop3_get_message_ids, NULL)
	POP3_ME_MAPPING(get_message_sizes, pop3_get_message_sizes, NULL)
	POP3_ME_MAPPING(get_message_header, pop3_get_message_header, NULL)
	POP3_ME_MAPPING(get_message_size, pop3_get_message_size, NULL)
	POP3_ME_MAPPING(get_message, pop3_get_message, NULL)
	POP3_ME_MAPPING(delete, pop3_delete_message, NULL)
	POP3_ME_MAPPING(undelete, pop3_undelete, NULL)
	{NULL, NULL, NULL}
};
/* }}} */


/* {{{ pop3_module_entry
 */
zend_module_entry pop3_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
	STANDARD_MODULE_HEADER,
#endif
	"pop3",
	pop3_functions,
	PHP_MINIT(pop3),
	PHP_MSHUTDOWN(pop3),
	PHP_RINIT(pop3),		/* Replace with NULL if there's nothing to do at request start */
	PHP_RSHUTDOWN(pop3),	/* Replace with NULL if there's nothing to do at request end */
	PHP_MINFO(pop3),
#if ZEND_MODULE_API_NO >= 20010901
	POP3_VERSION,
#endif
	STANDARD_MODULE_PROPERTIES
};
/* }}} */

#ifdef COMPILE_DL_POP3
ZEND_GET_MODULE(pop3)
#endif

/* {{{ PHP_INI
 */
/* Remove comments and fill if you need to have entries in php.ini
PHP_INI_BEGIN()
    STD_PHP_INI_ENTRY("pop3.global_value",      "42", PHP_INI_ALL, OnUpdateInt, global_value, zend_pop3_globals, pop3_globals)
    STD_PHP_INI_ENTRY("pop3.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_pop3_globals, pop3_globals)
PHP_INI_END()
*/
/* }}} */

/* {{{ php_pop3_init_globals
 */
/* Uncomment this function if you have INI entries
static void php_pop3_init_globals(zend_pop3_globals *pop3_globals)
{
	pop3_globals->global_value = 0;
	pop3_globals->global_string = NULL;
}
*/
/* }}} */

/* {{{ PHP_MINIT_FUNCTION
 */
PHP_MINIT_FUNCTION(pop3)
{
#ifdef ZEND_ENGINE_2

	zend_class_entry _pop3_class_entry;
	INIT_CLASS_ENTRY(_pop3_class_entry, "pop3", pop3_class_functions);
	_pop3_class_entry.create_object = pop3_objects_new;
	pop3_class_entry = zend_register_internal_class(&_pop3_class_entry TSRMLS_CC);

	/* copy the standard object handlers to you handler table */
	memcpy(&pop3_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));

#endif /* ZEND_ENGINE_2 */

	/* If you have INI entries, uncomment these lines 
	ZEND_INIT_MODULE_GLOBALS(pop3, php_pop3_init_globals, NULL);
	REGISTER_INI_ENTRIES();
	*/

	le_pop3_resource = zend_register_list_destructors_ex(pop3_resource_destructor, NULL, POP3_RESOURCE_NAME, module_number);

	return SUCCESS;
}
/* }}} */

/* {{{ PHP_MSHUTDOWN_FUNCTION
 */
PHP_MSHUTDOWN_FUNCTION(pop3)
{
	/* uncomment this line if you have INI entries
	UNREGISTER_INI_ENTRIES();
	*/
	return SUCCESS;
}
/* }}} */

/* {{{ PHP_RINIT_FUNCTION
 */
PHP_RINIT_FUNCTION(pop3)
{
	return SUCCESS;
}
/* }}} */

/* {{{ PHP_RSHUTDOWN_FUNCTION
 */
PHP_RSHUTDOWN_FUNCTION(pop3)
{
	return SUCCESS;
}
/* }}} */

/* {{{ PHP_MINFO_FUNCTION
 */
PHP_MINFO_FUNCTION(pop3)
{
	php_info_print_table_start();
	php_info_print_table_header(2, "pop3 support", "enabled");
	php_info_print_table_header(2, "Extension version", POP3_VERSION " - $id$");
	php_info_print_table_end();

	/* Remove comments if you have entries in php.ini
	DISPLAY_INI_ENTRIES();
	*/
}
/* }}} */

/* {{{ pop3_resource_destructor
 */

void pop3_resource_destructor (zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
	if (rsrc->ptr) {
		_pop3_send_command(rsrc->ptr, "QUIT", NULL TSRMLS_CC);
		php_stream_close(rsrc->ptr);
		rsrc->ptr = NULL;
	}
}

/* }}} */

/* {{{ proto resource pop3_open(string server_name, string user_name, string password)
  */
PHP_FUNCTION(pop3_open)
{
	char	*server_name, *user_name, *password, message[513];
	int	server_name_length, user_name_length, password_length;
	zend_bool auth_mode = 0;

	POP3_DECLARE_INIT_OBJECT(object)

	php_stream			*stream;
#ifndef ZEND_ENGINE_2
	php_url *resource = NULL;
#else
	char				*errstr = NULL;
#endif

	/* Parse parameters */

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|b", &server_name, &server_name_length, &user_name,
				&user_name_length, &password, &password_length, &auth_mode) == FAILURE)
	{
		RETURN_FALSE;
	}

#ifdef ZEND_ENGINE_2
	stream = php_stream_xport_create (server_name, 
							server_name_length, 
							0, 
							STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, 
							NULL,
							NULL,
							NULL,
							&errstr,
							NULL);

	if (errstr) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errstr);
		efree(errstr);
		RETURN_FALSE;
	}
#else 
	resource = php_url_parse(server_name);
	if (resource == NULL) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid server name identifier %s.", server_name);
		RETURN_FALSE;
	}

	stream = php_stream_sock_open_host(resource->host, resource->port, SOCK_STREAM, NULL, 0); 

	php_url_free(resource);
#endif


	if (!stream) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to connect to server");
		RETURN_FALSE;
	}

	if (!auth_mode) { /* Standard Auth */
		if (_pop3_receive_response (stream, NULL TSRMLS_CC) == FAILURE) {
			php_stream_close(stream);
			RETURN_FALSE;
		}

		snprintf(message, sizeof(message) - 1, "USER %s", user_name);

		if (_pop3_send_command (stream, message, NULL TSRMLS_CC) == FAILURE) {
			php_stream_close(stream);
			RETURN_FALSE;
		}

		snprintf(message, sizeof(message) - 1, "PASS %s", password);

		if (_pop3_send_command(stream, message, NULL TSRMLS_CC) == FAILURE) {
			php_stream_close(stream);
			RETURN_FALSE;
		}
	} else { /* APOP */
		char	*s, *e, pop3_return_val[2048] = {0};
		char md5str[33];
		PHP_MD5_CTX context;
		unsigned char digest[16];

		if (_pop3_receive_response(stream, pop3_return_val TSRMLS_CC) == FAILURE) {
			php_stream_close(stream);
			RETURN_FALSE;
		}

		if (!(s = strchr(pop3_return_val, '<'))) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Server does not support APOP authentication.");
			php_stream_close(stream);
			RETURN_FALSE;		
		}
		memmove(pop3_return_val, s, strlen(s));

		if (!(e = strchr(pop3_return_val, '>'))) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Server does not support APOP authentication.");
			php_stream_close(stream);
			RETURN_FALSE;		
		}
		e++;
		memcpy(e, password, password_length);

		md5str[0] = '\0';
		PHP_MD5Init(&context);
		PHP_MD5Update(&context, pop3_return_val, (e - pop3_return_val) + password_length);
		PHP_MD5Final(digest, &context);
		make_digest(md5str, digest);

		snprintf(message, sizeof(message) - 1, "APOP %s %s", user_name, md5str);

		if (_pop3_send_command(stream, message, NULL TSRMLS_CC) == FAILURE) {
			php_stream_close(stream);
			RETURN_FALSE;
		}
	}

	/* Create and return resource or object */

	if (object) {
		POP3_REGISTER_OBJECT(object, stream);
	} else {
		ZEND_REGISTER_RESOURCE(return_value, stream, le_pop3_resource);
	}
}
/* }}} */

/* {{{ _pop3_get_stats() */

static void _pop3_get_stats(INTERNAL_FUNCTION_PARAMETERS, int mode)
{
	POP3_DECLARE_INIT_OBJECT(object)
	zval	*session_container;
	char	pop3_return_val[2048] = {0};
	php_stream *stream;
	int messages, size;

	/* Retrieve session data */
	if (object) {
		POP3_FROM_OBJECT(stream, object);
	} else {
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &session_container) == FAILURE) {
			RETURN_FALSE;
		}
		POP3_FETCH_SESSION(stream, session_container);
	}

	if (_pop3_send_command(stream, "STAT", pop3_return_val TSRMLS_CC) == FAILURE) {
		RETURN_FALSE;
	}

	if (sscanf(pop3_return_val, "+OK %d %d", &messages, &size) < 2) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "POP3: Unable to perform STAT command");
		RETURN_FALSE;
	}

	if (!mode) {
		/* Stat number of messages */
		RETURN_LONG(messages);
	} else {
		/* return pop3 box size */
		RETURN_LONG(size);
	}
}

/* }}} */

/* {{{ proto long pop3_get_message_count (resource session)
  */
PHP_FUNCTION(pop3_get_message_count)
{
	_pop3_get_stats(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */

/* {{{ proto long pop3_get_account_size (resource session)
  */
PHP_FUNCTION(pop3_get_account_size)
{
	_pop3_get_stats(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */

/* {{{ proto array pop3_get_message_sizes (resource session)
  */
PHP_FUNCTION(pop3_get_message_sizes)
{
	POP3_DECLARE_INIT_OBJECT(object)
	zval	*session_container;
	char	pop3_return_val[2048];
	php_stream *stream;
	int message, size;

	/* Retrieve session data */
	if (object) {
		POP3_FROM_OBJECT(stream, object);
	} else {
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &session_container) == FAILURE) {
			RETURN_FALSE;
		}
		POP3_FETCH_SESSION(stream, session_container);
	}

	if (_pop3_send_command(stream, "LIST", NULL TSRMLS_CC) == FAILURE) {
		RETURN_FALSE;
	}

	/* Create return array */
	array_init(return_value);

	do {
		zval *row;

		if (!php_stream_gets(stream, pop3_return_val, sizeof(pop3_return_val) - 1)) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "POP3: Unable to retrieve message list");
			zval_dtor(return_value);
			RETURN_FALSE;
		}

		if (pop3_return_val[0] == '.') {
			return;
		}

		if (sscanf (pop3_return_val, "%d %d", &message, &size) < 2) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "POP3: Unable to retrieve message list");
			zval_dtor(return_value);
			RETURN_FALSE;
		}

		/* Create row array */
		MAKE_STD_ZVAL(row);
		array_init(row);

		add_assoc_long(row, "message_id", message);
		add_assoc_long(row, "size", size);

		/* Insert row in return value */

		add_index_zval(return_value, message, row);

	} while(1);
}
/* }}} */

/* {{{ proto array pop3_get_message_ids (resource session)
  */
PHP_FUNCTION(pop3_get_message_ids)
{
	POP3_DECLARE_INIT_OBJECT(object)
	zval		*session_container, *row;
	char		pop3_return_val[2048], id[513];
	php_stream	*stream;
	int		message;

	/* Retrieve session data */
	if (object) {
		POP3_FROM_OBJECT(stream, object);
	} else {
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &session_container) == FAILURE) {
			RETURN_FALSE;
		}
		POP3_FETCH_SESSION(stream, session_container);
	}

	if (_pop3_send_command(stream, "UIDL", NULL TSRMLS_CC) == FAILURE) {
		RETURN_FALSE;
	}

	/* Create return array */
	array_init(return_value);

	do {

		if (!php_stream_gets(stream, pop3_return_val, sizeof(pop3_return_val) - 1)) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "POP3: Unable to retrieve message list");
			zval_dtor(return_value);
			RETURN_FALSE;
		}

		if (pop3_return_val[0] == '.') {
			return;
		}

		if (sscanf(pop3_return_val, "%d %512s", &message, id) < 2) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "POP3: Unable to retrieve message list");
			zval_dtor(return_value);
			RETURN_FALSE;
		}

		/* Create row array */
		MAKE_STD_ZVAL(row);
		array_init(row);

		add_assoc_long(row, "message_id", message);
		add_assoc_string(row, "id", id, 1);

		/* Insert row in return value */
		add_index_zval(return_value, message, row);

	} while(1);
}
/* }}} */

/* {{{ _pop3_fetch_message_part() */

static void _pop3_fetch_message_part(INTERNAL_FUNCTION_PARAMETERS, int mode)
{
	POP3_DECLARE_INIT_OBJECT (object)
	zval		*session_container;
	char		pop3_return_val[2048] = {0}, *result, message[513];
	php_stream	*stream;
	int		message_id, n_lines = 0, length, size;
	size_t		fetch_len;

	/* Retrieve session data and other parameters */
	if (object) {
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|l", &message_id, &n_lines) == FAILURE) {
			RETURN_FALSE;
		}
		POP3_FROM_OBJECT(stream, object);
	} else {
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|l", &session_container, &message_id, &n_lines) == FAILURE) {
			RETURN_FALSE;
		}
		POP3_FETCH_SESSION(stream, session_container);
	}

	switch (mode) {
		case PHP_POP3_HEADER:
			/* Retrieve message headers */
			snprintf(message, sizeof(message) - 1, "TOP %d %d", message_id, 0);

			if (_pop3_send_command(stream, message, pop3_return_val TSRMLS_CC) == FAILURE) {
				RETURN_FALSE;
			}
			break;

		case PHP_POP3_MESSAGE:
			if (!n_lines) { /* Retrieve message */
				snprintf(message, sizeof(message) - 1, "RETR %d", message_id);

				if (_pop3_send_command(stream, message, pop3_return_val TSRMLS_CC) == FAILURE) {
					RETURN_FALSE;
				}
			} else { /* Retrieve n_lines from the message */
				snprintf(message, sizeof(message) - 1, "RETR %d %d", message_id, n_lines);

				if (_pop3_send_command(stream, message, pop3_return_val TSRMLS_CC) == FAILURE) {
					RETURN_FALSE;
				}
			}
			break;

		case PHP_POP3_DELETE:
			/* Delete message */
			snprintf(message, sizeof(message) - 1, "DELE %d", message_id);

			if (_pop3_send_command(stream, message, pop3_return_val TSRMLS_CC) == FAILURE) {
				RETURN_FALSE;
			}
			RETURN_TRUE;

		case PHP_POP3_SIZE:
			/* get message size */
			snprintf(message, sizeof(message) - 1, "LIST %d", message_id);

			if (_pop3_send_command(stream, message, pop3_return_val TSRMLS_CC) == FAILURE) {
				RETURN_FALSE;
			}

			{
				int tmp, len = 0;
				if (sscanf(pop3_return_val, "+OK %d %d", &tmp, &len) < 2) {
					php_error_docref(NULL TSRMLS_CC, E_WARNING, "POP3: Unable to fetch message size.");
					RETURN_FALSE;
				}
				RETURN_LONG(len);
			}

		default: /* can't happen */
			RETURN_FALSE;
	}

	size = 2048;
	result = emalloc(size);
	length = 0;

	do {
		if (!php_stream_get_line(stream, pop3_return_val, sizeof(pop3_return_val) - 1, &fetch_len)) {
			efree(result);
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "POP3: I/O failure reading from server");
			RETURN_FALSE;
		}

		if (fetch_len == 3 && !strcmp(pop3_return_val, ".\r\n")) {
			result[length] = '\0';
			RETURN_STRINGL(result, length, 0);
		}

		if (length + fetch_len > size) {
			size += 2048;
			result = erealloc(result, size);
		}

		memcpy(result + length, pop3_return_val, fetch_len);
		length += fetch_len;
	} while (1);
}

/* }}} */

/* {{{ proto string pop3_get_message_header (resource session, int message_id)
  */
PHP_FUNCTION (pop3_get_message_header)
{
	_pop3_fetch_message_part(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_POP3_HEADER);	
}
/* }}} */

/* {{{ proto int pop3_get_message_size (resource session, int message_id)
  */
PHP_FUNCTION (pop3_get_message_size)
{
	_pop3_fetch_message_part(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_POP3_SIZE);	
}
/* }}} */

/* {{{ proto string pop3_get_message (resource session, int message_id, [int n_lines])
  */
PHP_FUNCTION (pop3_get_message)
{
	_pop3_fetch_message_part(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_POP3_MESSAGE);
}
/* }}} */

/* {{{ proto bool pop3_delete_message (resource session, int message_id)
  */
PHP_FUNCTION (pop3_delete_message)
{
	_pop3_fetch_message_part(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_POP3_DELETE);
}
/* }}} */

/* {{{ proto bool pop3_undelete (resource session)
  */
PHP_FUNCTION (pop3_undelete)
{
	POP3_DECLARE_INIT_OBJECT (object)
	zval	*session_container;
	php_stream	*stream;

	if (object) {
		if (ZEND_NUM_ARGS()) {
			WRONG_PARAM_COUNT;
		}
		POP3_FROM_OBJECT(stream, object);
	} else {
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &session_container) == FAILURE) {
			RETURN_FALSE;
		}
		POP3_FETCH_SESSION(stream, session_container);
	}

	if (_pop3_send_command(stream, "RSET", NULL TSRMLS_CC) == FAILURE) {
		RETURN_FALSE;
	} else {
		RETURN_TRUE;
	}
}
/* }}} */

/* {{{ proto bool pop3_close (resource session)
  */
PHP_FUNCTION (pop3_close)
{
	zval		*session_container;
	php_stream	*stream;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &session_container) == FAILURE) {
		RETURN_FALSE;
	}
	POP3_FETCH_SESSION(stream, session_container);

	zend_list_delete(Z_RESVAL_P(session_container));

	RETURN_TRUE;
}
/* }}} */

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


syntax highlighted by Code2HTML, v. 0.9.1