[evolution-data-server] ews-I#87 - Meta backends not moved back online after connect failure



commit 43655e7662bb2288519de5efa7a12b3a22f42134
Author: Milan Crha <mcrha redhat com>
Date:   Tue Mar 17 11:31:41 2020 +0100

    ews-I#87 - Meta backends not moved back online after connect failure
    
    Closes https://gitlab.gnome.org/GNOME/evolution-ews/issues/87

 .../libedata-book/e-book-meta-backend.c            | 49 +++++++++++++---------
 src/calendar/libedata-cal/e-cal-meta-backend.c     | 49 +++++++++++++---------
 tests/libedata-book/test-book-meta-backend.c       | 21 ++++++++++
 tests/libedata-cal/test-cal-meta-backend.c         | 21 ++++++++++
 4 files changed, 100 insertions(+), 40 deletions(-)
---
diff --git a/src/addressbook/libedata-book/e-book-meta-backend.c 
b/src/addressbook/libedata-book/e-book-meta-backend.c
index 2edbe2f4c..200c5e7ca 100644
--- a/src/addressbook/libedata-book/e-book-meta-backend.c
+++ b/src/addressbook/libedata-book/e-book-meta-backend.c
@@ -326,6 +326,7 @@ ebmb_get_changes_sync (EBookMetaBackend *meta_backend,
                       GCancellable *cancellable,
                       GError **error)
 {
+       EBackend *backend;
        GSList *existing_objects = NULL;
        gboolean success;
 
@@ -338,8 +339,13 @@ ebmb_get_changes_sync (EBookMetaBackend *meta_backend,
        *out_modified_objects = NULL;
        *out_removed_objects = NULL;
 
-       if (!e_backend_get_online (E_BACKEND (meta_backend)))
+       backend = E_BACKEND (meta_backend);
+
+       if (!e_backend_get_online (backend) &&
+           !e_backend_is_destination_reachable (backend, cancellable, NULL))
                return TRUE;
+       else
+               e_backend_set_online (backend, TRUE);
 
        if (!e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, error) ||
            !e_book_meta_backend_list_existing_sync (meta_backend, out_new_sync_tag, &existing_objects, 
cancellable, error)) {
@@ -780,8 +786,7 @@ ebmb_refresh_internal_sync (EBookMetaBackend *meta_backend,
 
        e_book_backend_foreach_view_notify_progress (E_BOOK_BACKEND (meta_backend), TRUE, 0, 
_("Refreshing…"));
 
-       if (!e_backend_get_online (E_BACKEND (meta_backend)) ||
-           !e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, with_connection_error ? 
error : NULL) ||
+       if (!e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, with_connection_error ? 
error : NULL) ||
            !e_backend_get_online (E_BACKEND (meta_backend))) { /* Failed connecting moves backend to offline 
*/
                g_mutex_lock (&meta_backend->priv->property_lock);
                meta_backend->priv->refresh_after_authenticate = TRUE;
@@ -942,8 +947,8 @@ ebmb_source_changed_thread_func (EBookBackend *book_backend,
 
        g_signal_emit (meta_backend, signals[SOURCE_CHANGED], 0, NULL);
 
-       if (e_backend_get_online (E_BACKEND (meta_backend)) &&
-           e_book_meta_backend_requires_reconnect (meta_backend)) {
+       if (e_book_meta_backend_requires_reconnect (meta_backend) &&
+           (e_backend_get_online (E_BACKEND (meta_backend)) || e_backend_is_destination_reachable (E_BACKEND 
(meta_backend), cancellable, NULL))) {
                gboolean can_refresh;
 
                g_mutex_lock (&meta_backend->priv->connect_lock);
@@ -1338,8 +1343,7 @@ ebmb_create_contact_sync (EBookMetaBackend *meta_backend,
        }
 
        if (*offline_flag == E_CACHE_OFFLINE_UNKNOWN) {
-               if (e_backend_get_online (E_BACKEND (meta_backend)) &&
-                   e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL)) {
+               if (e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL)) {
                        *offline_flag = E_CACHE_IS_ONLINE;
                } else {
                        *offline_flag = E_CACHE_IS_OFFLINE;
@@ -1489,8 +1493,7 @@ ebmb_modify_contact_sync (EBookMetaBackend *meta_backend,
                extra = NULL;
 
        if (success && *offline_flag == E_CACHE_OFFLINE_UNKNOWN) {
-               if (e_backend_get_online (E_BACKEND (meta_backend)) &&
-                   e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL)) {
+               if (e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL)) {
                        *offline_flag = E_CACHE_IS_ONLINE;
                } else {
                        *offline_flag = E_CACHE_IS_OFFLINE;
@@ -1622,8 +1625,7 @@ ebmb_remove_contact_sync (EBookMetaBackend *meta_backend,
        }
 
        if (*offline_flag == E_CACHE_OFFLINE_UNKNOWN) {
-               if (e_backend_get_online (E_BACKEND (meta_backend)) &&
-                   e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL)) {
+               if (e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL)) {
                        *offline_flag = E_CACHE_IS_ONLINE;
                } else {
                        *offline_flag = E_CACHE_IS_OFFLINE;
@@ -1743,8 +1745,7 @@ ebmb_get_contact_sync (EBookBackendSync *book_backend,
                g_clear_error (&local_error);
 
                /* Ignore errors here, just try whether it's on the remote side, but not in the local cache */
-               if (e_backend_get_online (E_BACKEND (meta_backend)) &&
-                   e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL) &&
+               if (e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL) &&
                    ebmb_load_contact_wrapper_sync (meta_backend, book_cache, uid, NULL, NULL, &loaded_uid, 
cancellable, NULL)) {
                        found = e_book_cache_get_contact (book_cache, loaded_uid, FALSE, &contact, 
cancellable, NULL);
                }
@@ -2058,7 +2059,8 @@ ebmb_authenticate_sync (EBackend *backend,
 
        meta_backend = E_BOOK_META_BACKEND (backend);
 
-       if (!e_backend_get_online (E_BACKEND (meta_backend))) {
+       if (!e_backend_get_online (backend) &&
+           !e_backend_is_destination_reachable (backend, cancellable, NULL)) {
                g_propagate_error (error, e_client_error_create (E_CLIENT_ERROR_REPOSITORY_OFFLINE, NULL));
 
                g_mutex_lock (&meta_backend->priv->wait_credentials_lock);
@@ -2071,11 +2073,11 @@ ebmb_authenticate_sync (EBackend *backend,
 
        g_mutex_lock (&meta_backend->priv->connect_lock);
 
-       e_source_set_connection_status (e_backend_get_source (backend), 
E_SOURCE_CONNECTION_STATUS_CONNECTING);
-
        /* Always disconnect first, then provide new credentials. */
        e_book_meta_backend_disconnect_sync (meta_backend, cancellable, NULL);
 
+       e_source_set_connection_status (e_backend_get_source (backend), 
E_SOURCE_CONNECTION_STATUS_CONNECTING);
+
        success = e_book_meta_backend_connect_sync (meta_backend, credentials, &auth_result,
                out_certificate_pem, out_certificate_errors, cancellable, error);
 
@@ -3280,6 +3282,7 @@ e_book_meta_backend_ensure_connected_sync (EBookMetaBackend *meta_backend,
                                           GCancellable *cancellable,
                                           GError **error)
 {
+       EBackend *backend;
        ENamedParameters *credentials;
        ESource *source;
        ESourceAuthenticationResult auth_result = E_SOURCE_AUTHENTICATION_UNKNOWN;
@@ -3290,7 +3293,13 @@ e_book_meta_backend_ensure_connected_sync (EBookMetaBackend *meta_backend,
 
        g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
 
-       if (!e_backend_get_online (E_BACKEND (meta_backend))) {
+       backend = E_BACKEND (meta_backend);
+
+       if (!e_backend_get_online (backend) &&
+           e_backend_is_destination_reachable (backend, cancellable, NULL))
+               e_backend_set_online (backend, TRUE);
+
+       if (!e_backend_get_online (backend)) {
                g_propagate_error (error, e_client_error_create (E_CLIENT_ERROR_REPOSITORY_OFFLINE, NULL));
 
                return FALSE;
@@ -3302,7 +3311,7 @@ e_book_meta_backend_ensure_connected_sync (EBookMetaBackend *meta_backend,
 
        g_mutex_lock (&meta_backend->priv->connect_lock);
 
-       source = e_backend_get_source (E_BACKEND (meta_backend));
+       source = e_backend_get_source (backend);
 
        if (e_source_get_connection_status (source) != E_SOURCE_CONNECTION_STATUS_CONNECTED)
                e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
@@ -3326,7 +3335,7 @@ e_book_meta_backend_ensure_connected_sync (EBookMetaBackend *meta_backend,
        g_warn_if_fail (auth_result != E_SOURCE_AUTHENTICATION_ACCEPTED);
 
        if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND)) {
-               e_backend_set_online (E_BACKEND (meta_backend), FALSE);
+               e_backend_set_online (backend, FALSE);
                g_propagate_error (error, local_error);
                g_free (certificate_pem);
 
@@ -3356,7 +3365,7 @@ e_book_meta_backend_ensure_connected_sync (EBookMetaBackend *meta_backend,
                break;
        }
 
-       e_backend_schedule_credentials_required (E_BACKEND (meta_backend), creds_reason, certificate_pem, 
certificate_errors,
+       e_backend_schedule_credentials_required (backend, creds_reason, certificate_pem, certificate_errors,
                local_error, cancellable, G_STRFUNC);
 
        g_clear_error (&local_error);
diff --git a/src/calendar/libedata-cal/e-cal-meta-backend.c b/src/calendar/libedata-cal/e-cal-meta-backend.c
index ea39eb547..8c89237ef 100644
--- a/src/calendar/libedata-cal/e-cal-meta-backend.c
+++ b/src/calendar/libedata-cal/e-cal-meta-backend.c
@@ -341,6 +341,7 @@ ecmb_get_changes_sync (ECalMetaBackend *meta_backend,
                       GCancellable *cancellable,
                       GError **error)
 {
+       EBackend *backend;
        GSList *existing_objects = NULL;
        gboolean success;
 
@@ -353,8 +354,13 @@ ecmb_get_changes_sync (ECalMetaBackend *meta_backend,
        *out_modified_objects = NULL;
        *out_removed_objects = NULL;
 
-       if (!e_backend_get_online (E_BACKEND (meta_backend)))
+       backend = E_BACKEND (meta_backend);
+
+       if (!e_backend_get_online (backend) &&
+           !e_backend_is_destination_reachable (backend, cancellable, NULL))
                return TRUE;
+       else
+               e_backend_set_online (backend, TRUE);
 
        if (!e_cal_meta_backend_ensure_connected_sync (meta_backend, cancellable, error) ||
            !e_cal_meta_backend_list_existing_sync (meta_backend, out_new_sync_tag, &existing_objects, 
cancellable, error)) {
@@ -683,8 +689,7 @@ ecmb_refresh_internal_sync (ECalMetaBackend *meta_backend,
 
        e_cal_backend_foreach_view_notify_progress (E_CAL_BACKEND (meta_backend), TRUE, 0, _("Refreshing…"));
 
-       if (!e_backend_get_online (E_BACKEND (meta_backend)) ||
-           !e_cal_meta_backend_ensure_connected_sync (meta_backend, cancellable, with_connection_error ? 
error : NULL) ||
+       if (!e_cal_meta_backend_ensure_connected_sync (meta_backend, cancellable, with_connection_error ? 
error : NULL) ||
            !e_backend_get_online (E_BACKEND (meta_backend))) { /* Failed connecting moves backend to offline 
*/
                g_mutex_lock (&meta_backend->priv->property_lock);
                meta_backend->priv->refresh_after_authenticate = TRUE;
@@ -844,8 +849,8 @@ ecmb_source_changed_thread_func (ECalBackend *cal_backend,
 
        g_signal_emit (meta_backend, signals[SOURCE_CHANGED], 0, NULL);
 
-       if (e_backend_get_online (E_BACKEND (meta_backend)) &&
-           e_cal_meta_backend_requires_reconnect (meta_backend)) {
+       if (e_cal_meta_backend_requires_reconnect (meta_backend) &&
+           (e_backend_get_online (E_BACKEND (meta_backend)) || e_backend_is_destination_reachable (E_BACKEND 
(meta_backend), cancellable, NULL))) {
                gboolean can_refresh;
 
                g_mutex_lock (&meta_backend->priv->connect_lock);
@@ -1375,8 +1380,7 @@ ecmb_get_object_sync (ECalBackendSync *sync_backend,
                g_clear_error (&local_error);
 
                /* Ignore errors here, just try whether it's on the remote side, but not in the local cache */
-               if (e_backend_get_online (E_BACKEND (meta_backend)) &&
-                   e_cal_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL) &&
+               if (e_cal_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL) &&
                    ecmb_load_component_wrapper_sync (meta_backend, cal_cache, uid, NULL, NULL, &loaded_uid, 
cancellable, NULL)) {
                        found = e_cal_cache_get_component_as_string (cal_cache, loaded_uid, rid, calobj, 
cancellable, NULL);
                }
@@ -1654,8 +1658,7 @@ ecmb_create_object_sync (ECalMetaBackend *meta_backend,
        g_clear_object (&itt);
 
        if (*offline_flag == E_CACHE_OFFLINE_UNKNOWN) {
-               if (e_backend_get_online (E_BACKEND (meta_backend)) &&
-                   e_cal_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL)) {
+               if (e_cal_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL)) {
                        *offline_flag = E_CACHE_IS_ONLINE;
                } else {
                        *offline_flag = E_CACHE_IS_OFFLINE;
@@ -1965,8 +1968,7 @@ ecmb_modify_object_sync (ECalMetaBackend *meta_backend,
        }
 
        if (success && *offline_flag == E_CACHE_OFFLINE_UNKNOWN) {
-               if (e_backend_get_online (E_BACKEND (meta_backend)) &&
-                   e_cal_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL)) {
+               if (e_cal_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL)) {
                        *offline_flag = E_CACHE_IS_ONLINE;
                } else {
                        *offline_flag = E_CACHE_IS_OFFLINE;
@@ -2136,8 +2138,7 @@ ecmb_remove_object_sync (ECalMetaBackend *meta_backend,
                old_comp = e_cal_component_clone (existing_comp);
 
        if (*offline_flag == E_CACHE_OFFLINE_UNKNOWN) {
-               if (e_backend_get_online (E_BACKEND (meta_backend)) &&
-                   e_cal_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL)) {
+               if (e_cal_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL)) {
                        *offline_flag = E_CACHE_IS_ONLINE;
                } else {
                        *offline_flag = E_CACHE_IS_OFFLINE;
@@ -2953,7 +2954,8 @@ ecmb_authenticate_sync (EBackend *backend,
 
        meta_backend = E_CAL_META_BACKEND (backend);
 
-       if (!e_backend_get_online (E_BACKEND (meta_backend))) {
+       if (!e_backend_get_online (backend) &&
+           !e_backend_is_destination_reachable (backend, cancellable, NULL)) {
                g_set_error_literal (error, E_CLIENT_ERROR, E_CLIENT_ERROR_REPOSITORY_OFFLINE,
                        e_client_error_to_string (E_CLIENT_ERROR_REPOSITORY_OFFLINE));
 
@@ -2967,11 +2969,11 @@ ecmb_authenticate_sync (EBackend *backend,
 
        g_mutex_lock (&meta_backend->priv->connect_lock);
 
-       e_source_set_connection_status (e_backend_get_source (backend), 
E_SOURCE_CONNECTION_STATUS_CONNECTING);
-
        /* Always disconnect first, then provide new credentials. */
        e_cal_meta_backend_disconnect_sync (meta_backend, cancellable, NULL);
 
+       e_source_set_connection_status (e_backend_get_source (backend), 
E_SOURCE_CONNECTION_STATUS_CONNECTING);
+
        success = e_cal_meta_backend_connect_sync (meta_backend, credentials, &auth_result,
                out_certificate_pem, out_certificate_errors, cancellable, error);
 
@@ -4397,6 +4399,7 @@ e_cal_meta_backend_ensure_connected_sync (ECalMetaBackend *meta_backend,
                                          GCancellable *cancellable,
                                          GError **error)
 {
+       EBackend *backend;
        ENamedParameters *credentials;
        ESource *source;
        ESourceAuthenticationResult auth_result = E_SOURCE_AUTHENTICATION_UNKNOWN;
@@ -4407,7 +4410,13 @@ e_cal_meta_backend_ensure_connected_sync (ECalMetaBackend *meta_backend,
 
        g_return_val_if_fail (E_IS_CAL_META_BACKEND (meta_backend), FALSE);
 
-       if (!e_backend_get_online (E_BACKEND (meta_backend))) {
+       backend = E_BACKEND (meta_backend);
+
+       if (!e_backend_get_online (backend) &&
+           e_backend_is_destination_reachable (backend, cancellable, NULL))
+               e_backend_set_online (backend, TRUE);
+
+       if (!e_backend_get_online (backend)) {
                g_set_error_literal (error, E_CLIENT_ERROR, E_CLIENT_ERROR_REPOSITORY_OFFLINE,
                        e_client_error_to_string (E_CLIENT_ERROR_REPOSITORY_OFFLINE));
 
@@ -4420,7 +4429,7 @@ e_cal_meta_backend_ensure_connected_sync (ECalMetaBackend *meta_backend,
 
        g_mutex_lock (&meta_backend->priv->connect_lock);
 
-       source = e_backend_get_source (E_BACKEND (meta_backend));
+       source = e_backend_get_source (backend);
 
        if (e_source_get_connection_status (source) != E_SOURCE_CONNECTION_STATUS_CONNECTED)
                e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
@@ -4444,7 +4453,7 @@ e_cal_meta_backend_ensure_connected_sync (ECalMetaBackend *meta_backend,
        g_warn_if_fail (auth_result != E_SOURCE_AUTHENTICATION_ACCEPTED);
 
        if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND)) {
-               e_backend_set_online (E_BACKEND (meta_backend), FALSE);
+               e_backend_set_online (backend, FALSE);
                g_propagate_error (error, local_error);
                g_free (certificate_pem);
 
@@ -4474,7 +4483,7 @@ e_cal_meta_backend_ensure_connected_sync (ECalMetaBackend *meta_backend,
                break;
        }
 
-       e_backend_schedule_credentials_required (E_BACKEND (meta_backend), creds_reason, certificate_pem, 
certificate_errors,
+       e_backend_schedule_credentials_required (backend, creds_reason, certificate_pem, certificate_errors,
                local_error, cancellable, G_STRFUNC);
 
        g_clear_error (&local_error);
diff --git a/tests/libedata-book/test-book-meta-backend.c b/tests/libedata-book/test-book-meta-backend.c
index 7419e2cd6..810057561 100644
--- a/tests/libedata-book/test-book-meta-backend.c
+++ b/tests/libedata-book/test-book-meta-backend.c
@@ -226,6 +226,23 @@ e_book_meta_backend_test_get_backend_property (EBookBackend *book_backend,
        return E_BOOK_BACKEND_CLASS (e_book_meta_backend_test_parent_class)->impl_get_backend_property 
(book_backend, prop_name);
 }
 
+static gboolean
+e_book_meta_backend_test_get_destination_address (EBackend *backend,
+                                                 gchar **host,
+                                                 guint16 *port)
+{
+       g_return_val_if_fail (E_IS_BOOK_META_BACKEND_TEST (backend), FALSE);
+
+       if (e_backend_get_online (backend))
+               return FALSE;
+
+       /* Provide something unreachable, to not have the meta backend switch the backend to online */
+       *host = g_strdup ("server.no.where");
+       *port = 65535;
+
+       return TRUE;
+}
+
 static gboolean
 e_book_meta_backend_test_connect_sync (EBookMetaBackend *meta_backend,
                                       const ENamedParameters *credentials,
@@ -515,6 +532,7 @@ e_book_meta_backend_test_class_init (EBookMetaBackendTestClass *klass)
 {
        EBookMetaBackendClass *book_meta_backend_class;
        EBookBackendClass *book_backend_class;
+       EBackendClass *backend_class;
        GObjectClass *object_class;
 
        book_meta_backend_class = E_BOOK_META_BACKEND_CLASS (klass);
@@ -529,6 +547,9 @@ e_book_meta_backend_test_class_init (EBookMetaBackendTestClass *klass)
        book_backend_class = E_BOOK_BACKEND_CLASS (klass);
        book_backend_class->impl_get_backend_property = e_book_meta_backend_test_get_backend_property;
 
+       backend_class = E_BACKEND_CLASS (klass);
+       backend_class->get_destination_address = e_book_meta_backend_test_get_destination_address;
+
        object_class = G_OBJECT_CLASS (klass);
        object_class->constructed = e_book_meta_backend_test_constructed;
        object_class->finalize = e_book_meta_backend_test_finalize;
diff --git a/tests/libedata-cal/test-cal-meta-backend.c b/tests/libedata-cal/test-cal-meta-backend.c
index a2637a37c..83dc6e1b2 100644
--- a/tests/libedata-cal/test-cal-meta-backend.c
+++ b/tests/libedata-cal/test-cal-meta-backend.c
@@ -295,6 +295,23 @@ e_cal_meta_backend_test_get_backend_property (ECalBackend *cal_backend,
        return E_CAL_BACKEND_CLASS (e_cal_meta_backend_test_parent_class)->impl_get_backend_property 
(cal_backend, prop_name);
 }
 
+static gboolean
+e_cal_meta_backend_test_get_destination_address (EBackend *backend,
+                                                gchar **host,
+                                                guint16 *port)
+{
+       g_return_val_if_fail (E_IS_CAL_META_BACKEND_TEST (backend), FALSE);
+
+       if (e_backend_get_online (backend))
+               return FALSE;
+
+       /* Provide something unreachable, to not have the meta backend switch the backend to online */
+       *host = g_strdup ("server.no.where");
+       *port = 65535;
+
+       return TRUE;
+}
+
 static gboolean
 e_cal_meta_backend_test_connect_sync (ECalMetaBackend *meta_backend,
                                      const ENamedParameters *credentials,
@@ -656,6 +673,7 @@ e_cal_meta_backend_test_class_init (ECalMetaBackendTestClass *klass)
 {
        ECalMetaBackendClass *cal_meta_backend_class;
        ECalBackendClass *cal_backend_class;
+       EBackendClass *backend_class;
        GObjectClass *object_class;
 
        cal_meta_backend_class = E_CAL_META_BACKEND_CLASS (klass);
@@ -670,6 +688,9 @@ e_cal_meta_backend_test_class_init (ECalMetaBackendTestClass *klass)
        cal_backend_class = E_CAL_BACKEND_CLASS (klass);
        cal_backend_class->impl_get_backend_property = e_cal_meta_backend_test_get_backend_property;
 
+       backend_class = E_BACKEND_CLASS (klass);
+       backend_class->get_destination_address = e_cal_meta_backend_test_get_destination_address;
+
        object_class = G_OBJECT_CLASS (klass);
        object_class->constructed = e_cal_meta_backend_test_constructed;
        object_class->finalize = e_cal_meta_backend_test_finalize;


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