PageRenderTime 43ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/tags/stable-1.1.1/Client/Config/PHPIniFile.cs

#
C# | 674 lines | 578 code | 73 blank | 23 comment | 59 complexity | 014a4f59eff5ea8bc14168da40209486 MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. //-----------------------------------------------------------------------
  2. // <copyright>
  3. // Copyright (C) Ruslan Yakushev for the PHP Manager for IIS project.
  4. //
  5. // This file is subject to the terms and conditions of the Microsoft Public License (MS-PL).
  6. // See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL for more details.
  7. // </copyright>
  8. //-----------------------------------------------------------------------
  9. using System;
  10. using System.Collections;
  11. using System.Collections.Generic;
  12. using System.Globalization;
  13. using System.IO;
  14. using System.Text;
  15. namespace Web.Management.PHP.Config
  16. {
  17. internal sealed class PHPIniFile : IRemoteObject
  18. {
  19. private object[] _data;
  20. private const int IndexSettings = 0;
  21. private const int IndexExtensions = 1;
  22. private const int Size = 2;
  23. private RemoteObjectCollection<PHPIniSetting> _settings;
  24. private RemoteObjectCollection<PHPIniExtension> _extensions;
  25. private List<PHPIniBase> _rawEntries;
  26. private string _filename;
  27. public PHPIniFile()
  28. {
  29. _data = new object[Size];
  30. }
  31. public PHPIniFile(string filename)
  32. : this()
  33. {
  34. _filename = filename;
  35. }
  36. public RemoteObjectCollection<PHPIniExtension> Extensions
  37. {
  38. get
  39. {
  40. if (_extensions == null)
  41. {
  42. _extensions = new RemoteObjectCollection<PHPIniExtension>((ArrayList)_data[IndexExtensions]);
  43. }
  44. return _extensions;
  45. }
  46. }
  47. public string FileName
  48. {
  49. get
  50. {
  51. return _filename;
  52. }
  53. }
  54. private IList<PHPIniBase> RawEntries
  55. {
  56. get
  57. {
  58. if (_rawEntries == null)
  59. {
  60. _rawEntries = new List<PHPIniBase>();
  61. }
  62. return _rawEntries;
  63. }
  64. }
  65. public RemoteObjectCollection<PHPIniSetting> Settings
  66. {
  67. get
  68. {
  69. if (_settings == null)
  70. {
  71. _settings = new RemoteObjectCollection<PHPIniSetting>((ArrayList)_data[IndexSettings]);
  72. }
  73. return _settings;
  74. }
  75. }
  76. private void AddAllAvailableExtensions(string extensionDir)
  77. {
  78. DirectoryInfo di = new DirectoryInfo(extensionDir);
  79. FileInfo[] files = di.GetFiles("php*.dll");
  80. int extensionCount = Extensions.Count;
  81. foreach (FileInfo file in files)
  82. {
  83. bool found = false;
  84. for (int i = 0; i < extensionCount; i++)
  85. {
  86. if (String.Equals(Extensions[i].Name, file.Name, StringComparison.OrdinalIgnoreCase))
  87. {
  88. found = true;
  89. break;
  90. }
  91. }
  92. if (!found)
  93. {
  94. Extensions.Add(new PHPIniExtension(file.Name, false));
  95. }
  96. }
  97. }
  98. internal void AddOrUpdateSettings(IEnumerable<PHPIniSetting> settings)
  99. {
  100. foreach (PHPIniSetting setting in settings)
  101. {
  102. bool settingFound = false;
  103. int index = -1;
  104. int lastIndex = -1;
  105. for (int i = 0; i < RawEntries.Count; i++)
  106. {
  107. PHPIniBase b = RawEntries[i];
  108. PHPIniSetting existing = b as PHPIniSetting;
  109. if (existing != null)
  110. {
  111. lastIndex = i;
  112. if (String.Equals(existing.Name, setting.Name, StringComparison.OrdinalIgnoreCase))
  113. {
  114. existing.Value = setting.Value;
  115. existing.UpdateText();
  116. settingFound = true;
  117. break;
  118. }
  119. // This finds the index after the last setting for a given section
  120. if (String.Equals(existing.Section, setting.Section, StringComparison.OrdinalIgnoreCase))
  121. {
  122. index = i;
  123. }
  124. }
  125. else
  126. {
  127. // This finds the index after section declaration,
  128. // in case there are no settings defined in that section
  129. PHPIniSection section = b as PHPIniSection;
  130. if ((section != null) && (String.Equals(section.Name, setting.Section, StringComparison.OrdinalIgnoreCase)))
  131. {
  132. index = i;
  133. }
  134. }
  135. }
  136. if (!settingFound)
  137. {
  138. setting.UpdateText();
  139. if (index == -1)
  140. {
  141. lastIndex++;
  142. RawEntries.Insert(lastIndex, new PHPIniString(""));
  143. lastIndex++;
  144. RawEntries.Insert(lastIndex, new PHPIniString('[' + setting.Section + ']'));
  145. lastIndex++;
  146. RawEntries.Insert(lastIndex, setting);
  147. }
  148. else
  149. {
  150. RawEntries.Insert(index + 1, setting);
  151. }
  152. }
  153. }
  154. }
  155. public object GetData()
  156. {
  157. if (_settings != null)
  158. {
  159. _data[IndexSettings] = _settings.GetData();
  160. }
  161. if (_extensions != null)
  162. {
  163. _data[IndexExtensions] = _extensions.GetData();
  164. }
  165. return _data;
  166. }
  167. internal int GetEnabledExtensionsCount()
  168. {
  169. int result = 0;
  170. foreach (PHPIniExtension extension in Extensions)
  171. {
  172. if (extension.Enabled)
  173. {
  174. result++;
  175. }
  176. }
  177. return result;
  178. }
  179. private static string GetExtensionSection(string extensionName)
  180. {
  181. string sectionName = Path.GetFileNameWithoutExtension(extensionName).ToUpper(CultureInfo.InvariantCulture);
  182. return '[' + sectionName + ']';
  183. }
  184. internal PHPIniSetting GetSetting(string name)
  185. {
  186. foreach (PHPIniSetting setting in Settings)
  187. {
  188. if (String.Equals(setting.Name, name, StringComparison.OrdinalIgnoreCase))
  189. {
  190. return setting;
  191. }
  192. }
  193. return null;
  194. }
  195. internal void Parse()
  196. {
  197. if (String.IsNullOrEmpty(FileName))
  198. {
  199. throw new InvalidOperationException();
  200. }
  201. string extensionDir = String.Empty;
  202. using (StreamReader reader = new StreamReader(FileName))
  203. {
  204. foreach (object o in PHPIniFile.ParseIniFile(reader))
  205. {
  206. PHPIniSetting directive = o as PHPIniSetting;
  207. if (directive != null)
  208. {
  209. Settings.Add(directive);
  210. RawEntries.Add(directive);
  211. // Get the path to the extension directory - this will be used later
  212. if (String.Equals(directive.Name, "extension_dir", StringComparison.OrdinalIgnoreCase))
  213. {
  214. extensionDir = directive.TrimmedValue;
  215. }
  216. }
  217. else
  218. {
  219. PHPIniExtension extension = o as PHPIniExtension;
  220. if (extension != null)
  221. {
  222. Extensions.Add(extension);
  223. RawEntries.Add(extension);
  224. }
  225. else
  226. {
  227. PHPIniBase entry = o as PHPIniBase;
  228. if (entry != null)
  229. {
  230. RawEntries.Add(entry);
  231. }
  232. }
  233. }
  234. }
  235. }
  236. if (String.IsNullOrEmpty(extensionDir) ||
  237. !Path.IsPathRooted(extensionDir))
  238. {
  239. extensionDir = Path.Combine(Path.GetDirectoryName(FileName), "ext");
  240. }
  241. if (Directory.Exists(extensionDir))
  242. {
  243. AddAllAvailableExtensions(extensionDir);
  244. }
  245. }
  246. private static IEnumerable<PHPIniBase> ParseIniFile(TextReader reader)
  247. {
  248. string section = String.Empty;
  249. string line = reader.ReadLine();
  250. while (line != null)
  251. {
  252. string tmp = line.Trim();
  253. // Process comments
  254. if (tmp.StartsWith(";", StringComparison.OrdinalIgnoreCase))
  255. {
  256. yield return new PHPIniString(line);
  257. }
  258. // Process section
  259. else if (tmp.StartsWith("[", StringComparison.OrdinalIgnoreCase))
  260. {
  261. int startindex = tmp.IndexOf('[');
  262. int endindex = tmp.IndexOf(']');
  263. if ((startindex >= 0) && (endindex > startindex))
  264. {
  265. string name = tmp.Substring(startindex + 1, endindex - startindex - 1);
  266. section = name;
  267. yield return new PHPIniSection(name, line);
  268. }
  269. }
  270. // Process the settings and extensions
  271. else if (!String.IsNullOrEmpty(tmp))
  272. {
  273. string[] split = tmp.Split(new Char[] { '=' }, 2);
  274. string name = split[0].Trim();
  275. string value = String.Empty;
  276. if (split.Length > 1)
  277. {
  278. value = RemoveInlineComment(split[1].Trim());
  279. }
  280. else
  281. {
  282. name = RemoveInlineComment(name);
  283. }
  284. if (String.Equals(name, "extension", StringComparison.OrdinalIgnoreCase) && !String.IsNullOrEmpty(value))
  285. {
  286. yield return new PHPIniExtension(value, true, line);
  287. }
  288. else
  289. {
  290. yield return new PHPIniSetting(name, value, section, line);
  291. }
  292. }
  293. else
  294. {
  295. // Return empty comment by default
  296. yield return new PHPIniString(line);
  297. }
  298. line = reader.ReadLine();
  299. }
  300. }
  301. internal bool Remove(PHPIniBase entry)
  302. {
  303. return RawEntries.Remove(entry);
  304. }
  305. private static string RemoveInlineComment(string line)
  306. {
  307. // Take care of the case when value is wrapped in quotes
  308. if (line.StartsWith("\"", StringComparison.OrdinalIgnoreCase) &&
  309. line.EndsWith("\"", StringComparison.OrdinalIgnoreCase))
  310. {
  311. return line;
  312. }
  313. int commentIndex = line.IndexOf(';');
  314. if (commentIndex >= 0)
  315. {
  316. return line.Substring(0, commentIndex);
  317. }
  318. return line;
  319. }
  320. internal void Save(string filename)
  321. {
  322. if (String.IsNullOrEmpty(filename))
  323. {
  324. throw new ArgumentNullException("filename");
  325. }
  326. using (StreamWriter writer = new StreamWriter(filename))
  327. {
  328. foreach (PHPIniBase entry in RawEntries)
  329. {
  330. writer.WriteLine(entry.Text);
  331. }
  332. }
  333. }
  334. public void SetData(object o)
  335. {
  336. _data = (object[])o;
  337. }
  338. internal void UpdateExtensions(IEnumerable<PHPIniExtension> extensions)
  339. {
  340. foreach (PHPIniExtension extension in extensions)
  341. {
  342. int foundIndex = -1;
  343. for (int i = 0; i < RawEntries.Count; i++)
  344. {
  345. PHPIniBase b = RawEntries[i];
  346. PHPIniExtension existing = b as PHPIniExtension;
  347. if (existing != null)
  348. {
  349. if (String.Equals(existing.Name, extension.Name, StringComparison.OrdinalIgnoreCase))
  350. {
  351. foundIndex = i;
  352. break;
  353. }
  354. }
  355. }
  356. // If extension is found...
  357. if (foundIndex >= 0)
  358. {
  359. // ... and is disabled then...
  360. if (!extension.Enabled)
  361. {
  362. PHPIniBase extensionLine = RawEntries[foundIndex];
  363. // ... remove the extension section name if it exists
  364. if (foundIndex > 0 &&
  365. String.Equals(RawEntries[foundIndex - 1].Text, GetExtensionSection(extension.Name), StringComparison.OrdinalIgnoreCase))
  366. {
  367. RawEntries.Remove(RawEntries[foundIndex - 1]);
  368. }
  369. // remove the exension
  370. RawEntries.Remove(extensionLine);
  371. }
  372. }
  373. else
  374. {
  375. // Extension is not found
  376. if (extension.Enabled)
  377. {
  378. extension.UpdateText();
  379. // Add it at the end of the file along with the extension section name
  380. int lastIndex = RawEntries.Count - 1;
  381. lastIndex++;
  382. RawEntries.Insert(lastIndex, new PHPIniString(GetExtensionSection(extension.Name)));
  383. lastIndex++;
  384. RawEntries.Insert(lastIndex, extension);
  385. }
  386. }
  387. }
  388. }
  389. }
  390. internal abstract class PHPIniBase
  391. {
  392. private string _text;
  393. protected PHPIniBase() { }
  394. protected PHPIniBase(string text)
  395. {
  396. _text = text;
  397. }
  398. public string Text
  399. {
  400. get
  401. {
  402. return _text;
  403. }
  404. set
  405. {
  406. _text = value;
  407. }
  408. }
  409. }
  410. internal class PHPIniString : PHPIniBase
  411. {
  412. public PHPIniString() { }
  413. public PHPIniString(string rawText) : base(rawText) { }
  414. }
  415. internal class PHPIniSetting : PHPIniBase, IRemoteObject
  416. {
  417. private object[] _data;
  418. private const int Size = 3;
  419. private const int IndexName = 0;
  420. private const int IndexValue = 1;
  421. private const int IndexSection = 2;
  422. public PHPIniSetting()
  423. {
  424. _data = new object[Size];
  425. }
  426. public PHPIniSetting(string name, string value, string section) : this(name, value, section, String.Empty) { }
  427. public PHPIniSetting(string name, string value, string section, string rawText)
  428. : base(rawText)
  429. {
  430. _data = new object[Size];
  431. Name = name;
  432. Value = value;
  433. Section = section;
  434. }
  435. public string Name
  436. {
  437. get
  438. {
  439. return (string)_data[IndexName];
  440. }
  441. set
  442. {
  443. _data[IndexName] = value;
  444. }
  445. }
  446. public string Section
  447. {
  448. get
  449. {
  450. return (string)_data[IndexSection];
  451. }
  452. set
  453. {
  454. _data[IndexSection] = value;
  455. }
  456. }
  457. public string Value
  458. {
  459. get
  460. {
  461. return (string)_data[IndexValue];
  462. }
  463. set
  464. {
  465. _data[IndexValue] = value;
  466. }
  467. }
  468. public string TrimmedValue
  469. {
  470. get
  471. {
  472. string result = (string)_data[IndexValue];
  473. return result.Trim(new char[] {' ', '"' });
  474. }
  475. }
  476. public override bool Equals(object obj)
  477. {
  478. PHPIniSetting setting = obj as PHPIniSetting;
  479. if (setting == null)
  480. {
  481. return false;
  482. }
  483. return (String.Equals(setting.Name, Name, StringComparison.OrdinalIgnoreCase) &&
  484. String.Equals(setting.Value, Value, StringComparison.OrdinalIgnoreCase) &&
  485. String.Equals(setting.Section, Section, StringComparison.OrdinalIgnoreCase));
  486. }
  487. public object GetData()
  488. {
  489. return _data;
  490. }
  491. public override int GetHashCode()
  492. {
  493. return Name.GetHashCode() ^ Value.GetHashCode() ^ Section.GetHashCode();
  494. }
  495. #region IRemoteObject Members
  496. public void SetData(object o)
  497. {
  498. _data = (object[])o;
  499. }
  500. #endregion
  501. internal void UpdateText()
  502. {
  503. Text = Name + " = " + Value;
  504. }
  505. }
  506. internal class PHPIniExtension : PHPIniBase, IRemoteObject
  507. {
  508. private object[] _data;
  509. private const int Size = 2;
  510. private const int IndexName = 0;
  511. private const int IndexEnabled = 1;
  512. public PHPIniExtension()
  513. {
  514. _data = new object[Size];
  515. Enabled = false;
  516. }
  517. public PHPIniExtension(string filename, bool enabled): this(filename, enabled, String.Empty) { }
  518. public PHPIniExtension(string filename, bool enabled, string rawText): base(rawText)
  519. {
  520. _data = new object[Size];
  521. Name = filename;
  522. Enabled = enabled;
  523. }
  524. public bool Enabled
  525. {
  526. get
  527. {
  528. return (bool)_data[IndexEnabled];
  529. }
  530. set
  531. {
  532. _data[IndexEnabled] = value;
  533. }
  534. }
  535. public string Name
  536. {
  537. get
  538. {
  539. return (string)_data[IndexName];
  540. }
  541. set
  542. {
  543. _data[IndexName] = value;
  544. }
  545. }
  546. public object GetData()
  547. {
  548. return _data;
  549. }
  550. public void SetData(object o)
  551. {
  552. _data = (object[])o;
  553. }
  554. internal void UpdateText()
  555. {
  556. Text = "extension=" + Name;
  557. }
  558. }
  559. internal class PHPIniSection : PHPIniBase
  560. {
  561. private string _name;
  562. public PHPIniSection() { }
  563. public PHPIniSection(string name, string rawText) : base(rawText)
  564. {
  565. Name = name;
  566. }
  567. public string Name
  568. {
  569. get
  570. {
  571. return _name;
  572. }
  573. set
  574. {
  575. _name = value;
  576. }
  577. }
  578. }
  579. }