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, ); } } }