// Ping
const PING = 'Ping:Ping';
const STATUS = 'Ping:Status';
- const LIFETIME = 'Ping:LifeTime';
+ const HEARTBEATINTERVAL = 'Ping:HeartbeatInterval';
const FOLDERS = 'Ping:Folders';
const FOLDER = 'Ping:Folder';
const SERVERENTRYID = 'Ping:ServerEntryId';
*/
public function handle(Horde_ActiveSync $activeSync, $devId)
{
+ $now = time();
parent::handle($activeSync, $devId);
- // FIXME
- $timeout = 3;
- $this->_logger->info('[' . $devId . '] Ping received.');
+ /* Get the settings for the server */
+ $ping_settings = $this->_driver->getHeartbeatConfig();
+ $timeout = $ping_settings['waitinterval'];
+
+ /* Notify */
+ $this->_logger->info('[' . $devId . '] Ping received at timestamp: ' . $now . '.');
/* Glass half full kinda guy... */
$this->_statusCode = self::STATUS_NOCHANGES;
* we read in the PING request since the PING request is allowed to omit
* sections if they have been sent previously */
$collections = array_values($state->initPingState($this->_devId));
- $lifetime = $state->getPingLifetime();
+ $lifetime = $state->getHeartbeatInterval();
+ if ($lifetime !== 0 && $lifetime < $ping_settings['heartbeatmin']) {
+ $this->_statusCode = self::STATUS_HBOUTOFBOUNDS;
+ $lifetime = $ping_settings['heartbeatmin'];
+ } elseif ($lifetime > $ping_settings['heartbeatmax']) {
+ $this->_statusCode = self::STATUS_HBOUTOFBOUNDS;
+ $lifetime = $ping_settings['heartbeatmax'];
+ }
+ if ($this->_statusCode == self::STATUS_HBOUTOFBOUNDS) {
+ $state->resetPingState();
+ $this->_handleHBError($lifetime);
+ $state->savePingState();
+ return false;
+ }
/* Build the $collections array if we receive request from PIM */
if ($this->_decoder->getElementStartTag(self::PING)) {
- if ($this->_decoder->getElementStartTag(self::LIFETIME)) {
+ if ($this->_decoder->getElementStartTag(self::HEARTBEATINTERVAL)) {
$lifetime = $this->_decoder->getElementContent();
- $state->setPingLifetime($lifetime);
+ if ($lifetime !== 0 && $lifetime < $ping_settings['heartbeatmin']) {
+ $this->_statusCode = self::STATUS_HBOUTOFBOUNDS;
+ $lifetime = $ping_settings['heartbeatmin'];
+ } elseif ($lifetime > $ping_settings['heartbeatmax']) {
+ $this->_statusCode = self::STATUS_HBOUTOFBOUNDS;
+ $lifetime = $ping_settings['heartbeatmax'];
+ }
+ if ($this->_statusCode == self::STATUS_HBOUTOFBOUNDS) {
+ $state->resetPingState();
+ $this->_handleHBError($lifetime);
+ $state->savePingState();
+ return false;
+ }
+ $state->setHeartbeatInterval($lifetime);
$this->_decoder->getElementEndTag();
}
/* Start waiting for changes, but only if we don't have any errors */
if ($this->_statusCode == self::STATUS_NOCHANGES) {
- $this->_logger->info(sprintf('[%s] Waiting for changes (lifetime: %d)', $this->_devId, $lifetime));
- // FIXME
- //for ($n = 0; $n < $lifetime / $timeout; $n++) {
- for ($n = 0; $n < 10; $n++) {
- //check the remote wipe status
+ $this->_logger->info(sprintf('[%s] Waiting for changes (heartbeat interval: %d)', $this->_devId, $lifetime));
+ $expire = $now + $lifetime;
+ while (time() <= $expire) {
+ /* Check the remote wipe status and request a foldersync if
+ * we want the device wiped. */
if ($this->_provisioning === true) {
$rwstatus = $state->getDeviceRWStatus($this->_devId);
if ($rwstatus == Horde_ActiveSync::PROVISION_RWSTATUS_PENDING || $rwstatus == Horde_ActiveSync::PROVISION_RWSTATUS_WIPED) {
for ($i = 0; $i < count($collections); $i++) {
$collection = $collections[$i];
- // Make sure we have the synckey (which is the devid for
- // PING requests.
$collection['synckey'] = $this->_devId;
$sync = $this->_driver->getSyncObject();
$state->loadPingCollectionState($collection);
} catch (Horde_ActiveSync_Exception $e) {
/* Stop ping if exporter cannot be configured */
$this->_logger->err('Ping error: Exporter can not be configured. Waiting 30 seconds before ping is retried.');
- $n = $lifetime/ $timeout;
sleep(30);
break;
}
$this->_statusCode = self::STATUS_NEEDSYNC;
}
- // Update the state, but don't bother with the backend since we
- // are not updating any data.
+ /* Update the state, but don't bother with the backend since we
+ * are not updating any data.*/
while (is_array($sync->syncronize(Horde_ActiveSync::BACKEND_DISCARD_DATA)));
}
$this->_logger->info('[' . $this->_devId . '] Changes available');
break;
}
+ /* Wait a bit before trying again */
sleep($timeout);
}
}
+
+ /* Prepare for response */
$this->_logger->info('[' . $this->_devId . '] Sending response for PING.');
$this->_encoder->StartWBXML();
return true;
}
+
+ protected function _handleHBError($lifetime)
+ {
+ $this->_logger->info('[' . $this->_devId . '] Sending response for PING.');
+ $this->_encoder->StartWBXML();
+ $this->_encoder->startTag(self::PING);
+ $this->_encoder->startTag(self::STATUS);
+ $this->_encoder->content($this->_statusCode);
+ $this->_encoder->endTag();
+ $this->_encoder->startTag(self::HEARTBEATINTERVAL);
+ $this->_encoder->content($lifetime);
+ $this->_encoder->endTag();
+ $this->_encoder->endTag();
+ }
}
</case>
</configswitch>
</configsection>
+ <configsection name="ping">
+ <configheader>Ping Settings</configheader>
+ <configdescription>The Heartbeat Interval specifies the length of time, in
+ seconds, that the server SHOULD wait before notifying the client of
+ changes in a folder on the server. These settings allow you to indicate
+ the allowable range for this setting. Note that not all clients will
+ request this information.</configdescription>
+ <configinteger name="heartbeatmin" desc="The minimum number of seconds to
+ wait before sending results. This value must be greater then zero.">10
+ </configinteger>
+ <configinteger name="heartbeatmax" desc="The maximum number of seconds to
+ wait before sending results. Note this value must not be greater then 3540
+ (59 minutes).">60</configinteger>
+ <configinteger name="heartbeatdefault" desc="If the client does not
+ request a heartbeat interval, this interval will be used by default.">10
+ </configinteger>
+ <configinteger name="waitinterval" desc="How many seconds should elapse
+ between checks for changes? Increasing this value will cause the server
+ to wait longer between checks for changes during the heartbeat,
+ essentially reducing the number of checks done during each heartbeat.
+ Lowering this value will increase the number of checks that are down
+ during each heartbeat.">5</configinteger>
+ </configsection>
<configsection name="securitypolicies">
<configheader>Security Policies</configheader>
<configswitch name="provisioning" quote="false" desc="How should device