Total Complexity | 463 |
Total Lines | 2194 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like mail_zpush often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use mail_zpush, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
23 | class mail_zpush implements activesync_plugin_write, activesync_plugin_sendmail, activesync_plugin_meeting_response, activesync_plugin_search_mailbox |
||
24 | { |
||
25 | /** |
||
26 | * var activesync_backend |
||
27 | */ |
||
28 | private $backend; |
||
29 | |||
30 | /** |
||
31 | * Instance of Mail |
||
32 | * |
||
33 | * @var Mail |
||
34 | */ |
||
35 | private $mail; |
||
36 | |||
37 | /** |
||
38 | * Provides the ability to change the line ending |
||
39 | * @var string |
||
40 | */ |
||
41 | public static $LE = "\n"; |
||
42 | |||
43 | /** |
||
44 | * Integer id of trash folder |
||
45 | * |
||
46 | * @var mixed |
||
47 | */ |
||
48 | private $_wasteID = false; |
||
49 | |||
50 | /** |
||
51 | * Integer id of sent folder |
||
52 | * |
||
53 | * @var mixed |
||
54 | */ |
||
55 | private $_sentID = false; |
||
56 | |||
57 | /** |
||
58 | * Integer id of current mail account / connection |
||
59 | * |
||
60 | * @var int |
||
61 | */ |
||
62 | private $account; |
||
63 | |||
64 | private $folders; |
||
65 | |||
66 | private $messages; |
||
67 | |||
68 | static $profileID; |
||
69 | |||
70 | // to control how deep one may dive into the past |
||
71 | const PAST_LIMIT = 178; |
||
72 | |||
73 | /** |
||
74 | * debugLevel - enables more debug |
||
75 | * |
||
76 | * @var int |
||
77 | */ |
||
78 | private $debugLevel = 0; |
||
79 | |||
80 | /** |
||
81 | * Constructor |
||
82 | * |
||
83 | * @param activesync_backend $backend |
||
84 | */ |
||
85 | public function __construct(activesync_backend $backend) |
||
86 | { |
||
87 | if ($GLOBALS['egw_setup']) return; |
||
88 | |||
89 | //$this->debugLevel=2; |
||
90 | $this->backend = $backend; |
||
91 | if (!isset($GLOBALS['egw_info']['user']['preferences']['activesync']['mail-ActiveSyncProfileID'])) |
||
92 | { |
||
93 | if ($this->debugLevel>1) error_log(__METHOD__.__LINE__.' Noprefs set: using 0 as default'); |
||
94 | // globals preferences add appname varname value |
||
95 | $GLOBALS['egw']->preferences->add('activesync','mail-ActiveSyncProfileID',0,'user'); |
||
96 | // save prefs |
||
97 | $GLOBALS['egw']->preferences->save_repository(true); |
||
98 | } |
||
99 | if ($this->debugLevel>1) error_log(__METHOD__.__LINE__.' ActiveProfileID:'.array2string(self::$profileID)); |
||
100 | |||
101 | if (is_null(self::$profileID)) |
||
102 | { |
||
103 | if ($this->debugLevel>1) error_log(__METHOD__.__LINE__.' self::ProfileID isNUll:'.array2string(self::$profileID)); |
||
104 | self::$profileID =& Api\Cache::getSession('mail','activeSyncProfileID'); |
||
105 | if ($this->debugLevel>1) error_log(__METHOD__.__LINE__.' ActiveProfileID (after reading Cache):'.array2string(self::$profileID)); |
||
106 | } |
||
107 | if (isset($GLOBALS['egw_info']['user']['preferences']['activesync']['mail-ActiveSyncProfileID'])) |
||
108 | { |
||
109 | if ($this->debugLevel>1) error_log(__METHOD__.__LINE__.' Pref for ProfileID:'.array2string($GLOBALS['egw_info']['user']['preferences']['activesync']['mail-ActiveSyncProfileID'])); |
||
110 | if ($GLOBALS['egw_info']['user']['preferences']['activesync']['mail-ActiveSyncProfileID'] == 'G') |
||
111 | { |
||
112 | self::$profileID = 'G'; // this should trigger the fetch of the first negative profile (or if no negative profile is available the firstb there is) |
||
113 | } |
||
114 | else |
||
115 | { |
||
116 | self::$profileID = (int)$GLOBALS['egw_info']['user']['preferences']['activesync']['mail-ActiveSyncProfileID']; |
||
117 | } |
||
118 | } |
||
119 | if ($this->debugLevel>1) error_log(__METHOD__.__LINE__.' Profile Selected (after reading Prefs):'.array2string(self::$profileID)); |
||
120 | |||
121 | // verify we are on an existing profile, if not running in setup (settings can not be static according to interface!) |
||
122 | if (!isset($GLOBALS['egw_setup'])) |
||
123 | { |
||
124 | try { |
||
125 | Mail\Account::read(self::$profileID); |
||
126 | } |
||
127 | catch(Exception $e) { |
||
128 | unset($e); |
||
129 | self::$profileID = Mail\Account::get_default_acc_id(); |
||
130 | } |
||
131 | } |
||
132 | if ($this->debugLevel>0) error_log(__METHOD__.'::'.__LINE__.' ProfileSelected:'.self::$profileID); |
||
133 | //$this->debugLevel=0; |
||
134 | } |
||
135 | |||
136 | /** |
||
137 | * Populates $settings for the preferences |
||
138 | * |
||
139 | * @param array|string $hook_data |
||
140 | * @return array |
||
141 | */ |
||
142 | function egw_settings($hook_data) |
||
143 | { |
||
144 | //error_log(__METHOD__.__LINE__.array2string($hook_data)); |
||
145 | $identities = array(); |
||
146 | if (!isset($hook_data['setup']) && in_array($hook_data['type'], array('user', 'group'))) |
||
147 | { |
||
148 | $identities = iterator_to_array(Mail\Account::search((int)$hook_data['account_id'])); |
||
149 | } |
||
150 | $identities += array( |
||
151 | 'G' => lang('Primary Profile'), |
||
152 | ); |
||
153 | |||
154 | $settings['mail-ActiveSyncProfileID'] = array( |
||
155 | 'type' => 'select', |
||
156 | 'label' => 'eMail Account to sync', |
||
157 | 'name' => 'mail-ActiveSyncProfileID', |
||
158 | 'help' => 'eMail Account to sync ', |
||
159 | 'values' => $identities, |
||
160 | 'default'=> 'G', |
||
161 | 'xmlrpc' => True, |
||
162 | 'admin' => False, |
||
163 | ); |
||
164 | $settings['mail-allowSendingInvitations'] = array( |
||
165 | 'type' => 'select', |
||
166 | 'label' => 'allow sending of calendar invitations using this profile?', |
||
167 | 'name' => 'mail-allowSendingInvitations', |
||
168 | 'help' => 'control the sending of calendar invitations while using this profile', |
||
169 | 'values' => array( |
||
170 | 'sendifnocalnotif'=>'only send if there is no notification in calendar', |
||
171 | 'send'=>'yes, always send', |
||
172 | 'nosend'=>'no, do not send', |
||
173 | ), |
||
174 | 'xmlrpc' => True, |
||
175 | 'default' => 'sendifnocalnotif', |
||
176 | 'admin' => False, |
||
177 | ); |
||
178 | $settings['mail-maximumSyncRange'] = array( |
||
179 | 'type' => 'integer', |
||
180 | 'label' => lang('How many days to sync in the past when client does not specify a date-range (default %1)', self::PAST_LIMIT), |
||
181 | 'name' => 'mail-maximumSyncRange', |
||
182 | 'help' => 'if the client sets no sync range, you may override the setting (preventing client crash that may be caused by too many mails/too much data). If you want to sync way-back into the past: set a large number', |
||
183 | 'xmlrpc' => True, |
||
184 | 'admin' => False, |
||
185 | ); |
||
186 | |||
187 | /* |
||
188 | $sigOptions = array( |
||
189 | 'send'=>'yes, always add EGroupware signatures to outgoing mails', |
||
190 | 'nosend'=>'no, never add EGroupware signatures to outgoing mails', |
||
191 | ); |
||
192 | if (!isset($hook_data['setup']) && in_array($hook_data['type'], array('user', 'group'))&&$hook_data['account_id']) |
||
193 | { |
||
194 | $pID=self::$profileID; |
||
195 | if ($GLOBALS['egw_info']['user']['preferences']['activesync']['mail-ActiveSyncProfileID']=='G') |
||
196 | { |
||
197 | $pID=Mail\Account::get_default_acc_id(); |
||
198 | } |
||
199 | $acc = Mail\Account::read($pID); |
||
200 | error_log(__METHOD__.__LINE__.':'.$pID.'->'.array2string($acc)); |
||
201 | $Identities = Mail\Account::identities($pID); |
||
202 | foreach($Identities as &$identity) |
||
203 | { |
||
204 | $Identity = self::identity_name($identity); |
||
205 | } |
||
206 | error_log(__METHOD__.__LINE__.array2string($Identities)); |
||
207 | } |
||
208 | $settings['mail-useSignature'] = array( |
||
209 | 'type' => 'select', |
||
210 | 'label' => 'control if and which available signature is added to outgoing mails', |
||
211 | 'name' => 'mail-useSignature', |
||
212 | 'help' => 'control the use of signatures', |
||
213 | 'values' => $sigOptions, |
||
214 | 'xmlrpc' => True, |
||
215 | 'default' => 'nosend', |
||
216 | 'admin' => False, |
||
217 | ); |
||
218 | */ |
||
219 | return $settings; |
||
220 | } |
||
221 | |||
222 | /** |
||
223 | * Verify preferences |
||
224 | * |
||
225 | * @param array|string $hook_data |
||
226 | * @return array with error-messages from all plugins |
||
227 | */ |
||
228 | function verify_settings($hook_data) |
||
261 | } |
||
262 | |||
263 | /** |
||
264 | * Open IMAP connection |
||
265 | * |
||
266 | * @param int $account integer id of account to use |
||
267 | * @todo support different accounts |
||
268 | */ |
||
269 | private function _connect($account=0) |
||
270 | { |
||
271 | if (!$account) $account = self::$profileID ? self::$profileID : 0; |
||
272 | if ($this->mail && $this->account != $account) $this->_disconnect(); |
||
273 | |||
274 | $this->_wasteID = false; |
||
275 | $this->_sentID = false; |
||
276 | |||
277 | if (!$this->mail) |
||
278 | { |
||
279 | $this->account = $account; |
||
280 | // todo: tell mail which account to use |
||
281 | //error_log(__METHOD__.__LINE__.' create object with ProfileID:'.array2string(self::$profileID)); |
||
282 | $this->mail = Mail::getInstance(false,self::$profileID,true,false,true); |
||
283 | if (self::$profileID == 0 && isset($this->mail->icServer->ImapServerId) && !empty($this->mail->icServer->ImapServerId)) self::$profileID = $this->mail->icServer->ImapServerId; |
||
284 | } |
||
285 | else |
||
286 | { |
||
287 | //error_log(__METHOD__.__LINE__." connect with profileID: ".self::$profileID); |
||
288 | if (self::$profileID == 0 && isset($this->mail->icServer->ImapServerId) && !empty($this->mail->icServer->ImapServerId)) self::$profileID = $this->mail->icServer->ImapServerId; |
||
289 | } |
||
290 | $this->mail->openConnection(self::$profileID,false); |
||
291 | |||
292 | $this->_wasteID = $this->mail->getTrashFolder(false); |
||
293 | //error_log(__METHOD__.__LINE__.' TrashFolder:'.$this->_wasteID); |
||
294 | $this->_sentID = $this->mail->getSentFolder(false); |
||
295 | $this->mail->getOutboxFolder(true); |
||
296 | //error_log(__METHOD__.__LINE__.' SentFolder:'.$this->_sentID); |
||
297 | //error_log(__METHOD__.__LINE__.' Connection Status for ProfileID:'.self::$profileID.'->'.$this->mail->icServer->_connected); |
||
298 | } |
||
299 | |||
300 | /** |
||
301 | * Close IMAP connection |
||
302 | */ |
||
303 | private function _disconnect() |
||
304 | { |
||
305 | if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__); |
||
306 | if ($this->mail) $this->mail->closeConnection(); |
||
307 | |||
308 | unset($this->mail); |
||
309 | unset($this->account); |
||
310 | unset($this->folders); |
||
311 | } |
||
312 | |||
313 | /** |
||
314 | * GetFolderList |
||
315 | * |
||
316 | * @ToDo loop over available email accounts |
||
317 | */ |
||
318 | public function GetFolderList() |
||
319 | { |
||
320 | $folderlist = array(); |
||
321 | if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__); |
||
322 | /*foreach($available_accounts as $account)*/ $account = 0; |
||
323 | { |
||
324 | $this->_connect($account); |
||
325 | if (!isset($this->folders)) $this->folders = $this->mail->getFolderObjects(true,false,$_alwaysGetDefaultFolders=true); |
||
326 | if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($this->folders)); |
||
327 | |||
328 | foreach ($this->folders as $folder => $folderObj) { |
||
329 | if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' folder='.$folder); |
||
330 | $folderlist[] = $f = array( |
||
331 | 'id' => $this->createID($account,$folder), |
||
332 | 'mod' => $folderObj->shortDisplayName, |
||
333 | 'parent' => $this->getParentID($account,$folder), |
||
334 | ); |
||
335 | if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."() returning ".array2string($f)); |
||
336 | } |
||
337 | } |
||
338 | if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."() returning ".array2string($folderlist)); |
||
339 | |||
340 | return $folderlist; |
||
341 | } |
||
342 | |||
343 | /** |
||
344 | * Sends a message which is passed as rfc822. You basically can do two things |
||
345 | * 1) Send the message to an SMTP server as-is |
||
346 | * 2) Parse the message yourself, and send it some other way |
||
347 | * It is up to you whether you want to put the message in the sent items folder. If you |
||
348 | * want it in 'sent items', then the next sync on the 'sent items' folder should return |
||
349 | * the new message as any other new message in a folder. |
||
350 | * |
||
351 | * @param array $smartdata = IMAP-SendMail: SyncSendMail ( |
||
352 | * (S) clientid => SendMail-30722448149304 |
||
353 | * (S) saveinsent => empty |
||
354 | * (S) replacemime => null |
||
355 | * (S) accountid => null |
||
356 | * (S) source => SyncSendMailSource ( |
||
357 | * (S) folderid => 101000000000 |
||
358 | * (S) itemid => 33776 |
||
359 | * (S) longid => null |
||
360 | * (S) instanceid => null |
||
361 | * unsetVars(Array) size: 0 |
||
362 | * flags => false |
||
363 | * content => null |
||
364 | * ) |
||
365 | * (S) mime => Date: Tue, 23 Jun 2015 14:13:23 +0200 |
||
366 | *Subject: AW: Blauer himmel |
||
367 | *.... |
||
368 | * (S) replyflag => true |
||
369 | * (S) forwardflag => null |
||
370 | * unsetVars(Array) size: 0 |
||
371 | * flags => false |
||
372 | * content => null |
||
373 | *) |
||
374 | * |
||
375 | * @return boolean true on success, false on error |
||
376 | * |
||
377 | * @see eg. BackendIMAP::SendMail() |
||
378 | * @todo implement either here or in mail backend |
||
379 | * (maybe sending here and storing to sent folder in plugin, as sending is supposed to always work in EGroupware) |
||
380 | */ |
||
381 | public function SendMail($smartdata) |
||
856 | } |
||
857 | } |
||
858 | |||
859 | /** |
||
860 | * For meeting requests (iCal attachments with method='request') we call calendar plugin with iCal to get SyncMeetingRequest object, |
||
861 | * and do NOT return the attachment itself! |
||
862 | * |
||
863 | * @param string $folderid |
||
864 | * @param string $id |
||
865 | * @param ContentParameters $contentparameters parameters of the requested message (truncation, mimesupport etc) |
||
866 | * object with attributes foldertype, truncation, rtftruncation, conflict, filtertype, bodypref, deletesasmoves, filtertype, contentclass, mimesupport, conversationmode |
||
867 | * bodypref object with attributes: ]truncationsize, allornone, preview |
||
868 | * @return $messageobject|boolean false on error |
||
869 | */ |
||
870 | public function GetMessage($folderid, $id, $contentparameters) |
||
1192 | } |
||
1193 | |||
1194 | /** |
||
1195 | * Process response to meeting request |
||
1196 | * |
||
1197 | * mail plugin only extracts the iCal attachment and let's calendar plugin deal with adding it |
||
1198 | * |
||
1199 | * @see BackendDiff::MeetingResponse() |
||
1200 | * @param string $folderid folder of meeting request mail |
||
1201 | * @param int|string $requestid uid of mail with meeting request |
||
1202 | * @param int $response 1=accepted, 2=tentative, 3=decline |
||
1203 | * @return int|boolean id of calendar item, false on error |
||
1204 | */ |
||
1205 | function MeetingResponse($folderid, $requestid, $response) |
||
1206 | { |
||
1207 | if (!class_exists('calendar_zpush')) |
||
1208 | { |
||
1209 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."(...) no EGroupware calendar installed!"); |
||
1210 | return null; |
||
1211 | } |
||
1212 | if (!($stat = $this->StatMessage($folderid, $requestid))) |
||
1213 | { |
||
1214 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."($requestid, '$folderid', $response) returning FALSE (can NOT stat message)"); |
||
1215 | return false; |
||
1216 | } |
||
1217 | $ret = false; |
||
1218 | foreach($this->mail->getMessageAttachments($requestid, $_partID='', $_structure=null, $fetchEmbeddedImages=true, $fetchTextCalendar=true) as $key => $attach) |
||
1219 | { |
||
1220 | if (strtolower($attach['mimeType']) == 'text/calendar' && strtolower($attach['method']) == 'request' && |
||
1221 | ($attachment = $this->mail->getAttachment($requestid, $attach['partID'],0,false))) |
||
1222 | { |
||
1223 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."($requestid, '$folderid', $response) iCal found, calling now backend->MeetingResponse('$attachment[attachment]')"); |
||
1224 | |||
1225 | // calling backend again with iCal attachment, to let calendar add the event |
||
1226 | $ret = $this->backend->MeetingResponse($attachment['attachment'], |
||
1227 | $this->backend->createID('calendar',$GLOBALS['egw_info']['user']['account_id']), |
||
1228 | $response); |
||
1229 | |||
1230 | // delete message after meeting-response is processed successful by calendar |
||
1231 | if ($ret) $this->DeleteMessage($folderid, $requestid, null); |
||
1232 | break; |
||
1233 | } |
||
1234 | } |
||
1235 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."($requestid, '$folderid', $response) returning ".array2string($ret)); |
||
1236 | return $ret; |
||
1237 | } |
||
1238 | |||
1239 | /** |
||
1240 | * GetAttachmentData |
||
1241 | * Should return attachment data for the specified attachment. The passed attachment identifier is |
||
1242 | * the exact string that is returned in the 'AttName' property of an SyncAttachment. So, you should |
||
1243 | * encode any information you need to find the attachment in that 'attname' property. |
||
1244 | * |
||
1245 | * @param string $fid - id |
||
1246 | * @param string $attname - should contain (folder)id |
||
1247 | * @return SyncItemOperationsAttachment-object |
||
1248 | */ |
||
1249 | function GetAttachmentData($fid,$attname) { |
||
1250 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": $fid (attname: '$attname')"); |
||
1251 | return $this->_GetAttachmentData($fid,$attname); |
||
1252 | } |
||
1253 | |||
1254 | /** |
||
1255 | * ItemOperationsGetAttachmentData |
||
1256 | * Should return attachment data for the specified attachment. The passed attachment identifier is |
||
1257 | * the exact string that is returned in the 'AttName' property of an SyncAttachment. So, you should |
||
1258 | * encode any information you need to find the attachment in that 'attname' property. |
||
1259 | * |
||
1260 | * @param string $fid - id |
||
1261 | * @param string $attname - should contain (folder)id |
||
1262 | * @return SyncItemOperationsAttachment-object |
||
1263 | */ |
||
1264 | function ItemOperationsGetAttachmentData($fid,$attname) { |
||
1265 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": $fid (attname: '$attname')"); |
||
1266 | return $this->_GetAttachmentData($fid,$attname); |
||
1267 | } |
||
1268 | |||
1269 | /** |
||
1270 | * _GetAttachmentData implements |
||
1271 | * -ItemOperationsGetAttachmentData |
||
1272 | * -GetAttachmentData |
||
1273 | * |
||
1274 | * @param string $fid - id |
||
1275 | * @param string $attname - should contain (folder)id |
||
1276 | * @return SyncItemOperationsAttachment-object |
||
1277 | */ |
||
1278 | private function _GetAttachmentData($fid,$attname) |
||
1279 | { |
||
1280 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": $fid (attname: '$attname')".function_backtrace()); |
||
1281 | //error_log(__METHOD__.__LINE__." Fid: $fid (attname: '$attname')"); |
||
1282 | list($folderid, $id, $part) = explode(":", $attname); |
||
1283 | |||
1284 | $this->splitID($folderid, $account, $folder); |
||
1285 | |||
1286 | if (!isset($this->mail)) $this->mail = Mail::getInstance(false,self::$profileID,true,false,true); |
||
1287 | |||
1288 | $this->mail->reopen($folder); |
||
1289 | $attachment = $this->mail->getAttachment($id,$part,0,false,true,$folder); |
||
1290 | $SIOattachment = new SyncItemOperationsAttachment(); |
||
1291 | fseek($attachment['attachment'], 0, SEEK_SET); // z-push requires stream seeked to start |
||
1292 | $SIOattachment->data = $attachment['attachment']; |
||
1293 | //ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": $fid (attname: '$attname') Data:".$attachment['attachment']); |
||
1294 | if (isset($attachment['type']) ) |
||
1295 | $SIOattachment->contenttype = $attachment['type']; |
||
1296 | |||
1297 | unset($attachment); |
||
1298 | |||
1299 | return $SIOattachment; |
||
1300 | } |
||
1301 | |||
1302 | /** |
||
1303 | * StatMessage should return message stats, analogous to the folder stats (StatFolder). Entries are: |
||
1304 | * |
||
1305 | * 'id' => Server unique identifier for the message. Again, try to keep this short (under 20 chars) |
||
1306 | * 'flags' => simply '0' for unread, '1' for read |
||
1307 | * 'mod' => modification signature. As soon as this signature changes, the item is assumed to be completely |
||
1308 | * changed, and will be sent to the PDA as a whole. Normally you can use something like the modification |
||
1309 | * time for this field, which will change as soon as the contents have changed. |
||
1310 | * |
||
1311 | * @param string $folderid |
||
1312 | * @param int $id id (uid) of message |
||
1313 | * @return array |
||
1314 | */ |
||
1315 | public function StatMessage($folderid, $id) |
||
1316 | { |
||
1317 | $messages = $this->fetchMessages($folderid, NULL, $id); |
||
1318 | //ZLog::Write(LOGLEVEL_DEBUG, __METHOD__."('$folderid','$id') returning ".array2string($messages[$id])); |
||
1319 | return $messages[$id]; |
||
1320 | } |
||
1321 | |||
1322 | /** |
||
1323 | * Called when a message has been changed on the mobile. |
||
1324 | * Added support for FollowUp flag |
||
1325 | * |
||
1326 | * @param string $folderid id of the folder |
||
1327 | * @param string $id id of the message |
||
1328 | * @param SyncXXX $message the SyncObject containing a message |
||
1329 | * @param ContentParameters $contentParameters |
||
1330 | * |
||
1331 | * @access public |
||
1332 | * @return array same return value as StatMessage() |
||
1333 | * @throws StatusException could throw specific SYNC_STATUS_* exceptions |
||
1334 | */ |
||
1335 | function ChangeMessage($folderid, $id, $message, $contentParameters) |
||
1336 | { |
||
1337 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__." $folderid, $id,".array2string($message).",".array2string($contentParameters)); |
||
1338 | //unset($folderid, $id, $message, $contentParameters); |
||
1339 | $account = $folder = null; |
||
1340 | $this->splitID($folderid, $account, $folder); |
||
1341 | if (isset($message->flag)) { |
||
1342 | if (isset($message->flag->flagstatus) && $message->flag->flagstatus == 2) { |
||
1343 | $rv = $this->mail->flagMessages((($message->flag->flagstatus == 2) ? "flagged" : "unflagged"), $id,$folder); |
||
1344 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__." -> set ".array2string($id).' in Folder '.$folder." as " . (($message->flag->flagstatus == 2) ? "flagged" : "unflagged") . "-->". $rv); |
||
1345 | } else { |
||
1346 | $rv = $this->mail->flagMessages("unflagged", $id,$folder); |
||
1347 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__." -> set ".array2string($id).' in Folder '.$folder." as " . "unflagged" . "-->". $rv); |
||
1348 | } |
||
1349 | } |
||
1350 | return $this->StatMessage($folderid, $id); |
||
1351 | } |
||
1352 | |||
1353 | /** |
||
1354 | * This function is called when the user moves an item on the PDA. You should do whatever is needed |
||
1355 | * to move the message on disk. After this call, StatMessage() and GetMessageList() should show the items |
||
1356 | * to have a new parent. This means that it will disappear from GetMessageList() will not return the item |
||
1357 | * at all on the source folder, and the destination folder will show the new message |
||
1358 | * |
||
1359 | * @param string $folderid id of the source folder |
||
1360 | * @param string $id id of the message |
||
1361 | * @param string $newfolderid id of the destination folder |
||
1362 | * @param ContentParameters $contentParameters |
||
1363 | * |
||
1364 | * @return boolean status of the operation |
||
1365 | * @throws StatusException could throw specific SYNC_MOVEITEMSSTATUS_* exceptions |
||
1366 | */ |
||
1367 | public function MoveMessage($folderid, $id, $newfolderid, $contentParameters) |
||
1368 | { |
||
1369 | unset($contentParameters); // not used, but required by function signature |
||
1370 | ZLog::Write(LOGLEVEL_DEBUG, "IMAP-MoveMessage: (sfid: '$folderid' id: '$id' dfid: '$newfolderid' )"); |
||
1371 | $account = $srcFolder = $destFolder = null; |
||
1372 | $this->splitID($folderid, $account, $srcFolder); |
||
1373 | $this->splitID($newfolderid, $account, $destFolder); |
||
1374 | ZLog::Write(LOGLEVEL_DEBUG, "IMAP-MoveMessage: (SourceFolder: '$srcFolder' id: '$id' DestFolder: '$destFolder' )"); |
||
1375 | if (!isset($this->mail)) $this->mail = Mail::getInstance(false,self::$profileID,true,false,true); |
||
1376 | $this->mail->reopen($destFolder); |
||
1377 | $status = $this->mail->getFolderStatus($destFolder); |
||
1378 | $uidNext = $status['uidnext']; |
||
1379 | $this->mail->reopen($srcFolder); |
||
1380 | |||
1381 | // move message |
||
1382 | $rv = $this->mail->moveMessages($destFolder,(array)$id,true,$srcFolder,true); |
||
1383 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.": New Status of $destFolder :".array2string($status).", ReturnValOf moveMessage".array2string($rv)); // this may be true, so try using the nextUID value by examine |
||
1384 | // return the new id "as string" |
||
1385 | return ($rv===true ? $uidNext : $rv[$id]) . ""; |
||
1386 | } |
||
1387 | |||
1388 | /** |
||
1389 | * Get all messages of a folder with optional cutoffdate |
||
1390 | * |
||
1391 | * @param int $cutoffdate =null timestamp with cutoffdate, default 12 weeks |
||
1392 | */ |
||
1393 | public function GetMessageList($folderid, $cutoffdate=NULL) |
||
1394 | { |
||
1395 | if ($cutoffdate > 0) |
||
1396 | { |
||
1397 | ZLog::Write(LOGLEVEL_DEBUG, __METHOD__.' for Folder:'.$folderid.' SINCE:'.$cutoffdate.'/'.date("d-M-Y", $cutoffdate)); |
||
1398 | } |
||
1399 | else |
||
1400 | { |
||
1401 | $maximumSyncRangeInDays = self::PAST_LIMIT; // corresponds to our default value |
||
1402 | if (isset($GLOBALS['egw_info']['user']['preferences']['activesync']['mail-maximumSyncRange'])) |
||
1403 | { |
||
1404 | $maximumSyncRangeInDays = $GLOBALS['egw_info']['user']['preferences']['activesync']['mail-maximumSyncRange']; |
||
1405 | } |
||
1406 | $cutoffdate = (is_numeric($maximumSyncRangeInDays) ? Api\DateTime::to('now','ts')-(3600*24*$maximumSyncRangeInDays):null); |
||
1407 | if (is_numeric($maximumSyncRangeInDays)) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.' Client set no truncationdate. Using '.$maximumSyncRangeInDays.' days.'.date("d-M-Y", $cutoffdate)); |
||
1408 | } |
||
1409 | try { |
||
1410 | return $this->fetchMessages($folderid, $cutoffdate); |
||
1411 | } catch (Exception $e) |
||
1412 | { |
||
1413 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' failed for '.$e->getMessage().($e->details?$e->details:'')); |
||
1414 | return array(); |
||
1415 | } |
||
1416 | } |
||
1417 | |||
1418 | /** |
||
1419 | * Fetch headers for one or all mail of a folder using optional cutoffdate |
||
1420 | * |
||
1421 | * Headers of last fetchMessage call of complate folder are cached in static $headers, |
||
1422 | * to allow to use them without fetching them again. |
||
1423 | * Next call clears cache |
||
1424 | * |
||
1425 | * @param int $folderid |
||
1426 | * @param int $cutoffdate timestamp with cutoffdate |
||
1427 | * @param string $_id =null uid of single message to fetch |
||
1428 | * @param boolean $return_all_headers =false true: additinal contain all headers eg. "subject" |
||
1429 | * @return array uid => array StatMessage($folderid, $_id) |
||
1430 | */ |
||
1431 | private function fetchMessages($folderid, $cutoffdate=NULL, $_id=NULL, $return_all_headers=false) |
||
1432 | { |
||
1433 | static $headers = array(); |
||
1434 | |||
1435 | if ($this->debugLevel>1) $gstarttime = microtime (true); |
||
1436 | //ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__); |
||
1437 | $rv_messages = array(); |
||
1438 | // if the message is still available within the class, we use it instead of fetching it again |
||
1439 | if ($_id && isset($headers[$_id]) && is_array($headers[$_id])) |
||
1440 | { |
||
1441 | //ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__." the message ".$_id[0]." is still available within the class, we use it instead of fetching it again"); |
||
1442 | $rv_messages = array('header'=>array($headers[$_id])); |
||
1443 | } |
||
1444 | else |
||
1445 | { |
||
1446 | $headers = array(); // clear cache to not use too much memory |
||
1447 | |||
1448 | if ($this->debugLevel>1) $starttime = microtime (true); |
||
1449 | $this->_connect($this->account); |
||
1450 | if ($this->debugLevel>1) |
||
1451 | { |
||
1452 | $endtime = microtime(true) - $starttime; |
||
1453 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__. " connect took : ".$endtime.' for account:'.$this->account); |
||
1454 | } |
||
1455 | $messagelist = $_filter = array(); |
||
1456 | // if not connected, any further action must fail |
||
1457 | if (!empty($cutoffdate)) $_filter = array('status'=>array('UNDELETED'),'range'=>"SINCE",'date'=> date("d-M-Y", $cutoffdate)); |
||
1458 | if ($this->debugLevel>1) $starttime = microtime (true); |
||
1459 | $account = $_folderName = $id = null; |
||
1460 | $this->splitID($folderid,$account,$_folderName,$id); |
||
1461 | if ($this->debugLevel>1) |
||
1462 | { |
||
1463 | $endtime = microtime(true) - $starttime; |
||
1464 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__. " splitID took : ".$endtime.' for FolderID:'.$folderid); |
||
1465 | } |
||
1466 | if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.' for Folder:'.$_folderName.' Filter:'.array2string($_filter).' Ids:'.array2string($_id).'/'.$id); |
||
1467 | if ($this->debugLevel>1) $starttime = microtime (true); |
||
1468 | $_numberOfMessages = (empty($cutoffdate)?250:99999); |
||
1469 | $rv_messages = $this->mail->getHeaders($_folderName, $_startMessage=1, $_numberOfMessages, $_sort=0, $_reverse=false, $_filter, $_id); |
||
1470 | if ($this->debugLevel>1) |
||
1471 | { |
||
1472 | $endtime = microtime(true) - $starttime; |
||
1473 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__. " getHeaders call took : ".$endtime.' for FolderID:'.$_folderName); |
||
1474 | } |
||
1475 | } |
||
1476 | if ($_id == NULL && $this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__." found :". count($rv_messages['header'])); |
||
1477 | //ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' Result:'.array2string($rv_messages)); |
||
1478 | $messagelist = array(); |
||
1479 | if (!isset($rv_messages['header'])||empty($rv_messages['header'])) return $messagelist; |
||
1480 | //if ($_returnModHash) $messageFolderHash = array(); |
||
1481 | foreach ((array)$rv_messages['header'] as $k => $vars) |
||
1482 | { |
||
1483 | if ($this->debugLevel>3) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' ID to process:'.$vars['uid'].' Subject:'.$vars['subject']); |
||
1484 | $headers[$vars['uid']] = $vars; |
||
1485 | if ($this->debugLevel>3) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' MailID:'.$k.'->'.array2string($vars)); |
||
1486 | if (!empty($vars['deleted'])) continue; // cut of deleted messages |
||
1487 | if ($cutoffdate && $vars['date'] < $cutoffdate) continue; // message is out of range for cutoffdate, ignore it |
||
1488 | if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' ID to report:'.$vars['uid'].' Subject:'.$vars['subject']); |
||
1489 | $mess = $return_all_headers ? $vars : array(); |
||
1490 | $mess["mod"] = self::doFlagsMod($vars).$vars['date']; |
||
1491 | $mess["id"] = $vars['uid']; |
||
1492 | // 'seen' aka 'read' is the only flag we want to know about |
||
1493 | $mess["flags"] = 0; |
||
1494 | // outlook supports additional flags, set them to 0 |
||
1495 | if($vars["seen"]) $mess["flags"] = 1; |
||
1496 | if ($this->debugLevel>3) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($mess)); |
||
1497 | $messagelist[$vars['uid']] = $mess; |
||
1498 | unset($mess); |
||
1499 | } |
||
1500 | if ($this->debugLevel>1) |
||
1501 | { |
||
1502 | $endtime = microtime(true) - $gstarttime; |
||
1503 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__. " total time used : ".$endtime.' for Folder:'.$_folderName.' Filter:'.array2string($_filter).' Ids:'.array2string($_id).'/'.$id); |
||
1504 | } |
||
1505 | return $messagelist; |
||
1506 | } |
||
1507 | |||
1508 | /** |
||
1509 | * Prepare headeinfo on a message to return some standardized string to tell which flags are set for a message |
||
1510 | * |
||
1511 | * AS currently only supports flagged, answered/replied and forwarded flags. |
||
1512 | * Seen/read is in under flags key of stat! |
||
1513 | * |
||
1514 | * @param array $headerFlags - array to process, a full return array from getHeaders |
||
1515 | * @link https://sourceforge.net/p/zimbrabackend/code/HEAD/tree/zimbra-backend/branches/z-push-2/zimbra.php#l11652 |
||
1516 | * @return string string of a representation of supported flags |
||
1517 | */ |
||
1518 | static function doFlagsMod($headerFlags) |
||
1519 | { |
||
1520 | $flags = 'nnn'; |
||
1521 | if ($headerFlags['flagged']) $flags[0] = 'f'; |
||
1522 | if ($headerFlags['answered']) $flags[1] = 'a'; |
||
1523 | if ($headerFlags['forwarded']) $flags[2] = 'f'; |
||
1524 | //ZLog::Write(LOGLEVEL_DEBUG, __METHOD__.'('.array2string($headerFlags).') returning '.array2string($flags)); |
||
1525 | return $flags; |
||
1526 | } |
||
1527 | |||
1528 | /** |
||
1529 | * Search mailbox for a given pattern |
||
1530 | * |
||
1531 | * @param object $_searchquery holds information specifying the query with GetDataArray it holds |
||
1532 | * [searchname] => MAILBOX |
||
1533 | * [searchfolderid] => 101000000000 |
||
1534 | * [searchfreetext] => somesearchtexgt |
||
1535 | * [searchdatereceivedgreater] => 1 |
||
1536 | * [searchvaluegreater] => 2015-07-06T22:00:00.000Z |
||
1537 | * [searchdatereceivedless] => 1 |
||
1538 | * [searchvalueless] => 2015-07-14T15:11:00.000Z |
||
1539 | * [searchrebuildresults] => 1 |
||
1540 | * [searchrange] => 0-99 |
||
1541 | * [bodypref] => Array([1] => BodyPreference Object([unsetdata:protected] => Array([truncationsize] => [allornone] => [preview] => )[SO_internalid:StateObject:private] => [data:protected] => |
||
1542 | * Array([truncationsize] => 2147483647)[changed:protected] => 1)) |
||
1543 | * [mimesupport] => 2) |
||
1544 | * @return array(["range"] = $_searchquery->GetSearchRange(), ['searchtotal'] = count of results, |
||
1545 | * array("class" => "Email", |
||
1546 | * "longid" => folderid.':'.uid', |
||
1547 | * "folderid" => folderid, |
||
1548 | * ), .... |
||
1549 | * ) |
||
1550 | */ |
||
1551 | public function getSearchResultsMailbox($_searchquery) |
||
1552 | { |
||
1553 | //$this->debugLevel=1; |
||
1554 | $searchquery=$_searchquery->GetDataArray(); |
||
1555 | if (!is_array($searchquery)) return array(); |
||
1556 | if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($searchquery)); |
||
1557 | |||
1558 | if (isset($searchquery['searchrebuildresults'])) { |
||
1559 | $rebuildresults = $searchquery['searchrebuildresults']; |
||
1560 | } else { |
||
1561 | $rebuildresults = false; |
||
1562 | } |
||
1563 | if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG, 'RebuildResults ['.$rebuildresults.']' ); |
||
1564 | |||
1565 | if (isset($searchquery['deeptraversal'])) { |
||
1566 | $deeptraversal = $searchquery['deeptraversal']; |
||
1567 | } else { |
||
1568 | $deeptraversal = false; |
||
1569 | } |
||
1570 | if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG, 'DeepTraversal ['.$deeptraversal.']' ); |
||
1571 | |||
1572 | if (isset($searchquery['searchrange'])) { |
||
1573 | $range = explode("-",$_searchquery->GetSearchRange()); |
||
1574 | $start =$range[0] + 1; |
||
1575 | $limit = $range[1] - $range[0] + 1; |
||
1576 | } else { |
||
1577 | $range = false; |
||
1578 | } |
||
1579 | if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG, 'Range ['.print_r($range, true).']' ); |
||
1580 | |||
1581 | //foreach($searchquery['query'] as $k => $value) { |
||
1582 | // $query = $value; |
||
1583 | //} |
||
1584 | if (isset($searchquery['searchfolderid'])) |
||
1585 | { |
||
1586 | $folderid = $searchquery['searchfolderid']; |
||
1587 | } |
||
1588 | /* |
||
1589 | // other types may be possible - we support quicksearch first (freeText in subject and from (or TO in Sent Folder)) |
||
1590 | if (is_null(Mail::$supportsORinQuery) || !isset(Mail::$supportsORinQuery[self::$profileID])) |
||
1591 | { |
||
1592 | Mail::$supportsORinQuery = Api\Cache::getCache(Api\Cache::INSTANCE,'email','supportsORinQuery'.trim($GLOBALS['egw_info']['user']['account_id']),$callback=null,$callback_params=array(),$expiration=60*60*10); |
||
1593 | if (!isset(Mail::$supportsORinQuery[self::$profileID])) Mail::$supportsORinQuery[self::$profileID]=true; |
||
1594 | } |
||
1595 | */ |
||
1596 | if (isset($searchquery['searchfreetext'])) |
||
1597 | { |
||
1598 | $searchText = $searchquery['searchfreetext']; |
||
1599 | } |
||
1600 | if (!$folderid) |
||
1601 | { |
||
1602 | $_folderName = ($this->mail->sessionData['mailbox']?$this->mail->sessionData['mailbox']:'INBOX'); |
||
1603 | $folderid = $this->createID($account=0,$_folderName); |
||
1604 | } |
||
1605 | $rv = $this->splitID($folderid,$account,$_folderName,$id); |
||
1606 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' ProfileID:'.self::$profileID.' FolderID:'.$folderid.' Foldername:'.$_folderName); |
||
1607 | $this->_connect($account); |
||
1608 | // this should not be needed ??? |
||
1609 | Mail::$supportsORinQuery[self::$profileID]=true; // trigger quicksearch (if possible) |
||
1610 | $_filter = array('type'=> (Mail::$supportsORinQuery[self::$profileID]?'quick':'subject'), |
||
1611 | 'string'=> $searchText, |
||
1612 | 'status'=>'any' |
||
1613 | ); |
||
1614 | |||
1615 | if (isset($searchquery['searchdatereceivedgreater']) || isset($searchquery['searchdatereceivedless'])) |
||
1616 | { |
||
1617 | /* |
||
1618 | * We respect only the DATEPART of the RANGE specified |
||
1619 | * [searchdatereceivedgreater] => 1 |
||
1620 | * [searchvaluegreater] => 2015-07-06T22:00:00.000Z , SINCE |
||
1621 | * [searchdatereceivedless] => 1 |
||
1622 | * [searchvalueless] => 2015-07-14T15:11:00.000Z , BEFORE |
||
1623 | */ |
||
1624 | $_filter['range'] = "BETWEEN"; |
||
1625 | list($sincedate,$crap) = explode('T',$searchquery['searchvaluegreater']); |
||
1626 | list($beforedate,$crap) = explode('T',$searchquery['searchvalueless']); |
||
1627 | $_filter['before'] = date("d-M-Y", Api\DateTime::to($beforedate,'ts')); |
||
1628 | $_filter['since'] = date("d-M-Y", Api\DateTime::to($sincedate,'ts')); |
||
1629 | } |
||
1630 | //$_filter[] = array('type'=>"SINCE",'string'=> date("d-M-Y", $cutoffdate)); |
||
1631 | if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG, __METHOD__.' for Folder:'.$_folderName.' Filter:'.array2string($_filter)); |
||
1632 | $rv_messages = $this->mail->getHeaders($_folderName, $_startMessage=($range?$start:1), $_numberOfMessages=($limit?$limit:9999999), $_sort=0, $_reverse=false, $_filter, $_id=NULL); |
||
1633 | //ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($rv_messages)); |
||
1634 | $list=array(); |
||
1635 | |||
1636 | $cnt = count($rv_messages['header']); |
||
1637 | //$list['status'] = 1; |
||
1638 | $list['searchtotal'] = $cnt; |
||
1639 | $list["range"] = $_searchquery->GetSearchRange(); |
||
1640 | foreach((array)$rv_messages['header'] as $i => $vars) |
||
1641 | { |
||
1642 | $list[] = array( |
||
1643 | "class" => "Email", |
||
1644 | "longid" => $folderid.':'.$vars['uid'], |
||
1645 | "folderid" => $folderid, |
||
1646 | ); |
||
1647 | } |
||
1648 | //error_log(__METHOD__.__LINE__.array2string($list)); |
||
1649 | //ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($list)); |
||
1650 | return $list; |
||
1651 | } |
||
1652 | |||
1653 | /** |
||
1654 | * Get ID of parent Folder or '0' for folders in root |
||
1655 | * |
||
1656 | * @param int $account |
||
1657 | * @param string $folder |
||
1658 | * @return string |
||
1659 | */ |
||
1660 | private function getParentID($account,$folder) |
||
1661 | { |
||
1662 | $this->_connect($account); |
||
1663 | if (!isset($this->folders)) $this->folders = $this->mail->getFolderObjects(true,false); |
||
1664 | |||
1665 | $mailFolder = $this->folders[$folder]; |
||
1666 | if (!isset($mailFolder)) return false; |
||
1667 | $delimiter = (isset($mailFolder->delimiter)?$mailFolder->delimiter:$this->mail->getHierarchyDelimiter()); |
||
1668 | $parent = explode($delimiter,$folder); |
||
1669 | array_pop($parent); |
||
1670 | $parent = implode($delimiter,$parent); |
||
1671 | |||
1672 | $id = $parent && $this->folders[$parent] ? $this->createID($account, $parent) : '0'; |
||
1673 | if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."('$folder') --> parent=$parent --> $id"); |
||
1674 | return $id; |
||
1675 | } |
||
1676 | |||
1677 | /** |
||
1678 | * Get Information about a folder |
||
1679 | * |
||
1680 | * @param string $id |
||
1681 | * @return SyncFolder|boolean false on error |
||
1682 | */ |
||
1683 | public function GetFolder($id) |
||
1684 | { |
||
1685 | static $last_id = null; |
||
1686 | static $folderObj = null; |
||
1687 | if (isset($last_id) && $last_id === $id) return $folderObj; |
||
1688 | |||
1689 | try { |
||
1690 | $account = $folder = null; |
||
1691 | $this->splitID($id, $account, $folder); |
||
1692 | } |
||
1693 | catch(Exception $e) { |
||
1694 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' failed for '.$e->getMessage()); |
||
1695 | return $folderObj=false; |
||
1696 | } |
||
1697 | $this->_connect($account); |
||
1698 | if (!isset($this->folders)) $this->folders = $this->mail->getFolderObjects(true,false); |
||
1699 | |||
1700 | $mailFolder = $this->folders[$folder]; |
||
1701 | if (!isset($mailFolder)) return $folderObj=false; |
||
1702 | |||
1703 | $folderObj = new SyncFolder(); |
||
1704 | $folderObj->serverid = $id; |
||
1705 | $folderObj->parentid = $this->getParentID($account,$folder); |
||
1706 | $folderObj->displayname = $mailFolder->shortDisplayName; |
||
1707 | if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__." ID: $id, Account:$account, Folder:$folder"); |
||
1708 | // get folder-type |
||
1709 | foreach($this->folders as $inbox => $mailFolder) break; |
||
1710 | if ($folder == $inbox) |
||
1711 | { |
||
1712 | $folderObj->type = SYNC_FOLDER_TYPE_INBOX; |
||
1713 | } |
||
1714 | elseif($this->mail->isDraftFolder($folder, false, true)) |
||
1715 | { |
||
1716 | //ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.' isDraft'); |
||
1717 | $folderObj->type = SYNC_FOLDER_TYPE_DRAFTS; |
||
1718 | $folderObj->parentid = 0; // required by devices |
||
1719 | } |
||
1720 | elseif($this->mail->isTrashFolder($folder, false, true)) |
||
1721 | { |
||
1722 | $folderObj->type = SYNC_FOLDER_TYPE_WASTEBASKET; |
||
1723 | $this->_wasteID = $folder; |
||
1724 | //error_log(__METHOD__.__LINE__.' TrashFolder:'.$this->_wasteID); |
||
1725 | $folderObj->parentid = 0; // required by devices |
||
1726 | } |
||
1727 | elseif($this->mail->isSentFolder($folder, false, true)) |
||
1728 | { |
||
1729 | $folderObj->type = SYNC_FOLDER_TYPE_SENTMAIL; |
||
1730 | $folderObj->parentid = 0; // required by devices |
||
1731 | $this->_sentID = $folder; |
||
1732 | //error_log(__METHOD__.__LINE__.' SentFolder:'.$this->_sentID); |
||
1733 | } |
||
1734 | elseif($this->mail->isOutbox($folder, false, true)) |
||
1735 | { |
||
1736 | //ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.' isOutbox'); |
||
1737 | $folderObj->type = SYNC_FOLDER_TYPE_OUTBOX; |
||
1738 | $folderObj->parentid = 0; // required by devices |
||
1739 | } |
||
1740 | else |
||
1741 | { |
||
1742 | //ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.' isOther Folder'.$folder); |
||
1743 | $folderObj->type = SYNC_FOLDER_TYPE_USER_MAIL; |
||
1744 | } |
||
1745 | |||
1746 | if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."($id) --> $folder --> type=$folderObj->type, parentID=$folderObj->parentid, displayname=$folderObj->displayname"); |
||
1747 | return $folderObj; |
||
1748 | } |
||
1749 | |||
1750 | /** |
||
1751 | * Return folder stats. This means you must return an associative array with the |
||
1752 | * following properties: |
||
1753 | * |
||
1754 | * "id" => The server ID that will be used to identify the folder. It must be unique, and not too long |
||
1755 | * How long exactly is not known, but try keeping it under 20 chars or so. It must be a string. |
||
1756 | * "parent" => The server ID of the parent of the folder. Same restrictions as 'id' apply. |
||
1757 | * "mod" => This is the modification signature. It is any arbitrary string which is constant as long as |
||
1758 | * the folder has not changed. In practice this means that 'mod' can be equal to the folder name |
||
1759 | * as this is the only thing that ever changes in folders. (the type is normally constant) |
||
1760 | * |
||
1761 | * @return array with values for keys 'id', 'mod' and 'parent' |
||
1762 | */ |
||
1763 | public function StatFolder($id) |
||
1764 | { |
||
1765 | $folder = $this->GetFolder($id); |
||
1766 | |||
1767 | $stat = array( |
||
1768 | 'id' => $id, |
||
1769 | 'mod' => $folder->displayname, |
||
1770 | 'parent' => $folder->parentid, |
||
1771 | ); |
||
1772 | |||
1773 | return $stat; |
||
1774 | } |
||
1775 | |||
1776 | |||
1777 | /** |
||
1778 | * Return a changes array |
||
1779 | * |
||
1780 | * if changes occurr default diff engine computes the actual changes |
||
1781 | * |
||
1782 | * @param string $folderid |
||
1783 | * @param string &$syncstate on call old syncstate, on return new syncstate |
||
1784 | * @return array|boolean false if $folderid not found, array() if no changes or array(array("type" => "fakeChange")) |
||
1785 | */ |
||
1786 | function AlterPingChanges($folderid, &$syncstate) |
||
1787 | { |
||
1788 | $account = $folder = null; |
||
1789 | $this->splitID($folderid, $account, $folder); |
||
1790 | if (is_numeric($account)) $type = 'mail'; |
||
1791 | |||
1792 | if ($type != 'mail') return false; |
||
1793 | |||
1794 | if (!isset($this->mail)) $this->mail = Mail::getInstance(false,self::$profileID,true,false,true); |
||
1795 | if (!$this->mail->folderIsSelectable($folder)) |
||
1796 | { |
||
1797 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": could not select folder $folder returning fake state"); |
||
1798 | $syncstate = "M:".'0'."-R:".'0'."-U:".'0'."-NUID:".'0'."-UIDV:".'0'; |
||
1799 | return array(); |
||
1800 | } |
||
1801 | |||
1802 | $this->mail->reopen($folder); |
||
1803 | |||
1804 | if (!($status = $this->mail->getFolderStatus($folder,$ignoreStatusCache=true))) |
||
1805 | { |
||
1806 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": could not stat folder $folder "); |
||
1807 | return false; |
||
1808 | } |
||
1809 | $syncstate = "M:". $status['messages'] ."-R:". $status['recent'] ."-U:". $status['unseen']."-NUID:".$status['uidnext']."-UIDV:".$status['uidvalidity']; |
||
1810 | |||
1811 | if ($this->debugLevel) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."($folderid, ...) $folder ($account) returning ".array2string($syncstate)); |
||
1812 | return array(); |
||
1813 | } |
||
1814 | |||
1815 | /** |
||
1816 | * Should return a wastebasket folder if there is one. This is used when deleting |
||
1817 | * items; if this function returns a valid folder ID, then all deletes are handled |
||
1818 | * as moves and are sent to your backend as a move. If it returns FALSE, then deletes |
||
1819 | * are always handled as real deletes and will be sent to your importer as a DELETE |
||
1820 | */ |
||
1821 | function GetWasteBasket() |
||
1822 | { |
||
1823 | $this->_connect($this->account); |
||
1824 | $id = $this->createID($account=0, $this->_wasteID); |
||
1825 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__."() account=$this->account returned $id for folder $this->_wasteID"); |
||
1826 | return $id; |
||
1827 | } |
||
1828 | |||
1829 | /** |
||
1830 | * Called when the user has requested to delete (really delete) a message. Usually |
||
1831 | * this means just unlinking the file its in or somesuch. After this call has succeeded, a call to |
||
1832 | * GetMessageList() should no longer list the message. If it does, the message will be re-sent to the mobile |
||
1833 | * as it will be seen as a 'new' item. This means that if this method is not implemented, it's possible to |
||
1834 | * delete messages on the PDA, but as soon as a sync is done, the item will be resynched to the mobile |
||
1835 | * |
||
1836 | * @param string $folderid id of the folder |
||
1837 | * @param string $id id of the message |
||
1838 | * @param ContentParameters $contentParameters |
||
1839 | * |
||
1840 | * @access public |
||
1841 | * @return boolean status of the operation |
||
1842 | * @throws StatusException could throw specific SYNC_STATUS_* exceptions |
||
1843 | */ |
||
1844 | public function DeleteMessage($folderid, $id, $contentParameters) |
||
1845 | { |
||
1846 | unset($contentParameters); // not used, but required by function signature |
||
1847 | ZLog::Write(LOGLEVEL_DEBUG, "IMAP-DeleteMessage: (fid: '$folderid' id: '$id' )"); |
||
1848 | /* |
||
1849 | $this->imap_reopenFolder($folderid); |
||
1850 | $s1 = @imap_delete ($this->_mbox, $id, FT_UID); |
||
1851 | $s11 = @imap_setflag_full($this->_mbox, $id, "\\Deleted", FT_UID); |
||
1852 | $s2 = @imap_expunge($this->_mbox); |
||
1853 | */ |
||
1854 | // we may have to split folderid |
||
1855 | $account = $folder = null; |
||
1856 | $this->splitID($folderid, $account, $folder); |
||
1857 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' '.$folderid.'->'.$folder); |
||
1858 | $_messageUID = (array)$id; |
||
1859 | |||
1860 | $this->_connect($this->account); |
||
1861 | $this->mail->reopen($folder); |
||
1862 | try |
||
1863 | { |
||
1864 | $rv = $this->mail->deleteMessages($_messageUID, $folder); |
||
1865 | } |
||
1866 | catch (Api\Exception $e) |
||
1867 | { |
||
1868 | $error = $e->getMessage(); |
||
1869 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__." $_messageUID, $folder ->".$error); |
||
1870 | // if the server thinks the message does not exist report deletion as success |
||
1871 | if (stripos($error,'[NONEXISTENT]')!==false) return true; |
||
1872 | return false; |
||
1873 | } |
||
1874 | |||
1875 | // this may be a bit rude, it may be sufficient that GetMessageList does not list messages flagged as deleted |
||
1876 | if ($this->mail->mailPreferences['deleteOptions'] == 'mark_as_deleted') |
||
1877 | { |
||
1878 | // ignore mark as deleted -> Expunge! |
||
1879 | //$this->mail->icServer->expunge(); // do not expunge as GetMessageList does not List messages flagged as deleted |
||
1880 | } |
||
1881 | ZLog::Write(LOGLEVEL_DEBUG, "IMAP-DeleteMessage: $rv"); |
||
1882 | |||
1883 | return $rv; |
||
1884 | } |
||
1885 | |||
1886 | /** |
||
1887 | * Changes the 'read' flag of a message on disk. The $flags |
||
1888 | * parameter can only be '1' (read) or '0' (unread). After a call to |
||
1889 | * SetReadFlag(), GetMessageList() should return the message with the |
||
1890 | * new 'flags' but should not modify the 'mod' parameter. If you do |
||
1891 | * change 'mod', simply setting the message to 'read' on the mobile will trigger |
||
1892 | * a full resync of the item from the server. |
||
1893 | * |
||
1894 | * @param string $folderid id of the folder |
||
1895 | * @param string $id id of the message |
||
1896 | * @param int $flags read flag of the message |
||
1897 | * @param ContentParameters $contentParameters |
||
1898 | * |
||
1899 | * @access public |
||
1900 | * @return boolean status of the operation |
||
1901 | * @throws StatusException could throw specific SYNC_STATUS_* exceptions |
||
1902 | */ |
||
1903 | public function SetReadFlag($folderid, $id, $flags, $contentParameters) |
||
1904 | { |
||
1905 | unset($contentParameters); // not used, but required by function signature |
||
1906 | // ZLog::Write(LOGLEVEL_DEBUG, "IMAP-SetReadFlag: (fid: '$folderid' id: '$id' flags: '$flags' )"); |
||
1907 | $account = $folder = null; |
||
1908 | $this->splitID($folderid, $account, $folder); |
||
1909 | |||
1910 | $_messageUID = (array)$id; |
||
1911 | $this->_connect($this->account); |
||
1912 | $rv = $this->mail->flagMessages((($flags) ? "read" : "unread"), $_messageUID,$folder); |
||
1913 | ZLog::Write(LOGLEVEL_DEBUG, "IMAP-SetReadFlag -> set ".array2string($_messageUID).' in Folder '.$folder." as " . (($flags) ? "read" : "unread") . "-->". $rv); |
||
1914 | |||
1915 | return $rv; |
||
1916 | } |
||
1917 | |||
1918 | /** |
||
1919 | * Creates or modifies a folder |
||
1920 | * |
||
1921 | * @param string $id of the parent folder |
||
1922 | * @param string $oldid => if empty -> new folder created, else folder is to be renamed |
||
1923 | * @param string $displayname => new folder name (to be created, or to be renamed to) |
||
1924 | * @param string $type folder type, ignored in IMAP |
||
1925 | * |
||
1926 | * @throws StatusException could throw specific SYNC_FSSTATUS_* exceptions |
||
1927 | * @return array|boolean stat array or false on error |
||
1928 | */ |
||
1929 | public function ChangeFolder($id, $oldid, $displayname, $type) |
||
1930 | { |
||
1931 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."('$id', '$oldid', '$displayname', $type)"); |
||
1932 | $account = $parent_id = null; |
||
1933 | $this->splitID($id, $account, $parentFolder, $app); |
||
1934 | |||
1935 | $parent_id = $this->folder2hash($account, $parentFolder); |
||
1936 | $old_hash = $oldFolder = null; |
||
1937 | |||
1938 | if (empty($oldid)) |
||
1939 | { |
||
1940 | $action = 'create'; |
||
1941 | } |
||
1942 | else |
||
1943 | { |
||
1944 | $action = 'rename'; |
||
1945 | $this->splitID($oldid, $account, $oldFolder, $app); |
||
1946 | $old_hash = $this->folder2hash($account, $oldFolder); |
||
1947 | } |
||
1948 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.":{$action}Folder('$id'=>($parentFolder ($parent_id)), '$oldid'".($oldid?"=>($oldFolder ($old_hash))":'').", '$displayname', $type)"); |
||
1949 | $this->_connect($this->account); |
||
1950 | try |
||
1951 | { |
||
1952 | if ($action=='rename') |
||
1953 | { |
||
1954 | $newFolderName = $this->mail->renameFolder($oldFolder, $parentFolder, $displayname); |
||
1955 | } |
||
1956 | elseif ($action=='create') |
||
1957 | { |
||
1958 | $error=null; |
||
1959 | $newFolderName = $this->mail->createFolder($parentFolder, $displayname, $error); |
||
1960 | } |
||
1961 | } |
||
1962 | catch (\Exception $e) |
||
1963 | { |
||
1964 | //throw new Exception(__METHOD__." $action failed for $oldFolder ($action: $displayname) with error:".$e->getMessage()); |
||
1965 | return false; |
||
1966 | } |
||
1967 | $newHash = $this->rename_folder_hash($account, $old_hash, $newFolderName); |
||
1968 | $newID = $this->createID($account, $newHash); |
||
1969 | $this->folders = $this->mail->getFolderObjects(true,false,$_alwaysGetDefaultFolders=true,false); |
||
1970 | ZLog::Write(LOGLEVEL_DEBUG,":{$action}Folder('$id'=>($parentFolder), '$oldid'".($oldid?"=>($oldFolder)":'').", '$displayname' => $newFolderName (ID:$newID))"); |
||
1971 | return $this->StatFolder($newID); |
||
1972 | } |
||
1973 | |||
1974 | /** |
||
1975 | * Deletes (really delete) a Folder |
||
1976 | * |
||
1977 | * @param string $id of the folder to delete |
||
1978 | * @param string $parentid (=false) of the folder to delete, may be false/not set |
||
1979 | * |
||
1980 | * @throws StatusException could throw specific SYNC_FSSTATUS_* exceptions |
||
1981 | * @return boolean true or false on error |
||
1982 | */ |
||
1983 | public function DeleteFolder($id, $parentid=false) |
||
1984 | { |
||
1985 | $account = $parent_id = $app = null; |
||
1986 | $this->splitID($id, $account, $folder, $app); |
||
1987 | $old_hash = $this->folder2hash($account, $folder); |
||
1988 | if ($parentid) $this->splitID($parentid, $account, $parentfolder, $app); |
||
1989 | ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."( '$id (-> $folder)','$parentid ".($parentid?'(->'.$parentfolder.')':'')."') called!"); |
||
1990 | $ret = $this->mail->deleteFolder($folder); |
||
1991 | if ($ret) $newHash = $this->rename_folder_hash($account, $old_hash, "##Dele#edFolder#$folder##"); |
||
1992 | $this->folders = $this->mail->getFolderObjects(true,false,$_alwaysGetDefaultFolders=true,false); |
||
1993 | return $ret; |
||
1994 | } |
||
1995 | |||
1996 | /** |
||
1997 | * modify olflags (outlook style) flag of a message |
||
1998 | * |
||
1999 | * @param $folderid |
||
2000 | * @param $id |
||
2001 | * @param $flags |
||
2002 | * |
||
2003 | * |
||
2004 | * @DESC The $flags parameter must contains the poommailflag Object |
||
2005 | */ |
||
2006 | function ChangeMessageFlag($folderid, $id, $flags) |
||
2007 | { |
||
2008 | $_messageUID = (array)$id; |
||
2009 | $this->_connect($this->account); |
||
2010 | $account = $folder = null; |
||
2011 | $this->splitID($folderid, $account, $folder); |
||
2012 | $rv = $this->mail->flagMessages((($flags->flagstatus == 2) ? "flagged" : "unflagged"), $_messageUID,$folder); |
||
2013 | ZLog::Write(LOGLEVEL_DEBUG, "IMAP-SetFlaggedFlag -> set ".array2string($_messageUID).' in Folder '.$folder." as " . (($flags->flagstatus == 2) ? "flagged" : "unflagged") . "-->". $rv); |
||
2014 | |||
2015 | return $rv; |
||
2016 | } |
||
2017 | |||
2018 | /** |
||
2019 | * Create a max. 32 hex letter ID, current 20 chars are used |
||
2020 | * |
||
2021 | * @param int $account mail account id |
||
2022 | * @param string $folder |
||
2023 | * @param int $id =0 |
||
2024 | * @return string |
||
2025 | * @throws Api\Exception\WrongParameter |
||
2026 | */ |
||
2027 | private function createID($account,$folder,$id=0) |
||
2040 | } |
||
2041 | |||
2042 | /** |
||
2043 | * Split an ID string into $app, $account $folder and $appid |
||
2044 | * |
||
2045 | * @param string $str |
||
2046 | * @param int &$account mail account id |
||
2047 | * @param string &$folder |
||
2048 | * @param int &$appid=null (for mail=mail is to be expected) |
||
2049 | * @throws Api\Exception\WrongParameter |
||
2050 | */ |
||
2051 | private function splitID($str,&$account,&$folder,&$appid=null) |
||
2052 | { |
||
2053 | $this->backend->splitID($str, $account, $folder, $appid); |
||
2054 | |||
2055 | // convert numeric folder-id back to folder name |
||
2056 | $folder = $this->hash2folder($account,$f=$folder); |
||
2057 | |||
2058 | if ($this->debugLevel>1) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."('$str','$account','$folder',$appid)"); |
||
2059 | } |
||
2060 | |||
2061 | /** |
||
2062 | * Methods to convert (hierarchical) folder names to nummerical id's |
||
2063 | * |
||
2064 | * This is currently done by storing a serialized array in the device specific |
||
2065 | * state directory. |
||
2066 | */ |
||
2067 | |||
2068 | /** |
||
2069 | * Convert folder string to nummeric hash |
||
2070 | * |
||
2071 | * @param int $account |
||
2072 | * @param string $folder |
||
2073 | * @return int |
||
2074 | */ |
||
2075 | private function folder2hash($account,$folder) |
||
2089 | } |
||
2090 | |||
2091 | /** |
||
2092 | * Convert numeric hash to folder string |
||
2093 | * |
||
2094 | * @param int $account |
||
2095 | * @param int $index |
||
2096 | * @return string NULL if not used so far |
||
2097 | */ |
||
2098 | private function hash2folder($account,$index) |
||
2099 | { |
||
2100 | if(!isset($this->folderHashes)) $this->readFolderHashes(); |
||
2101 | |||
2102 | return isset($this->folderHashes[$account]) ? $this->folderHashes[$account][$index] : null; |
||
2103 | } |
||
2104 | |||
2105 | /** |
||
2106 | * Rename or create a folder in hash table |
||
2107 | * |
||
2108 | * @param int $account |
||
2109 | * @param int $index or null to create |
||
2110 | * @param string $new_name |
||
2111 | * @return int $index or new hash if $index is not found |
||
2112 | */ |
||
2113 | private function rename_folder_hash($account, $index, $new_name) |
||
2114 | { |
||
2115 | if ((string)$index === '' || !$this->hash2folder($account, $index)) |
||
2116 | { |
||
2117 | return $this->folder2hash($account, $new_name); |
||
2118 | } |
||
2119 | $this->folderHashes[$account][$index] = $new_name; |
||
2120 | $this->storeFolderHashes(); |
||
2121 | return $index; |
||
2122 | } |
||
2123 | |||
2124 | private $folderHashes; |
||
2125 | |||
2126 | /** |
||
2127 | * Statemaschine instance used to store folders |
||
2128 | * |
||
2129 | * @var activesync_statemaschine |
||
2130 | */ |
||
2131 | private $fh_state_maschine; |
||
2132 | |||
2133 | /** |
||
2134 | * state_type (and _key) used to store folder hashes |
||
2135 | */ |
||
2136 | const FOLDER_STATE_TYPE = 'folder_hashes'; |
||
2137 | |||
2138 | /** |
||
2139 | * Read hashfile from state dir |
||
2140 | */ |
||
2141 | private function readFolderHashes() |
||
2142 | { |
||
2143 | if (!isset($this->fh_state_maschine)) |
||
2144 | { |
||
2145 | $this->fh_state_maschine = new activesync_statemachine($this->backend); |
||
2146 | } |
||
2147 | try { |
||
2148 | $this->folderHashes = $this->fh_state_maschine->getState(Request::GetDeviceID(), |
||
2149 | self::FOLDER_STATE_TYPE, self::FOLDER_STATE_TYPE, 0); |
||
2150 | } |
||
2151 | catch (Exception $e) { |
||
2152 | unset($e); |
||
2153 | if ((file_exists($file = $this->hashFile()) || file_exists($file = $this->hashFile(true))) && |
||
2154 | ($hashes = file_get_contents($file))) |
||
2155 | { |
||
2156 | $this->folderHashes = json_decode($hashes,true); |
||
2157 | // fallback in case hashes have been serialized instead of being json-encoded |
||
2158 | if (json_last_error()!=JSON_ERROR_NONE) |
||
2159 | { |
||
2160 | //error_log(__METHOD__.__LINE__." error decoding with json"); |
||
2161 | $this->folderHashes = unserialize($hashes); |
||
2162 | } |
||
2163 | // store folder-hashes to state |
||
2164 | $this->storeFolderHashes(); |
||
2165 | } |
||
2166 | else |
||
2167 | { |
||
2168 | $this->folderHashes = array(); |
||
2169 | } |
||
2170 | } |
||
2171 | } |
||
2172 | |||
2173 | /** |
||
2174 | * Store hashfile via state-maschine |
||
2175 | * |
||
2176 | * return int|boolean false on error |
||
2177 | */ |
||
2178 | private function storeFolderHashes() |
||
2193 | } |
||
2194 | |||
2195 | /** |
||
2196 | * Get name of hashfile in state dir |
||
2197 | * |
||
2198 | * New z-push 2 directory is in activesync_statemachine::getDeviceDirectory. |
||
2199 | * On request this function also returns, but never creates (!), old z-push 1 directory. |
||
2200 | * |
||
2201 | * @param boolean $old =false true: return old / pre-15 hash-file |
||
2202 | * @throws Api\Exception\AssertionFailed |
||
2203 | */ |
||
2204 | private function hashFile($old=false) |
||
2205 | { |
||
2217 | } |
||
2218 | } |
||
2219 |
Let?s assume that you have a directory layout like this:
and let?s assume the following content of
Bar.php
:If both files
OtherDir/Foo.php
andSomeDir/Foo.php
are loaded in the same runtime, you will see a PHP error such as the following:PHP Fatal error: Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php
However, as
OtherDir/Foo.php
does not necessarily have to be loaded and the error is only triggered if it is loaded beforeOtherDir/Bar.php
, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias: