321 lines
9.5 KiB
PHP
321 lines
9.5 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Civi\Mailinglistsync;
|
|
|
|
use Civi\Mailinglistsync\Exceptions\MailinglistException;
|
|
|
|
/**
|
|
* The MailingListApi class provides methods to interact with the mlmmj and
|
|
* Dovecot APIs.
|
|
*/
|
|
class MailingListApi {
|
|
|
|
// Use the singleton trait
|
|
use Singleton;
|
|
|
|
protected string $mlmmjUrl;
|
|
|
|
protected string $mlmmjToken;
|
|
|
|
protected int $mlmmjPort;
|
|
|
|
protected string $dovecotUrl;
|
|
|
|
protected string $dovecotToken;
|
|
|
|
protected int $dovecotPort;
|
|
|
|
protected function __construct() {
|
|
$this->mlmmjUrl = MailingListSettings::get('mlmmj_host');
|
|
$this->mlmmjToken = MailingListSettings::get('mlmmj_token');
|
|
$this->mlmmjPort = MailingListSettings::get('mlmmj_port');
|
|
$this->dovecotUrl = MailingListSettings::get('dovecot_host');
|
|
$this->dovecotToken = MailingListSettings::get('dovecot_token');
|
|
$this->dovecotPort = MailingListSettings::get('dovecot_port');
|
|
}
|
|
|
|
/**
|
|
* Prepares a curl handle for the mlmmj API.
|
|
*
|
|
* @param null $path
|
|
*
|
|
* @return \CurlHandle
|
|
*/
|
|
private function prepareMlmmjCurl($path = NULL): \CurlHandle {
|
|
// Build URL
|
|
$url = $path
|
|
? "{$this->mlmmjUrl}api/{$path}"
|
|
: "{$this->mlmmjUrl}api/";
|
|
|
|
// Set up curl handle
|
|
$curl = curl_init();
|
|
|
|
// Set options
|
|
curl_setopt_array($curl, [
|
|
CURLOPT_URL => $url,
|
|
CURLOPT_PORT => $this->mlmmjPort,
|
|
CURLOPT_RETURNTRANSFER => TRUE,
|
|
CURLOPT_HTTPHEADER => [
|
|
'X-MLMMJADMIN-API-AUTH-TOKEN: ' . $this->mlmmjToken,
|
|
],
|
|
]);
|
|
|
|
return $curl;
|
|
}
|
|
|
|
/**
|
|
* Prepares a curl handle for the Dovecot API.
|
|
*
|
|
* @param $path
|
|
*
|
|
* @return \CurlHandle
|
|
*/
|
|
private function prepareDovecotCurl($path = NULL) {
|
|
// Build URL
|
|
$url = $path
|
|
? "{$this->dovecotUrl}/{$path}"
|
|
: "{$this->dovecotUrl}";
|
|
|
|
// Set up curl handle
|
|
$curl = curl_init();
|
|
|
|
// Set options
|
|
curl_setopt_array($curl, [
|
|
CURLOPT_URL => $url,
|
|
CURLOPT_PORT => $this->dovecotPort,
|
|
CURLOPT_RETURNTRANSFER => TRUE,
|
|
CURLOPT_HTTPHEADER => [
|
|
'Authorization: Bearer ' . $this->dovecotToken,
|
|
]
|
|
]);
|
|
|
|
return $curl;
|
|
}
|
|
|
|
/**
|
|
* Get a mailing list from mlmmj.
|
|
*
|
|
* @param string $mailinglistEmail
|
|
*
|
|
* @return array
|
|
* @throws \Civi\Mailinglistsync\Exceptions\MailinglistException
|
|
*/
|
|
function getMailingList(string $mailinglistEmail): array {
|
|
$curl = $this->prepareMlmmjCurl($mailinglistEmail);
|
|
$result = json_decode(curl_exec($curl), TRUE);
|
|
|
|
// Check result
|
|
if (curl_getinfo($curl, CURLINFO_HTTP_CODE) !== 200) {
|
|
throw new MailinglistException(
|
|
"Could not get mailinglist '$mailinglistEmail'",
|
|
MailinglistException::ERROR_CODE_GET_MAILINGLIST_FAILED,
|
|
);
|
|
}
|
|
if ($result['_success'] === FALSE) {
|
|
// Return empty array if the account does not exist yet
|
|
if ($result['_msg'] === 'NO_SUCH_ACCOUNT') {
|
|
return [];
|
|
}
|
|
// Throw exception if the request failed
|
|
else {
|
|
throw new MailinglistException(
|
|
"Could not get mailinglist '$mailinglistEmail'",
|
|
MailinglistException::ERROR_CODE_GET_MAILINGLIST_FAILED,
|
|
);
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Get the subscribers from mlmmj.
|
|
*
|
|
* @param string $mailinglistEmail
|
|
*
|
|
* @return array
|
|
*
|
|
* @throws \Civi\Mailinglistsync\Exceptions\MailinglistException
|
|
*/
|
|
function getMailingListSubscribers(string $mailinglistEmail): array {
|
|
$curl = $this->prepareMlmmjCurl($mailinglistEmail . '/subscribers');
|
|
$result = json_decode(curl_exec($curl), TRUE);
|
|
|
|
// Check result
|
|
if (!$result['_success']) {
|
|
throw new MailinglistException(
|
|
"Could not get subscribers for mailinglist '$mailinglistEmail'",
|
|
MailinglistException::ERROR_CODE_GET_RECIPIENTS_FAILED,
|
|
);
|
|
}
|
|
|
|
return array_map(function($subscriber) {
|
|
return $subscriber['mail'];
|
|
}, $result['_data']);
|
|
}
|
|
|
|
/**
|
|
* Create the email address via dovecot and the mailing list via mlmmj.
|
|
*
|
|
* @param string $mailinglistEmail
|
|
* @param array $options
|
|
*
|
|
* @return void
|
|
*
|
|
* @throws \Civi\Mailinglistsync\Exceptions\MailinglistException
|
|
*/
|
|
function createMailingList(string $mailinglistEmail, array $options): void {
|
|
// Create email address via Dovecot API
|
|
$username = explode('@', $mailinglistEmail)[0];
|
|
$dovecotCurl = $this->prepareDovecotCurl('list/' . $username);
|
|
curl_setopt($dovecotCurl, CURLOPT_CUSTOMREQUEST, 'PUT');
|
|
$dovecutResult = json_decode(curl_exec($dovecotCurl), TRUE);
|
|
|
|
// Check dovecot result (ignore 409 for already existing email addresses)
|
|
$statusCode = curl_getinfo($dovecotCurl, CURLINFO_HTTP_CODE);
|
|
if ($statusCode !== 201 && $statusCode !== 409) {
|
|
$message = "Could not create email address for '$mailinglistEmail'";
|
|
if (!empty($dovecutResult['Message'])) {
|
|
$message .= ': ' . $dovecutResult['Message'];
|
|
}
|
|
throw new MailinglistException(
|
|
$message,
|
|
MailinglistException::ERROR_CODE_DOVECOT_CREATE_EMAIL_ADDRESS_FAILED,
|
|
);
|
|
}
|
|
|
|
// Create mailing list via mlmmj API
|
|
$mlmmjCurl = $this->prepareMlmmjCurl($mailinglistEmail);
|
|
curl_setopt($mlmmjCurl, CURLOPT_CUSTOMREQUEST, 'POST');
|
|
curl_setopt($mlmmjCurl, CURLOPT_POSTFIELDS, http_build_query($options));
|
|
$mlmmjResult = json_decode(curl_exec($mlmmjCurl), TRUE);
|
|
|
|
// Check mlmmj result
|
|
if (!is_array($mlmmjResult) || $mlmmjResult['_success'] !== TRUE) {
|
|
$message = "Could not create mailinglist '$mailinglistEmail'";
|
|
if (!empty($mlmmjResult['_msg'])) {
|
|
$message .= ': ' . $mlmmjResult['_msg'];
|
|
}
|
|
throw new MailinglistException(
|
|
$message,
|
|
MailinglistException::ERROR_CODE_CREATE_MAILING_LIST_FAILED,
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update a mailing list.
|
|
*
|
|
* @param string $mailinglistEmail
|
|
* @param array $options
|
|
*
|
|
* @return void
|
|
* @throws \Civi\Mailinglistsync\Exceptions\MailinglistException
|
|
*/
|
|
function updateMailingList(string $mailinglistEmail, array $options): void {
|
|
$curl = $this->prepareMlmmjCurl($mailinglistEmail);
|
|
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT');
|
|
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($options));
|
|
$result = json_decode(curl_exec($curl), TRUE);
|
|
|
|
// Check result
|
|
if (!empty($result['_success']) && $result['_success'] !== TRUE) {
|
|
$message = "Could not update mailinglist '$mailinglistEmail'";
|
|
if (!empty($result['_msg'])) {
|
|
$message .= ': ' . $result['_msg'];
|
|
}
|
|
throw new MailinglistException(
|
|
$message,
|
|
MailinglistException::ERROR_CODE_UPDATE_MAILING_LIST_FAILED
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update the subscribers of a mailing list.
|
|
*
|
|
* @param string $mailinglistEmail
|
|
* @param ?array $recipientsToAdd
|
|
* @param ?array $recipientsToRemove
|
|
*
|
|
* @return void
|
|
* @throws \Civi\Mailinglistsync\Exceptions\MailinglistException
|
|
*/
|
|
function updateSubscribers(
|
|
string $mailinglistEmail,
|
|
array $recipientsToAdd = NULL,
|
|
array $recipientsToRemove = NULL,
|
|
): void {
|
|
$query = [];
|
|
if ($recipientsToAdd) {
|
|
$query['add_subscribers'] = implode(',', $recipientsToAdd);
|
|
}
|
|
if ($recipientsToRemove) {
|
|
$query['remove_subscribers'] = implode(',', $recipientsToRemove);
|
|
}
|
|
|
|
$curl = $this->prepareMlmmjCurl($mailinglistEmail . '/subscribers');
|
|
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');
|
|
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($query));
|
|
$result = json_decode(curl_exec($curl), TRUE);
|
|
|
|
// Check result
|
|
if (!empty($result['_success']) && $result['_success'] !== TRUE) {
|
|
throw new MailinglistException(
|
|
"Could not update subscribers for mailinglist '$mailinglistEmail'",
|
|
MailinglistException::ERROR_CODE_UPDATE_SUBSCRIBERS_FAILED,
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete a mailing list and its email address.
|
|
*
|
|
* @throws \Civi\Mailinglistsync\Exceptions\MailinglistException
|
|
*/
|
|
function deleteMailingList(string $mailinglistEmail): void {
|
|
// Delete mlmmj mailing list
|
|
$mlmmjCurl = $this->prepareMlmmjCurl($mailinglistEmail);
|
|
curl_setopt($mlmmjCurl, CURLOPT_CUSTOMREQUEST, 'DELETE');
|
|
$mlmmjResult = json_decode(curl_exec($mlmmjCurl), TRUE);
|
|
|
|
// Check mlmmj result
|
|
if (!empty($mlmmjResult['_success']) && $mlmmjResult['_success'] !== TRUE) {
|
|
$message = "Could not delete mailinglist '$mailinglistEmail'";
|
|
if (!empty($mlmmjResult['_msg'])) {
|
|
$message .= ': ' . $mlmmjResult['_msg'];
|
|
}
|
|
throw new MailinglistException(
|
|
$message,
|
|
MailinglistException::ERROR_CODE_DELETE_MAILING_LIST_FAILED,
|
|
);
|
|
}
|
|
|
|
// Delete dovecot email address
|
|
$username = explode('@', $mailinglistEmail)[0];
|
|
$dovecotCurl = $this->prepareDovecotCurl('list/' . $username);
|
|
curl_setopt($dovecotCurl, CURLOPT_CUSTOMREQUEST, 'DELETE');
|
|
$dovecotResult = json_decode(curl_exec($dovecotCurl), TRUE);
|
|
|
|
// Check dovecot result (ignore 404 for non-existing email addresses)
|
|
$statusCode = curl_getinfo($dovecotCurl, CURLINFO_HTTP_CODE);
|
|
if ($statusCode === 404) {
|
|
$message = "Email '$mailinglistEmail' does not exist in Dovecot";
|
|
throw new MailinglistException(
|
|
$message,
|
|
MailinglistException::ERROR_CODE_DELETE_EMAIL_ADDRESS_FAILED,
|
|
);
|
|
}
|
|
if ($statusCode !== 200) {
|
|
$message = "Could not delete email address for '$mailinglistEmail'";
|
|
if (!empty($dovecotResult['Message'])) {
|
|
$message .= ': ' . $dovecotResult['Message'];
|
|
}
|
|
throw new MailinglistException(
|
|
$message,
|
|
MailinglistException::ERROR_CODE_DOVECOT_CREATE_EMAIL_ADDRESS_FAILED,
|
|
);
|
|
}
|
|
}
|
|
}
|