diff options
author | Zeev Suraski <zeev@php.net> | 2001-03-02 14:36:29 +0000 |
---|---|---|
committer | Zeev Suraski <zeev@php.net> | 2001-03-02 14:36:29 +0000 |
commit | 9f5b255b3f8625bf04d1f96e075d7b55697bad5f (patch) | |
tree | c96ddc6f99f6753b997290f79ac617681dabf0f9 /ext/cybermut | |
parent | f262b2a7383952951bf36018b9c0ccb449104dae (diff) | |
download | php-git-9f5b255b3f8625bf04d1f96e075d7b55697bad5f.tar.gz |
Add CyberMut extension from Sylvain PAGES <spages@free.fr>
Diffstat (limited to 'ext/cybermut')
-rw-r--r-- | ext/cybermut/Makefile.in | 8 | ||||
-rw-r--r-- | ext/cybermut/config.m4 | 49 | ||||
-rw-r--r-- | ext/cybermut/cybermut.c | 228 | ||||
-rw-r--r-- | ext/cybermut/cybermut.xml | 214 | ||||
-rw-r--r-- | ext/cybermut/libs.mk | 7 | ||||
-rw-r--r-- | ext/cybermut/php_cybermut.h | 75 | ||||
-rw-r--r-- | ext/cybermut/tests/001.phpt | 23 |
7 files changed, 604 insertions, 0 deletions
diff --git a/ext/cybermut/Makefile.in b/ext/cybermut/Makefile.in new file mode 100644 index 0000000000..6344d0f4d1 --- /dev/null +++ b/ext/cybermut/Makefile.in @@ -0,0 +1,8 @@ +# $Id$ + +LTLIBRARY_NAME = libcybermut.la +LTLIBRARY_SOURCES = cybermut.c +LTLIBRARY_SHARED_NAME = cybermut.la +LTLIBRARY_SHARED_LIBADD = $(CYBERMUT_SHARED_LIBADD) + +include $(top_srcdir)/build/dynlib.mk diff --git a/ext/cybermut/config.m4 b/ext/cybermut/config.m4 new file mode 100644 index 0000000000..dd2bc82205 --- /dev/null +++ b/ext/cybermut/config.m4 @@ -0,0 +1,49 @@ +dnl $Id$ +dnl config.m4 for extension cybermut + + +PHP_ARG_WITH(cybermut, for cybermut support, +[ --with-cybermut[=DIR] Include CyberMut (french Credit Mutuel telepaiement)]) + + +if test "$PHP_CYBERMUT" != "no"; then + for i in /usr/local /usr $PHP_CYBERMUT; do + if test -r $i/cm-mac.h; then + CYBERMUT_INC_DIR=$i + elif test -r $i/include/cm-mac.h; then + CYBERMUT_INC_DIR=$i/include + fi + + if test -r $i/libcm-mac.a; then + CYBERMUT_LIB_DIR=$i + elif test -r $i/lib/libcm-mac.a; then + CYBERMUT_LIB_DIR=$i/lib + fi + done + + if test -z "$CYBERMUT_INC_DIR"; then + AC_MSG_ERROR(Could not find cm-mac.h Please make sure you have the + CyberMut SDK installed. Use + ./configure --with-cybermut=<cybermut-dir> if necessary) + fi + + if test -z "$CYBERMUT_LIB_DIR"; then + AC_MSG_ERROR(Could not find libcm-mac.a Please make sure you have the + CyberMut SDK installed. Use + ./configure --with-cybermut=<cybermut-dir> if necessary) + fi + AC_MSG_RESULT(found in $CYBERMUT_LIB_DIR) + + AC_MSG_RESULT(linking libcm-mac.a with libcybermut.a) + ln -sf $CYBERMUT_LIB_DIR/libcm-mac.a $CYBERMUT_LIB_DIR/libcybermut.a + + AC_ADD_INCLUDE($CYBERMUT_INC_DIR) + + PHP_SUBST(CYBERMUT_SHARED_LIBADD) + AC_ADD_LIBRARY_WITH_PATH(cybermut, $CYBERMUT_LIB_DIR, CYBERMUT_SHARED_LIBADD) + + AC_DEFINE(HAVE_CYBERMUT, 1, [ ]) + + + PHP_EXTENSION(cybermut, $ext_shared) +fi diff --git a/ext/cybermut/cybermut.c b/ext/cybermut/cybermut.c new file mode 100644 index 0000000000..7dd83600f5 --- /dev/null +++ b/ext/cybermut/cybermut.c @@ -0,0 +1,228 @@ +/* + +----------------------------------------------------------------------+ + | 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: Sylvain PAGES <spages@free.fr> | + | | + +----------------------------------------------------------------------+ + */ + +#include "php.h" +#include "php_ini.h" +#include "php_cybermut.h" + +#include "cm-mac.h" + +/* If you declare any globals in php_cybermut.h uncomment this: +ZEND_DECLARE_MODULE_GLOBALS(cybermut) +*/ + +/* True global resources - no need for thread safety here */ +static int le_cybermut; + +/* Every user visible function must have an entry in cybermut_functions[]. +*/ +function_entry cybermut_functions[] = { + PHP_FE(confirm_cybermut_compiled, NULL) /* For testing, remove later. */ + PHP_FE(cybermut_creerformulairecm, NULL) + PHP_FE(cybermut_testmac, NULL) + PHP_FE(cybermut_creerreponsecm, NULL) + {NULL, NULL, NULL} /* Must be the last line in cybermut_functions[] */ +}; + +zend_module_entry cybermut_module_entry = { + "cybermut", + cybermut_functions, + PHP_MINIT(cybermut), + PHP_MSHUTDOWN(cybermut), + PHP_RINIT(cybermut), /* Replace with NULL if there's nothing to do at request start */ + PHP_RSHUTDOWN(cybermut), /* Replace with NULL if there's nothing to do at request end */ + PHP_MINFO(cybermut), + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_CYBERMUT +ZEND_GET_MODULE(cybermut) +#endif + +/* Remove comments and fill if you need to have entries in php.ini +PHP_INI_BEGIN() +PHP_INI_END() +*/ + +PHP_MINIT_FUNCTION(cybermut) +{ +/* Remove comments if you have entries in php.ini + REGISTER_INI_ENTRIES(); +*/ + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(cybermut) +{ +/* Remove comments if you have entries in php.ini + UNREGISTER_INI_ENTRIES(); +*/ + return SUCCESS; +} + +/* Remove if there's nothing to do at request start */ +PHP_RINIT_FUNCTION(cybermut) +{ + return SUCCESS; +} + +/* Remove if there's nothing to do at request end */ +PHP_RSHUTDOWN_FUNCTION(cybermut) +{ + return SUCCESS; +} + +PHP_MINFO_FUNCTION(cybermut) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "cybermut support", "enabled"); + php_info_print_table_end(); + + /* Remove comments if you have entries in php.ini + DISPLAY_INI_ENTRIES(); + */ +} + +/* Remove the following function when you have succesfully modified config.m4 + so that your module can be compiled into PHP, it exists only for testing + purposes. */ + +/* Every user-visible function in PHP should document itself in the source */ +/* {{{ proto string confirm_cybermut_compiled(string arg) + Return a string to confirm that the module is compiled in */ +PHP_FUNCTION(confirm_cybermut_compiled) +{ + zval **arg; + int len; + char string[256]; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) { + WRONG_PARAM_COUNT; + } + + convert_to_string_ex(arg); + + len = sprintf(string, "Congratulations, you have successfully modified ext/cybermut/config.m4, module %s is compiled into PHP", Z_STRVAL_PP(arg)); + RETURN_STRINGL(string, len, 1); +} +/* }}} */ +/* The previous line is meant for emacs, so it can correctly fold and unfold + functions in source code. See the corresponding marks just before function + definition, where the functions purpose is also documented. Please follow + this convention for the convenience of others editing your code. +*/ + +/* {{{ proto string cybermut_creerformulairecm(string url_CM, string version, string TPE, string montant, string ref_commande, string texte_libre, string url_retour, string url_retour_ok, string url_retour_err, string langue, string code_societe, string texte_bouton) + Return a string containing source HTML of the form of request for payment. + This result corresponds to the last parameter "formulaire" of the original function + which was removed */ +PHP_FUNCTION(cybermut_creerformulairecm) +{ + zval **url_CM, **version, **TPE, **montant, **ref_commande, **texte_libre, **url_retour, **url_retour_ok, **url_retour_err, **langue, **code_societe, **texte_bouton; + + char formulaire [10000]; + + if (ZEND_NUM_ARGS() != 12 || zend_get_parameters_ex(12, &url_CM, &version, &TPE, &montant, &ref_commande, &texte_libre, &url_retour, &url_retour_ok, &url_retour_err, &langue, &code_societe, &texte_bouton) == FAILURE){ + WRONG_PARAM_COUNT; + } + + convert_to_string_ex(url_CM); + convert_to_string_ex(version); + convert_to_string_ex(TPE); + convert_to_string_ex(montant); + convert_to_string_ex(ref_commande); + convert_to_string_ex(texte_libre); + convert_to_string_ex(url_retour); + convert_to_string_ex(url_retour_ok); + convert_to_string_ex(url_retour_err); + convert_to_string_ex(langue); + convert_to_string_ex(code_societe); + convert_to_string_ex(texte_bouton); + + CreerFormulaireCM ( Z_STRVAL_PP(url_CM), Z_STRVAL_PP(version), Z_STRVAL_PP(TPE), Z_STRVAL_PP(montant), Z_STRVAL_PP(ref_commande), Z_STRVAL_PP(texte_libre), Z_STRVAL_PP(url_retour), Z_STRVAL_PP(url_retour_ok), Z_STRVAL_PP(url_retour_err), Z_STRVAL_PP(langue), Z_STRVAL_PP(code_societe), Z_STRVAL_PP(texte_bouton), formulaire); + + + RETURN_STRING(formulaire, 1); + +} +/* }}} */ + +/* {{{ proto bool cybermut_testmac(string code_MAC, string version, string TPE, string cdate, string montant, string ref_commande, string texte_libre, string code_retour) + Return a boolean attesting that the authentification proceeded well + true : the received message is authenticated + false: if not */ +PHP_FUNCTION(cybermut_testmac) +{ + zval **code_MAC, **version, **TPE, **cdate, **montant, **ref_commande, **texte_libre, **code_retour; + int cdr_test; + + if (ZEND_NUM_ARGS() != 8 || zend_get_parameters_ex(8, &code_MAC, &version, &TPE, &cdate, &montant, &ref_commande, &texte_libre, &code_retour) == FAILURE){ + WRONG_PARAM_COUNT; + } + + convert_to_string_ex(code_MAC); + convert_to_string_ex(version); + convert_to_string_ex(TPE); + convert_to_string_ex(cdate); + convert_to_string_ex(montant); + convert_to_string_ex(ref_commande); + convert_to_string_ex(texte_libre); + convert_to_string_ex(code_retour); + + cdr_test = TestMAC ( Z_STRVAL_PP(code_MAC), Z_STRVAL_PP(version), Z_STRVAL_PP(TPE), Z_STRVAL_PP(cdate), Z_STRVAL_PP(montant), Z_STRVAL_PP(ref_commande), Z_STRVAL_PP(texte_libre), Z_STRVAL_PP(code_retour)); + + if (cdr_test == 1) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } + +} +/* }}} */ + +/* {{{ proto string cybermut_creerreponsecm(string phrase) + Return a string containing the message of acknowledgement of delivery + (headers and body of the message). + This result corresponds to the last parameter "reponse" of the original function + which was removed. */ +PHP_FUNCTION(cybermut_creerreponsecm) +{ + zval **phrase; + char buf[5000]; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &phrase) == FAILURE){ + WRONG_PARAM_COUNT; + } + + convert_to_string_ex(phrase); + + CreerReponseCM( Z_STRVAL_PP(phrase), buf); + + RETURN_STRING (buf,1); +} +/* }}} */ + + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/ext/cybermut/cybermut.xml b/ext/cybermut/cybermut.xml new file mode 100644 index 0000000000..d0f57a231e --- /dev/null +++ b/ext/cybermut/cybermut.xml @@ -0,0 +1,214 @@ + <reference id="ref.cybermut"> + <title>Credit Mutuel CyberMUT functions</title> + <titleabbrev>CyberMUT</titleabbrev> + <partintro> + <simpara> + This extension allows you to process credit cards transactions using Credit + Mutuel CyberMUT system (<ulink url="http://www.creditmutuel.fr/centre_commercial/vendez_sur_internet.html">http://www.creditmutuel.fr/centre_commercial/vendez_sur_internet.html</ulink>). + </simpara> + <simpara> + CynerMUT is a popular Web Payment Service in France, provided by the + Credit Mutuel bank. If you are foreign in France, these functions will not + be useful for you. + </simpara> + <simpara> + These functions are only available if PHP has been compiled with the + <option role="configure">--with-cybermut[=DIR]</option> option, where DIR is + the location of libcm-mac.a and cm-mac.h. You will require the + appropriate SDK for your platform, which may be sent to you after your + CyberMUT's subscription (contact them via Web, or go to the nearest + Credit Mutuel). + </simpara> + <simpara> + The use of these functions is almost identical to the original functions, + except for the parameters of return for CreerFormulaireCM and CreerReponseCM, + which are returned directly by functions PHP, whereas they had passed in + reference in the original functions. + </simpara> + <simpara> + These functions have been added in PHP 4.0.4. ??? + </simpara> + + <note><para> + These functions only provide a link to CyberMUT SDK. Be sure to read the + CynerMUT Developers Guide for full details of the required parameters. + </para></note> + </partintro> + + <refentry id="function.cybermut_creerformulairecm"> + <refnamediv> + <refname>cybermut_creerformulairecm</refname> + <refpurpose>Generate HTML form of request for payment</refpurpose> + </refnamediv> + <refsect1> + <title>Description</title> + <funcsynopsis> + <funcprototype> + <funcdef>string <function>pfpro_init</function></funcdef> + <paramdef>string <parameter>url_CM</parameter></paramdef> + <paramdef>string <parameter>version</parameter></paramdef> + <paramdef>string <parameter>TPE</parameter></paramdef> + <paramdef>string <parameter>montant</parameter></paramdef> + <paramdef>string <parameter>ref_commande</parameter></paramdef> + <paramdef>string <parameter>texte_libre</parameter></paramdef> + <paramdef>string <parameter>url_retour</parameter></paramdef> + <paramdef>string <parameter>url_retour_ok</parameter></paramdef> + <paramdef>string <parameter>url_retour_err</parameter></paramdef> + <paramdef>string <parameter>langue</parameter></paramdef> + <paramdef>string <parameter>code_societe</parameter></paramdef> + <paramdef>string <parameter>texte_bouton</parameter></paramdef> + </funcprototype> + </funcsynopsis> + <para> + <function>cynermut_creerformulairecm</function> is used to generate the + HTML form of request for payment. + </para> + <para> + See also <function>cybermut_testmac</function> + <function>cybermut_creerreponsecm</function>. + </para> + <example> + <title>First step of payment (equiv cgi1.c)</title> + <programlisting role="php"> +<?php +// Directory where are located the keys +putenv("CMKEYDIR=/var/creditmut/cles"); + +// Version number +$VERSION="1.2"; + + $retour = creditmut_creerformulairecm( + "https://www.creditmutuel.fr/test/telepaiement/paiement.cgi", + $VERSION, + "1234567890", + "300FRF", + $REFERENCE, + $TEXTE_LIBRE, + $URL_RETOUR, + $URL_RETOUR_OK, + $URL_RETOUR_ERR, + "francais", + "company", + "Paiement par carte bancaire"); + +echo $retour; +?> + </programlisting> + </example> + </refsect1> + </refentry> + + <refentry id="function.cybermut_testmac"> + <refnamediv> + <refname>cybermut_testmac</refname> + <refpurpose>Make sure that there no was data diddling contained + in the received message of confirmation </refpurpose> + </refnamediv> + <refsect1> + <title>Description</title> + <funcsynopsis> + <funcprototype> + <funcdef>bool <function>cybermut_testmac</function></funcdef> + <paramdef>string <parameter>code_MAC</parameter></paramdef> + <paramdef>string <parameter>version</parameter></paramdef> + <paramdef>string <parameter>TPE</parameter></paramdef> + <paramdef>string <parameter>cdate</parameter></paramdef> + <paramdef>string <parameter>montant</parameter></paramdef> + <paramdef>string <parameter>ref_commande</parameter></paramdef> + <paramdef>string <parameter>texte_libre</parameter></paramdef> + <paramdef>string <parameter>code-retour</parameter></paramdef> + </funcprototype> + </funcsynopsis> + <para> + <function>cybermut_testmac</function> is used to make sure that there was + not data diddling contained in the received message of confirmation. + Pay attention to parameters code-retour and texte-libre, which cannot be + evaluated as is, because auf the dash. You must retrieve them by using: + <programlisting role="php"> +$code_retour=$HTTP_GET_VARS["code-retour"]; +$texte_libre=$HTTP_GET_VARS["texte-libre"]; + </programlisting> + </para> + <para> + See also <function>cybermut_creerformulairecm</function> + <function>cybermut_creerreponsecm</function>. + </para> + <example> + <title>Last step of payment (equiv cgi2.c)</title> + <programlisting role="php"> +<?php_track_vars?> +<?php +// Directory where are located the keys +putenv("CMKEYDIR=/var/creditmut/cles"); + +// Version number +$VERSION="1.2"; + +$texte_libre = $HTTP_GET_VARS["texte-libre"]; +$code_retour = $HTTP_GET_VARS["code-retour"]; + +$mac_ok = creditmut_testmac($MAC,$VERSION,$TPE,$date,$montant,$reference,$texte_libre,$code_retour); + +if ($mac_ok) { + + // + // insert data processing here + // + // + + $result=creditmut_creerreponsecm("OK"); + } else { + $result=creditmut_creerreponsecm("Document Falsifié");} + +?> + </programlisting> + </example> + </refsect1> + </refentry> + + <refentry id="function.cybermut_creerreponsecm"> + <refnamediv> + <refname>cybermut_creerreponsecm</refname> + <refpurpose>Generate the acknowledgement of delivery of the confirmation + of payment</refpurpose> + </refnamediv> + <refsect1> + <title>Description</title> + <funcsynopsis> + <funcprototype> + <funcdef>string <function>cybermut_creerreponsecm</function></funcdef> + <paramdef>string <parameter>phrase</parameter></paramdef> + </funcprototype> + </funcsynopsis> + <para> + Returns: string containing the message of aknowledgement of delivery. + </para> + <para> + The parameter is "OK" if the message of confirmation of the payment + were correctly auhentified by <function>cybermut_testmac</function>. + Any other chain is regarded as an error message. + </para> + <para> + See also <function>cybermut_creerformulairecm</function> + <function>cybermut_testmac</function>. + </para> + </refsect1> + </refentry> + </reference> + +<!-- Keep this comment at the end of the file +Local variables: +mode: sgml +sgml-omittag:t +sgml-shorttag:t +sgml-minimize-attributes:nil +sgml-always-quote-attributes:t +sgml-indent-step:1 +sgml-indent-data:t +sgml-parent-document:nil +sgml-default-dtd-file:"../../manual.ced" +sgml-exposed-tags:nil +sgml-local-catalogs:nil +sgml-local-ecat-files:nil +End: +--> diff --git a/ext/cybermut/libs.mk b/ext/cybermut/libs.mk new file mode 100644 index 0000000000..7c6e176717 --- /dev/null +++ b/ext/cybermut/libs.mk @@ -0,0 +1,7 @@ +include $(top_builddir)/config_vars.mk +LTLIBRARY_OBJECTS = $(LTLIBRARY_SOURCES:.c=.lo) $(LTLIBRARY_OBJECTS_X) +LTLIBRARY_SHARED_OBJECTS = $(LTLIBRARY_OBJECTS:.lo=.slo) +$(LTLIBRARY_NAME): $(LTLIBRARY_OBJECTS) $(LTLIBRARY_DEPENDENCIES) + $(LINK) $(LTLIBRARY_LDFLAGS) $(LTLIBRARY_OBJECTS) $(LTLIBRARY_LIBADD) + +targets = $(LTLIBRARY_NAME) diff --git a/ext/cybermut/php_cybermut.h b/ext/cybermut/php_cybermut.h new file mode 100644 index 0000000000..a4c0755b2c --- /dev/null +++ b/ext/cybermut/php_cybermut.h @@ -0,0 +1,75 @@ +/* + +----------------------------------------------------------------------+ + | 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: Sylvain PAGES <spages@free.fr> | + | | + +----------------------------------------------------------------------+ + */ + +#ifndef PHP_CYBERMUT_H +#define PHP_CYBERMUT_H + +extern zend_module_entry cybermut_module_entry; +#define phpext_cybermut_ptr &cybermut_module_entry + +#ifdef PHP_WIN32 +#define PHP_CYBERMUT_API __declspec(dllexport) +#else +#define PHP_CYBERMUT_API +#endif + +PHP_MINIT_FUNCTION(cybermut); +PHP_MSHUTDOWN_FUNCTION(cybermut); +PHP_RINIT_FUNCTION(cybermut); +PHP_RSHUTDOWN_FUNCTION(cybermut); +PHP_MINFO_FUNCTION(cybermut); + +PHP_FUNCTION(confirm_cybermut_compiled); /* For testing, remove later. */ +PHP_FUNCTION(cybermut_creerformulairecm); +PHP_FUNCTION(cybermut_testmac); +PHP_FUNCTION(cybermut_creerreponsecm); + +/* + Declare any global variables you may need between the BEGIN + and END macros here: + +ZEND_BEGIN_MODULE_GLOBALS(cybermut) + int global_variable; +ZEND_END_MODULE_GLOBALS(cybermut) +*/ + +/* In every function that needs to use variables in php_cybermut_globals, + do call CYBERMUTLS_FETCH(); after declaring other variables used by + that function, and always refer to them as CYBERMUTG(variable). + You are encouraged to rename these macros something shorter, see + examples in any other php module directory. +*/ + +#ifdef ZTS +#define CYBERMUTG(v) (cybermut_globals->v) +#define CYBERMUTLS_FETCH() php_cybermut_globals *cybermut_globals = ts_resource(cybermut_globals_id) +#else +#define CYBERMUTG(v) (cybermut_globals.v) +#define CYBERMUTLS_FETCH() +#endif + +#endif /* PHP_CYBERMUT_H */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/ext/cybermut/tests/001.phpt b/ext/cybermut/tests/001.phpt new file mode 100644 index 0000000000..9fb02fd784 --- /dev/null +++ b/ext/cybermut/tests/001.phpt @@ -0,0 +1,23 @@ +--TEST-- +Check for cybermut presence +--SKIPIF-- +<?php if (!extension_loaded("cybermut")) print "skip"; ?> +--POST-- +--GET-- +--FILE-- +<?php +echo "cybermut extension is available"; +/* + you can add regression tests for your extension here + + the output of your test code has to be equal to the + text in the --EXPECT-- section below for the tests + to pass, differences between the output and the + expected text are interpreted as failure + + see php4/tests/README for further information on + writing regression tests +*/ +?> +--EXPECT-- +cybermut extension is available
\ No newline at end of file |