Overview

Packages

  • Horde
    • Icalendar
      • UnitTests
  • Ingo
    • UnitTests
  • None

Classes

  • Horde_Core_Ui_VarRenderer_Ingo
  • Ingo
  • Ingo_Api
  • Ingo_Exception
  • Ingo_Exception_Pear
  • Ingo_LoginTasks_SystemTask_Upgrade
  • Ingo_Script
  • Ingo_Script_Imap
  • Ingo_Script_Imap_Api
  • Ingo_Script_Imap_Live
  • Ingo_Script_Maildrop
  • Ingo_Script_Maildrop_Comment
  • Ingo_Script_Maildrop_Recipe
  • Ingo_Script_Maildrop_Variable
  • Ingo_Script_Procmail
  • Ingo_Script_Procmail_Comment
  • Ingo_Script_Procmail_Recipe
  • Ingo_Script_Procmail_Variable
  • Ingo_Script_Sieve
  • Ingo_Script_Sieve_Action
  • Ingo_Script_Sieve_Action_Addflag
  • Ingo_Script_Sieve_Action_Discard
  • Ingo_Script_Sieve_Action_Fileinto
  • Ingo_Script_Sieve_Action_Flag
  • Ingo_Script_Sieve_Action_Keep
  • Ingo_Script_Sieve_Action_Notify
  • Ingo_Script_Sieve_Action_Redirect
  • Ingo_Script_Sieve_Action_Reject
  • Ingo_Script_Sieve_Action_Removeflag
  • Ingo_Script_Sieve_Action_Stop
  • Ingo_Script_Sieve_Action_Vacation
  • Ingo_Script_Sieve_Comment
  • Ingo_Script_Sieve_Else
  • Ingo_Script_Sieve_Elsif
  • Ingo_Script_Sieve_If
  • Ingo_Script_Sieve_Test
  • Ingo_Script_Sieve_Test_Address
  • Ingo_Script_Sieve_Test_Allof
  • Ingo_Script_Sieve_Test_Anyof
  • Ingo_Script_Sieve_Test_Body
  • Ingo_Script_Sieve_Test_Exists
  • Ingo_Script_Sieve_Test_False
  • Ingo_Script_Sieve_Test_Header
  • Ingo_Script_Sieve_Test_Not
  • Ingo_Script_Sieve_Test_Relational
  • Ingo_Script_Sieve_Test_Size
  • Ingo_Script_Sieve_Test_True
  • Ingo_Storage
  • Ingo_Storage_Blacklist
  • Ingo_Storage_Filters
  • Ingo_Storage_Filters_Sql
  • Ingo_Storage_Forward
  • Ingo_Storage_Mock
  • Ingo_Storage_Prefs
  • Ingo_Storage_Rule
  • Ingo_Storage_Spam
  • Ingo_Storage_Sql
  • Ingo_Storage_Vacation
  • Ingo_Storage_VacationTest
  • Ingo_Storage_Whitelist
  • Ingo_Test
  • Ingo_Transport
  • Ingo_Transport_Ldap
  • Ingo_Transport_Null
  • Ingo_Transport_Sivtest
  • Ingo_Transport_Timsieved
  • Ingo_Transport_Vfs
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Ingo base class.
  4:  *
  5:  * See the enclosed file LICENSE for license information (ASL).  If you
  6:  * did not receive this file, see http://www.horde.org/licenses/apache.
  7:  *
  8:  * @author  Mike Cochrane <mike@graftonhall.co.nz>
  9:  * @author  Jan Schneider <jan@horde.org>
 10:  * @package Ingo
 11:  */
 12: class Ingo
 13: {
 14:     /**
 15:      * String that can't be a valid folder name used to mark blacklisted email
 16:      * as deleted.
 17:      */
 18:     const BLACKLIST_MARKER = '++DELETE++';
 19: 
 20:     /**
 21:      * Define the key to use to indicate a user-defined header is requested.
 22:      */
 23:     const USER_HEADER = '++USER_HEADER++';
 24: 
 25:     /**
 26:      * hasSharePermission() cache.
 27:      *
 28:      * @var integer
 29:      */
 30:     static private $_shareCache = null;
 31: 
 32:     /**
 33:      * Create an ingo session.
 34:      *
 35:      * Creates the $ingo session variable with the following entries:
 36:      * <pre>
 37:      * 'backend' (array) - The backend configuration to use.
 38:      * 'change' (integer) - The timestamp of the last time the rules were
 39:      *                      altered.
 40:      * 'storage' (array) - Used by Ingo_Storage:: for caching data.
 41:      * 'script_categories' (array) - The list of available categories for the
 42:      *                               Ingo_Script driver in use.
 43:      * 'script_generate' (boolean) - Is the Ingo_Script::generate() call
 44:      *                               available?
 45:      * </pre>
 46:      *
 47:      * @throws Ingo_Exception
 48:      */
 49:     static public function createSession()
 50:     {
 51:         global $prefs, $session;
 52: 
 53:         if ($session->exists('ingo', 'script_generate')) {
 54:             return;
 55:         }
 56: 
 57:         /* getBackend() and loadIngoScript() will both throw Exceptions, so
 58:          * do these first as errors are fatal. */
 59:         foreach (self::getBackend() as $key => $val) {
 60:             if ($val) {
 61:                 $session->set('ingo', 'backend/' . $key, $val);
 62:             }
 63:         }
 64: 
 65:         $ingo_script = self::loadIngoScript();
 66:         $session->set('ingo', 'script_generate', $ingo_script->generateAvailable());
 67: 
 68:         /* Disable categories as specified in preferences */
 69:         $categories = array_flip(
 70:             array_merge($ingo_script->availableActions(),
 71:                         $ingo_script->availableCategories()));
 72:         if ($prefs->isLocked('blacklist')) {
 73:             unset($categories[Ingo_Storage::ACTION_BLACKLIST]);
 74:         }
 75:         if ($prefs->isLocked('whitelist')) {
 76:             unset($categories[Ingo_Storage::ACTION_WHITELIST]);
 77:         }
 78:         if ($prefs->isLocked('vacation')) {
 79:             unset($categories[Ingo_Storage::ACTION_VACATION]);
 80:         }
 81:         if ($prefs->isLocked('forward')) {
 82:             unset($categories[Ingo_Storage::ACTION_FORWARD]);
 83:         }
 84:         if ($prefs->isLocked('spam')) {
 85:             unset($categories[Ingo_Storage::ACTION_SPAM]);
 86:         }
 87:         $categories = array_flip($categories);
 88: 
 89:         /* Set the list of categories this driver supports. */
 90:         $session->set('ingo', 'script_categories', $categories);
 91:     }
 92: 
 93:     /**
 94:      * Generates a folder widget.
 95:      * If an application is available that provides a folderlist method
 96:      * then a &lt;select&gt; input is created. Otherwise a simple text field
 97:      * is returned.
 98:      *
 99:      * @param string $value    The current value for the field.
100:      * @param string $form     The form name for the newFolderName() call.
101:      * @param string $tagname  The label for the select tag.
102:      *
103:      * @return string  The HTML to render the field.
104:      */
105:     static public function flistSelect($value = null, $form = null,
106:                                        $tagname = 'actionvalue')
107:     {
108:         global $conf, $registry;
109: 
110:         if ($registry->hasMethod('mail/folderlist')) {
111:             $createfolder = $registry->hasMethod('mail/createFolder');
112:             try {
113:                 $mailboxes = $registry->call('mail/folderlist');
114: 
115:                 $text = '<select class="flistSelect" id="' . $tagname . '" name="' . $tagname . '">' .
116:                     '<option value="">' . _("Select target folder:") . '</option>' .
117:                     '<option disabled="disabled">- - - - - - - - - -</option>';
118: 
119:                 if ($createfolder) {
120:                     $text .= '<option class="flistCreate" value="">' . _("Create new folder") . '</option>' .
121:                         '<option disabled="disabled">- - - - - - - - - -</option>';
122:                 }
123: 
124:                 foreach ($mailboxes as $key => $val) {
125:                     $text .= sprintf(
126:                         "<option value=\"%s\"%s>%s</option>\n",
127:                         htmlspecialchars($key),
128:                         ($key === $value) ? ' selected="selected"' : '',
129:                         str_repeat('&nbsp;', $val['level'] * 2) . htmlspecialchars($val['label'])
130:                     );
131:                 }
132: 
133:                 Horde::addScriptFile('new_folder.js', 'ingo');
134:                 Horde::addInlineJsVars(array(
135:                     'IngoNewFolder.folderprompt' => _("Please enter the name of the new folder:")
136:                 ));
137: 
138:                 return $text . '</select>';
139:             } catch (Horde_Exception $e) {}
140:         }
141: 
142:         return '<input id="' . $tagname . '" name="' . $tagname . '" size="40" value="' . $value . '" />';
143:     }
144: 
145:     /**
146:      * Validates an IMAP mailbox provided by user input.
147:      *
148:      * @param Horde_Variables $vars  An variables object.
149:      * @param string $name           The form name of the folder input.
150:      *
151:      * @return string  The IMAP mailbox name.
152:      * @throws Horde_Exception
153:      */
154:     static public function validateFolder(Horde_Variables $vars, $name)
155:     {
156:         $new_id = $name . '_new';
157:         if (isset($vars->$new_id)) {
158:             if ($GLOBALS['registry']->hasMethod('mail/createFolder') &&
159:                 $GLOBALS['registry']->call('mail/createFolder', array(Horde_String::convertCharset($vars->$new_id, 'UTF-8', 'UTF7-IMAP')))) {
160:                 return $vars->$new_id;
161:             }
162:         } elseif (isset($vars->$name) && strlen($vars->$name)) {
163:             return $vars->$name;
164:         }
165: 
166:         throw new Ingo_Exception(_("Could not validate IMAP mailbox."));
167:     }
168: 
169:     /**
170:      * Returns the user whose rules are currently being edited.
171:      *
172:      * @param boolean $full  Always return the full user name with realm?
173:      *
174:      * @return string  The current user.
175:      */
176:     static public function getUser($full = true)
177:     {
178:         if (empty($GLOBALS['ingo_shares'])) {
179:             $baseuser = ($full ||
180:                         ($GLOBALS['session']->get('ingo', 'backend/hordeauth') === 'full'));
181:             $user = $GLOBALS['registry']->getAuth($baseuser ? null : 'bare');
182:         } else {
183:             list(, $user) = explode(':', $GLOBALS['session']->get('ingo', 'current_share'), 2);
184:         }
185: 
186:         return $user;
187:     }
188: 
189:     /**
190:      * Returns the domain name, if any of the user whose rules are currently
191:      * being edited.
192:      *
193:      * @return string  The current user's domain name.
194:      */
195:     static public function getDomain()
196:     {
197:         $user = self::getUser(true);
198:         $pos = strpos($user, '@');
199: 
200:         return ($pos !== false)
201:             ? substr($user, $pos + 1)
202:             : false;
203:     }
204: 
205:     /**
206:      * Connects to the backend and uploads the script and sets it active.
207:      *
208:      * @param string $script       The script to set active.
209:      * @param boolean $deactivate  If true, notification will identify the
210:      *                             script as deactivated instead of activated.
211:      * @param array $additional    Any additional scripts that need to uploaded.
212:      *
213:      * @return boolean  True on success, false on failure.
214:      */
215:     static public function activateScript($script, $deactivate = false,
216:                                           $additional = array())
217:     {
218:         $transport = self::getTransport();
219: 
220:         try {
221:             $transport->setScriptActive($script, $additional);
222:         } catch (Ingo_Exception $e) {
223:             $msg = ($deactivate)
224:               ? _("There was an error deactivating the script.")
225:               : _("There was an error activating the script.");
226:             $GLOBALS['notification']->push($msg . ' ' . _("The driver said: ") . $e->getMessage(), 'horde.error');
227:             return false;
228:         }
229: 
230:         $msg = ($deactivate)
231:             ? _("Script successfully deactivated.")
232:             : _("Script successfully activated.");
233:         $GLOBALS['notification']->push($msg, 'horde.success');
234: 
235:         return true;
236:     }
237: 
238:     /**
239:      * Connects to the backend and returns the currently active script.
240:      *
241:      * @return string  The currently active script.
242:      */
243:     static public function getScript()
244:     {
245:         return self::getTransport()->getScript();
246:     }
247: 
248:     /**
249:      * Does all the work in updating the script on the server.
250:      */
251:     static public function updateScript()
252:     {
253:         if ($GLOBALS['session']->get('ingo', 'script_generate')) {
254:             try {
255:                 $ingo_script = self::loadIngoScript();
256: 
257:                 /* Generate and activate the script. */
258:                 self::activateScript($ingo_script->generate(),
259:                                      false,
260:                                      $ingo_script->additionalScripts());
261:             } catch (Ingo_Exception $e) {
262:                 $GLOBALS['notification']->push(_("Script not updated."), 'horde.error');
263:             }
264:         }
265:     }
266: 
267:     /**
268:      * Determine the backend to use.
269:      *
270:      * This decision is based on the global 'SERVER_NAME' and 'HTTP_HOST'
271:      * server variables and the contents of the 'preferred' either field
272:      * in the backend's definition.  The 'preferred' field may take a
273:      * single value or an array of multiple values.
274:      *
275:      * @return array  The backend entry.
276:      * @throws Ingo_Exception
277:      */
278:     static public function getBackend()
279:     {
280:         $backends = Horde::loadConfiguration('backends.php', 'backends', 'ingo');
281:         if (!isset($backends) || !is_array($backends)) {
282:             throw new Ingo_Exception(_("No backends configured in backends.php"));
283:         }
284: 
285:         $backend = null;
286:         foreach ($backends as $name => $temp) {
287:             if (!empty($temp['disabled'])) {
288:                 continue;
289:             }
290:             if (!isset($backend)) {
291:                 $backend = $name;
292:             } elseif (!empty($temp['preferred'])) {
293:                 if (is_array($temp['preferred'])) {
294:                     foreach ($temp['preferred'] as $val) {
295:                         if (($val == $_SERVER['SERVER_NAME']) ||
296:                             ($val == $_SERVER['HTTP_HOST'])) {
297:                             $backend = $name;
298:                         }
299:                     }
300:                 } elseif (($temp['preferred'] == $_SERVER['SERVER_NAME']) ||
301:                           ($temp['preferred'] == $_SERVER['HTTP_HOST'])) {
302:                     $backend = $name;
303:                 }
304:             }
305:         }
306: 
307:         /* Check for valid backend configuration. */
308:         if (is_null($backend)) {
309:             throw new Ingo_Exception(_("No backend configured for this host"));
310:         }
311: 
312:         $backends[$backend]['id'] = $name;
313:         $backend = $backends[$backend];
314: 
315:         foreach (array('script', 'transport') as $val) {
316:             if (empty($backend[$val])) {
317:                 throw new Ingo_Exception(sprintf(_("No \"%s\" element found in backend configuration."), $val));
318:             }
319:         }
320: 
321:         /* Make sure the 'params' entry exists. */
322:         if (!isset($backend['params'])) {
323:             $backend['params'] = array();
324:         }
325: 
326:         return $backend;
327:     }
328: 
329:     /**
330:      * Loads a Ingo_Script:: backend and checks for errors.
331:      *
332:      * @return Ingo_Script  Script object on success.
333:      * @throws Ingo_Exception
334:      */
335:     static public function loadIngoScript()
336:     {
337:         return Ingo_Script::factory(
338:             $GLOBALS['session']->get('ingo', 'backend/script'),
339:             $GLOBALS['session']->get('ingo', 'backend/scriptparams', Horde_Session::TYPE_ARRAY)
340:         );
341:     }
342: 
343:     /**
344:      * Returns an instance of the configured transport driver.
345:      *
346:      * @return Ingo_Transport  The configured driver.
347:      * @throws Ingo_Exception
348:      */
349:     static public function getTransport()
350:     {
351:         global $registry, $session;
352: 
353:         $params = $session->get('ingo', 'backend/params');
354: 
355:         // Set authentication parameters.
356:         if (($hordeauth = $session->get('ingo', 'backend/hordeauth')) ||
357:             !isset($params['username']) ||
358:             !isset($params['password'])) {
359:             $params['username'] = $registry->getAuth(($hordeauth === 'full') ? null : 'bare');
360:             $params['password'] = $registry->getAuthCredential('password');
361:         }
362: 
363:         return Ingo_Transport::factory($GLOBALS['session']->get('ingo', 'backend/transport'), $params);
364:     }
365: 
366:     /**
367:      * Returns all rulesets a user has access to, according to several
368:      * parameters/permission levels.
369:      *
370:      * @param boolean $owneronly   Only return rulesets that this user owns?
371:      *                             Defaults to false.
372:      * @param integer $permission  The permission to filter rulesets by.
373:      *
374:      * @return array  The ruleset list.
375:      */
376:     static public function listRulesets($owneronly = false,
377:                                         $permission = Horde_Perms::SHOW)
378:     {
379:         try {
380:             $rulesets = $GLOBALS['ingo_shares']->listShares(
381:                 $GLOBALS['registry']->getAuth(),
382:                 array('perm' => $permission,
383:                       'attributes' => $owneronly ? $GLOBALS['registry']->getAuth() : null));
384:         } catch (Horde_Share_Exception $e) {
385:             Horde::logMessage($e, 'ERR');
386:             return array();
387:         }
388: 
389:         return $rulesets;
390:     }
391: 
392:     /**
393:      * TODO
394:      */
395:     static public function hasSharePermission($mask = null)
396:     {
397:         if (!isset($GLOBALS['ingo_shares'])) {
398:             return true;
399:         }
400: 
401:         if (is_null(self::$_shareCache)) {
402:             self::$_shareCache = $GLOBALS['ingo_shares']->getPermissions($GLOBALS['session']->get('ingo', 'current_share'), $GLOBALS['registry']->getAuth());
403:         }
404: 
405:         return self::$_shareCache & $mask;
406:     }
407: 
408:     /**
409:      * Returns whether an address is empty or only contains a "@".
410:      * Helper function for array_filter().
411:      *
412:      * @param string $address  An email address to test.
413:      *
414:      * @return boolean  True if the address is not empty.
415:      */
416:     static public function filterEmptyAddress($address)
417:     {
418:         $address = trim($address);
419:         return !empty($address) && ($address != '@');
420:     }
421: 
422:     /**
423:      * Create ingo's menu.
424:      *
425:      * @return string  The menu text.
426:      */
427:     static public function menu()
428:     {
429:         $t = $GLOBALS['injector']->createInstance('Horde_Template');
430:         $t->set('form_url', Horde::url('filters.php'));
431:         $t->set('forminput', Horde_Util::formInput());
432: 
433:         if (!empty($GLOBALS['ingo_shares']) &&
434:             (count($GLOBALS['all_rulesets']) > 1)) {
435:             $options = array();
436:             foreach (array_keys($GLOBALS['all_rulesets']) as $id) {
437:                 $options[] = array(
438:                     'name' => htmlspecialchars($GLOBALS['all_rulesets'][$id]->get('name')),
439:                     'selected' => ($GLOBALS['session']->get('ingo', 'current_share') == $id),
440:                     'val' => htmlspecialchars($id)
441:                 );
442:             }
443:             $t->set('options', $options);
444:         }
445: 
446:         $t->set('menu_string', Horde::menu(array('menu_ob' => true))->render());
447: 
448:         $menu = $t->fetch(INGO_TEMPLATES . '/menu/menu.html');
449: 
450:         /* Need to buffer sidebar output here, because it may add things like
451:          * cookies which need to be sent before output begins. */
452:         Horde::startBuffer();
453:         require HORDE_BASE . '/services/sidebar.php';
454:         return $menu . Horde::endBuffer();
455:     }
456: 
457:     /**
458:      * Outputs Ingo's status/notification bar.
459:      */
460:     static public function status()
461:     {
462:         $GLOBALS['notification']->notify(array('listeners' => array('status', 'audio')));
463:     }
464: 
465: }
466: 
API documentation generated by ApiGen