/framework/ActiveSync/lib/Horde/ActiveSync/Connector/Importer.php

https://github.com/ewandor/horde · PHP · 321 lines · 128 code · 37 blank · 156 comment · 26 complexity · 7c1faeb320a7bce3c0e7aebed47e613c MD5 · raw file

  1. <?php
  2. /**
  3. * Connector class for importing ActiveSync messages from the wbxml input stream
  4. * Contains code written by the Z-Push project. Original file header preserved
  5. * below.
  6. *
  7. * Copyright 2010-2012 Horde LLC (http://www.horde.org/)
  8. *
  9. * @author Michael J. Rubinsky <mrubinsk@horde.org>
  10. * @package ActiveSync
  11. */
  12. /**
  13. * File : streamimporter.php
  14. * Project : Z-Push
  15. * Descr : Stream import classes
  16. *
  17. * Created : 01.10.2007
  18. *
  19. * © Zarafa Deutschland GmbH, www.zarafaserver.de
  20. * This file is distributed under GPL-2.0.
  21. * Consult COPYING file for details
  22. */
  23. class Horde_ActiveSync_Connector_Importer
  24. {
  25. /**
  26. * State machine
  27. *
  28. * @var Horde_ActiveSync_State_Base
  29. */
  30. protected $_state;
  31. /**
  32. * The backend driver for communicating with the server we are syncing with.
  33. *
  34. * @var Horde_ActiveSync_Driver_Base
  35. */
  36. protected $_backend;
  37. /**
  38. * Flags
  39. *
  40. * @var integer
  41. */
  42. protected $_flags;
  43. /**
  44. * The server specific folder id
  45. *
  46. * @var string
  47. */
  48. protected $_folderId;
  49. protected $_logger;
  50. /**
  51. * Const'r
  52. *
  53. * @param Horde_ActiveSync_Driver_Base $backend
  54. */
  55. public function __construct(Horde_ActiveSync_Driver_Base $backend)
  56. {
  57. $this->_backend = $backend;
  58. }
  59. /**
  60. * Initialize the exporter for this collection
  61. *
  62. * @param Horde_ActiveSync_State_Base $state The state machine
  63. * @param string $folderId The collection's id
  64. * @param integer $flags Any flags
  65. */
  66. public function init(Horde_ActiveSync_State_Base &$state, $folderId, $flags = 0)
  67. {
  68. $this->_state = &$state;
  69. $this->_flags = $flags;
  70. $this->_folderId = $folderId;
  71. }
  72. /**
  73. * Setter for a logger instance
  74. *
  75. * @param Horde_Log_Logger $logger The logger
  76. */
  77. public function setLogger($logger)
  78. {
  79. $this->_logger = $logger;
  80. }
  81. /**
  82. * Import a message change from the wbxml stream
  83. *
  84. * @param mixed $id A server message id or
  85. * false if a new message
  86. * @param Horde_ActiveSync_Message_Base $message A message object
  87. * @param StdClass $device A device descriptor
  88. * @param integer $clientid Client id sent from PIM
  89. * on message addition.
  90. *
  91. * @return mixed The server message id or false
  92. */
  93. public function importMessageChange($id, $message, $device, $clientid)
  94. {
  95. /* do nothing if it is in a dummy folder */
  96. if ($this->_folderId == Horde_ActiveSync::FOLDER_TYPE_DUMMY) {
  97. return false;
  98. }
  99. /* Changing an existing object */
  100. if ($id) {
  101. /* Check for conflicts */
  102. $conflict = $this->_isConflict('change', $this->_folderId, $id);
  103. /* Update client state before we attempt to save changes, so we
  104. * have a record of the change. This way, if the server change fails
  105. * the server copy will be re-sync'd back to the PIM, maintaining
  106. * at least some sort of consistency. */
  107. $change = array();
  108. $change['id'] = $id;
  109. // mod is 0 to force a re-synch in the case of server failure. This
  110. // is updated after the change succeeds in the next updateState()
  111. $change['mod'] = 0;
  112. $change['parent'] = $this->_folderId;
  113. $change['flags'] = (isset($message->read)) ? $message->read : 0;
  114. $this->_state->updateState('change', $change, Horde_ActiveSync::CHANGE_ORIGIN_NA);
  115. /* If this is a conflict, see if the server wins */
  116. if ($conflict && $this->_flags == Horde_ActiveSync::CONFLICT_OVERWRITE_PIM) {
  117. return $id;
  118. }
  119. } else {
  120. if ($uid = $this->_state->isDuplicatePIMAddition($clientid)) {
  121. // Already saw this addition, but PIM never received UID
  122. return $uid;
  123. }
  124. }
  125. /* Tell the backend about the change */
  126. if (!$stat = $this->_backend->changeMessage($this->_folderId, $id, $message, $device)) {
  127. return false;
  128. }
  129. $stat['parent'] = $this->_folderId;
  130. /* Record the state of the message */
  131. $this->_state->updateState(
  132. 'change', $stat, Horde_ActiveSync::CHANGE_ORIGIN_PIM,
  133. $this->_backend->getUser(), $clientid);
  134. return $stat['id'];
  135. }
  136. /**
  137. * Import a message deletion. This may conflict if the local object has been
  138. * modified.
  139. *
  140. * @param string $id Server message uid
  141. *
  142. * @return boolean
  143. */
  144. public function importMessageDeletion($id)
  145. {
  146. /* Do nothing if it is in a dummy folder */
  147. if ($this->_folderId == Horde_ActiveSync::FOLDER_TYPE_DUMMY) {
  148. return true;
  149. }
  150. /* Check for conflict */
  151. $conflict = $this->_isConflict('delete', $this->_folderId, $id);
  152. /* Update client state */
  153. $change = array();
  154. $change['id'] = $id;
  155. $change['mod'] = time();
  156. $change['parent'] = $this->_folderId;
  157. $this->_state->updateState('delete', $change, Horde_ActiveSync::CHANGE_ORIGIN_PIM, $this->_backend->getUser());
  158. /* If server wins the conflict, don't import change - it will be
  159. * detected on next sync and sent back to PIM (since we updated the PIM
  160. * state). */
  161. if ($conflict && $this->_flags == Horde_ActiveSync::CONFLICT_OVERWRITE_PIM) {
  162. return true;
  163. }
  164. /* Tell backend about the deletion */
  165. $this->_backend->deleteMessage($this->_folderId, $id);
  166. return true;
  167. }
  168. /**
  169. * Import a change in 'read' flags .. This can never conflict
  170. *
  171. * @param string $id Server message id
  172. * @param ?? $flags The read flags to set
  173. */
  174. public function importMessageReadFlag($id, $flags)
  175. {
  176. /* Do nothing if it is a dummy folder */
  177. if ($this->_folderId == Horde_ActiveSync::FOLDER_TYPE_DUMMY) {
  178. return true;
  179. }
  180. /* Update client state */
  181. $change = array();
  182. $change['id'] = $id;
  183. $change['flags'] = $flags;
  184. $this->_state->updateState('flags', $change, Horde_ActiveSync::CHANGE_ORIGIN_NA);
  185. /* Tell backend */
  186. $this->_backend->setReadFlag($this->_folderId, $id, $flags);
  187. return true;
  188. }
  189. /**
  190. * Perform a message move initiated on the PIM.
  191. *
  192. * @TODO
  193. *
  194. * @param string $id The message id
  195. * @param $newfolder
  196. *
  197. * @return boolean
  198. */
  199. public function importMessageMove($id, $newfolder)
  200. {
  201. return true;
  202. }
  203. /**
  204. * Import a folder change from the wbxml stream
  205. *
  206. * @param string $id The folder id
  207. * @param string $parent The parent folder id?
  208. * @param string $displayname The folder display name
  209. * @param <unknown_type> $type The collection type?
  210. *
  211. * @return boolean
  212. */
  213. public function importFolderChange($id, $parent, $displayname, $type)
  214. {
  215. /* do nothing if it is a dummy folder */
  216. if ($parent == Horde_ActiveSync::FOLDER_TYPE_DUMMY) {
  217. return false;
  218. }
  219. if ($id) {
  220. $change = array();
  221. $change['id'] = $id;
  222. $change['mod'] = $displayname;
  223. $change['parent'] = $parent;
  224. $change['flags'] = 0;
  225. $this->_state->updateState('change', $change, Horde_ActiveSync::CHANGE_ORIGIN_NA);
  226. }
  227. /* Tell the backend */
  228. $stat = $this->_backend->ChangeFolder($parent, $id, $displayname, $type);
  229. if ($stat) {
  230. $this->_state->updateState('change', $stat, Horde_ActiveSync::CHANGE_ORIGIN_NA);
  231. }
  232. return $stat['id'];
  233. }
  234. /**
  235. * Imports a folder deletion from the PIM
  236. *
  237. * @param string $id The folder id
  238. * @param string $parent The folder id of the parent folder
  239. *
  240. * @return boolean
  241. */
  242. public function importFolderDeletion($id, $parent)
  243. {
  244. /* Do nothing if it is a dummy folder */
  245. if ($parent == Horde_ActiveSync::FOLDER_TYPE_DUMMY) {
  246. return false;
  247. }
  248. $change = array();
  249. $change['id'] = $id;
  250. $this->_state->updateState('delete', $change, Horde_ActiveSync::CHANGE_ORIGIN_NA);
  251. $this->_backend->deleteFolder($parent, $id);
  252. return true;
  253. }
  254. /**
  255. * Check if this change conflicts with server changes
  256. * This is only true in the following situations:
  257. *
  258. * Changed here and changed there
  259. * Changed here and deleted there
  260. * Deleted here and changed there
  261. *
  262. * Any other combination of operations can be done
  263. * (e.g. change flags & move or move & delete)
  264. *
  265. * @param string $type The type of change('change', 'delete' etc...)
  266. * @param string $folderid The id of the folder this change is from.
  267. * @param string $id The uid for the changed message.
  268. *
  269. * @return boolean
  270. */
  271. protected function _isConflict($type, $folderid, $id)
  272. {
  273. $stat = $this->_backend->statMessage($folderid, $id);
  274. if (!$stat) {
  275. /* Message is gone, if type is change, this is a conflict */
  276. if ($type == 'change') {
  277. return true;
  278. } else {
  279. return false;
  280. }
  281. }
  282. return $this->_state->isConflict($stat, $type);
  283. }
  284. }