1: <?php
2: /**
3: * Copyright 2003-2012 Horde LLC (http://www.horde.org/)
4: *
5: * See the enclosed file LICENSE for license information (BSD). If you did not
6: * did not receive this file, see http://cvs.horde.org/co.php/vilma/LICENSE.
7: *
8: * @author Marko Djukic <marko@oblo.com>
9: * @author Daniel Collins <horde_dev@argentproductions.com>
10: * @package Vilma
11: */
12: abstract class Vilma_Driver
13: {
14: /**
15: * A hash containing any parameters for the current driver.
16: *
17: * @var array
18: */
19: protected $_params = array();
20:
21: /**
22: * Constructor.
23: *
24: * @param array $params Any parameters needed for this driver.
25: */
26: public function __construct(array $params)
27: {
28: $this->_params = $params;
29: }
30:
31: /**
32: * Returns the list of domains from the backend.
33: *
34: * @return array All the domains and their data in an array.
35: */
36: abstract public function getDomains();
37:
38: /**
39: * Returns the specified domain information from the backend.
40: *
41: * @param integer $domain_id The id of the domain to fetch.
42: *
43: * @return array The domain's information in an array.
44: */
45: abstract public function getDomain($domain_id);
46:
47: /**
48: * Given a domain name returns the information from the backend.
49: *
50: * @param string $name The name of the domain to fetch.
51: *
52: * @return array The domain's information in an array.
53: */
54: abstract public function getDomainByName($domain_name);
55:
56: /**
57: * Saves a domain with the provided information.
58: *
59: * @param array $info Array of details to save the domain.
60: *
61: * @throws Vilma_Exception
62: */
63: public function saveDomain($info)
64: {
65: $this->_saveDomain($info);
66: try {
67: Horde::callHook('saveDomain', array($info), 'vilma');
68: } catch (Horde_Exception_HookNotSet $e) {
69: }
70: }
71:
72: /**
73: * Saves a domain with the provided information.
74: *
75: * @param array $info Array of details to save the domain.
76: */
77: abstract protected function _saveDomain($info);
78:
79: /**
80: * Deletes a domain and all the users and virtuals within it.
81: *
82: * @param integer $domain_id The id of the domain to delete.
83: *
84: * @throws Vilma_Exception
85: */
86: public function deleteDomain($domain_id)
87: {
88: $domain_record = $this->getDomain($domain_id);
89: $users = $this->getUsers($domain_record['domain_name']);
90: foreach ($users as $user) {
91: $this->deleteUser($user['user_id']);
92: }
93: $this->_deleteDomain($domain_id);
94: try {
95: Horde::callHook('deleteDomain', array($domain_record['domain_name']), 'vilma');
96: } catch (Horde_Exception_HookNotSet $e) {
97: }
98: }
99:
100: /**
101: * Deletes a domain.
102: *
103: * @param integer $domain_id The id of the domain to delete.
104: *
105: * @throws Vilma_Exception
106: */
107: abstract protected function _deleteDomain($domain_id);
108:
109: /**
110: * Returns the user who is the domain administrator.
111: *
112: * @todo This should be replaced by moving all permissions into Horde
113: * permissions.
114: *
115: * @param string $domain_name The name of the domain for which to
116: * return the administrator.
117: *
118: * @return string The domain adminstrator.
119: * @throws Vilma_Exception
120: */
121: public function getDomainAdmin($domain_name)
122: {
123: $domain = $this->getDomainByName($domain_name);
124: return $domain['domain_admin'];
125: }
126:
127: /**
128: * Returns the configured quota for this domain.
129: *
130: * @param string $domain_name The name of the domain for which to
131: * return the quota.
132: *
133: * @return integer The domain quota.
134: * @throws Vilma_Exception
135: */
136: public function getDomainQuota($domain_name)
137: {
138: $domain = $this->getDomainByName($domain_name);
139: return $domain['domain_quota'];
140: }
141:
142: /**
143: * Returns the maximum number of users allowed for a given domain.
144: *
145: * @param string $domain_name The name of the domain for which to
146: * return the maximum users.
147: *
148: * @return integer The maximum number of allowed users.
149: * @throws Vilma_Exception
150: */
151: public function getDomainMaxUsers($domain_name)
152: {
153: $domain = $this->getDomainByName($domain_name);
154: return $domain['max_users'];
155: }
156:
157: /**
158: * Returns the current number of users for a domain.
159: *
160: * @param string $domain_name The name of the domain for which to
161: * get the current number of users.
162: *
163: * @return integer The current number of users.
164: */
165: abstract public function getDomainNumUsers($domain_name);
166:
167: /**
168: * Checks if the given domain is below the maximum allowed users.
169: *
170: * @param string $domain The domain name to check.
171: *
172: * @return boolean True if the domain does not have a maximum limit (0) or
173: * current number of users is below the maximum number
174: * allowed.
175: */
176: public function isBelowMaxUsers($domain)
177: {
178: /* Get the maximum number of users for this domain. */
179: $max_users = $this->getDomainMaxUsers($domain);
180: if (!$max_users) {
181: /* No maximum. */
182: return true;
183: }
184:
185: /* Get the current number of users. */
186: return $this->getDomainNumUsers($domain) < $max_users;
187: }
188:
189: /**
190: * Returns all available users, if a domain name is passed then limit the
191: * list of users only to those users.
192: *
193: * @param string $domain The name of the domain for which to fetch the
194: * users.
195: *
196: * @return array The available users and their stored information.
197: * @throws Vilma_Exception
198: */
199: abstract public function getUsers($domain = null);
200:
201: /**
202: * Returns all the users sorted by domain and as arrays of each domain.
203: *
204: * @return array An array of domains then users for each domain.
205: */
206: public function getAllUsers()
207: {
208: /* Determine the domain for each user and plug into array by domain. */
209: $users = array();
210: foreach ($this->getUsers() as $user) {
211: $domain = Vilma::stripDomain($user['user_name']);
212: $users[$domain][] = $user;
213: }
214:
215: /* Sort by domain. */
216: ksort($users);
217:
218: /* Sort each domain's users by user name. */
219: foreach ($users as $key => $val) {
220: Horde_Array::arraySort($users[$key], 'user_name');
221: }
222:
223: return $users;
224: }
225:
226: /**
227: * Returns the user information for a given user id.
228: *
229: * @param integer $user_id The id of the user for which to fetch
230: * information.
231: *
232: * @return array The user information.
233: */
234: abstract public function getUser($user_id);
235:
236: /**
237: * Does a series of checks for a given user to determine the status.
238: *
239: * @param array $user The user's details in an array as returned by the
240: * getUser() function.
241: *
242: * @return array Either an array of error messages found during the checks
243: * or an array with a single element stating that the user
244: * is ready.
245: * @throws Vilma_Exception if an error occurs looking up the user status.
246: */
247: public function getUserStatus($user)
248: {
249: /* Some needed vars. */
250: $error = false;
251: $status = array();
252: $domain_name = Vilma::stripDomain($user['user_name']);
253: $user_name = Vilma::stripUser($user['user_name']);
254:
255: /* Check if user enabled. */
256: if ($user['user_enabled'] !== 'active') {
257: $error = true;
258: $err_msg = _("User disabled.");
259: $status[] = Horde::img('alerts/error.png', $err_msg) . ' ' . $err_msg;
260: }
261:
262: /* Check if mailbox exists. */
263: try {
264: Vilma_MailboxDriver::factory()->checkMailbox($user_name, $domain_name);
265: } catch (Exception $result) {
266: $error = true;
267: $err_msg = $result->getMessage();
268: $status[] = Horde::img('alerts/warning.png', $err_msg) . ' ' . $err_msg;
269: }
270:
271: /* TODO: Quota checking would be nice too. */
272:
273: /* If no errors have been found output a success message for this
274: * user's status. */
275: if (!$error) {
276: $msg = _("User ready.");
277: $status = array(Horde::img('alerts/success.png', $msg) . ' ' . $msg);
278: }
279:
280: return $status;
281: }
282:
283: /**
284: * @throws Vilma_Exception
285: */
286: public function saveUser($info)
287: {
288: $create = empty($info['user_id']);
289: $info['user_id'] = $this->_saveUser($info);
290:
291: if ($create) {
292: try {
293: Vilma_MailboxDriver::factory()
294: ->createMailbox(Vilma::stripUser($info['user_name']),
295: Vilma::stripDomain($info['user_name']));
296: } catch (Exception $e) {
297: $this->_deleteUser($info['user_id']);
298: throw $e;
299: }
300: }
301:
302: if (isset($GLOBALS['conf']['mta']['auth_update_script']) &&
303: !empty($info['password'])) {
304: $cmd = sprintf('%s set %s %s 2>&1',
305: $GLOBALS['conf']['mta']['auth_update_script'],
306: escapeshellarg($info['user_name']),
307: escapeshellarg($info['password']));
308: $msg = system($cmd, $ec);
309: if ($msg === false) {
310: throw new Vilma_Exception(_("Error running authentication update script."));
311: }
312: if ($ec != 0) {
313: throw new Vilma_Exception(_("Unknown error running authentication update script."));
314: }
315: }
316: }
317:
318: /**
319: * Saves a user to the backend.
320: *
321: * @param array $info The user information to save.
322: *
323: * @return string The user ID.
324: * @throws Vilma_Exception
325: */
326: abstract protected function _saveUser($info);
327:
328: /**
329: * Deletes a user.
330: *
331: * @param integer $user_id The id of the user to delete.
332: *
333: * @throws Vilma_Exception
334: */
335: abstract public function deleteUser($user_id);
336:
337: public function getUserFormAttributes()
338: {
339: }
340:
341: /**
342: * Returns a list of all users, aliases, or groups and forwards for a
343: * domain.
344: *
345: * @param string $domain Domain on which to search.
346: * @param string $type Only return a specific type. One of 'all',
347: * 'user', 'alias', 'forward', or 'group'.
348: * @param string $key Sort list by this key.
349: * @param integer $direction Sort direction.
350: *
351: * @return array Account information for this domain.
352: */
353: public function getAddresses($domain, $type = 'all', $key = 'user_name',
354: $direction = 0)
355: {
356: $addresses = $this->_getAddresses($domain, $type);
357: Horde_Array::arraySort($addresses, $key, $direction, true);
358: return $addresses;
359: }
360:
361: /**
362: * Returns a list of all users, aliases, or groups and forwards for a
363: * domain.
364: *
365: * @param string $domain Domain on which to search.
366: * @param string $type Only return a specific type. One of 'all',
367: * 'user', 'alias','forward', or 'group'.
368: * @param string $key Sort list by this key.
369: * @param integer $direction Sort direction.
370: *
371: * @return array Account information for this domain.
372: */
373: abstract protected function _getAddresses($domain, $type = 'all');
374:
375: /**
376: * Returns an array of information related to the address passed in.
377: *
378: * This method may be overridden by the backend driver if there is a more
379: * efficient way to do this than a linear array search.
380: *
381: * @param string $address Address for which information will be pulled.
382: * @param string $type Address type to request.
383: * One of 'all', 'user', 'alias', 'forward' or
384: * 'group'.
385: *
386: * @return array Array of user information on success or empty array
387: * if the user does not exist.
388: * @throws Vilma_Exception if address of that type doesn't exist.
389: */
390: public function getAddressInfo($address, $type = 'all')
391: {
392: $domain = Vilma::stripDomain($address);
393: $addresses = $this->getAddresses($domain, $type);
394: foreach ($addresses as $addrinfo) {
395: if ($addrinfo['id'] == $address ||
396: $addrinfo['address'] == $address) {
397: return $addrinfo;
398: }
399: }
400: throw new Vilma_Exception(sprintf(_("No such address %s of type %s found."), $address, $type));
401: }
402:
403: /**
404: * Returns available virtual emails.
405: *
406: * @param string $filter If passed a domain then return all virtual emails
407: * for the domain, otherwise if passed a user name
408: * return all virtual emails for that user.
409: *
410: * @return array The available virtual emails.
411: */
412: public function getVirtuals($filter)
413: {
414: }
415:
416: /**
417: * Returns information for a virtual id.
418: *
419: * @param integer $virtual_id The virtual id for which to return
420: * information.
421: *
422: * @return array The virtual email information.
423: */
424: public function getVirtual($virtual_id)
425: {
426: }
427:
428: /**
429: * Saves virtual email address to the backend.
430: *
431: * @param array $info The virtual email data.
432: * @param string $domain The name of the domain for this virtual email.
433: *
434: * @throws Vilma_Exception
435: */
436: public function saveVirtual($info, $domain)
437: {
438: }
439:
440: /**
441: * Deletes a virtual email.
442: *
443: * @param integer $virtual_id The id of the virtual email to delete.
444: */
445: public function deleteVirtual($virtual_id)
446: {
447: }
448:
449: /**
450: * Saves or creates alias records for a user.
451: *
452: * @param array $info The info used to store the information.
453: * Required fields are:
454: * - 'address': The destination address (used for LDAP
455: * ID lookup).
456: * - 'alias_address': The alias to create or the new
457: * data for the modified entry.
458: * - 'alias': The alias we are modifying, if we are
459: * modifying an existing one.
460: *
461: * @throws Vilma_Exception
462: */
463: public function saveAlias($info)
464: {
465: }
466:
467: /**
468: * Deletes alias records for a given user.
469: *
470: * @param array $info The info used to store the information.
471: * Required fields are:
472: * - 'address': The destination address (used for LDAP
473: * ID lookup).
474: * - 'alias': The alias we are deleting.
475: *
476: * @throws Vilma_Exception
477: */
478: public function deleteAlias($info)
479: {
480: }
481:
482: /**
483: * Saves or creates forward records for a given user.
484: *
485: * @param array $info The info used to store the information.
486: * Required fields are:
487: * - 'address': The destination address (used for LDAP
488: * ID lookup).
489: * - 'forward_address': The forward to create or the
490: * new data for the modified entry.
491: * - 'forward': The forward we are modifying, if we are
492: * modifying an existing one.
493: *
494: * @throws Vilma_Exception
495: */
496: public function saveForward($info)
497: {
498: }
499:
500: /**
501: * Deletes forward records for a given user.
502: *
503: * @param array $info The info used to store the information.
504: * Required fields are:
505: * - 'address': The destination address (used for LDAP
506: * ID lookup).
507: * - 'forward': The forward we are deleting.
508: *
509: * @throws Vilma_Exception
510: */
511: public function deleteForward($info)
512: {
513: }
514:
515: /**
516: * Attempts to return a concrete Vilma_Driver instance based on $driver.
517: *
518: * @param string $driver The type of concrete Vilma_Driver subclass to
519: * return.
520: * @param array $params A hash containing any additional configuration or
521: * connection parameters a subclass might need.
522: *
523: * @return Vilma_Driver The newly created concrete Vilma_Driver instance.
524: * @throws Vilma_Exception
525: */
526: static public function factory($driver = null, $params = null)
527: {
528: if (is_null($driver)) {
529: $driver = $GLOBALS['conf']['storage']['driver'];
530: }
531: $driver = Horde_String::ucfirst(basename($driver));
532:
533: if (is_null($params)) {
534: $params = Horde::getDriverConfig('storage', $driver);
535: }
536:
537: $class = 'Vilma_Driver_' . $driver;
538: if (class_exists($class)) {
539: return new $class($params);
540: }
541:
542: throw new Vilma_Exception(sprintf(_("No such backend \"%s\" found"), $driver));
543: }
544: }
545: