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
commit 466973978fca0361fbc899b33a7abe1ed29fb9d4 Author: Benoit Tellier <btell...@linagora.com> AuthorDate: Thu Jul 9 13:52:27 2020 +0700 JAMES-3302 Migrate WebAdmin documentation to Antora Adapted to remove 'only on some products' mentions. Note that some liks needs to be fixed. --- .../pages/distributed/operate/webadmin.adoc | 4047 +++++++++++++++++++- .../servers/pages/distributed/run-docker.adoc | 38 +- 2 files changed, 4079 insertions(+), 6 deletions(-) diff --git a/docs/modules/servers/pages/distributed/operate/webadmin.adoc b/docs/modules/servers/pages/distributed/operate/webadmin.adoc index ed1b75a..55c589d 100644 --- a/docs/modules/servers/pages/distributed/operate/webadmin.adoc +++ b/docs/modules/servers/pages/distributed/operate/webadmin.adoc @@ -1,4 +1,4047 @@ = WebAdmin REST administration API -(TODO migrate and adapt content from -https://github.com/linagora/james-project/blob/master/src/site/markdown/server/manage-webadmin.md) \ No newline at end of file +The web administration supports for now the CRUD operations on the domains, the users, their mailboxes and their quotas, + managing mail repositories, performing cassandra migrations, and much more, as described in the following sections. + +*WARNING*: This API allow authentication only via the use of JWT. If not +configured with JWT, an administrator should ensure an attacker can not +use this API. + +By the way, some endpoints are not filtered by authentication. Those endpoints are not related to data stored in James, +for example: Swagger documentation & James health checks. + +In case of any error, the system will return an error message which is +json format like this: + +.... +{ + statusCode: <error_code>, + type: <error_type>, + message: <the_error_message> + cause: <the_detail_message_from_throwable> +} +.... + +Also be aware that, in case things go wrong, all endpoints might return +a 500 internal error (with a JSON body formatted as exposed above). To +avoid information duplication, this is omitted on endpoint specific +documentation. + +Finally, please note that in case of a malformed URL the 400 bad request +response will contain an HTML body. + +== HealthCheck + +=== Check all components + +This endpoint is simple for now and is just returning the http status +code corresponding to the state of checks (see below). The user has to +check in the logs in order to have more information about failing +checks. + +.... +curl -XGET http://ip:port/healthcheck +.... + +Will return a list of healthChecks execution result, with an aggregated +result: + +.... +{ + "status": "healthy", + "checks": [ + { + "componentName": "Cassandra backend", + "escapedComponentName": "Cassandra%20backend", + "status": "healthy" + "cause": null + } + ] +} +.... + +*status* field can be: + +* *healthy*: Component works normally +* *degraded*: Component works in degraded mode. Some non-critical +services may not be working, or latencies are high, for example. Cause +contains explanations. +* *unhealthy*: The component is currently not working. Cause contains +explanations. + +Supported health checks include: + +* *Cassandra backend*: Cassandra storage. +* *ElasticSearch Backend*: ElasticSearch storage. +* *EventDeadLettersHealthCheck* +* *Guice application lifecycle* +* *JPA Backend*: JPA storage. +* *MessageFastViewProjection* Health check of the component storing JMAP properties +which are fast to retrieve. Those properties are computed in advance +from messages and persisted in order to archive a better performance. +There are some latencies between a source update and its projections +updates. Incoherency problems arise when reads are performed in this +time-window. We piggyback the projection update on missed JMAP read in +order to decrease the outdated time window for a given entry. The health +is determined by the ratio of missed projection reads. (lower than 10% +causes `degraded`) +* *RabbitMQ backend*: RabbitMQ messaging. + +Response codes: + +* 200: All checks have answered with a Healthy or Degraded status. James +services can still be used. +* 503: At least one check have answered with a Unhealthy status + +=== Check single component + +Performs a health check for the given component. The component is +referenced by its URL encoded name. + +.... +curl -XGET http://ip:port/healthcheck/checks/Cassandra%20backend +.... + +Will return the component’s name, the component’s escaped name, the +health status and a cause. + +.... +{ + "componentName": "Cassandra backend", + "escapedComponentName": "Cassandra%20backend", + "status": "healthy" + "cause": null +} +.... + +Response codes: + +* 200: The check has answered with a Healthy or Degraded status. +* 404: A component with the given name was not found. +* 503: The check has anwered with a Unhealthy status. + +=== List all health checks + +This endpoint lists all the available health checks. + +.... +curl -XGET http://ip:port/healthcheck/checks +.... + +Will return the list of all available health checks. + +.... +[ + { + "componentName": "Cassandra backend", + "escapedComponentName": "Cassandra%20backend" + } +] +.... + +Response codes: + +* 200: List of available health checks + +== Administrating domains + +=== Create a domain + +.... +curl -XPUT http://ip:port/domains/domainToBeCreated +.... + +Resource name domainToBeCreated: + +* can not be null or empty +* can not contain `@' +* can not be more than 255 characters +* can not contain `/' + +Response codes: + +* 204: The domain was successfully added +* 400: The domain name is invalid + +=== Delete a domain + +.... +curl -XDELETE http://ip:port/domains/{domainToBeDeleted} +.... + +Note: Deletion of an auto-detected domain, default domain or of an +auto-detected ip is not supported. We encourage you instead to review +your https://james.apache.org/server/config-domainlist.html[domain list +configuration]. + +Response codes: + +* 204: The domain was successfully removed + +=== Test if a domain exists + +.... +curl -XGET http://ip:port/domains/{domainName} +.... + +Response codes: + +* 204: The domain exists +* 404: The domain does not exist + +=== Get the list of domains + +.... +curl -XGET http://ip:port/domains +.... + +Possible response: + +.... +["domain1", "domain2"] +.... + +Response codes: + +* 200: The domain list was successfully retrieved + +=== Get the list of aliases for a domain + +.... +curl -XGET http://ip:port/domains/destination.domain.tld/aliases +.... + +Possible response: + +.... +[ + {"source": "source1.domain.tld"}, + {"source": "source2.domain.tld"} +] +.... + +When sending an email to an email address having `source1.domain.tld` or +`source2.domain.tld` as a domain part (example: +`u...@source1.domain.tld`), then the domain part will be rewritten into +destination.domain.tld (so into `u...@destination.domain.tld`). + +Response codes: + +* 200: The domain aliases was successfully retrieved +* 400: destination.domain.tld has an invalid syntax +* 404: destination.domain.tld is not part of handled domains and does +not have local domains as aliases. + +=== Create an alias for a domain + +To create a domain alias execute the following query: + +.... +curl -XPUT http://ip:port/domains/destination.domain.tld/aliases/source.domain.tld +.... + +When sending an email to an email address having `source.domain.tld` as +a domain part (example: `u...@source.domain.tld`), then the domain part +will be rewritten into `destination.domain.tld` (so into +`u...@destination.domain.tld`). + +Response codes: + +* 204: The redirection now exists +* 400: `source.domain.tld` or `destination.domain.tld` have an invalid +syntax +* 400: `source, domain` and `destination domain` are the same +* 404: `source.domain.tld` are not part of handled domains. + +=== Delete an alias for a domain + +To delete a domain alias execute the following query: + +.... +curl -XDELETE http://ip:port/domains/destination.domain.tld/aliases/source.domain.tld +.... + +When sending an email to an email address having `source.domain.tld` as +a domain part (example: `u...@source.domain.tld`), then the domain part +will be rewritten into `destination.domain.tld` (so into +`u...@destination.domain.tld`). + +Response codes: + +* 204: The redirection now no longer exists +* 400: `source.domain.tld` or destination.domain.tld have an invalid +syntax +* 400: source, domain and destination domain are the same +* 404: `source.domain.tld` are not part of handled domains. + +== Administrating users + +=== Create a user + +.... +curl -XPUT http://ip:port/users/usernameToBeUsed \ + -d '{"password":"passwordToBeUsed"}' \ + -H "Content-Type: application/json" +.... + +Resource name usernameToBeUsed representing valid users, hence it should +match the criteria at (TODO) link:/server/config-users.html[User Repositories +documentation] + +Response codes: + +* 204: The user was successfully created +* 400: The user name or the payload is invalid + +Note: if the user exists already, its password will be updated. + +=== Testing a user existence + +.... +curl -XHEAD http://ip:port/users/usernameToBeUsed +.... + +Resource name ``usernameToBeUsed'' represents a valid user, hence it +should match the criteria at (TODO) link:/server/config-users.html[User +Repositories documentation] + +Response codes: + +* 200: The user exists +* 400: The user name is invalid +* 404: The user does not exist + +=== Updating a user password + +Same than Create, but a user need to exist. + +If the user do not exist, then it will be created. + +=== Deleting a user + +.... +curl -XDELETE http://ip:port/users/{userToBeDeleted} +.... + +Response codes: + +* 204: The user was successfully deleted + +=== Retrieving the user list + +.... +curl -XGET http://ip:port/users +.... + +The answer looks like: + +.... +[{"username":"usern...@domain-jmapauthentication.tld"},{"username":"usern...@domain.tld"}] +.... + +Response codes: + +* 200: The user name list was successfully retrieved + +=== Retrieving the list of allowed `From` headers for a given user + +This endpoint allows to know which From headers a given user is allowed to use when sending mails. + +.... +curl -XGET http://ip:port/users/givenUser/allowedFromHeaders +.... + +The answer looks like: + +.... +["u...@domain.tld","al...@domain.tld"] +.... + +Response codes: + +* 200: The list was successfully retrieved +* 400: The user is invalid +* 404: The user is unknown + +== Administrating mailboxes + +=== All mailboxes + +Several actions can be performed on the server mailboxes. + +Request pattern is: + +.... +curl -XPOST /mailboxes?action={action1},... +.... + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +Response codes: + +* 201: Success. Corresponding task id is returned. +* 400: Error in the request. Details can be found in the reported error. + +The kind of task scheduled depends on the action parameter. See below +for details. + +==== Fixing mailboxes inconsistencies + +.... +curl -XPOST /mailboxes?task=SolveInconsistencies +.... + +Will schedule a task for fixing inconsistencies for the mailbox +deduplicated object stored in Cassandra. + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +The `I-KNOW-WHAT-I-M-DOING` header is mandatory (you can read more +information about it in the warning section below). + +The scheduled task will have the following type +`solve-mailbox-inconsistencies` and the following +`additionalInformation`: + +.... +{ + "type":"solve-mailbox-inconsistencies", + "processedMailboxEntries": 3, + "processedMailboxPathEntries": 3, + "fixedInconsistencies": 2, + "errors": 1, + "conflictingEntries":[{ + "mailboxDaoEntry":{ + "mailboxPath":"#private:user:mailboxName", + "mailboxId":"464765a0-e4e7-11e4-aba4-710c1de3782b" + }," + + "mailboxPathDaoEntry":{ + "mailboxPath":"#private:user:mailboxName2", + "mailboxId":"464765a0-e4e7-11e4-aba4-710c1de3782b" + } + }] +} +.... + +Note that conflicting entry inconsistencies will not be fixed and will +require to explicitly use link:#correcting-ghost-mailbox[ghost mailbox] +endpoint in order to merge the conflicting mailboxes and prevent any +message loss. + +*WARNING*: this task can cancel concurrently running legitimate user +operations upon dirty read. As such this task should be run offline. + +A dirty read is when data is read between the two writes of the +denormalization operations (no isolation). + +In order to ensure being offline, stop the traffic on SMTP, JMAP and +IMAP ports, for example via re-configuration or firewall rules. + +Due to all of those risks, a `I-KNOW-WHAT-I-M-DOING` header should be +positioned to `ALL-SERVICES-ARE-OFFLINE` in order to prevent accidental +calls. + +==== Recomputing mailbox counters + +.... +curl -XPOST /mailboxes?task=RecomputeMailboxCounters +.... + +Will recompute counters (unseen & total count) for the mailbox object +stored in Cassandra. + +Cassandra maintains a per mailbox projection for message count and +unseen message count. As with any projection, it can go out of sync, +leading to inconsistent results being returned to the client. + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +The scheduled task will have the following type +`recompute-mailbox-counters` and the following `additionalInformation`: + +.... +{ + "type":"recompute-mailbox-counters", + "processedMailboxes": 3, + "failedMailboxes": ["464765a0-e4e7-11e4-aba4-710c1de3782b"] +} +.... + +Note that conflicting inconsistencies entries will not be fixed and will +require to explicitly use link:#correcting-ghost-mailbox[ghost mailbox] +endpoint in order to merge the conflicting mailboxes and prevent any +message loss. + +*WARNING*: this task do not take into account concurrent modifications +upon a single mailbox counter recomputation. Rerunning the task will +_eventually_ provide the consistent result. As such we advise to run +this task offline. + +In order to ensure being offline, stop the traffic on SMTP, JMAP and +IMAP ports, for example via re-configuration or firewall rules. + +`trustMessageProjection` query parameter can be set to `true`. Content +of `messageIdTable` (listing messages by their mailbox context) table +will be trusted and not compared against content of `imapUidTable` table +(listing messages by their messageId mailbox independent identifier). +This will result in a better performance running the task at the cost of +safety in the face of message denormalization inconsistencies. + +Defaults to false, which generates additional checks. You can read +https://github.com/apache/james-project/blob/master/src/adr/0022-cassandra-message-inconsistency.md[this +ADR] to better understand the message projection and how it can become +inconsistent. + +==== Recomputing Global JMAP fast message view projection + +Message fast view projection stores message properties expected to be +fast to fetch but are actually expensive to compute, in order for +GetMessages operation to be fast to execute for these properties. + +These projection items are asynchronously computed on mailbox events. + +You can force the full projection recomputation by calling the following +endpoint: + +.... +curl -XPOST /mailboxes?task=recomputeFastViewProjectionItems +.... + +Will schedule a task for recomputing the fast message view projection +for all mailboxes. + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +An admin can specify the concurrency that should be used when running +the task: + +* `messagesPerSecond` rate at which messages should be processed, per +second. Defaults to 10. + +This optional parameter must have a strictly positive integer as a value +and be passed as query parameters. + +Example: + +.... +curl -XPOST /mailboxes?task=recomputeFastViewProjectionItems&messagesPerSecond=20 +.... + +The scheduled task will have the following type +`RecomputeAllFastViewProjectionItemsTask` and the following +`additionalInformation`: + +.... +{ + "type":"RecomputeAllPreviewsTask", + "processedUserCount": 3, + "processedMessageCount": 3, + "failedUserCount": 2, + "failedMessageCount": 1, + "runningOptions": { + "messagesPerSecond":20 + } +} +.... + +Response codes: + +* 201: Success. Corresponding task id is returned. +* 400: Error in the request. Details can be found in the reported error. + +==== ReIndexing action + +Be also aware of the limits of this API: + +Warning: During the re-indexing, the result of search operations might +be altered. + +Warning: Canceling this task should be considered unsafe as it will +leave the currently reIndexed mailbox as partially indexed. + +Warning: While we have been trying to reduce the inconsistency window to +a maximum (by keeping track of ongoing events), concurrent changes done +during the reIndexing might be ignored. + +===== ReIndexing all mails + +.... +curl -XPOST http://ip:port/mailboxes?task=reIndex +.... + +Will schedule a task for reIndexing all the mails stored on this James +server. + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +An admin can specify the concurrency that should be used when running +the task: + +* `messagesPerSecond` rate at which messages should be processed per +second. Default is 50. + +This optional parameter must have a strictly positive integer as a value +and be passed as query parameter. + +An admin can also specify the reindexing mode it wants to use when +running the task: + +* `mode` the reindexing mode used. There are 2 modes for the moment: +** `rebuildAll` allows to rebuild all indexes. This is the default mode. +** `fixOutdated` will check for outdated indexed document and reindex +only those. + +This optional parameter must be passed as query parameter. + +It’s good to note as well that there is a limitation with the +`fixOutdated` mode. As we first collect metadata of stored messages to +compare them with the ones in the index, a failed `expunged` operation +might not be well corrected (as the message might not exist anymore but +still be indexed). + +Example: + + curl -XPOST http://ip:port/mailboxes?task=reIndex&messagesPerSecond=200&mode=rebuildAll + +The scheduled task will have the following type `full-reindexing` and +the following `additionalInformation`: + +.... +{ + "type":"full-reindexing", + "runningOptions":{ + "messagesPerSecond":200, + "mode":"REBUILD_ALL" + }, + "successfullyReprocessedMailCount":18, + "failedReprocessedMailCount": 3, + "mailboxFailures": ["12", "23" ], + "messageFailures": [ + { + "mailboxId": "1", + "uids": [1, 36] + }] +} +.... + +===== Fixing previously failed ReIndexing + +Will schedule a task for reIndexing all the mails which had failed to be +indexed from the ReIndexingAllMails task. + +Given `bbdb69c9-082a-44b0-a85a-6e33e74287a5` being a `taskId` generated +for a reIndexing tasks + +.... +curl -XPOST 'http://ip:port/mailboxes?task=reIndex&reIndexFailedMessagesOf=bbdb69c9-082a-44b0-a85a-6e33e74287a5' +.... + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +An admin can specify the concurrency that should be used when running +the task: + +* `messagesPerSecond` rate at which messages should be processed per +second. Default is 50. + +This optional parameter must have a strictly positive integer as a value +and be passed as query parameter. + +An admin can also specify the reindexing mode it wants to use when +running the task: + +* `mode` the reindexing mode used. There are 2 modes for the moment: +** `rebuildAll` allows to rebuild all indexes. This is the default mode. +** `fixOutdated` will check for outdated indexed document and reindex +only those. + +This optional parameter must be passed as query parameter. + +It’s good to note as well that there is a limitation with the +`fixOutdated` mode. As we first collect metadata of stored messages to +compare them with the ones in the index, a failed `expunged` operation +might not be well corrected (as the message might not exist anymore but +still be indexed). + +Example: + +.... +curl -XPOST http://ip:port/mailboxes?task=reIndex&reIndexFailedMessagesOf=bbdb69c9-082a-44b0-a85a-6e33e74287a5&messagesPerSecond=200&mode=rebuildAll +.... + +The scheduled task will have the following type +`error-recovery-indexation` and the following `additionalInformation`: + +.... +{ + "type":"error-recovery-indexation" + "runningOptions":{ + "messagesPerSecond":200, + "mode":"REBUILD_ALL" + }, + "successfullyReprocessedMailCount":18, + "failedReprocessedMailCount": 3, + "mailboxFailures": ["12", "23" ], + "messageFailures": [{ + "mailboxId": "1", + "uids": [1, 36] + }] +} +.... + +== Single mailbox + +=== ReIndexing a mailbox mails + +.... +curl -XPOST http://ip:port/mailboxes/{mailboxId}?task=reIndex +.... + +Will schedule a task for reIndexing all the mails in one mailbox. + +Note that `mailboxId' path parameter needs to be a (implementation +dependent) valid mailboxId. + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +An admin can specify the concurrency that should be used when running +the task: + +* `messagesPerSecond` rate at which messages should be processed per +second. Default is 50. + +This optional parameter must have a strictly positive integer as a value +and be passed as query parameter. + +An admin can also specify the reindexing mode it wants to use when +running the task: + +* `mode` the reindexing mode used. There are 2 modes for the moment: +** `rebuildAll` allows to rebuild all indexes. This is the default mode. +** `fixOutdated` will check for outdated indexed document and reindex +only those. + +This optional parameter must be passed as query parameter. + +It’s good to note as well that there is a limitation with the +`fixOutdated` mode. As we first collect metadata of stored messages to +compare them with the ones in the index, a failed `expunged` operation +might not be well corrected (as the message might not exist anymore but +still be indexed). + +Example: + +.... +curl -XPOST http://ip:port/mailboxes/{mailboxId}?task=reIndex&messagesPerSecond=200&mode=fixOutdated +.... + +Response codes: + +* 201: Success. Corresponding task id is returned. +* 400: Error in the request. Details can be found in the reported error. + +The scheduled task will have the following type `mailbox-reindexing` and +the following `additionalInformation`: + +.... +{ + "type":"mailbox-reindexing", + "runningOptions":{ + "messagesPerSecond":200, + "mode":"FIX_OUTDATED" + }, + "mailboxId":"{mailboxId}", + "successfullyReprocessedMailCount":18, + "failedReprocessedMailCount": 3, + "mailboxFailures": ["12"], + "messageFailures": [ + { + "mailboxId": "1", + "uids": [1, 36] + }] +} +.... + +Warning: During the re-indexing, the result of search operations might +be altered. + +Warning: Canceling this task should be considered unsafe as it will +leave the currently reIndexed mailbox as partially indexed. + +Warning: While we have been trying to reduce the inconsistency window to +a maximum (by keeping track of ongoing events), concurrent changes done +during the reIndexing might be ignored. + +== Administrating Messages + +=== ReIndexing a single mail by messageId + +.... +curl -XPOST http://ip:port/messages/{messageId}?task=reIndex +.... + +Will schedule a task for reIndexing a single email in all the mailboxes +containing it. + +Note that `messageId' path parameter needs to be a (implementation +dependent) valid messageId. + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +Response codes: + +* 201: Success. Corresponding task id is returned. +* 400: Error in the request. Details can be found in the reported error. + +The scheduled task will have the following type `messageId-reindexing` +and the following `additionalInformation`: + +.... +{ + "messageId":"18" +} +.... + +Warning: During the re-indexing, the result of search operations might +be altered. + +=== Fixing message inconsistencies + +This task is only available on top of Guice Cassandra products. + +.... +curl -XPOST /messages?task=SolveInconsistencies +.... + +Will schedule a task for fixing message inconsistencies created by the +message denormalization process. + +Messages are denormalized and stored in separated data tables in +Cassandra, so they can be accessed by their unique identifier or mailbox +identifier & local mailbox identifier through different protocols. + +Failure in the denormalization process will lead to inconsistencies, for +example: + +.... +BOB receives a message +The denormalization process fails +BOB can read the message via JMAP +BOB cannot read the message via IMAP + +BOB marks a message as SEEN +The denormalization process fails +The message is SEEN via JMAP +The message is UNSEEN via IMAP +.... + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +An admin can specify the concurrency that should be used when running +the task: + +* `messagesPerSecond` rate of messages to be processed per second. +Default is 100. + +This optional parameter must have a strictly positive integer as a value +and be passed as query parameter. + +An admin can also specify the reindexing mode it wants to use when +running the task: + +* `mode` the reindexing mode used. There are 2 modes for the moment: +** `rebuildAll` allows to rebuild all indexes. This is the default mode. +** `fixOutdated` will check for outdated indexed document and reindex +only those. + +This optional parameter must be passed as query parameter. + +It’s good to note as well that there is a limitation with the +`fixOutdated` mode. As we first collect metadata of stored messages to +compare them with the ones in the index, a failed `expunged` operation +might not be well corrected (as the message might not exist anymore but +still be indexed). + +Example: + +.... +curl -XPOST /messages?task=SolveInconsistencies&messagesPerSecond=200&mode=rebuildAll +.... + +Response codes: + +* 201: Success. Corresponding task id is returned. +* 400: Error in the request. Details can be found in the reported error. + +The scheduled task will have the following type +`solve-message-inconsistencies` and the following +`additionalInformation`: + +.... +{ + "type":"solve-message-inconsistencies", + "timestamp":"2007-12-03T10:15:30Z", + "processedImapUidEntries": 2, + "processedMessageIdEntries": 1, + "addedMessageIdEntries": 1, + "updatedMessageIdEntries": 0, + "removedMessageIdEntries": 1, + "runningOptions":{ + "messagesPerSecond": 200, + "mode":"REBUILD_ALL" + }, + "fixedInconsistencies": [ + { + "mailboxId": "551f0580-82fb-11ea-970e-f9c83d4cf8c2", + "messageId": "d2bee791-7e63-11ea-883c-95b84008f979", + "uid": 1 + }, + { + "mailboxId": "551f0580-82fb-11ea-970e-f9c83d4cf8c2", + "messageId": "d2bee792-7e63-11ea-883c-95b84008f979", + "uid": 2 + } + ], + "errors": [ + { + "mailboxId": "551f0580-82fb-11ea-970e-f9c83d4cf8c2", + "messageId": "ffffffff-7e63-11ea-883c-95b84008f979", + "uid": 3 + } + ] +} +.... + +User actions concurrent to the inconsistency fixing task could result in +concurrency issues. New inconsistencies could be created. + +However the source of truth will not be impacted, hence rerunning the +task will eventually fix all issues. + +This task could be run safely online and can be scheduled on a recurring +basis outside of peak traffic by an admin to ensure Cassandra message +consistency. + +== Administrating user mailboxes + +=== Creating a mailbox + +.... +curl -XPUT http://ip:port/users/{usernameToBeUsed}/mailboxes/{mailboxNameToBeCreated} +.... + +Resource name `usernameToBeUsed` should be an existing user Resource +name `mailboxNameToBeCreated` should not be empty, nor contain # & % * +characters. + +Response codes: + +* 204: The mailbox now exists on the server +* 400: Invalid mailbox name +* 404: The user name does not exist + +To create nested mailboxes, for instance a work mailbox inside the INBOX +mailbox, people should use the . separator. The sample query is: + +.... +curl -XDELETE http://ip:port/users/{usernameToBeUsed}/mailboxes/INBOX.work +.... + +=== Deleting a mailbox and its children + +.... +curl -XDELETE http://ip:port/users/{usernameToBeUsed}/mailboxes/{mailboxNameToBeDeleted} +.... + +Resource name `usernameToBeUsed` should be an existing user Resource +name `mailboxNameToBeDeleted` should not be empty + +Response codes: + +* 204: The mailbox now does not exist on the server +* 400: Invalid mailbox name +* 404: The user name does not exist + +=== Testing existence of a mailbox + +.... +curl -XGET http://ip:port/users/{usernameToBeUsed}/mailboxes/{mailboxNameToBeTested} +.... + +Resource name `usernameToBeUsed` should be an existing user Resource +name `mailboxNameToBeTested` should not be empty + +Response codes: + +* 204: The mailbox exists +* 400: Invalid mailbox name +* 404: The user name does not exist, the mailbox does not exist + +=== Listing user mailboxes + +.... +curl -XGET http://ip:port/users/{usernameToBeUsed}/mailboxes +.... + +The answer looks like: + +.... +[{"mailboxName":"INBOX"},{"mailboxName":"outbox"}] +.... + +Resource name `usernameToBeUsed` should be an existing user + +Response codes: + +* 200: The mailboxes list was successfully retrieved +* 404: The user name does not exist + +=== Deleting user mailboxes + +.... +curl -XDELETE http://ip:port/users/{usernameToBeUsed}/mailboxes +.... + +Resource name `usernameToBeUsed` should be an existing user + +Response codes: + +* 204: The user do not have mailboxes anymore +* 404: The user name does not exist + +=== Exporting user mailboxes + +.... +curl -XPOST http://ip:port/users/{usernameToBeUsed}/mailboxes?action=export +.... + +Resource name `usernameToBeUsed` should be an existing user + +Response codes: + +* 201: Success. Corresponding task id is returned +* 404: The user name does not exist + +The scheduled task will have the following type `MailboxesExportTask` +and the following `additionalInformation`: + +.... +{ + "type":"MailboxesExportTask", + "timestamp":"2007-12-03T10:15:30Z", + "username": "user", + "stage": "STARTING" +} +.... + +=== ReIndexing a user mails + +.... +curl -XPOST http://ip:port/users/{usernameToBeUsed}/mailboxes?task=reIndex +.... + +Will schedule a task for reIndexing all the mails in ``u...@domain.com'' +mailboxes (encoded above). + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +An admin can specify the concurrency that should be used when running +the task: + +* `messagesPerSecond` rate at which messages should be processed per +second. Default is 50. + +This optional parameter must have a strictly positive integer as a value +and be passed as query parameter. + +An admin can also specify the reindexing mode it wants to use when +running the task: + +* `mode` the reindexing mode used. There are 2 modes for the moment: +** `rebuildAll` allows to rebuild all indexes. This is the default mode. +** `fixOutdated` will check for outdated indexed document and reindex +only those. + +This optional parameter must be passed as query parameter. + +It’s good to note as well that there is a limitation with the +`fixOutdated` mode. As we first collect metadata of stored messages to +compare them with the ones in the index, a failed `expunged` operation +might not be well corrected (as the message might not exist anymore but +still be indexed). + +Example: + +.... +curl -XPOST http://ip:port/users/{usernameToBeUsed}/mailboxes?task=reIndex&messagesPerSecond=200&mode=fixOutdated +.... + +Response codes: + +* 201: Success. Corresponding task id is returned. +* 400: Error in the request. Details can be found in the reported error. + +The scheduled task will have the following type `user-reindexing` and +the following `additionalInformation`: + +.... +{ + "type":"user-reindexing", + "runningOptions":{ + "messagesPerSecond":200, + "mode":"FIX_OUTDATED" + }, + "user":"u...@domain.com", + "successfullyReprocessedMailCount":18, + "failedReprocessedMailCount": 3, + "mailboxFailures": ["12", "23" ], + "messageFailures": [ + { + "mailboxId": "1", + "uids": [1, 36] + }] +} +.... + +Warning: During the re-indexing, the result of search operations might +be altered. + +Warning: Canceling this task should be considered unsafe as it will +leave the currently reIndexed mailbox as partially indexed. + +Warning: While we have been trying to reduce the inconsistency window to +a maximum (by keeping track of ongoing events), concurrent changes done +during the reIndexing might be ignored. + +=== Recomputing User JMAP fast message view projection + +This action is only available for backends supporting JMAP protocol. + +Message fast view projection stores message properties expected to be +fast to fetch but are actually expensive to compute, in order for +GetMessages operation to be fast to execute for these properties. + +These projection items are asynchronously computed on mailbox events. + +You can force the full projection recomputation by calling the following +endpoint: + +.... +curl -XPOST /users/{usernameToBeUsed}/mailboxes?task=recomputeFastViewProjectionItems +.... + +Will schedule a task for recomputing the fast message view projection +for all mailboxes of `usernameToBeUsed`. + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +An admin can specify the concurrency that should be used when running +the task: + +* `messagesPerSecond` rate at which messages should be processed, per +second. Defaults to 10. + +This optional parameter must have a strictly positive integer as a value +and be passed as query parameters. + +Example: + +.... +curl -XPOST /mailboxes?task=recomputeFastViewProjectionItems&messagesPerSecond=20 +.... + +The scheduled task will have the following type +`RecomputeUserFastViewProjectionItemsTask` and the following +`additionalInformation`: + +.... +{ + "type":"RecomputeUserFastViewProjectionItemsTask", + "username": "{usernameToBeUsed}", + "processedMessageCount": 3, + "failedMessageCount": 1, + "runningOptions": { + "messagesPerSecond":20 + } +} +.... + +Response codes: + +* 201: Success. Corresponding task id is returned. +* 400: Error in the request. Details can be found in the reported error. +* 404: User not found. + +== Administrating quotas by users + +=== Getting the quota for a user + +.... +curl -XGET http://ip:port/quota/users/{usernameToBeUsed} +.... + +Resource name `usernameToBeUsed` should be an existing user + +The answer is the details of the quota of that user. + +.... +{ + "global": { + "count":252, + "size":242 + }, + "domain": { + "count":152, + "size":142 + }, + "user": { + "count":52, + "size":42 + }, + "computed": { + "count":52, + "size":42 + }, + "occupation": { + "size":13, + "count":21, + "ratio": { + "size":0.25, + "count":0.5, + "max":0.5 + } + } +} +.... + +* The `global` entry represent the quota limit allowed on this James +server. +* The `domain` entry represent the quota limit allowed for the user of +that domain. +* The `user` entry represent the quota limit allowed for this specific +user. +* The `computed` entry represent the quota limit applied for this user, +resolved from the upper values. +* The `occupation` entry represent the occupation of the quota for this +user. This includes used count and size as well as occupation ratio +(used / limit). + +Note that `quota` object can contain a fixed value, an empty value +(null) or an unlimited value (-1): + +.... +{"count":52,"size":42} + +{"count":null,"size":null} + +{"count":52,"size":-1} +.... + +Response codes: + +* 200: The user’s quota was successfully retrieved +* 404: The user does not exist + +=== Updating the quota for a user + +.... +curl -XPUT http://ip:port/quota/users/{usernameToBeUsed} +.... + +Resource name `usernameToBeUsed` should be an existing user + +The body can contain a fixed value, an empty value (null) or an +unlimited value (-1): + +.... +{"count":52,"size":42} + +{"count":null,"size":null} + +{"count":52,"size":-1} +.... + +Response codes: + +* 204: The quota has been updated +* 400: The body is not a positive integer neither an unlimited value +(-1). +* 404: The user does not exist + +=== Getting the quota count for a user + +.... +curl -XGET http://ip:port/quota/users/{usernameToBeUsed}/count +.... + +Resource name `usernameToBeUsed` should be an existing user + +The answer looks like: + +.... +52 +.... + +Response codes: + +* 200: The user’s quota was successfully retrieved +* 204: No quota count limit is defined at the user level for this user +* 404: The user does not exist + +=== Updating the quota count for a user + +.... +curl -XPUT http://ip:port/quota/users/{usernameToBeUsed}/count +.... + +Resource name `usernameToBeUsed` should be an existing user + +The body can contain a fixed value or an unlimited value (-1): + +.... +52 +.... + +Response codes: + +* 204: The quota has been updated +* 400: The body is not a positive integer neither an unlimited value +(-1). +* 404: The user does not exist + +=== Deleting the quota count for a user + +.... +curl -XDELETE http://ip:port/quota/users/{usernameToBeUsed}/count +.... + +Resource name `usernameToBeUsed` should be an existing user + +Response codes: + +* 204: The quota has been updated to unlimited value. +* 404: The user does not exist + +=== Getting the quota size for a user + +.... +curl -XGET http://ip:port/quota/users/{usernameToBeUsed}/size +.... + +Resource name `usernameToBeUsed` should be an existing user + +The answer looks like: + +.... +52 +.... + +Response codes: + +* 200: The user’s quota was successfully retrieved +* 204: No quota size limit is defined at the user level for this user +* 404: The user does not exist + +=== Updating the quota size for a user + +.... +curl -XPUT http://ip:port/quota/users/{usernameToBeUsed}/size +.... + +Resource name `usernameToBeUsed` should be an existing user + +The body can contain a fixed value or an unlimited value (-1): + +.... +52 +.... + +Response codes: + +* 204: The quota has been updated +* 400: The body is not a positive integer neither an unlimited value +(-1). +* 404: The user does not exist + +=== Deleting the quota size for a user + +.... +curl -XDELETE http://ip:port/quota/users/{usernameToBeUsed}/size +.... + +Resource name `usernameToBeUsed` should be an existing user + +Response codes: + +* 204: The quota has been updated to unlimited value. +* 404: The user does not exist + +=== Searching user by quota ratio + +.... +curl -XGET 'http://ip:port/quota/users?minOccupationRatio=0.8&maxOccupationRatio=0.99&limit=100&offset=200&domain=domain.com' +.... + +Will return: + +.... +[ + { + "username":"u...@domain.com", + "detail": { + "global": { + "count":252, + "size":242 + }, + "domain": { + "count":152, + "size":142 + }, + "user": { + "count":52, + "size":42 + }, + "computed": { + "count":52, + "size":42 + }, + "occupation": { + "size":48, + "count":21, + "ratio": { + "size":0.9230, + "count":0.5, + "max":0.9230 + } + } + } + }, + ... +] +.... + +Where: + +* *minOccupationRatio* is a query parameter determining the minimum +occupation ratio of users to be returned. +* *maxOccupationRatio* is a query parameter determining the maximum +occupation ratio of users to be returned. +* *domain* is a query parameter determining the domain of users to be +returned. +* *limit* is a query parameter determining the maximum number of users +to be returned. +* *offset* is a query parameter determining the number of users to skip. + +Please note that users are alphabetically ordered on username. + +The response is a list of usernames, with attached quota details as +defined link:#getting-the-quota-for-a-user[here]. + +Response codes: + +* 200: List of users had successfully been returned. +* 400: Validation issues with parameters + +=== Recomputing current quotas for users + +.... +curl -XPOST /quota/users?task=RecomputeCurrentQuotas +.... + +Will recompute current quotas (count and size) for all users stored in +James. + +James maintains per quota a projection for current quota count and size. +As with any projection, it can go out of sync, leading to inconsistent +results being returned to the client. + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +An admin can specify the concurrency that should be used when running +the task: + +* `usersPerSecond` rate at which users quotas should be reprocessed, per +second. Defaults to 1. + +This optional parameter must have a strictly positive integer as a value +and be passed as query parameters. + +Example: + +.... +curl -XPOST /quota/users?task=RecomputeCurrentQuotas&usersPerSecond=20 +.... + +The scheduled task will have the following type +`recompute-current-quotas` and the following `additionalInformation`: + +.... +{ + "type":"recompute-current-quotas", + "processedQuotaRoots": 3, + "failedQuotaRoots": ["#private&bob@localhost"], + "runningOptions": { + "usersPerSecond":20 + } +} +.... + +*WARNING*: this task do not take into account concurrent modifications +upon a single current quota re-computation. Rerunning the task will +_eventually_ provide the consistent result. + +== Administrating quotas by domains + +=== Getting the quota for a domain + +.... +curl -XGET http://ip:port/quota/domains/{domainToBeUsed} +.... + +Resource name `domainToBeUsed` should be an existing domain. For +example: + +.... +curl -XGET http://ip:port/quota/domains/james.org +.... + +The answer will detail the default quota applied to users belonging to +that domain: + +.... +{ + "global": { + "count":252, + "size":null + }, + "domain": { + "count":null, + "size":142 + }, + "computed": { + "count":252, + "size":142 + } +} +.... + +* The `global` entry represents the quota limit defined on this James +server by default. +* The `domain` entry represents the quota limit allowed for the user of +that domain by default. +* The `computed` entry represents the quota limit applied for the users +of that domain, by default, resolved from the upper values. + +Note that `quota` object can contain a fixed value, an empty value +(null) or an unlimited value (-1): + +.... +{"count":52,"size":42} + +{"count":null,"size":null} + +{"count":52,"size":-1} +.... + +Response codes: + +* 200: The domain’s quota was successfully retrieved +* 404: The domain does not exist +* 405: Domain Quota configuration not supported when virtual hosting is +desactivated. + +=== Updating the quota for a domain + +.... +curl -XPUT http://ip:port/quota/domains/{domainToBeUsed} +.... + +Resource name `domainToBeUsed` should be an existing domain. + +The body can contain a fixed value, an empty value (null) or an +unlimited value (-1): + +.... +{"count":52,"size":42} + +{"count":null,"size":null} + +{"count":52,"size":-1} +.... + +Response codes: + +* 204: The quota has been updated +* 400: The body is not a positive integer neither an unlimited value +(-1). +* 404: The domain does not exist +* 405: Domain Quota configuration not supported when virtual hosting is +desactivated. + +=== Getting the quota count for a domain + +.... +curl -XGET http://ip:port/quota/domains/{domainToBeUsed}/count +.... + +Resource name `domainToBeUsed` should be an existing domain. + +The answer looks like: + +.... +52 +.... + +Response codes: + +* 200: The domain’s quota was successfully retrieved +* 204: No quota count limit is defined at the domain level for this +domain +* 404: The domain does not exist +* 405: Domain Quota configuration not supported when virtual hosting is +desactivated. + +=== Updating the quota count for a domain + +.... +curl -XPUT http://ip:port/quota/domains/{domainToBeUsed}/count +.... + +Resource name `domainToBeUsed` should be an existing domain. + +The body can contain a fixed value or an unlimited value (-1): + +.... +52 +.... + +Response codes: + +* 204: The quota has been updated +* 400: The body is not a positive integer neither an unlimited value +(-1). +* 404: The domain does not exist +* 405: Domain Quota configuration not supported when virtual hosting is +desactivated. + +=== Deleting the quota count for a domain + +.... +curl -XDELETE http://ip:port/quota/domains/{domainToBeUsed}/count +.... + +Resource name `domainToBeUsed` should be an existing domain. + +Response codes: + +* 204: The quota has been updated to unlimited value. +* 404: The domain does not exist +* 405: Domain Quota configuration not supported when virtual hosting is +desactivated. + +=== Getting the quota size for a domain + +.... +curl -XGET http://ip:port/quota/domains/{domainToBeUsed}/size +.... + +Resource name `domainToBeUsed` should be an existing domain. + +The answer looks like: + +.... +52 +.... + +Response codes: + +* 200: The domain’s quota was successfully retrieved +* 204: No quota size limit is defined at the domain level for this +domain +* 404: The domain does not exist +* 405: Domain Quota configuration not supported when virtual hosting is +desactivated. + +=== Updating the quota size for a domain + +.... +curl -XPUT http://ip:port/quota/domains/{domainToBeUsed}/size +.... + +Resource name `domainToBeUsed` should be an existing domain. + +The body can contain a fixed value or an unlimited value (-1): + +.... +52 +.... + +Response codes: + +* 204: The quota has been updated +* 400: The body is not a positive integer neither an unlimited value +(-1). +* 404: The domain does not exist +* 405: Domain Quota configuration not supported when virtual hosting is +desactivated. + +=== Deleting the quota size for a domain + +.... +curl -XDELETE http://ip:port/quota/domains/{domainToBeUsed}/size +.... + +Resource name `domainToBeUsed` should be an existing domain. + +Response codes: + +* 204: The quota has been updated to unlimited value. +* 404: The domain does not exist + +== Administrating global quotas + +=== Getting the global quota + +.... +curl -XGET http://ip:port/quota +.... + +The answer is the details of the global quota. + +.... +{ + "count":252, + "size":242 +} +.... + +Note that `quota` object can contain a fixed value, an empty value +(null) or an unlimited value (-1): + +.... +{"count":52,"size":42} + +{"count":null,"size":null} + +{"count":52,"size":-1} +.... + +Response codes: + +* 200: The quota was successfully retrieved + +=== Updating global quota + +.... +curl -XPUT http://ip:port/quota +.... + +The body can contain a fixed value, an empty value (null) or an +unlimited value (-1): + +.... +{"count":52,"size":42} + +{"count":null,"size":null} + +{"count":52,"size":-1} +.... + +Response codes: + +* 204: The quota has been updated +* 400: The body is not a positive integer neither an unlimited value +(-1). + +=== Getting the global quota count + +.... +curl -XGET http://ip:port/quota/count +.... + +Resource name usernameToBeUsed should be an existing user + +The answer looks like: + +.... +52 +.... + +Response codes: + +* 200: The quota was successfully retrieved +* 204: No quota count limit is defined at the global level + +=== Updating the global quota count + +.... +curl -XPUT http://ip:port/quota/count +.... + +The body can contain a fixed value or an unlimited value (-1): + +.... +52 +.... + +Response codes: + +* 204: The quota has been updated +* 400: The body is not a positive integer neither an unlimited value +(-1). + +=== Deleting the global quota count + +.... +curl -XDELETE http://ip:port/quota/count +.... + +Response codes: + +* 204: The quota has been updated to unlimited value. + +=== Getting the global quota size + +.... +curl -XGET http://ip:port/quota/size +.... + +The answer looks like: + +.... +52 +.... + +Response codes: + +* 200: The quota was successfully retrieved +* 204: No quota size limit is defined at the global level + +=== Updating the global quota size + +.... +curl -XPUT http://ip:port/quota/size +.... + +The body can contain a fixed value or an unlimited value (-1): + +.... +52 +.... + +Response codes: + +* 204: The quota has been updated +* 400: The body is not a positive integer neither an unlimited value +(-1). + +=== Deleting the global quota size + +.... +curl -XDELETE http://ip:port/quota/size +.... + +Response codes: + +* 204: The quota has been updated to unlimited value. + +== Cassandra Schema upgrades + +Cassandra upgrades implies the creation of a new table. Thus restarting +James is needed, as new tables are created on restart. + +Once done, we ship code that tries to read from new tables, and if not +possible backs up to old tables. You can thus safely run without running +additional migrations. + +On the fly migration can be enabled. However, one might want to force +the migration in a controlled fashion, and update automatically current +schema version used (assess in the database old versions is no more +used, as the corresponding tables are empty). Note that this process is +safe: we ensure the service is not running concurrently on this James +instance, that it does not bump version upon partial failures, that race +condition in version upgrades will be idempotent, etc… + +These schema updates can be triggered by webadmin using the Cassandra +backend. + +Note that currently the progress can be tracked by logs. + +* link:#Retrieving_current_Cassandra_schema_version[Retrieving current +Cassandra schema version] +* link:#Retrieving_latest_available_Cassandra_schema_version[Retrieving +latest available Cassandra schema version] +* link:#Upgrading_to_a_specific_version[Upgrading to a specific version] +* link:#Upgrading_to_the_latest_version[Upgrading to the latest version] + +=== Retrieving current Cassandra schema version + +.... +curl -XGET http://ip:port/cassandra/version +.... + +Will return: + +.... +{"version": 2} +.... + +Where the number corresponds to the current schema version of the +database you are using. + +Response codes: + +* 200: Success + +=== Retrieving latest available Cassandra schema version + +.... +curl -XGET http://ip:port/cassandra/version/latest +.... + +Will return: + +.... +{"version": 3} +.... + +Where the number corresponds to the latest available schema version of +the database you are using. This means you can be migrating to this +schema version. + +Response codes: + +* 200: Success + +=== Upgrading to a specific version + +.... +curl -XPOST http://ip:port/cassandra/version/upgrade -d '3' +.... + +Will schedule the run of the migrations you need to reach schema version +3. + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +Response codes: + +* 200: Success. The scheduled task `taskId` is returned. +* 400: The version is invalid. The version should be a strictly positive +number. +* 410: Error while planning this migration. This resource is gone away. +Reason is mentionned in the body. + +Note that several calls to this endpoint will be run in a sequential +pattern. + +If the server restarts during the migration, the migration is silently +aborted. + +The scheduled task will have the following type `cassandra-migration` +and the following `additionalInformation`: + +.... +{"targetVersion":3} +.... + +=== Upgrading to the latest version + +.... +curl -XPOST http://ip:port/cassandra/version/upgrade/latest +.... + +Will schedule the run of the migrations you need to reach the latest +schema version. + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +Response codes: + +* 200: Success. The scheduled task `taskId` is returned. +* 410: Error while planning this migration. This resource is gone away. +Reason is mentionned in the body. + +Note that several calls to this endpoint will be run in a sequential +pattern. + +If the server restarts during the migration, the migration is silently +aborted. + +The scheduled task will have the following type `cassandra-migration` +and the following `additionalInformation`: + +.... +{"toVersion":2} +.... + +== Correcting ghost mailbox + +This is a temporary workaround for the *Ghost mailbox* bug encountered +using the Cassandra backend, as described in MAILBOX-322. + +You can use the mailbox merging feature in order to merge the old +``ghosted'' mailbox with the new one. + +.... +curl -XPOST http://ip:port/cassandra/mailbox/merging \ + -d '{"mergeOrigin":"{id1}", "mergeDestination":"{id2}"}' \ + -H "Content-Type: application/json" +.... + +Will scedule a task for : + +* Delete references to `id1` mailbox +* Move it’s messages into `id2` mailbox +* Union the rights of both mailboxes + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +Response codes: + +* 201: Task generation succeeded. Corresponding task id is returned. +* 400: Unable to parse the body. + +The scheduled task will have the following type `mailbox-merging` and +the following `additionalInformation`: + +.... +{ + "oldMailboxId":"5641376-02ed-47bd-bcc7-76ff6262d92a", + "newMailboxId":"4555159-52ae-895f-ccb7-586a4412fb50", + "totalMessageCount": 1, + "messageMovedCount": 1, + "messageFailedCount": 0 +} +.... + +== Creating address group + +You can use *webadmin* to define address groups. + +When a specific email is sent to the group mail address, every group +member will receive it. + +Note that the group mail address is virtual: it does not correspond to +an existing user. + +This feature uses (TODO) +link:/server/config-recipientrewritetable.html[Recipients rewrite table] +and requires the +https://github.com/apache/james-project/blob/master/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java[RecipientRewriteTable +mailet] to be configured. + +Note that email addresses are restricted to ASCII character set. Mail +addresses not matching this criteria will be rejected. + +=== Listing groups + +.... +curl -XGET http://ip:port/address/groups +.... + +Will return the groups as a list of JSON Strings representing mail +addresses. For instance: + +.... +["gro...@domain.com", "gro...@domain.com"] +.... + +Response codes: + +* 200: Success + +=== Listing members of a group + +.... +curl -XGET http://ip:port/address/groups/gr...@domain.com +.... + +Will return the group members as a list of JSON Strings representing +mail addresses. For instance: + +.... +["memb...@domain.com", "memb...@domain.com"] +.... + +Response codes: + +* 200: Success +* 400: Group structure is not valid +* 404: The group does not exist + +=== Adding a group member + +.... +curl -XPUT http://ip:port/address/groups/gr...@domain.com/mem...@domain.com +.... + +Will add mem...@domain.com to gr...@domain.com, creating the group if +needed + +Response codes: + +* 204: Success +* 400: Group structure or member is not valid +* 400: Domain in the source is not managed by the DomainList +* 409: Requested group address is already used for another purpose + +=== Removing a group member + +.... +curl -XDELETE http://ip:port/address/groups/gr...@domain.com/mem...@domain.com +.... + +Will remove mem...@domain.com from gr...@domain.com, removing the group +if group is empty after deletion + +Response codes: + +* 204: Success +* 400: Group structure or member is not valid + +== Creating address forwards + +You can use *webadmin* to define address forwards. + +When a specific email is sent to the base mail address, every forward +destination addresses will receive it. + +Please note that the base address can be optionaly part of the forward +destination. In that case, the base recipient also receive a copy of the +mail. Otherwise he is ommitted. + +Forwards can be defined for existing users. It then defers from +``groups''. + +This feature uses (TODO) +link:/server/config-recipientrewritetable.html[Recipients rewrite table] +and requires the +https://github.com/apache/james-project/blob/master/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java[RecipientRewriteTable +mailet] to be configured. + +Note that email addresses are restricted to ASCII character set. Mail +addresses not matching this criteria will be rejected. + +=== Listing Forwards + +.... +curl -XGET http://ip:port/address/forwards +.... + +Will return the users having forwards configured as a list of JSON +Strings representing mail addresses. For instance: + +.... +["us...@domain.com", "us...@domain.com"] +.... + +Response codes: + +* 200: Success + +=== Listing destinations in a forward + +.... +curl -XGET http://ip:port/address/forwards/u...@domain.com +.... + +Will return the destination addresses of this forward as a list of JSON +Strings representing mail addresses. For instance: + +.... +[ + {"mailAddress":"destinati...@domain.com"}, + {"mailAddress":"destinati...@domain.com"} +] +.... + +Response codes: + +* 200: Success +* 400: Forward structure is not valid +* 404: The given user don’t have forwards or does not exist + +=== Adding a new destination to a forward + +.... +curl -XPUT http://ip:port/address/forwards/u...@domain.com/targets/destinat...@domain.com +.... + +Will add destinat...@domain.com to u...@domain.com, creating the forward +if needed + +Response codes: + +* 204: Success +* 400: Forward structure or member is not valid +* 400: Domain in the source is not managed by the DomainList +* 404: Requested forward address does not match an existing user + +=== Removing a destination of a forward + +.... +curl -XDELETE http://ip:port/address/forwards/u...@domain.com/targets/destinat...@domain.com +.... + +Will remove destinat...@domain.com from u...@domain.com, removing the +forward if forward is empty after deletion + +Response codes: + +* 204: Success +* 400: Forward structure or member is not valid + +== Creating address aliases + +You can use *webadmin* to define aliases for an user. + +When a specific email is sent to the alias address, the destination +address of the alias will receive it. + +Aliases can be defined for existing users. + +This feature uses (TODO) +link:/server/config-recipientrewritetable.html[Recipients rewrite table] +and requires the +https://github.com/apache/james-project/blob/master/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java[RecipientRewriteTable +mailet] to be configured. + +Note that email addresses are restricted to ASCII character set. Mail +addresses not matching this criteria will be rejected. + +=== Listing users with aliases + +.... +curl -XGET http://ip:port/address/aliases +.... + +Will return the users having aliases configured as a list of JSON +Strings representing mail addresses. For instance: + +.... +["us...@domain.com", "us...@domain.com"] +.... + +Response codes: + +* 200: Success + +=== Listing alias sources of an user + +.... +curl -XGET http://ip:port/address/aliases/u...@domain.com +.... + +Will return the aliases of this user as a list of JSON Strings +representing mail addresses. For instance: + +.... +[ + {"source":"ali...@domain.com"}, + {"source":"ali...@domain.com"} +] +.... + +Response codes: + +* 200: Success +* 400: Alias structure is not valid + +=== Adding a new alias to an user + +.... +curl -XPUT http://ip:port/address/aliases/u...@domain.com/sources/al...@domain.com +.... + +Will add al...@domain.com to u...@domain.com, creating the alias if +needed + +Response codes: + +* 204: OK +* 400: Alias structure or member is not valid +* 400: The alias source exists as an user already +* 400: Source and destination can’t be the same! +* 400: Domain in the destination or source is not managed by the +DomainList + +=== Removing an alias of an user + +.... +curl -XDELETE http://ip:port/address/aliases/u...@domain.com/sources/al...@domain.com +.... + +Will remove al...@domain.com from u...@domain.com, removing the alias if +needed + +Response codes: + +* 204: OK +* 400: Alias structure or member is not valid + +== Creating domain mappings + +You can use *webadmin* to define domain mappings. + +Given a configured source (from) domain and a destination (to) domain, +when an email is sent to an address belonging to the source domain, then +the domain part of this address is overwritten, the destination domain +is then used. A source (from) domain can have many destination (to) +domains. + +For example: with a source domain `james.apache.org` maps to two +destination domains `james.org` and `apache-james.org`, when a mail is +sent to `ad...@james.apache.org`, then it will be routed to +`ad...@james.org` and `ad...@apache-james.org` + +This feature uses (TODO) +link:/server/config-recipientrewritetable.html[Recipients rewrite table] +and requires the +https://github.com/apache/james-project/blob/master/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java[RecipientRewriteTable +mailet] to be configured. + +Note that email addresses are restricted to ASCII character set. Mail +addresses not matching this criteria will be rejected. + +=== Listing all domain mappings + +.... +curl -XGET http://ip:port/domainMappings +.... + +Will return all configured domain mappings + +.... +{ + "firstSource.org" : ["firstDestination.com", "secondDestination.net"], + "secondSource.com" : ["thirdDestination.com", "fourthDestination.net"], +} +.... + +Response codes: + +* 200: OK + +=== Listing all destination domains for a source domain + +.... +curl -XGET http://ip:port/domainMappings/sourceDomain.tld +.... + +With `sourceDomain.tld` as the value passed to `fromDomain` resource +name, the API will return all destination domains configured to that +domain + +.... +["firstDestination.com", "secondDestination.com"] +.... + +Response codes: + +* 200: OK +* 400: The `fromDomain` resource name is invalid +* 404: The `fromDomain` resource name is not found + +=== Adding a domain mapping + +.... +curl -XPUT http://ip:port/domainMappings/sourceDomain.tld +.... + +Body: + +.... +destination.tld +.... + +With `sourceDomain.tld` as the value passed to `fromDomain` resource +name, the API will add a destination domain specified in the body to +that domain + +Response codes: + +* 204: OK +* 400: The `fromDomain` resource name is invalid +* 400: The destination domain specified in the body is invalid + +=== Removing a domain mapping + +.... +curl -XDELETE http://ip:port/domainMappings/sourceDomain.tld +.... + +Body: + +.... +destination.tld +.... + +With `sourceDomain.tld` as the value passed to `fromDomain` resource +name, the API will remove a destination domain specified in the body +mapped to that domain + +Response codes: + +* 204: OK +* 400: The `fromDomain` resource name is invalid +* 400: The destination domain specified in the body is invalid + +== Creating regex mapping + +You can use *webadmin* to create regex mappings. + +A regex mapping contains a mapping source and a Java Regular Expression +(regex) in String as the mapping value. Everytime, if a mail containing +a recipient matched with the mapping source, then that mail will be +re-routed to a new recipient address which is re written by the regex. + +This feature uses (TODO) +link:/server/config-recipientrewritetable.html[Recipients rewrite table] +and requires the +https://github.com/apache/james-project/blob/master/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java[RecipientRewriteTable +API] to be configured. + +=== Adding a regex mapping + +.... +POST /mappings/regex/mappingSource/targets/regex +.... + +Where: + +* the `mappingSource` is the path parameter represents for the Regex +Mapping mapping source +* the `regex` is the path parameter represents for the Regex Mapping +regex + +The route will add a regex mapping made from `mappingSource` and `regex` +to RecipientRewriteTable. + +Example: + +.... +curl -XPOST http://ip:port/mappings/regex/ja...@domain.tld/targets/james@.*:james-int...@james.org +.... + +Response codes: + +* 204: Mapping added successfully. +* 400: Invalid `mappingSource` path parameter. +* 400: Invalid `regex` path parameter. + +=== Removing a regex mapping + +.... +DELETE /mappings/regex/{mappingSource}/targets/{regex} +.... + +Where: + +* the `mappingSource` is the path parameter representing the Regex +Mapping mapping source +* the `regex` is the path parameter representing the Regex Mapping regex + +The route will remove the regex mapping made from `regex` from the +mapping source `mappingSource` to RecipientRewriteTable. + +Example: + +.... +curl -XDELETE http://ip:port/mappings/regex/ja...@domain.tld/targets/[O_O]:james-int...@james.org +.... + +Response codes: + +* 204: Mapping deleted successfully. +* 400: Invalid `mappingSource` path parameter. +* 400: Invalid `regex` path parameter. + +== Address Mappings + +You can use *webadmin* to define address mappings. + +When a specific email is sent to the base mail address, every +destination addresses will receive it. + +This feature uses (TODO) +link:/server/config-recipientrewritetable.html[Recipients rewrite table] +and requires the +https://github.com/apache/james-project/blob/master/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java[RecipientRewriteTable +mailet] to be configured. + +Note that email addresses are restricted to ASCII character set. Mail +addresses not matching this criteria will be rejected. + +Please use address mappings with caution, as it’s not a typed address. +If you know the type of your address (forward, alias, domain, group, +etc), prefer using the corresponding routes to those types. + +Here are the following actions available on address mappings: + +=== Add an address mapping + +.... +curl -XPOST http://ip:port/mappings/address/{mappingSource}/targets/{destinationAddress} +.... + +Add an address mapping to the Recipients rewrite table +Mapping source is the value of \{mappingSource} Mapping destination is +the value of \{destinationAddress} Type of mapping destination is +Address + +Response codes: + +* 204: Action successfully performed +* 400: Invalid parameters + +=== Remove an address mapping + +.... +curl -XDELETE http://ip:port/mappings/address/{mappingSource}/targets/{destinationAddress} +.... + +* Remove an address mapping from the Recipients rewrite table +* Mapping source is the value of `mappingSource` +* Mapping destination is the value of `destinationAddress` +* Type of mapping destination is Address + +Response codes: + +* 204: Action successfully performed +* 400: Invalid parameters + +== List all mappings + +.... +curl -XGET http://ip:port/mappings +.... + +Get all mappings from the +link:/server/config-recipientrewritetable.html[Recipients rewrite table] +Supported mapping types are the following: + +(TODO) + +* link:#Creating_address_aliases[Alias] +* link:#Address_Mappings[Address] +* link:#Creating_address_domain[Domain] +* Error +* link:#Creating_address_forwards[Forward] +* link:#Creating_address_group[Group] +* Regex + +Response body: + +.... +{ + "al...@domain.tld": [ + { + "type": "Alias", + "mapping": "u...@domain.tld" + }, + { + "type": "Group", + "mapping": "group-u...@domain.tld" + } + ], + "aliasdomain.tld": [ + { + "type": "Domain", + "mapping": "realdomain.tld" + } + ], + "gr...@domain.tld": [ + { + "type": "Address", + "mapping": "u...@domain.tld" + } + ] +} +.... + +Response code: + +* 200: OK + +== User Mappings + +=== Listing User Mappings + +This endpoint allows receiving all mappings of a corresponding user. + +.... +curl -XGET http://ip:port/mappings/user/{userAddress} +.... + +Return all mappings of a user where: + +* `userAddress`: is the selected user + +Response body: + +.... +[ + { + "type": "Address", + "mapping": "user...@domain.tld" + }, + { + "type": "Alias", + "mapping": "aliasuser...@domain.tld" + }, + { + "type": "Group", + "mapping": "group...@domain.tld" + } +] +.... + +Response codes: + +* 200: OK +* 400: Invalid parameter value + +== Administrating mail repositories + +=== Create a mail repository + +.... +curl -XPUT http://ip:port/mailRepositories/{encodedPathOfTheRepository}?protocol={someProtocol} +.... + +Resource name `encodedPathOfTheRepository` should be the resource path +of the created mail repository. Example: + +.... +curl -XPUT http://ip:port/mailRepositories/mailRepo?protocol=file +.... + +Response codes: + +* 204: The repository is created + +=== Listing mail repositories + +.... +curl -XGET http://ip:port/mailRepositories +.... + +The answer looks like: + +.... +[ + { + "repository": "var/mail/error/", + "path": "var%2Fmail%2Ferror%2F" + }, + { + "repository": "var/mail/relay-denied/", + "path": "var%2Fmail%2Frelay-denied%2F" + }, + { + "repository": "var/mail/spam/", + "path": "var%2Fmail%2Fspam%2F" + }, + { + "repository": "var/mail/address-error/", + "path": "var%2Fmail%2Faddress-error%2F" + } +] +.... + +You can use `id`, the encoded URL of the repository, to access it in +later requests. + +Response codes: + +* 200: The list of mail repositories + +=== Getting additional information for a mail repository + +.... +curl -XGET http://ip:port/mailRepositories/{encodedPathOfTheRepository} +.... + +Resource name `encodedPathOfTheRepository` should be the resource path +of an existing mail repository. Example: + +.... +curl -XGET http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F +.... + +The answer looks like: + +.... +{ + "repository": "var/mail/error/", + "path": "mail%2Ferror%2F", + "size": 243 +} +.... + +Response codes: + +* 200: Additonnal information for that repository +* 404: This repository can not be found + +=== Listing mails contained in a mail repository + +.... +curl -XGET http://ip:port/mailRepositories/{encodedPathOfTheRepository}/mails +.... + +Resource name `encodedPathOfTheRepository` should be the resource path +of an existing mail repository. Example: + +.... +curl -XGET http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails +.... + +The answer will contains all mailKey contained in that repository. + +.... +[ + "mail-key-1", + "mail-key-2", + "mail-key-3" +] +.... + +Note that this can be used to read mail details. + +You can pass additional URL parameters to this call in order to limit +the output: - A limit: no more elements than the specified limit will be +returned. This needs to be strictly positive. If no value is specified, +no limit will be applied. - An offset: allow to skip elements. This +needs to be positive. Default value is zero. + +Example: + +.... +curl -XGET 'http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails?limit=100&offset=500' +.... + +Response codes: + +* 200: The list of mail keys contained in that mail repository +* 400: Invalid parameters +* 404: This repository can not be found + +=== Reading/downloading a mail details + +.... +curl -XGET http://ip:port/mailRepositories/{encodedPathOfTheRepository}/mails/mailKey +.... + +Resource name `encodedPathOfTheRepository` should be the resource path +of an existing mail repository. Resource name `mailKey` should be the +key of a mail stored in that repository. Example: + +.... +curl -XGET http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails/mail-key-1 +.... + +If the Accept header in the request is ``application/json'', then the +response looks like: + +.... +{ + "name": "mail-key-1", + "sender": "sen...@domain.com", + "recipients": ["recipie...@domain.com", "recipie...@domain.com"], + "state": "address-error", + "error": "A small message explaining what happened to that mail...", + "remoteHost": "111.222.333.444", + "remoteAddr": "127.0.0.1", + "lastUpdated": null +} +.... + +If the Accept header in the request is ``message/rfc822'', then the +response will be the _eml_ file itself. + +Additional query parameter `additionalFields` add the existing +information to the response for the supported values (only work with +``application/json'' Accept header): + +* attributes +* headers +* textBody +* htmlBody +* messageSize +* perRecipientsHeaders + +.... +curl -XGET http://ip:port/mailRepositories/file%3A%2F%2Fvar%2Fmail%2Ferror%2F/mails/mail-key-1?additionalFields=attributes,headers,textBody,htmlBody,messageSize,perRecipientsHeaders +.... + +Give the following kind of response: + +.... +{ + "name": "mail-key-1", + "sender": "sen...@domain.com", + "recipients": ["recipie...@domain.com", "recipie...@domain.com"], + "state": "address-error", + "error": "A small message explaining what happened to that mail...", + "remoteHost": "111.222.333.444", + "remoteAddr": "127.0.0.1", + "lastUpdated": null, + "attributes": { + "name2": "value2", + "name1": "value1" + }, + "perRecipientsHeaders": { + "third@party": { + "headerName1": [ + "value1", + "value2" + ], + "headerName2": [ + "value3", + "value4" + ] + } + }, + "headers": { + "headerName4": [ + "value6", + "value7" + ], + "headerName3": [ + "value5", + "value8" + ] + }, + "textBody": "My body!!", + "htmlBody": "My <em>body</em>!!", + "messageSize": 42424242 +} +.... + +Response codes: + +* 200: Details of the mail +* 404: This repository or mail can not be found + +=== Removing a mail from a mail repository + +.... +curl -XDELETE http://ip:port/mailRepositories/{encodedPathOfTheRepository}/mails/mailKey +.... + +Resource name `encodedPathOfTheRepository` should be the resource path +of an existing mail repository. Resource name `mailKey` should be the +key of a mail stored in that repository. Example: + +.... +curl -XDELETE http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails/mail-key-1 +.... + +Response codes: + +* 204: This mail no longer exists in this repository +* 404: This repository can not be found + +=== Removing all mails from a mail repository + +.... +curl -XDELETE http://ip:port/mailRepositories/{encodedPathOfTheRepository}/mails +.... + +Resource name `encodedPathOfTheRepository` should be the resource path +of an existing mail repository. Example: + +.... +curl -XDELETE http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails +.... + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +Response codes: + +* 201: Task generation succeeded. Corresponding task id is returned. +* 404: Could not find that mail repository + +The scheduled task will have the following type `clear-mail-repository` +and the following `additionalInformation`: + +.... +{ + "mailRepositoryPath":"var/mail/error/", + "initialCount": 243, + "remainingCount": 17 +} +.... + +=== Reprocessing mails from a mail repository + +Sometime, you want to re-process emails stored in a mail repository. For +instance, you can make a configuration error, or there can be a James +bug that makes processing of some mails fail. Those mail will be stored +in a mail repository. Once you solved the problem, you can reprocess +them. + +To reprocess mails from a repository: + +.... +curl -XPATCH http://ip:port/mailRepositories/{encodedPathOfTheRepository}/mails?action=reprocess +.... + +Resource name `encodedPathOfTheRepository` should be the resource path +of an existing mail repository. Example: + +For instance: + +.... +curl -XPATCH http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails?action=reprocess +.... + +Additional query parameters are supported: - `queue` allows you to +target the mail queue you want to enqueue the mails in. Defaults to +`spool`. - `processor` allows you to overwrite the state of the +reprocessing mails, and thus select the processors they will start their +processing in. Defaults to the `state` field of each processed email. + +For instance: + +.... +curl -XPATCH 'http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails?action=reprocess&processor=transport&queue=spool' +.... + +Note that the `action` query parameter is compulsary and can only take +value `reprocess`. + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +Response codes: + +* 201: Task generation succeeded. Corresponding task id is returned. +* 404: Could not find that mail repository + +The scheduled task will have the following type `reprocessing-all` and +the following `additionalInformation`: + +.... +{ + "mailRepositoryPath":"var/mail/error/", + "targetQueue":"spool", + "targetProcessor":"transport", + "initialCount": 243, + "remainingCount": 17 +} +.... + +=== Reprocessing a specific mail from a mail repository + +To reprocess a specific mail from a mail repository: + +.... +curl -XPATCH http://ip:port/mailRepositories/{encodedPathOfTheRepository}/mails/mailKey?action=reprocess +.... + +Resource name `encodedPathOfTheRepository` should be the resource id of +an existing mail repository. Resource name `mailKey` should be the key +of a mail stored in that repository. Example: + +For instance: + +.... +curl -XPATCH http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails/name1?action=reprocess +.... + +Additional query parameters are supported: - `queue` allows you to +target the mail queue you want to enqueue the mails in. Defaults to +`spool`. - `processor` allows you to overwrite the state of the +reprocessing mails, and thus select the processors they will start their +processing in. Defaults to the `state` field of each processed email. + +While `processor` being an optional parameter, not specifying it will +result reprocessing the mails in their current state +(https://james.apache.org/server/feature-mailetcontainer.html#Processors[see +documentation about processors and state]). Consequently, only few cases +will give a different result, definitively storing them out of the mail +repository. + +For instance: + +.... +curl -XPATCH 'http://ip:port/mailRepositories/var%2Fmail%2Ferror%2F/mails/name1?action=reprocess&processor=transport&queue=spool' +.... + +Note that the `action` query parameter is compulsary and can only take +value `reprocess`. + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +Response codes: + +* 201: Task generation succeeded. Corresponding task id is returned. +* 404: Could not find that mail repository + +The scheduled task will have the following type `reprocessing-one` and +the following `additionalInformation`: + +.... +{ + "mailRepositoryPath":"var/mail/error/", + "targetQueue":"spool", + "targetProcessor":"transport", + "mailKey":"name1" +} +.... + +== Administrating mail queues + +=== Listing mail queues + +.... +curl -XGET http://ip:port/mailQueues +.... + +The answer looks like: + +.... +["outgoing","spool"] +.... + +Response codes: + +* 200: The list of mail queues + +=== Getting a mail queue details + +.... +curl -XGET http://ip:port/mailQueues/{mailQueueName} +.... + +Resource name `mailQueueName` is the name of a mail queue, this command +will return the details of the given mail queue. For instance: + +.... +{"name":"outgoing","size":0} +.... + +Response codes: + +* 200: Success +* 400: Mail queue is not valid +* 404: The mail queue does not exist + +=== Listing the mails of a mail queue + +.... +curl -XGET http://ip:port/mailQueues/{mailQueueName}/mails +.... + +Additional URL query parameters: + +* `limit`: Maximum number of mails returned in a single call. Only +strictly positive integer values are accepted. Example: + +.... +curl -XGET http://ip:port/mailQueues/{mailQueueName}/mails?limit=100 +.... + +The answer looks like: + +.... +[{ + "name": "Mail1516976156284-8b3093b9-eebf-4c40-9c26-1450f4fcdc3c-to-test.com", + "sender": "u...@james.linagora.com", + "recipients": ["some...@test.com"], + "nextDelivery": "1969-12-31T23:59:59.999Z" +}] +.... + +Response codes: + +* 200: Success +* 400: Mail queue is not valid or limit is invalid +* 404: The mail queue does not exist + +=== Deleting mails from a mail queue + +.... +curl -XDELETE http://ip:port/mailQueues/{mailQueueName}/mails?sender=senderMailAddress +.... + +This request should have exactly one query parameter from the following +list: + +* sender: which is a mail address (i.e. sen...@james.org) +* name: which is a string +* recipient: which is a mail address (i.e. recipi...@james.org) + +The mails from the given mail queue matching the query parameter will be +deleted. + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +Response codes: + +* 201: Task generation succeeded. Corresponding task id is returned. +* 400: Invalid request +* 404: The mail queue does not exist + +The scheduled task will have the following type +`delete-mails-from-mail-queue` and the following +`additionalInformation`: + +.... +{ + "queue":"outgoing", + "initialCount":10, + "remainingCount": 5, + "sender": "sen...@james.org", + "name": "Java Developer", + "recipient: "recipi...@james.org" +} +.... + +=== Clearing a mail queue + +.... +curl -XDELETE http://ip:port/mailQueues/{mailQueueName}/mails +.... + +All mails from the given mail queue will be deleted. + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +Response codes: + +* 201: Task generation succeeded. Corresponding task id is returned. +* 400: Invalid request +* 404: The mail queue does not exist + +The scheduled task will have the following type `clear-mail-queue` and +the following `additionalInformation`: + +.... +{ + "queue":"outgoing", + "initialCount":10, + "remainingCount": 0 +} +.... + +=== Flushing mails from a mail queue + +.... +curl -XPATCH http://ip:port/mailQueues/{mailQueueName}?delayed=true \ + -d '{"delayed": false}' \ + -H "Content-Type: application/json" +.... + +This request should have the query parameter _delayed_ set to _true_, in +order to indicate only delayed mails are affected. The payload should +set the `delayed` field to false inorder to remove the delay. This is +the only supported combination, and it performs a flush. + +The mails delayed in the given mail queue will be flushed. + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +Response codes: + +* 204: Success (No content) +* 400: Invalid request +* 404: The mail queue does not exist + +== Administrating DLP Configuration + +DLP (stands for Data Leak Prevention) is supported by James. A DLP +matcher will, on incoming emails, execute regular expressions on email +sender, recipients or content, in order to report suspicious emails to +an administrator. WebAdmin can be used to manage these DLP rules on a +per `senderDomain` basis. + +`senderDomain` is domain of the sender of incoming emails, for example: +`apache.org`, `james.org`,… Each `senderDomain` correspond to a distinct +DLP configuration. + +=== List DLP configuration by sender domain + +Retrieve a DLP configuration for corresponding `senderDomain`, a +configuration contains list of configuration items + +.... +curl -XGET http://ip:port/dlp/rules/{senderDomain} +.... + +Response codes: + +* 200: A list of dlp configuration items is returned +* 400: Invalid `senderDomain` or payload in request +* 404: The domain does not exist. + +This is an example of returned body. The rules field is a list of rules +as described below. + +.... +{"rules : [ + { + "id": "1", + "expression": "james.org", + "explanation": "Find senders or recipients containing james[any char]org", + "targetsSender": true, + "targetsRecipients": true, + "targetsContent": false + }, + { + "id": "2", + "expression": "Find senders containing apache[any char]org", + "explanation": "apache.org", + "targetsSender": true, + "targetsRecipients": false, + "targetsContent": false + } +]} +.... + +=== Store DLP configuration by sender domain + +Store a DLP configuration for corresponding `senderDomain`, if any item +of DLP configuration in the request is stored before, it will not be +stored anymore + +.... +curl -XPUT http://ip:port/dlp/rules/{senderDomain} +.... + +The body can contain a list of DLP configuration items formed by those +fields: - `id`(String) is mandatory, unique identifier of the +configuration item - `expression`(String) is mandatory, regular +expression to match contents of targets - `explanation`(String) is +optional, description of the configuration item - +`targetsSender`(boolean) is optional and defaults to false. If true, +`expression` will be applied to Sender and to From headers of the mail - +`targetsContent`(boolean) is optional and defaults to false. If true, +`expression` will be applied to Subject headers and textual bodies +(text/plain and text/html) of the mail - `targetsRecipients`(boolean) is +optional and defaults to false. If true, `expression` will be applied to +recipients of the mail + +This is an example of returned body. The rules field is a list of rules +as described below. + +.... +{"rules": [ + { + "id": "1", + "expression": "james.org", + "explanation": "Find senders or recipients containing james[any char]org", + "targetsSender": true, + "targetsRecipients": true, + "targetsContent": false + }, + { + "id": "2", + "expression": "Find senders containing apache[any char]org", + "explanation": "apache.org", + "targetsSender": true, + "targetsRecipients": false, + "targetsContent": false + } +]} +.... + +Response codes: + +* 204: List of dlp configuration items is stored +* 400: Invalid `senderDomain` or payload in request +* 404: The domain does not exist. + +=== Remove DLP configuration by sender domain + +Remove a DLP configuration for corresponding `senderDomain` + +.... +curl -XDELETE http://ip:port/dlp/rules/{senderDomain} +.... + +Response codes: + +* 204: DLP configuration is removed +* 400: Invalid `senderDomain` or payload in request +* 404: The domain does not exist. + +=== Fetch a DLP configuration item by sender domain and rule id + +Retrieve a DLP configuration rule for corresponding `senderDomain` and a +`ruleId` + +.... +curl -XGET http://ip:port/dlp/rules/{senderDomain}/rules/{ruleId} +.... + +Response codes: + +* 200: A dlp configuration item is returned +* 400: Invalid `senderDomain` or payload in request +* 404: The domain and/or the rule does not exist. + +This is an example of returned body. + +.... +{ + "id": "1", + "expression": "james.org", + "explanation": "Find senders or recipients containing james[any char]org", + "targetsSender": true, + "targetsRecipients": true, + "targetsContent": false +} +.... + +== Administrating Sieve quotas + +Some limitations on space Users Sieve script can occupy can be +configured by default, and overridden by user. + +=== Retrieving global sieve quota + +This endpoints allows to retrieve the global Sieve quota, which will be +users default: + +.... +curl -XGET http://ip:port/sieve/quota/default +.... + +Will return the bytes count allowed by user per default on this server. + +.... +102400 +.... + +Response codes: + +* 200: Request is a success and the value is returned +* 204: No default quota is being configured + +=== Updating global sieve quota + +This endpoints allows to update the global Sieve quota, which will be +users default: + +.... +curl -XPUT http://ip:port/sieve/quota/default +.... + +With the body being the bytes count allowed by user per default on this +server. + +.... +102400 +.... + +Response codes: + +* 204: Operation succeeded +* 400: Invalid payload + +=== Removing global sieve quota + +This endpoints allows to remove the global Sieve quota. There will no +more be users default: + +.... +curl -XDELETE http://ip:port/sieve/quota/default +.... + +Response codes: + +* 204: Operation succeeded + +=== Retrieving user sieve quota + +This endpoints allows to retrieve the Sieve quota of a user, which will +be this users quota: + +.... +curl -XGET http://ip:port/sieve/quota/users/u...@domain.com +.... + +Will return the bytes count allowed for this user. + +.... +102400 +.... + +Response codes: + +* 200: Request is a success and the value is returned +* 204: No quota is being configured for this user + +=== Updating user sieve quota + +This endpoints allows to update the Sieve quota of a user, which will be +users default: + +.... +curl -XPUT http://ip:port/sieve/quota/users/u...@domain.com +.... + +With the body being the bytes count allowed for this user on this +server. + +.... +102400 +.... + +Response codes: + +* 204: Operation succeeded +* 400: Invalid payload + +=== Removing user sieve quota + +This endpoints allows to remove the Sieve quota of a user. There will no +more quota for this user: + +.... +curl -XDELETE http://ip:port/sieve/quota/users/u...@domain.com +.... + +Response codes: + +* 204: Operation succeeded + +== Event Dead Letter + +The EventBus allows to register `group listeners' that are called in a +distributed fashion. These group listeners enable the implementation of +some advanced mailbox manager feature like indexing, spam reporting, +quota management and the like. + +Upon exceptions, a bounded number of retries are performed (with +exponential backoff delays). If after those retries the listener is +still failing, then the event will be stored in the ``Event Dead +Letter''. This API allows diagnosing issues, as well as performing event +replay. + +=== Listing mailbox listener groups + +This endpoint allows discovering the list of mailbox listener groups. + +.... +curl -XGET http://ip:port/events/deadLetter/groups +.... + +Will return a list of group names that can be further used to interact +with the dead letter API: + +.... +["org.apache.james.mailbox.events.EventBusTestFixture$GroupA", "org.apache.james.mailbox.events.GenericGroup-abc"] +.... + +Response codes: + +* 200: Success. A list of group names is returned. + +=== Listing failed events + +This endpoint allows listing failed events for a given group: + +.... +curl -XGET http://ip:port/events/deadLetter/groups/org.apache.james.mailbox.events.EventBusTestFixture$GroupA +.... + +Will return a list of insertionIds: + +.... +["6e0dd59d-660e-4d9b-b22f-0354479f47b4", "58a8f59d-660e-4d9b-b22f-0354486322a2"] +.... + +Response codes: + +* 200: Success. A list of insertion ids is returned. +* 400: Invalid group name + +=== Getting event details + +.... +curl -XGET http://ip:port/events/deadLetter/groups/org.apache.james.mailbox.events.EventBusTestFixture$GroupA/6e0dd59d-660e-4d9b-b22f-0354479f47b4 +.... + +Will return the full JSON associated with this event. + +Response codes: + +* 200: Success. A JSON representing this event is returned. +* 400: Invalid group name or `insertionId` +* 404: No event with this `insertionId` + +=== Deleting an event + +.... +curl -XDELETE http://ip:port/events/deadLetter/groups/org.apache.james.mailbox.events.EventBusTestFixture$GroupA/6e0dd59d-660e-4d9b-b22f-0354479f47b4 +.... + +Will delete this event. + +Response codes: + +* 204: Success +* 400: Invalid group name or `insertionId` + +=== Redeliver all events + +.... +curl -XPOST http://ip:port/events/deadLetter?action=redeliver +.... + +Will create a task that will attempt to redeliver all events stored in +``Event Dead Letter''. If successful, redelivered events will then be +removed from ``Dead Letter''. + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +Response codes: + +* 201: the taskId of the created task +* 400: Invalid action argument + +=== Redeliver group events + +.... +curl -XPOST http://ip:port/events/deadLetter/groups/org.apache.james.mailbox.events.EventBusTestFixture$GroupA +.... + +Will create a task that will attempt to redeliver all events of a +particular group stored in ``Event Dead Letter''. If successful, +redelivered events will then be removed from ``Dead Letter''. + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +Response codes: + +* 201: the taskId of the created task +* 400: Invalid group name or action argument + +=== Redeliver a single event + +.... +curl -XPOST http://ip:port/events/deadLetter/groups/org.apache.james.mailbox.events.EventBusTestFixture$GroupA/6e0dd59d-660e-4d9b-b22f-0354479f47b4?action=reDeliver +.... + +Will create a task that will attempt to redeliver a single event of a +particular group stored in ``Event Dead Letter''. If successful, +redelivered event will then be removed from ``Dead Letter''. + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +Response codes: + +* 201: the taskId of the created task +* 400: Invalid group name, insertion id or action argument +* 404: No event with this insertionId + +== Deleted Messages Vault + +The `Deleted Message Vault plugin' allows you to keep users deleted +messages during a given retention time. This set of routes allow you to +_restore_ users deleted messages or export them in an archive. + +To move deleted messages in the vault, you need to specifically +configure the DeletedMessageVault PreDeletionHook. + +=== Restore Deleted Messages + +Deleted messages of a specific user can be restored by calling the +following endpoint: + +.... +curl -XPOST http://ip:port/deletedMessages/users/usertorest...@domain.ext?action=restore + +{ + "combinator": "and", + "criteria": [ + { + "fieldName": "subject", + "operator": "containsIgnoreCase", + "value": "Apache James" + }, + { + "fieldName": "deliveryDate", + "operator": "beforeOrEquals", + "value": "2014-10-30T14:12:00Z" + }, + { + "fieldName": "deletionDate", + "operator": "afterOrEquals", + "value": "2015-10-20T09:08:00Z" + }, + { + "fieldName": "recipients"," + "operator": "contains"," + "value": "recipi...@james.org" + }, + { + "fieldName": "hasAttachment", + "operator": "equals", + "value": "false" + }, + { + "fieldName": "sender", + "operator": "equals", + "value": "sen...@apache.org" + }, + { + "fieldName": "originMailboxes", + "operator": "contains", + "value": "02874f7c-d10e-102f-acda-0015176f7922" + } + ] +}; +.... + +The requested Json body is made from a list of criterion objects which +have the following structure: + +.... +{ + "fieldName": "supportedFieldName", + "operator": "supportedOperator", + "value": "A plain string representing the matching value of the corresponding field" +} +.... + +Deleted Messages which are matched with the *all* criterion in the query +body will be restored. Here are a list of supported fieldName for the +restoring: + +* subject: represents for deleted message `subject` field matching. +Supports below string operators: +** contains +** containsIgnoreCase +** equals +** equalsIgnoreCase +* deliveryDate: represents for deleted message `deliveryDate` field +matching. Tested value should follow the right date time with zone +offset format (ISO-8601) like `2008-09-15T15:53:00+05:00` or +`2008-09-15T15:53:00Z` Supports below date time operators: +** beforeOrEquals: is the deleted message’s `deliveryDate` before or +equals the time of tested value. +** afterOrEquals: is the deleted message’s `deliveryDate` after or +equals the time of tested value +* deletionDate: represents for deleted message `deletionDate` field +matching. Tested value & Supports operators: similar to `deliveryDate` +* sender: represents for deleted message `sender` field matching. Tested +value should be a valid mail address. Supports mail address operator: +** equals: does the tested sender equal to the sender of the tested +deleted message ? + +* recipients: represents for deleted message `recipients` field +matching. Tested value should be a valid mail address. Supports list +mail address operator: +** contains: does the tested deleted message’s recipients contain tested +recipient ? +* hasAttachment: represents for deleted message `hasAttachment` field +matching. Tested value could be `false` or `true`. Supports boolean +operator: +** equals: does the tested deleted message’s hasAttachment property +equal to the tested hasAttachment value? +* originMailboxes: represents for deleted message `originMailboxes` +field matching. Tested value is a string serialized of mailbox id. +Supports list mailbox id operators: +** contains: does the tested deleted message’s originMailbox ids contain +tested mailbox id ? + +Messages in the Deleted Messages Vault of a specified user that are +matched with Query Json Object in the body will be appended to his +`Restored-Messages' mailbox, which will be created if needed. + +*Note*: + +* Query parameter `action` is required and should have the value +`restore` to represent the restoring feature. Otherwise, a bad request +response will be returned +* Query parameter `action` is case sensitive +* fieldName & operator passed to the routes are case sensitive +* Currently, we only support query combinator `and` value, otherwise, +requests will be rejected +* If you only want to restore by only one criterion, the json body could +be simplified to a single criterion: + +.... +{ + "fieldName": "subject", + "operator": "containsIgnoreCase", + "value": "Apache James" +} +.... + +* For restoring all deleted messages, passing a query json with an empty +criterion list to represent `matching all deleted messages`: + +.... +{ + "combinator": "and", + "criteria": [] +} +.... + +*Warning*: Current web-admin uses `US` locale as the default. Therefore, +there might be some conflicts when using String `containsIgnoreCase` +comparators to apply on the String data of other special locales stored +in the Vault. More details at +https://issues.apache.org/jira/browse/MAILBOX-384[JIRA] + +Response code: + +* 201: Task for restoring deleted has been created +* 400: Bad request: +** action query param is not present +** action query param is not a valid action +** user parameter is invalid +** can not parse the JSON body +** Json query object contains unsupported operator, fieldName +** Json query object values violate parsing rules +* 404: User not found + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +The scheduled task will have the following type +`deleted-messages-restore` and the following `additionalInformation`: + +.... +{ + "successfulRestoreCount": 47, + "errorRestoreCount": 0, + "user": "usertorest...@domain.ext" +} +.... + +while: + +* successfulRestoreCount: number of restored messages +* errorRestoreCount: number of messages that failed to restore +* user: owner of deleted messages need to restore + +=== Export Deleted Messages + +Retrieve deleted messages matched with requested query from an user then +share the content to a targeted mail address (exportTo) + +.... +curl -XPOST 'http://ip:port/deletedMessages/users/userexportf...@domain.ext?action=export&exportTo=userreceiv...@domain.ext' + +BODY: is the json query has the same structure with Restore Deleted Messages section +.... + +*Note*: Json query passing into the body follows the same rules & +restrictions like in link:#Restore_deleted_messages[Restore Deleted +Messages] + +Response code: + +* 201: Task for exporting has been created +* 400: Bad request: +** exportTo query param is not present +** exportTo query param is not a valid mail address +** action query param is not present +** action query param is not a valid action +** user parameter is invalid +** can not parse the JSON body +** Json query object contains unsupported operator, fieldName +** Json query object values violate parsing rules +* 404: User not found + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +The scheduled task will have the following type +`deleted-messages-export` and the following `additionalInformation`: + +.... +{ + "userExportFrom": "usertorest...@domain.ext", + "exportTo": "userreceiv...@domain.ext", + "totalExportedMessages": 1432 +} +.... + +while: + + * userExportFrom: export deleted messages from this user + * exportTo: content of deleted messages have been shared to this mail +address + * totalExportedMessages: number of deleted messages match with +json query, then being shared to sharee. + +=== Purge Deleted Messages + +You can overwrite `retentionPeriod' configuration in +`deletedMessageVault' configuration file or use the default value of 1 +year. + +Purge all deleted messages older than the configured `retentionPeriod' + +.... +curl -XDELETE http://ip:port/deletedMessages?scope=expired +.... + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +Response code: + +* 201: Task for purging has been created +* 400: Bad request: +** action query param is not present +** action query param is not a valid action + +You may want to call this endpoint on a regular basis. + +=== Permanently Remove Deleted Message + +Delete a Deleted Message with `MessageId` + +.... +curl -XDELETE http://ip:port/deletedMessages/users/u...@domain.ext/messages/3294a976-ce63-491e-bd52-1b6f465ed7a2 +.... + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +Response code: + +* 201: Task for deleting message has been created +* 400: Bad request: +** user parameter is invalid +** messageId parameter is invalid +* 404: User not found + +The scheduled task will have the following type +`deleted-messages-delete` and the following `additionalInformation`: + +.... + { + "userName": "u...@domain.ext", + "messageId": "3294a976-ce63-491e-bd52-1b6f465ed7a2" + } +.... + +while: - user: delete deleted messages from this user - deleteMessageId: +messageId of deleted messages will be delete + +== Task management + +Some webadmin features schedules tasks. The task management API allow to +monitor and manage the execution of the following tasks. + +Note that the `taskId` used in the following APIs is returned by other +WebAdmin APIs scheduling tasks. + +=== Getting a task details + +.... +curl -XGET http://ip:port/tasks/3294a976-ce63-491e-bd52-1b6f465ed7a2 +.... + +An Execution Report will be returned: + +.... +{ + "submitDate": "2017-12-27T15:15:24.805+0700", + "startedDate": "2017-12-27T15:15:24.809+0700", + "completedDate": "2017-12-27T15:15:24.815+0700", + "cancelledDate": null, + "failedDate": null, + "taskId": "3294a976-ce63-491e-bd52-1b6f465ed7a2", + "additionalInformation": {}, + "status": "completed", + "type": "type-of-the-task" +} +.... + +Note that: + +* `status` can have the value: +** `waiting`: The task is scheduled but its execution did not start yet +** `inProgress`: The task is currently executed +** `cancelled`: The task had been cancelled +** `completed`: The task execution is finished, and this execution is a +success +** `failed`: The task execution is finished, and this execution is a +failure +* `additionalInformation` is a task specific object giving additional +information and context about that task. The structure of this +`additionalInformation` field is provided along the specific task +submission endpoint. + +Response codes: + +* 200: The specific task was found and the execution report exposed +above is returned +* 400: Invalid task ID +* 404: Task ID was not found + +=== Awaiting a task + +One can await the end of a task, then receive it’s final execution +report. + +That feature is especially usefull for testing purpose but still can +serve real-life scenari. + +.... +curl -XGET http://ip:port/tasks/3294a976-ce63-491e-bd52-1b6f465ed7a2/await?timeout=duration +.... + +An Execution Report will be returned. + +`timeout` is optional. By default it is set to 365 days (the maximum +value). The expected value is expressed in the following format: +`Nunit`. `N` should be strictly positive. `unit` could be either in the +short form (`s`, `m`, `h`, etc.), or in the long form (`day`, `week`, +`month`, etc.). + +Examples: + +* `30s` +* `5m` +* `7d` +* `1y` + +Response codes: + +* 200: The specific task was found and the execution report exposed +above is returned +* 400: Invalid task ID or invalid timeout +* 404: Task ID was not found +* 408: The timeout has been reached + +=== Cancelling a task + +You can cancel a task by calling: + +.... +curl -XDELETE http://ip:port/tasks/3294a976-ce63-491e-bd52-1b6f465ed7a2 +.... + +Response codes: + +* 204: Task had been cancelled +* 400: Invalid task ID + +=== Listing tasks + +A list of all tasks can be retrieved: + +.... +curl -XGET http://ip:port/tasks +.... + +Will return a list of Execution reports + +One can filter the above results by status. For example: + +.... +curl -XGET http://ip:port/tasks?status=inProgress +.... + +Will return a list of Execution reports that are currently in progress. + +Response codes: + +* 200: A list of corresponding tasks is returned +* 400: Invalid status value + +=== Endpoints returning a task + +Many endpoints do generate a task. + +Example: + +.... +curl -XPOST /endpoint?action={action} +.... + +The response to these requests will be the scheduled `taskId` : + +.... +{"taskId":"5641376-02ed-47bd-bcc7-76ff6262d92a"} +.... + +Positionned headers: + +* Location header indicates the location of the resource associated with +the scheduled task. Example: + +.... +Location: /tasks/3294a976-ce63-491e-bd52-1b6f465ed7a2 +.... + +Response codes: + +* 201: Task generation succeeded. Corresponding task id is returned. +* Other response codes might be returned depending on the endpoint + +The additional information returned depends on the scheduled task type +and is documented in the endpoint documentation. + +== Cassandra extra operations + +Some webadmin features to manage some extra operations on Cassandra +tables, like solving inconsistencies on projection tables. Such +inconsistencies can be for example created by a fail of the DAO to add a +mapping into +’mappings_sources`, while it was successful regarding the`rrt` table. + +=== Operations on mappings sources + +You can do a series of action on `mappings_sources` projection table : + +.... +curl -XPOST /cassandra/mappings?action={action} +.... + +Will return the taskId corresponding to the related task. Actions +supported so far are : + +* SolveInconsistencies : cleans up first all the mappings in +`mappings_sources` index and then repopulate it correctly. In the +meantime, listing sources of a mapping might create temporary +inconsistencies during the process. + +For example : + +.... +curl -XPOST /cassandra/mappings?action=SolveInconsistencies +.... + +link:#_endpoints_returning_a_task[More details about endpoints returning +a task]. + +Response codes : + +* 201: the taskId of the created task +* 400: Invalid action argument for performing operation on mappings data \ No newline at end of file diff --git a/docs/modules/servers/pages/distributed/run-docker.adoc b/docs/modules/servers/pages/distributed/run-docker.adoc index d3fd897..2646ae1 100644 --- a/docs/modules/servers/pages/distributed/run-docker.adoc +++ b/docs/modules/servers/pages/distributed/run-docker.adoc @@ -1,6 +1,36 @@ = Run with docker -== Requirements +== Running via docker-compose + + +Requirements: docker & docker-compose installed. + +When you try James this way, you will use the most current state of James. +It will be configured to run with Cassandra & ElasticSearch. +All those three components will be started with a single command. + +You can retrieve the docker-compose file : + + $ wget https://raw.githubusercontent.com/apache/james-project/master/dockerfiles/run/docker-compose.yml + +Then, you just have to start the services: + + $ docker-compose up + +Wait a few seconds in order to have all those services start up. You will see the following log when James is available: +james | Started : true + +A default domain, james.local, has been created. You can see this by running: + + $ docker exec james java -jar /root/james-cli.jar -h 127.0.0.1 -p 9999 listdomains + +James will respond to IMAP port 143 and SMTP port 25. +You have to create users before playing with james. You may also want to create other domains. +Follow the 'Useful commands' section for more information about James CLI. + +== Run with docker + +=== Requirements Built artifacts should be in ./dockerfiles/run/guice/cassandra-rabbitmq/destination folder for cassandra. If you haven't already: @@ -10,7 +40,7 @@ If you haven't already: -v $PWD/dockerfiles/run/guice/cassandra-rabbitmq/destination:/cassandra-rabbitmq/destination \ -t james/project -s HEAD -== Running +=== Running You need a running *cassandra* in docker. To achieve this run: @@ -62,7 +92,7 @@ If you want to pass additional options to the underlying java command, you can c To have log file accessible on a volume, add *-v $PWD/logs:/logs* option to the above command line, where *$PWD/logs* is your local directory to put files in. -== Instrumentation +=== Instrumentation You can use Glowroot to instrumentalize James. The provided guice docker files allow a simple way to do it. In order to activate Glowroot you need to run the container with the environment variable _GLOWROOT_ACTIVATED_ set to _true_ and to expose the glowroot instrumentation ui port. @@ -76,7 +106,7 @@ See the https://github.com/glowroot/glowroot/wiki/Agent-Installation-(with-Embed Or by mapping the 4000 port to the IP of the desired network interface, for example `-p 127.0.0.1:4000:4000`. -== Handling attachment indexing +=== Handling attachment indexing You can handle attachment text extraction before indexing in ElasticSearch. This makes attachments searchable. To enable this: --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org