/**
  * Given a permission string or array, check for access requirements. 
  * if this is a permissions-challenged Joomla instance, don't enforce
  * CiviMailchimp-defined permissions.
  *
  * @param mixed $permissions The permission(s) to check as an array or string.
  *        See parent class for examples.
  * @return boolean
  */
 public static function check($permissions)
 {
     $permissions = (array) $permissions;
     if (!CRM_Core_Config::singleton()->userPermissionClass->isModulePermissionSupported()) {
         array_walk_recursive($permissions, function (&$v, $k) {
             if (array_key_exists($v, CRM_Mailchimp_Permission::getMailchimpPermissions())) {
                 $v = CRM_Core_Permission::ALWAYS_ALLOW_PERMISSION;
             }
         });
     }
     return parent::check($permissions);
 }
 /**
  * Validate and process the request.
  *
  * This is separated from the run() method for testing purposes.
  *
  * This method serves as a router to other methods named after the type of
  * webhook we're called with.
  *
  * Methods may return data for mailchimp, or may throw RuntimeException
  * objects, the error code of which will be used for the response.
  * So you can throw a `RuntimeException("Invalid webhook configuration", 500);`
  * to tell mailchimp the webhook failed, but you can equally throw a 
  * `RuntimeException("soft fail", 200)` which will not tell Mailchimp there
  * was any problem. Mailchimp retries if there was a problem.
  *
  * If an exception is thrown, it is logged. @todo where?
  *
  * @return array with two values: $response_code, $response_object.
  */
 public function processRequest($expected_key, $key, $request_data)
 {
     // Check CMS's permission for (presumably) anonymous users.
     if (CRM_Core_Config::singleton()->userPermissionClass->isModulePermissionSupported() && !CRM_Mailchimp_Permission::check('allow webhook posts')) {
         throw new RuntimeException("Missing allow webhook posts permission.", 500);
     }
     // Check the 2 keys exist and match.
     if (!$key || !$expected_key || $key != $expected_key) {
         throw new RuntimeException("Invalid security key.", 500);
     }
     if (empty($request_data['data']['list_id']) || empty($request_data['type']) || !in_array($request_data['type'], ['subscribe', 'unsubscribe', 'profile', 'upemail', 'cleaned'])) {
         // We are not programmed to respond to this type of request.
         // But maybe Mailchimp introduced something new, so we'll just say OK.
         throw new RuntimeException("Missing or invalid data in request: " . json_encode($request_data), 200);
     }
     $method = $request_data['type'];
     // Check list config at Mailchimp.
     $list_id = $request_data['data']['list_id'];
     $api = CRM_Mailchimp_Utils::getMailchimpApi();
     $result = $api->get("/lists/{$list_id}/webhooks")->data->webhooks;
     $url = CRM_Mailchimp_Utils::getWebhookUrl();
     // Find our webhook and check for a particularly silly configuration.
     foreach ($result as $webhook) {
         if ($webhook->url == $url) {
             if ($webhook->sources->api) {
                 // To continue could cause a nasty loop.
                 throw new RuntimeException("The list '{$list_id}' is not configured correctly at Mailchimp. It has the 'API' source set so processing this using the API could cause a loop.", 500);
             }
         }
     }
     // Disable post hooks. We're updating *from* Mailchimp so we don't want
     // to fire anything *at* Mailchimp.
     CRM_Mailchimp_Utils::$post_hook_enabled = FALSE;
     // Pretty much all the request methods use these:
     $this->sync = new CRM_Mailchimp_Sync($request_data['data']['list_id']);
     $this->request_data = $request_data['data'];
     // Call the appropriate handler method.
     CRM_Mailchimp_Utils::checkDebug("Webhook: {$method} with request data: " . json_encode($request_data));
     $this->{$method}();
     // re-set the post hooks.
     CRM_Mailchimp_Utils::$post_hook_enabled = TRUE;
     // Return OK response.
     return [200, NULL];
 }
 function run()
 {
     $my_key = CRM_Core_BAO_Setting::getItem(self::MC_SETTING_GROUP, 'security_key', NULL, FALSE);
     CRM_Mailchimp_Utils::checkDebug('CRM_Mailchimp_Page_WebHook run $my_key= ', $my_key);
     if (CRM_Core_Config::singleton()->userPermissionClass->isModulePermissionSupported() && !CRM_Mailchimp_Permission::check('allow webhook posts')) {
         CRM_Core_Error::fatal();
     }
     // Check the key
     // @todo is this a DOS attack vector? seems a lot of work for saying 403, go away, to a robot!
     if (!isset($_GET['key']) || $_GET['key'] != $my_key) {
         CRM_Core_Error::fatal();
     }
     if (!empty($_POST['data']['list_id']) && !empty($_POST['type'])) {
         $requestType = $_POST['type'];
         $requestData = $_POST['data'];
         switch ($requestType) {
             case 'subscribe':
             case 'unsubscribe':
             case 'profile':
                 // Create/Update contact details in CiviCRM
                 $delay = $requestType == 'profile';
                 $contactID = CRM_Mailchimp_Utils::updateContactDetails($requestData['merges'], $delay);
                 $contactArray = array($contactID);
                 // Subscribe/Unsubscribe to related CiviCRM groups
                 self::manageCiviCRMGroupSubcription($contactID, $requestData, $requestType);
                 CRM_Mailchimp_Utils::checkDebug('Start - CRM_Mailchimp_Page_WebHook run $_POST= ', $_POST);
                 CRM_Mailchimp_Utils::checkDebug('Start - CRM_Mailchimp_Page_WebHook run $contactID= ', $contactID);
                 CRM_Mailchimp_Utils::checkDebug('Start - CRM_Mailchimp_Page_WebHook run $requestData= ', $requestData);
                 CRM_Mailchimp_Utils::checkDebug('Start - CRM_Mailchimp_Page_WebHook run $requestType= ', $requestType);
                 break;
             case 'upemail':
                 // Mailchimp Email Update event
                 // Try to find the email address
                 $email = new CRM_Core_BAO_Email();
                 $email->get('email', $requestData['old_email']);
                 CRM_Mailchimp_Utils::checkDebug('CRM_Mailchimp_Page_WebHook run- case upemail $requestData[old_email]= ', $requestData['old_email']);
                 // If the Email was found.
                 if (!empty($email->contact_id)) {
                     $email->email = $requestData['new_email'];
                     $email->save();
                     CRM_Mailchimp_Utils::checkDebug('CRM_Mailchimp_Page_WebHook run- case upemail inside condition $requestData[new_email]= ', $requestData['new_email']);
                 }
                 break;
             case 'cleaned':
                 // Try to find the email address
                 $email = new CRM_Core_BAO_Email();
                 $email->get('email', $requestData['email']);
                 CRM_Mailchimp_Utils::checkDebug('CRM_Mailchimp_Page_WebHook run - case cleaned $requestData[new_email]= ', $requestData['email']);
                 // If the Email was found.
                 if (!empty($email->contact_id)) {
                     $email->on_hold = 1;
                     $email->holdEmail($email);
                     $email->save();
                     CRM_Mailchimp_Utils::checkDebug('CRM_Mailchimp_Page_WebHook run - case cleaned inside condition $email= ', $email);
                     CRM_Mailchimp_Utils::checkDebug('CRM_Mailchimp_Page_WebHook run - case cleaned inside condition $requestData[new_email]= ', $requestData['email']);
                 }
                 break;
             default:
                 // unhandled webhook
                 CRM_Mailchimp_Utils::checkDebug('End- CRM_Mailchimp_Page_WebHook run $contactID= ', $contactID);
                 CRM_Mailchimp_Utils::checkDebug('End- CRM_Mailchimp_Page_WebHook run $requestData= ', $requestData);
                 CRM_Mailchimp_Utils::checkDebug('End- CRM_Mailchimp_Page_WebHook run $requestType= ', $requestType);
                 CRM_Mailchimp_Utils::checkDebug('End - CRM_Mailchimp_Page_WebHook run $email= ', $email);
         }
     }
     // Return the JSON output
     header('Content-type: application/json');
     $data = NULL;
     // We should ideally throw some status
     print json_encode($data);
     CRM_Utils_System::civiExit();
 }
/**
 * Implementation of hook_civicrm_permission
 *
 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_permission
 */
function mailchimp_civicrm_permission(&$permissions)
{
    //Until the Joomla/Civi integration is fixed, don't declare new perms
    // for Joomla installs
    if (CRM_Core_Config::singleton()->userPermissionClass->isModulePermissionSupported()) {
        $permissions = array_merge($permissions, CRM_Mailchimp_Permission::getMailchimpPermissions());
    }
}