This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git


The following commit(s) were added to refs/heads/master by this push:
     new 73d549cd5a JAMES-4196 Allow CRUD operation on shared folder for JMAP 
(#2988)
73d549cd5a is described below

commit 73d549cd5ab2365a6c400a88dda33a06b206961d
Author: Benoit TELLIER <[email protected]>
AuthorDate: Fri Apr 3 14:01:01 2026 +0700

    JAMES-4196 Allow CRUD operation on shared folder for JMAP (#2988)
---
 .../contract/MailboxGetMethodContract.scala        | 108 ++++++++++
 .../contract/MailboxSetMethodContract.scala        | 224 ++++++++++++++++++++-
 .../apache/james/jmap/mail/MailboxFactory.scala    |   4 +-
 .../scala/org/apache/james/jmap/mail/Rights.scala  |   3 +-
 .../org/apache/james/jmap/mail/RightsTest.scala    |   4 +-
 5 files changed, 335 insertions(+), 8 deletions(-)

diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala
 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala
index f378f52c7b..9d5d142ee2 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala
@@ -1115,6 +1115,114 @@ trait MailboxGetMethodContract {
       .body(s"$FIRST_MAILBOX.myRights.maySetKeywords", equalTo(false))
   }
 
+  @Test
+  def 
getMailboxesShouldReturnMayCreateChildTrueWhenDelegatedWithCreateMailboxRight(server:
 GuiceJamesServer): Unit = {
+    val sharedMailboxName: String = "AndreShared"
+    val andreMailboxPath: MailboxPath = MailboxPath.forUser(ANDRE, 
sharedMailboxName)
+    val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(andreMailboxPath)
+      .serialize
+
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(andreMailboxPath, BOB.asString, new 
MailboxACL.Rfc4314Rights(Right.Lookup, Right.CreateMailbox))
+
+    `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(s"""{
+               |  "using": [
+               |    "urn:ietf:params:jmap:core",
+               |    "urn:ietf:params:jmap:mail",
+               |    "urn:apache:james:params:jmap:mail:shares"],
+               |  "methodCalls": [[
+               |      "Mailbox/get",
+               |      {
+               |        "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+               |        "ids": ["${mailboxId}"]
+               |      },
+               |      "c1"]]
+               |}""".stripMargin)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .body(s"$ARGUMENTS.list", hasSize(1))
+      .body(s"$FIRST_MAILBOX.myRights.mayCreateChild", equalTo(true))
+      .body(s"$FIRST_MAILBOX.myRights.maySubmit", equalTo(false))
+  }
+
+  @Test
+  def getMailboxesShouldReturnMaySubmitTrueWhenDelegatedWithPostRight(server: 
GuiceJamesServer): Unit = {
+    val sharedMailboxName: String = "AndreShared"
+    val andreMailboxPath: MailboxPath = MailboxPath.forUser(ANDRE, 
sharedMailboxName)
+    val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(andreMailboxPath)
+      .serialize
+
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(andreMailboxPath, BOB.asString, new 
MailboxACL.Rfc4314Rights(Right.Lookup, Right.Post))
+
+    `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(s"""{
+               |  "using": [
+               |    "urn:ietf:params:jmap:core",
+               |    "urn:ietf:params:jmap:mail",
+               |    "urn:apache:james:params:jmap:mail:shares"],
+               |  "methodCalls": [[
+               |      "Mailbox/get",
+               |      {
+               |        "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+               |        "ids": ["${mailboxId}"]
+               |      },
+               |      "c1"]]
+               |}""".stripMargin)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .body(s"$ARGUMENTS.list", hasSize(1))
+      .body(s"$FIRST_MAILBOX.myRights.mayCreateChild", equalTo(false))
+      .body(s"$FIRST_MAILBOX.myRights.maySubmit", equalTo(true))
+  }
+
+  @Test
+  def getMailboxesShouldIncludeCreateMailboxRightInRightsFieldWhenSet(server: 
GuiceJamesServer): Unit = {
+    val sharedMailboxName: String = "AndreShared"
+    val andreMailboxPath: MailboxPath = MailboxPath.forUser(ANDRE, 
sharedMailboxName)
+    val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(andreMailboxPath)
+      .serialize
+
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(andreMailboxPath, BOB.asString, new 
MailboxACL.Rfc4314Rights(Right.Lookup, Right.Read, Right.CreateMailbox))
+
+    `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(s"""{
+               |  "using": [
+               |    "urn:ietf:params:jmap:core",
+               |    "urn:ietf:params:jmap:mail",
+               |    "urn:apache:james:params:jmap:mail:shares"],
+               |  "methodCalls": [[
+               |      "Mailbox/get",
+               |      {
+               |        "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+               |        "ids": ["${mailboxId}"]
+               |      },
+               |      "c1"]]
+               |}""".stripMargin)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .body(s"$ARGUMENTS.list", hasSize(1))
+      .body(s"$FIRST_MAILBOX.rights['${BOB.asString}']", containsInAnyOrder(
+        Right.Lookup.asCharacter.toString,
+        Right.Read.asCharacter.toString,
+        Right.CreateMailbox.asCharacter.toString))
+      .body(s"$FIRST_MAILBOX.myRights.mayCreateChild", equalTo(true))
+  }
+
   @Test
   @Tag(CategoryTags.BASIC_FEATURE)
   def getMailboxesShouldNotReturnInboxRoleToShareeWhenDelegatedInbox(server: 
GuiceJamesServer): Unit = {
diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
index 5f83406267..fd3a02ba4c 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
@@ -2729,6 +2729,41 @@ trait MailboxSetMethodContract {
       .body("methodResponses[0][1].destroyed[0]", equalTo(mailboxId.serialize))
   }
 
+  @Test
+  def deleteSharedMailboxShouldFailWhenDoesNotHaveRight(server: 
GuiceJamesServer): Unit = {
+    val path = MailboxPath.forUser(ANDRE, "mailbox")
+    val mailboxId: MailboxId = 
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(path)
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(path, BOB.asString, MailboxACL.FULL_RIGHTS.except(new 
MailboxACL.Rfc4314Rights(Right.DeleteMailbox)))
+
+    `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(
+        s"""
+           |{
+           |   "using": [ "urn:ietf:params:jmap:core", 
"urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ],
+           |   "methodCalls": [
+           |       [
+           |           "Mailbox/set",
+           |           {
+           |                "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+           |                "destroy": ["${mailboxId.serialize}"]
+           |           },
+           |    "c1"
+           |       ]
+           |   ]
+           |}
+           |""".stripMargin)
+    .when
+      .post
+    .`then`
+      .log().ifValidationFails()
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .body("methodResponses[0][1].notDestroyed", hasKey(mailboxId.serialize))
+      .body("methodResponses[0][1].notDestroyed." + mailboxId.serialize + 
".type", equalTo("invalidArguments"))
+  }
+
   @Test
   def deleteShouldHandleInvalidMailboxId(): Unit = {
     val request =
@@ -3910,13 +3945,13 @@ trait MailboxSetMethodContract {
            |              "mayRemoveItems": true,
            |              "maySetSeen": true,
            |              "maySetKeywords": true,
-           |              "mayCreateChild": false,
+           |              "mayCreateChild": true,
            |              "mayRename": true,
            |              "mayDelete": true,
-           |              "maySubmit": false
+           |              "maySubmit": true
            |            },
            |            "rights": {
-           |              "[email protected]": [ "a", "e", "i", "l", "p", "r", 
"s", "t", "w", "x" ]
+           |              "[email protected]": [ "a", "e", "i", "k", "l", "p", 
"r", "s", "t", "w", "x" ]
            |            }
            |          }
            |        ],
@@ -9075,4 +9110,187 @@ trait MailboxSetMethodContract {
       }
   }
 
+  @Test
+  def 
createSubfolderInSharedMailboxShouldSucceedWhenHasCreateMailboxRight(server: 
GuiceJamesServer): Unit = {
+    val path = MailboxPath.forUser(ANDRE, "shared")
+    val sharedMailboxId: MailboxId = 
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(path)
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(path, BOB.asString, new 
MailboxACL.Rfc4314Rights(Right.Lookup, Right.CreateMailbox))
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(s"""{
+               |   "using": [ "urn:ietf:params:jmap:core", 
"urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ],
+               |   "methodCalls": [
+               |     [ "Mailbox/set",
+               |       {
+               |         "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+               |         "create": {
+               |           "C42": {
+               |             "name": "child",
+               |             "parentId": "${sharedMailboxId.serialize}"
+               |           }
+               |         }
+               |       }, "c1"],
+               |     [ "Mailbox/get",
+               |       {
+               |         "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+               |         "ids": ["#C42"],
+               |         "properties": ["id", "name", "parentId"]
+               |       }, "c2"]]
+               |}""".stripMargin)
+    .when
+      .post
+    .`then`
+      .log().ifValidationFails()
+      .statusCode(SC_OK)
+      .extract.body.asString
+
+    assertThatJson(response)
+      .inPath("methodResponses[1][1].list[0]")
+      .isEqualTo(s"""{
+                    |  "id": "$${json-unit.ignore}",
+                    |  "name": "child",
+                    |  "parentId": "${sharedMailboxId.serialize}"
+                    |}""".stripMargin)
+  }
+
+  @Test
+  def 
renameSubfolderInSharedMailboxShouldSucceedWhenHasDeleteMailboxRight(server: 
GuiceJamesServer): Unit = {
+    val parentPath = MailboxPath.forUser(ANDRE, "shared")
+    val parentId: MailboxId = 
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(parentPath)
+    val childPath = parentPath.child("child", '.')
+    val childId: MailboxId = 
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(childPath)
+
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(parentPath, BOB.asString, new 
MailboxACL.Rfc4314Rights(Right.CreateMailbox))
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(childPath, BOB.asString, new 
MailboxACL.Rfc4314Rights(Right.Lookup, Right.DeleteMailbox))
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(s"""{
+               |   "using": [ "urn:ietf:params:jmap:core", 
"urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ],
+               |   "methodCalls": [
+               |     [ "Mailbox/set",
+               |       {
+               |         "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+               |         "update": {
+               |           "${childId.serialize}": {
+               |             "name": "renamed"
+               |           }
+               |         }
+               |       }, "c1"],
+               |     [ "Mailbox/get",
+               |       {
+               |         "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+               |         "ids": ["${childId.serialize}"],
+               |         "properties": ["id", "name", "parentId"]
+               |       }, "c2"]]
+               |}""".stripMargin)
+    .when
+      .post
+    .`then`
+      .log().ifValidationFails()
+      .statusCode(SC_OK)
+      .extract.body.asString
+
+    assertThatJson(response)
+      .inPath("methodResponses[1][1].list[0]")
+      .isEqualTo(s"""{
+                    |  "id": "${childId.serialize}",
+                    |  "name": "renamed",
+                    |  "parentId": "${parentId.serialize}"
+                    |}""".stripMargin)
+  }
+
+  @Test
+  def moveSubfolderInSharedMailboxShouldSucceedWhenHasRequiredRights(server: 
GuiceJamesServer): Unit = {
+    val srcParentPath = MailboxPath.forUser(ANDRE, "src")
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(srcParentPath)
+    val childPath = srcParentPath.child("child", '.')
+    val childId: MailboxId = 
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(childPath)
+    val destParentPath = MailboxPath.forUser(ANDRE, "dest")
+    val destParentId: MailboxId = 
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(destParentPath)
+
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(childPath, BOB.asString, new 
MailboxACL.Rfc4314Rights(Right.Lookup, Right.DeleteMailbox))
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(destParentPath, BOB.asString, new 
MailboxACL.Rfc4314Rights(Right.Lookup, Right.CreateMailbox))
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(s"""{
+               |   "using": [ "urn:ietf:params:jmap:core", 
"urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ],
+               |   "methodCalls": [
+               |     [ "Mailbox/set",
+               |       {
+               |         "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+               |         "update": {
+               |           "${childId.serialize}": {
+               |             "parentId": "${destParentId.serialize}"
+               |           }
+               |         }
+               |       }, "c1"],
+               |     [ "Mailbox/get",
+               |       {
+               |         "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+               |         "ids": ["${childId.serialize}"],
+               |         "properties": ["id", "name", "parentId"]
+               |       }, "c2"]]
+               |}""".stripMargin)
+    .when
+      .post
+    .`then`
+      .log().ifValidationFails()
+      .statusCode(SC_OK)
+      .extract.body.asString
+
+    assertThatJson(response)
+      .inPath("methodResponses[1][1].list[0]")
+      .isEqualTo(s"""{
+                    |  "id": "${childId.serialize}",
+                    |  "name": "child",
+                    |  "parentId": "${destParentId.serialize}"
+                    |}""".stripMargin)
+  }
+
+  @Test
+  def 
deleteSubfolderInSharedMailboxShouldSucceedWhenHasDeleteMailboxRight(server: 
GuiceJamesServer): Unit = {
+    val parentPath = MailboxPath.forUser(ANDRE, "shared")
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(parentPath)
+    val childPath = parentPath.child("child", '.')
+    val childId: MailboxId = 
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(childPath)
+
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(childPath, BOB.asString, new 
MailboxACL.Rfc4314Rights(Right.Lookup, Right.DeleteMailbox))
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(s"""{
+               |   "using": [ "urn:ietf:params:jmap:core", 
"urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ],
+               |   "methodCalls": [
+               |     [ "Mailbox/set",
+               |       {
+               |         "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+               |         "destroy": ["${childId.serialize}"]
+               |       }, "c1"],
+               |     [ "Mailbox/get",
+               |       {
+               |         "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+               |         "ids": ["${childId.serialize}"]
+               |       }, "c2"]]
+               |}""".stripMargin)
+    .when
+      .post
+    .`then`
+      .log().ifValidationFails()
+      .statusCode(SC_OK)
+      .extract.body.asString
+
+    assertThatJson(response)
+      .inPath("methodResponses[1][1].notFound")
+      .isEqualTo(s"""["${childId.serialize}"]""")
+  }
+
 }
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxFactory.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxFactory.scala
index 8589f879d0..9e980f76f6 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxFactory.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxFactory.scala
@@ -110,10 +110,10 @@ class MailboxFactory @Inject() (mailboxManager: 
MailboxManager,
         mayRemoveItems = MayRemoveItems(rights.contains(Right.DeleteMessages)),
         maySetSeen = MaySetSeen(rights.contains(Right.Seen)),
         maySetKeywords = MaySetKeywords(rights.contains(Right.Write)),
-        mayCreateChild = MayCreateChild(false),
+        mayCreateChild = MayCreateChild(rights.contains(Right.CreateMailbox)),
         mayRename = MayRename(rights.contains(Right.DeleteMailbox)),
         mayDelete = MayDelete(rights.contains(Right.DeleteMailbox)),
-        maySubmit = MaySubmit(false))
+        maySubmit = MaySubmit(rights.contains(Right.Post)))
   }
 
   def create(mailboxMetaData: MailboxMetaData,
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Rights.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Rights.scala
index 888cca1395..3a37cafe92 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Rights.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Rights.scala
@@ -68,8 +68,9 @@ object Right {
   val Write = Right(JavaRight.Write)
   val Post = Right(JavaRight.Post)
   val DeleteMailbox = Right(JavaRight.DeleteMailbox)
+  val CreateMailbox = Right(JavaRight.CreateMailbox)
 
-  private val allRights = Seq(Administer, Expunge, Insert, Lookup, Read, Seen, 
DeleteMessages, Write, Post, DeleteMailbox)
+  private val allRights = Seq(Administer, Expunge, Insert, Lookup, Read, Seen, 
DeleteMessages, Write, Post, DeleteMailbox, CreateMailbox)
 
   def forRight(right: JavaRight): Option[Right] = 
allRights.find(_.right.equals(right))
 
diff --git 
a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/mail/RightsTest.scala
 
b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/mail/RightsTest.scala
index 13f174a92f..13a1b10068 100644
--- 
a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/mail/RightsTest.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/mail/RightsTest.scala
@@ -58,7 +58,7 @@ class RightsTest extends AnyWordSpec with Matchers {
       Right.forChar('p') must be(Some(Right.Post))
     }
     "return empty when unknown" in {
-      Right.forChar('k') must be(None)
+      Right.forChar('b') must be(None)
     }
   }
   "From ACL" should  {
@@ -84,7 +84,7 @@ class RightsTest extends AnyWordSpec with Matchers {
       val acl = new JavaMailboxACL(Map(
         USER_ENTRYKEY -> 
JavaRfc4314Rights.fromSerializedRfc4314Rights("aetxk")).asJava)
 
-      Rights.fromACL(MailboxACL.fromJava(acl)) must 
be(Rights.of(USER_ENTRYKEY, Seq(Right.Administer, Right.Expunge, 
Right.DeleteMessages, Right.DeleteMailbox)))
+      Rights.fromACL(MailboxACL.fromJava(acl)) must 
be(Rights.of(USER_ENTRYKEY, Seq(Right.Administer, Right.Expunge, 
Right.CreateMailbox, Right.DeleteMessages, Right.DeleteMailbox)))
     }
   }
   "To ACL" should  {


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

Reply via email to