1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
16: class Horde_Config
17: {
18: 19: 20: 21: 22:
23: protected $_app;
24:
25: 26: 27: 28: 29: 30:
31: protected $_xmlConfigTree = null;
32:
33: 34: 35: 36: 37:
38: protected $_phpConfig;
39:
40: 41: 42: 43: 44:
45: protected $_oldConfig;
46:
47: 48: 49: 50: 51:
52: protected $_preConfig;
53:
54: 55: 56: 57: 58:
59: protected $_postConfig;
60:
61: 62: 63: 64: 65:
66: protected $_currentConfig = array();
67:
68: 69: 70: 71: 72: 73:
74: protected $_versionTag = '';
75:
76: 77: 78: 79: 80:
81: protected $_configBegin = "/* CONFIG START. DO NOT CHANGE ANYTHING IN OR AFTER THIS LINE. */\n";
82:
83: 84: 85: 86: 87:
88: protected $_configEnd = "/* CONFIG END. DO NOT CHANGE ANYTHING IN OR BEFORE THIS LINE. */\n";
89:
90: 91: 92: 93: 94:
95: protected $_versionUrl = 'http://www.horde.org/versions.php';
96:
97: 98: 99: 100: 101:
102: public function __construct($app = 'horde')
103: {
104: $this->_app = $app;
105: }
106:
107: 108: 109: 110: 111: 112: 113: 114:
115: public function checkVersions()
116: {
117: if (!Horde_Util::extensionExists('SimpleXML')) {
118: throw new Horde_Exception('SimpleXML not available.');
119: }
120:
121: $http = $GLOBALS['injector']->getInstance('Horde_Core_Factory_HttpClient')->create();
122: $response = $http->get($this->_versionUrl);
123: if ($response->code != 200) {
124: throw new Horde_Exception('Unexpected response from server.');
125: }
126:
127: $xml = new SimpleXMLElement($response->getBody());
128: $versions = array();
129:
130: foreach ($xml->stable->application as $app) {
131: $versions[strval($app['name'])] = array(
132: 'version' => $app->version,
133: 'url' => $app->url
134: );
135: }
136:
137: return $versions;
138: }
139:
140: 141: 142:
143: public function configFile()
144: {
145: $path = $GLOBALS['registry']->get('fileroot', $this->_app) . '/config';
146: $configFile = $path . '/conf.php';
147: if (is_link($configFile)) {
148: $configFile = readlink($configFile);
149: }
150: return $configFile;
151: }
152:
153: 154: 155: 156: 157: 158: 159: 160: 161: 162:
163: public function readXMLConfig($custom_conf = null)
164: {
165: if (!is_null($this->_xmlConfigTree) && !$custom_conf) {
166: return $this->_xmlConfigTree;
167: }
168:
169: $path = $GLOBALS['registry']->get('fileroot', $this->_app) . '/config';
170:
171: if ($custom_conf) {
172: $this->_currentConfig = $custom_conf;
173: } else {
174:
175: @eval($this->getPHPConfig());
176: if (isset($conf)) {
177: $this->_currentConfig = $conf;
178: }
179: }
180:
181:
182: $dom = new DOMDocument();
183: $dom->load($path . '/conf.xml');
184:
185:
186: $node = $dom->firstChild;
187: while (!empty($node)) {
188: if (($node->nodeType == XML_COMMENT_NODE) &&
189: ($vers_tag = $this->getVersion($node->nodeValue))) {
190: $this->_versionTag = $vers_tag . "\n";
191: break;
192: }
193: $node = $node->nextSibling;
194: }
195:
196:
197: $this->_xmlConfigTree = array();
198: $root = $dom->documentElement;
199: if ($root->hasChildNodes()) {
200: $this->_parseLevel($this->_xmlConfigTree, $root->childNodes, '');
201: }
202:
203:
204: foreach (glob($path . '/conf.d/*.xml') as $additional) {
205: $dom->load($additional);
206: $root = $dom->documentElement;
207: if ($root->hasChildNodes()) {
208: $tree = array();
209: $this->_parseLevel($tree, $root->childNodes, '');
210: $this->_xmlConfigTree = Horde_Array::replaceRecursive($this->_xmlConfigTree, $tree);
211: }
212: }
213:
214: return $this->_xmlConfigTree;
215: }
216:
217: 218: 219: 220: 221: 222: 223:
224: public function getVersion($text)
225: {
226:
227: if (preg_match('/\$.*?conf\.xml,v .*? .*\$/', $text, $match) ||
228:
229: preg_match('/\$Id:\s*[0-9a-f]+\s*\$/', $text, $match)) {
230: return $match[0];
231: }
232:
233: return false;
234: }
235:
236: 237: 238: 239: 240:
241: public function getPHPConfig()
242: {
243: if (!is_null($this->_oldConfig)) {
244: return $this->_oldConfig;
245: }
246:
247: $path = $GLOBALS['registry']->get('fileroot', $this->_app) . '/config';
248: if (file_exists($path . '/conf.php')) {
249: $this->_oldConfig = file_get_contents($path . '/conf.php');
250: if (!empty($this->_oldConfig)) {
251: $this->_oldConfig = preg_replace('/<\?php\n?/', '', $this->_oldConfig);
252: $pos = strpos($this->_oldConfig, $this->_configBegin);
253: if ($pos !== false) {
254: $this->_preConfig = substr($this->_oldConfig, 0, $pos);
255: $this->_oldConfig = substr($this->_oldConfig, $pos);
256: }
257: $pos = strpos($this->_oldConfig, $this->_configEnd);
258: if ($pos !== false) {
259: $this->_postConfig = substr($this->_oldConfig, $pos + strlen($this->_configEnd));
260: $this->_oldConfig = substr($this->_oldConfig, 0, $pos);
261: }
262: }
263: } else {
264: $this->_oldConfig = '';
265: }
266:
267: return $this->_oldConfig;
268: }
269:
270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283:
284: public function writePHPConfig($formvars, &$php = null)
285: {
286: $php = $this->generatePHPConfig($formvars);
287: $path = $GLOBALS['registry']->get('fileroot', $this->_app) . '/config';
288: $configFile = $this->configFile();
289: if (file_exists($configFile)) {
290: if (@copy($configFile, $path . '/conf.bak.php')) {
291: $GLOBALS['notification']->push(sprintf(_("Successfully saved the backup configuration file %s."), Horde_Util::realPath($path . '/conf.bak.php')), 'horde.success');
292: } else {
293: $GLOBALS['notification']->push(sprintf(_("Could not save the backup configuration file %s."), Horde_Util::realPath($path . '/conf.bak.php')), 'horde.warning');
294: }
295: }
296: if ($fp = @fopen($configFile, 'w')) {
297:
298: fwrite($fp, $php);
299: fclose($fp);
300: $GLOBALS['registry']->rebuild();
301: return true;
302: }
303:
304:
305: $GLOBALS['session']->set('horde', 'config/' . $this->_app, $php);
306:
307: return false;
308: }
309:
310: 311: 312: 313: 314: 315: 316: 317: 318: 319:
320: public function generatePHPConfig($formvars, $custom_conf = null)
321: {
322: $this->readXMLConfig($custom_conf);
323: $this->getPHPConfig();
324:
325: $this->_phpConfig = "<?php\n" . $this->_preConfig . $this->_configBegin;
326: if (!empty($this->_versionTag)) {
327: $this->_phpConfig .= '// ' . $this->_versionTag;
328: }
329: $this->_generatePHPConfig($this->_xmlConfigTree, '', $formvars);
330: $this->_phpConfig .= $this->_configEnd . $this->_postConfig;
331:
332: return $this->_phpConfig;
333: }
334:
335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351:
352: protected function _generatePHPConfig($section, $prefix, $formvars)
353: {
354: if (!is_array($section)) {
355: return;
356: }
357:
358: foreach ($section as $name => $configitem) {
359: $prefixedname = empty($prefix)
360: ? $name
361: : $prefix . '|' . $name;
362: $configname = str_replace('|', '__', $prefixedname);
363: $quote = (!isset($configitem['quote']) || $configitem['quote'] !== false);
364:
365: if ($configitem == 'placeholder') {
366: $this->_phpConfig .= '$conf[\'' . str_replace('|', '\'][\'', $prefix) . "'] = array();\n";
367: } elseif (isset($configitem['switch'])) {
368: $val = $formvars->getExists($configname, $wasset);
369: if (!$wasset) {
370: $val = isset($configitem['default']) ? $configitem['default'] : null;
371: }
372: if (isset($configitem['switch'][$val])) {
373: $value = $val;
374: if ($quote && $value != 'true' && $value != 'false') {
375: $value = "'" . $value . "'";
376: }
377: $this->_generatePHPConfig($configitem['switch'][$val]['fields'], $prefix, $formvars);
378: }
379: } elseif (isset($configitem['_type'])) {
380: $val = $formvars->getExists($configname, $wasset);
381: if (!$wasset &&
382: ((array_key_exists('is_default', $configitem) && $configitem['is_default'])
383: || !array_key_exists('is_default', $configitem))) {
384:
385: $val = isset($configitem['default']) ? $configitem['default'] : null;
386: }
387:
388: $type = $configitem['_type'];
389: switch ($type) {
390: case 'multienum':
391: if (is_array($val)) {
392: $encvals = array();
393: foreach ($val as $v) {
394: $encvals[] = $this->_quote($v);
395: }
396: $arrayval = "'" . implode('\', \'', $encvals) . "'";
397: if ($arrayval == "''") {
398: $arrayval = '';
399: }
400: } else {
401: $arrayval = '';
402: }
403: $value = 'array(' . $arrayval . ')';
404: break;
405:
406: case 'boolean':
407: if (is_bool($val)) {
408: $value = $val ? 'true' : 'false';
409: } else {
410: $value = ($val == 'on') ? 'true' : 'false';
411: }
412: break;
413:
414: case 'stringlist':
415: $values = explode(',', $val);
416: if (!is_array($values)) {
417: $value = "array('" . $this->_quote(trim($values)) . "')";
418: } else {
419: $encvals = array();
420: foreach ($values as $v) {
421: $encvals[] = $this->_quote(trim($v));
422: }
423: $arrayval = "'" . implode('\', \'', $encvals) . "'";
424: if ($arrayval == "''") {
425: $arrayval = '';
426: }
427: $value = 'array(' . $arrayval . ')';
428: }
429: break;
430:
431: case 'int':
432: if (strlen($val)) {
433: $value = (int)$val;
434: }
435: break;
436:
437: case 'octal':
438: $value = sprintf('0%o', octdec($val));
439: break;
440:
441: case 'header':
442: case 'description':
443: break;
444:
445: default:
446: if ($val != '') {
447: $value = $val;
448: if ($quote && $value != 'true' && $value != 'false') {
449: $value = "'" . $this->_quote($value) . "'";
450: }
451: }
452: break;
453: }
454: } else {
455: $this->_generatePHPConfig($configitem, $prefixedname, $formvars);
456: }
457:
458: if (isset($value)) {
459: $this->_phpConfig .= '$conf[\'' . str_replace('__', '\'][\'', $configname) . '\'] = ' . $value . ";\n";
460: }
461: unset($value);
462: }
463: }
464:
465: 466: 467: 468: 469: 470: 471: 472: 473: 474: 475: 476: 477:
478: protected function _parseLevel(&$conf, $children, $ctx)
479: {
480: foreach ($children as $node) {
481: if ($node->nodeType != XML_ELEMENT_NODE) {
482: continue;
483: }
484: $name = $node->getAttribute('name');
485: $desc = $GLOBALS['injector']->getInstance('Horde_Core_Factory_TextFilter')->filter($node->getAttribute('desc'), 'linkurls');
486: $required = !($node->getAttribute('required') == 'false');
487: $quote = !($node->getAttribute('quote') == 'false');
488:
489: $curctx = empty($ctx)
490: ? $name
491: : $ctx . '|' . $name;
492:
493: switch ($node->tagName) {
494: case 'configdescription':
495: if (empty($name)) {
496: $name = uniqid(mt_rand());
497: }
498:
499: $conf[$name] = array(
500: '_type' => 'description',
501: 'desc' => $GLOBALS['injector']->getInstance('Horde_Core_Factory_TextFilter')->filter($this->_default($curctx, $this->_getNodeOnlyText($node)), 'linkurls')
502: );
503: break;
504:
505: case 'configheader':
506: if (empty($name)) {
507: $name = uniqid(mt_rand());
508: }
509:
510: $conf[$name] = array(
511: '_type' => 'header',
512: 'desc' => $this->_default($curctx, $this->_getNodeOnlyText($node))
513: );
514: break;
515:
516: case 'configswitch':
517: $values = $this->_getSwitchValues($node, $ctx);
518: list($default, $isDefault) = $quote
519: ? $this->__default($curctx, $this->_getNodeOnlyText($node))
520: : $this->__defaultRaw($curctx, $this->_getNodeOnlyText($node));
521:
522: if ($default === '') {
523: $default = key($values);
524: }
525:
526: if (is_bool($default)) {
527: $default = $default ? 'true' : 'false';
528: }
529:
530: $conf[$name] = array(
531: 'desc' => $desc,
532: 'switch' => $values,
533: 'default' => $default,
534: 'is_default' => $isDefault
535: );
536: break;
537:
538: case 'configenum':
539: $values = $this->_getEnumValues($node);
540: list($default, $isDefault) = $quote
541: ? $this->__default($curctx, $this->_getNodeOnlyText($node))
542: : $this->__defaultRaw($curctx, $this->_getNodeOnlyText($node));
543:
544: if ($default === '') {
545: $default = key($values);
546: }
547:
548: if (is_bool($default)) {
549: $default = $default ? 'true' : 'false';
550: }
551:
552: $conf[$name] = array(
553: '_type' => 'enum',
554: 'required' => $required,
555: 'quote' => $quote,
556: 'values' => $values,
557: 'desc' => $desc,
558: 'default' => $default,
559: 'is_default' => $isDefault
560: );
561: break;
562:
563: case 'configlist':
564: list($default, $isDefault) = $this->__default($curctx, null);
565:
566: if (is_null($default)) {
567: $default = $this->_getNodeOnlyText($node);
568: } elseif (is_array($default)) {
569: $default = implode(', ', $default);
570: }
571:
572: $conf[$name] = array(
573: '_type' => 'stringlist',
574: 'required' => $required,
575: 'desc' => $desc,
576: 'default' => $default,
577: 'is_default' => $isDefault
578: );
579: break;
580:
581: case 'configmultienum':
582: $default = $this->_getNodeOnlyText($node);
583: if (strlen($default)) {
584: $default = explode(',', $default);
585: } else {
586: $default = array();
587: }
588: list($default, $isDefault) = $this->__default($curctx, $default);
589:
590: $conf[$name] = array(
591: '_type' => 'multienum',
592: 'required' => $required,
593: 'values' => $this->_getEnumValues($node),
594: 'desc' => $desc,
595: 'default' => Horde_Array::valuesToKeys($default),
596: 'is_default' => $isDefault
597: );
598: break;
599:
600: case 'configpassword':
601: $conf[$name] = array(
602: '_type' => 'password',
603: 'required' => $required,
604: 'desc' => $desc,
605: 'default' => $this->_default($curctx, $this->_getNodeOnlyText($node)),
606: 'is_default' => $this->_isDefault($curctx, $this->_getNodeOnlyText($node))
607: );
608: break;
609:
610: case 'configstring':
611: $conf[$name] = array(
612: '_type' => 'text',
613: 'required' => $required,
614: 'desc' => $desc,
615: 'default' => $this->_default($curctx, $this->_getNodeOnlyText($node)),
616: 'is_default' => $this->_isDefault($curctx, $this->_getNodeOnlyText($node))
617: );
618:
619: if ($conf[$name]['default'] === false) {
620: $conf[$name]['default'] = 'false';
621: } elseif ($conf[$name]['default'] === true) {
622: $conf[$name]['default'] = 'true';
623: }
624: break;
625:
626: case 'configboolean':
627: $default = $this->_getNodeOnlyText($node);
628: $default = !(empty($default) || $default === 'false');
629:
630: $conf[$name] = array(
631: '_type' => 'boolean',
632: 'required' => $required,
633: 'desc' => $desc,
634: 'default' => $this->_default($curctx, $default),
635: 'is_default' => $this->_isDefault($curctx, $default)
636: );
637: break;
638:
639: case 'configinteger':
640: $values = $this->_getEnumValues($node);
641:
642: $conf[$name] = array(
643: '_type' => 'int',
644: 'required' => $required,
645: 'values' => $values,
646: 'desc' => $desc,
647: 'default' => $this->_default($curctx, $this->_getNodeOnlyText($node)),
648: 'is_default' => $this->_isDefault($curctx, $this->_getNodeOnlyText($node))
649: );
650:
651: if ($node->getAttribute('octal') == 'true' &&
652: $conf[$name]['default'] != '') {
653: $conf[$name]['_type'] = 'octal';
654: $conf[$name]['default'] = sprintf('0%o', $this->_default($curctx, octdec($this->_getNodeOnlyText($node))));
655: }
656: break;
657:
658: case 'configldap':
659: $conf[$node->getAttribute('switchname')] = $this->_configLDAP($ctx, $node);
660: break;
661:
662: case 'configphp':
663: $conf[$name] = array(
664: '_type' => 'php',
665: 'required' => $required,
666: 'quote' => false,
667: 'desc' => $desc,
668: 'default' => $this->_defaultRaw($curctx, $this->_getNodeOnlyText($node)),
669: 'is_default' => $this->_isDefaultRaw($curctx, $this->_getNodeOnlyText($node))
670: );
671: break;
672:
673: case 'configsecret':
674: $conf[$name] = array(
675: '_type' => 'text',
676: 'required' => true,
677: 'desc' => $desc,
678: 'default' => $this->_default($curctx, strval(new Horde_Support_Uuid())),
679: 'is_default' => $this->_isDefault($curctx, $this->_getNodeOnlyText($node))
680: );
681: break;
682:
683: case 'configsql':
684: $conf[$node->getAttribute('switchname')] = $this->configSQL($ctx, $node);
685: break;
686:
687: case 'configvfs':
688: $conf[$node->getAttribute('switchname')] = $this->_configVFS($ctx, $node);
689: break;
690:
691: case 'configsection':
692: $conf[$name] = array();
693: $cur = &$conf[$name];
694: if ($node->hasChildNodes()) {
695: $this->_parseLevel($cur, $node->childNodes, $curctx);
696: }
697: break;
698:
699: case 'configtab':
700: $key = uniqid(mt_rand());
701:
702: $conf[$key] = array(
703: 'tab' => $name,
704: 'desc' => $desc
705: );
706:
707: if ($node->hasChildNodes()) {
708: $this->_parseLevel($conf, $node->childNodes, $ctx);
709: }
710: break;
711:
712: case 'configplaceholder':
713: $conf[uniqid(mt_rand())] = 'placeholder';
714: break;
715:
716: default:
717: $conf[$name] = array();
718: $cur = &$conf[$name];
719: if ($node->hasChildNodes()) {
720: $this->_parseLevel($cur, $node->childNodes, $curctx);
721: }
722: break;
723: }
724: }
725: }
726:
727: 728: 729: 730: 731: 732: 733: 734: 735: 736: 737: 738: 739: 740:
741: protected function _configLDAP($ctx, $node = null,
742: $switchname = 'driverconfig')
743: {
744: $fields = array(
745: 'hostspec' => array(
746: '_type' => 'text',
747: 'required' => true,
748: 'desc' => 'LDAP server/hostname',
749: 'default' => $this->_default($ctx . '|hostspec', '')
750: ),
751:
752: 'port' => array(
753: '_type' => 'int',
754: 'required' => false,
755: 'desc' => 'Port on which LDAP is listening, if non-standard',
756: 'default' => $this->_default($ctx . '|port', null)
757: ),
758:
759: 'tls' => array(
760: '_type' => 'boolean',
761: 'required' => false,
762: 'desc' => 'Use TLS to connect to the server?',
763: 'default' => $this->_default($ctx . '|tls', false)
764: ),
765:
766: 'version' => array(
767: '_type' => 'int',
768: 'required' => true,
769: 'quote' => false,
770: 'desc' => 'LDAP protocol version',
771: 'switch' => array(
772: '2' => array(
773: 'desc' => '2 (deprecated)',
774: 'fields' => array()
775: ),
776: '3' => array(
777: 'desc' => '3',
778: 'fields' => array()
779: )
780: ),
781: 'default' => $this->_default($ctx . '|version', 3)
782: ),
783:
784: 'bindas' => array(
785: 'desc' => 'Bind to LDAP as which user?',
786: 'default' => $this->_default($ctx . '|bindas', 'admin'),
787: 'switch' => array(
788: 'anon' => array(
789: 'desc' => 'Bind anonymously',
790: 'fields' => array()
791: ),
792: 'user' => array(
793: 'desc' => 'Bind as the currently logged-in user',
794: 'fields' => array(
795: 'user' => array(
796: 'binddn' => array(
797: '_type' => 'text',
798: 'required' => false,
799: 'desc' => 'DN used to bind for searching the user\'s DN (leave empty for anonymous bind)',
800: 'default' => $this->_default($ctx . '|binddn', '')
801: ),
802: 'bindpw' => array(
803: '_type' => 'text',
804: 'required' => false,
805: 'desc' => 'Password for bind DN',
806: 'default' => $this->_default($ctx . '|bindpw', '')
807: ),
808: 'uid' => array(
809: '_type' => 'text',
810: 'required' => true,
811: 'desc' => 'The username search key (set to samaccountname for AD).',
812: 'default' => $this->_default($ctx . '|user|uid', 'uid')
813: ),
814: 'filter_type' => array(
815: 'required' => false,
816: 'desc' => 'How to specify a filter for the user lists.',
817: 'default' => $this->_default($ctx . '|user|filter_type', 'objectclass'),
818: 'switch' => array(
819: 'filter' => array(
820: 'desc' => 'LDAP filter string',
821: 'fields' => array(
822: 'filter' => array(
823: '_type' => 'text',
824: 'required' => true,
825: 'desc' => 'The LDAP filter string used to search for users.',
826: 'default' => $this->_default($ctx . '|user|filter', '(objectClass=*)')
827: ),
828: ),
829: ),
830: 'objectclass' => array(
831: 'desc' => 'List of objectClasses',
832: 'fields' => array(
833: 'objectclass' => array(
834: '_type' => 'stringlist',
835: 'required' => true,
836: 'desc' => 'The objectclass filter used to search for users. Can be a single objectclass or a comma-separated list.',
837: 'default' => implode(', ', $this->_default($ctx . '|user|objectclass', array('*')))
838: ),
839: ),
840: ),
841: ),
842: ),
843: ),
844: ),
845: ),
846: 'admin' => array(
847: 'desc' => 'Bind with administrative/system credentials',
848: 'fields' => array(
849: 'binddn' => array(
850: '_type' => 'text',
851: 'required' => true,
852: 'desc' => 'DN used to bind to LDAP',
853: 'default' => $this->_default($ctx . '|binddn', '')
854: ),
855: 'bindpw' => array(
856: '_type' => 'text',
857: 'required' => true,
858: 'desc' => 'Password for bind DN',
859: 'default' => $this->_default($ctx . '|bindpw', '')
860: )
861: )
862: ),
863: )
864: ),
865: );
866:
867: if (isset($node) && $node->getAttribute('excludebind')) {
868: $excludes = explode(',', $node->getAttribute('excludebind'));
869: foreach ($excludes as $exclude) {
870: unset($fields['bindas']['switch'][$exclude]);
871: }
872: }
873:
874: if (isset($node) && $node->getAttribute('baseconfig') == 'true') {
875: return array(
876: 'desc' => 'Use LDAP?',
877: 'default' => $this->_default($ctx . '|' . $node->getAttribute('switchname'), false),
878: 'switch' => array(
879: 'false' => array(
880: 'desc' => 'No',
881: 'fields' => array()
882: ),
883: 'true' => array(
884: 'desc' => 'Yes',
885: 'fields' => $fields
886: ),
887: )
888: );
889: }
890:
891: $standardFields = array(
892: 'basedn' => array(
893: '_type' => 'text',
894: 'required' => true,
895: 'desc' => 'Base DN',
896: 'default' => $this->_default($ctx . '|basedn', '')
897: ),
898: 'scope' => array(
899: '_type' => 'enum',
900: 'required' => true,
901: 'desc' => 'Search scope',
902: 'default' => $this->_default($ctx . '|scope', ''),
903: 'values' => array(
904: 'sub' => 'Subtree search',
905: 'one' => 'One level'),
906: ),
907: );
908:
909: list($default, $isDefault) = $this->__default($ctx . '|' . (isset($node) ? $node->getAttribute('switchname') : $switchname), 'horde');
910: $config = array(
911: 'desc' => 'Driver configuration',
912: 'default' => $default,
913: 'is_default' => $isDefault,
914: 'switch' => array(
915: 'horde' => array(
916: 'desc' => 'Horde defaults',
917: 'fields' => $standardFields,
918: ),
919: 'custom' => array(
920: 'desc' => 'Custom parameters',
921: 'fields' => $fields + $standardFields,
922: )
923: )
924: );
925:
926: if (isset($node) && $node->hasChildNodes()) {
927: $cur = array();
928: $this->_parseLevel($cur, $node->childNodes, $ctx);
929: $config['switch']['horde']['fields'] = array_merge($config['switch']['horde']['fields'], $cur);
930: $config['switch']['custom']['fields'] = array_merge($config['switch']['custom']['fields'], $cur);
931: }
932:
933: return $config;
934: }
935:
936: 937: 938: 939: 940: 941: 942: 943: 944: 945: 946: 947: 948: 949:
950: public function configSQL($ctx, $node = null, $switchname = 'driverconfig')
951: {
952: $persistent = array(
953: '_type' => 'boolean',
954: 'required' => false,
955: 'desc' => 'Request persistent connections?',
956: 'default' => $this->_default($ctx . '|persistent', false)
957: );
958:
959: $hostspec = array(
960: '_type' => 'text',
961: 'required' => true,
962: 'desc' => 'Database server/host',
963: 'default' => $this->_default($ctx . '|hostspec', '')
964: );
965:
966: $username = array(
967: '_type' => 'text',
968: 'required' => true,
969: 'desc' => 'Username to connect to the database as',
970: 'default' => $this->_default($ctx . '|username', '')
971: );
972:
973: $password = array(
974: '_type' => 'text',
975: 'required' => false,
976: 'desc' => 'Password to connect with',
977: 'default' => $this->_default($ctx . '|password', '')
978: );
979:
980: $database = array(
981: '_type' => 'text',
982: 'required' => true,
983: 'desc' => 'Database name to use',
984: 'default' => $this->_default($ctx . '|database', '')
985: );
986:
987: $socket = array(
988: '_type' => 'text',
989: 'required' => false,
990: 'desc' => 'Location of UNIX socket',
991: 'default' => $this->_default($ctx . '|socket', '')
992: );
993:
994: $port = array(
995: '_type' => 'int',
996: 'required' => false,
997: 'desc' => 'Port the DB is running on, if non-standard',
998: 'default' => $this->_default($ctx . '|port', null)
999: );
1000:
1001: $protocol = array(
1002: 'desc' => 'How should we connect to the database?',
1003: 'default' => $this->_default($ctx . '|protocol', 'unix'),
1004: 'switch' => array(
1005: 'unix' => array(
1006: 'desc' => 'UNIX Sockets',
1007: 'fields' => array(
1008: 'socket' => $socket
1009: )
1010: ),
1011: 'tcp' => array(
1012: 'desc' => 'TCP/IP',
1013: 'fields' => array(
1014: 'hostspec' => $hostspec,
1015: 'port' => $port
1016: )
1017: )
1018: )
1019: );
1020:
1021: $mysql_protocol = $protocol;
1022: $mysql_protocol['switch']['tcp']['fields']['port']['default'] = $this->_default($ctx . '|port', 3306);
1023:
1024: $charset = array(
1025: '_type' => 'text',
1026: 'required' => true,
1027: 'desc' => 'Internally used charset',
1028: 'default' => $this->_default($ctx . '|charset', 'utf-8')
1029: );
1030:
1031: $ssl = array(
1032: '_type' => 'boolean',
1033: 'required' => false,
1034: 'desc' => 'Use SSL to connect to the server?',
1035: 'default' => $this->_default($ctx . '|ssl', false)
1036: );
1037:
1038: $ca = array(
1039: '_type' => 'text',
1040: 'required' => false,
1041: 'desc' => 'Certification Authority to use for SSL connections',
1042: 'default' => $this->_default($ctx . '|ca', '')
1043: );
1044:
1045: $read_hostspec = array(
1046: '_type' => 'text',
1047: 'required' => true,
1048: 'desc' => 'Read database server/host',
1049: 'default' => $this->_default($ctx . '|read|hostspec', '')
1050: );
1051:
1052: $read_port = array(
1053: '_type' => 'int',
1054: 'required' => false,
1055: 'desc' => 'Port the read DB is running on, if non-standard',
1056: 'default' => $this->_default($ctx . '|read|port', null)
1057: );
1058:
1059: $splitread = array(
1060: '_type' => 'boolean',
1061: 'required' => false,
1062: 'desc' => 'Split reads to a different server?',
1063: 'default' => $this->_default($ctx . '|splitread', 'false'),
1064: 'switch' => array(
1065: 'false' => array(
1066: 'desc' => 'Disabled',
1067: 'fields' => array()
1068: ),
1069: 'true' => array(
1070: 'desc' => 'Enabled',
1071: 'fields' => array(
1072: 'read' => array(
1073: 'persistent' => $persistent,
1074: 'username' => $username,
1075: 'password' => $password,
1076: 'protocol' => $protocol,
1077: 'database' => $database,
1078: 'charset' => $charset
1079: )
1080: )
1081: )
1082: )
1083: );
1084:
1085: $custom_fields = array(
1086: 'required' => true,
1087: 'desc' => 'What database backend should we use?',
1088: 'default' => $this->_default($ctx . '|phptype', ''),
1089: 'switch' => array(
1090: 'false' => array(
1091: 'desc' => '[None]',
1092: 'fields' => array()
1093: ),
1094: 'mysql' => array(
1095: 'desc' => 'MySQL / PDO',
1096: 'fields' => array(
1097: 'persistent' => $persistent,
1098: 'username' => $username,
1099: 'password' => $password,
1100: 'protocol' => $mysql_protocol,
1101: 'database' => $database,
1102: 'charset' => $charset,
1103: 'ssl' => $ssl,
1104: 'ca' => $ca,
1105: 'splitread' => Horde_Array::replaceRecursive(
1106: $splitread,
1107: array(
1108: 'switch' => array(
1109: 'true' => array(
1110: 'fields' => array(
1111: 'read' => array(
1112: 'protocol' => $mysql_protocol,
1113: )
1114: )
1115: )
1116: )
1117: )
1118: )
1119: )
1120: ),
1121: 'mysqli' => array(
1122: 'desc' => 'MySQL (mysqli)',
1123: 'fields' => array(
1124: 'username' => $username,
1125: 'password' => $password,
1126: 'protocol' => $mysql_protocol,
1127: 'database' => $database,
1128: 'charset' => $charset,
1129: 'ssl' => $ssl,
1130: 'ca' => $ca,
1131: 'splitread' => Horde_Array::replaceRecursive(
1132: $splitread,
1133: array(
1134: 'switch' => array(
1135: 'true' => array(
1136: 'fields' => array(
1137: 'read' => array(
1138: 'protocol' => $mysql_protocol,
1139: )
1140: )
1141: )
1142: )
1143: )
1144: )
1145: )
1146: ),
1147: 'pgsql' => array(
1148: 'desc' => 'PostgreSQL',
1149: 'fields' => array(
1150: 'persistent' => $persistent,
1151: 'username' => $username,
1152: 'password' => $password,
1153: 'protocol' => $protocol,
1154: 'database' => $database,
1155: 'charset' => $charset,
1156: 'splitread' => $splitread,
1157: )
1158: ),
1159: 'sqlite' => array(
1160: 'desc' => 'SQLite',
1161: 'fields' => array(
1162: 'database' => array(
1163: '_type' => 'text',
1164: 'required' => true,
1165: 'desc' => 'Absolute path to the database file',
1166: 'default' => $this->_default($ctx . '|database', '')
1167: ),
1168: 'charset' => $charset
1169: )
1170: )
1171: )
1172: );
1173:
1174: if (isset($node) && $node->getAttribute('baseconfig') == 'true') {
1175: return $custom_fields;
1176: }
1177:
1178: list($default, $isDefault) = $this->__default($ctx . '|' . (isset($node) ? $node->getAttribute('switchname') : $switchname), 'horde');
1179: $config = array(
1180: 'desc' => 'Driver configuration',
1181: 'default' => $default,
1182: 'is_default' => $isDefault,
1183: 'switch' => array(
1184: 'horde' => array(
1185: 'desc' => 'Horde defaults',
1186: 'fields' => array()
1187: ),
1188: 'custom' => array(
1189: 'desc' => 'Custom parameters',
1190: 'fields' => array(
1191: 'phptype' => $custom_fields
1192: )
1193: )
1194: )
1195: );
1196:
1197: if (isset($node) && $node->hasChildNodes()) {
1198: $cur = array();
1199: $this->_parseLevel($cur, $node->childNodes, $ctx);
1200: $config['switch']['horde']['fields'] = array_merge($config['switch']['horde']['fields'], $cur);
1201: $config['switch']['custom']['fields'] = array_merge($config['switch']['custom']['fields'], $cur);
1202: }
1203:
1204: return $config;
1205: }
1206:
1207: 1208: 1209: 1210: 1211: 1212: 1213: 1214: 1215: 1216: 1217: 1218:
1219: protected function _configVFS($ctx, $node)
1220: {
1221: $sql = $this->configSQL($ctx . '|params');
1222: $default = $node->getAttribute('default');
1223: $default = empty($default) ? 'horde' : $default;
1224: list($default, $isDefault) = $this->__default($ctx . '|' . $node->getAttribute('switchname'), $default);
1225:
1226: $config = array(
1227: 'desc' => 'What VFS driver should we use?',
1228: 'default' => $default,
1229: 'is_default' => $isDefault,
1230: 'switch' => array(
1231: 'File' => array(
1232: 'desc' => 'Files on the local system',
1233: 'fields' => array(
1234: 'params' => array(
1235: 'vfsroot' => array(
1236: '_type' => 'text',
1237: 'desc' => 'Where on the real filesystem should Horde use as root of the virtual filesystem?',
1238: 'default' => $this->_default($ctx . '|params|vfsroot', '/tmp')
1239: )
1240: )
1241: )
1242: ),
1243: 'Sql' => array(
1244: 'desc' => 'SQL database',
1245: 'fields' => array(
1246: 'params' => array(
1247: 'driverconfig' => $sql
1248: )
1249: )
1250: ),
1251: 'Ssh2' => array(
1252: 'desc' => 'SSH2 (SFTP)',
1253: 'fields' => array(
1254: 'params' => array(
1255: 'hostspec' => array(
1256: '_type' => 'text',
1257: 'required' => true,
1258: 'desc' => 'SSH server/host',
1259: 'default' => $this->_default($ctx . '|hostspec', '')
1260: ),
1261: 'port' => array(
1262: '_type' => 'text',
1263: 'required' => false,
1264: 'desc' => 'Port number on which SSH listens',
1265: 'default' => $this->_default($ctx . '|port', '22')
1266: ),
1267: 'username' => array(
1268: '_type' => 'text',
1269: 'required' => true,
1270: 'desc' => 'Username to connect to the SSH server',
1271: 'default' => $this->_default($ctx . '|username', '')
1272: ),
1273: 'password' => array(
1274: '_type' => 'text',
1275: 'required' => true,
1276: 'desc' => 'Password with which to connect',
1277: 'default' => $this->_default($ctx . '|password', '')
1278: ),
1279: 'vfsroot' => array(
1280: '_type' => 'text',
1281: 'desc' => 'Where on the real filesystem should Horde use as root of the virtual filesystem?',
1282: 'default' => $this->_default($ctx . '|vfsroot', '/tmp')
1283: )
1284: )
1285: )
1286: )
1287: )
1288: );
1289:
1290: if (isset($node) && $node->getAttribute('baseconfig') != 'true') {
1291: $config['switch']['horde'] = array(
1292: 'desc' => 'Horde defaults',
1293: 'fields' => array()
1294: );
1295: }
1296: $cases = $this->_getSwitchValues($node, $ctx . '|params');
1297: foreach ($cases as $case => $fields) {
1298: if (isset($config['switch'][$case])) {
1299: $config['switch'][$case]['fields']['params'] = array_merge($config['switch'][$case]['fields']['params'], $fields['fields']);
1300: }
1301: }
1302:
1303: return $config;
1304: }
1305:
1306: 1307: 1308: 1309: 1310: 1311: 1312: 1313: 1314: 1315: 1316: 1317:
1318: protected function _default($ctx, $default)
1319: {
1320: list ($ptr,) = $this->__default($ctx, $default);
1321: return $ptr;
1322: }
1323:
1324: 1325: 1326: 1327: 1328: 1329: 1330: 1331: 1332: 1333: 1334:
1335: protected function _isDefault($ctx, $default)
1336: {
1337: list (,$isDefault) = $this->__default($ctx, $default);
1338: return $isDefault;
1339: }
1340:
1341: 1342: 1343: 1344: 1345: 1346: 1347: 1348: 1349: 1350: 1351: 1352: 1353: 1354: 1355: 1356:
1357: protected function __default($ctx, $default)
1358: {
1359: $ctx = explode('|', $ctx);
1360: $ptr = $this->_currentConfig;
1361:
1362: for ($i = 0, $ctx_count = count($ctx); $i < $ctx_count; ++$i) {
1363: if (!isset($ptr[$ctx[$i]])) {
1364: return array($default, true);
1365: }
1366:
1367: $ptr = $ptr[$ctx[$i]];
1368: }
1369:
1370: return array($ptr, false);
1371: }
1372:
1373: 1374: 1375: 1376: 1377: 1378: 1379: 1380: 1381: 1382: 1383: 1384: 1385: 1386:
1387: protected function _defaultRaw($ctx, $default)
1388: {
1389: list ($ptr,) = $this->__defaultRaw($ctx, $default);
1390: return $ptr;
1391: }
1392:
1393: 1394: 1395: 1396: 1397: 1398: 1399: 1400: 1401: 1402: 1403:
1404: protected function _isDefaultRaw($ctx, $default)
1405: {
1406: list (,$isDefault) = $this->__defaultRaw($ctx, $default);
1407: return $isDefault;
1408: }
1409:
1410: 1411: 1412: 1413: 1414: 1415: 1416: 1417: 1418: 1419: 1420: 1421: 1422: 1423: 1424: 1425: 1426: 1427: 1428:
1429: protected function __defaultRaw($ctx, $default)
1430: {
1431: $ctx = explode('|', $ctx);
1432: $pattern = '/^\$conf\[\'' . implode("'\]\['", $ctx) . '\'\] = (.*);\r?$/m';
1433:
1434: return preg_match($pattern, $this->getPHPConfig(), $matches)
1435: ? array($matches[1], false)
1436: : array($default, true);
1437: }
1438:
1439: 1440: 1441: 1442: 1443: 1444: 1445: 1446:
1447: protected function _getNodeOnlyText($node)
1448: {
1449: $text = '';
1450:
1451: if (!$node->hasChildNodes()) {
1452: return $node->textContent;
1453: }
1454:
1455: foreach ($node->childNodes as $tnode) {
1456: if ($tnode->nodeType == XML_TEXT_NODE) {
1457: $text .= $tnode->textContent;
1458: }
1459: }
1460:
1461: return trim($text);
1462: }
1463:
1464: 1465: 1466: 1467: 1468: 1469: 1470: 1471: 1472: 1473: 1474: 1475:
1476: protected function _getEnumValues($node)
1477: {
1478: $values = array();
1479:
1480: if (!$node->hasChildNodes()) {
1481: return $values;
1482: }
1483:
1484: foreach ($node->childNodes as $vnode) {
1485: if ($vnode->nodeType == XML_ELEMENT_NODE &&
1486: $vnode->tagName == 'values') {
1487: if (!$vnode->hasChildNodes()) {
1488: return array();
1489: }
1490:
1491: foreach ($vnode->childNodes as $value) {
1492: if ($value->nodeType == XML_ELEMENT_NODE) {
1493: if ($value->tagName == 'configspecial') {
1494: return $this->_handleSpecials($value);
1495: }
1496: if ($value->tagName == 'value') {
1497: $text = $value->textContent;
1498: $desc = $value->getAttribute('desc');
1499: $values[$text] = empty($desc) ? $text : $desc;
1500: }
1501: }
1502: }
1503: }
1504: }
1505:
1506: return $values;
1507: }
1508:
1509: 1510: 1511: 1512: 1513: 1514: 1515: 1516: 1517:
1518: protected function _getSwitchValues(&$node, $curctx)
1519: {
1520: $values = array();
1521:
1522: if (!$node->hasChildNodes()) {
1523: return $values;
1524: }
1525:
1526: foreach ($node->childNodes as $case) {
1527: if ($case->nodeType == XML_ELEMENT_NODE) {
1528: $name = $case->getAttribute('name');
1529: $values[$name] = array(
1530: 'desc' => $case->getAttribute('desc'),
1531: 'fields' => array()
1532: );
1533: if ($case->hasChildNodes()) {
1534: $this->_parseLevel($values[$name]['fields'], $case->childNodes, $curctx);
1535: }
1536: }
1537: }
1538:
1539: return $values;
1540: }
1541:
1542: 1543: 1544: 1545: 1546: 1547: 1548: 1549: 1550:
1551: protected function _handleSpecials($node)
1552: {
1553: $app = $node->getAttribute('application');
1554: if (!in_array($app, $GLOBALS['registry']->listApps())) {
1555: $app = $GLOBALS['registry']->hasInterface($app);
1556: }
1557: if (!$app) {
1558: return array();
1559: }
1560: return $GLOBALS['registry']->callAppMethod($app, 'configSpecialValues', array('args' => array($node->getAttribute('name')), 'noperms' => true));
1561: }
1562:
1563: 1564: 1565: 1566: 1567: 1568: 1569:
1570: protected function _quote($string)
1571: {
1572: return str_replace("'", "\'", $string);
1573: }
1574:
1575: }
1576: