PostfixAdmin - web based virtual user administration interface for Postfix mail servers https://postfixadmin.github.io/postfixadmin/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1568 lines
38 KiB

  1. <?php
  2. //
  3. // Postfix Admin
  4. // by Mischa Peters <mischa at high5 dot net>
  5. // Copyright (c) 2002 - 2005 High5!
  6. // Licensed under GPL for more info check GPL-LICENSE.TXT
  7. //
  8. // File: functions.inc.php
  9. //
  10. //error_reporting (E_NOTICE | E_ERROR | E_WARNING | E_PARSE);
  11. if (ereg ("functions.inc.php", $_SERVER['PHP_SELF']))
  12. {
  13. header ("Location: login.php");
  14. exit;
  15. }
  16. $version = "2.1.1";
  17. //
  18. // check_session
  19. // Action: Check if a session already exists, if not redirect to login.php
  20. // Call: check_session ()
  21. //
  22. function check_session ()
  23. {
  24. global $CONF;
  25. session_start ();
  26. session_fixid ();
  27. if (!session_is_registered ("sessid"))
  28. {
  29. header ("Location: " . $CONF['postfix_admin_url'] . "/login.php");
  30. exit;
  31. }
  32. $SESSID_USERNAME = $_SESSION['sessid']['username'];
  33. return $SESSID_USERNAME;
  34. }
  35. function check_user_session ()
  36. {
  37. global $CONF;
  38. session_start ();
  39. session_fixid ();
  40. if (!session_is_registered ("userid"))
  41. {
  42. header ("Location: " . $CONF['postfix_admin_url'] . "/login.php");
  43. exit;
  44. }
  45. $USERID_USERNAME = $_SESSION['userid']['username'];
  46. return $USERID_USERNAME;
  47. }
  48. //
  49. // session_fixid
  50. // Action: should avoid 'session fixation'
  51. // Call: session_fixid ()
  52. //
  53. function session_fixid ()
  54. {
  55. if (!isset($_SESSION['exist']))
  56. {
  57. if ( !session_regenerate_id() ) die("Couldn't regenerate your session id.");
  58. $_SESSION['exist'] = true;
  59. }
  60. }
  61. //
  62. // check_language
  63. // Action: checks what language the browser uses
  64. // Call: check_language
  65. //
  66. function check_language ()
  67. {
  68. global $CONF;
  69. $lang = $CONF['default_language'];
  70. $supported_languages = array ('bg', 'ca', 'cn', 'cs', 'da', 'de', 'en', 'es', 'et', 'eu', 'fi', 'fo', 'fr', 'hu', 'is', 'it', 'mk', 'nl', 'nn', 'pl', 'pt-br', 'ru', 'sl', 'sv', 'tr', 'tw');
  71. if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
  72. {
  73. $lang_array = preg_split ('/(\s*,\s*)/', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
  74. for($i = 0; $i < count($lang_array); $i++)
  75. {
  76. $lang_next = $lang_array[$i];
  77. $lang_next = strtolower(substr(trim($lang_next), 0, 2));
  78. if(in_array($lang_next, $supported_languages))
  79. {
  80. $lang = $lang_next;
  81. break;
  82. }
  83. }
  84. }
  85. return $lang;
  86. }
  87. //
  88. // check_string
  89. // Action: checks if a string is valid and returns TRUE if this is the case.
  90. // Call: check_string (string var)
  91. //
  92. function check_string ($var)
  93. {
  94. if (preg_match ('/^([A-Za-z0-9 ]+)+$/', $var))
  95. {
  96. return true;
  97. }
  98. else
  99. {
  100. return false;
  101. }
  102. }
  103. //
  104. // check_domain
  105. // Action: Checks if domain is valid and returns TRUE if this is the case.
  106. // Call: check_domain (string domain)
  107. //
  108. // TODO: make check_domain able to handle as example .local domains
  109. function check_domain ($domain)
  110. {
  111. if (preg_match ('/([-0-9A-Z]+\.)+' . '([0-9A-Z]){2,4}$/i', trim ($domain)))
  112. {
  113. return true;
  114. }
  115. else
  116. {
  117. return false;
  118. }
  119. }
  120. //
  121. // check_email
  122. // Action: Checks if email is valid and returns TRUE if this is the case.
  123. // Call: check_email (string email)
  124. //
  125. // TODO: make check_email able to handle already added domains
  126. function check_email ($email)
  127. {
  128. global $CONF;
  129. $ce_email=$email;
  130. //strip the vacation domain out if we are using it
  131. if ($CONF['vacation'] == 'YES')
  132. {
  133. $vacation_domain = $CONF['vacation_domain'];
  134. $ce_email = preg_replace("/@$vacation_domain/", '', $ce_email);
  135. }
  136. if (
  137. isset($CONF['emailcheck_resolve_domain'])
  138. && 'YES'==$CONF['emailcheck_resolve_domain']
  139. && 'WINDOWS'!=(strtoupper(substr(php_uname('s'), 0, 7)))
  140. ) {
  141. // Perform non-domain-part sanity checks
  142. if (!preg_match ('/^[-!#$%&\'*+\\.\/0-9=?A-Z^_{|}~]+' . '@' . '[^@]+$/i', trim ($ce_email)))
  143. {
  144. return false;
  145. }
  146. // Determine domain name
  147. $matches=array();
  148. if (!preg_match('|@(.+)$|',$ce_email,$matches))
  149. {
  150. return false;
  151. }
  152. $domain=$matches[1];
  153. // Look for an AAAA, A, or MX record for the domain
  154. // AAAA (IPv6) is only available in PHP v. >= 5
  155. if (version_compare(phpversion(), "5.0.0", ">="))
  156. {
  157. if (checkdnsrr($domain,'AAAA')) return true;
  158. }
  159. if (checkdnsrr($domain,'A')) return true;
  160. if (checkdnsrr($domain,'MX')) return true;
  161. return false;
  162. }
  163. if (preg_match ('/^[-!#$%&\'*+\\.\/0-9=?A-Z^_{|}~]+' . '@' . '([-0-9A-Z]+\.)+' . '([0-9A-Z]){2,6}$/i', trim ($ce_email)))
  164. {
  165. return true;
  166. }
  167. else
  168. {
  169. return false;
  170. }
  171. }
  172. //
  173. // escape_string
  174. // Action: Escape a string
  175. // Call: escape_string (string string)
  176. //
  177. (ini_get('magic_quotes_gpc') ? ini_set('magic_quotes_runtime', '0') : '1');
  178. (ini_get('magic_quotes_gpc') ? ini_set('magic_quotes_sybase', '0') : '1');
  179. function escape_string ($string)
  180. {
  181. global $CONF;
  182. if (get_magic_quotes_gpc ())
  183. {
  184. $string = stripslashes($string);
  185. }
  186. if (!is_numeric($string))
  187. {
  188. if ($CONF['database_type'] == "mysql")
  189. {
  190. $link = db_connect();
  191. $escaped_string = mysql_real_escape_string($string, $link);
  192. }
  193. if ($CONF['database_type'] == "mysqli")
  194. {
  195. $link = db_connect();
  196. $escaped_string = mysqli_real_escape_string($link, $string);
  197. }
  198. if ($CONF['database_type'] == "pgsql") $escaped_string = pg_escape_string($string);
  199. }
  200. else
  201. {
  202. $escaped_string = $string;
  203. }
  204. return $escaped_string;
  205. }
  206. //
  207. // get_domain_properties
  208. // Action: Get all the properties of a domain.
  209. // Call: get_domain_properties (string domain)
  210. //
  211. function get_domain_properties ($domain)
  212. {
  213. global $CONF;
  214. global $table_alias, $table_mailbox, $table_domain;
  215. $list = array ();
  216. $result = db_query ("SELECT COUNT(*) FROM $table_alias WHERE domain='$domain'");
  217. $row = db_row ($result['result']);
  218. $list['alias_count'] = $row[0];
  219. $result = db_query ("SELECT COUNT(*) FROM $table_mailbox WHERE domain='$domain'");
  220. $row = db_row ($result['result']);
  221. $list['mailbox_count'] = $row[0];
  222. $result = db_query ("SELECT SUM(quota) FROM $table_mailbox WHERE domain='$domain'");
  223. $row = db_row ($result['result']);
  224. $list['quota_sum'] = $row[0];
  225. $list['alias_count'] = $list['alias_count'] - $list['mailbox_count'];
  226. $query="SELECT * FROM $table_domain WHERE domain='$domain'";
  227. if ('pgsql'==$CONF['database_type'])
  228. {
  229. $query="
  230. SELECT
  231. *,
  232. EXTRACT(epoch FROM created) AS uts_created,
  233. EXTRACT(epoch FROM modified) AS uts_modified
  234. FROM $table_domain
  235. WHERE domain='$domain'
  236. ";
  237. }
  238. $result = db_query ($query);
  239. $row = db_array ($result['result']);
  240. $list['description'] = $row['description'];
  241. $list['aliases'] = $row['aliases'];
  242. $list['mailboxes'] = $row['mailboxes'];
  243. $list['maxquota'] = $row['maxquota'];
  244. $list['quota'] = $row['quota'];
  245. $list['transport'] = $row['transport'];
  246. $list['backupmx'] = $row['backupmx'];
  247. $list['created'] = $row['created'];
  248. $list['modified'] = $row['modified'];
  249. $list['active'] = $row['active'];
  250. if ($CONF['database_type'] == "pgsql")
  251. {
  252. $list['active']=('t'==$row['active']) ? 1 : 0;
  253. $list['backupmx']=('t'==$row['backupmx']) ? 1 : 0;
  254. $list['created']= gmstrftime('%c %Z',$row['uts_created']);
  255. $list['modified']= gmstrftime('%c %Z',$row['uts_modified']);
  256. }
  257. else
  258. {
  259. $list['active'] = $row['active'];
  260. $list['backupmx'] = $row['backupmx'];
  261. }
  262. return $list;
  263. }
  264. //
  265. // get_mailbox_properties
  266. // Action: Get all the properties of a mailbox.
  267. // Call: get_mailbox_properties (string mailbox)
  268. //
  269. function get_mailbox_properties ($username)
  270. {
  271. global $CONF;
  272. global $table_mailbox;
  273. $query="SELECT * FROM $table_mailbox WHERE username='$username'";
  274. if ('pgsql'==$CONF['database_type'])
  275. {
  276. $query="
  277. SELECT
  278. *,
  279. EXTRACT(epoch FROM created) AS uts_created,
  280. EXTRACT(epoch FROM modified) AS uts_modified
  281. FROM $table_mailbox
  282. WHERE username='$username'
  283. ";
  284. }
  285. $result = db_query ($query);
  286. $row = db_array ($result['result']);
  287. $list['name'] = $row['name'];
  288. $list['maildir'] = $row['maildir'];
  289. $list['quota'] = $row['quota'];
  290. $list['domain'] = $row['domain'];
  291. $list['created'] = $row['created'];
  292. $list['modified'] = $row['modified'];
  293. $list['active'] = $row['active'];
  294. if ($CONF['database_type'] == "pgsql")
  295. {
  296. $list['active']=('t'==$row['active']) ? 1 : 0;
  297. $list['created']= gmstrftime('%c %Z',$row['uts_created']);
  298. $list['modified']= gmstrftime('%c %Z',$row['uts_modified']);
  299. }
  300. else
  301. {
  302. $list['active'] = $row['active'];
  303. }
  304. return $list;
  305. }
  306. //
  307. // check_alias
  308. // Action: Checks if the domain is still able to create aliases.
  309. // Call: check_alias (string domain)
  310. //
  311. function check_alias ($domain)
  312. {
  313. $limit = get_domain_properties ($domain);
  314. if ($limit['aliases'] == 0)
  315. {
  316. return true;
  317. }
  318. if ($limit['aliases'] < 0)
  319. {
  320. return false;
  321. }
  322. if ($limit['alias_count'] >= $limit['aliases'])
  323. {
  324. return false;
  325. }
  326. else
  327. {
  328. return true;
  329. }
  330. }
  331. //
  332. // check_mailbox
  333. // Action: Checks if the domain is still able to create mailboxes.
  334. // Call: check_mailbox (string domain)
  335. //
  336. function check_mailbox ($domain)
  337. {
  338. $limit = get_domain_properties ($domain);
  339. if ($limit['mailboxes'] == 0)
  340. {
  341. return true;
  342. }
  343. if ($limit['mailboxes'] < 0)
  344. {
  345. return false;
  346. }
  347. if ($limit['mailbox_count'] >= $limit['mailboxes'])
  348. {
  349. return false;
  350. }
  351. else
  352. {
  353. return true;
  354. }
  355. }
  356. //
  357. // check_quota
  358. // Action: Checks if the user is creating a mailbox with the correct quota
  359. // Call: check_quota (string domain)
  360. //
  361. function check_quota ($quota, $domain)
  362. {
  363. $limit = get_domain_properties ($domain);
  364. if ($limit['maxquota'] == 0)
  365. {
  366. return true;
  367. }
  368. if (($limit['maxquota'] < 0) and ($quota < 0))
  369. {
  370. return true;
  371. }
  372. if (($limit['maxquota'] > 0) and ($quota == 0))
  373. {
  374. return false;
  375. }
  376. if ($quota > $limit['maxquota'])
  377. {
  378. return false;
  379. }
  380. else
  381. {
  382. return true;
  383. }
  384. }
  385. //
  386. // multiply_quota
  387. // Action: Recalculates the quota from bytes to MBs (multiply, *)
  388. // Call: multiply_quota (string $quota)
  389. //
  390. function multiply_quota ($quota)
  391. {
  392. global $CONF;
  393. if ($quota == -1) return $quota;
  394. $value = $quota * $CONF['quota_multiplier'];
  395. return $value;
  396. }
  397. //
  398. // divide_quota
  399. // Action: Recalculates the quota from MBs to bytes (divide, /)
  400. // Call: divide_quota (string $quota)
  401. //
  402. function divide_quota ($quota)
  403. {
  404. global $CONF;
  405. if ($quota == -1) return $quota;
  406. $value = $quota / $CONF['quota_multiplier'];
  407. return $value;
  408. }
  409. //
  410. // check_owner
  411. // Action: Checks if the admin is the owner of the domain.
  412. // Call: check_owner (string admin, string domain)
  413. //
  414. function check_owner ($username, $domain)
  415. {
  416. global $table_domain_admins;
  417. $result = db_query ("SELECT 1 FROM $table_domain_admins WHERE username='$username' AND (domain='$domain' OR domain='ALL') AND active='1'");
  418. if ($result['rows'] != 1)
  419. {
  420. return false;
  421. }
  422. else
  423. {
  424. return true;
  425. }
  426. }
  427. //
  428. // check_alias_owner
  429. // Action: Checks if the admin is the owner of the alias.
  430. // Call: check_alias_owner (string admin, string alias)
  431. //
  432. function check_alias_owner ($username, $alias)
  433. {
  434. global $CONF;
  435. if (check_admin ($username)) return true;
  436. $tmp = preg_split('/\@/', $alias);
  437. if (($CONF['special_alias_control'] == 'NO') && array_key_exists($tmp[0], $CONF['default_aliases']))
  438. {
  439. return false;
  440. }
  441. else
  442. {
  443. return true;
  444. }
  445. }
  446. //
  447. // list_domains_for_admin
  448. // Action: Lists all the domains for an admin.
  449. // Call: list_domains_for_admin (string admin)
  450. //
  451. function list_domains_for_admin ($username)
  452. {
  453. global $CONF;
  454. global $table_domain, $table_domain_admins;
  455. $list = array ();
  456. $query = "SELECT $table_domain.domain FROM $table_domain LEFT JOIN $table_domain_admins ON $table_domain.domain=$table_domain_admins.domain WHERE $table_domain_admins.username='$username' AND $table_domain.active='1' AND $table_domain.backupmx='0' ORDER BY $table_domain_admins.domain";
  457. if ('pgsql'==$CONF['database_type'])
  458. {
  459. $query = "SELECT $table_domain.domain FROM $table_domain LEFT JOIN $table_domain_admins ON $table_domain.domain=$table_domain_admins.domain WHERE $table_domain_admins.username='$username' AND $table_domain.active=true AND $table_domain.backupmx=false ORDER BY $table_domain_admins.domain";
  460. }
  461. $result = db_query ($query);
  462. if ($result['rows'] > 0)
  463. {
  464. $i = 0;
  465. while ($row = db_array ($result['result']))
  466. {
  467. if ('pgsql'==$CONF['database_type'])
  468. {
  469. $row['active'] = ('t'==$row['active'] ? 1 : 0);
  470. $row['backupmx'] = ('t'==$row['backupmx'] ? 1 : 0);
  471. }
  472. $list[$i] = $row['domain'];
  473. $i++;
  474. }
  475. }
  476. return $list;
  477. }
  478. //
  479. // list_domains
  480. // Action: List all available domains.
  481. // Call: list_domains ()
  482. //
  483. function list_domains ()
  484. {
  485. global $table_domain;
  486. $list = array();
  487. $result = db_query ("SELECT domain FROM $table_domain WHERE domain!='ALL' ORDER BY domain");
  488. if ($result['rows'] > 0)
  489. {
  490. $i = 0;
  491. while ($row = db_array ($result['result']))
  492. {
  493. $list[$i] = $row['domain'];
  494. $i++;
  495. }
  496. }
  497. return $list;
  498. }
  499. //
  500. // check_admin
  501. // Action: Checks if the admin is super-admin.
  502. // Call: check_admin (string admin)
  503. //
  504. function check_admin ($username)
  505. {
  506. global $table_domain_admins;
  507. $result = db_query ("SELECT 1 FROM $table_domain_admins WHERE username='$username' AND domain='ALL' AND active='1'");
  508. if ($result['rows'] != 1)
  509. {
  510. return false;
  511. }
  512. else
  513. {
  514. return true;
  515. }
  516. }
  517. //
  518. // admin_exist
  519. // Action: Checks if the admin already exists.
  520. // Call: admin_exist (string admin)
  521. //
  522. // was check_admin
  523. //
  524. function admin_exist ($username)
  525. {
  526. global $table_admin;
  527. $result = db_query ("SELECT 1 FROM $table_admin WHERE username='$username'");
  528. if ($result['rows'] != 1)
  529. {
  530. return false;
  531. }
  532. else
  533. {
  534. return true;
  535. }
  536. }
  537. //
  538. // domain_exist
  539. // Action: Checks if the domain already exists.
  540. // Call: domain_exist (string domain)
  541. //
  542. function domain_exist ($domain)
  543. {
  544. global $table_domain;
  545. $result = db_query ("SELECT 1 FROM $table_domain WHERE domain='$domain'");
  546. if ($result['rows'] != 1)
  547. {
  548. return false;
  549. }
  550. else
  551. {
  552. return true;
  553. }
  554. }
  555. //
  556. // list_admins
  557. // Action: Lists all the admins
  558. // Call: list_admins ()
  559. //
  560. // was admin_list_admins
  561. //
  562. function list_admins ()
  563. {
  564. global $table_admin;
  565. $list = "";
  566. $result = db_query ("SELECT username FROM $table_admin ORDER BY username");
  567. if ($result['rows'] > 0)
  568. {
  569. $i = 0;
  570. while ($row = db_array ($result['result']))
  571. {
  572. $list[$i] = $row['username'];
  573. $i++;
  574. }
  575. }
  576. return $list;
  577. }
  578. //
  579. // get_admin_properties
  580. // Action: Get all the admin properties.
  581. // Call: get_admin_properties (string admin)
  582. //
  583. function get_admin_properties ($username)
  584. {
  585. global $CONF;
  586. global $table_admin, $table_domain_admins;
  587. $list = array ();
  588. $result = db_query ("SELECT * FROM $table_domain_admins WHERE username='$username' AND domain='ALL'");
  589. if ($result['rows'] == 1)
  590. {
  591. $list['domain_count'] = 'ALL';
  592. }
  593. else
  594. {
  595. $result = db_query ("SELECT COUNT(*) FROM $table_domain_admins WHERE username='$username'");
  596. $row = db_row ($result['result']);
  597. $list['domain_count'] = $row[0];
  598. }
  599. $query = "SELECT * FROM $table_admin WHERE username='$username'";
  600. if ('pgsql'==$CONF['database_type']) {
  601. $query="
  602. SELECT
  603. *,
  604. EXTRACT(epoch FROM created) AS uts_created,
  605. EXTRACT (epoch FROM modified) AS uts_modified
  606. FROM $table_admin
  607. WHERE username='$username'
  608. ";
  609. }
  610. $result = db_query ($query);
  611. $row = db_array ($result['result']);
  612. $list['created'] = $row['created'];
  613. $list['modified'] = $row['modified'];
  614. $list['active'] = $row['active'];
  615. if ('pgsql'==$CONF['database_type']) {
  616. $list['active'] = ('t'==$row['active']) ? 1 : 0;
  617. $list['created']= gmstrftime('%c %Z',$row['uts_created']);
  618. $list['modified']= gmstrftime('%c %Z',$row['uts_modified']);
  619. }
  620. return $list;
  621. }
  622. //
  623. // encode_header
  624. // Action: Encode a string according to RFC 1522 for use in headers if it contains 8-bit characters.
  625. // Call: encode_header (string header, string charset)
  626. //
  627. function encode_header ($string, $default_charset)
  628. {
  629. if (strtolower ($default_charset) == 'iso-8859-1')
  630. {
  631. $string = str_replace ("\240",' ',$string);
  632. }
  633. $j = strlen ($string);
  634. $max_l = 75 - strlen ($default_charset) - 7;
  635. $aRet = array ();
  636. $ret = '';
  637. $iEncStart = $enc_init = false;
  638. $cur_l = $iOffset = 0;
  639. for ($i = 0; $i < $j; ++$i)
  640. {
  641. switch ($string{$i})
  642. {
  643. case '=':
  644. case '<':
  645. case '>':
  646. case ',':
  647. case '?':
  648. case '_':
  649. if ($iEncStart === false)
  650. {
  651. $iEncStart = $i;
  652. }
  653. $cur_l+=3;
  654. if ($cur_l > ($max_l-2))
  655. {
  656. $aRet[] = substr ($string,$iOffset,$iEncStart-$iOffset);
  657. $aRet[] = "=?$default_charset?Q?$ret?=";
  658. $iOffset = $i;
  659. $cur_l = 0;
  660. $ret = '';
  661. $iEncStart = false;
  662. }
  663. else
  664. {
  665. $ret .= sprintf ("=%02X",ord($string{$i}));
  666. }
  667. break;
  668. case '(':
  669. case ')':
  670. if ($iEncStart !== false)
  671. {
  672. $aRet[] = substr ($string,$iOffset,$iEncStart-$iOffset);
  673. $aRet[] = "=?$default_charset?Q?$ret?=";
  674. $iOffset = $i;
  675. $cur_l = 0;
  676. $ret = '';
  677. $iEncStart = false;
  678. }
  679. break;
  680. case ' ':
  681. if ($iEncStart !== false)
  682. {
  683. $cur_l++;
  684. if ($cur_l > $max_l)
  685. {
  686. $aRet[] = substr ($string,$iOffset,$iEncStart-$iOffset);
  687. $aRet[] = "=?$default_charset?Q?$ret?=";
  688. $iOffset = $i;
  689. $cur_l = 0;
  690. $ret = '';
  691. $iEncStart = false;
  692. }
  693. else
  694. {
  695. $ret .= '_';
  696. }
  697. }
  698. break;
  699. default:
  700. $k = ord ($string{$i});
  701. if ($k > 126)
  702. {
  703. if ($iEncStart === false)
  704. {
  705. // do not start encoding in the middle of a string, also take the rest of the word.
  706. $sLeadString = substr ($string,0,$i);
  707. $aLeadString = explode (' ',$sLeadString);
  708. $sToBeEncoded = array_pop ($aLeadString);
  709. $iEncStart = $i - strlen ($sToBeEncoded);
  710. $ret .= $sToBeEncoded;
  711. $cur_l += strlen ($sToBeEncoded);
  712. }
  713. $cur_l += 3;
  714. // first we add the encoded string that reached it's max size
  715. if ($cur_l > ($max_l-2))
  716. {
  717. $aRet[] = substr ($string,$iOffset,$iEncStart-$iOffset);
  718. $aRet[] = "=?$default_charset?Q?$ret?= ";
  719. $cur_l = 3;
  720. $ret = '';
  721. $iOffset = $i;
  722. $iEncStart = $i;
  723. }
  724. $enc_init = true;
  725. $ret .= sprintf ("=%02X", $k);
  726. }
  727. else
  728. {
  729. if ($iEncStart !== false)
  730. {
  731. $cur_l++;
  732. if ($cur_l > $max_l)
  733. {
  734. $aRet[] = substr ($string,$iOffset,$iEncStart-$iOffset);
  735. $aRet[] = "=?$default_charset?Q?$ret?=";
  736. $iEncStart = false;
  737. $iOffset = $i;
  738. $cur_l = 0;
  739. $ret = '';
  740. }
  741. else
  742. {
  743. $ret .= $string{$i};
  744. }
  745. }
  746. }
  747. break;
  748. }
  749. }
  750. if ($enc_init)
  751. {
  752. if ($iEncStart !== false)
  753. {
  754. $aRet[] = substr ($string,$iOffset,$iEncStart-$iOffset);
  755. $aRet[] = "=?$default_charset?Q?$ret?=";
  756. }
  757. else
  758. {
  759. $aRet[] = substr ($string,$iOffset);
  760. }
  761. $string = implode ('',$aRet);
  762. }
  763. return $string;
  764. }
  765. //
  766. // generate_password
  767. // Action: Generates a random password
  768. // Call: generate_password ()
  769. //
  770. function generate_password ()
  771. {
  772. $password = substr (md5 (mt_rand ()), 0, 8);
  773. return $password;
  774. }
  775. //
  776. // pacrypt
  777. // Action: Encrypts password based on config settings
  778. // Call: pacrypt (string cleartextpassword)
  779. //
  780. function pacrypt ($pw, $pw_db="")
  781. {
  782. global $CONF;
  783. $pw = stripslashes($pw);
  784. $password = "";
  785. $salt = "";
  786. if ($CONF['encrypt'] == 'md5crypt')
  787. {
  788. $split_salt = preg_split ('/\$/', $pw_db);
  789. if (isset ($split_salt[2])) $salt = $split_salt[2];
  790. $password = md5crypt ($pw, $salt);
  791. }
  792. if ($CONF['encrypt'] == 'system')
  793. {
  794. if (ereg ("\$1\$", $pw_db))
  795. {
  796. $split_salt = preg_split ('/\$/', $pw_db);
  797. $salt = $split_salt[2];
  798. }
  799. else
  800. {
  801. if (strlen($pw_db) == 0)
  802. {
  803. $salt = substr (md5 (mt_rand ()), 0, 2);
  804. }
  805. else
  806. {
  807. $salt = substr ($pw_db, 0, 2);
  808. }
  809. }
  810. $password = crypt ($pw, $salt);
  811. }
  812. if ($CONF['encrypt'] == 'cleartext')
  813. {
  814. $password = $pw;
  815. }
  816. $password = escape_string ($password);
  817. return $password;
  818. }
  819. //
  820. // md5crypt
  821. // Action: Creates MD5 encrypted password
  822. // Call: md5crypt (string cleartextpassword)
  823. //
  824. $MAGIC = "$1$";
  825. $ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  826. function md5crypt ($pw, $salt="", $magic="")
  827. {
  828. global $MAGIC;
  829. if ($magic == "") $magic = $MAGIC;
  830. if ($salt == "") $salt = create_salt ();
  831. $slist = explode ("$", $salt);
  832. if ($slist[0] == "1") $salt = $slist[1];
  833. $salt = substr ($salt, 0, 8);
  834. $ctx = $pw . $magic . $salt;
  835. $final = hex2bin (md5 ($pw . $salt . $pw));
  836. for ($i=strlen ($pw); $i>0; $i-=16)
  837. {
  838. if ($i > 16)
  839. {
  840. $ctx .= substr ($final,0,16);
  841. }
  842. else
  843. {
  844. $ctx .= substr ($final,0,$i);
  845. }
  846. }
  847. $i = strlen ($pw);
  848. while ($i > 0)
  849. {
  850. if ($i & 1) $ctx .= chr (0);
  851. else $ctx .= $pw[0];
  852. $i = $i >> 1;
  853. }
  854. $final = hex2bin (md5 ($ctx));
  855. for ($i=0;$i<1000;$i++)
  856. {
  857. $ctx1 = "";
  858. if ($i & 1)
  859. {
  860. $ctx1 .= $pw;
  861. }
  862. else
  863. {
  864. $ctx1 .= substr ($final,0,16);
  865. }
  866. if ($i % 3) $ctx1 .= $salt;
  867. if ($i % 7) $ctx1 .= $pw;
  868. if ($i & 1)
  869. {
  870. $ctx1 .= substr ($final,0,16);
  871. }
  872. else
  873. {
  874. $ctx1 .= $pw;
  875. }
  876. $final = hex2bin (md5 ($ctx1));
  877. }
  878. $passwd = "";
  879. $passwd .= to64 (((ord ($final[0]) << 16) | (ord ($final[6]) << 8) | (ord ($final[12]))), 4);
  880. $passwd .= to64 (((ord ($final[1]) << 16) | (ord ($final[7]) << 8) | (ord ($final[13]))), 4);
  881. $passwd .= to64 (((ord ($final[2]) << 16) | (ord ($final[8]) << 8) | (ord ($final[14]))), 4);
  882. $passwd .= to64 (((ord ($final[3]) << 16) | (ord ($final[9]) << 8) | (ord ($final[15]))), 4);
  883. $passwd .= to64 (((ord ($final[4]) << 16) | (ord ($final[10]) << 8) | (ord ($final[5]))), 4);
  884. $passwd .= to64 (ord ($final[11]), 2);
  885. return "$magic$salt\$$passwd";
  886. }
  887. function create_salt ()
  888. {
  889. srand ((double) microtime ()*1000000);
  890. $salt = substr (md5 (rand (0,9999999)), 0, 8);
  891. return $salt;
  892. }
  893. function hex2bin ($str)
  894. {
  895. $len = strlen ($str);
  896. $nstr = "";
  897. for ($i=0;$i<$len;$i+=2)
  898. {
  899. $num = sscanf (substr ($str,$i,2), "%x");
  900. $nstr.=chr ($num[0]);
  901. }
  902. return $nstr;
  903. }
  904. function to64 ($v, $n)
  905. {
  906. global $ITOA64;
  907. $ret = "";
  908. while (($n - 1) >= 0)
  909. {
  910. $n--;
  911. $ret .= $ITOA64[$v & 0x3f];
  912. $v = $v >> 6;
  913. }
  914. return $ret;
  915. }
  916. //
  917. // smtp_mail
  918. // Action: Sends email to new account.
  919. // Call: smtp_mail (string To, string From, string Data)
  920. //
  921. function smtp_mail ($to, $from, $data)
  922. {
  923. global $CONF;
  924. $smtpd_server = $CONF['smtp_server'];
  925. $smtpd_port = $CONF['smtp_port'];
  926. $smtp_server = $_SERVER["SERVER_NAME"];
  927. $errno = "0";
  928. $errstr = "0";
  929. $timeout = "30";
  930. $fh = @fsockopen ($smtpd_server, $smtpd_port, $errno, $errstr, $timeout);
  931. if (!$fh)
  932. {
  933. return false;
  934. }
  935. else
  936. {
  937. fputs ($fh, "EHLO $smtp_server\r\n");
  938. $res = smtp_get_response($fh);
  939. fputs ($fh, "MAIL FROM:<$from>\r\n");
  940. $res = smtp_get_response($fh);
  941. fputs ($fh, "RCPT TO:<$to>\r\n");
  942. $res = smtp_get_response($fh);
  943. fputs ($fh, "DATA\r\n");
  944. $res = smtp_get_response($fh);
  945. fputs ($fh, "$data\r\n.\r\n");
  946. $res = smtp_get_response($fh);
  947. fputs ($fh, "QUIT\r\n");
  948. $res = smtp_get_response($fh);
  949. fclose ($fh);
  950. }
  951. return true;
  952. }
  953. //
  954. // smtp_get_response
  955. // Action: Get response from mail server
  956. // Call: smtp_get_response (string FileHandle)
  957. //
  958. function smtp_get_response ($fh)
  959. {
  960. $res ='';
  961. do
  962. {
  963. $line = fgets($fh, 256);
  964. $res .= $line;
  965. }
  966. while (preg_match("/^\d\d\d\-/", $line));
  967. return $res;
  968. }
  969. $DEBUG_TEXT = "\n
  970. <p />\n
  971. Please check the documentation and website for more information.\n
  972. <p />\n
  973. <a href=\"http://high5.net/postfixadmin/\">Postfix Admin</a><br />\n
  974. <a href=\"http://forums.high5.net/index.php?showforum=7\">Knowledge Base</a>\n
  975. ";
  976. //
  977. // db_connect
  978. // Action: Makes a connection to the database if it doesn't exist
  979. // Call: db_connect ()
  980. //
  981. function db_connect ()
  982. {
  983. global $CONF;
  984. global $DEBUG_TEXT;
  985. if ($CONF['database_type'] == "mysql")
  986. {
  987. if (function_exists ("mysql_connect"))
  988. {
  989. $link = @mysql_connect ($CONF['database_host'], $CONF['database_user'], $CONF['database_password']) or die ("<p />DEBUG INFORMATION:<br />Connect: " . mysql_error () . "$DEBUG_TEXT");
  990. $succes = @mysql_select_db ($CONF['database_name'], $link) or die ("<p />DEBUG INFORMATION:<br />MySQL Select Database: " . mysql_error () . "$DEBUG_TEXT");
  991. }
  992. else
  993. {
  994. print "<p />DEBUG INFORMATION:<br />MySQL 3.x / 4.0 functions not available!<br />database_type = 'mysql' in config.inc.php, are you using a different database? $DEBUG_TEXT";
  995. die;
  996. }
  997. }
  998. if ($CONF['database_type'] == "mysqli")
  999. {
  1000. if (function_exists ("mysqli_connect"))
  1001. {
  1002. $link = @mysqli_connect ($CONF['database_host'], $CONF['database_user'], $CONF['database_password']) or die ("<p />DEBUG INFORMATION:<br />Connect: " . mysqli_connect_error () . "$DEBUG_TEXT");
  1003. $succes = @mysqli_select_db ($link, $CONF['database_name']) or die ("<p />DEBUG INFORMATION:<br />MySQLi Select Database: " . mysqli_error () . "$DEBUG_TEXT");
  1004. }
  1005. else
  1006. {
  1007. print "<p />DEBUG INFORMATION:<br />MySQL 4.1 functions not available!<br />database_type = 'mysqli' in config.inc.php, are you using a different database? $DEBUG_TEXT";
  1008. die;
  1009. }
  1010. }
  1011. if ($CONF['database_type'] == "pgsql")
  1012. {
  1013. if (function_exists ("pg_pconnect"))
  1014. {
  1015. $connect_string = "host=" . $CONF['database_host'] . " dbname=" . $CONF['database_name'] . " user=" . $CONF['database_user'] . " password=" . $CONF['database_password'];
  1016. $link = @pg_pconnect ($connect_string) or die ("<p />DEBUG INFORMATION:<br />Connect: " . pg_last_error () . "$DEBUG_TEXT");
  1017. }
  1018. else
  1019. {
  1020. print "<p />DEBUG INFORMATION:<br />PostgreSQL functions not available!<br />database_type = 'pgsql' in config.inc.php, are you using a different database? $DEBUG_TEXT";
  1021. die;
  1022. }
  1023. }
  1024. if ($link)
  1025. {
  1026. return $link;
  1027. }
  1028. else
  1029. {
  1030. print "DEBUG INFORMATION:<br />\n";
  1031. print "Connect: Unable to connect to database<br />\n";
  1032. print "<br />\n";
  1033. print "Make sure that you have set the correct database type in the config.inc.php file<br />\n";
  1034. print $DEBUG_TEXT;
  1035. die;
  1036. }
  1037. }
  1038. //
  1039. // db_query
  1040. // Action: Sends a query to the database and returns query result and number of rows
  1041. // Call: db_query (string query)
  1042. //
  1043. function db_query ($query)
  1044. {
  1045. global $CONF;
  1046. global $DEBUG_TEXT;
  1047. $result = "";
  1048. $number_rows = "";
  1049. static $link;
  1050. if (!is_resource($link)) $link = db_connect ();
  1051. if ($CONF['database_type'] == "mysql") $result = @mysql_query ($query, $link) or die ("<p />DEBUG INFORMATION:<br />Invalid query: " . mysql_error() . "$DEBUG_TEXT");
  1052. if ($CONF['database_type'] == "mysqli") $result = @mysqli_query ($link, $query) or die ("<p />DEBUG INFORMATION:<br />Invalid query: " . mysqli_error() . "$DEBUG_TEXT");
  1053. if ($CONF['database_type'] == "pgsql")
  1054. {
  1055. $result = @pg_query ($link, $query) or die ("<p />DEBUG INFORMATION:<br />Invalid query: " . pg_last_error() . "$DEBUG_TEXT");
  1056. }
  1057. if (eregi ("^SELECT", $query))
  1058. {
  1059. // if $query was a SELECT statement check the number of rows with [database_type]_num_rows ().
  1060. if ($CONF['database_type'] == "mysql") $number_rows = mysql_num_rows ($result);
  1061. if ($CONF['database_type'] == "mysqli") $number_rows = mysqli_num_rows ($result);
  1062. if ($CONF['database_type'] == "pgsql") $number_rows = pg_num_rows ($result);
  1063. }
  1064. else
  1065. {
  1066. // if $query was something else, UPDATE, DELETE or INSERT check the number of rows with
  1067. // [database_type]_affected_rows ().
  1068. if ($CONF['database_type'] == "mysql") $number_rows = mysql_affected_rows ($link);
  1069. if ($CONF['database_type'] == "mysqli") $number_rows = mysqli_affected_rows ($link);
  1070. if ($CONF['database_type'] == "pgsql") $number_rows = pg_affected_rows ($result);
  1071. }
  1072. $return = array (
  1073. "result" => $result,
  1074. "rows" => $number_rows
  1075. );
  1076. return $return;
  1077. }
  1078. // db_row
  1079. // Action: Returns a row from a table
  1080. // Call: db_row (int result)
  1081. //
  1082. function db_row ($result)
  1083. {
  1084. global $CONF;
  1085. $row = "";
  1086. if ($CONF['database_type'] == "mysql") $row = mysql_fetch_row ($result);
  1087. if ($CONF['database_type'] == "mysqli") $row = mysqli_fetch_row ($result);
  1088. if ($CONF['database_type'] == "pgsql") $row = pg_fetch_row ($result);
  1089. return $row;
  1090. }
  1091. // db_array
  1092. // Action: Returns a row from a table
  1093. // Call: db_array (int result)
  1094. //
  1095. function db_array ($result)
  1096. {
  1097. global $CONF;
  1098. $row = "";
  1099. if ($CONF['database_type'] == "mysql") $row = mysql_fetch_array ($result);
  1100. if ($CONF['database_type'] == "mysqli") $row = mysqli_fetch_array ($result);
  1101. if ($CONF['database_type'] == "pgsql") $row = pg_fetch_array ($result);
  1102. return $row;
  1103. }
  1104. // db_assoc
  1105. // Action: Returns a row from a table
  1106. // Call: db_assoc(int result)
  1107. //
  1108. function db_assoc ($result)
  1109. {
  1110. global $CONF;
  1111. $row = "";
  1112. if ($CONF['database_type'] == "mysql") $row = mysql_fetch_assoc ($result);
  1113. if ($CONF['database_type'] == "mysqli") $row = mysqli_fetch_assoc ($result);
  1114. if ($CONF['database_type'] == "pgsql") $row = pg_fetch_assoc ($result);
  1115. return $row;
  1116. }
  1117. //
  1118. // db_delete
  1119. // Action: Deletes a row from a specified table
  1120. // Call: db_delete (string table, string where, string delete)
  1121. //
  1122. function db_delete ($table,$where,$delete)
  1123. {
  1124. $result = db_query ("DELETE FROM $table WHERE $where='$delete'");
  1125. if ($result['rows'] >= 1)
  1126. {
  1127. return $result['rows'];
  1128. }
  1129. else
  1130. {
  1131. return true;
  1132. }
  1133. }
  1134. //
  1135. // db_log
  1136. // Action: Logs actions from admin
  1137. // Call: db_log (string username, string domain, string action, string data)
  1138. //
  1139. function db_log ($username,$domain,$action,$data)
  1140. {
  1141. global $CONF;
  1142. global $table_log;
  1143. $REMOTE_ADDR = $_SERVER['REMOTE_ADDR'];
  1144. if ($CONF['logging'] == 'YES')
  1145. {
  1146. $result = db_query ("INSERT INTO $table_log (timestamp,username,domain,action,data) VALUES (NOW(),'$username ($REMOTE_ADDR)','$domain','$action','$data')");
  1147. if ($result['rows'] != 1)
  1148. {
  1149. return false;
  1150. }
  1151. else
  1152. {
  1153. return true;
  1154. }
  1155. }
  1156. }
  1157. //
  1158. // table_by_key
  1159. // Action: Return table name for given key
  1160. // Call: table_by_key (string table_key)
  1161. //
  1162. function table_by_key ($table_key)
  1163. {
  1164. global $CONF;
  1165. $table = $CONF['database_prefix'].$CONF['database_tables'][$table_key];
  1166. return $table;
  1167. }
  1168. //
  1169. // table_by_pos
  1170. // Action: Return table name for given position
  1171. // Call: table_by_pos (int pos)
  1172. //
  1173. function table_by_pos ($pos)
  1174. {
  1175. global $CONF;
  1176. $x=0;
  1177. foreach($CONF['database_tables'] as $i=>$v)
  1178. {
  1179. if($pos==$x++) return table_by_key ($i);
  1180. }
  1181. return false;
  1182. }
  1183. /*
  1184. Called after a mailbox has been created in the DBMS.
  1185. Returns: boolean.
  1186. */
  1187. function mailbox_postcreation($username,$domain,$maildir)
  1188. {
  1189. if (empty($username) || empty($domain) || empty($maildir))
  1190. {
  1191. trigger_error('In '.__FUNCTION__.': empty username, domain and/or maildir parameter',E_USER_ERROR);
  1192. return FALSE;
  1193. }
  1194. global $CONF;
  1195. $confpar='mailbox_postcreation_script';
  1196. if (!isset($CONF[$confpar]) || empty($CONF[$confpar])) return TRUE;
  1197. $cmdarg1=escapeshellarg($username);
  1198. $cmdarg2=escapeshellarg($domain);
  1199. $cmdarg3=escapeshellarg($maildir);
  1200. $command=$CONF[$confpar]." $cmdarg1 $cmdarg2 $cmdarg3";
  1201. $retval=0;
  1202. $output=array();
  1203. $firstline='';
  1204. $firstline=exec($command,$output,$retval);
  1205. if (0!=$retval)
  1206. {
  1207. error_log("Running $command yielded return value=$retval, first line of output=$firstline");
  1208. print '<p>WARNING: Problems running mailbox postcreation script!</p>';
  1209. return FALSE;
  1210. }
  1211. return TRUE;
  1212. }
  1213. /*
  1214. Called after a mailbox has been deleted in the DBMS.
  1215. Returns: boolean.
  1216. */
  1217. function mailbox_postdeletion($username,$domain)
  1218. {
  1219. global $CONF;
  1220. $confpar='mailbox_postdeletion_script';
  1221. if (!isset($CONF[$confpar]) || empty($CONF[$confpar]))
  1222. {
  1223. return true;
  1224. }
  1225. if (empty($username) || empty($domain))
  1226. {
  1227. print '<p>Warning: empty username and/or domain parameter.</p>';
  1228. return false;
  1229. }
  1230. $cmdarg1=escapeshellarg($username);
  1231. $cmdarg2=escapeshellarg($domain);
  1232. $command=$CONF[$confpar]." $cmdarg1 $cmdarg2";
  1233. $retval=0;
  1234. $output=array();
  1235. $firstline='';
  1236. $firstline=exec($command,$output,$retval);
  1237. if (0!=$retval)
  1238. {
  1239. error_log("Running $command yielded return value=$retval, first line of output=$firstline");
  1240. print '<p>WARNING: Problems running mailbox postdeletion script!</p>';
  1241. return FALSE;
  1242. }
  1243. return TRUE;
  1244. }
  1245. /*
  1246. Called after a domain has been deleted in the DBMS.
  1247. Returns: boolean.
  1248. */
  1249. function domain_postdeletion($domain)
  1250. {
  1251. global $CONF;
  1252. $confpar='domain_postdeletion_script';
  1253. if (!isset($CONF[$confpar]) || empty($CONF[$confpar]))
  1254. {
  1255. return true;
  1256. }
  1257. if (empty($domain))
  1258. {
  1259. print '<p>Warning: empty domain parameter.</p>';
  1260. return false;
  1261. }
  1262. $cmdarg1=escapeshellarg($domain);
  1263. $command=$CONF[$confpar]." $cmdarg1";
  1264. $retval=0;
  1265. $output=array();
  1266. $firstline='';
  1267. $firstline=exec($command,$output,$retval);
  1268. if (0!=$retval)
  1269. {
  1270. error_log("Running $command yielded return value=$retval, first line of output=$firstline");
  1271. print '<p>WARNING: Problems running domain postdeletion script!</p>';
  1272. return FALSE;
  1273. }
  1274. return TRUE;
  1275. }
  1276. /*
  1277. Called by mailbox_postcreation() after a mailbox has been
  1278. created. Immediately returns, unless configuration indicates
  1279. that one or more sub-folders should be created.
  1280. Triggers E_USER_ERROR if configuration error is detected.
  1281. If IMAP login fails, the problem is logged to the system log
  1282. (such as /var/log/httpd/error_log), and the function returns
  1283. FALSE.
  1284. Returns FALSE on all other errors, or TRUE if everything
  1285. succeeds.
  1286. Doesn't clean up, if only some of the folders could be
  1287. created.
  1288. */
  1289. function create_mailbox_subfolders($login,$cleartext_password)
  1290. {
  1291. global $CONF;
  1292. if (empty($login))
  1293. {
  1294. trigger_error('In '.__FUNCTION__.': empty $login',E_USER_ERROR);
  1295. return FALSE;
  1296. }
  1297. if (!isset($CONF['create_mailbox_subdirs']) || empty($CONF['create_mailbox_subdirs'])) return TRUE;
  1298. if (!is_array($CONF['create_mailbox_subdirs']))
  1299. {
  1300. trigger_error('create_mailbox_subdirs must be an array',E_USER_ERROR);
  1301. return FALSE;
  1302. }
  1303. if (!isset($CONF['create_mailbox_subdirs_host']) || empty($CONF['create_mailbox_subdirs_host']))
  1304. {
  1305. trigger_error('An IMAP/POP server host ($CONF["create_mailbox_subdirs_host"]) must be configured, if sub-folders are to be created',E_USER_ERROR);
  1306. return FALSE;
  1307. }
  1308. $s_host=$CONF['create_mailbox_subdirs_host'];
  1309. $s_options='';
  1310. $s_port='';
  1311. if (
  1312. isset($CONF['create_mailbox_subdirs_hostoptions'])
  1313. && !empty($CONF['create_mailbox_subdirs_hostoptions'])
  1314. ) {
  1315. if (!is_array($CONF['create_mailbox_subdirs_hostoptions']))
  1316. {
  1317. trigger_error('The $CONF["create_mailbox_subdirs_hostoptions"] parameter must be an array',E_USER_ERROR);
  1318. return FALSE;
  1319. }
  1320. foreach ($CONF['create_mailbox_subdirs_hostoptions'] as $o)
  1321. {
  1322. $s_options.='/'.$o;
  1323. }
  1324. }
  1325. if (isset($CONF['create_mailbox_subdirs_hostport']) && !empty($CONF['create_mailbox_subdirs_hostport']))
  1326. {
  1327. $s_port=$CONF['create_mailbox_subdirs_hostport'];
  1328. if (intval($s_port)!=$s_port)
  1329. {
  1330. trigger_error('The $CONF["create_mailbox_subdirs_hostport"] parameter must be an integer',E_USER_ERROR);
  1331. return FALSE;
  1332. }
  1333. $s_port=':'.$s_port;
  1334. }
  1335. $s='{'.$s_host.$s_port.$s_options.'}';
  1336. $i=@imap_open($s,$login,$cleartext_password);
  1337. if (FALSE==$i)
  1338. {
  1339. error_log('Could not log into IMAP/POP server: '.imap_last_error());
  1340. return FALSE;
  1341. }
  1342. foreach($CONF['create_mailbox_subdirs'] as $f)
  1343. {
  1344. $f='{'.$s_host.'}INBOX.'.$f;
  1345. $res=imap_createmailbox($i,$f);
  1346. if (!$res) {
  1347. @imap_close($i);
  1348. return FALSE;
  1349. }
  1350. @imap_subscribe($i,$f);
  1351. }
  1352. @imap_close($i);
  1353. return TRUE;
  1354. }
  1355. $table_admin = table_by_key ('admin');
  1356. $table_alias = table_by_key ('alias');
  1357. $table_domain = table_by_key ('domain');
  1358. $table_domain_admins = table_by_key ('domain_admins');
  1359. $table_log = table_by_key ('log');
  1360. $table_mailbox = table_by_key ('mailbox');
  1361. $table_vacation = table_by_key ('vacation');
  1362. /* vim: set expandtab softtabstop=3 tabstop=3 shiftwidth=3: */
  1363. ?>