PageRenderTime 56ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

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

#
C# | 648 lines | 553 code | 72 blank | 23 comment | 57 complexity | b61e3900a4e755b5b7b54a3e342fa43c 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. if (String.Equals(existing.Section, setting.Section, StringComparison.OrdinalIgnoreCase))
  120. {
  121. index = i;
  122. }
  123. }
  124. }
  125. if (!settingFound)
  126. {
  127. setting.UpdateText();
  128. if (index == -1)
  129. {
  130. lastIndex++;
  131. RawEntries.Insert(lastIndex, new PHPIniString(""));
  132. lastIndex++;
  133. RawEntries.Insert(lastIndex, new PHPIniString('[' + setting.Section + ']'));
  134. lastIndex++;
  135. RawEntries.Insert(lastIndex, setting);
  136. }
  137. else
  138. {
  139. RawEntries.Insert(index + 1, setting);
  140. }
  141. }
  142. }
  143. }
  144. public object GetData()
  145. {
  146. if (_settings != null)
  147. {
  148. _data[IndexSettings] = _settings.GetData();
  149. }
  150. if (_extensions != null)
  151. {
  152. _data[IndexExtensions] = _extensions.GetData();
  153. }
  154. return _data;
  155. }
  156. internal int GetEnabledExtensionsCount()
  157. {
  158. int result = 0;
  159. foreach (PHPIniExtension extension in Extensions)
  160. {
  161. if (extension.Enabled)
  162. {
  163. result++;
  164. }
  165. }
  166. return result;
  167. }
  168. private static string GetExtensionSection(string extensionName)
  169. {
  170. string sectionName = Path.GetFileNameWithoutExtension(extensionName).ToUpper(CultureInfo.InvariantCulture);
  171. return '[' + sectionName + ']';
  172. }
  173. internal PHPIniSetting GetSetting(string name)
  174. {
  175. foreach (PHPIniSetting setting in Settings)
  176. {
  177. if (String.Equals(setting.Name, name, StringComparison.OrdinalIgnoreCase))
  178. {
  179. return setting;
  180. }
  181. }
  182. return null;
  183. }
  184. internal void Parse()
  185. {
  186. if (String.IsNullOrEmpty(FileName))
  187. {
  188. throw new InvalidOperationException();
  189. }
  190. string extensionDir = String.Empty;
  191. using (StreamReader reader = new StreamReader(FileName))
  192. {
  193. foreach (object o in PHPIniFile.ParseIniFile(reader))
  194. {
  195. PHPIniSetting directive = o as PHPIniSetting;
  196. if (directive != null)
  197. {
  198. Settings.Add(directive);
  199. RawEntries.Add(directive);
  200. // Get the path to the extension directory - this will be used later
  201. if (String.Equals(directive.Name, "extension_dir", StringComparison.OrdinalIgnoreCase))
  202. {
  203. extensionDir = directive.TrimmedValue;
  204. }
  205. }
  206. else
  207. {
  208. PHPIniExtension extension = o as PHPIniExtension;
  209. if (extension != null)
  210. {
  211. Extensions.Add(extension);
  212. RawEntries.Add(extension);
  213. }
  214. else
  215. {
  216. PHPIniBase entry = o as PHPIniBase;
  217. if (entry != null)
  218. {
  219. RawEntries.Add(entry);
  220. }
  221. }
  222. }
  223. }
  224. }
  225. if (String.IsNullOrEmpty(extensionDir) ||
  226. !Path.IsPathRooted(extensionDir))
  227. {
  228. extensionDir = Path.Combine(Path.GetDirectoryName(FileName), "ext");
  229. }
  230. if (Directory.Exists(extensionDir))
  231. {
  232. AddAllAvailableExtensions(extensionDir);
  233. }
  234. }
  235. private static IEnumerable<PHPIniBase> ParseIniFile(TextReader reader)
  236. {
  237. string section = String.Empty;
  238. string line = reader.ReadLine();
  239. while (line != null)
  240. {
  241. string tmp = line.Trim();
  242. // Process comments
  243. if (tmp.StartsWith(";", StringComparison.OrdinalIgnoreCase))
  244. {
  245. yield return new PHPIniString(line);
  246. }
  247. // Process section
  248. else if (tmp.StartsWith("[", StringComparison.OrdinalIgnoreCase))
  249. {
  250. int startindex = tmp.IndexOf('[');
  251. int endindex = tmp.IndexOf(']');
  252. if ((startindex >= 0) && (endindex > startindex))
  253. {
  254. string name = tmp.Substring(startindex + 1, endindex - startindex - 1);
  255. section = name;
  256. yield return new PHPIniSection(line);
  257. }
  258. }
  259. // Process an extension
  260. else if (tmp.StartsWith("extension=", StringComparison.OrdinalIgnoreCase))
  261. {
  262. // Take care of the inline comment
  263. tmp = RemoveInlineComment(tmp);
  264. string[] split = tmp.Split(new Char[] { '=' });
  265. if (split.Length == 2)
  266. {
  267. string fileName = split[1].Trim();
  268. yield return new PHPIniExtension(fileName, true, line);
  269. }
  270. else
  271. {
  272. // If extension file is not specified then treat the line as unusable
  273. yield return new PHPIniString(line);
  274. }
  275. }
  276. // Process a directive
  277. else if (!String.IsNullOrEmpty(tmp))
  278. {
  279. // Take care of the inline comment
  280. tmp = RemoveInlineComment(tmp);
  281. string[] split = tmp.Split(new Char[] { '=' });
  282. if (split.Length > 1)
  283. {
  284. yield return new PHPIniSetting(split[0].Trim(), split[1].Trim(), section, line);
  285. }
  286. else
  287. {
  288. yield return new PHPIniSetting(split[0].Trim(), String.Empty, section, line);
  289. }
  290. }
  291. else
  292. {
  293. // Return empty comment by default
  294. yield return new PHPIniString(line);
  295. }
  296. line = reader.ReadLine();
  297. }
  298. }
  299. internal bool Remove(PHPIniBase entry)
  300. {
  301. return RawEntries.Remove(entry);
  302. }
  303. private static string RemoveInlineComment(string line)
  304. {
  305. int commentIndex = line.IndexOf(';');
  306. if (commentIndex > 0)
  307. {
  308. return line.Substring(0, commentIndex);
  309. }
  310. return line;
  311. }
  312. internal void Save(string filename)
  313. {
  314. if (String.IsNullOrEmpty(filename))
  315. {
  316. throw new ArgumentNullException("filename");
  317. }
  318. using (StreamWriter writer = new StreamWriter(filename))
  319. {
  320. foreach (PHPIniBase entry in RawEntries)
  321. {
  322. writer.WriteLine(entry.Text);
  323. }
  324. }
  325. }
  326. public void SetData(object o)
  327. {
  328. _data = (object[])o;
  329. }
  330. internal void UpdateExtensions(IEnumerable<PHPIniExtension> extensions)
  331. {
  332. foreach (PHPIniExtension extension in extensions)
  333. {
  334. int foundIndex = -1;
  335. for (int i = 0; i < RawEntries.Count; i++)
  336. {
  337. PHPIniBase b = RawEntries[i];
  338. PHPIniExtension existing = b as PHPIniExtension;
  339. if (existing != null)
  340. {
  341. if (String.Equals(existing.Name, extension.Name, StringComparison.OrdinalIgnoreCase))
  342. {
  343. foundIndex = i;
  344. break;
  345. }
  346. }
  347. }
  348. // If extension is found...
  349. if (foundIndex >= 0)
  350. {
  351. // ... and is disabled then...
  352. if (!extension.Enabled)
  353. {
  354. PHPIniBase extensionLine = RawEntries[foundIndex];
  355. // ... remove the extension section name if it exists
  356. if (foundIndex > 0 &&
  357. String.Equals(RawEntries[foundIndex - 1].Text, GetExtensionSection(extension.Name), StringComparison.OrdinalIgnoreCase))
  358. {
  359. RawEntries.Remove(RawEntries[foundIndex - 1]);
  360. }
  361. // remove the exension
  362. RawEntries.Remove(extensionLine);
  363. }
  364. }
  365. else
  366. {
  367. // Extension is not found
  368. if (extension.Enabled)
  369. {
  370. extension.UpdateText();
  371. // Add it at the end of the file along with the extension section name
  372. int lastIndex = RawEntries.Count - 1;
  373. lastIndex++;
  374. RawEntries.Insert(lastIndex, new PHPIniString(GetExtensionSection(extension.Name)));
  375. lastIndex++;
  376. RawEntries.Insert(lastIndex, extension);
  377. }
  378. }
  379. }
  380. }
  381. }
  382. internal abstract class PHPIniBase
  383. {
  384. private string _text;
  385. protected PHPIniBase() { }
  386. protected PHPIniBase(string text)
  387. {
  388. _text = text;
  389. }
  390. public string Text
  391. {
  392. get
  393. {
  394. return _text;
  395. }
  396. set
  397. {
  398. _text = value;
  399. }
  400. }
  401. }
  402. internal class PHPIniString : PHPIniBase
  403. {
  404. public PHPIniString() { }
  405. public PHPIniString(string rawText) : base(rawText) { }
  406. }
  407. internal class PHPIniSetting : PHPIniBase, IRemoteObject
  408. {
  409. private object[] _data;
  410. private const int Size = 3;
  411. private const int IndexName = 0;
  412. private const int IndexValue = 1;
  413. private const int IndexSection = 2;
  414. public PHPIniSetting()
  415. {
  416. _data = new object[Size];
  417. }
  418. public PHPIniSetting(string name, string value, string section) : this(name, value, section, String.Empty) { }
  419. public PHPIniSetting(string name, string value, string section, string rawText)
  420. : base(rawText)
  421. {
  422. _data = new object[Size];
  423. Name = name;
  424. Value = value;
  425. Section = section;
  426. }
  427. public string Name
  428. {
  429. get
  430. {
  431. return (string)_data[IndexName];
  432. }
  433. set
  434. {
  435. _data[IndexName] = value;
  436. }
  437. }
  438. public string Section
  439. {
  440. get
  441. {
  442. return (string)_data[IndexSection];
  443. }
  444. set
  445. {
  446. _data[IndexSection] = value;
  447. }
  448. }
  449. public string Value
  450. {
  451. get
  452. {
  453. return (string)_data[IndexValue];
  454. }
  455. set
  456. {
  457. _data[IndexValue] = value;
  458. }
  459. }
  460. public string TrimmedValue
  461. {
  462. get
  463. {
  464. string result = (string)_data[IndexValue];
  465. return result.Trim(new char[] {' ', '"' });
  466. }
  467. }
  468. public override bool Equals(object obj)
  469. {
  470. PHPIniSetting setting = obj as PHPIniSetting;
  471. if (setting == null)
  472. {
  473. return false;
  474. }
  475. return (String.Equals(setting.Name, Name, StringComparison.OrdinalIgnoreCase) &&
  476. String.Equals(setting.Value, Value, StringComparison.OrdinalIgnoreCase) &&
  477. String.Equals(setting.Section, Section, StringComparison.OrdinalIgnoreCase));
  478. }
  479. public object GetData()
  480. {
  481. return _data;
  482. }
  483. public override int GetHashCode()
  484. {
  485. return Name.GetHashCode() ^ Value.GetHashCode() ^ Section.GetHashCode();
  486. }
  487. #region IRemoteObject Members
  488. public void SetData(object o)
  489. {
  490. _data = (object[])o;
  491. }
  492. #endregion
  493. internal void UpdateText()
  494. {
  495. Text = Name + " = " + Value;
  496. }
  497. }
  498. internal class PHPIniExtension : PHPIniBase, IRemoteObject
  499. {
  500. private object[] _data;
  501. private const int Size = 2;
  502. private const int IndexName = 0;
  503. private const int IndexEnabled = 1;
  504. public PHPIniExtension()
  505. {
  506. _data = new object[Size];
  507. Enabled = false;
  508. }
  509. public PHPIniExtension(string filename, bool enabled): this(filename, enabled, String.Empty) { }
  510. public PHPIniExtension(string filename, bool enabled, string rawText): base(rawText)
  511. {
  512. _data = new object[Size];
  513. Name = filename;
  514. Enabled = enabled;
  515. }
  516. public bool Enabled
  517. {
  518. get
  519. {
  520. return (bool)_data[IndexEnabled];
  521. }
  522. set
  523. {
  524. _data[IndexEnabled] = value;
  525. }
  526. }
  527. public string Name
  528. {
  529. get
  530. {
  531. return (string)_data[IndexName];
  532. }
  533. set
  534. {
  535. _data[IndexName] = value;
  536. }
  537. }
  538. public object GetData()
  539. {
  540. return _data;
  541. }
  542. public void SetData(object o)
  543. {
  544. _data = (object[])o;
  545. }
  546. internal void UpdateText()
  547. {
  548. Text = "extension=" + Name;
  549. }
  550. }
  551. internal class PHPIniSection : PHPIniBase
  552. {
  553. public PHPIniSection() { }
  554. public PHPIniSection(string rawText) : base(rawText) { }
  555. }
  556. }