1<?php 2/** 3 * This class creates the actual XML data and passes it on to a ContentHandler 4 * for optional WBXML encoding. 5 * 6 * Each member public function creates one type of SyncML artefact (like a 7 * Status response). Currently some of the information is retrieved from 8 * state. Maybe remove these dependencies (by providing the data as parameter) 9 * for an even cleaner implementation. 10 * 11 * The Horde_SyncMl_XmlOutput class takes automatically care of creating a 12 * unique CmdID for each command created. 13 * 14 * Copyright 2006-2016 Horde LLC (http://www.horde.org/) 15 * 16 * See the enclosed file COPYING for license information (LGPL). If you 17 * did not receive this file, see http://www.horde.org/licenses/lgpl21. 18 * 19 * @author Karsten Fourmont <karsten@horde.org> 20 * @package SyncMl 21 */ 22class Horde_SyncMl_XmlOutput 23{ 24 /** 25 * The CmdID provides a unique ID for each command in a syncml packet. 26 */ 27 protected $_msg_CmdID; 28 29 /** 30 * The outputhandler to whom the XML is passed: like 31 * Horde_Xml_Wbxml_Encoder 32 */ 33 public $output; 34 35 protected $_uri; 36 37 /** 38 * The final output as procuded by the _output Encoder. Either an 39 * XML string or a WBXML string. 40 */ 41 public function getOutput() 42 { 43 return $this->output->getOutput(); 44 } 45 46 /** 47 * The length of the output as produced by the Encoder. To limit the 48 * size of individual messages. 49 */ 50 public function getOutputSize() 51 { 52 return $this->output->getOutputSize(); 53 } 54 55 /** 56 * To we create wbxml or not? 57 */ 58 public function isWBXML() 59 { 60 return is_a($this->output, 'Horde_Xml_Wbxml_Encoder'); 61 } 62 63 public function &singleton() 64 { 65 static $instance; 66 if (!isset($instance)) { 67 $instance = new Horde_SyncMl_XmlOutput(); 68 } 69 return $instance; 70 } 71 72 public function init(&$theoutputhandler) 73 { 74 $this->output = $theoutputhandler; 75 $this->_msg_CmdID = 1; 76 77 } 78 79 /** 80 * Creates a SyncHdr output. 81 * 82 * Required data is retrieved from state. 83 * 84 * @param string $respURI The url of the server endpoint. 85 * 86 * @throws Horde_Xml_Wbxml_Exception 87 */ 88 public function outputHeader($respURI) 89 { 90 $state = $GLOBALS['backend']->state; 91 92 $this->_uriMeta = $state->uriMeta; 93 94 $this->output->startElement($this->_uri, 'SyncHdr'); 95 96 $this->output->startElement($this->_uri, 'VerDTD'); 97 $chars = $state->getVerDTD(); 98 $this->output->characters($chars); 99 $this->output->endElement($this->_uri, 'VerDTD'); 100 101 $this->output->startElement($this->_uri, 'VerProto'); 102 $chars = $state->getProtocolName(); 103 $this->output->characters($chars); 104 $this->output->endElement($this->_uri, 'VerProto'); 105 106 $this->output->startElement($this->_uri, 'SessionID'); 107 $this->output->characters($state->sessionID); 108 $this->output->endElement($this->_uri, 'SessionID'); 109 110 $this->output->startElement($this->_uri, 'MsgID'); 111 $this->output->characters($state->messageID); 112 $this->output->endElement($this->_uri, 'MsgID'); 113 114 $this->output->startElement($this->_uri, 'Target'); 115 $this->output->startElement($this->_uri, 'LocURI'); 116 // Source URI sent from client is Target for the server 117 $this->output->characters($state->sourceURI); 118 $this->output->endElement($this->_uri, 'LocURI'); 119 if ($state->user) { 120 $this->output->startElement($this->_uri, 'LocName'); 121 $this->output->characters($state->user); 122 $this->output->endElement($this->_uri, 'LocName'); 123 } 124 $this->output->endElement($this->_uri, 'Target'); 125 126 $this->output->startElement($this->_uri, 'Source'); 127 $this->output->startElement($this->_uri, 'LocURI'); 128 // Target URI sent from client is Source for the server 129 $this->output->characters($state->targetURI); 130 $this->output->endElement($this->_uri, 'LocURI'); 131 $this->output->endElement($this->_uri, 'Source'); 132 133 if ($respURI) { 134 $this->output->startElement($this->_uri, 'RespURI'); 135 $this->output->characters($respURI); 136 $this->output->endElement($this->_uri, 'RespURI'); 137 } 138 139 // @Todo: omit this in SyncML1.0? 140 $this->output->startElement($this->_uri, 'Meta'); 141 142 // Dummy Max MsqSize, this is just put in to make the packet 143 // work, it is not a real value. 144 $this->output->startElement($this->_uriMeta, 'MaxMsgSize'); 145 $chars = Horde_SyncMl::SERVER_MAXMSGSIZE; // 1Meg 146 $this->output->characters($chars); 147 $this->output->endElement($this->_uriMeta, 'MaxMsgSize'); 148 149 150 // MaxObjSize, required by protocol for SyncML1.1 and higher. 151 if ($state->version > 0) { 152 $this->output->startElement($this->_uriMeta, 'MaxObjSize'); 153 $this->output->characters(Horde_SyncMl::SERVER_MAXOBJSIZE); 154 $this->output->endElement($this->_uriMeta, 'MaxObjSize'); 155 } 156 $this->output->endElement($this->_uri, 'Meta'); 157 158 $this->output->endElement($this->_uri, 'SyncHdr'); 159 } 160 161 public function outputInit() 162 { 163 $this->_uri = $GLOBALS['backend']->state->getURI(); 164 165 $this->output->startElement($this->_uri, 'SyncML', array()); 166 } 167 168 public function outputBodyStart() 169 { 170 $this->output->startElement($this->_uri, 'SyncBody', array()); 171 } 172 173 public function outputFinal() 174 { 175 $this->output->startElement($this->_uri, 'Final', array()); 176 $this->output->endElement($this->_uri, 'Final'); 177 } 178 179 public function outputEnd() 180 { 181 $this->output->endElement($this->_uri, 'SyncBody', array()); 182 $this->output->endElement($this->_uri, 'SyncML', array()); 183 } 184 185 186 public function outputStatus($cmdRef, $cmd, $data, 187 $targetRef = '', $sourceRef = '', 188 $syncAnchorNext = '', 189 $syncAnchorLast = '') 190 { 191 $state = $GLOBALS['backend']->state; 192 $uriMeta = $state->uriMeta; 193 194 $this->output->startElement($this->_uri, 'Status'); 195 $this->_outputCmdID(); 196 197 $this->output->startElement($this->_uri, 'MsgRef'); 198 $chars = $state->messageID; 199 $this->output->characters($chars); 200 $this->output->endElement($this->_uri, 'MsgRef'); 201 202 $this->output->startElement($this->_uri, 'CmdRef'); 203 $chars = $cmdRef; 204 $this->output->characters($chars); 205 $this->output->endElement($this->_uri, 'CmdRef'); 206 207 $this->output->startElement($this->_uri, 'Cmd'); 208 $chars = $cmd; 209 $this->output->characters($chars); 210 $this->output->endElement($this->_uri, 'Cmd'); 211 212 if (!empty($targetRef)) { 213 $this->output->startElement($this->_uri, 'TargetRef'); 214 $this->output->characters($targetRef); 215 $this->output->endElement($this->_uri, 'TargetRef'); 216 } 217 218 if (!empty($sourceRef)) { 219 $this->output->startElement($this->_uri, 'SourceRef'); 220 $this->output->characters($sourceRef); 221 $this->output->endElement($this->_uri, 'SourceRef'); 222 } 223 224 // If we are responding to the SyncHdr and we are not 225 // authenticated then request basic authorization. 226 if ($cmd == 'SyncHdr' && !$state->authenticated) { 227 // Keep Horde_SyncMl::RESPONSE_CREDENTIALS_MISSING, otherwise set to 228 // Horde_SyncMl::RESPONSE_INVALID_CREDENTIALS. 229 $data = $data == Horde_SyncMl::RESPONSE_CREDENTIALS_MISSING 230 ? Horde_SyncMl::RESPONSE_CREDENTIALS_MISSING 231 : Horde_SyncMl::RESPONSE_INVALID_CREDENTIALS; 232 233 $this->output->startElement($this->_uri, 'Chal'); 234 $this->output->startElement($this->_uri, 'Meta'); 235 236 $this->output->startElement($uriMeta, 'Type'); 237 $this->output->characters('syncml:auth-basic'); 238 $this->output->endElement($uriMeta, 'Type'); 239 240 $this->output->startElement($uriMeta, 'Format'); 241 $this->output->characters('b64'); 242 $this->output->endElement($uriMeta, 'Format'); 243 244 $this->output->endElement($this->_uri, 'Meta'); 245 $this->output->endElement($this->_uri, 'Chal'); 246 247 } 248 249 $this->output->startElement($this->_uri, 'Data'); 250 $this->output->characters($data); 251 $this->output->endElement($this->_uri, 'Data'); 252 253 if (!empty($syncAnchorNext) || !empty($syncAnchorNLast)) { 254 $this->output->startElement($this->_uri, 'Item'); 255 $this->output->startElement($this->_uri, 'Data'); 256 257 $this->output->startElement($uriMeta, 'Anchor'); 258 259 if (!empty($syncAnchorLast)) { 260 $this->output->startElement($uriMeta, 'Last'); 261 $this->output->characters($syncAnchorLast); 262 $this->output->endElement($uriMeta, 'Last'); 263 } 264 265 if (!empty($syncAnchorNext)) { 266 $this->output->startElement($uriMeta, 'Next'); 267 $this->output->characters($syncAnchorNext); 268 $this->output->endElement($uriMeta, 'Next'); 269 } 270 271 $this->output->endElement($uriMeta, 'Anchor'); 272 273 $this->output->endElement($this->_uri, 'Data'); 274 $this->output->endElement($this->_uri, 'Item'); 275 } 276 277 $this->output->endElement($this->_uri, 'Status'); 278 279 } 280 281 public function outputDevInf($cmdRef) 282 { 283 $state = $GLOBALS['backend']->state; 284 $uriMeta = $state->uriMeta; 285 $uriDevInf = $state->uriDevInf; 286 287 $this->output->startElement($this->_uri, 'Results'); 288 $this->_outputCmdID(); 289 290 $this->output->startElement($this->_uri, 'MsgRef'); 291 $chars = $state->messageID; 292 $this->output->characters($chars); 293 $this->output->endElement($this->_uri, 'MsgRef'); 294 295 $this->output->startElement($this->_uri, 'CmdRef'); 296 $chars = $cmdRef; 297 $this->output->characters($chars); 298 $this->output->endElement($this->_uri, 'CmdRef'); 299 300 $this->output->startElement($this->_uri, 'Meta'); 301 $this->output->startElement($uriMeta, 'Type'); 302 if ($state->wbxml) { 303 $this->output->characters(Horde_SyncMl::MIME_SYNCML_DEVICE_INFO_WBXML); 304 } else { 305 $this->output->characters(Horde_SyncMl::MIME_SYNCML_DEVICE_INFO_XML); 306 } 307 308 $this->output->endElement($uriMeta, 'Type'); 309 $this->output->endElement($this->_uri, 'Meta'); 310 311 $this->output->startElement($this->_uri, 'Item'); 312 $this->output->startElement($this->_uri, 'Source'); 313 $this->output->startElement($this->_uri, 'LocURI'); 314 $this->output->characters($state->getDevInfURI()); 315 $this->output->endElement($this->_uri, 'LocURI'); 316 $this->output->endElement($this->_uri, 'Source'); 317 318 $this->output->startElement($this->_uri, 'Data'); 319 320 /* DevInf data is stored in wbxml not as a seperate codepage but 321 * rather as a complete wbxml stream as opaque data. So we need a 322 * new Handler. */ 323 $devinfoutput = $this->output->createSubHandler(); 324 325 $devinfoutput->startElement($uriDevInf , 'DevInf'); 326 $devinfoutput->startElement($uriDevInf , 'VerDTD'); 327 $devinfoutput->characters($state->getVerDTD()); 328 $devinfoutput->endElement($uriDevInf , 'VerDTD'); 329 $devinfoutput->startElement($uriDevInf , 'Man'); 330 $devinfoutput->characters('The Horde Project (http://www.horde.org/)'); 331 $devinfoutput->endElement($uriDevInf , 'Man'); 332 $devinfoutput->startElement($uriDevInf , 'DevID'); 333 $devinfoutput->characters(isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost'); 334 $devinfoutput->endElement($uriDevInf , 'DevID'); 335 $devinfoutput->startElement($uriDevInf , 'DevTyp'); 336 $devinfoutput->characters('server'); 337 $devinfoutput->endElement($uriDevInf , 'DevTyp'); 338 339 if ($state->version > 0) { 340 $devinfoutput->startElement($uriDevInf , 'SupportLargeObjs'); 341 $devinfoutput->endElement($uriDevInf , 'SupportLargeObjs'); 342 343 $devinfoutput->startElement($uriDevInf , 'SupportNumberOfChanges'); 344 $devinfoutput->endElement($uriDevInf , 'SupportNumberOfChanges'); 345 } 346 $this->_writeDataStore('notes', 'text/plain', '1.0', $devinfoutput); 347 $this->_writeDataStore('contacts', 'text/directory', '3.0', 348 $devinfoutput, array('text/x-vcard' => '2.1')); 349 $this->_writeDataStore('tasks', 'text/calendar', '2.0', $devinfoutput, 350 array('text/x-vcalendar' => '1.0')); 351 $this->_writeDataStore('calendar', 'text/calendar', '2.0', 352 $devinfoutput, 353 array('text/x-vcalendar' => '1.0')); 354 $devinfoutput->endElement($uriDevInf , 'DevInf'); 355 356 $this->output->opaque($devinfoutput->getOutput()); 357 $this->output->endElement($this->_uri, 'Data'); 358 $this->output->endElement($this->_uri, 'Item'); 359 $this->output->endElement($this->_uri, 'Results'); 360 } 361 362 /** 363 * Writes DevInf data for one DataStore. 364 * 365 * @param string $sourceref Data for <SourceRef> element. 366 * @param string $mimetype Data for <Rx-Pref><CTType> and 367 * <Tx-Pref><CTType>. 368 * @param string $version Data for <Rx-Pref><VerCT> and 369 * <Tx-Pref><VerCT>. 370 * @param Horde_Xml_Wbxml_ContentHandler $output Content handler that will 371 * received the output. 372 * @param array $additionaltypes Array of additional types for 373 * <Tx> and <Rx>; format 374 * array('text/directory' => '3.0') 375 */ 376 protected function _writeDataStore($sourceref, $mimetype, $version, &$output, 377 $additionaltypes = array()) 378 { 379 $uriDevInf = $GLOBALS['backend']->state->uriDevInf; 380 381 $output->startElement($uriDevInf , 'DataStore'); 382 $output->startElement($uriDevInf , 'SourceRef'); 383 $output->characters($sourceref); 384 $output->endElement($uriDevInf , 'SourceRef'); 385 386 $output->startElement($uriDevInf , 'Rx-Pref'); 387 $output->startElement($uriDevInf , 'CTType'); 388 $output->characters($mimetype); 389 $output->endElement($uriDevInf , 'CTType'); 390 $output->startElement($uriDevInf , 'VerCT'); 391 $output->characters($version); 392 $output->endElement($uriDevInf , 'VerCT'); 393 $output->endElement($uriDevInf , 'Rx-Pref'); 394 395 foreach ($additionaltypes as $ct => $ctver){ 396 $output->startElement($uriDevInf , 'Rx'); 397 $output->startElement($uriDevInf , 'CTType'); 398 $output->characters($ct); 399 $output->endElement($uriDevInf , 'CTType'); 400 $output->startElement($uriDevInf , 'VerCT'); 401 $output->characters($ctver); 402 $output->endElement($uriDevInf , 'VerCT'); 403 $output->endElement($uriDevInf , 'Rx'); 404 } 405 406 $output->startElement($uriDevInf , 'Tx-Pref'); 407 $output->startElement($uriDevInf , 'CTType'); 408 $output->characters($mimetype); 409 $output->endElement($uriDevInf , 'CTType'); 410 $output->startElement($uriDevInf , 'VerCT'); 411 $output->characters($version); 412 $output->endElement($uriDevInf , 'VerCT'); 413 $output->endElement($uriDevInf , 'Tx-Pref'); 414 415 foreach ($additionaltypes as $ct => $ctver){ 416 $output->startElement($uriDevInf , 'Tx'); 417 $output->startElement($uriDevInf , 'CTType'); 418 $output->characters($ct); 419 $output->endElement($uriDevInf , 'CTType'); 420 $output->startElement($uriDevInf , 'VerCT'); 421 $output->characters($ctver); 422 $output->endElement($uriDevInf , 'VerCT'); 423 $output->endElement($uriDevInf , 'Tx'); 424 } 425 426 $output->startElement($uriDevInf , 'SyncCap'); 427 // We support all sync Types from 1-6: two way, slow, refresh|update 428 // from client|server 429 for ($i = 1; $i <= 6; ++$i) { 430 $output->startElement($uriDevInf , 'SyncType'); 431 $output->characters($i); 432 $output->endElement($uriDevInf , 'SyncType'); 433 } 434 $output->endElement($uriDevInf , 'SyncCap'); 435 $output->endElement($uriDevInf , 'DataStore'); 436 } 437 438 public function outputAlert($alertCode, $clientDB = '', $serverDB = '', $lastAnchor = '', $nextAnchor = '') 439 { 440 $uriMeta = $GLOBALS['backend']->state->uriMeta; 441 442 $this->output->startElement($this->_uri, 'Alert'); 443 $this->_outputCmdID(); 444 445 $this->output->startElement($this->_uri, 'Data'); 446 $chars = $alertCode; 447 $this->output->characters($chars); 448 $this->output->endElement($this->_uri, 'Data'); 449 450 $this->output->startElement($this->_uri, 'Item'); 451 452 if (!empty($clientDB)) { 453 $this->output->startElement($this->_uri, 'Target'); 454 $this->output->startElement($this->_uri, 'LocURI'); 455 $this->output->characters($clientDB); 456 $this->output->endElement($this->_uri, 'LocURI'); 457 $this->output->endElement($this->_uri, 'Target'); 458 } 459 460 if (!empty($serverDB)) { 461 $this->output->startElement($this->_uri, 'Source'); 462 $this->output->startElement($this->_uri, 'LocURI'); 463 $this->output->characters($serverDB); 464 $this->output->endElement($this->_uri, 'LocURI'); 465 $this->output->endElement($this->_uri, 'Source'); 466 } 467 468 $this->output->startElement($this->_uri, 'Meta'); 469 470 $this->output->startElement($uriMeta, 'Anchor'); 471 472 $this->output->startElement($uriMeta, 'Last'); 473 $this->output->characters($lastAnchor); 474 $this->output->endElement($uriMeta, 'Last'); 475 476 $this->output->startElement($uriMeta, 'Next'); 477 $this->output->characters($nextAnchor); 478 $this->output->endElement($uriMeta, 'Next'); 479 480 $this->output->endElement($uriMeta, 'Anchor'); 481 482 483 // MaxObjSize, required by protocol for SyncML1.1 and higher. 484 if ($GLOBALS['backend']->state->version > 0) { 485 $this->output->startElement($uriMeta, 'MaxObjSize'); 486 $this->output->characters(Horde_SyncMl::SERVER_MAXOBJSIZE); 487 $this->output->endElement($uriMeta, 'MaxObjSize'); 488 } 489 $this->output->endElement($this->_uri, 'Meta'); 490 491 $this->output->endElement($this->_uri, 'Item'); 492 $this->output->endElement($this->_uri, 'Alert'); 493 494 } 495 496 497 public function outputGetDevInf() 498 { 499 $state = $GLOBALS['backend']->state; 500 $uriMeta = $state->uriMeta; 501 502 $this->output->startElement($this->_uri, 'Get'); 503 $this->_outputCmdID(); 504 505 $this->output->startElement($this->_uri, 'Meta'); 506 $this->output->startElement($uriMeta, 'Type'); 507 if ($state->wbxml) { 508 $chars = Horde_SyncMl::MIME_SYNCML_DEVICE_INFO_WBXML; 509 } else { 510 $chars = Horde_SyncMl::MIME_SYNCML_DEVICE_INFO_XML; 511 } 512 $this->output->characters($chars); 513 $this->output->endElement($uriMeta, 'Type'); 514 $this->output->endElement($this->_uri, 'Meta'); 515 516 $this->output->startElement($this->_uri, 'Item'); 517 $this->output->startElement($this->_uri, 'Target'); 518 $this->output->startElement($this->_uri, 'LocURI'); 519 $this->output->characters($state->getDevInfURI()); 520 $this->output->endElement($this->_uri, 'LocURI'); 521 $this->output->endElement($this->_uri, 'Target'); 522 $this->output->endElement($this->_uri, 'Item'); 523 524 $this->output->endElement($this->_uri, 'Get'); 525 } 526 527 /** 528 * Creates a single Sync command 529 * 530 * @param string $command The Sync command (Add, Delete, Replace). 531 * @param string $content The actual object content. 532 * @param string $contentType The content's MIME type. 533 * @param string $encodingType The content encoding of the object. 534 * @param string $cuid The client's object UID. 535 * @param string $suid The server's object UID. 536 * @param int $contentSize The total content size. 537 * @param bool $moreData Add MoreData tag when splitting messages. 538 * 539 * @return integer The CmdID used for this command. 540 */ 541 public function outputSyncCommand($command, $content = null, $contentType = null, 542 $encodingType = null, $cuid = null, $suid = null, 543 $contentSize = null, $moreData = false) 544 { 545 $uriMeta = $GLOBALS['backend']->state->uriMeta; 546 547 $this->output->startElement($this->_uri, $command); 548 $this->_outputCmdID(); 549 550 if (isset($contentType)) { 551 $this->output->startElement($this->_uri, 'Meta'); 552 $this->output->startElement($uriMeta, 'Type'); 553 $this->output->characters($contentType); 554 $this->output->endElement($uriMeta, 'Type'); 555 if (isset($content) && isset($contentSize)) { 556 $this->output->startElement($uriMeta, 'Size'); 557 $this->output->characters($contentSize); 558 $this->output->endElement($uriMeta, 'Size'); 559 } 560 $this->output->endElement($this->_uri, 'Meta'); 561 } 562 563 if (isset($content) || isset($cuid) || isset($suid)) { 564 $this->output->startElement($this->_uri, 'Item'); 565 if ($suid != null) { 566 $this->output->startElement($this->_uri, 'Source'); 567 $this->output->startElement($this->_uri, 'LocURI'); 568 $this->output->characters($suid); 569 $this->output->endElement($this->_uri, 'LocURI'); 570 $this->output->endElement($this->_uri, 'Source'); 571 } 572 573 if ($cuid != null) { 574 $this->output->startElement($this->_uri, 'Target'); 575 $this->output->startElement($this->_uri, 'LocURI'); 576 $this->output->characters($cuid); 577 $this->output->endElement($this->_uri, 'LocURI'); 578 $this->output->endElement($this->_uri, 'Target'); 579 } 580 581 if (!empty($encodingType)) { 582 $this->output->startElement($this->_uri, 'Meta'); 583 $this->output->startElement($uriMeta, 'Format'); 584 $this->output->characters($encodingType); 585 $this->output->endElement($uriMeta, 'Format'); 586 $this->output->endElement($this->_uri, 'Meta'); 587 } 588 if (isset($content)) { 589 $this->output->startElement($this->_uri, 'Data'); 590 if($this->isWBXML()) { 591 $this->output->characters($content); 592 } else { 593 $device = $GLOBALS['backend']->state->getDevice(); 594 if ($device->useCdataTag()) { 595 /* Enclose data in CDATA if possible to avoid */ 596 /* problems with &,< and >. */ 597 $this->output->opaque('<![CDATA[' . $content . ']]>'); 598 } else { 599 $this->output->characters($content); 600 } 601 } 602 $this->output->endElement($this->_uri, 'Data'); 603 if ($moreData) { 604 $this->output->startElement($this->_uri, 'MoreData'); 605 $this->output->endElement($this->_uri, 'MoreData'); 606 } 607 } 608 $this->output->endElement($this->_uri, 'Item'); 609 } 610 611 $this->output->endElement($this->_uri, $command); 612 613 return $this->_msg_CmdID - 1; 614 } 615 616 public function outputSyncStart($clientLocURI, $serverLocURI, $numberOfChanges = null) 617 { 618 $this->output->startElement($this->_uri, 'Sync'); 619 $this->_outputCmdID(); 620 621 $this->output->startElement($this->_uri, 'Target'); 622 $this->output->startElement($this->_uri, 'LocURI'); 623 $this->output->characters($clientLocURI); 624 $this->output->endElement($this->_uri, 'LocURI'); 625 $this->output->endElement($this->_uri, 'Target'); 626 627 $this->output->startElement($this->_uri, 'Source'); 628 $this->output->startElement($this->_uri, 'LocURI'); 629 $this->output->characters($serverLocURI); 630 $this->output->endElement($this->_uri, 'LocURI'); 631 $this->output->endElement($this->_uri, 'Source'); 632 633 if (is_int($numberOfChanges)) { 634 $this->output->startElement($this->_uri, 'NumberOfChanges'); 635 $this->output->characters($numberOfChanges); 636 $this->output->endElement($this->_uri, 'NumberOfChanges'); 637 } 638 639 } 640 641 public function outputSyncEnd() 642 { 643 $this->output->endElement($this->_uri, 'Sync'); 644 } 645 646 647 // internal helper functions: 648 649 protected function _outputCmdID() 650 { 651 $this->output->startElement($this->_uri, 'CmdID'); 652 $this->output->characters($this->_msg_CmdID); 653 $this->_msg_CmdID++; 654 $this->output->endElement($this->_uri, 'CmdID'); 655 } 656 657 /** 658 * Output a single <ele>$str</ele> element. 659 */ 660 protected function _singleEle($tag, $str, $uri = null) 661 { 662 if (empty($uri)) { 663 $uri = $this->_uri; 664 } 665 $this->output->startElement($uri, $tag); 666 $this->output->characters($str); 667 $this->output->endElement($uri, $tag); 668 } 669} 670