[ Index ] |
|
Code source de Horde 3.1.3 |
1 <?php 2 /** 3 * $Horde: framework/SyncML/SyncML/Sync.php,v 1.8.4.9 2006/05/01 12:05:14 jan Exp $ 4 * 5 * Copyright 2003-2006 Anthony Mills <amills@pyramid6.com> 6 * 7 * See the enclosed file COPYING for license information (LGPL). If you 8 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. 9 * 10 * @author Anthony Mills <amills@pyramid6.com> 11 * @since Horde 3.0 12 * @package SyncML 13 */ 14 class SyncML_Sync { 15 16 /** 17 * Target: contacts, notes, calendar, tasks, 18 */ 19 var $_targetLocURI; // target means server here 20 21 var $_sourceLocURI; // source means client here 22 23 var $_alert; 24 25 /** 26 * True indicates that there are still sync elements that have not been 27 * sent yet due to message size limitations and have to be sent in the next 28 * message. 29 * 30 * @var boolean 31 */ 32 var $_pendingElements; 33 34 /** 35 * Remember entries we have handled already: once we send a delete for an 36 * entry, we don't want to send an add afterwards. This array is also used 37 * if a sync is sent in multiple messages due to message size restrictions. 38 * 39 * @var array 40 */ 41 var $_done; 42 43 function SyncML_Sync($alert, $serverURI, $clientURI) 44 { 45 $this->_alert = $alert; 46 $this->_pendingElements = false; 47 $this->_done = array(); 48 $this->_targetLocURI = basename($serverURI); 49 $this->_sourceLocURI = $clientURI; 50 global $backend; 51 $backend->logMessage("create for syncType=$serverURI", __FILE__, __LINE__, PEAR_LOG_DEBUG); 52 } 53 54 /** 55 * Here's where the actual processing of a client-sent Sync 56 * Item takes place. Entries are added, deleted or replaced 57 * from the server database by using Horde API (Registry) calls. 58 */ 59 function handleSyncItem($item) 60 { 61 global $backend; 62 63 $state = &$_SESSION['SyncML.state']; 64 $device = &$state->getDevice(); 65 $syncIdentifier = $state->getSyncIdentifier(); 66 67 $hordeType = $type = $this->_targetLocURI; 68 69 // Use contentType explicitly specified in this sync command. 70 $contentType = $item->getContentType(); 71 72 // If not provided, use default from device info. 73 if (!$contentType) { 74 $contentType = $device->getPreferredContentType($type); 75 } 76 77 list($content, $contentType) = 78 $device->convertClient2Server($item->getContent(), 79 $contentType); 80 $cuid = $item->getCuid(); 81 $suid = false; 82 83 // Handle client add requests. 84 if ($item->getElementType() =='Add') { 85 $suid = $backend->importEntry($syncIdentifier, $hordeType, 86 $content, $contentType, $cuid); 87 if (!is_a($suid, 'PEAR_Error')) { 88 $state->log("Client-Add"); 89 $backend->logMessage('added client entry as ' . $suid, 90 __FILE__, __LINE__, PEAR_LOG_DEBUG); 91 } else { 92 $state->log("Client-AddFailure"); 93 $backend->logMessage('Error in adding client entry:' . $suid->message, __FILE__, __LINE__, PEAR_LOG_ERR); 94 } 95 96 // Handle client delete requests. 97 } elseif ($item->getElementType() =='Delete') { 98 $suid = $backend->deleteEntry($syncIdentifier, $type, $cuid); 99 if ($type == 'calendar' 100 && $device->handleTasksInCalendar()) { 101 // deleteEntry does not need to return an error on 102 // deletion of nonexistent entries. So just to be sure 103 // we have to delete from the tasks database as well: 104 $backend->logMessage('special tasks delete ' . $suid . ' due to client request', __FILE__, __LINE__, PEAR_LOG_DEBUG); 105 $suid = $backend->deleteEntry($syncIdentifier, 'tasks', $cuid); 106 } 107 if (!is_a($suid, 'PEAR_Error')) { 108 $state->log("Client-Delete"); 109 $backend->logMessage('deleted entry ' . $suid . ' due to client request', __FILE__, __LINE__, PEAR_LOG_DEBUG); 110 } else { 111 $state->log("Client-DeleteFailure"); 112 $this->logMessage('Failure deleting client entry, maybe gone already on server. msg:'. $suid->message, __FILE__, __LINE__, PEAR_LOG_ERR); 113 } 114 115 // Handle client replace requests. 116 } elseif ($item->getElementType() == 'Replace') { 117 $suid = $backend->replaceEntry($syncIdentifier, $hordeType, 118 $content, $contentType, $cuid); 119 120 if (!is_a($suid, 'PEAR_Error')) { 121 $state->log("Client-Replace"); 122 $backend->logMessage('replaced entry ' . $suid . ' due to client request', __FILE__, __LINE__, PEAR_LOG_DEBUG); 123 } else { 124 $backend->logMessage($suid->message, __FILE__, __LINE__, PEAR_LOG_DEBUG); 125 $backend->logMessage($suid, __FILE__, __LINE__, PEAR_LOG_DEBUG); 126 127 // Entry may have been deleted; try adding it. 128 $suid = $backend->importEntry($syncIdentifier, $hordeType, 129 $content, $contentType, $cuid); 130 if (!is_a($suid, 'PEAR_Error')) { 131 $state->log("Client-AddReplace"); 132 $backend->logMessage('added client entry due to replace request as ' . $suid, __FILE__, __LINE__, PEAR_LOG_DEBUG); 133 } else { 134 $state->log("Client-AddFailure"); 135 $backend->logMessage('Error in adding client entry due to replace request:' . $suid->message, __FILE__, __LINE__, PEAR_LOG_ERR); 136 } 137 } 138 } else { 139 $backend->logMessage('Unexpected elementType: ' . $item->getElementType(), 140 __FILE__, __LINE__, PEAR_LOG_ERR); 141 } 142 143 return $suid; 144 } 145 146 /** 147 * Creates a <Sync> output. 148 */ 149 function createSyncOutput($currentCmdID, &$output) 150 { 151 $state = &$_SESSION['SyncML.state']; 152 $attrs = array(); 153 154 $output->startElement($state->getURI(), 'Sync', $attrs); 155 $output->startElement($state->getURI(), 'CmdID', $attrs); 156 $output->characters($currentCmdID); 157 $currentCmdID++; 158 $output->endElement($state->getURI(), 'CmdID'); 159 160 $output->startElement($state->getURI(), 'Target', $attrs); 161 $output->startElement($state->getURI(), 'LocURI', $attrs); 162 $output->characters($this->_sourceLocURI); 163 $output->endElement($state->getURI(), 'LocURI'); 164 $output->endElement($state->getURI(), 'Target'); 165 166 $output->startElement($state->getURI(), 'Source', $attrs); 167 $output->startElement($state->getURI(), 'LocURI', $attrs); 168 $output->characters($this->_targetLocURI); 169 $output->endElement($state->getURI(), 'LocURI'); 170 $output->endElement($state->getURI(), 'Source'); 171 172 $syncType = $this->_targetLocURI; 173 174 global $backend; 175 $backend->logMessage("handleSync for syncType=$syncType", __FILE__, __LINE__, PEAR_LOG_DEBUG); 176 177 // Here's where server modifications are sent to the client: 178 $refts = $state->getServerAnchorLast($syncType); 179 $currentCmdID = $this->handleSync($currentCmdID, $syncType, 180 $output, $refts); 181 182 $output->endElement($state->getURI(), 'Sync'); 183 184 return $currentCmdID; 185 } 186 187 /** 188 * Sends server changes to the client. 189 */ 190 function handleSync($currentCmdID, $syncType, &$output, $refts, $end_ts = 0) 191 { 192 global $backend; 193 global $messageFull; 194 195 /* $messageFull will be set to true to indicate that there's no room 196 * for other data in this message. If it's false (empty) and there 197 * are pending Sync data, the final command will sent the pending data. 198 * This global data should be moved to a global object, together 199 * with currentCmdID and $output. */ 200 $messageFull = false; 201 202 $state = &$_SESSION['SyncML.state']; 203 $device = &$state->getDevice(); 204 $syncIdentifier = $state->getSyncIdentifier(); 205 $contentType = $device->getPreferredContentTypeClient($syncType, $this->_sourceLocURI); 206 207 /* We faithfully expect to deal with all remaining elements. Will be 208 * set to true if we run out of space for message creation. */ 209 $this->setPendingElements(false); 210 211 // Handle deletions. 212 $deletions = $backend->getServerDeletions($syncIdentifier, $syncType, $refts, $end_ts); 213 214 if ($refts > 0) { 215 // Don't send deletes on SlowSync. 216 foreach ($deletions as $suid => $cuid) { 217 $this->_done[$suid] = true; 218 $backend->logMessage("delete: cuid=$cuid suid=$suid refts: $refts", __FILE__, __LINE__, PEAR_LOG_DEBUG); 219 // Create a Delete request for client. 220 $currentCmdID = $this->outputCommand($currentCmdID, $output, 'Delete', 221 null, null, $cuid, null); 222 $state->log('Server-Delete'); 223 } 224 } 225 // Handle additions. 226 $adds = $backend->getServerAdditions($syncIdentifier, $syncType, $refts, $end_ts); 227 foreach ($adds as $suid => $cuid) { 228 if (!empty($this->_done[$suid])) { 229 // Already sent delete, no need to add. 230 continue; 231 } 232 $backend->logMessage("add: $suid", __FILE__, __LINE__, PEAR_LOG_DEBUG); 233 234 $c = $backend->retrieveEntry($syncType, $suid, $contentType); 235 if (!is_a($c, 'PEAR_Error')) { 236 // Item in history but not in database. Strange, but can happen. 237 list($clientContent, $clientContentType) = 238 $device->convertServer2Client($c, $contentType); 239 // Check if we have space left in the message. 240 if (($state->getMaxMsgSize() 241 - $output->getOutputSize() 242 - strlen($clientContent)) < 50) { 243 $backend->logMessage('max message size reached cursize=' 244 . $output->getOutputSize(), 245 __FILE__, __LINE__, PEAR_LOG_DEBUG); 246 $messageFull = true; 247 $this->setPendingElements(true); 248 return $currentCmdID; 249 } 250 $this->_done[$suid] = true; 251 $currentCmdID = $this->outputCommand($currentCmdID, $output, 252 'Add', 253 $clientContent, 254 $clientContentType, 255 null, 256 $suid); 257 $state->log('Server-Add'); 258 } else { 259 $backend->logMessage('api export call for ' . $suid . ' failed: ' . $c->getMessage(), 260 __FILE__, __LINE__, PEAR_LOG_DEBUG); 261 } 262 } 263 264 // Handle changes. 265 if ($refts != 0) { 266 // Don't send changes for SlowSync, confuses some clients. 267 $end_ts = time(); 268 $changes = $backend->getServerModifications($syncIdentifier, 269 $syncType, 270 $refts, $end_ts); 271 if (is_a($changes, 'PEAR_Error')) { 272 $backend->logMessage($changes, __FILE__, __LINE__, PEAR_LOG_ERR); 273 return $changes; 274 } 275 276 foreach ($changes as $suid => $cuid) { 277 if (!empty($this->_done[$suid])) { 278 // Already sent delete or add, no need to modify. 279 continue; 280 } 281 282 if (!$cuid) { 283 // TODO: create an "add" here instead? 284 continue; 285 } 286 287 $c = $backend->retrieveEntry($syncType, $suid, $contentType); 288 if (!is_a($c, 'PEAR_Error')) { 289 $backend->logMessage("change: $suid", 290 __FILE__, __LINE__, PEAR_LOG_DEBUG); 291 list($clientContent, $clientContentType) = 292 $device->convertServer2Client($c, $contentType); 293 // Check if we have space left in the message. 294 if (($state->getMaxMsgSize() 295 - $output->getOutputSize() 296 - strlen($clientContent)) < 50) { 297 $backend->logMessage('max message size reached cursize=' 298 . $output->getOutputSize(), 299 __FILE__, __LINE__, PEAR_LOG_DEBUG); 300 $messageFull = true; 301 $this->setPendingElements(true); 302 return $currentCmdID; 303 } 304 $this->_done[$suid] = true; 305 $currentCmdID = $this->outputCommand($currentCmdID, $output, 306 'Replace', 307 $clientContent, 308 $clientContentType, 309 $cuid, 310 null); 311 $state->log('Server-Replace'); 312 } else { 313 // Item in history but not in database. Strange, but 314 // can happen. 315 } 316 } 317 } 318 319 // If tasks are handled inside calendar, do the same again for 320 // tasks. 321 if ($syncType == 'calendar' && $device->handleTasksInCalendar()) { 322 $backend->logMessage("handling tasks in calendar sync", 323 __FILE__, __LINE__, PEAR_LOG_DEBUG); 324 325 $currentCmdID = $this->handleSync($currentCmdID, 'tasks', 326 $output, $refts, $end_ts); 327 } 328 329 return $currentCmdID; 330 } 331 332 /** 333 * Output a single Sync command (Add, Delete, Replace). 334 */ 335 function outputCommand($currentCmdID, &$output, $command, 336 $content, $contentType = null, 337 $cuid = null, $suid = null) 338 { 339 $state = &$_SESSION['SyncML.state']; 340 341 $attrs = array(); 342 $output->startElement($state->getURI(), $command, $attrs); 343 344 $output->startElement($state->getURI(), 'CmdID', $attrs); 345 $chars = $currentCmdID; 346 $output->characters($chars); 347 $output->endElement($state->getURI(), 'CmdID'); 348 349 if (isset($contentType)) { 350 $output->startElement($state->getURI(), 'Meta', $attrs); 351 $output->startElement($state->getURIMeta(), 'Type', $attrs); 352 $output->characters($contentType); 353 $output->endElement($state->getURIMeta(), 'Type'); 354 $output->endElement($state->getURI(), 'Meta'); 355 } 356 357 if (isset($content) 358 || isset($cuid) || isset($suid)) { 359 $output->startElement($state->getURI(), 'Item', $attrs); 360 if ($suid != null) { 361 $output->startElement($state->getURI(), 'Source', $attrs); 362 $output->startElement($state->getURI(), 'LocURI', $attrs); 363 $output->characters($suid); 364 $output->endElement($state->getURI(), 'LocURI'); 365 $output->endElement($state->getURI(), 'Source'); 366 } 367 368 if ($cuid != null) { 369 $output->startElement($state->getURI(), 'Target', $attrs); 370 $output->startElement($state->getURI(), 'LocURI', $attrs); 371 $output->characters($cuid); 372 $output->endElement($state->getURI(), 'LocURI'); 373 $output->endElement($state->getURI(), 'Target'); 374 } 375 if (isset($content)) { 376 $output->startElement($state->getURI(), 'Data', $attrs); 377 $output->characters($content); 378 $output->endElement($state->getURI(), 'Data'); 379 } 380 $output->endElement($state->getURI(), 'Item'); 381 } 382 383 $output->endElement($state->getURI(), $command); 384 385 $currentCmdID++; 386 387 return $currentCmdID; 388 } 389 390 function setPendingElements($e) 391 { 392 $this->_pendingElements = $e; 393 } 394 395 function hasPendingElements() 396 { 397 return $this->_pendingElements; 398 } 399 400 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sun Feb 25 18:01:28 2007 | par Balluche grâce à PHPXref 0.7 |