Overview

Packages

  • Kolab
    • Storage

Classes

  • Horde_Kolab_Storage_Base
  • Horde_Kolab_Storage_Cache
  • Horde_Kolab_Storage_Cache_Data
  • Horde_Kolab_Storage_Cache_List
  • Horde_Kolab_Storage_Cached
  • Horde_Kolab_Storage_Data_Base
  • Horde_Kolab_Storage_Data_Cached
  • Horde_Kolab_Storage_Data_Decorator_Log
  • Horde_Kolab_Storage_Data_Format_Mime
  • Horde_Kolab_Storage_Data_Modifiable
  • Horde_Kolab_Storage_Data_Old
  • Horde_Kolab_Storage_Data_Parser_Structure
  • Horde_Kolab_Storage_Data_Query_History_Base
  • Horde_Kolab_Storage_Data_Query_History_Cache
  • Horde_Kolab_Storage_Data_Query_Preferences_Base
  • Horde_Kolab_Storage_Data_Query_Preferences_Cache
  • Horde_Kolab_Storage_Decorator_Synchronization
  • Horde_Kolab_Storage_Driver_Base
  • Horde_Kolab_Storage_Driver_Cclient
  • Horde_Kolab_Storage_Driver_Decorator_Base
  • Horde_Kolab_Storage_Driver_Decorator_Log
  • Horde_Kolab_Storage_Driver_Decorator_Timer
  • Horde_Kolab_Storage_Driver_Imap
  • Horde_Kolab_Storage_Driver_Mock
  • Horde_Kolab_Storage_Driver_Mock_Data
  • Horde_Kolab_Storage_Driver_Pear
  • Horde_Kolab_Storage_Driver_Rcube
  • Horde_Kolab_Storage_Exception
  • Horde_Kolab_Storage_Exception_Pear
  • Horde_Kolab_Storage_Factory
  • Horde_Kolab_Storage_Folder_Base
  • Horde_Kolab_Storage_Folder_Decorator_Base
  • Horde_Kolab_Storage_Folder_Decorator_Trigger
  • Horde_Kolab_Storage_Folder_Namespace
  • Horde_Kolab_Storage_Folder_Namespace_Config
  • Horde_Kolab_Storage_Folder_Namespace_Element
  • Horde_Kolab_Storage_Folder_Namespace_Element_Other
  • Horde_Kolab_Storage_Folder_Namespace_Element_Personal
  • Horde_Kolab_Storage_Folder_Namespace_Element_Shared
  • Horde_Kolab_Storage_Folder_Namespace_Element_SharedWithPrefix
  • Horde_Kolab_Storage_Folder_Namespace_Fixed
  • Horde_Kolab_Storage_Folder_Namespace_Imap
  • Horde_Kolab_Storage_Folder_Stamp_Uids
  • Horde_Kolab_Storage_Folder_Type
  • Horde_Kolab_Storage_List_Base
  • Horde_Kolab_Storage_List_Decorator_Cache
  • Horde_Kolab_Storage_List_Decorator_Log
  • Horde_Kolab_Storage_List_Query_Acl_Base
  • Horde_Kolab_Storage_List_Query_Acl_Cache
  • Horde_Kolab_Storage_List_Query_ActiveSync_Base
  • Horde_Kolab_Storage_List_Query_ActiveSync_Cache
  • Horde_Kolab_Storage_List_Query_List_Base
  • Horde_Kolab_Storage_List_Query_List_Cache
  • Horde_Kolab_Storage_List_Query_Share_Base
  • Horde_Kolab_Storage_List_Query_Share_Cache
  • Horde_Kolab_Storage_QuerySet_Base
  • Horde_Kolab_Storage_QuerySet_Cached
  • Horde_Kolab_Storage_QuerySet_Uncached
  • Horde_Kolab_Storage_Synchronization
  • Horde_Kolab_Storage_Translation
  • Horde_Kolab_Storage_Uncached

