/**
* Handle the Provision request. This is a 3-phase process. Phase 1 is
* actually the enforcement, when the server rejects a request and forces
* the client to perform this PROVISION request...so we are handling phase
* 2 (download policies) and 3 (acknowledge policies) here.
*
* @return boolean
* @throws Horde_ActiveSync_Exception
*/
protected function _handle()
{
// Be optimistic
$status = self::STATUS_SUCCESS;
$policyStatus = self::STATUS_SUCCESS;
if ($error = $this->_activeSync->checkGlobalError()) {
$this->_globalError($error);
return true;
}
// Start by assuming we are in stage 2
$phase2 = true;
if (!$this->_decoder->getElementStartTag(Horde_ActiveSync::PROVISION_PROVISION)) {
return $this->_globalError(self::STATUS_PROTERROR);
}
// Handle remote wipe status response for Android devices.
if ($this->_decoder->getElementStartTag(Horde_ActiveSync::PROVISION_REMOTEWIPE)) {
if (!$this->_decoder->getElementStartTag(Horde_ActiveSync::PROVISION_STATUS)) {
return $this->_globalError(self::STATUS_PROTERROR);
}
$status = $this->_decoder->getElementContent();
if (!$this->_decoder->getElementEndTag() || !$this->_decoder->getElementEndTag()) {
return $this->_globalError(self::STATUS_PROTERROR);
}
if ($status == self::STATUS_CLIENT_SUCCESS) {
$this->_state->setDeviceRWStatus($this->_devId, Horde_ActiveSync::RWSTATUS_WIPED);
}
$policytype = Horde_ActiveSync::POLICYTYPE_XML;
} else {
if ($deviceinfo = $this->_handleSettings()) {
$deviceinfo['version'] = $this->_device->version;
$this->_device->setDeviceProperties($deviceinfo);
$this->_device->save();
}
if (!$this->_decoder->getElementStartTag(Horde_ActiveSync::PROVISION_POLICIES) || !$this->_decoder->getElementStartTag(Horde_ActiveSync::PROVISION_POLICY)) {
return $this->_globalError(self::STATUS_PROTERROR);
}
// iOS (at least 5.0.1) incorrectly sends a STATUS tag before the
// REMOTEWIPE response.
if (!$this->_decoder->getElementStartTag(Horde_ActiveSync::PROVISION_POLICYTYPE)) {
if ($this->_decoder->getElementStartTag(Horde_ActiveSync::PROVISION_STATUS)) {
$this->_decoder->getElementContent();
$this->_decoder->getElementEndTag();
// status
}
} else {
$policytype = $this->_decoder->getElementContent();
if ($this->_device->version < Horde_ActiveSync::VERSION_TWELVE && $policytype != Horde_ActiveSync::POLICYTYPE_XML) {
$this->_logger->err('EAS version < 12.0 but policy type is not POLICYTYPE_XML');
$policyStatus = self::STATUS_POLICYUNKNOWN;
}
if ($this->_device->version >= Horde_ActiveSync::VERSION_TWELVE && $policytype != Horde_ActiveSync::POLICYTYPE_WBXML) {
$this->_logger->err('EAS version >= 12.0 but policy type is not POLICYTYPE_WBXML');
$policyStatus = self::STATUS_POLICYUNKNOWN;
}
if (!$this->_decoder->getElementEndTag()) {
//policytype
return $this->_globalError(self::STATUS_PROTERROR);
}
}
// POLICYKEY is only sent by client in phase 3
if ($this->_decoder->getElementStartTag(Horde_ActiveSync::PROVISION_POLICYKEY)) {
$policykey = $this->_decoder->getElementContent();
$this->_logger->info('[' . $this->_device->id . '] PHASE 3 policykey sent from client: ' . $policykey);
if (!$this->_decoder->getElementEndTag() || !$this->_decoder->getElementStartTag(Horde_ActiveSync::PROVISION_STATUS)) {
return $this->_globalError(self::STATUS_PROTERROR);
}
if ($this->_decoder->getElementContent() != self::STATUS_SUCCESS) {
$this->_logger->err('Policy not accepted by device: ' . $this->_device->id);
if ($this->_provisioning == Horde_ActiveSync::PROVISIONING_LOOSE) {
// Loose provisioning, don't error out, just don't reqiure provision.
$this->_sendNoProvisionNeededResponse($status);
return true;
}
$policyStatus = self::STATUS_POLICYCORRUPT;
}
if (!$this->_decoder->getElementEndTag()) {
return $this->_globalError(self::STATUS_PROTERROR);
}
$phase2 = false;
}
if (!$this->_decoder->getElementEndTag() || !$this->_decoder->getElementEndTag()) {
return $this->_globalError(self::STATUS_PROTERROR);
}
// Handle remote wipe status for other devices
if ($this->_decoder->getElementStartTag(Horde_ActiveSync::PROVISION_REMOTEWIPE)) {
if (!$this->_decoder->getElementStartTag(Horde_ActiveSync::PROVISION_STATUS)) {
return $this->_globalError(self::STATUS_PROTERROR);
}
$status = $this->_decoder->getElementContent();
if (!$this->_decoder->getElementEndTag() || !$this->_decoder->getElementEndTag()) {
return $this->_globalError(self::STATUS_PROTERROR);
}
if ($status == self::STATUS_CLIENT_SUCCESS) {
$this->_state->setDeviceRWStatus($this->_device->id, Horde_ActiveSync::RWSTATUS_WIPED);
}
}
}
if (!$this->_decoder->getElementEndTag()) {
//provision
return $this->_globalError(self::STATUS_PROTERROR);
}
// Check to be sure that we *need* to PROVISION
if ($this->_provisioning === false) {
$this->_sendNoProvisionNeededResponse($status);
return true;
}
// Start handling request and sending output
$this->_encoder->StartWBXML();
// End of Phase 3 - We create the "final" policy key, store it, then
// send it to the client.
if (!$phase2) {
// Verify intermediate key
$this->_logger->info(sprintf('Verifying Phase 3 policykey: From Device: %s, Stored: %s', $policykey, $this->_device->policykey));
if ($this->_state->getPolicyKey($this->_device->id) != $policykey) {
$policyStatus = self::STATUS_POLKEYMISM;
} else {
// Set the final key
$policykey = $this->_state->generatePolicyKey();
$this->_state->setPolicyKey($this->_device->id, $policykey);
$this->_state->setDeviceRWStatus($this->_device->id, Horde_ActiveSync::RWSTATUS_OK);
}
$this->_cleanUpAfterPairing();
} elseif (empty($policykey)) {
// This is phase2 - we need to set the intermediate key
$policykey = $this->_state->generatePolicyKey();
$this->_logger->info(sprintf('Generating PHASE2 policy key: %s', $policykey));
$this->_state->setPolicyKey($this->_device->id, $policykey);
}
// If we are phase2 we need to check this here, before the status is
// sent. Prevents devices not supporting the required policies from
// being able to connect.
if ($phase2 && $status == self::STATUS_SUCCESS && $policyStatus == self::STATUS_SUCCESS && $this->_provisioning == Horde_ActiveSync::PROVISIONING_FORCE) {
$policyHandler = new Horde_ActiveSync_Policies($this->_encoder, $this->_device->version, $this->_driver->getCurrentPolicy($deviceinfo));
if (!$policyHandler->validatePolicyVersion()) {
$this->_handleVersionMismatch();
return true;
}
}
$this->_encoder->startTag(Horde_ActiveSync::PROVISION_PROVISION);
$this->_encoder->startTag(Horde_ActiveSync::PROVISION_STATUS);
$this->_encoder->content($status);
$this->_encoder->endTag();
$this->_encoder->startTag(Horde_ActiveSync::PROVISION_POLICIES);
$this->_encoder->startTag(Horde_ActiveSync::PROVISION_POLICY);
$this->_encoder->startTag(Horde_ActiveSync::PROVISION_POLICYTYPE);
$this->_encoder->content($policytype);
$this->_encoder->endTag();
$this->_encoder->startTag(Horde_ActiveSync::PROVISION_STATUS);
$this->_encoder->content($policyStatus);
$this->_encoder->endTag();
$this->_encoder->startTag(Horde_ActiveSync::PROVISION_POLICYKEY);
$this->_encoder->content($policykey);
$this->_encoder->endTag();
// Send security policies.
if ($phase2 && $status == self::STATUS_SUCCESS && $policyStatus == self::STATUS_SUCCESS) {
$this->_encoder->startTag(Horde_ActiveSync::PROVISION_DATA);
if ($policytype == Horde_ActiveSync::POLICYTYPE_XML) {
$policyHandler->toXml();
} else {
$policyHandler->toWbxml();
}
$this->_encoder->endTag();
//data
}
$this->_encoder->endTag();
//policy
$this->_encoder->endTag();
//policies
// Remote wipe if requested.
$rwstatus = $this->_state->getDeviceRWStatus($this->_device->id);
if ($rwstatus == Horde_ActiveSync::RWSTATUS_PENDING || $rwstatus == Horde_ActiveSync::RWSTATUS_WIPED) {
$this->_encoder->startTag(Horde_ActiveSync::PROVISION_REMOTEWIPE, false, true);
$this->_state->setDeviceRWStatus($this->_device->id, Horde_ActiveSync::RWSTATUS_WIPED);
}
$this->_encoder->endTag();
//provision
return true;
}