JAMES-2529 Extract ContentMatcher and HeaderExtractor from MailMatcher

Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/7527daec
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/7527daec
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/7527daec

Branch: refs/heads/master
Commit: 7527daec794b8e0022161e079e9a2bf710c58e77
Parents: aee5424
Author: Benoit Tellier <[email protected]>
Authored: Thu Aug 30 15:02:55 2018 +0700
Committer: Antoine Duprat <[email protected]>
Committed: Thu Aug 30 15:07:03 2018 +0200

----------------------------------------------------------------------
 .../jmap/mailet/filter/ContentMatcher.java      | 118 +++++++++++++++
 .../jmap/mailet/filter/HeaderExtractor.java     |  93 ++++++++++++
 .../james/jmap/mailet/filter/MailMatcher.java   | 150 -------------------
 3 files changed, 211 insertions(+), 150 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/7527daec/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ContentMatcher.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ContentMatcher.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ContentMatcher.java
new file mode 100644
index 0000000..a388332
--- /dev/null
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/ContentMatcher.java
@@ -0,0 +1,118 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.mailet.filter;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.james.jmap.api.filtering.Rule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableMap;
+
+public interface ContentMatcher {
+
+    class AddressHeader {
+        private static final Logger LOGGER = 
LoggerFactory.getLogger(AddressHeader.class);
+
+        private final Optional<String> personal;
+        private final Optional<String> address;
+        private final String fullAddress;
+
+        private AddressHeader(String fullAddress) {
+            this.fullAddress = fullAddress;
+            Optional<InternetAddress> internetAddress = parseFullAddress();
+            this.personal = internetAddress.map(InternetAddress::getPersonal);
+            this.address = internetAddress.map(InternetAddress::getAddress);
+        }
+
+        private Optional<InternetAddress> parseFullAddress() {
+            try {
+                return Optional.of(new InternetAddress(fullAddress));
+            } catch (AddressException e) {
+                LOGGER.error("error while parsing full address {}", 
fullAddress, e);
+                return Optional.empty();
+            }
+        }
+    }
+
+    ContentMatcher STRING_CONTAINS_MATCHER = (contents, valueToMatch) -> 
contents.anyMatch(content -> StringUtils.contains(content, valueToMatch));
+    ContentMatcher STRING_NOT_CONTAINS_MATCHER = 
negate(STRING_CONTAINS_MATCHER);
+    ContentMatcher STRING_EXACTLY_EQUALS_MATCHER = (contents, valueToMatch) -> 
contents.anyMatch(content -> StringUtils.equals(content, valueToMatch));
+    ContentMatcher STRING_NOT_EXACTLY_EQUALS_MATCHER = 
negate(STRING_EXACTLY_EQUALS_MATCHER);
+
+    ContentMatcher ADDRESS_CONTAINS_MATCHER = (contents, valueToMatch) -> 
contents
+        .map(ContentMatcher::asAddressHeader)
+        .anyMatch(addressHeader -> 
StringUtils.containsIgnoreCase(addressHeader.fullAddress, valueToMatch));
+    ContentMatcher ADDRESS_NOT_CONTAINS_MATCHER = 
negate(ADDRESS_CONTAINS_MATCHER);
+    ContentMatcher ADDRESS_EXACTLY_EQUALS_MATCHER = (contents, valueToMatch) 
-> contents
+        .map(ContentMatcher::asAddressHeader)
+        .anyMatch(addressHeader ->
+            valueToMatch.equalsIgnoreCase(addressHeader.fullAddress)
+                || 
addressHeader.address.map(valueToMatch::equalsIgnoreCase).orElse(false)
+                || 
addressHeader.personal.map(valueToMatch::equalsIgnoreCase).orElse(false));
+    ContentMatcher ADDRESS_NOT_EXACTLY_EQUALS_MATCHER = 
negate(ADDRESS_EXACTLY_EQUALS_MATCHER);
+
+    Map<Rule.Condition.Comparator, ContentMatcher> 
HEADER_ADDRESS_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Comparator, 
ContentMatcher>builder()
+        .put(Rule.Condition.Comparator.CONTAINS, ADDRESS_CONTAINS_MATCHER)
+        .put(Rule.Condition.Comparator.NOT_CONTAINS, 
ADDRESS_NOT_CONTAINS_MATCHER)
+        .put(Rule.Condition.Comparator.EXACTLY_EQUALS, 
ADDRESS_EXACTLY_EQUALS_MATCHER)
+        .put(Rule.Condition.Comparator.NOT_EXACTLY_EQUALS, 
ADDRESS_NOT_EXACTLY_EQUALS_MATCHER)
+        .build();
+
+    Map<Rule.Condition.Comparator, ContentMatcher> 
CONTENT_STRING_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Comparator, 
ContentMatcher>builder()
+        .put(Rule.Condition.Comparator.CONTAINS, STRING_CONTAINS_MATCHER)
+        .put(Rule.Condition.Comparator.NOT_CONTAINS, 
STRING_NOT_CONTAINS_MATCHER)
+        .put(Rule.Condition.Comparator.EXACTLY_EQUALS, 
STRING_EXACTLY_EQUALS_MATCHER)
+        .put(Rule.Condition.Comparator.NOT_EXACTLY_EQUALS, 
STRING_NOT_EXACTLY_EQUALS_MATCHER)
+        .build();
+
+    Map<Rule.Condition.Field, Map<Rule.Condition.Comparator, ContentMatcher>> 
CONTENT_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Field, 
Map<Rule.Condition.Comparator, ContentMatcher>>builder()
+        .put(Rule.Condition.Field.SUBJECT, CONTENT_STRING_MATCHER_REGISTRY)
+        .put(Rule.Condition.Field.TO, HEADER_ADDRESS_MATCHER_REGISTRY)
+        .put(Rule.Condition.Field.CC, HEADER_ADDRESS_MATCHER_REGISTRY)
+        .put(Rule.Condition.Field.RECIPIENT, HEADER_ADDRESS_MATCHER_REGISTRY)
+        .put(Rule.Condition.Field.FROM, HEADER_ADDRESS_MATCHER_REGISTRY)
+        .build();
+
+    static ContentMatcher negate(ContentMatcher contentMatcher) {
+        return (Stream<String> contents, String valueToMatch) ->
+            !contentMatcher.match(contents, valueToMatch);
+    }
+
+    static Optional<ContentMatcher> asContentMatcher(Rule.Condition.Field 
field, Rule.Condition.Comparator comparator) {
+        return Optional
+            .ofNullable(CONTENT_MATCHER_REGISTRY.get(field))
+            .map(matcherRegistry -> matcherRegistry.get(comparator));
+    }
+
+    static AddressHeader asAddressHeader(String addressAsString) {
+        return new AddressHeader(addressAsString);
+    }
+
+    boolean match(Stream<String> contents, String valueToMatch);
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/7527daec/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/HeaderExtractor.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/HeaderExtractor.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/HeaderExtractor.java
new file mode 100644
index 0000000..65d1629
--- /dev/null
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/HeaderExtractor.java
@@ -0,0 +1,93 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.mailet.filter;
+
+import static org.apache.mailet.base.RFC2822Headers.FROM;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import javax.mail.Address;
+import javax.mail.Message;
+
+import org.apache.james.javax.AddressHelper;
+import org.apache.james.jmap.api.filtering.Rule;
+import org.apache.james.mime4j.util.MimeUtil;
+import org.apache.james.util.StreamUtils;
+import org.apache.mailet.Mail;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.fge.lambdas.functions.ThrowingFunction;
+import com.google.common.collect.ImmutableMap;
+
+public interface HeaderExtractor extends ThrowingFunction<Mail, 
Stream<String>> {
+    Logger LOGGER = LoggerFactory.getLogger(HeaderExtractor.class);
+
+    HeaderExtractor SUBJECT_EXTRACTOR = mail ->
+        StreamUtils.ofNullables(mail.getMessage().getSubject());
+    HeaderExtractor CC_EXTRACTOR = 
recipientExtractor(Message.RecipientType.CC);
+    HeaderExtractor TO_EXTRACTOR = 
recipientExtractor(Message.RecipientType.TO);
+    HeaderExtractor RECIPIENT_EXTRACTOR = and(TO_EXTRACTOR, CC_EXTRACTOR);
+    HeaderExtractor FROM_EXTRACTOR = addressExtractor(mail -> 
mail.getMessage().getFrom(), FROM);
+
+    Map<Rule.Condition.Field, HeaderExtractor> HEADER_EXTRACTOR_REGISTRY = 
ImmutableMap.<Rule.Condition.Field, HeaderExtractor>builder()
+        .put(Rule.Condition.Field.SUBJECT, SUBJECT_EXTRACTOR)
+        .put(Rule.Condition.Field.RECIPIENT, RECIPIENT_EXTRACTOR)
+        .put(Rule.Condition.Field.FROM, FROM_EXTRACTOR)
+        .put(Rule.Condition.Field.CC, CC_EXTRACTOR)
+        .put(Rule.Condition.Field.TO, TO_EXTRACTOR)
+        .build();
+
+    static HeaderExtractor and(HeaderExtractor headerExtractor1, 
HeaderExtractor headerExtractor2) {
+        return (Mail mail) -> 
StreamUtils.flatten(headerExtractor1.apply(mail), headerExtractor2.apply(mail));
+    }
+
+    static HeaderExtractor recipientExtractor(Message.RecipientType type) {
+        ThrowingFunction<Mail, Address[]> addressGetter = mail -> 
mail.getMessage().getRecipients(type);
+        String fallbackHeaderName = type.toString();
+
+        return addressExtractor(addressGetter, fallbackHeaderName);
+    }
+
+    static HeaderExtractor addressExtractor(ThrowingFunction<Mail, Address[]> 
addressGetter, String fallbackHeaderName) {
+        return mail -> {
+            try {
+                return toContent(addressGetter.apply(mail));
+            } catch (Exception e) {
+                LOGGER.info("Failed parsing header. Falling back to unparsed 
header value matching", e);
+                return 
Stream.of(mail.getMessage().getHeader(fallbackHeaderName))
+                    .map(MimeUtil::unscrambleHeaderValue);
+            }
+        };
+    }
+
+    static Stream<String> toContent(Address[] addresses) {
+        return Optional.ofNullable(addresses)
+            .map(AddressHelper::asStringStream)
+            .orElse(Stream.empty());
+    }
+
+    static Optional<HeaderExtractor> asHeaderExtractor(Rule.Condition.Field 
field) {
+        return Optional.ofNullable(
+            HeaderExtractor.HEADER_EXTRACTOR_REGISTRY.get(field));
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/7527daec/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
index 031e696..ed5db25 100644
--- 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java
@@ -20,169 +20,19 @@
 package org.apache.james.jmap.mailet.filter;
 
 import static org.apache.james.jmap.api.filtering.Rule.Condition;
-import static org.apache.mailet.base.RFC2822Headers.FROM;
 
-import java.util.Map;
 import java.util.Optional;
 import java.util.stream.Stream;
 
-import javax.mail.Address;
-import javax.mail.Message;
-import javax.mail.internet.AddressException;
-import javax.mail.internet.InternetAddress;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.james.javax.AddressHelper;
 import org.apache.james.jmap.api.filtering.Rule;
-import org.apache.james.jmap.api.filtering.Rule.Condition.Field;
-import org.apache.james.mime4j.util.MimeUtil;
-import org.apache.james.util.StreamUtils;
 import org.apache.mailet.Mail;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.github.fge.lambdas.functions.ThrowingFunction;
 import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableMap;
 
 public interface MailMatcher {
 
-    interface HeaderExtractor extends ThrowingFunction<Mail, Stream<String>> {
-        Logger LOGGER = LoggerFactory.getLogger(HeaderExtractor.class);
-
-        HeaderExtractor SUBJECT_EXTRACTOR = mail ->
-            StreamUtils.ofNullables(mail.getMessage().getSubject());
-        HeaderExtractor CC_EXTRACTOR = 
recipientExtractor(Message.RecipientType.CC);
-        HeaderExtractor TO_EXTRACTOR = 
recipientExtractor(Message.RecipientType.TO);
-        HeaderExtractor RECIPIENT_EXTRACTOR = and(TO_EXTRACTOR, CC_EXTRACTOR);
-        HeaderExtractor FROM_EXTRACTOR = addressExtractor(mail -> 
mail.getMessage().getFrom(), FROM);
-
-        Map<Field, HeaderExtractor> HEADER_EXTRACTOR_REGISTRY = 
ImmutableMap.<Field, HeaderExtractor>builder()
-            .put(Field.SUBJECT, SUBJECT_EXTRACTOR)
-            .put(Field.RECIPIENT, RECIPIENT_EXTRACTOR)
-            .put(Field.FROM, FROM_EXTRACTOR)
-            .put(Field.CC, CC_EXTRACTOR)
-            .put(Field.TO, TO_EXTRACTOR)
-            .build();
-
-        static HeaderExtractor and(HeaderExtractor headerExtractor1, 
HeaderExtractor headerExtractor2) {
-            return (Mail mail) -> 
StreamUtils.flatten(headerExtractor1.apply(mail), headerExtractor2.apply(mail));
-        }
-
-        static HeaderExtractor recipientExtractor(Message.RecipientType type) {
-            ThrowingFunction<Mail, Address[]> addressGetter = mail -> 
mail.getMessage().getRecipients(type);
-            String fallbackHeaderName = type.toString();
-
-            return addressExtractor(addressGetter, fallbackHeaderName);
-        }
-
-        static HeaderExtractor addressExtractor(ThrowingFunction<Mail, 
Address[]> addressGetter, String fallbackHeaderName) {
-            return mail -> {
-                try {
-                    return toContent(addressGetter.apply(mail));
-                } catch (Exception e) {
-                    LOGGER.info("Failed parsing header. Falling back to 
unparsed header value matching", e);
-                    return 
Stream.of(mail.getMessage().getHeader(fallbackHeaderName))
-                        .map(MimeUtil::unscrambleHeaderValue);
-                }
-            };
-        }
-
-        static Stream<String> toContent(Address[] addresses) {
-            return Optional.ofNullable(addresses)
-                .map(AddressHelper::asStringStream)
-                .orElse(Stream.empty());
-        }
-
-        static Optional<HeaderExtractor> asHeaderExtractor(Field field) {
-            return Optional.ofNullable(
-                HeaderExtractor.HEADER_EXTRACTOR_REGISTRY.get(field));
-        }
-    }
-
-    interface ContentMatcher {
-
-        class AddressHeader {
-            private static final Logger LOGGER = 
LoggerFactory.getLogger(AddressHeader.class);
-
-            private final Optional<String> personal;
-            private final Optional<String> address;
-            private final String fullAddress;
-
-            private AddressHeader(String fullAddress) {
-                this.fullAddress = fullAddress;
-                Optional<InternetAddress> internetAddress = parseFullAddress();
-                this.personal = 
internetAddress.map(InternetAddress::getPersonal);
-                this.address = 
internetAddress.map(InternetAddress::getAddress);
-            }
-
-            private Optional<InternetAddress> parseFullAddress() {
-                try {
-                    return Optional.of(new InternetAddress(fullAddress));
-                } catch (AddressException e) {
-                    LOGGER.error("error while parsing full address {}", 
fullAddress, e);
-                    return Optional.empty();
-                }
-            }
-        }
-
-        ContentMatcher STRING_CONTAINS_MATCHER = (contents, valueToMatch) -> 
contents.anyMatch(content -> StringUtils.contains(content, valueToMatch));
-        ContentMatcher STRING_NOT_CONTAINS_MATCHER = 
negate(STRING_CONTAINS_MATCHER);
-        ContentMatcher STRING_EXACTLY_EQUALS_MATCHER = (contents, 
valueToMatch) -> contents.anyMatch(content -> StringUtils.equals(content, 
valueToMatch));
-        ContentMatcher STRING_NOT_EXACTLY_EQUALS_MATCHER = 
negate(STRING_EXACTLY_EQUALS_MATCHER);
-
-        ContentMatcher ADDRESS_CONTAINS_MATCHER = (contents, valueToMatch) -> 
contents
-            .map(ContentMatcher::asAddressHeader)
-            .anyMatch(addressHeader -> 
StringUtils.containsIgnoreCase(addressHeader.fullAddress, valueToMatch));
-        ContentMatcher ADDRESS_NOT_CONTAINS_MATCHER = 
negate(ADDRESS_CONTAINS_MATCHER);
-        ContentMatcher ADDRESS_EXACTLY_EQUALS_MATCHER = (contents, 
valueToMatch) -> contents
-            .map(ContentMatcher::asAddressHeader)
-            .anyMatch(addressHeader ->
-                valueToMatch.equalsIgnoreCase(addressHeader.fullAddress)
-                    || 
addressHeader.address.map(valueToMatch::equalsIgnoreCase).orElse(false)
-                    || 
addressHeader.personal.map(valueToMatch::equalsIgnoreCase).orElse(false));
-        ContentMatcher ADDRESS_NOT_EXACTLY_EQUALS_MATCHER = 
negate(ADDRESS_EXACTLY_EQUALS_MATCHER);
-
-        Map<Rule.Condition.Comparator, ContentMatcher> 
HEADER_ADDRESS_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Comparator, 
ContentMatcher>builder()
-            .put(Condition.Comparator.CONTAINS, ADDRESS_CONTAINS_MATCHER)
-            .put(Condition.Comparator.NOT_CONTAINS, 
ADDRESS_NOT_CONTAINS_MATCHER)
-            .put(Condition.Comparator.EXACTLY_EQUALS, 
ADDRESS_EXACTLY_EQUALS_MATCHER)
-            .put(Condition.Comparator.NOT_EXACTLY_EQUALS, 
ADDRESS_NOT_EXACTLY_EQUALS_MATCHER)
-            .build();
-
-        Map<Rule.Condition.Comparator, ContentMatcher> 
CONTENT_STRING_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Comparator, 
ContentMatcher>builder()
-            .put(Condition.Comparator.CONTAINS, STRING_CONTAINS_MATCHER)
-            .put(Condition.Comparator.NOT_CONTAINS, 
STRING_NOT_CONTAINS_MATCHER)
-            .put(Condition.Comparator.EXACTLY_EQUALS, 
STRING_EXACTLY_EQUALS_MATCHER)
-            .put(Condition.Comparator.NOT_EXACTLY_EQUALS, 
STRING_NOT_EXACTLY_EQUALS_MATCHER)
-            .build();
-
-        Map<Rule.Condition.Field, Map<Rule.Condition.Comparator, 
ContentMatcher>> CONTENT_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Field, 
Map<Rule.Condition.Comparator, ContentMatcher>>builder()
-            .put(Condition.Field.SUBJECT, CONTENT_STRING_MATCHER_REGISTRY)
-            .put(Condition.Field.TO, HEADER_ADDRESS_MATCHER_REGISTRY)
-            .put(Condition.Field.CC, HEADER_ADDRESS_MATCHER_REGISTRY)
-            .put(Condition.Field.RECIPIENT, HEADER_ADDRESS_MATCHER_REGISTRY)
-            .put(Condition.Field.FROM, HEADER_ADDRESS_MATCHER_REGISTRY)
-            .build();
-
-        static ContentMatcher negate(ContentMatcher contentMatcher) {
-            return (Stream<String> contents, String valueToMatch) ->
-                !contentMatcher.match(contents, valueToMatch);
-        }
-
-        static Optional<ContentMatcher> asContentMatcher(Condition.Field 
field, Condition.Comparator comparator) {
-            return Optional
-                .ofNullable(CONTENT_MATCHER_REGISTRY.get(field))
-                .map(matcherRegistry -> matcherRegistry.get(comparator));
-        }
-
-        static AddressHeader asAddressHeader(String addressAsString) {
-            return new AddressHeader(addressAsString);
-        }
-
-        boolean match(Stream<String> contents, String valueToMatch);
-    }
-
     class HeaderMatcher implements MailMatcher {
 
         private static final Logger LOGGER = 
LoggerFactory.getLogger(HeaderMatcher.class);


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to