Interfaces

  • Horde_Kolab_Storage
  • Horde_Kolab_Storage_Data
  • Horde_Kolab_Storage_Data_Format
  • Horde_Kolab_Storage_Data_Parser
  • Horde_Kolab_Storage_Data_Query
  • Horde_Kolab_Storage_Data_Query_History
  • Horde_Kolab_Storage_Data_Query_Preferences
  • Horde_Kolab_Storage_Driver
  • Horde_Kolab_Storage_Folder
  • Horde_Kolab_Storage_Folder_Stamp
  • Horde_Kolab_Storage_List
  • Horde_Kolab_Storage_List_Query
  • Horde_Kolab_Storage_List_Query_Acl
  • Horde_Kolab_Storage_List_Query_ActiveSync
  • Horde_Kolab_Storage_List_Query_List
  • Horde_Kolab_Storage_List_Query_Share
  • Horde_Kolab_Storage_Queriable
  • Horde_Kolab_Storage_Query
  • Horde_Kolab_Storage_QuerySet
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * A Roundcube Imap based Kolab storage driver.
  4:  *
  5:  * PHP version 5
  6:  *
  7:  * @category Kolab
  8:  * @package  Kolab_Storage
  9:  * @author   Gunnar Wrobel <wrobel@pardus.de>
 10:  * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 11:  * @link     http://pear.horde.org/index.php?package=Kolab_Storage
 12:  */
 13: 
 14: /**
 15:  * A Roundcube Imap based Kolab storage driver.
 16:  *
 17:  * Copyright 2010-2012 Horde LLC (http://www.horde.org/)
 18:  *
 19:  * See the enclosed file COPYING for license information (LGPL). If you
 20:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 21:  *
 22:  * @category Kolab
 23:  * @package  Kolab_Storage
 24:  * @author   Gunnar Wrobel <wrobel@pardus.de>
 25:  * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 26:  * @link     http://pear.horde.org/index.php?package=Kolab_Storage
 27:  */
 28: class Horde_Kolab_Storage_Driver_Rcube
 29: extends Horde_Kolab_Storage_Driver_Base
 30: {
 31:     /**
 32:      * Debug log
 33:      *
 34:      * @var resource
 35:      */
 36:     private $_debug_log;
 37: 
 38:     /**
 39:      * Write a line of debugging output to the log.
 40:      *
 41:      * @return NULL
 42:      */
 43:     public function debugLog($driver, $message)
 44:     {
 45:         fwrite($this->_debug_log, $message . "\n");
 46:     }
 47: 
 48:     /**
 49:      * Destructor.
 50:      */
 51:     public function __destruct()
 52:     {
 53:         if (is_resource($this->_debug_log)) {
 54:             fflush($this->_debug_log);
 55:             fclose($this->_debug_log);
 56:             $this->_debug_log = null;
 57:         }
 58:     }
 59: 
 60:     /**
 61:      * Create the backend driver.
 62:      *
 63:      * @return mixed The backend driver.
 64:      */
 65:     public function createBackend()
 66:     {
 67:         $config = $this->getParams();
 68:         $client = new rcube_imap_generic();
 69:         if (isset($config['debug'])) {
 70:             if ($config['debug'] == 'STDOUT') {
 71:                 $client->setDebug(true);
 72:             } else {
 73:                 $this->_debug_log = fopen($config['debug'], 'a');
 74:                 $client->setDebug(true, array($this, 'debugLog'));
 75:             }
 76:         }
 77:         $client->connect(
 78:             $config['host'], $config['username'], $config['password'],
 79:             array(
 80:                 'ssl_mode' => $config['secure'],
 81:                 'port' => $config['port'],
 82:                 'timeout' => 0,
 83:                 'force_caps' => false,
 84:             )
 85:         );
 86:         return $client;
 87:     }
 88: 
 89:     /**
 90:      * Retrieves a list of folders on the server.
 91:      *
 92:      * @return array The list of folders.
 93:      */
 94:     public function listFolders()
 95:     {
 96:         return $this->decodeList($this->getBackend()->listMailboxes('', '*'));
 97:     }
 98: 
 99:     /**
100:      * Create the specified folder.
101:      *
102:      * @param string $folder The folder to create.
103:      *
104:      * @return NULL
105:      */
106:     public function create($folder)
107:     {
108:         $this->getBackend()->createFolder($this->encodePath($folder));
109:         if ($this->getBackend()->errornum != 0) {
110:             throw new Horde_Kolab_Storage_Exception(
111:                 sprintf(
112:                     Horde_Kolab_Storage_Translation::t(
113:                         "Creating folder %s failed. Error: %s"
114:                     ),
115:                     $folder,
116:                     $this->getBackend()->error
117:                 )
118:             );
119:         }
120:     }
121: 
122:     /**
123:      * Delete the specified folder.
124:      *
125:      * @param string $folder  The folder to delete.
126:      *
127:      * @return NULL
128:      */
129:     public function delete($folder)
130:     {
131:         $this->getBackend()->deleteFolder($this->encodePath($folder));
132:         if ($this->getBackend()->errornum != 0) {
133:             throw new Horde_Kolab_Storage_Exception(
134:                 sprintf(
135:                     Horde_Kolab_Storage_Translation::t(
136:                         "Deleting folder %s failed. Error: %s"
137:                     ),
138:                     $folder,
139:                     $this->getBackend()->error
140:                 )
141:             );
142:         }
143:     }
144: 
145:     /**
146:      * Rename the specified folder.
147:      *
148:      * @param string $old  The folder to rename.
149:      * @param string $new  The new name of the folder.
150:      *
151:      * @return NULL
152:      */
153:     public function rename($old, $new)
154:     {
155:         $this->getBackend()->renameFolder(
156:             $this->encodePath($old),
157:             $this->encodePath($new)
158:         );
159:         if ($this->getBackend()->errornum != 0) {
160:             throw new Horde_Kolab_Storage_Exception(
161:                 sprintf(
162:                     Horde_Kolab_Storage_Translation::t(
163:                         "Renaming folder %s to %s failed. Error: %s"
164:                     ),
165:                     $old,
166:                     $new,
167:                     $this->getBackend()->error
168:                 )
169:             );
170:         }
171:     }
172: 
173:     /**
174:      * Does the backend support ACL?
175:      *
176:      * @return boolean True if the backend supports ACLs.
177:      */
178:     public function hasAclSupport()
179:     {
180:         return $this->getBackend()->getCapability('ACL');
181:     }
182: 
183:     /**
184:      * Retrieve the access rights for a folder.
185:      *
186:      * @param string $folder The folder to retrieve the ACL for.
187:      *
188:      * @return array An array of rights.
189:      */
190:     public function getAcl($folder)
191:     {
192:         $acl = $this->getBackend()->getACL($this->encodePath($folder));
193:         if ($this->getBackend()->errornum != 0) {
194:             throw new Horde_Kolab_Storage_Exception(
195:                 sprintf(
196:                     Horde_Kolab_Storage_Translation::t(
197:                         "Failed reading ACL on folder %s. Error: %s"
198:                     ),
199:                     $folder,
200:                     $this->getBackend()->error
201:                 )
202:             );
203:         }
204:         $result = array();
205:         foreach ($acl as $user => $rights) {
206:             $result[$user] = join('', $rights);
207:         }
208:         return $result;
209:     }
210: 
211:     /**
212:      * Retrieve the access rights the current user has on a folder.
213:      *
214:      * @param string $folder The folder to retrieve the user ACL for.
215:      *
216:      * @return string The user rights.
217:      */
218:     public function getMyAcl($folder)
219:     {
220:         $result = $this->getBackend()->myRights($this->encodePath($folder));
221:         if ($this->getBackend()->errornum != 0) {
222:             throw new Horde_Kolab_Storage_Exception(
223:                 sprintf(
224:                     Horde_Kolab_Storage_Translation::t(
225:                         "Failed reading user rights on folder %s. Error: %s"
226:                     ),
227:                     $folder,
228:                     $this->getBackend()->error
229:                 )
230:             );
231:         }
232:         return join('', $result);
233:     }
234: 
235:     /**
236:      * Set the access rights for a folder.
237:      *
238:      * @param string $folder  The folder to act upon.
239:      * @param string $user    The user to set the ACL for.
240:      * @param string $acl     The ACL.
241:      *
242:      * @return NULL
243:      */
244:     public function setAcl($folder, $user, $acl)
245:     {
246:         $this->getBackend()->setACL($this->encodePath($folder), $user, $acl);
247:         if ($this->getBackend()->errornum != 0) {
248:             throw new Horde_Kolab_Storage_Exception(
249:                 sprintf(
250:                     Horde_Kolab_Storage_Translation::t(
251:                         "Failed setting ACL on folder %s for user %s to %s. Error: %s"
252:                     ),
253:                     $folder,
254:                     $user,
255:                     $acl,
256:                     $this->getBackend()->error
257:                 )
258:             );
259:         }
260:     }
261: 
262:     /**
263:      * Delete the access rights for user on a folder.
264:      *
265:      * @param string $folder  The folder to act upon.
266:      * @param string $user    The user to delete the ACL for
267:      *
268:      * @return NULL
269:      */
270:     public function deleteAcl($folder, $user)
271:     {
272:         $this->getBackend()->deleteACL($this->encodePath($folder), $user);
273:         if ($this->getBackend()->errornum != 0) {
274:             throw new Horde_Kolab_Storage_Exception(
275:                 sprintf(
276:                     Horde_Kolab_Storage_Translation::t(
277:                         "Failed deleting ACL on folder %s for user %s. Error: %s"
278:                     ),
279:                     $folder,
280:                     $user,
281:                     $this->getBackend()->error
282:                 )
283:             );
284:         }
285:     }
286: 
287:     /**
288:      * Retrieves the specified annotation for the complete list of folders.
289:      *
290:      * @param string $annotation The name of the annotation to retrieve.
291:      *
292:      * @return array An associative array combining the folder names as key with
293:      *               the corresponding annotation value.
294:      */
295:     public function listAnnotation($annotation)
296:     {
297:         list($entry, $value) = $this->_getAnnotateMoreEntry($annotation);
298:         $result = $this->getBackend()->getAnnotation('*', $entry, $value);
299:         if (empty($result)) {
300:             return array();
301:         }
302:         $data = array();
303:         foreach ($result as $folder => $annotations) {
304:             if (isset($annotations[$annotation])) {
305:                 $data[$folder] = $annotations[$annotation];
306:             }
307:         }
308:         return $this->decodeListKeys($data);
309:     }
310: 
311:     /**
312:      * Fetches the annotation from a folder.
313:      *
314:      * @param string $folder    The name of the folder.
315:      * @param string $annotation The annotation to get.
316:      *
317:      * @return string The annotation value.
318:      */
319:     public function getAnnotation($folder, $annotation)
320:     {
321:         list($attr, $type) = $this->_getAnnotateMoreEntry($annotation);
322:         $result = $this->getBackend()->getAnnotation(
323:             $this->encodePath($folder), $attr, $type
324:         );
325:         if ($this->getBackend()->errornum != 0) {
326:             throw new Horde_Kolab_Storage_Exception(
327:                 sprintf(
328:                     Horde_Kolab_Storage_Translation::t(
329:                         "Setting annotation %s[%s] on folder %s failed. Error: %s"
330:                     ),
331:                     $attr,
332:                     $type,
333:                     $folder,
334:                     $this->getBackend()->error
335:                 )
336:             );
337:         }
338:         return $result[$folder][$annotation];
339:     }
340: 
341:     /**
342:      * Sets the annotation on a folder.
343:      *
344:      * @param string $folder    The name of the folder.
345:      * @param string $annotation The annotation to set.
346:      * @param array  $value      The values to set
347:      *
348:      * @return NULL
349:      */
350:     public function setAnnotation($folder, $annotation, $value)
351:     {
352:         list($attr, $type) = $this->_getAnnotateMoreEntry($annotation);
353:         $this->getBackend()->setAnnotation(
354:             $this->encodePath($folder), array(array($attr, $type, $value))
355:         );
356:         if ($this->getBackend()->errornum != 0) {
357:             throw new Horde_Kolab_Storage_Exception(
358:                 sprintf(
359:                     Horde_Kolab_Storage_Translation::t(
360:                         "Setting annotation %s[%s] on folder %s to %s failed. Error: %s"
361:                     ),
362:                     $attr,
363:                     $type,
364:                     $folder,
365:                     $value,
366:                     $this->getBackend()->error
367:                 )
368:             );
369:         }
370:     }
371: 
372:     /**
373:      * Retrieve the namespace information for this connection.
374:      *
375:      * @return Horde_Kolab_Storage_Driver_Namespace The initialized namespace handler.
376:      */
377:     public function getNamespace()
378:     {
379:         if ($this->getBackend()->getCapability('NAMESPACE') === true) {
380:             $namespaces = array();
381:             foreach ($this->getBackend()->getNamespace() as $type => $elements) {
382:                 if (is_array($elements)) {
383:                     foreach ($elements as $namespace) {
384:                         $namespace['name'] = $namespace[0];
385:                         $namespace['delimiter'] = $namespace[1];
386:                         $namespace['type'] = $type;
387:                         $namespaces[] = $namespace;
388:                     }
389:                 }
390:             }
391:             $this->_namespace = $this->getFactory()->createNamespace('imap', $this->getAuth(), $namespaces);
392:         }
393:         return parent::getNamespace();
394:     }
395: 
396:     /**
397:      * Opens the given folder.
398:      *
399:      * @param string $folder  The folder to open
400:      *
401:      * @return NULL
402:      */
403:     public function select($folder)
404:     {
405:         $this->getBackend()->select($this->encodePath($folder));
406:         if ($this->getBackend()->errornum != 0) {
407:             throw new Horde_Kolab_Storage_Exception(
408:                 sprintf(
409:                     Horde_Kolab_Storage_Translation::t(
410:                         "Selecting folder %s failed. Error: %s"
411:                     ),
412:                     $folder,
413:                     $this->getBackend()->error
414:                 )
415:             );
416:         }
417:     }
418: 
419:     /**
420:      * Returns the status of the current folder.
421:      *
422:      * @param string $folder Check the status of this folder.
423:      *
424:      * @return array  An array that contains 'uidvalidity' and 'uidnext'.
425:      */
426:     public function status($folder)
427:     {
428:         $result = $this->getBackend()->status(
429:             $this->encodePath($folder),
430:             array('UIDVALIDITY', 'UIDNEXT')
431:         );
432:         if ($this->getBackend()->errornum != 0) {
433:             throw new Horde_Kolab_Storage_Exception(
434:                 sprintf(
435:                     Horde_Kolab_Storage_Translation::t(
436:                         "Retrieving the status for folder %s failed. Error: %s"
437:                     ),
438:                     $folder,
439:                     $this->getBackend()->error
440:                 )
441:             );
442:         }
443:         return array(
444:             'uidvalidity' => $result['UIDVALIDITY'],
445:             'uidnext' => $result['UIDNEXT']
446:         );
447:     }
448: 
449:     /**
450:      * Returns the message ids of the messages in this folder.
451:      *
452:      * @param string $folder Check the status of this folder.
453:      *
454:      * @return array  The message ids.
455:      */
456:     public function getUids($folder)
457:     {
458:         $uids = $this->getBackend()->search(
459:             $this->encodePath($folder),
460:             'UNDELETED',
461:             true
462:         );
463:         if ($this->getBackend()->errornum != 0) {
464:             throw new Horde_Kolab_Storage_Exception(
465:                 sprintf(
466:                     Horde_Kolab_Storage_Translation::t(
467:                         "Failed retrieving UIDs for folder %s. Error: %s"
468:                     ),
469:                     $folder,
470:                     $this->getBackend()->error
471:                 )
472:             );
473:         }
474:         if (!is_array($uids)) {
475:             $uids = array();
476:         }
477:         return $uids;
478:     }
479: 
480:     /**
481:      * Retrieves a complete message.
482:      *
483:      * @param string $folder The folder to fetch the messages from.
484:      * @param array  $uid    The message UID.
485:      *
486:      * @return array The message encapsuled as an array that contains a
487:      *               Horde_Mime_Headers and a Horde_Mime_Part object.
488:      */
489:     public function fetchComplete($folder, $uid)
490:     {
491:         $msg = $this->getBackend()->handlePartBody(
492:             $this->encodePath($folder), $uid, true, '', null, false
493:         );
494:         if ($this->getBackend()->errornum != 0) {
495:             throw new Horde_Kolab_Storage_Exception(
496:                 sprintf(
497:                     Horde_Kolab_Storage_Translation::t(
498:                         "Failed retrieving message %s in folder %s. Error: %s"
499:                     ),
500:                     $uid,
501:                     $folder,
502:                     $this->getBackend()->error
503:                 )
504:             );
505:         }
506: 
507:         return array(
508:             Horde_Mime_Headers::parseHeaders($msg),
509:             Horde_Mime_Part::parseMessage(
510:                 Horde_Mime_Part::getRawPartText($msg, 'body', 0)
511:             )
512:         );
513:     }
514: 
515:     /**
516:      * Retrieves the messages for the given message ids.
517:      *
518:      * @param string $folder The folder to fetch the messages from.
519:      * @param array  $uids                The message UIDs.
520:      *
521:      * @return array An array of message structures parsed into Horde_Mime_Part
522:      *               instances.
523:      */
524:     public function fetchStructure($folder, $uids)
525:     {
526:         $result = array();
527:         foreach ($uids as $uid) {
528:             $structure = $this->getBackend()->tokenizeResponse(
529:                 $this->getBackend()->fetchStructureString(
530:                     $this->encodePath($folder), $uid, true
531:                 )
532:             );
533:             if ($this->getBackend()->errornum != 0) {
534:                 throw new Horde_Kolab_Storage_Exception(
535:                     sprintf(
536:                         Horde_Kolab_Storage_Translation::t(
537:                             "Failed retrieving structure of message %s in folder %s. Error: %s"
538:                         ),
539:                         $uid,
540:                         $folder,
541:                         $this->getBackend()->error
542:                     )
543:                 );
544:             }
545:             $ob = $this->_parseStructure($structure[0]);
546:             $ob->buildMimeIds();
547:             $result[$uid]['structure'] = $ob;
548:         }
549:         return $result;
550:     }
551: 
552:     /**
553:      * Retrieves a bodypart for the given message ID and mime part ID.
554:      *
555:      * @param string $folder The folder to fetch the messages from.
556:      * @param array  $uid                 The message UID.
557:      * @param array  $id                  The mime part ID.
558:      *
559:      * @return resource|string The body part, as a stream resource or string.
560:      */
561:     public function fetchBodypart($folder, $uid, $id)
562:     {
563:         $resource = fopen('php://temp', 'r+');
564:         $this->getBackend()->handlePartBody(
565:             $this->encodePath($folder), $uid, true, $id, null, false, $resource
566:         );
567:         if ($this->getBackend()->errornum != 0) {
568:             throw new Horde_Kolab_Storage_Exception(
569:                 sprintf(
570:                     Horde_Kolab_Storage_Translation::t(
571:                         "Failed retrieving mime part %s of message %s in folder %s. Error: %s"
572:                     ),
573:                     $id,
574:                     $uid,
575:                     $folder,
576:                     $this->getBackend()->error
577:                 )
578:             );
579:         }
580:         return $resource;
581:     }
582: 
583:     /**
584:      * Appends a message to the given folder.
585:      *
586:      * @param string   $folder  The folder to append the message(s) to.
587:      * @param resource $msg     The message to append.
588:      *
589:      * @return mixed True or the UID of the new message in case the backend
590:      *               supports UIDPLUS.
591:      */
592:     public function appendMessage($folder, $msg)
593:     {
594:         rewind($msg);
595:         $this->getBackend()->append(
596:             $this->encodePath($folder), stream_get_contents($msg)
597:         );
598:         if ($this->getBackend()->errornum != 0) {
599:             throw new Horde_Kolab_Storage_Exception(
600:                 sprintf(
601:                     Horde_Kolab_Storage_Translation::t(
602:                         "Failed appending new message to folder %s. Error: %s"
603:                     ),
604:                     $folder,
605:                     $this->getBackend()->error
606:                 )
607:             );
608:         }
609:         return true;
610:     }
611: 
612:     /**
613:      * Deletes messages from the specified folder.
614:      *
615:      * @param string  $folder  The folder to delete messages from.
616:      * @param integer $uids    IMAP message ids.
617:      *
618:      * @return NULL
619:      */
620:     public function deleteMessages($folder, $uids)
621:     {
622:         $this->getBackend()->delete(
623:             $this->encodePath($folder), $uids
624:         );
625:         if ($this->getBackend()->errornum != 0) {
626:             throw new Horde_Kolab_Storage_Exception(
627:                 sprintf(
628:                     Horde_Kolab_Storage_Translation::t(
629:                         "Failed deleting messages from folder %s. Error: %s"
630:                     ),
631:                     $folder,
632:                     $this->getBackend()->error
633:                 )
634:             );
635:         }
636:     }
637: 
638:     /**
639:      * Moves a message to a new folder.
640:      *
641:      * @param integer $uid         IMAP message id.
642:      * @param string  $old_folder  Source folder.
643:      * @param string  $new_folder  Target folder.
644:      *
645:      * @return NULL
646:      */
647:     public function moveMessage($uid, $old_folder, $new_folder)
648:     {
649:         $this->getBackend()->move(
650:             $uid,
651:             $this->encodePath($old_folder),
652:             $this->encodePath($new_folder)
653:         );
654:         if ($this->getBackend()->errornum != 0) {
655:             throw new Horde_Kolab_Storage_Exception(
656:                 sprintf(
657:                     Horde_Kolab_Storage_Translation::t(
658:                         "Failed moving message %s from folder %s to folder %s. Error: %s"
659:                     ),
660:                     $uid,
661:                     $old_folder,
662:                     $new_folder,
663:                     $this->getBackend()->error
664:                 )
665:             );
666:         }
667:         $this->expunge($old_folder);
668:     }
669: 
670:     /**
671:      * Expunges messages in the current folder.
672:      *
673:      * @return mixed  True or a PEAR error in case of an error.
674:      */
675:     public function expunge($folder)
676:     {
677:         $this->getBackend()->expunge($this->encodePath($folder));
678:         if ($this->getBackend()->errornum != 0) {
679:             throw new Horde_Kolab_Storage_Exception(
680:                 sprintf(
681:                     Horde_Kolab_Storage_Translation::t(
682:                         "Failed expunging folder %s. Error: %s"
683:                     ),
684:                     $folder,
685:                     $this->getBackend()->error
686:                 )
687:             );
688:         }
689:     }
690: 
691:     /**
692:      * Parse the output from imap_fetchstructure() into a MIME Part object.
693:      *
694:      * @param object $data  Data from imap_fetchstructure().
695:      *
696:      * @return Horde_Mime_Part  A MIME Part object.
697:      */
698:     /**
699:      * Recursively parse BODYSTRUCTURE data from a FETCH return (see
700:      * RFC 3501 [7.4.2]).
701:      *
702:      * @param array $data  The tokenized information from the server.
703:      *
704:      * @return array  The array of bodystructure information.
705:      */
706:     protected function _parseStructure($data)
707:     {
708:         $ob = new Horde_Mime_Part();
709: 
710:         // If index 0 is an array, this is a multipart part.
711:         if (is_array($data[0])) {
712:             // Keep going through array values until we find a non-array.
713:             for ($i = 0, $cnt = count($data); $i < $cnt; ++$i) {
714:                 if (!is_array($data[$i])) {
715:                     break;
716:                 }
717:                 $ob->addPart($this->_parseStructure($data[$i]));
718:             }
719: 
720:             // The first string entry after an array entry gives us the
721:             // subpart type.
722:             $ob->setType('multipart/' . $data[$i]);
723: 
724:             // After the subtype is further extension information. This
725:             // information MAY not appear for BODYSTRUCTURE requests.
726: 
727:             // This is parameter information.
728:             if (isset($data[++$i]) && is_array($data[$i])) {
729:                 foreach ($this->_parseStructureParams($data[$i], 'content-type') as $key => $val) {
730:                     $ob->setContentTypeParameter($key, $val);
731:                 }
732:             }
733: 
734:             // This is disposition information.
735:             if (isset($data[++$i]) && is_array($data[$i])) {
736:                 $ob->setDisposition($data[$i][0]);
737: 
738:                 foreach ($this->_parseStructureParams($data[$i][1], 'content-disposition') as $key => $val) {
739:                     $ob->setDispositionParameter($key, $val);
740:                 }
741:             }
742: 
743:             // This is language information. It is either a single value or
744:             // a list of values.
745:             if (isset($data[++$i])) {
746:                 $ob->setLanguage($data[$i]);
747:             }
748: 
749:             // Ignore: location (RFC 2557)
750:             // There can be further information returned in the future, but
751:             // for now we are done.
752:         } else {
753:             $ob->setType($data[0] . '/' . $data[1]);
754: 
755:             foreach ($this->_parseStructureParams($data[2], 'content-type') as $key => $val) {
756:                 $ob->setContentTypeParameter($key, $val);
757:             }
758: 
759:             if ($data[3] !== null) {
760:                 $ob->setContentId($data[3]);
761:             }
762: 
763:             if ($data[4] !== null) {
764:                 $ob->setDescription(Horde_Mime::decode($data[4], 'UTF-8'));
765:             }
766: 
767:             if ($data[5] !== null) {
768:                 $ob->setTransferEncoding($data[5]);
769:             }
770: 
771:             if ($data[6] !== null) {
772:                 $ob->setBytes($data[6]);
773:             }
774: 
775:             // If the type is 'message/rfc822' or 'text/*', several extra
776:             // fields are included
777:             switch ($ob->getPrimaryType()) {
778:             case 'message':
779:                 if ($ob->getSubType() == 'rfc822') {
780:                     // Ignore: envelope
781:                     $ob->addPart($this->_parseStructure($data[8]));
782:                     // Ignore: lines
783:                     $i = 10;
784:                 } else {
785:                     $i = 7;
786:                 }
787:                 break;
788: 
789:             case 'text':
790:                 // Ignore: lines
791:                 $i = 8;
792:                 break;
793: 
794:             default:
795:                 $i = 7;
796:                 break;
797:             }
798: 
799:             // After the subtype is further extension information. This
800:             // information MAY appear for BODYSTRUCTURE requests.
801: 
802:             // Ignore: MD5
803: 
804:             // This is disposition information
805:             if (isset($data[++$i]) && is_array($data[$i])) {
806:                 $ob->setDisposition($data[$i][0]);
807: 
808:                 foreach ($this->_parseStructureParams($data[$i][1], 'content-disposition') as $key => $val) {
809:                     $ob->setDispositionParameter($key, $val);
810:                 }
811:             }
812: 
813:             // This is language information. It is either a single value or
814:             // a list of values.
815:             if (isset($data[++$i])) {
816:                 $ob->setLanguage($data[$i]);
817:             }
818: 
819:             // Ignore: location (RFC 2557)
820:         }
821: 
822:         return $ob;
823:     }
824: 
825:     /**
826:      * Helper function to parse a parameters-like tokenized array.
827:      *
828:      * @param array $data   The tokenized data.
829:      * @param string $type  The header name.
830:      *
831:      * @return array  The parameter array.
832:      */
833:     protected function _parseStructureParams($data, $type)
834:     {
835:         $params = array();
836: 
837:         if (is_array($data)) {
838:             for ($i = 0, $cnt = count($data); $i < $cnt; ++$i) {
839:                 $params[strtolower($data[$i])] = $data[++$i];
840:             }
841:         }
842: 
843:         $ret = Horde_Mime::decodeParam($type, $params, 'UTF-8');
844: 
845:         return $ret['params'];
846:     }
847: }
848: 
API documentation generated by ApiGen