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