PageRenderTime 62ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/xengine.php

https://github.com/xopherdeep/x4deep0.1
PHP | 1452 lines | 1025 code | 197 blank | 230 comment | 122 complexity | 062a8eca9e4b54df39a52c4060d24268 MD5 | raw file
  1. <?php
  2. /**
  3. * Xengine Version 2.x
  4. * @author XopherDeeP <heylisten@xtiv.net>
  5. * @version v2.0.0-beta1.1
  6. **/
  7. /*
  8. Xengine Version 2 address the fact that Xtras and HTML files are stored in their own containing Directory.
  9. v1 split the Xtras into x/xtra and html files into html/frontdoor & html/backdoor
  10. v2 now splits the xtrans into x/_suite_/_xTra_/
  11. where xTra contains all php and html/tpl files, along with css & js files.
  12. */
  13. # Xengine is Small and PowerFull; Hook into it using Xtras. The Xengine Idea is: Drop and Build!
  14. # It setups an easy to use connection between Clean URLs and PHP Classes
  15. # The eXtend PHP Classes with Xengine allowing Easy Access to the the DB using $q = $this->q();
  16. # The DB doesn't connect until the function is called, once connected, it can be accessed via $this->Q
  17. # The CleanURL Structure is as Follows: domain.com/class/method/param1/param2/param3/etc
  18. # where class is the class name found in the Xtra's. ex: xClass.php, the method should be found there.
  19. # If there is an HTML page to render with the method, the files go in the html dir,
  20. class Xengine
  21. {
  22. var $_CFG;
  23. var $_SET = array(
  24. 'action' => '',
  25. 'method' => '',
  26. 'HTML' => array(
  27. 'HEAD' => array(
  28. 'TITLE' => null,
  29. 'CSS' => '',
  30. 'JS' => '',
  31. 'STYLE' => ''
  32. ),
  33. 'BODY' => array(
  34. 'HTML' => '',
  35. 'CSS' => '',
  36. 'JS' => ''
  37. )
  38. )
  39. );
  40. var $_DBF;
  41. var $Xtra;
  42. var $method;
  43. var $_debugReport = array();
  44. function __construct($cfg = false,$parent = false)
  45. {
  46. define('DOC_ROOT' ,$_SERVER['DOCUMENT_ROOT']);
  47. // Glue some configss together
  48. $cfg['dir']['cfg'] = DOC_ROOT.'/'.$cfg['dir']['backdoor'].'/'. $cfg['dir']['cfg'];
  49. $cfg['dir']['libs'] = $cfg['dir']['backdoor'].'/'. $cfg['dir']['libs'];
  50. $cfg['dir']['Xtra'] = $cfg['dir']['backdoor'] + '/' + $cfg['dir']['Xtra'];
  51. define('LIBS_DIR' ,DOC_ROOT.'/'.$cfg['dir']['libs']); # Location of the Library Files
  52. define('XPHP_DIR' ,DOC_ROOT.'/'.$cfg['dir']['Xtra']); # Location of the Xtras Files
  53. $cfg['dir']['libs'] = LIBS_DIR;
  54. // SETUP Xengine based on x.cfg
  55. define('BIN' ,DOC_ROOT.'/'.$cfg['dir']['Xtra']); # Location of the Bin Files
  56. set_include_path(LIBS_DIR);
  57. $this->_comment("Xengine Started!");
  58. ini_set('display_errors', $cfg['debug']['on']);
  59. if(!$cfg)
  60. die("Configuration Needed");
  61. // We need to be able to write to this directory...
  62. if(!is_writeable($cfg['dir']['cfg'])){
  63. die($cfg['dir']['cfg']." Not Writable!");
  64. }
  65. $this->_CFG = $cfg;
  66. $this->_LANG = $cfg['lang'] ;
  67. $this->_BOTS = $cfg['bots_list'];
  68. if(!defined('DB_CFG'))
  69. define("DB_CFG", $cfg['dir']['cfg']."/cfg.db.$_SERVER[HTTP_HOST].inc");
  70. }
  71. /*
  72. The Client has 'Knocked' on the website's 'Door'
  73. Identify the whole: user, location, content, module, timestamp
  74. Decide what to do with them.
  75. */
  76. public function knock()
  77. {
  78. try {
  79. register_shutdown_function(array( &$this, "shutDownFunction" ));
  80. $this->keyhole(); // Identify the Whole.
  81. $this->openDoor(); // Open the Door a.k.a Execute Mods.
  82. $this->browse(); // Display the Content, HTML, JSON, XML, JPEG.
  83. exit; // EXIT
  84. }catch(Exception $e){
  85. $this->reportSystemError(array(
  86. 'summary' => 'X ERROR :: '.$e->getMessage(),
  87. 'description' => $e->getMessage(),
  88. 'attr' => array(
  89. 'type' => 'defect',
  90. 'component' => 'x'.ucfirst($this->_SET['action']),
  91. 'priority' => 'trivial',
  92. 'reporter' => $_SESSION['user']['username'].'@'.$_SERVER['HTTP_HOST'],
  93. 'keywords' => $this->_SET['action'].'::'. $this->_SET['method'],
  94. 'milestone' => 'Bee Hive'
  95. )
  96. ));
  97. $this->set(array(
  98. 'action' => 'access',
  99. 'method' => 'error',
  100. 'params' => array(
  101. 'error' => $e->getMessage()
  102. ),
  103. 'request' => array(
  104. 'action' => $this->_SET['action'],
  105. 'method' => $this->_SET['method']
  106. )
  107. ));
  108. //$this->dump($this->_SET);
  109. $this->browse();
  110. }
  111. }
  112. private function keyhole()
  113. {
  114. session_start();
  115. // Who Am I?
  116. $this->whoAmI();
  117. // Where Am I?
  118. $this->whereAmI();
  119. // What Content Type am I
  120. $this->whatAmI();
  121. // How Am I Built
  122. $this->howAmI();
  123. // When Am I Happening
  124. // $this->whenAmI();
  125. // Why Am I Existing
  126. // $this->whyAmI();
  127. }
  128. // The Key holds an array of Bool
  129. private function whoAmI($who=false)
  130. {
  131. // Define Their KEY - Refrenced for Access.
  132. $this->Key = array(
  133. 'is' => array(
  134. 'admin' => isset($_SESSION['user']) ? ($_SESSION['user']['power_lvl'] > 7) : false,
  135. 'user' => (isset($_SESSION['user']) && !empty($_SESSION['user'])) ,
  136. 'guest' => ( isset($_SESSION['user'] ) != true),
  137. 'bot' => ( preg_match('/'.implode("|", $this->_BOTS).'/', $_SERVER['HTTP_USER_AGENT'], $matches) ) ?
  138. array_search($matches[0], $bots_list) : false
  139. )
  140. );
  141. }
  142. private function whereAmI()
  143. {
  144. // This Function Sets all the variables on where the Client IS based on the URL they've Hit.
  145. $this->uri = substr( $_SERVER['REQUEST_URI'], 1 ); // Begins with a / - slice it off.
  146. $this->url = parse_url( $this->uri );
  147. $this->_SET['params'] = (isset($this->url['path'])) ? explode('/', $this->url['path']) : array('/');
  148. if(!isset($this->_SET['params'][1])){
  149. $this->_SET['params'] = array('','');
  150. }
  151. // BOOL
  152. $this->atSideDoor = ($this->_SET['params'][0] === $this->_CFG['dir']['sidedoor']
  153. || $this->_SET['params'][1] === $this->_CFG['dir']['sidedoor']);
  154. // Back Door - Admin Panel of Pages.
  155. $this->atBackDoor = ($this->_SET['params'][0] === $this->_CFG['dir']['backdoor']); // BOOL
  156. $this->atMailBox = ($this->_SET['params'][0] === $this->_CFG['dir']['bin']); // BOOL
  157. }
  158. public function whatAmI()
  159. {
  160. // Let's Discover if this document is looking for an extension
  161. $e = explode('.', $this->url['path']);
  162. $this->Key['is']['content'] = (count($e) > 1) ? $e[count($e)-1] : 'html';
  163. }
  164. // Here we determine what it is the client is trying to access.
  165. private function howAmI()
  166. {
  167. // ../Xtra/method/param1/param2/param3/etc
  168. $p = $this->_SET['params'];
  169. $this->_SET['action'] = $this->_SET['method'] = 'index';
  170. // If we are at the back door, remove it out of our params.
  171. if ($this->atBackDoor) {
  172. unset($p[0]);
  173. $p = array_values($p);
  174. }
  175. if ($this->atSideDoor) {
  176. unset($p[0]);
  177. $p = array_values($p);
  178. }
  179. if ( isset($p[0]) ) {
  180. $this->_SET['action'] = ($p[0]) ? $p[0] : 'index';
  181. unset($p[0]);
  182. }
  183. if ( isset($p[1]) ) {
  184. $this->_SET['method'] = ($p[1]) ? $p[1] : 'index';
  185. unset($p[1]);
  186. }
  187. foreach ($p as $key => $value) {
  188. if($value == '.json' || $value == '.xml'){
  189. $_SESSION['output'] = 'json';
  190. unset($p[$key]);
  191. }
  192. }
  193. $this->_SET['params'] = $p;
  194. }
  195. private function openDoor()
  196. {
  197. // Door #1
  198. // If we Do not have a DB Connected & Setup; Run through the DB Setup.
  199. if( false == file_exists(DB_CFG) ){
  200. // We need to know how to connect to our db first!
  201. // This Xtra configures the Connection to the Database.
  202. $this->_SET['action'] = 'wwwSetup';
  203. $this->_SET['method'] = 'install';
  204. $this->atSideDoor = true;
  205. //$this->dump()
  206. } else {
  207. // With the DB communicating, we able to Run!
  208. // Let the Dogs Run and Bark at them: AutoRuns.
  209. // Access, Login, Analytics, Backup, wwwSetup
  210. $this->autoRunSniff();
  211. }
  212. // The Door is Open; All the Xtras are Locked & Loaded; the Xengine is Up and Running;
  213. // Allow them to the Walk the Path Requested, ie: /login/logout )
  214. $this->walkPath();
  215. }
  216. /*
  217. Here is where we loop through the autoRun methods
  218. allowing them to sniff the Request and do their own Magic.
  219. */
  220. private function autoRunSniff(){
  221. // autoRun the requisets.
  222. $this->_comment("Running AutoRun Xtra's");
  223. // $this->dump($this->q());
  224. $q = $this->q();
  225. // ok... we have a db file. but do we have a db!?
  226. $this->Q("SHOW TABLES LIKE '".$this->Q->db['prefix']."configs'");
  227. if($q->ERROR){
  228. // Log the Error to The Trac System!!!!
  229. $sql = '';
  230. if(isset( $this->Q->sql )){
  231. $sql = $this->removeLocalData($this->lang(
  232. $this->_LANG['DB']['ERROR']['SQL'],
  233. array('sql' => $this->Q->sql)
  234. ),$this->Q);
  235. }
  236. $description = '';
  237. $description .= $sql;
  238. $error = array(
  239. 'summary' => 'DB ERROR :: '.$this->removeLocalData($this->Q->ERROR,$this->Q),
  240. 'description' => $description,
  241. 'attr' => array(
  242. 'type' => 'defect',
  243. //'component' => 'x'.ucfirst($this->_SET['action']),
  244. 'priority' => 'trivial',
  245. 'reporter' => $_SESSION['user']['username'].'@'.$_SERVER['HTTP_HOST'],
  246. 'keywords' => $this->_SET['action'].'::'. $this->_SET['method']
  247. )
  248. );
  249. $this->reportSystemError($error);
  250. $this->set(array(
  251. 'action' => 'access',
  252. 'method' => 'db',
  253. 'params' => array( $this->uri, $q->ERROR )
  254. ));
  255. }else{
  256. if(isset($_GET['theme'])){
  257. $this->STYLE = $_GET['theme'];
  258. }
  259. $xphp = $this->getXtras();
  260. foreach($xphp as $k => $x){
  261. try {
  262. $class = $x['class'];
  263. $methods = get_class_methods($class);
  264. if(in_array('autoRun', $methods)){
  265. $this->_comment("Auto Run: ". $class);
  266. $this->_xtra = $class;
  267. // $return = $class::autoRun($this);
  268. $class = new $class($this->_CFG);
  269. $class = $this->mergeO($class,$this);
  270. $return = call_user_func_array(array($class,'autoRun'), array($this));
  271. $this->_comment('AutoRan '.get_class($class).': ');
  272. foreach ($class as $key => $value) {
  273. $this->$key = $value;
  274. }
  275. if(is_array($return)){
  276. $this->_SET = $this->set($return);
  277. $this->_SET = array_merge($return,$this->_SET);
  278. }
  279. $this->_comment("AutoRun Found in: ".get_class($class)." ~ Complete...");
  280. }
  281. } catch (Exception $e) {
  282. $this->_comment($e->getMessage());
  283. }
  284. }
  285. }
  286. }
  287. private function walkPath()
  288. {
  289. $this->_comment("Xtra: ".$this->_SET['action']." | Method:".$this->_SET['method']);
  290. // Look to see if any Xtra matches
  291. $Xtra = 'x'.ucwords($this->_SET['action']);
  292. $this->_comment("Looking for Class $Xtra");
  293. $php = XPHP_DIR."/$Xtra.php";
  294. $this->_comment("Looking for file $php");
  295. if( file_exists($php) ){
  296. $this->runXtra($Xtra,$php);
  297. }
  298. }
  299. function is_class_method($type="public", $method, $class) {
  300. $refl = new ReflectionMethod($class, $method);
  301. switch($type) {
  302. case "static":
  303. return $refl->isStatic();
  304. break;
  305. case "public":
  306. return $refl->isPublic();
  307. break;
  308. case "private":
  309. return $refl->isPrivate();
  310. break;
  311. }
  312. }
  313. private function runXtra($Xtra,$php)
  314. {
  315. # require the dynamic file.
  316. require_once($php);
  317. # create a new class
  318. $Xtra = new $Xtra($this->_CFG);
  319. $Xtra = $this->mergeO($Xtra,$this);
  320. # if the method exists...
  321. if( method_exists($Xtra,$this->_SET['method']) && $this->is_class_method('public',$this->_SET['method'],$Xtra) )
  322. {
  323. // $Xtra->Q = $this->Q;
  324. # call the function w/ params
  325. $this->_comment("Running $php");
  326. array_values($this->_SET['params']);
  327. $return = call_user_func_array( array($Xtra,$this->_SET['method']), $this->_SET['params'] );
  328. $this->_SET = $this->apply($this->_SET,$Xtra->_SET);
  329. if(is_array($return))
  330. $this->_SET = array_merge($return,$this->_SET);
  331. // $this->dump($this->_SET);
  332. # We might have logged in...
  333. $this->whoAmI($this->Key);
  334. $this->whatAmI($this->Key);
  335. if($return && $this->_SET['action'] == 'login' && $this->Key['is']['user']){
  336. //$this->_SET['action'] = (isset($this->wait['Xtra'])) ? $this->wait['Xtra'] : $this->_SET['action'] ;
  337. //$this->_SET['method'] = (isset($this->wait['method'])) ? $this->wait['method'] : $this->_SET['method'];
  338. // Run this over on more time...
  339. //$this->walkThru();
  340. // makes for a quick No-redirect ;)
  341. //unset($_POST['password']);
  342. }
  343. # Illegal Method has been Called!
  344. }else{
  345. // Test to see if Navi knows where to go.
  346. # Kill the Engine send a 404!!
  347. $this->_SET['action'] = 'access';
  348. $this->_SET['method'] = 'notFound';
  349. $this->_SET['params']($this->_LANG['404'],$this->_LANG['404_msg']);
  350. }
  351. }
  352. private function browse()
  353. {
  354. $this->_comment("Sending data to Browser");
  355. // Dislays/Outputs Data To Browser.
  356. $this->set('IS_USER',$this->Key['is']['user']);
  357. $this->set('IS_ADMIN',$this->Key['is']['admin']);
  358. $this->display($this->Key['is']['content'], $this->_SET);
  359. //$this->dump($this->_SET);
  360. // exit;
  361. }
  362. private function display($type='html',$array)
  363. {
  364. // Choose which type of content we are displaying.
  365. //exit;
  366. ob_clean();
  367. if(isset($_REQUEST['callback'])){
  368. $callback = $_REQUEST['callback'];
  369. //start output
  370. if ($callback) {
  371. header('Content-Type: text/javascript');
  372. echo $callback . '(' . json_encode($array) . ');';
  373. } else {
  374. header('Content-Type: application/x-json');
  375. echo json_encode($array);
  376. }
  377. exit;
  378. }else{
  379. switch ($type) {
  380. default:
  381. # HTML
  382. header('Content-Type: text/html');
  383. $this->viewTemplate();
  384. break;
  385. case 'json':
  386. ob_clean();
  387. unset($array['HTML']);
  388. unset($array['ICON']);
  389. unset($array['admin_menu']);
  390. unset($array['xtras']);
  391. header('Content-type: text/javascript');
  392. echo json_encode($array);
  393. break;
  394. case ('xml'):
  395. ob_clean();
  396. header('Content-Type: text/xml');
  397. echo $this->viewXml($array);
  398. break;
  399. }
  400. }
  401. }
  402. private function viewTemplate(){
  403. // This handles the templating, which file to load, what theme to request, what thumbnailer to use.
  404. // Pre Bootstrap...
  405. // $layout = ($this->atBackDoor) ? 'backdoor' : 'frontdoor';
  406. $layout = ($this->atBackDoor) ? 'watchtower' : 'frontdoor';
  407. $layout = ($this->atSideDoor) ? 'sidedoor' : $layout;
  408. // Regardless of Door, if there is a DB error...
  409. // @TODO This Could be caught earlier... though
  410. // $this->dump($this->lang(
  411. // $this->_LANG['DB']['ERROR']['SQL'],
  412. // array('sql' => $this->Q->mSql)
  413. // ));
  414. // DATABASE ERROR
  415. if(false !== $this->Q->ERROR && file_exists(DB_CFG)){
  416. // function removeLocalData($r,$Q)
  417. // {
  418. // $r = str_replace($Q->db['database'].'.', '', $r);
  419. // $r = str_replace($Q->db['prefix'], '', $r);
  420. // return $r;
  421. // }
  422. $sql = '';
  423. if(isset( $this->Q->sql )){
  424. $sql = $this->removeLocalData($this->lang(
  425. $this->_LANG['DB']['ERROR']['SQL'],
  426. array('sql' => $this->Q->sql)
  427. ),$this->Q);
  428. }
  429. $description = '';
  430. $description .= $sql;
  431. $error = array(
  432. 'summary' => 'DB ERROR :: '.$this->removeLocalData($this->Q->ERROR,$this->Q),
  433. 'description' => $description,
  434. 'attr' => array(
  435. 'type' => 'defect',
  436. 'component' => 'xCore',//.ucfirst($this->_SET['action']),
  437. 'priority' => 'trivial',
  438. 'reporter' => $_SESSION['user']['username'].'@'.$_SERVER['HTTP_HOST'],
  439. 'keywords' => $this->_SET['action'].'::'. $this->_SET['method']
  440. )
  441. );
  442. //$this->reportSystemError($error);
  443. $layout = 'sidedoor';
  444. $this->set(array(
  445. 'action' => 'access',
  446. 'method' => 'db',
  447. 'request' => $this->Q->ERROR,
  448. 'reason' => $this->lang(
  449. $this->_LANG['DB']['ERROR']['SQL'],
  450. array('sql' => $this->Q->sql)
  451. )
  452. ));
  453. }
  454. $lib = explode('/', $this->_CFG['dir']['libs']);
  455. $lib = $lib[count($lib)-1];
  456. // This is our last chance to change the output's HTML template.
  457. $html_door = ($this->atBackDoor) ? $this->_CFG['html']['private'] : $this->_CFG['html']['public'];
  458. // Override any all all links with custom navigation.
  459. if( isset($this->Key['heylisten']) ){
  460. // Get List of Bloxs.
  461. $this->set(array(
  462. 'action' => 'index',
  463. 'method' => 'index'
  464. ));
  465. }else if(!file_exists($this->_CFG['dir']['html']
  466. .'/'.$html_door
  467. .'/'.$this->_SET['action']
  468. .'/'.$this->_SET['method']
  469. .'.html')
  470. ){
  471. $this->set(array(
  472. 'action' => 'access',
  473. 'method' => '404'
  474. ));
  475. //$this->_SET['HTML']['HEAD']['TITLE'] = "Page Template Not Found";
  476. }
  477. if(is_array($this->_SET['_LANG'])){
  478. $lang = array_merge_recursive($this->_LANG,$this->_SET['_LANG']);
  479. }
  480. // $this->dump($lang);
  481. $assign = array(
  482. 'Xtra' => $this->_SET['action'],
  483. 'method' => $this->_SET['method'],
  484. 'params' => $this->_SET['params'],
  485. 'Door' => $html_door,
  486. 'toBackDoor' => $this->_CFG['dir']['backdoor'],
  487. 'toFrontDoor' => $this->_CFG['dir']['frontdoor'],
  488. 'toSideDoor' => $this->_CFG['dir']['sidedoor'],
  489. 'HTTP_HOST' => $_SERVER['HTTP_HOST'],
  490. 'html_title' => $_SERVER['HTTP_HOST'],
  491. 'USER' => $_SESSION['user'],
  492. 'ERROR' => false, // Set this to Display an Error
  493. // Sets the template variable TPL_EXISTS to make sure we have the page
  494. 'LANG' => $lang ,
  495. // KEY
  496. 'masterKey' => $this->Key,
  497. // Depreciate
  498. 'IS_ADMIN' => $this->_LANG,
  499. 'blox' => false,
  500. 'LAYOUT' => $layout,
  501. 'LAYOUTS' => $this->_CFG['html'],
  502. 'thumb' => '/'.$this->_CFG['dir']['backdoor'].'/'.$lib.'/phpThumb/phpThumb.php?f=png&q=100&'
  503. );
  504. $assign['TPL_EXISTS'] = file_exists(str_replace("//","/",$this->_CFG['dir']['html'].'/'.$assign['Door'].'/'.$this->_SET['action'].'/'.$this->_SET['method'].'.html'));
  505. $this->_SET['HTML']['JSAN'] = file_get_contents($this->_CFG['dir']['bin'].'/js/ux/JSAN.js');
  506. // Initate Smarty and Pass the assigned vars.
  507. $this->initSmarty(array_merge($assign, $this->_SET));
  508. }
  509. /**
  510. * initSmarty() includes the smarty files and configures the new class:
  511. * @return new Smarty() w/ cfg;
  512. */
  513. private function initSmarty($a){
  514. # Include the Smarty Class
  515. $this->lib($this->_CFG['SMARTY_V'].'/libs/Smarty.class.php');
  516. $dir = LIBS_DIR."/".$this->_CFG['SMARTY_V'];
  517. // Be sure to clean again.
  518. //ob_clean();
  519. # Start the Smarty Class
  520. $this->smarty = new Smarty();
  521. # Configure Smarty
  522. $this->smarty->compile_dir = $dir."/templates_c";
  523. $this->smarty->cache_dir = $dir."/cache";
  524. $this->smarty->config_dir = $dir."/configs";
  525. $this->smarty->template_dir = $this->_CFG['dir']['html'];
  526. $this->smarty->assign($a);
  527. //
  528. // if($this->_CFG['debug']['cache'] == false){
  529. // $this->smarty->clearAllCache();
  530. // }
  531. ob_clean();
  532. $this->smarty->display('index.tpl');
  533. //
  534. if($this->_CFG['debug']['cache'] == false){
  535. $this->smarty->clearAllCache();
  536. }
  537. return $this->smarty;
  538. }
  539. private function ob_start(){
  540. /** Improve buffer usage and compression */
  541. if (function_exists('ob_start')){
  542. /** Check that Zlib is not enabled by default (value should be 1-9, else it will be an empty string) */
  543. if (@ini_get('zlib.output_compression') || !function_exists('ob_gzhandler')){
  544. ob_start();
  545. }else{
  546. ob_start('ob_gzhandler');
  547. }
  548. }
  549. }
  550. /**
  551. * The main function for converting to an XML document.
  552. * Pass in a multi dimensional array and this recrusively loops through and builds up an XML document.
  553. *
  554. * @param array $data
  555. * @param string $rootNodeName - what you want the root node to be - defaultsto data.
  556. * @param SimpleXMLElement $xml - should only be used recursively
  557. * @return string XML
  558. */
  559. private function viewXml($data, $rootNodeName = 'data', $xml=null)
  560. {
  561. // turn off compatibility mode as simple xml throws a wobbly if you don't.
  562. ini_set ('zend.ze1_compatibility_mode', 0);
  563. if ($xml == null)
  564. {
  565. $xml = simplexml_load_string("<?xml version='1.0' encoding='utf-8'?><$rootNodeName />");
  566. }
  567. // loop through the data passed in.
  568. foreach($data as $key => $value)
  569. {
  570. // no numeric keys in our xml please!
  571. if (is_numeric($key))
  572. {
  573. // make string key...
  574. $key = "unknownNode_". (string) $key;
  575. }
  576. // replace anything not alpha numeric
  577. $key = preg_replace('/[^a-z]/i', '', $key);
  578. // if there is another array found recrusively call this function
  579. if (is_array($value))
  580. {
  581. $node = $xml->addChild($key);
  582. // recrusive call.
  583. $this->viewXml($value, $rootNodeName, $node);
  584. } else {
  585. // add single node.
  586. $value = htmlentities($value);
  587. $xml->addChild($key,$value);
  588. }
  589. }
  590. // pass back as string. or simple xml object if you want!
  591. return $xml->asXML();
  592. }
  593. /*
  594. * lib($file) a simple include function to easily grab
  595. * the location of our library files
  596. */
  597. public function lib($file){
  598. $this->_comment(get_class($this)." Requesting Library $file");
  599. try{
  600. require_once(LIBS_DIR.'/'.$file);
  601. }catch(Exception $e){
  602. throw new Exception(get_class($this)." Failed to Load Library: ".$file, 1);
  603. }
  604. }
  605. ////////////////////////////////
  606. ////////////////////////////////
  607. ////////////////////////////////
  608. public function q()
  609. {
  610. if(!isset($this->Q)){
  611. /*if(get_parent_class($this) != ''){
  612. echo get_class($this);
  613. $c = get_parent_class($this);
  614. $this->Q = parent::q();
  615. } */
  616. $this->_comment("Init DB from: ". get_class($this));
  617. require(DB_CFG);
  618. foreach($this->db as $key => $value){
  619. if($key == 'pass'){
  620. for($i=0;$i<count($this->mainDomain()); $i++){
  621. $value = base64_decode($value);
  622. }
  623. }
  624. $this->db[$key] = $value;
  625. }
  626. $db = "x".$this->_CFG['db'];
  627. if(!class_exists($db)){
  628. $this->_comment('Loading DB');
  629. $this->lib("x4deep/$db.php");
  630. }
  631. $this->Q = new $db($this->db['host'],$this->db['user'],$this->db['pass'],$this->db['database'],$this->db['prefix']);
  632. }else{
  633. $this->_comment("Recycled DB from: ". get_class($this));
  634. //$this->Q = parent::q();
  635. }
  636. // Return the class that may have already been created...
  637. return $this->Q;
  638. }
  639. function getXTras(){
  640. // This is where we should get the control panel icons.
  641. if(!isset($this->_xtras)){
  642. // We should only run this once
  643. if ($handle = opendir(XPHP_DIR)) {
  644. $time = microtime(true);
  645. $this->_comment("Loading Xtra Files...");
  646. while (false !== ($file = readdir($handle))) {
  647. if ($file != "." && $file != "..") {
  648. $ext = explode(".",$file);
  649. if(strtolower($ext[count($ext)-1]) === 'php'){
  650. $class = str_replace('.php','',$file);
  651. require_once(XPHP_DIR.'/'.$file);
  652. //$this->_comment("Loaded Xtra File ".$file);
  653. $rc = new ReflectionClass($class);
  654. $doc = $rc->getDocComment();
  655. if($doc){
  656. $data = trim(preg_replace('/\r?\n *\* */', ' ', $doc));
  657. preg_match_all('/@([a-z]+)\s+(.*?)\s*(?=$|@[a-z]+\s)/s', $data, $matches);
  658. $info = array_combine($matches[1], $matches[2]);
  659. $ext = explode('.',$file);
  660. $jig = array(
  661. 'author' => '',
  662. 'class' => $ext[0],
  663. 'file' => $file,
  664. 'icon' => '',
  665. 'link' => '',
  666. 'mini' => '',
  667. 'name' => '',
  668. 'version' => 0
  669. );
  670. $files[$file] = array_merge($jig,$info);
  671. }
  672. }
  673. }
  674. }
  675. closedir($handle);
  676. ksort($files);
  677. //$this->set('xphp_files',$files);
  678. $this->_xtras = $this->_SET['xtras'] = $files;
  679. $time = round(microtime(true) - $time,5);
  680. $this->_comment("Loaded ".count($files)." Xtra Files in ".$time);
  681. }
  682. }else{
  683. $files = $this->_SET['xtras'] = $this->_xtras;
  684. }
  685. return $files;
  686. }
  687. function syncDbTables(){
  688. // Go through all the modules.
  689. $mods = $this->getXTras();
  690. foreach($mods as $k => $v){
  691. $php = str_replace('.php','',$k);
  692. if( method_exists($php,'dbSync') ){
  693. $db = $php::dbSync();
  694. if(!empty($db)){
  695. foreach($db as $table => $columns){
  696. if(!empty($columns)){
  697. $this->syncTable($table,$columns);
  698. }
  699. }
  700. }
  701. }
  702. }
  703. }
  704. // MySQL Function...
  705. function syncTable($table,$columns){
  706. $q = $this->q();
  707. // Get Current Table
  708. // $chk = $q->Q("DESC $table");
  709. // // RENAME THIS TABLE TO STANDARD PREFIX TEQ
  710. // if(!empty($chk) && $q->PREFIX){
  711. // $q->Q("RENAME TABLE $table TO $q->PREFIX$table");
  712. // }
  713. //$c = $q->Q("DESC $q->PREFIX$table");
  714. $sql = '';
  715. $c = $q->Q("SHOW TABLES LIKE '$q->PREFIX$table'");
  716. if(!empty($c)){
  717. $c = $q->Q("DESC $q->PREFIX$table");
  718. }
  719. if(!empty($c) && is_array($columns)){
  720. foreach($c as $k => $v){
  721. $col = $v['Field'];
  722. // Check Columns
  723. if( isset( $columns[$col] ) ){
  724. // Column Doesn't Match
  725. if(is_array($columns[$col])){
  726. if($columns[$col]['Type'] != $v['Type'] || (isset($columns[$col]['Default']) && $v['Default'] != $columns[$col]['Default']) ){
  727. // Add Sync to Sql
  728. $sync = "`$v[Field]` `$v[Field]` ".$columns[$col]['Type'];
  729. if(isset($columns[$col]['Default'])){
  730. $sync .= ' DEFAULT "'.$columns[$col]['Default'].'"';
  731. }
  732. $sql = ($sql) ? $sql.", CHANGE $sync " : $sync;
  733. }
  734. }else if(is_string($columns[$col]) && isset($columns[$columns[$col]]['Type'])){
  735. $sync = "`$col` `$columns[$col]` ".$columns[$columns[$col]]['Type'];
  736. $sql = ($sql) ? $sql.", CHANGE $sync " : $sync;
  737. }
  738. unset( $columns[$col] );
  739. unset( $c[$k] );
  740. }
  741. }
  742. // Change Columns
  743. if($sql){
  744. // Run SQL
  745. $q->Q("ALTER TABLE `$q->PREFIX$table` CHANGE $sql");
  746. if(isset($_GET['debug'])){
  747. //echo $q->mSql.'<hr>';
  748. //echo $q->error;
  749. }
  750. return $q->error;
  751. }
  752. // New columns
  753. if(!empty($columns)){
  754. foreach($columns as $k => $v){
  755. if(is_array($v)){
  756. $sync = "`$k` ".$v['Type'];
  757. $q->Q("ALTER TABLE `$q->PREFIX$table` ADD $sync");
  758. }
  759. //echo $q->error;
  760. }
  761. }
  762. }else{
  763. if( is_array($columns) ){
  764. # CREATE TABLE
  765. foreach($columns as $k => $v){
  766. if(is_array($v)){
  767. $sync = "`$k` ".$v['Type'];
  768. $sql = ($sql) ? $sql.", $sync " : $sync;
  769. }
  770. }
  771. $q->Q("CREATE TABLE `$q->PREFIX$table`( id INT(6) NOT NULL AUTO_INCREMENT, PRIMARY KEY(id),$sql );");
  772. // echo $q->error;
  773. }else{
  774. # Rename Table...
  775. $chk = $q->Q("SHOW TABLES LIKE '$table'");
  776. if(empty($chk)){
  777. $chk = $q->Q("SHOW TABLES LIKE '$q->PREFIX$table'");
  778. // RENAME THIS TABLE TO STANDARD PREFIX TEQ
  779. if(!empty($chk)){
  780. $q->Q("RENAME TABLE $q->PREFIX$table TO $q->PREFIX$columns");
  781. }
  782. }else{
  783. $q->Q("RENAME TABLE $table TO $q->PREFIX$columns");
  784. }
  785. }
  786. }
  787. }
  788. function mainDomain(){
  789. $domain = $_SERVER['HTTP_HOST'];
  790. $domain = explode('.',$domain);
  791. $i = 0;
  792. while(count($domain) > 2){
  793. unset($domain[$i]);
  794. $i++;
  795. }
  796. $domain = implode('.',$domain);
  797. return $domain;
  798. }
  799. /*
  800. */
  801. public function set($k,$v=false){
  802. //$this->dump($this->_SET);
  803. if(!is_array($k)){
  804. //var_dump($k);
  805. return $this->_SET[$k] = $v;
  806. $class = get_class($this);
  807. $_SESSION[$class][$k] = $v;
  808. }else if(is_array($k)){
  809. return $this->_SET = array_merge($this->_SET,$k);
  810. }
  811. }
  812. private function apply($array,$default){
  813. $new = array();
  814. foreach ($default as $key => $value) {
  815. // if array has new from default.
  816. if( isset( $array[$key] )){
  817. if( is_array($array[$key]) ){
  818. // we are dealing with an array, dig deeper.
  819. $new[$key] = $this->apply($array[$key],$default[$key]);
  820. } else {
  821. // ok - we've hit a value, lets set it in the new array.
  822. $new[$key] = $array[$key];
  823. }
  824. }else{
  825. // the new array that got passed.
  826. $new[$key] = $value;
  827. }
  828. }
  829. return $new;
  830. }
  831. private function mergeO($obj,$default){
  832. foreach ($default as $key => $value) {
  833. $obj->$key = $value;
  834. }
  835. return $obj;
  836. }
  837. public function shutDownFunction() {
  838. $error = error_get_last();
  839. $t = $error['type'];
  840. if($t === E_ERROR /*|| $t === E_WARNING*/){
  841. //ob_clean();
  842. function FriendlyErrorType($type){
  843. switch($type){
  844. case E_ERROR: // 1 //
  845. return 'E_ERROR';
  846. case E_WARNING: // 2 //
  847. return 'E_WARNING';
  848. case E_PARSE: // 4 //
  849. return 'E_PARSE';
  850. case E_NOTICE: // 8 //
  851. return 'E_NOTICE';
  852. case E_CORE_ERROR: // 16 //
  853. return 'E_CORE_ERROR';
  854. case E_CORE_WARNING: // 32 //
  855. return 'E_CORE_WARNING';
  856. case E_CORE_ERROR: // 64 //
  857. return 'E_COMPILE_ERROR';
  858. case E_CORE_WARNING: // 128 //
  859. return 'E_COMPILE_WARNING';
  860. case E_USER_ERROR: // 256 //
  861. return 'E_USER_ERROR';
  862. case E_USER_WARNING: // 512 //
  863. return 'E_USER_WARNING';
  864. case E_USER_NOTICE: // 1024 //
  865. return 'E_USER_NOTICE';
  866. case E_STRICT: // 2048 //
  867. return 'E_STRICT';
  868. case E_RECOVERABLE_ERROR: // 4096 //
  869. return 'E_RECOVERABLE_ERROR';
  870. case E_DEPRECATED: // 8192 //
  871. return 'E_DEPRECATED';
  872. case E_USER_DEPRECATED: // 16384 //
  873. return 'E_USER_DEPRECATED';
  874. }
  875. return "";
  876. }
  877. $file = realpath($_SERVER['DOCUMENT_ROOT']);
  878. $file = str_replace($file , '', $error['file']);
  879. $e = array(
  880. 'summary' => FriendlyErrorType($error['type'])
  881. .' :: '.$error['message'].' :: '.$file.'#L'.$error['line'],
  882. 'description' => $error['message'].' :: '.'[source:trunk'.$file.'#L'.$error['line'].']',
  883. 'attr' => array(
  884. 'type' => 'defect',
  885. //'component' => 'x'.ucfirst($this->_SET['action']),
  886. 'priority' => 'trivial',
  887. 'reporter' => $_SESSION['user']['username'].'@'.$_SERVER['HTTP_HOST'],
  888. 'keywords' => $this->_SET['action'].'::'. $this->_SET['method']
  889. )
  890. );
  891. $this->reportSystemError($e);
  892. }
  893. }
  894. private function removeLocalData($r,$Q)
  895. {
  896. $r = str_replace($Q->db['database'].'.', '', $r);
  897. $r = str_replace($Q->db['prefix'], '', $r);
  898. return $r;
  899. }
  900. /////////////////////////////
  901. //// Debug Functions
  902. //
  903. //
  904. // This should be used sparingly, and in production only!
  905. public function _comment($msg,$clear=false){
  906. $time = round(microtime(true) - $this->_CFG['debug']['runtime'],5);
  907. $echo = $this->_debugReport[] = ">'''[wiki:".get_class($this)."]''': ^[~~".$time."~~]^ $msg";
  908. if($this->_CFG['debug']['on'])
  909. echo $echo;
  910. }
  911. public function devBug($var,$title,$dump = true,$halt = true)
  912. {
  913. # code...
  914. $this->_comment($title);
  915. $this->_dump($var,$dump,$halt);
  916. }
  917. // Same with this ^^^
  918. public function dump($var,$dump=true,$halt = true)
  919. {
  920. if($this->_CFG['debug']['on']){
  921. if($dump){
  922. echo '<pre>';
  923. var_dump($var);
  924. echo '</pre>';
  925. } else {
  926. echo $var;
  927. }
  928. }
  929. if($halt)
  930. exit();
  931. }
  932. /**
  933. * rebuildSession();
  934. * If accessing the site and users php session was lost.
  935. * This will attempt to restore the session quitly using
  936. * the browser cookies .
  937. *
  938. */
  939. function rebuildSession(){
  940. # If there are cookies, but there isnt a session...
  941. if( isset($_COOKIE['user']['secret']) && !isset($_SESSION['user']['secret']) ){
  942. //// Level 1 Security Check ////
  943. $u = $_COOKIE['user'];
  944. $secret = sha1(
  945. md5(
  946. $u['email'].base64_decode($u['hash']).$u['id'].$u['username']
  947. )
  948. );
  949. # Check Cookie Secret before hitting the DB
  950. if( $secret === $u['secret'] ){
  951. # Passed Level 1 Authority - Allows access to find user in DB.
  952. $q = $this->q();
  953. $row = $q->Select('email,hash,id,username,user_secret,user_lastvisit','Users',array(
  954. 'email'=>$u['email']
  955. ));
  956. //// Level 2 Security Check ////
  957. if($row[0]['user_secret'] === $secret){
  958. # Client is the Correct User.- ReBuild Session Data
  959. $this->setUser($row);
  960. }
  961. }
  962. }
  963. }
  964. function setConfig($option,$value){
  965. $q = $this->q();
  966. $cfg = $q->Select('*','config',array('config_option'=>$option));
  967. if(empty($cfg)){
  968. return $q->Insert('config',array(
  969. 'config_option' => $option,
  970. 'config_value' => $value
  971. ));
  972. }else{
  973. return $q->Update('config',array(
  974. 'config_value' => $value
  975. ),array(
  976. 'id' => $cfg[0]['id']
  977. ));
  978. }
  979. }
  980. // allows
  981. public function __call($method,$args){
  982. if(isset($this->_xtra)){
  983. $class = $this->_xtra;
  984. if(method_exists($class,$method)){
  985. $class = new $class($this->_CFG);
  986. $class = $this->mergeO($class,$this);
  987. return call_user_func_array(array($class,$method), $args);
  988. }
  989. }
  990. if(!isset($this->_xtras)){
  991. $this->getXtras();
  992. }
  993. foreach($this->_xtras as $x){
  994. $class = $x['class'];
  995. if(method_exists($class,$method)){
  996. $class = new $class($this->_CFG);
  997. $class = $this->mergeO($class,$this);
  998. return call_user_func_array(array($class,$method), $args);
  999. }
  1000. }
  1001. // if(method_exists($x['class'],$method))
  1002. // return call_user_method_array($method,$x['class'],$args);
  1003. //throw new Exception("This Method {$method} doesn't exist in ".get_class($this));
  1004. $this->_comment("This Method {$method} doesn't exist in ".get_class($this));
  1005. }
  1006. public function lang($str,$extract)
  1007. {
  1008. extract($extract);
  1009. // $this->dump($username);
  1010. // exit();
  1011. return eval('return "'.$str.'";');
  1012. }
  1013. public function initRPC($rpc=false)
  1014. {
  1015. if(!$rpc && isset($this->_RPC))
  1016. $rpc = $this->_RPC;
  1017. else
  1018. return false;
  1019. if(!class_exists('Zend_Loader')){
  1020. $this->lib('Zend/Loader.php');
  1021. $z = new Zend_Loader();
  1022. $z->loadClass('Zend_XmlRpc_Client');
  1023. }
  1024. $c = new Zend_XmlRpc_Client("http://$rpc[user]:$rpc[pass]@$rpc[www]");
  1025. // Need to bypass the webdav auth
  1026. $http = $c ->getHttpClient();
  1027. $http->setAuth( $rpc['user'], $rpc['pass'], Zend_Http_Client::AUTH_BASIC );
  1028. $c->setHttpClient($http);
  1029. return $this->RPC = $c;
  1030. }
  1031. function is_email ($email, $checkDNS = false) {
  1032. // Check that $email is a valid address
  1033. // (http://tools.ietf.org/html/rfc3696)
  1034. // (http://tools.ietf.org/html/rfc2822)
  1035. // (http://tools.ietf.org/html/rfc5322#section-3.4.1)
  1036. // (http://tools.ietf.org/html/rfc5321#section-4.1.3)
  1037. // (http://tools.ietf.org/html/rfc4291#section-2.2)
  1038. // (http://tools.ietf.org/html/rfc1123#section-2.1)
  1039. // the upper limit on address lengths should normally be considered to be 256
  1040. // (http://www.rfc-editor.org/errata_search.php?rfc=3696)
  1041. if (strlen($email) > 256) return false; // Too long
  1042. // Contemporary email addresses consist of a "local part" separated from
  1043. // a "domain part" (a fully-qualified domain name) by an at-sign ("@").
  1044. // (http://tools.ietf.org/html/rfc3696#section-3)
  1045. $index = strrpos($email,'@');
  1046. if ($index === false) return false; // No at-sign
  1047. if ($index === 0) return false; // No local part
  1048. if ($index > 64) return false; // Local part too long
  1049. $localPart = substr($email, 0, $index);
  1050. $domain = substr($email, $index + 1);
  1051. $domainLength = strlen($domain);
  1052. if ($domainLength === 0) return false; // No domain part
  1053. if ($domainLength > 255) return false; // Domain part too long
  1054. // Let's check the local part for RFC compliance...
  1055. //
  1056. // Any ASCII graphic (printing) character other than the
  1057. // at-sign ("@"), backslash, double quote, comma, or square brackets may
  1058. // appear without quoting. If any of that list of excluded characters
  1059. // are to appear, they must be quoted
  1060. // (http://tools.ietf.org/html/rfc3696#section-3)
  1061. if (preg_match('/^"(?:.)*"$/', $localPart) > 0) {
  1062. // Quoted-string tests:
  1063. //
  1064. // Note that since quoted-pair
  1065. // is allowed in a quoted-string, the quote and backslash characters may
  1066. // appear in a quoted-string so long as they appear as a quoted-pair.
  1067. // (http://tools.ietf.org/html/rfc2822#section-3.2.5)
  1068. $groupCount = preg_match_all('/(?:^"|"$|\\\\\\\\|\\\\")|(\\\\|")/', $localPart, $matches);
  1069. array_multisort($matches[1], SORT_DESC);
  1070. if ($matches[1][0] !== '') return false; // Unescaped quote or backslash character inside quoted string
  1071. if (preg_match('/^"\\\\*"$/', $localPart) > 0) return false; // "" and "\" are slipping through - must tidy this up
  1072. } else {
  1073. // Unquoted string tests:
  1074. //
  1075. // Period (".") may...appear, but may not be used to start or end the
  1076. // local part, nor may two or more consecutive periods appear.
  1077. // (http://tools.ietf.org/html/rfc3696#section-3)
  1078. if (preg_match('/^\\.|\\.\\.|\\.$/', $localPart) > 0) return false; // Dots in wrong place
  1079. // Any excluded characters? i.e. <space>, @, [, ], \, ", <comma>
  1080. if (preg_match('/[ @\\[\\]\\\\",]/', $localPart) > 0)
  1081. // Check all excluded characters are escaped
  1082. $stripped = preg_replace('/\\\\[ @\\[\\]\\\\",]/', '', $localPart);
  1083. if (preg_match('/[ @\\[\\]\\\\",]/', $stripped) > 0) return false; // Unquoted excluded characters
  1084. }
  1085. // Now let's check the domain part...
  1086. // The domain name can also be replaced by an IP address in square brackets
  1087. // (http://tools.ietf.org/html/rfc3696#section-3)
  1088. // (http://tools.ietf.org/html/rfc5321#section-4.1.3)
  1089. // (http://tools.ietf.org/html/rfc4291#section-2.2)
  1090. if (preg_match('/^\\[(.)+]$/', $domain) === 1) {
  1091. // It's an address-literal
  1092. $addressLiteral = substr($domain, 1, $domainLength - 2);
  1093. $matchesIP = array();
  1094. // Extract IPv4 part from the end of the address-literal (if there is one)
  1095. if (preg_match('/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/', $addressLiteral, $matchesIP) > 0) {
  1096. $index = strrpos($addressLiteral, $matchesIP[0]);
  1097. if ($index === 0) {
  1098. // Nothing there except a valid IPv4 address, so...
  1099. return true;
  1100. } else {
  1101. // Assume it's an attempt at a mixed address (IPv6 + IPv4)
  1102. if ($addressLiteral[$index - 1] !== ':') return false; // Character preceding IPv4 address must be ':'
  1103. if (substr($addressLiteral, 0, 5) !== 'IPv6:') return false; // RFC5321 section 4.1.3
  1104. $IPv6 = substr($addressLiteral, 5, ($index ===7) ? 2 : $index - 6);
  1105. $groupMax = 6;
  1106. }
  1107. } else {
  1108. // It must be an attempt at pure IPv6
  1109. if (substr($addressLiteral, 0, 5) !== 'IPv6:') return false; // RFC5321 section 4.1.3
  1110. $IPv6 = substr($addressLiteral, 5);
  1111. $groupMax = 8;
  1112. }
  1113. $groupCount = preg_match_all('/^[0-9a-fA-F]{0,4}|\\:[0-9a-fA-F]{0,4}|(.)/', $IPv6, $matchesIP);
  1114. $index = strpos($IPv6,'::');
  1115. if ($index === false) {
  1116. // We need exactly the right number of groups
  1117. if ($groupCount !== $groupMax) return false; // RFC5321 section 4.1.3
  1118. } else {
  1119. if ($index !== strrpos($IPv6,'::')) return false; // More than one '::'
  1120. $groupMax = ($index === 0 || $index === (strlen($IPv6) - 2)) ? $groupMax : $groupMax - 1;
  1121. if ($groupCount > $groupMax) return false; // Too many IPv6 groups in address
  1122. }
  1123. // Check for unmatched characters
  1124. array_multisort($matchesIP
  1125. [1], SORT_DESC);
  1126. if ($matchesIP[1][0] !== '') return false; // Illegal characters in address
  1127. // It's a valid IPv6 address, so...
  1128. return true;
  1129. } else {
  1130. // It's a domain name...
  1131. // The syntax of a legal Internet host name was specified in RFC-952
  1132. // One aspect of host name syntax is hereby changed: the
  1133. // restriction on the first character is relaxed to allow either a
  1134. // letter or a digit.
  1135. // (http://tools.ietf.org/html/rfc1123#section-2.1)
  1136. //
  1137. // NB RFC 1123 updates RFC 1035, but this is not currently apparent from reading RFC 1035.
  1138. //
  1139. // Most common applications, including email and the Web, will generally not permit...escaped strings
  1140. // (http://tools.ietf.org/html/rfc3696#section-2)
  1141. //
  1142. // Characters outside the set of alphabetic characters, digits, and hyphen MUST NOT appear in domain name
  1143. // labels for SMTP clients or servers
  1144. // (http://tools.ietf.org/html/rfc5321#section-4.1.2)
  1145. //
  1146. // RFC5321 precludes the use of a trailing dot in a domain name for SMTP purposes
  1147. // (http://tools.ietf.org/html/rfc5321#section-4.1.2)
  1148. $matches = array();
  1149. $groupCount = preg_match_all('/(?:[0-9a-zA-Z][0-9a-zA-Z-]{0,61}[0-9a-zA-Z]|[a-zA-Z])(?:\\.|$)|(.)/', $domain, $matches);
  1150. $level = count($matches[0]);
  1151. if ($level == 1) return false; // Mail host can't be a TLD
  1152. $TLD = $matches[0][$level - 1];
  1153. if (substr($TLD, strlen($TLD) - 1, 1) === '.') return false; // TLD can't end in a dot
  1154. if (preg_match('/^[0-9]+$/', $TLD) > 0) return false; // TLD can't be all-numeric
  1155. // Check for unmatched characters
  1156. array_multisort($matches[1], SORT_DESC);
  1157. if ($matches[1][0] !== '') return false; // Illegal characters in domain, or label longer than 63 characters
  1158. // Check DNS?
  1159. if ($checkDNS && function_exists('checkdnsrr')) {
  1160. if (!(checkdnsrr($domain, 'A') || checkdnsrr($domain, 'MX'))) {
  1161. return false; // Domain doesn't actually exist
  1162. }
  1163. }
  1164. // Eliminate all other factors, and the one which remains must be the truth.
  1165. // (Sherlock Holmes, The Sign of Four)
  1166. return true;
  1167. }
  1168. }
  1169. }
  1170. ?>