gnome-keyring r1230 - in trunk: . daemon/pk daemon/pkcs11 daemon/pkix daemon/pkix/tests



Author: nnielsen
Date: Sun Aug 10 15:43:48 2008
New Revision: 1230
URL: http://svn.gnome.org/viewvc/gnome-keyring?rev=1230&view=rev

Log:
	* daemon/pk/gkr-pk-cert.c:
	* daemon/pk/gkr-pk-import.c:
	* daemon/pk/gkr-pk-netscape-trust.c:
	* daemon/pk/gkr-pk-object-storage.c:
	* daemon/pk/gkr-pk-root-storage.c:
	* daemon/pkcs11/gkr-pkcs11-daemon-session.c:
	* daemon/pkix/gkr-pkix-parser.c:
	* daemon/pkix/gkr-pkix-parser.h:
	* daemon/pkix/gkr-pkix-types.h: 
	* daemon/pkix/tests/unit-test-pkix-parser.c:
	* daemon/pkix/tests/unit-test-pkix-serialize.c: Fix problems where
	the parser is used interactively, and continues to prompt the user
	after they've cancelled the operation.


Modified:
   trunk/ChangeLog
   trunk/daemon/pk/gkr-pk-cert.c
   trunk/daemon/pk/gkr-pk-import.c
   trunk/daemon/pk/gkr-pk-netscape-trust.c
   trunk/daemon/pk/gkr-pk-object-storage.c
   trunk/daemon/pk/gkr-pk-root-storage.c
   trunk/daemon/pkcs11/gkr-pkcs11-daemon-session.c
   trunk/daemon/pkix/gkr-pkix-parser.c
   trunk/daemon/pkix/gkr-pkix-parser.h
   trunk/daemon/pkix/gkr-pkix-types.h
   trunk/daemon/pkix/tests/unit-test-pkix-parser.c
   trunk/daemon/pkix/tests/unit-test-pkix-serialize.c

Modified: trunk/daemon/pk/gkr-pk-cert.c
==============================================================================
--- trunk/daemon/pk/gkr-pk-cert.c	(original)
+++ trunk/daemon/pk/gkr-pk-cert.c	Sun Aug 10 15:43:48 2008
@@ -165,7 +165,8 @@
 	g_free (data);
 	
 	if (res != GKR_PKIX_SUCCESS) {
-		g_warning ("invalid public-key in certificate: %s", g_quark_to_string (obj->location));
+		if (res != GKR_PKIX_CANCELLED)
+			g_warning ("invalid public-key in certificate: %s", g_quark_to_string (obj->location));
 		return NULL;
 	}
 	
@@ -266,7 +267,8 @@
 		g_free (extension);
 	
 		if (res != GKR_PKIX_SUCCESS) {
-			g_warning ("invalid enhanced usage in certificate");
+			if (res != GKR_PKIX_CANCELLED)
+				g_warning ("invalid enhanced usage in certificate");
 			return CKR_GENERAL_ERROR;
 		}
 	}
@@ -475,6 +477,8 @@
 
 			res = gkr_pkix_der_read_basic_constraints (data, n_data, &is_ca, NULL);
 			g_free (data);
+			if (res == GKR_PKIX_CANCELLED)
+				return CKR_FUNCTION_CANCELED;
 			if (res != GKR_PKIX_SUCCESS)
 				return CKR_FUNCTION_FAILED;
 			if (is_ca)

Modified: trunk/daemon/pk/gkr-pk-import.c
==============================================================================
--- trunk/daemon/pk/gkr-pk-import.c	(original)
+++ trunk/daemon/pk/gkr-pk-import.c	Sun Aug 10 15:43:48 2008
@@ -144,7 +144,7 @@
 {
 	GkrAskRequest *ask;
 	gchar *secondary;
-	gboolean ret;
+	gboolean ret = TRUE;
 	GkrPkIndex *index;
 	guint flags;
 	
@@ -168,7 +168,7 @@
 		return TRUE;
 	}
 	
-	if (!label) 
+	if (!label || !label[0]) 
 		label = import->import_label;
 		
 	/* Build up the prompt */
@@ -245,14 +245,14 @@
 	if (!gkr_pk_object_has_label (object) && import->import_label)
 		g_object_set (object, "label", import->import_label, NULL);
 	
