1<?php 2/** 3 * Copyright 2014-2016 Horde LLC (http://www.horde.org/) 4 * 5 * See the enclosed file COPYING for license information (LGPL). If you 6 * did not receive this file, see http://www.horde.org/licenses/lgpl21. 7 * 8 * @category Horde 9 * @copyright 2014-2016 Horde LLC 10 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 11 * @package Mail_Autoconfig 12 */ 13 14/** 15 * Perform RFC 6186 DNS SRV record lookups to determine mail configuration. 16 * 17 * @author Michael Slusarz <slusarz@horde.org> 18 * @category Horde 19 * @copyright 2014-2016 Horde LLC 20 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 21 * @package Mail_Autoconfig 22 */ 23class Horde_Mail_Autoconfig_Driver_Srv extends Horde_Mail_Autoconfig_Driver 24{ 25 /** 26 * DNS resolver. 27 * 28 * @var Net_DNS2_Resolver 29 */ 30 public $dns; 31 32 /** 33 * High priority: this is a standardized (RFC) method of determining 34 * configuration values. 35 */ 36 public $priority = 10; 37 38 /** 39 */ 40 public function msaSearch($domains, array $opts = array()) 41 { 42 $queries = array('_submission'); 43 return $this->_srvSearch($domains, $queries); 44 } 45 46 /** 47 */ 48 public function mailSearch($domains, array $opts = array()) 49 { 50 $queries = array(); 51 if (empty($opts['no_imap'])) { 52 $queries[] = '_imap'; 53 $queries[] = '_imaps'; 54 } 55 if (empty($opts['no_pop3'])) { 56 $queries[] = '_pop3'; 57 $queries[] = '_pop3s'; 58 } 59 60 return $this->_srvSearch($domains, $queries); 61 } 62 63 /** 64 * Perform the SRV search. 65 * 66 * @param array $domains List of domains to search. 67 * @param array $queries The SRV queries to run. 68 * 69 * @return mixed False if no servers found, or a list of server objects 70 * in order of decreasing priority. 71 */ 72 protected function _srvSearch($domains, $queries) 73 { 74 $obs = $out = array(); 75 76 if (is_null($this->dns)) { 77 $this->dns = new Net_DNS2_Resolver(); 78 } 79 80 foreach ($domains as $val) { 81 foreach ($queries as $val2) { 82 try { 83 $res = $this->dns->query($val2 . '._tcp.' . $val, 'SRV'); 84 foreach ($res->answer as $val3) { 85 if (strlen($val3->target)) { 86 $val3->query = $val2; 87 $obs[$val3->priority][] = $val3; 88 } 89 } 90 } catch (Net_DNS2_Exception $e) { 91 // Not found; ignore. 92 } 93 } 94 } 95 96 if (empty($obs)) { 97 return false; 98 } 99 100 /* Sort via priority ranking. Lower value is higher priority. */ 101 ksort($obs, SORT_NUMERIC); 102 103 foreach ($obs as $val) { 104 /* Do weight determination if a multiple servers have identical 105 * priorities. */ 106 if (count($val) > 1) { 107 /* Weight determination algorithm is defined in RFC 2782. 108 * First, move all entries with weight 0 to beginning of 109 * list. */ 110 $tmp = array(); 111 foreach ($val as $key2 => $val2) { 112 if (empty($val2->weight)) { 113 $tmp[] = $val2; 114 unset($val[$key2]); 115 } 116 } 117 $tmp = array_merge($tmp, $val); 118 119 $val = array(); 120 121 while (count($tmp) > 1) { 122 $i = 0; 123 124 /* Next, iterate over list and update the "running 125 * sum": the incremental value of each entry's weight. */ 126 foreach ($tmp as $val2) { 127 $i += $val2->weight; 128 $val2->running = $i; 129 } 130 131 /* Finally, select a random number in the range of 0->$i. 132 * The first entry in the list (sequentially) that has a 133 * running total >= to this random number is the next 134 * server in the priority list. */ 135 $rand = mt_rand(0, $i); 136 foreach ($tmp as $key2 => $val2) { 137 if ($val2->running >= $rand) { 138 $val[] = $val2; 139 /* Remove this server from the list. */ 140 unset($tmp[$key2]); 141 break; 142 } 143 } 144 145 /* Repeat until we have a single entry left in $tmp. */ 146 } 147 148 /* One entry left in $tmp, so add to $val. */ 149 $val[] = reset($tmp); 150 } 151 152 foreach ($val as $val2) { 153 switch ($val2->query) { 154 case '_imap': 155 $tmp = new Horde_Mail_Autoconfig_Server_Imap(); 156 break; 157 158 case '_imaps': 159 $tmp = new Horde_Mail_Autoconfig_Server_Imap(); 160 $tmp->tls = 'tls'; 161 break; 162 163 case '_pop3': 164 $tmp = new Horde_Mail_Autoconfig_Server_Pop3(); 165 break; 166 167 case '_pop3s': 168 $tmp = new Horde_Mail_Autoconfig_Server_Pop3(); 169 $tmp->tls = 'tls'; 170 break; 171 172 case '_submission': 173 $tmp = new Horde_Mail_Autoconfig_Server_Msa(); 174 break; 175 } 176 177 $tmp->host = strval($val2->target); 178 $tmp->port = intval($val2->port); 179 180 $out[] = $tmp; 181 } 182 } 183 184 return $out; 185 } 186 187} 188