[xmlsec] GOST support for xmlsec-openssl (patch from Dmitry Belyavsky)



commit a47190407b8df1d32ea866b5697f7e1d9c5b77ec
Author: Aleksey Sanin <aleksey aleksey com>
Date:   Tue Sep 6 15:31:01 2011 -0700

    GOST support for xmlsec-openssl (patch from Dmitry Belyavsky)

 ChangeLog                       |    3 +
 include/xmlsec/openssl/crypto.h |   44 ++++++++++++
 src/openssl/crypto.c            |   13 ++++
 src/openssl/digests.c           |   65 +++++++++++++++++
 src/openssl/evp.c               |  148 +++++++++++++++++++++++++++++++++++++++
 src/openssl/signatures.c        |   70 ++++++++++++++++++
 tests/testDSig.sh               |    6 +-
 7 files changed, 346 insertions(+), 3 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 7941b6b..bec8f27 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,6 @@
+2011-09-06  Aleksey Sanin  <aleksey aleksey com>
+        * GOST support for xmlsec-openssl (patch from Dmitry Belyavsky)
+
 2011-05-11  Aleksey Sanin  <aleksey aleksey com>
         * 1.2.18 release
 
diff --git a/include/xmlsec/openssl/crypto.h b/include/xmlsec/openssl/crypto.h
index 78f907a..8d4687a 100644
--- a/include/xmlsec/openssl/crypto.h
+++ b/include/xmlsec/openssl/crypto.h
@@ -186,6 +186,33 @@ XMLSEC_CRYPTO_EXPORT xmlSecTransformId xmlSecOpenSSLTransformDsaSha1GetKlass(voi
 
 /********************************************************************
  *
+ * GOST2001 transform
+ *
+ *******************************************************************/
+#ifndef XMLSEC_NO_GOST
+
+/**
+ * xmlSecOpenSSLKeyDataGost2001Id:
+ *
+ * The GOST2001 key klass.
+ */
+#define xmlSecOpenSSLKeyDataGost2001Id \
+        xmlSecOpenSSLKeyDataGost2001GetKlass()
+XMLSEC_CRYPTO_EXPORT xmlSecKeyDataId    xmlSecOpenSSLKeyDataGost2001GetKlass   (void);
+
+/**
+ * xmlSecOpenSSLTransformGost2001GostR3411_94Id:
+ *
+ * The GOST2001 GOSTR3411_94 signature transform klass.
+ */
+#define xmlSecOpenSSLTransformGost2001GostR3411_94Id \
+        xmlSecOpenSSLTransformGost2001GostR3411_94GetKlass()
+XMLSEC_CRYPTO_EXPORT xmlSecTransformId xmlSecOpenSSLTransformGost2001GostR3411_94GetKlass(void);
+
+#endif /* XMLSEC_NO_GOST */
+
+/********************************************************************
+ *
  * HMAC transforms
  *
  *******************************************************************/
@@ -523,6 +550,23 @@ XMLSEC_CRYPTO_EXPORT xmlSecTransformId xmlSecOpenSSLTransformSha512GetKlass(void
 #endif /* XMLSEC_NO_SHA512 */
 
 
+/********************************************************************
+ *
+ * GOSTR3411_94 transform
+ *
+ *******************************************************************/
+#ifndef XMLSEC_NO_GOST
+
+/**
+ * xmlSecOpenSSLTransformGostR3411_94Id:
+ *
+ * The GOSTR3411_94 digest transform klass.
+ */
+#define xmlSecOpenSSLTransformGostR3411_94Id \
+        xmlSecOpenSSLTransformGostR3411_94GetKlass()
+XMLSEC_CRYPTO_EXPORT xmlSecTransformId xmlSecOpenSSLTransformGostR3411_94GetKlass(void);
+#endif /* XMLSEC_NO_GOST */
+
 
 
 /**************************************************************
diff --git a/src/openssl/crypto.c b/src/openssl/crypto.c
index aac8d09..d141a5c 100644
--- a/src/openssl/crypto.c
+++ b/src/openssl/crypto.c
@@ -74,6 +74,10 @@ xmlSecCryptoGetFunctions_openssl(void) {
     gXmlSecOpenSSLFunctions->keyDataDsaGetKlass         = xmlSecOpenSSLKeyDataDsaGetKlass;
 #endif /* XMLSEC_NO_DSA */
 
+#ifndef XMLSEC_NO_GOST
+    gXmlSecOpenSSLFunctions->keyDataGost2001GetKlass           = xmlSecOpenSSLKeyDataGost2001GetKlass;
+#endif /* XMLSEC_NO_GOST*/
+
 #ifndef XMLSEC_NO_HMAC
     gXmlSecOpenSSLFunctions->keyDataHmacGetKlass        = xmlSecOpenSSLKeyDataHmacGetKlass;
 #endif /* XMLSEC_NO_HMAC */
@@ -127,6 +131,15 @@ xmlSecCryptoGetFunctions_openssl(void) {
 
 #endif /* XMLSEC_NO_DSA */
 
+    /******************************* GOST ********************************/
+#ifndef XMLSEC_NO_GOST
+    gXmlSecOpenSSLFunctions->transformGost2001GostR3411_94GetKlass             = xmlSecOpenSSLTransformGost2001GostR3411_94GetKlass;
+#endif /* XMLSEC_NO_GOST */
+
+#ifndef XMLSEC_NO_GOST
+    gXmlSecOpenSSLFunctions->transformGostR3411_94GetKlass             = xmlSecOpenSSLTransformGostR3411_94GetKlass;
+#endif /* XMLSEC_NO_GOST */
+
     /******************************* HMAC ********************************/
 #ifndef XMLSEC_NO_HMAC
 
diff --git a/src/openssl/digests.c b/src/openssl/digests.c
index 2dc3236..fa26fa6 100644
--- a/src/openssl/digests.c
+++ b/src/openssl/digests.c
@@ -102,6 +102,12 @@ xmlSecOpenSSLEvpDigestCheckId(xmlSecTransformPtr transform) {
     } else
 #endif /* XMLSEC_NO_SHA512 */
 
+#ifndef XMLSEC_NO_GOST
+    if(xmlSecTransformCheckId(transform, xmlSecOpenSSLTransformGostR3411_94Id)) {
+        return(1);
+    } else
+#endif /* XMLSEC_NO_GOST*/
+
 
     {
         return(0);
@@ -165,6 +171,21 @@ xmlSecOpenSSLEvpDigestInitialize(xmlSecTransformPtr transform) {
     } else
 #endif /* XMLSEC_NO_SHA512 */
 
+#ifndef XMLSEC_NO_GOST
+    if(xmlSecTransformCheckId(transform, xmlSecOpenSSLTransformGostR3411_94Id)) {
+        ctx->digest = EVP_get_digestbyname("md_gost94");
+				if (!ctx->digest)
+				{
+        xmlSecError(XMLSEC_ERRORS_HERE,
+                    xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
+                    NULL,
+                    XMLSEC_ERRORS_R_INVALID_TRANSFORM,
+                    XMLSEC_ERRORS_NO_MESSAGE);
+        return(-1);
+				}
+    } else
+#endif /* XMLSEC_NO_GOST*/
+
     {
         xmlSecError(XMLSEC_ERRORS_HERE,
                     xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
@@ -680,3 +701,47 @@ xmlSecOpenSSLTransformSha512GetKlass(void) {
 }
 #endif /* XMLSEC_NO_SHA512 */
 
+#ifndef XMLSEC_NO_GOST
+/******************************************************************************
+ *
+ * GOSTR3411_94
+ *
+ *****************************************************************************/
+static xmlSecTransformKlass xmlSecOpenSSLGostR3411_94Klass = {
+    /* klass/object sizes */
+    sizeof(xmlSecTransformKlass),               /* size_t klassSize */
+    xmlSecOpenSSLEvpDigestSize,                   /* size_t objSize */
+
+    xmlSecNameGostR3411_94,                             /* const xmlChar* name; */
+    xmlSecHrefGostR3411_94,                             /* const xmlChar* href; */
+    xmlSecTransformUsageDigestMethod,           /* xmlSecTransformUsage usage; */
+    xmlSecOpenSSLEvpDigestInitialize,             /* xmlSecTransformInitializeMethod initialize; */
+    xmlSecOpenSSLEvpDigestFinalize,               /* xmlSecTransformFinalizeMethod finalize; */
+    NULL,                                       /* xmlSecTransformNodeReadMethod readNode; */
+    NULL,                                       /* xmlSecTransformNodeWriteMethod writeNode; */
+    NULL,                                       /* xmlSecTransformSetKeyReqMethod setKeyReq; */
+    NULL,                                       /* xmlSecTransformSetKeyMethod setKey; */
+    xmlSecOpenSSLEvpDigestVerify,                 /* xmlSecTransformVerifyMethod verify; */
+    xmlSecTransformDefaultGetDataType,          /* xmlSecTransformGetDataTypeMethod getDataType; */
+    xmlSecTransformDefaultPushBin,              /* xmlSecTransformPushBinMethod pushBin; */
+    xmlSecTransformDefaultPopBin,               /* xmlSecTransformPopBinMethod popBin; */
+    NULL,                                       /* xmlSecTransformPushXmlMethod pushXml; */
+    NULL,                                       /* xmlSecTransformPopXmlMethod popXml; */
+    xmlSecOpenSSLEvpDigestExecute,                /* xmlSecTransformExecuteMethod execute; */
+    NULL,                                       /* void* reserved0; */
+    NULL,                                       /* void* reserved1; */
+};
+
+/**
+ * xmlSecOpenSSLTransformGostR3411_94GetKlass:
+ *
+ * GOSTR3411_94 digest transform klass.
+ *
+ * Returns: pointer to GOSTR3411_94 digest transform klass.
+ */
+xmlSecTransformId
+xmlSecOpenSSLTransformGostR3411_94GetKlass(void) {
+    return(&xmlSecOpenSSLGostR3411_94Klass);
+}
+#endif /* XMLSEC_NO_GOST*/
+
diff --git a/src/openssl/evp.c b/src/openssl/evp.c
index e48defd..ab98c6c 100644
--- a/src/openssl/evp.c
+++ b/src/openssl/evp.c
@@ -237,6 +237,19 @@ xmlSecOpenSSLEvpKeyAdopt(EVP_PKEY *pKey) {
         }
         break;
 #endif /* XMLSEC_NO_DSA */
+#ifndef XMLSEC_NO_GOST
+    case NID_id_GostR3410_2001:
+        data = xmlSecKeyDataCreate(xmlSecOpenSSLKeyDataGost2001Id);
+        if(data == NULL) {
+            xmlSecError(XMLSEC_ERRORS_HERE,
+                        NULL,
+                        "xmlSecKeyDataCreate",
+                        XMLSEC_ERRORS_R_XMLSEC_FAILED,
+                        "xmlSecOpenSSLKeyDataGost2001Id");
+            return(NULL);
+        }
+        break;
+#endif /* XMLSEC_NO_GOST */
     default:
         xmlSecError(XMLSEC_ERRORS_HERE,
                     NULL,
@@ -1556,4 +1569,139 @@ xmlSecOpenSSLKeyDataRsaDebugXmlDump(xmlSecKeyDataPtr data, FILE* output) {
 #endif /* XMLSEC_NO_RSA */
 
 
+#ifndef XMLSEC_NO_GOST
+/**************************************************************************
+ *
+ * GOST2001 xml key representation processing. Contain errors.
+ *
+ *************************************************************************/
+static int              xmlSecOpenSSLKeyDataGost2001Initialize(xmlSecKeyDataPtr data);
+static int              xmlSecOpenSSLKeyDataGost2001Duplicate(xmlSecKeyDataPtr dst,
+                                                         xmlSecKeyDataPtr src);
+static void             xmlSecOpenSSLKeyDataGost2001Finalize(xmlSecKeyDataPtr data);
+static int              xmlSecOpenSSLKeyDataGost2001XmlRead    (xmlSecKeyDataId id,
+                                                         xmlSecKeyPtr key,
+                                                         xmlNodePtr node,
+                                                         xmlSecKeyInfoCtxPtr keyInfoCtx);
+static int              xmlSecOpenSSLKeyDataGost2001XmlWrite(xmlSecKeyDataId id,
+                                                         xmlSecKeyPtr key,
+                                                         xmlNodePtr node,
+                                                         xmlSecKeyInfoCtxPtr keyInfoCtx);
+static int              xmlSecOpenSSLKeyDataGost2001Generate(xmlSecKeyDataPtr data,
+                                                         xmlSecSize sizeBits,
+                                                         xmlSecKeyDataType type);
+
+static xmlSecKeyDataType xmlSecOpenSSLKeyDataGost2001GetType(xmlSecKeyDataPtr data);
+static xmlSecSize        xmlSecOpenSSLKeyDataGost2001GetSize(xmlSecKeyDataPtr data);
+static void              xmlSecOpenSSLKeyDataGost2001DebugDump(xmlSecKeyDataPtr data,
+                                                         FILE* output);
+static void             xmlSecOpenSSLKeyDataGost2001DebugXmlDump(xmlSecKeyDataPtr data,
+                                                         FILE* output);
+
+static xmlSecKeyDataKlass xmlSecOpenSSLKeyDataGost2001Klass = {
+    sizeof(xmlSecKeyDataKlass),
+    xmlSecOpenSSLEvpKeyDataSize,
+
+    /* data */
+    xmlSecNameGOST2001KeyValue,
+    xmlSecKeyDataUsageKeyValueNode | xmlSecKeyDataUsageRetrievalMethodNodeXml,
+                                        /* xmlSecKeyDataUsage usage; */
+    xmlSecHrefGOST2001KeyValue,         /* const xmlChar* href; */
+    xmlSecNodeGOST2001KeyValue,         /* const xmlChar* dataNodeName; */
+    xmlSecDSigNs,                       /* const xmlChar* dataNodeNs; */
+
+    /* constructors/destructor */
+    xmlSecOpenSSLKeyDataGost2001Initialize,    /* xmlSecKeyDataInitializeMethod initialize; */
+    xmlSecOpenSSLKeyDataGost2001Duplicate,     /* xmlSecKeyDataDuplicateMethod duplicate; */
+    xmlSecOpenSSLKeyDataGost2001Finalize,      /* xmlSecKeyDataFinalizeMethod finalize; */
+    NULL, /* xmlSecOpenSSLKeyDataGost2001Generate,*/   /* xmlSecKeyDataGenerateMethod generate; */
+
+    /* get info */
+    xmlSecOpenSSLKeyDataGost2001GetType,       /* xmlSecKeyDataGetTypeMethod getType; */
+    xmlSecOpenSSLKeyDataGost2001GetSize,       /* xmlSecKeyDataGetSizeMethod getSize; */
+    NULL,                               /* xmlSecKeyDataGetIdentifier getIdentifier; */
+
+    /* read/write */
+    NULL,       /* xmlSecKeyDataXmlReadMethod xmlRead; */
+    NULL,       /* xmlSecKeyDataXmlWriteMethod xmlWrite; */
+    NULL,                               /* xmlSecKeyDataBinReadMethod binRead; */
+    NULL,                               /* xmlSecKeyDataBinWriteMethod binWrite; */
+
+    /* debug */
+    xmlSecOpenSSLKeyDataGost2001DebugDump,     /* xmlSecKeyDataDebugDumpMethod debugDump; */
+    xmlSecOpenSSLKeyDataGost2001DebugXmlDump,/* xmlSecKeyDataDebugDumpMethod debugXmlDump; */
+
+    /* reserved for the future */
+    NULL,                               /* void* reserved0; */
+    NULL,                               /* void* reserved1; */
+};
+
+/**
+ * xmlSecOpenSSLKeyDataGost2001GetKlass:
+ *
+ * The GOST2001 key data klass.
+ *
+ * Returns: pointer to GOST2001 key data klass.
+ */
+xmlSecKeyDataId
+xmlSecOpenSSLKeyDataGost2001GetKlass(void) {
+    return(&xmlSecOpenSSLKeyDataGost2001Klass);
+}
+
+
+static int
+xmlSecOpenSSLKeyDataGost2001Initialize(xmlSecKeyDataPtr data) {
+    xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecOpenSSLKeyDataGost2001Id), -1);
+
+    return(xmlSecOpenSSLEvpKeyDataInitialize(data));
+}
+
+static int
+xmlSecOpenSSLKeyDataGost2001Duplicate(xmlSecKeyDataPtr dst, xmlSecKeyDataPtr src) {
+    xmlSecAssert2(xmlSecKeyDataCheckId(dst, xmlSecOpenSSLKeyDataGost2001Id), -1);
+    xmlSecAssert2(xmlSecKeyDataCheckId(src, xmlSecOpenSSLKeyDataGost2001Id), -1);
+
+    return(xmlSecOpenSSLEvpKeyDataDuplicate(dst, src));
+}
+
+static void
+xmlSecOpenSSLKeyDataGost2001Finalize(xmlSecKeyDataPtr data) {
+    xmlSecAssert(xmlSecKeyDataCheckId(data, xmlSecOpenSSLKeyDataGost2001Id));
+
+    xmlSecOpenSSLEvpKeyDataFinalize(data);
+}
+
+static xmlSecKeyDataType
+xmlSecOpenSSLKeyDataGost2001GetType(xmlSecKeyDataPtr data) {
+	/* Now I don't know how to find whether we have both private and public key 
+	or the public only*/
+	return(xmlSecKeyDataTypePublic | xmlSecKeyDataTypePrivate);
+}
+
+static xmlSecSize
+xmlSecOpenSSLKeyDataGost2001GetSize(xmlSecKeyDataPtr data) {
+    xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecOpenSSLKeyDataGost2001Id), 0);
+
+    return xmlSecOpenSSLKeyDataGetSize(data);
+}
+
+static void
+xmlSecOpenSSLKeyDataGost2001DebugDump(xmlSecKeyDataPtr data, FILE* output) {
+    xmlSecAssert(xmlSecKeyDataCheckId(data, xmlSecOpenSSLKeyDataGost2001Id));
+    xmlSecAssert(output != NULL);
+
+    fprintf(output, "=== gost key: size = %d\n",
+            xmlSecOpenSSLKeyDataGost2001GetSize(data));
+}
+
+static void
+xmlSecOpenSSLKeyDataGost2001DebugXmlDump(xmlSecKeyDataPtr data, FILE* output) {
+    xmlSecAssert(xmlSecKeyDataCheckId(data, xmlSecOpenSSLKeyDataGost2001Id));
+    xmlSecAssert(output != NULL);
+
+    fprintf(output, "<GOST2001KeyValue size=\"%d\" />\n",
+            xmlSecOpenSSLKeyDataGost2001GetSize(data));
+}
+
+#endif /* XMLSEC_NO_GOST*/
 
diff --git a/src/openssl/signatures.c b/src/openssl/signatures.c
index 38f42b3..1fc4ff3 100644
--- a/src/openssl/signatures.c
+++ b/src/openssl/signatures.c
@@ -127,6 +127,12 @@ xmlSecOpenSSLEvpSignatureCheckId(xmlSecTransformPtr transform) {
 
 #endif /* XMLSEC_NO_RSA */
 
+#ifndef XMLSEC_NO_GOST
+    if(xmlSecTransformCheckId(transform, xmlSecOpenSSLTransformGost2001GostR3411_94Id)) {
+        return(1);
+    } else
+#endif /* XMLSEC_NO_GOST*/
+
     {
         return(0);
     }
@@ -210,6 +216,22 @@ xmlSecOpenSSLEvpSignatureInitialize(xmlSecTransformPtr transform) {
 
 #endif /* XMLSEC_NO_RSA */
 
+#ifndef XMLSEC_NO_GOST
+    if(xmlSecTransformCheckId(transform, xmlSecOpenSSLTransformGost2001GostR3411_94Id)) {
+        ctx->keyId          = xmlSecOpenSSLKeyDataGost2001Id;
+        ctx->digest = EVP_get_digestbyname("md_gost94");
+				if (!ctx->digest)
+				{
+        xmlSecError(XMLSEC_ERRORS_HERE,
+                    xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
+                    NULL,
+                    XMLSEC_ERRORS_R_INVALID_TRANSFORM,
+                    XMLSEC_ERRORS_NO_MESSAGE);
+        return(-1);
+				}
+    } else
+#endif /* XMLSEC_NO_GOST*/
+
     if(1) {
         xmlSecError(XMLSEC_ERRORS_HERE,
                     xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
@@ -1062,4 +1084,52 @@ xmlSecOpenSSLTransformRsaSha512GetKlass(void) {
 #endif /* XMLSEC_NO_RSA */
 
 
+#ifndef XMLSEC_NO_GOST
+/****************************************************************************
+ *
+ * GOST2001-GOSTR3411_94 signature transform
+ *
+ ***************************************************************************/
+
+static xmlSecTransformKlass xmlSecOpenSSLGost2001GostR3411_94Klass = {
+    /* klass/object sizes */
+    sizeof(xmlSecTransformKlass),               /* xmlSecSize klassSize */
+    xmlSecOpenSSLEvpSignatureSize,                /* xmlSecSize objSize */
+
+    xmlSecNameGost2001GostR3411_94,                             /* const xmlChar* name; */
+    xmlSecHrefGost2001GostR3411_94,                             /* const xmlChar* href; */
+    xmlSecTransformUsageSignatureMethod,        /* xmlSecTransformUsage usage; */
+
+    xmlSecOpenSSLEvpSignatureInitialize,          /* xmlSecTransformInitializeMethod initialize; */
+    xmlSecOpenSSLEvpSignatureFinalize,            /* xmlSecTransformFinalizeMethod finalize; */
+    NULL,                                       /* xmlSecTransformNodeReadMethod readNode; */
+    NULL,                                       /* xmlSecTransformNodeWriteMethod writeNode; */
+    xmlSecOpenSSLEvpSignatureSetKeyReq,           /* xmlSecTransformSetKeyReqMethod setKeyReq; */
+    xmlSecOpenSSLEvpSignatureSetKey,              /* xmlSecTransformSetKeyMethod setKey; */
+    xmlSecOpenSSLEvpSignatureVerify,              /* xmlSecTransformVerifyMethod verify; */
+    xmlSecTransformDefaultGetDataType,          /* xmlSecTransformGetDataTypeMethod getDataType; */
+    xmlSecTransformDefaultPushBin,              /* xmlSecTransformPushBinMethod pushBin; */
+    xmlSecTransformDefaultPopBin,               /* xmlSecTransformPopBinMethod popBin; */
+    NULL,                                       /* xmlSecTransformPushXmlMethod pushXml; */
+    NULL,                                       /* xmlSecTransformPopXmlMethod popXml; */
+    xmlSecOpenSSLEvpSignatureExecute,             /* xmlSecTransformExecuteMethod execute; */
+
+    NULL,                                       /* void* reserved0; */
+    NULL,                                       /* void* reserved1; */
+};
+
+/**
+ * xmlSecOpenSSLTransformGost2001GostR3411_94GetKlass:
+ *
+ * The GOST2001-GOSTR3411_94 signature transform klass.
+ *
+ * Returns: GOST2001-GOSTR3411_94 signature transform klass.
+ */
+xmlSecTransformId
+xmlSecOpenSSLTransformGost2001GostR3411_94GetKlass(void) {
+    return(&xmlSecOpenSSLGost2001GostR3411_94Klass);
+}
+
+#endif /* XMLSEC_NO_GOST*/
+
 
diff --git a/tests/testDSig.sh b/tests/testDSig.sh
index 2ef9bc7..b8d85aa 100755
--- a/tests/testDSig.sh
+++ b/tests/testDSig.sh
@@ -806,9 +806,9 @@ echo "--------- These tests CAN FAIL (extra OS config required) ----------"
 execDSigTest $res_success \
     "" \
     "aleksey-xmldsig-01/enveloped-gost" \
-    "enveloped-signature gostr34102001-gostr3411 gostr3411" \
-    "gost" \
-    "--trusted-$cert_format $topfolder/keys/gost2001ca.$cert_format --untrusted-$cert_format $topfolder/keys/ca2cert.$cert_format  --enabled-key-data x509" \
+    "enveloped-signature gostr3411" \
+    "gost2001" \
+    "--trusted-$cert_format $topfolder/keys/gost2001ca.$cert_format --untrusted-$cert_format $topfolder/keys/ca2cert.$cert_format  --enabled-key-data x509 --verification-time 2007-01-01+10:00:00" \
     "" \
     ""
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]