+	import->import_objects = g_slist_prepend (import->import_objects, object);
+	g_object_weak_ref (G_OBJECT (object), object_disappeared, import);
+
 	if (created) {
 		gkr_pk_storage_store (import->import_storage, object, &pv->error);
 		g_object_unref (object);
 	}
 	
-	import->import_objects = g_slist_prepend (import->import_objects, object);
-	g_object_weak_ref (G_OBJECT (object), object_disappeared, import);
-	
 	return TRUE;
 }
 
@@ -525,10 +525,14 @@
 		g_object_unref (*object);
 		*object = NULL;
 
-		if (err->domain == GKR_PKIX_PARSE_ERROR)
-			ret = CKR_DATA_INVALID;
-		else
+		if (err->domain == GKR_PKIX_PARSE_ERROR) {
+			if (err->code == GKR_PKIX_CANCELLED)
+				ret = CKR_FUNCTION_CANCELED;
+			else
+				ret = CKR_DATA_INVALID;
+		} else {
 			ret = CKR_FUNCTION_FAILED;
+		}
 
 		g_message ("couldn't import data: %s", err && err->message ? err->message : "");
 		g_clear_error (&err);
@@ -559,7 +563,7 @@
 	
  	g_return_val_if_fail (GKR_IS_PK_IMPORT (import), FALSE);
  	
-	parser = gkr_pkix_parser_new ();
+	parser = gkr_pkix_parser_new (TRUE);
 	g_signal_connect (parser, "parsed-asn1", G_CALLBACK (parser_parsed_asn1), import);
 	g_signal_connect (parser, "parsed-sexp", G_CALLBACK (parser_parsed_sexp), import);
  	g_signal_connect (parser, "ask-password", G_CALLBACK (parser_ask_password), import);

Modified: trunk/daemon/pk/gkr-pk-netscape-trust.c
==============================================================================
--- trunk/daemon/pk/gkr-pk-netscape-trust.c	(original)
+++ trunk/daemon/pk/gkr-pk-netscape-trust.c	Sun Aug 10 15:43:48 2008
@@ -101,6 +101,8 @@
 	res = gkr_pkix_der_read_key_usage (extension, n_extension, &usage);
 	g_free (extension);
 	
