[chora] Fwd: [PECL-CVS] cvs: pecl /cvsclient config.m4 cvsclient.c package.xml php_cvsclient.h

Jan Schneider jan at horde.org
Sun Nov 30 09:54:34 PST 2003


This might become an interesting extension.

----- Weitergeleitete Nachricht von pollita at php.net -----
    Datum: Sun, 30 Nov 2003 01:53:38 -0000
    Von: Sara Golemon <pollita at php.net>
Antwort an: Sara Golemon <pollita at php.net>
 Betreff: [PECL-CVS] cvs: pecl /cvsclient config.m4 cvsclient.c package.xml
php_cvsclient.h
      An: pecl-cvs at lists.php.net

pollita		Sat Nov 29 20:53:38 2003 EDT

  Added files:
    /pecl/cvsclient	config.m4 cvsclient.c package.xml php_cvsclient.h
  Log:
  Initial Release



----- Ende der weitergeleiteten Nachricht -----


Jan.

--
http://www.horde.org - The Horde Project
http://www.ammma.de - discover your knowledge
http://www.tip4all.de - Deine private Tippgemeinschaft
-------------- next part --------------

Index: pecl/cvsclient/config.m4
+++ pecl/cvsclient/config.m4
PHP_ARG_ENABLE(cvsclient, whether to enable the CVS client stream wrapper,
[  --enable-cvsclient      Enable PHP cvs:// wrapper.])

if test "$PHP_CVSCLIENT" != "no"; then
  AC_DEFINE(WITH_CVSCLIENT, 1, [Whether you want the CVS client])
  PHP_NEW_EXTENSION(cvsclient, cvsclient.c,
	$ext_shared,,)
fi

Index: pecl/cvsclient/cvsclient.c
+++ pecl/cvsclient/cvsclient.c
/*
   +----------------------------------------------------------------------+
   | PHP Version 4                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2002 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 at php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Author: Sara Golemon <pollita at php.net>                               |
   +----------------------------------------------------------------------+
*/

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

#include "php.h"

#if WITH_CVSCLIENT

#include "php_cvsclient.h"
#include "php_globals.h"
#include "php_network.h"
#include "ext/standard/url.h"
#include "ext/standard/info.h"
#include <sys/socket.h>
#include <ctype.h>

#define PHP_CVSCLIENT_REQ_ARGUMENT		0x00000001
#define PHP_CVSCLIENT_REQ_ARGUMENTX		0x00000002
#define PHP_CVSCLIENT_REQ_DIRECTORY		0x00000004
#define PHP_CVSCLIENT_REQ_ROOT			0x00000008
#define PHP_CVSCLIENT_REQ_REPOSITORY	0x00000010
#define PHP_CVSCLIENT_REQ_UPDATE		0x00000020
#define PHP_CVSCLIENT_REQ_CI			0x00000040
#define PHP_CVSCLIENT_REQ_CO			0x00000080
#define PHP_CVSCLIENT_REQ_DIFF			0x00000100
#define PHP_CVSCLIENT_REQ_LOG			0x00000200
#define PHP_CVSCLIENT_REQ_ADD			0x00000400
#define PHP_CVSCLIENT_REQ_REMOVE		0x00000800
#define PHP_CVSCLIENT_REQ_HISTORY		0x00001000

typedef struct _php_cvsclient_requests {
	long request;
	char *label;
} php_cvsclient_requests;

static php_cvsclient_requests cvsclient_requests[] = {
	{ PHP_CVSCLIENT_REQ_ARGUMENT,		"argument"		},
	{ PHP_CVSCLIENT_REQ_ARGUMENTX,		"argumentx"		},
	{ PHP_CVSCLIENT_REQ_DIRECTORY,		"directory"		},
	{ PHP_CVSCLIENT_REQ_ROOT,			"root"			},
	{ PHP_CVSCLIENT_REQ_REPOSITORY,		"repository"	},
	{ PHP_CVSCLIENT_REQ_UPDATE,			"update"		},
	{ PHP_CVSCLIENT_REQ_CI,				"ci"			},
	{ PHP_CVSCLIENT_REQ_CO,				"co"			},
	{ PHP_CVSCLIENT_REQ_DIFF,			"diff"			},
	{ PHP_CVSCLIENT_REQ_LOG,			"log"			},
	{ PHP_CVSCLIENT_REQ_ADD,			"add"			},
	{ PHP_CVSCLIENT_REQ_REMOVE,			"remove"		},
	{ PHP_CVSCLIENT_REQ_HISTORY,		"history"		},
	{ 0,								NULL			}
};

static char cvs_encode[96] =
	{  32, 120,  53,  35,  36, 109,  72, 108,
	   70,  64,  76,  67, 116,  74,  68,  87,
	  111,  52,  75, 119,  49,  34,  82,  81,
	   95,  65, 112,  86, 118, 110, 122, 105,
	   64,  57,  83,  43,  46, 102,  40,  89,
	   38, 103,  45,  50,  42, 123,  91,  35,
	  125,  55,  54,  66, 124, 126,  59,  47,
	   92,  71, 115,  91,  92,  93,  94,  56,
	   96, 121, 117, 104, 101, 100,  69,  73,
	   99,  63,  94,  93,  39,  37,  61,  48,
	   58, 113,  32,  90,  44,  98,  60,  51,
	   33,  97,  62, 123, 124, 125, 126, 127 };

function_entry cvsclient_functions[] = {
	/* TODO: Resource based single-session access and committment */
	{NULL, NULL, NULL}
};

zend_module_entry cvsclient_module_entry = {
	STANDARD_MODULE_HEADER,
	"cvsclient",
	cvsclient_functions,
	PHP_MINIT(cvsclient),
	PHP_MSHUTDOWN(cvsclient),
	NULL, /* RINIT */
	NULL, /* RSHUTDOWN */
	PHP_MINFO(cvsclient),
	NO_VERSION_YET,
	STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_CVSCLIENT
ZEND_GET_MODULE(cvsclient)
#endif


/* {{{ php_cvsclient_do_connect */
static php_stream * php_cvsclient_do_connect(char *path, int options, php_stream_context *context, php_url **presource STREAMS_DC TSRMLS_DC)
{
	php_stream *stream = NULL;
	php_url *resource = NULL;

	resource = php_url_parse((char *) path);
	if (!resource || !resource->scheme || !resource->host) {
		goto connect_errexit;
	}

	if (strcasecmp("cvs", resource->scheme)) {
		goto connect_errexit;
	}

	/* use port 2401 if one wasn't specified */
	if (resource->port == 0) {
		resource->port = 2401;
	}

	stream = php_stream_sock_open_host(resource->host, resource->port, SOCK_STREAM, NULL, 0);
	if (stream == NULL) {
		goto connect_errexit;
	}

    php_stream_context_set(stream, context);
    php_stream_notify_info(context, PHP_STREAM_NOTIFY_CONNECT, NULL, 0);

	if (presource) {
		*presource = resource;
	} else {
		php_url_free(resource);
	}

	return stream;

 connect_errexit:
	if (resource) {
		php_url_free(resource);
	}
	if (stream) {
		php_stream_close(stream);
	}

	return NULL;
}
/* }}} */

/* {{{ php_cvsclient_encode
 */
static char * php_cvsclient_encode(const char *orig)
{
	unsigned char *enc;
	int i;

	enc = estrdup(orig);
	for(i=0; i<strlen(enc); i++) {
		if (enc[i] >= 32 && enc[i] <= 127) {
			enc[i] = cvs_encode[enc[i] - 32];
		}
	}

	return enc;
}
/* }}} */

/* {{{ php_cvsclient_authenticate
 */
static int php_cvsclient_authenticate(php_stream *stream, const char *cvsroot, const char *user, const char *pass STREAMS_DC TSRMLS_DC)
{
	char *encpw;
	char response[128];

	encpw = php_cvsclient_encode(pass);
	
	php_stream_write_string(stream, "BEGIN AUTH REQUEST\n");
	php_stream_printf(stream TSRMLS_CC, "%s\n", cvsroot);
	php_stream_printf(stream TSRMLS_CC, "%s\n", user);
	php_stream_printf(stream TSRMLS_CC, "A%s\n", encpw);
	php_stream_write_string(stream, "END AUTH REQUEST\n");

	efree(encpw);

	/* Who do you love? */
	if (php_stream_gets(stream, response, sizeof(response)-1) == NULL) {
		return FAILURE;
	}
	if (strncmp(response, "I LOVE YOU", strlen("I LOVE YOU"))) {
		return FAILURE;
	}
	return SUCCESS;
}
/* }}} */

/* {{{ php_cvsclient_negotiate */
static int php_cvsclient_negotiate(php_stream *stream, const char *cvsroot STREAMS_DC TSRMLS_DC)
{
	php_cvsclient_requests *verbs = cvsclient_requests;
	char response[4096];
	int requests = 0, i;

	php_stream_printf(stream TSRMLS_CC, "Root %s\n", cvsroot);
	php_stream_printf(stream TSRMLS_CC, "Valid-responses %s %s %s\n",
		"Valid-requests New-entry Updated Created Update-existing Merged Rcs-diff Patched Mode Mod-time Checksum",
		"Copy-file Removed Remove-entry Set-static-directory Clear-static-directory Set-sticky Clear-sticky Template Set-checkin-prog",
		"Set-update-prog Notified Module-expansion Wrapper-rcsOption ok error Checked-in M E F MT");
	php_stream_write_string(stream, "valid-requests\n");

	if (php_stream_gets(stream, response, sizeof(response)-1) == NULL) {
		return 0;
	}

	for(i=0; i<strlen(response); i++) {
		response[i] = tolower(response[i]);
	}

	/* TODO: Improve this, we could (and probably do) get false positives */
	while (verbs->request && verbs->label) {
		if (strstr(response, verbs->label)) {
			requests |= verbs->request;
		}
		verbs++;
	}

	return requests;
}
/* }}} */

/* {{{ php_stream_url_wrap_cvsclient
 */
static php_stream * php_stream_url_wrap_cvsclient(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
{
	php_cvsclient_requests *verbs = cvsclient_requests;
	php_stream *stream, *memstream;
	php_url *resource = NULL;
	char *module = NULL;
	char *cvsroot = NULL;
	char *filepath = NULL;
	char *filename, *p;
	char response[4096];
	int valid_requests, i;
	long filesize;
	zval *wrapperdata = NULL;
	zval **tmpzval;

	if (strpbrk(mode, "awx+")) {
		php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "CVS wrapper does not support writeable connections (yet).");
		return NULL;
	}

	/* Connect */
	stream = php_cvsclient_do_connect(path, options, context, &resource STREAMS_CC TSRMLS_CC);
	if (!stream || !resource->path) {
		goto wrap_errexit;
	}
	p = strchr(resource->path + 1, '/');
	if (p) {
		cvsroot = estrndup(resource->path, p - resource->path);
		filename = p;
		p = strchr(filename + 1, '/');
		if (p) {
			module = estrndup(filename, p - filename);
			filename = p;
			p = strrchr(filename + 1, '/');
			if (p) {
				filepath = estrndup(filename, p - filename);
				filename = p + 1;
			} else {
				filename++;
			}
		} else {
			goto wrap_errexit;
		}
	} else {
		goto wrap_errexit;
	}

	/* Authenticate */
	if (resource && resource->user && resource->pass &&
		php_cvsclient_authenticate(stream, cvsroot, resource->user, resource->pass STREAMS_CC TSRMLS_CC) == FAILURE) {
		goto wrap_errexit;
	}

	/* Negotiate */
	if ((valid_requests = php_cvsclient_negotiate(stream, cvsroot STREAMS_CC TSRMLS_CC)) == 0) {
		goto wrap_errexit;
	}

	/* Request */
	if (context &&
		php_stream_context_get_option(context, "cvs", "revision", &tmpzval) == SUCCESS) {
		SEPARATE_ZVAL(tmpzval);
		convert_to_string_ex(tmpzval);
		php_stream_printf(stream TSRMLS_CC, "Argument -r\nArgument %s\n", Z_STRVAL_PP(tmpzval));
	}
	php_stream_printf(stream TSRMLS_CC, "Argument %s\n", filename);
	php_stream_write_string(stream, "Directory .\n");
	php_stream_printf(stream TSRMLS_CC, "%s%s%s\n", cvsroot, module, filepath ? filepath : "");
	php_stream_write_string(stream, "update\n");

	efree(cvsroot);
	cvsroot = NULL;
	efree(module);
	module = NULL;
	if (filepath) {
		efree(filepath);
		filepath = NULL;
	}

	/* Parse response */
	ALLOC_INIT_ZVAL(wrapperdata);
	array_init(wrapperdata);
	while (php_stream_gets(stream, response, sizeof(response)-1) != NULL) {
		if (strncasecmp(response, "error", 5) == 0) {
			goto wrap_errexit;
		}
		if (strncasecmp(response, "mod-time ", 9) == 0) {
			add_assoc_string(wrapperdata, "mod-time", response + 9, 1);
		}
		if ((strlen(response) > strlen(filename) + 4) && 
			response[0] == '/' && 
			strncmp(response + 1, filename, strlen(filename)) == 0 &&
			response[strlen(filename)+1] == '/') {
			p = response + strlen(filename) + 2;
			p = strchr(p, '/');
			if (p) {
				*p = '\0';
				p = response + strlen(filename) + 2;
				add_assoc_string(wrapperdata, "revision", p, 1);
			}
		}
		/* TODO: Parse other elements into wrapperdata */
		for(i=0; i<strlen(response); i++) {
			if (!isdigit(response[i]) && !iscntrl(response[i])) {
				goto header_loop;
			}
		}
		break;
 header_loop:
	}
	filesize = atoi(response);
	add_assoc_long(wrapperdata, "filesize", filesize);

	memstream = php_stream_fopen_tmpfile();
	if (!memstream) {
		goto wrap_errexit;
	}
	while (filesize) {
		int read;

		read = php_stream_read(stream, response, (filesize > (sizeof(response) - 1)) ? sizeof(response)-1 : filesize);
		php_stream_write(memstream, response, read);

		filesize -= read;
	}
	php_stream_rewind(memstream);
	php_stream_close(stream);

	memstream->wrapperdata = wrapperdata;

	php_url_free(resource);
	resource = NULL;

	return memstream;

 wrap_errexit:
	if (filepath) {
		efree(filepath);
	}
	if (module) {
		efree(module);
	}
	if (cvsroot) {
		efree(cvsroot);
	}
	if (wrapperdata) {
		zval_ptr_dtor(&wrapperdata);
	}
	if (resource) {
		php_url_free(resource);
	}
	if (stream) {
		php_stream_close(stream);
	}
	return NULL;
}
/* }}} */

/* {{{ php_stream_cvsclient_stat
 */
static int php_stream_cvsclient_stat(php_stream_wrapper *wrapper,
		php_stream *stream,
		php_stream_statbuf *ssb
		TSRMLS_DC)
{
	/* For now, we return with a failure code to prevent the underlying
	 * file's details from being used instead. */
	return -1;
}
/* }}} */

static php_stream_wrapper_ops cvsclient_stream_wops = {
	php_stream_url_wrap_cvsclient,
	NULL, /* stream_close */
	php_stream_cvsclient_stat,
	NULL, /* stat_url */
	NULL, /* opendir */
	"CVS"
#if PHP_MAJOR_VERSION >= 5
	,NULL /* unlink */
#endif
};

PHPAPI php_stream_wrapper php_stream_cvsclient_wrapper =  {
	&cvsclient_stream_wops,
	NULL,
	1 /* is_url */
};

PHP_MINIT_FUNCTION(cvsclient)
{
	/* TODO: Readonly support for cvs.log, cvs.history, etc... */
	return php_register_url_stream_wrapper("cvs", &php_stream_cvsclient_wrapper TSRMLS_CC);
}

PHP_MSHUTDOWN_FUNCTION(cvsclient)
{
	return php_unregister_url_stream_wrapper("cvs" TSRMLS_CC);
}


PHP_MINFO_FUNCTION(cvsclient)
{
	php_info_print_table_start();
	php_info_print_table_row(2, "CVS Client", "enabled");
	php_info_print_table_row(2, "Wrapper", "cvs://");
	php_info_print_table_end();
}

#endif

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

Index: pecl/cvsclient/package.xml
+++ pecl/cvsclient/package.xml
<package version="1.0">
  <name>cvsclient</name>
  <summary>CVS pserver client</summary>
  <description>
    pserver client extension.  Current version has read-only streams wrapper.
    Later versions to include commit/diff/log support.
  </description>
  <license>PHP</license>
  <maintainers>
    <maintainer>
      <user>pollita</user>
      <name>Sara Golemon</name>
      <email>pollita at php.net</email>
      <role>lead</role>
    </maintainer>
  </maintainers>

  <release>
    <version>0.1</version>
    <state>alpha</state>
    <date>2003-11-29</date>
    <notes>Initial Release</notes>
    <filelist>
      <file role="src" name="config.m4"/>
      <file role="src" name="cvsclient.c"/>
      <file role="src" name="php_cvsclient.h"/>      
    </filelist>
  </release>

  <deps>
    <dep type="php" rel="ge">4.3.0</dep>
  </deps>
</package>

Index: pecl/cvsclient/php_cvsclient.h
+++ pecl/cvsclient/php_cvsclient.h
/* 
   +----------------------------------------------------------------------+
   | PHP Version 4                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2002 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 at php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Author: Sara Golemon <pollita at php.net>                               |
   +----------------------------------------------------------------------+
*/

#ifndef PHP_CVSCLIENT_H
#define PHP_CVSCLIENT_H

#if WITH_CVSCLIENT

extern zend_module_entry cvsclient_module_entry;
#define phpext_cvsclient_ptr &cvsclient_module_entry

PHP_MINIT_FUNCTION(cvsclient);
PHP_MSHUTDOWN_FUNCTION(cvsclient);
PHP_MINFO_FUNCTION(cvsclient);

#else

#define phpext_cvsclient_ptr NULL

#endif /* WITH_CVSCLIENT */

#endif /* PHP_CVSCLIENT_H */

-------------- next part --------------
-- 
PECL CVS Mailing List 
To unsubscribe, visit: http://www.php.net/unsub.php


More information about the chora mailing list