+	if (res != GKR_PKIX_CANCELLED)
+		return CKR_FUNCTION_CANCELED;
 	if (res != GKR_PKIX_SUCCESS) {
 		g_warning ("invalid key usage in certificate");
 		return CKR_GENERAL_ERROR;

Modified: trunk/daemon/pk/gkr-pk-object-storage.c
==============================================================================
--- trunk/daemon/pk/gkr-pk-object-storage.c	(original)
+++ trunk/daemon/pk/gkr-pk-object-storage.c	Sun Aug 10 15:43:48 2008
@@ -244,7 +244,7 @@
 	ctx.checks = gkr_pk_storage_checks_prepare (GKR_PK_STORAGE (storage), loc);
 
 	/* TODO: Try and use a shared parser? */
-	parser = gkr_pkix_parser_new ();
+	parser = gkr_pkix_parser_new (FALSE);
 	g_signal_connect (parser, "parsed-asn1", G_CALLBACK (parser_parsed_asn1), &ctx);
 	g_signal_connect (parser, "parsed-sexp", G_CALLBACK (parser_parsed_sexp), &ctx);
 	g_signal_connect (parser, "parsed-partial", G_CALLBACK (parser_parsed_partial), &ctx);
@@ -367,11 +367,11 @@
 	if (gtype == GKR_TYPE_PK_PRIVKEY) {
 		type = GKR_PKIX_PRIVATE_KEY;
 		g_object_get (obj, "gcrypt-sexp", &what, NULL);
-	} else if (gtype == GKR_PKIX_PUBLIC_KEY) {
-		type = GKR_TYPE_PK_PUBKEY;
+	} else if (gtype == GKR_TYPE_PK_PUBKEY) {
+		type = GKR_PKIX_PUBLIC_KEY;
 		g_object_get (obj, "gcrypt-sexp", &what, NULL);
-	} else if (gtype == GKR_PKIX_CERTIFICATE) {
-		type = GKR_TYPE_PK_CERT;
+	} else if (gtype == GKR_TYPE_PK_CERT) {
+		type = GKR_PKIX_CERTIFICATE;
 		g_object_get (obj, "asn1-tree", &what, NULL);
 	} else {
 		g_return_val_if_reached (FALSE);
@@ -513,7 +513,7 @@
 	GkrPkStorage *storage;
 	
 	storage = g_object_new (GKR_TYPE_PK_OBJECT_STORAGE, NULL);
-	gkr_pk_storage_register (storage, FALSE);
+	gkr_pk_storage_register (storage, TRUE);
 	g_object_unref (storage);
 	
 	return TRUE;

Modified: trunk/daemon/pk/gkr-pk-root-storage.c
==============================================================================
--- trunk/daemon/pk/gkr-pk-root-storage.c	(original)
+++ trunk/daemon/pk/gkr-pk-root-storage.c	Sun Aug 10 15:43:48 2008
@@ -141,7 +141,7 @@
 	ctx.checks = gkr_pk_storage_checks_prepare (GKR_PK_STORAGE (storage), loc);
 
 	/* TODO: Try and use a shared parser? */
-	parser = gkr_pkix_parser_new ();
+	parser = gkr_pkix_parser_new (FALSE);
 	g_signal_connect (parser, "parsed-asn1", G_CALLBACK (parser_parsed_asn1), &ctx);
 	ret = gkr_pkix_parser_parse_location (parser, loc, err);
 	g_object_unref (parser);

Modified: trunk/daemon/pkcs11/gkr-pkcs11-daemon-session.c
==============================================================================
--- trunk/daemon/pkcs11/gkr-pkcs11-daemon-session.c	(original)
+++ trunk/daemon/pkcs11/gkr-pkcs11-daemon-session.c	Sun Aug 10 15:43:48 2008
@@ -421,7 +421,7 @@
                         GkrPkcs11Message *resp)
 {
 	CK_OBJECT_CLASS cls;
-	GkrPkObject *object;
+	GkrPkObject *object = NULL;
 	GArray *attrs = NULL;
 	CK_BBOOL token;
 	CK_RV ret;
@@ -466,7 +466,8 @@
 	ret = CKR_OK;
 	
 done:
-	g_object_unref (object);
+	if (object)
+		g_object_unref (object);
 	gkr_pk_attributes_free (attrs);
 	return ret;
 }

Modified: trunk/daemon/pkix/gkr-pkix-parser.c
==============================================================================
--- trunk/daemon/pkix/gkr-pkix-parser.c	(original)
+++ trunk/daemon/pkix/gkr-pkix-parser.c	Sun Aug 10 15:43:48 2008
@@ -122,6 +122,11 @@
 } GkrPkixParserPrivate;
 
 enum {
+	PROP_0,
+	PROP_INTERACTIVE
+};
+
+enum {
 	PARSED_PARTIAL,
 	PARSED_SEXP,
 	PARSED_ASN1,
@@ -218,19 +223,22 @@
 	return FALSE;
 }
 
-static void
+static GkrPkixResult
 fire_parsed_partial (GkrPkixParser *parser, GQuark location, 
                      gkrconstid digest, GQuark type)
 {
 	gboolean owned = FALSE;
 	
-	g_assert (location);
+	g_assert (digest);
 	
-	if (!gkr_async_is_stopping ())
-		g_signal_emit (parser, signals[PARSED_PARTIAL], 0, location, digest, type, &owned);
+	if (gkr_async_is_stopping ())
+		return GKR_PKIX_CANCELLED;
+	
+	g_signal_emit (parser, signals[PARSED_PARTIAL], 0, location, digest, type, &owned);	
+	return GKR_PKIX_SUCCESS;
 }
 
-static void
+static GkrPkixResult
 fire_parsed_sexp (GkrPkixParser *parser, GQuark location, gkrconstid digest, 
                   GQuark type, gcry_sexp_t sexp)
 {
@@ -238,14 +246,19 @@
 	
 	g_assert (sexp);
 	g_assert (type);
+	g_assert (digest);
+
+	if (gkr_async_is_stopping ())
+		return GKR_PKIX_CANCELLED;
 	
-	if (!gkr_async_is_stopping ())
-		g_signal_emit (parser, signals[PARSED_SEXP], 0, location, digest, type, sexp, &owned);
+	g_signal_emit (parser, signals[PARSED_SEXP], 0, location, digest, type, sexp, &owned);
 	if (!owned)
 		gcry_sexp_release (sexp);
+	
+	return GKR_PKIX_SUCCESS;	
 }
 
-static void
+static GkrPkixResult
 fire_parsed_asn1 (GkrPkixParser *parser, GQuark location, gkrconstid digest, 
                   GQuark type, ASN1_TYPE asn1)
 {
@@ -253,11 +266,16 @@
 	
 	g_assert (asn1);
 	g_assert (type);
-	
-	if (!gkr_async_is_stopping ())
-		g_signal_emit (parser, signals[PARSED_ASN1], 0, location, digest, type, asn1, &owned);
+	g_assert (digest);
+
+	if (gkr_async_is_stopping ())
+		return GKR_PKIX_CANCELLED;
+
+	g_signal_emit (parser, signals[PARSED_ASN1], 0, location, digest, type, asn1, &owned);
 	if (!owned)
 		asn1_delete_structure (&asn1);
+	
+	return GKR_PKIX_SUCCESS;
 }
 
 /* -----------------------------------------------------------------------------
@@ -271,6 +289,32 @@
 	pv->seen_passwords = NULL;	
 }
 
+static void
+gkr_pkix_parser_get_property (GObject *obj, guint prop_id, GValue *value, 
+                              GParamSpec *pspec)
+{
+	GkrPkixParser *parser = GKR_PKIX_PARSER (obj);
+
+	switch (prop_id) {
+	case PROP_INTERACTIVE:
+		g_value_set_boolean (value, parser->interactive);
+		break;
+	}
+}
+
+static void
+gkr_pkix_parser_set_property (GObject *obj, guint prop_id, const GValue *value, 
+                              GParamSpec *pspec)
+{
+	GkrPkixParser *parser = GKR_PKIX_PARSER (obj);
+	
+	switch (prop_id) {
+	case PROP_INTERACTIVE:
+		parser->interactive = g_value_get_boolean (value);
+		break;
+	};
+}
+
 static gboolean 
 gkr_pkix_parser_parsed_partial (GkrPkixParser *parser, GQuark loc, gkrconstid digest, 
                                 GQuark type)
@@ -333,6 +377,12 @@
 	klass->ask_password = gkr_pkix_parser_ask_password;
 	
 	gobject_class->finalize = gkr_pkix_parser_finalize;
+	gobject_class->get_property = gkr_pkix_parser_get_property;
+	gobject_class->set_property = gkr_pkix_parser_set_property;
+
+	g_object_class_install_property (gobject_class, PROP_INTERACTIVE,
+		g_param_spec_boolean ("interactive", "Interactive", "Is this parser acting upon user actions directly",
+		                      FALSE, G_PARAM_READWRITE));
 
 	signals[PARSED_PARTIAL] = g_signal_new ("parsed-partial", GKR_TYPE_PKIX_PARSER, 
 			G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GkrPkixParserClass, parsed_partial),
@@ -362,9 +412,9 @@
  */
 
 GkrPkixParser*
-gkr_pkix_parser_new (void)
+gkr_pkix_parser_new (gboolean interactive)
 {
-	return g_object_new (GKR_TYPE_PKIX_PARSER, NULL);
+	return g_object_new (GKR_TYPE_PKIX_PARSER, "interactive", interactive, NULL);
 }
 
 GQuark
@@ -398,6 +448,10 @@
 		return TRUE;
 		
 	switch (ret) {
+	case GKR_PKIX_CANCELLED:
+		g_set_error (err, GKR_PKIX_PARSE_ERROR, ret, "%s",
+		             _("The operation was cancelled"));
+		return FALSE;
 	case GKR_PKIX_UNRECOGNIZED:
 		g_set_error (err, GKR_PKIX_PARSE_ERROR, ret, "%s",
 		             _("Unrecognized or unsupported file."));
@@ -509,7 +563,7 @@
 	if (ret == GKR_PKIX_UNRECOGNIZED)
 		ret = gkr_pkix_der_read_private_key_dsa (data, n_data, &s_key);
 	if (ret == GKR_PKIX_SUCCESS)
-		fire_parsed_sexp (parser, loc, digest, GKR_PKIX_PRIVATE_KEY, s_key);
+		ret = fire_parsed_sexp (parser, loc, digest, GKR_PKIX_PRIVATE_KEY, s_key);
 		
 	gkr_id_free (digest);
 	
@@ -584,7 +638,7 @@
 		};
 		
 		if (ret == GKR_PKIX_SUCCESS)
-			fire_parsed_sexp (parser, loc, digest, GKR_PKIX_PRIVATE_KEY, s_key);
+			ret = fire_parsed_sexp (parser, loc, digest, GKR_PKIX_PRIVATE_KEY, s_key);
 		
 	} else if (ret == GKR_PKIX_FAILURE) {
 		g_message ("invalid PKCS#8 key");
@@ -647,8 +701,10 @@
 		
     	/* If no password is available, we still know it's a key, so 'partial' parse */
         if (!enum_next_password (parser, location, digest, GKR_PKIX_PRIVATE_KEY, NULL, &pstate, &password)) {
-        	fire_parsed_partial (parser, location, digest, GKR_PKIX_PRIVATE_KEY);
-        	ret = GKR_PKIX_SUCCESS;
+        	if (parser->interactive)
+        		ret = GKR_PKIX_CANCELLED;
+        	else 
+        		ret = fire_parsed_partial (parser, location, digest, GKR_PKIX_PRIVATE_KEY);
         	goto done; 
         }
 	        
@@ -752,7 +808,7 @@
 
 	ret = gkr_pkix_der_read_certificate (data, n_data, &asn1);	
 	if(ret == GKR_PKIX_SUCCESS)
-		fire_parsed_asn1 (parser, loc, digest, GKR_PKIX_CERTIFICATE, asn1);
+		ret = fire_parsed_asn1 (parser, loc, digest, GKR_PKIX_CERTIFICATE, asn1);
 	gkr_id_free (digest);
 	
 	return ret;
@@ -793,7 +849,7 @@
 	
 	ret = gkr_pkix_der_read_certificate (certificate, n_certificate, &casn);
 	if(ret == GKR_PKIX_SUCCESS)
-		fire_parsed_asn1 (parser, loc, digest, GKR_PKIX_CERTIFICATE, casn);
+		ret = fire_parsed_asn1 (parser, loc, digest, GKR_PKIX_CERTIFICATE, casn);
 		
 done:
 	if (asn)
@@ -862,7 +918,7 @@
 			r = GKR_PKIX_UNRECOGNIZED;
 		}
 		 
-		if (r == GKR_PKIX_FAILURE) {
+		if (r == GKR_PKIX_FAILURE || r == GKR_PKIX_CANCELLED) {
 			ret = r;
 			goto done;
 		}
@@ -916,8 +972,10 @@
 		g_assert (cih == NULL);
 		
 	        if (!enum_next_password (parser, loc, digest, 0, NULL, &pstate, &password)) {
-	        	fire_parsed_partial (parser, loc, digest, 0);
-	        	ret = GKR_PKIX_SUCCESS;
+	        	if (parser->interactive)
+	        		ret = GKR_PKIX_CANCELLED;
+	        	else 
+	        		ret = fire_parsed_partial (parser, loc, digest, 0);
 	        	goto done; 
 	        }
 	        
@@ -1039,7 +1097,7 @@
 			r = GKR_PKIX_UNRECOGNIZED;
 		}
 		
-		if (r == GKR_PKIX_FAILURE) {
+		if (r == GKR_PKIX_FAILURE || r == GKR_PKIX_CANCELLED) {
 			ret = r;
 			goto done;
 		}
@@ -1133,7 +1191,7 @@
 	
 		ret = gkr_pkix_der_read_certificate (certificate, n_certificate, &casn);
 		if (ret == GKR_PKIX_SUCCESS)
-			fire_parsed_asn1 (parser, loc, digest, GKR_PKIX_CERTIFICATE, casn);
+			ret = fire_parsed_asn1 (parser, loc, digest, GKR_PKIX_CERTIFICATE, casn);
 		if (ret == GKR_PKIX_FAILURE)
 			goto done;
 	}
@@ -1273,9 +1331,9 @@
 		g_assert (parsed);
 		
 		if (s_key)
-			fire_parsed_sexp (parser, location, digest, parsed, s_key);
+			res = fire_parsed_sexp (parser, location, digest, parsed, s_key);
 		else
-			fire_parsed_asn1 (parser, location, digest, parsed, asn1);
+			res = fire_parsed_asn1 (parser, location, digest, parsed, asn1);
 	}
 
 	return res;
@@ -1311,11 +1369,13 @@
 		
 	while (!gkr_async_is_stopping ()) {
 
-    	/* If no password is available, we still know what it was, so 'partial' parse */
+		/* If no password is available, we still know what it was, so 'partial' parse */
 		if (!enum_next_password (parser, location, digest, parsed, NULL, &pstate, &password)) {
-        	fire_parsed_partial (parser, location, digest, parsed);
-        	return GKR_PKIX_SUCCESS;
-        }
+	        	if (parser->interactive)
+	        		return GKR_PKIX_CANCELLED;
+	        	else 
+	        		return fire_parsed_partial (parser, location, digest, parsed);
+		}
 		
 		decrypted = NULL;
 		n_decrypted = 0;

Modified: trunk/daemon/pkix/gkr-pkix-parser.h
==============================================================================
--- trunk/daemon/pkix/gkr-pkix-parser.h	(original)
+++ trunk/daemon/pkix/gkr-pkix-parser.h	Sun Aug 10 15:43:48 2008
@@ -50,6 +50,7 @@
 
 struct _GkrPkixParser {
 	 GObject parent;
+	 gboolean interactive;
 };
 
 struct _GkrPkixParserClass {
@@ -80,7 +81,7 @@
 
 GQuark 	            gkr_pkix_parser_get_error_domain        (void) G_GNUC_CONST;
 
-GkrPkixParser*      gkr_pkix_parser_new                     (void);
+GkrPkixParser*      gkr_pkix_parser_new                     (gboolean interactive);
 
 gboolean            gkr_pkix_parser_parse                   (GkrPkixParser *parser, GQuark loc,
                                                              const guchar *data, gsize n_data, 

Modified: trunk/daemon/pkix/gkr-pkix-types.h
==============================================================================
--- trunk/daemon/pkix/gkr-pkix-types.h	(original)
+++ trunk/daemon/pkix/gkr-pkix-types.h	Sun Aug 10 15:43:48 2008
@@ -2,7 +2,8 @@
 #define GKRPKIXTYPES_H_
 
 typedef enum _GkrPkixResult {
-	GKR_PKIX_FAILURE = -1,
+	GKR_PKIX_FAILURE = -2,
+	GKR_PKIX_CANCELLED = -1,
 	GKR_PKIX_UNRECOGNIZED = 0,
 	GKR_PKIX_SUCCESS = 1
 } GkrPkixResult;

Modified: trunk/daemon/pkix/tests/unit-test-pkix-parser.c
==============================================================================
--- trunk/daemon/pkix/tests/unit-test-pkix-parser.c	(original)
+++ trunk/daemon/pkix/tests/unit-test-pkix-parser.c	Sun Aug 10 15:43:48 2008
@@ -185,7 +185,7 @@
 
 void unit_test_start_parser (CuTest *cu)
 {
-	parser = gkr_pkix_parser_new ();
+	parser = gkr_pkix_parser_new (FALSE);
 	g_signal_connect (parser, "parsed-partial", G_CALLBACK (parsed_partial), NULL);
 	g_signal_connect (parser, "parsed-sexp", G_CALLBACK (parsed_sexp), NULL);
 	g_signal_connect (parser, "parsed-asn1", G_CALLBACK (parsed_asn1), NULL);

Modified: trunk/daemon/pkix/tests/unit-test-pkix-serialize.c
==============================================================================
--- trunk/daemon/pkix/tests/unit-test-pkix-serialize.c	(original)
+++ trunk/daemon/pkix/tests/unit-test-pkix-serialize.c	Sun Aug 10 15:43:48 2008
@@ -126,7 +126,7 @@
 	/* Serializes as PKCS8 */
 	output = gkr_pkix_serialize_to_data (GKR_PKIX_PRIVATE_KEY, key, "booo", &n_output);
 	
-	parser = gkr_pkix_parser_new ();
+	parser = gkr_pkix_parser_new (FALSE);
 	g_signal_connect (parser, "ask-password", G_CALLBACK (ask_password), cu);
 
 	result = gkr_pkix_parser_der_pkcs8 (parser, 0, output, n_output);



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