also, disable the call do BindColumns in
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / Mime.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 //
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2006 Alexander Olk
21 //
22 // Authors:
23 //
24 //  Alexander Olk       alex.olk@googlemail.com
25 //
26
27 using System;
28 using System.IO;
29 using System.Collections;
30 using System.Collections.Specialized;
31 using System.Text.RegularExpressions;
32 using System.Text;
33
34 // Usage:
35 // - for files:
36 //   string mimeType = Mime.GetMimeTypeForFile( string filename );
37 // - for byte array:
38 //   string mimeType = Mime.GetMimeTypeForData( byte[] data );
39 // - for string (maybe an email):
40 //   string mimeType = Mime.GetMimeTypeForString( string input );
41
42 // - get alias for mime type:
43 //   string alias = Mime.GetMimeAlias( string mimeType );
44 // - get subclass for mime type:
45 //   string subtype = Mime.GetMimeSubClass( string mimeType );
46 // - get all available mime types:
47 //   string[] available = Mime.AvailableMimeTypes;
48
49 // TODO:
50 // - optimize even more :)
51 // - async callback ?!?
52 // - freedesktop org file extensions can have regular expressions also, resolve them too
53 // - sort match collections by magic priority ( higher = first ) ?
54
55 // internal test:
56 // looking up the mime types 20 times for 2757 files in /usr/lib without caching (mime_file_cache)
57 // old version: Time: 00:00:32.3791220
58 // new version: Time: 00:00:16.9991810
59
60 namespace System.Windows.Forms
61 {
62         internal class Mime
63         {
64                 public static Mime Instance = new Mime();
65                 
66                 private string current_file_name;
67                 private string global_result = octet_stream;
68                 
69                 private FileStream file_stream;
70                 
71                 private byte[] buffer = null;
72                 
73                 private const string octet_stream = "application/octet-stream";
74                 private const string text_plain = "text/plain";
75                 private const string zero_file = "application/x-zerosize";
76                 
77                 private StringDictionary mime_file_cache = new StringDictionary();
78                 
79                 private const int mime_file_cache_max_size = 3000;
80                 
81                 private string search_string;
82                 
83                 private static object lock_object = new Object();
84                 
85 //              private int platform = (int) Environment.OSVersion.Platform;
86                 
87                 private bool is_zero_file = false;
88                 
89                 private int bytes_read = 0;
90                 
91                 private bool mime_available = false;
92                 
93                 public static NameValueCollection Aliases;
94                 public static NameValueCollection SubClasses;
95                 
96                 public static NameValueCollection GlobalPatternsShort;
97                 public static NameValueCollection GlobalPatternsLong;
98                 public static NameValueCollection GlobalLiterals;
99                 public static NameValueCollection GlobalSufPref;
100                 
101                 public static ArrayList Matches80Plus;
102                 public static ArrayList MatchesBelow80;
103                 
104                 private Mime( )
105                 {
106 #if NET_2_0
107                         Aliases = new NameValueCollection (StringComparer.CurrentCultureIgnoreCase);
108                         SubClasses = new NameValueCollection (StringComparer.CurrentCultureIgnoreCase);
109                         GlobalPatternsShort = new NameValueCollection (StringComparer.CurrentCultureIgnoreCase);
110                         GlobalPatternsLong = new NameValueCollection (StringComparer.CurrentCultureIgnoreCase);
111                         GlobalLiterals = new NameValueCollection (StringComparer.CurrentCultureIgnoreCase);
112                         GlobalSufPref = new NameValueCollection (StringComparer.CurrentCultureIgnoreCase);
113 #else
114                         Aliases = new NameValueCollection (new CaseInsensitiveHashCodeProvider (), new Comparer (System.Globalization.CultureInfo.CurrentUICulture));
115                         SubClasses = new NameValueCollection (new CaseInsensitiveHashCodeProvider (), new Comparer (System.Globalization.CultureInfo.CurrentUICulture));
116                         GlobalPatternsShort = new NameValueCollection (new CaseInsensitiveHashCodeProvider (), new Comparer (System.Globalization.CultureInfo.CurrentUICulture));
117                         GlobalPatternsLong = new NameValueCollection (new CaseInsensitiveHashCodeProvider (), new Comparer (System.Globalization.CultureInfo.CurrentUICulture));
118                         GlobalLiterals = new NameValueCollection (new CaseInsensitiveHashCodeProvider (), new Comparer (System.Globalization.CultureInfo.CurrentUICulture));
119                         GlobalSufPref = new NameValueCollection (new CaseInsensitiveHashCodeProvider (), new Comparer (System.Globalization.CultureInfo.CurrentUICulture));
120 #endif
121                         
122                         Matches80Plus = new ArrayList ();
123                         MatchesBelow80 = new ArrayList ();
124                         
125                         FDOMimeConfigReader fmcr = new FDOMimeConfigReader ();
126                         int buffer_length = fmcr.Init ();
127                         
128                         if (buffer_length != -1) {
129                                 buffer = new byte[ buffer_length ];
130                                 mime_available = true;
131                         }
132                 }
133                 
134                 public static bool MimeAvailable
135                 {
136                         get {
137                                 return Instance.mime_available;
138                         }
139                 }
140                 
141                 public static string GetMimeTypeForFile( string filename )
142                 {
143                         lock ( lock_object )
144                         {
145                                 Instance.StartByFileName( filename );
146                         }
147                         
148                         return Instance.global_result;
149                 }
150                 
151                 // not tested
152                 public static string GetMimeTypeForData( byte[] data )
153                 {
154                         lock ( lock_object )
155                         {
156                                 Instance.StartDataLookup( data );
157                         }
158                         
159                         return Instance.global_result;
160                 }
161                 
162                 public static string GetMimeTypeForString( string input )
163                 {
164                         lock ( lock_object )
165                         {
166                                 Instance.StartStringLookup( input );
167                         }
168                         
169                         return Instance.global_result;
170                 }
171                 
172                 public static string GetMimeAlias( string mimetype )
173                 {
174                         return Aliases[ mimetype ];
175                 }
176                 
177                 public static string GetMimeSubClass( string mimetype )
178                 {
179                         return SubClasses[ mimetype ];
180                 }
181                 
182                 private void StartByFileName( string filename )
183                 {
184                         if ( mime_file_cache.ContainsKey( filename ) )
185                         {
186                                 global_result = mime_file_cache[ filename ];
187                                 return;
188                         }
189                         
190                         current_file_name = filename;
191                         is_zero_file = false;
192                         
193 //                      if ( !CheckForInode( ) )
194 //                      {
195                         global_result = octet_stream;
196                         
197                         GoByFileName( );
198 //                      }
199                         
200                         mime_file_cache.Add( current_file_name, global_result );
201                         
202                         if (mime_file_cache.Count > mime_file_cache_max_size) {
203                                 IEnumerator enumerator = mime_file_cache.GetEnumerator ();
204                                 
205                                 int counter = mime_file_cache_max_size - 1000;
206                                 
207                                 while (enumerator.MoveNext ()) {
208                                         mime_file_cache.Remove (enumerator.Current.ToString ());
209                                         counter--;
210                                         
211                                         if (counter == 0)
212                                                 break;
213                                 }
214                         }
215                 }
216                 
217                 private void StartDataLookup( byte[] data )
218                 {
219                         global_result = octet_stream;
220                         
221                         System.Array.Clear( buffer, 0, buffer.Length );
222                         
223                         if ( data.Length > buffer.Length )
224                         {
225                                 System.Array.Copy( data, buffer, buffer.Length );
226                         }
227                         else
228                         {
229                                 System.Array.Copy( data, buffer, data.Length );
230                         }
231                         
232                         if ( CheckMatch80Plus( ) )
233                                 return;
234                         
235                         if ( CheckMatchBelow80( ) )
236                                 return;
237                         
238                         CheckForBinaryOrText( );
239                 }
240                 
241                 private void StartStringLookup( string input )
242                 {
243                         global_result = text_plain;
244                         
245                         search_string = input;
246                         
247                         if ( CheckForContentTypeString( ) )
248                                 return;
249                 }
250                 
251 //              private bool CheckForInode( )
252 //              {
253 //                      if ( ( platform == 4 ) || ( platform == 128 ) )
254 //                      {
255 //#if __MonoCS__
256 //                              try
257 //                              {
258 //                                      // *nix platform
259 //                                      Mono.Unix.UnixFileInfo ufi = new Mono.Unix.UnixFileInfo( current_file_name );
260 //
261 //                                      if ( ufi.IsFile )
262 //                                      {
263 //                                              return false;
264 //                                      }
265 //                                      else
266 //                                      if ( ufi.IsDirectory )
267 //                                      {
268 //                                              global_result = "inode/directory";
269 //                                              return true;
270 //                                      }
271 //                                      else
272 //                                      if ( ufi.IsBlockDevice )
273 //                                      {
274 //                                              global_result = "inode/blockdevice";
275 //                                              return true;
276 //                                      }
277 //                                      else
278 //                                      if ( ufi.IsSocket )
279 //                                      {
280 //                                              global_result = "inode/socket";
281 //                                              return true;
282 //                                      }
283 //                                      else
284 //                                      if ( ufi.IsSymbolicLink )
285 //                                      {
286 //                                              global_result = "inode/symlink";
287 //                                              return true;
288 //                                      }
289 //                                      else
290 //                                      if ( ufi.IsCharacterDevice )
291 //                                      {
292 //                                              global_result = "inode/chardevice";
293 //                                              return true;
294 //                                      }
295 //                                      else
296 //                                      if ( ufi.IsFIFO )
297 //                                      {
298 //                                              global_result = "inode/fifo";
299 //                                              return true;
300 //                                      }
301 //                              } catch( Exception e )
302 //                              {
303 //                                      return false;
304 //                              }
305 //#endif
306 //                      }
307 //                      else
308 //                      {
309 //                              // TODO!!!!
310 //                              // windows platform
311 //                      }
312 //                      
313 //                      return false;
314 //              }
315                 
316                 private void GoByFileName( )
317                 {
318                         // check if we can open the file
319                         if ( !OpenFile( ) )
320                         {
321                                 // couldn't open the file, check globals only
322                                 CheckGlobalPatterns( );
323                                 
324                                 return;
325                         }
326                         
327                         if ( !is_zero_file )
328                         {
329                                 // check for matches with a priority >= 80
330                                 if ( CheckMatch80Plus( ) )
331                                         return;
332                         }
333                         
334                         // check global patterns, aka file extensions...
335                         // this should be done for zero size files also,
336                         // for example zero size file trash.ccc~ should return
337                         // application/x-trash instead of application/x-zerosize
338                         if ( CheckGlobalPatterns( ) )
339                                 return;
340                         
341                         // if file size is zero, no other checks are needed
342                         if ( is_zero_file )
343                                 return;
344                         
345                         // ok, still nothing matches then try matches with a priority < 80
346                         if ( CheckMatchBelow80( ) )
347                                 return;
348                         
349                         // wow, still nothing... return application/octet-stream for binary data, or text/plain for textual data
350                         CheckForBinaryOrText( );
351                 }
352                 
353                 private bool CheckMatch80Plus( )
354                 {
355                         foreach ( Match match in Matches80Plus )
356                         {
357                                 if ( TestMatch( match ) )
358                                 {
359                                         global_result = match.MimeType;
360                                         
361                                         return true;
362                                 }
363                         }
364                         
365                         return false;
366                 }
367                 
368                 // this little helper method gives us a real speed improvement
369                 private bool FastEndsWidth(string input, string value)
370                 {
371                         if (value.Length > input.Length)
372                                 return false;
373                         
374                         int z = input.Length - 1;
375                         
376                         for (int i = value.Length - 1; i > -1; i--) {
377                                 if (value[i] != input[z])
378                                         return false;
379                                 
380                                 z--;
381                         }
382                         
383                         return true;
384                 }
385                 
386                 private bool FastStartsWith(string input, string value)
387                 {
388                         if (value.Length > input.Length)
389                                 return false;
390                         
391                         for (int i = 0; i < value.Length; i++)
392                                 if (value[i] != input[i])
393                                         return false;
394                         
395                         return true;
396                 }
397                 
398                 // start always with index = 0
399                 private int FastIndexOf(string input, char value)
400                 {
401                         if (input.Length == 0)
402                                 return -1;
403                         
404                         for (int i = 0; i < input.Length; i++)
405                                 if (input[i] == value)
406                                         return i;
407                         
408                         return -1;
409                 }
410                 
411                 private int FastIndexOf(string input, string value)
412                 {
413                         if (input.Length == 0)
414                                 return -1;
415                         
416                         for (int i = 0; i < input.Length - value.Length; i++) {
417                                 if (input[i] == value[0]) {
418                                         int counter = 0;
419                                         for (int z = 1; z < value.Length; z++) {
420                                                 if (input[i+z] != value[z])
421                                                         break;
422                                                 
423                                                 counter++;
424                                         }
425                                         if (counter == value.Length -1) {
426                                                 return i;
427                                         }
428                                 }
429                         }
430                         
431                         return -1;
432                 }
433                 
434                 private void CheckGlobalResult( )
435                 {
436                         int comma_index = FastIndexOf(global_result, ',');
437                         
438                         if ( comma_index != -1 )
439                         {
440                                 global_result = global_result.Substring( 0, comma_index );
441                         }
442                 }
443                 
444                 private bool CheckGlobalPatterns( )
445                 {
446                         string filename = Path.GetFileName( current_file_name );
447                         
448                         // first check for literals
449                         for ( int i = 0; i < GlobalLiterals.Count; i++ )
450                         {
451                                 string key = GlobalLiterals.GetKey(i);
452                                 
453                                 // no regex char
454                                 if ( FastIndexOf(key, '[' ) == -1 )
455                                 {
456                                         if (FastIndexOf(filename, key) != -1)
457                                         {
458                                                 global_result = GlobalLiterals[i];
459                                                 CheckGlobalResult( );
460                                                 return true;
461                                         }
462                                 }
463                                 else // regex it ;)
464                                 {
465                                         if ( Regex.IsMatch( filename, key ) )
466                                         {
467                                                 global_result = GlobalLiterals[ i ];
468                                                 CheckGlobalResult( );
469                                                 return true;
470                                         }
471                                 }
472                         }
473                         
474                         if ( FastIndexOf(filename, '.' ) != -1 )
475                         {
476                                 // check for double extension like .tar.gz
477                                 for ( int i = 0; i < GlobalPatternsLong.Count; i++ )
478                                 {
479                                         string key = GlobalPatternsLong.GetKey( i );
480                                         
481                                         if (FastEndsWidth (filename, key))
482                                         {
483                                                 global_result = GlobalPatternsLong[ i ];
484                                                 CheckGlobalResult( );
485                                                 return true;
486                                         }
487                                         else
488                                         {
489                                                 if ( FastEndsWidth (filename.ToLower( ), key ) )
490                                                 {
491                                                         global_result = GlobalPatternsLong[ i ];
492                                                         CheckGlobalResult( );
493                                                         return true;
494                                                 }
495                                         }
496                                 }
497                                 
498                                 // check normal extensions...
499                                 string extension = Path.GetExtension( current_file_name );
500                                 
501                                 if ( extension.Length != 0 )
502                                 {
503                                         string global_result_tmp = GlobalPatternsShort[ extension ];
504                                         
505                                         if ( global_result_tmp != null )
506                                         {
507                                                 global_result = global_result_tmp;
508                                                 CheckGlobalResult( );
509                                                 return true;
510                                         }
511                                         
512                                         global_result_tmp = GlobalPatternsShort[ extension.ToLower( ) ];
513                                         
514                                         if ( global_result_tmp != null )
515                                         {
516                                                 global_result = global_result_tmp;
517                                                 CheckGlobalResult( );
518                                                 return true;
519                                         }
520                                 }
521                         }
522                         
523                         // finally check if a prefix or suffix matches
524                         for ( int i = 0; i < GlobalSufPref.Count; i++ )
525                         {
526                                 string key = GlobalSufPref.GetKey( i );
527                                 
528                                 if ( key[0] == '*' )
529                                 {
530                                         if (FastEndsWidth(filename, key.Replace( "*", String.Empty )))
531                                         {
532                                                 global_result = GlobalSufPref[ i ];
533                                                 CheckGlobalResult( );
534                                                 return true;
535                                         }
536                                 }
537                                 else
538                                 {
539                                         if ( FastStartsWith(filename, key.Replace( "*", String.Empty ) ) )
540                                         {
541                                                 global_result = GlobalSufPref[ i ];
542                                                 CheckGlobalResult( );
543                                                 return true;
544                                         }
545                                 }
546                         }
547                         
548                         return false;
549                 }
550                 
551                 private bool CheckMatchBelow80( )
552                 {
553                         foreach ( Match match in MatchesBelow80 )
554                         {
555                                 if ( TestMatch( match ) )
556                                 {
557                                         global_result = match.MimeType;
558                                         
559                                         return true;
560                                 }
561                         }
562                         
563                         return false;
564                 }
565                 
566                 private void CheckForBinaryOrText( )
567                 {
568                         // check the first 32 bytes
569                         
570                         for ( int i = 0; i < 32; i++ )
571                         {
572                                 char c = System.Convert.ToChar( buffer[ i ] );
573                                 
574                                 if ( c != '\t' &&  c != '\n' && c != '\r' && c != 12 && c < 32 )
575                                 {
576                                         global_result = octet_stream;
577                                         return;
578                                 }
579                         }
580                         
581                         global_result = text_plain;
582                 }
583                 
584                 private bool TestMatch (Match match)
585                 {
586                         foreach (Matchlet matchlet in match.Matchlets)
587                                 if (TestMatchlet (matchlet))
588                                         return true;
589                         
590                         return false;
591                 }
592                 
593                 private bool TestMatchlet( Matchlet matchlet )
594                 {
595                         //  using a simple brute force search algorithm
596                         // compare each (masked) value from the buffer with the (masked) value from the matchlet
597                         
598                         // no need to check if the offset + the bytevalue length exceed the # bytes read
599                         if (matchlet.Offset + matchlet.ByteValue.Length > bytes_read)
600                                 return false;
601                         
602                         for ( int offset_counter = 0; offset_counter < matchlet.OffsetLength; offset_counter++ )
603                         {
604                                 if (matchlet.Offset + offset_counter + matchlet.ByteValue.Length > bytes_read)
605                                         return false;
606                                 
607                                 if ( matchlet.Mask == null )
608                                 {
609                                         if ( buffer[ matchlet.Offset + offset_counter ] == matchlet.ByteValue[ 0 ] )
610                                         {
611                                                 if ( matchlet.ByteValue.Length == 1 )
612                                                 {
613                                                         if ( matchlet.Matchlets.Count > 0 )
614                                                         {
615                                                                 foreach ( Matchlet sub_matchlet in matchlet.Matchlets )
616                                                                 {
617                                                                         if ( TestMatchlet( sub_matchlet ) )
618                                                                                 return true;
619                                                                 }
620                                                         }
621                                                         else
622                                                                 return true;
623                                                 }
624                                                 
625                                                 int minus = 0;
626                                                 // check if the last matchlet byte value is the same as the byte value in the buffer...
627                                                 if (matchlet.ByteValue.Length > 2) {
628                                                         if (buffer[ matchlet.Offset + offset_counter + matchlet.ByteValue.Length - 1 ] != matchlet.ByteValue[ matchlet.ByteValue.Length - 1 ])
629                                                                 return false;
630                                                         
631                                                         minus = 1;
632                                                 }
633                                                 
634                                                 for ( int i = 1; i < matchlet.ByteValue.Length - minus; i++ )
635                                                 {
636                                                         if ( buffer[ matchlet.Offset + offset_counter + i ] != matchlet.ByteValue[ i ] )
637                                                                 return false;
638                                                 }
639                                                 
640                                                 if ( matchlet.Matchlets.Count > 0 )
641                                                 {
642                                                         foreach ( Matchlet sub_matchlets in matchlet.Matchlets )
643                                                         {
644                                                                 if ( TestMatchlet( sub_matchlets ) )
645                                                                         return true;
646                                                         }
647                                                 }
648                                                 else
649                                                         return true;
650                                         }
651                                 }
652                                 else // with mask ( it's the same as above, only AND the byte with the corresponding mask byte
653                                 {
654                                         if ( ( buffer[ matchlet.Offset + offset_counter ] & matchlet.Mask[ 0 ] )  ==
655                                             ( matchlet.ByteValue[ 0 ] & matchlet.Mask[ 0 ] ) )
656                                         {
657                                                 if ( matchlet.ByteValue.Length == 1 )
658                                                 {
659                                                         if ( matchlet.Matchlets.Count > 0 )
660                                                         {
661                                                                 foreach ( Matchlet sub_matchlets in matchlet.Matchlets )
662                                                                 {
663                                                                         if ( TestMatchlet( sub_matchlets ) )
664                                                                                 return true;
665                                                                 }
666                                                         }
667                                                         else
668                                                                 return true;
669                                                 }
670                                                 
671                                                 int minus = 0;
672                                                 // check if the last matchlet byte value is the same as the byte value in the buffer...
673                                                 if (matchlet.ByteValue.Length > 2) {
674                                                         
675                                                         if ((buffer[ matchlet.Offset + offset_counter + matchlet.ByteValue.Length - 1 ] & matchlet.Mask[ matchlet.ByteValue.Length - 1 ])
676                                                             != (matchlet.ByteValue[ matchlet.ByteValue.Length - 1 ] & matchlet.Mask[ matchlet.ByteValue.Length - 1 ]))
677                                                                 return false;
678                                                         
679                                                         minus = 1;
680                                                 }
681                                                 
682                                                 for ( int i = 1; i < matchlet.ByteValue.Length - minus; i++ )
683                                                 {
684                                                         if ( ( buffer[ matchlet.Offset + offset_counter + i ]  & matchlet.Mask[ i ] ) !=
685                                                             ( matchlet.ByteValue[ i ] & matchlet.Mask[ i ] ) )
686                                                                 return false;
687                                                 }
688                                                 
689                                                 if ( matchlet.Matchlets.Count > 0 )
690                                                 {
691                                                         foreach ( Matchlet sub_matchlets in matchlet.Matchlets )
692                                                         {
693                                                                 if ( TestMatchlet( sub_matchlets ) )
694                                                                         return true;
695                                                         }
696                                                 }
697                                                 else
698                                                         return true;
699                                         }
700                                 }
701                         }
702                         
703                         return false;
704                 }
705                 
706                 private bool OpenFile( )
707                 {
708                         try
709                         {
710                                 file_stream = new FileStream( current_file_name, FileMode.Open, FileAccess.Read ); // FileShare ??? use BinaryReader ???
711                                 
712                                 if ( file_stream.Length == 0 )
713                                 {
714                                         global_result = zero_file;
715                                         is_zero_file = true;
716                                 }
717                                 else
718                                 {
719                                         bytes_read = file_stream.Read( buffer, 0, buffer.Length );
720                                         
721                                         // do not clear the whole buffer everytime; clear only what's needed
722                                         if (bytes_read < buffer.Length) {
723                                                 System.Array.Clear( buffer, bytes_read, buffer.Length - bytes_read );
724                                         }
725                                 }
726                                 
727                                 file_stream.Close( );
728                         }
729                         catch (Exception)
730                         {
731                                 return false;
732                         }
733                         
734                         return true;
735                 }
736                 
737                 private bool CheckForContentTypeString( )
738                 {
739                         int index = search_string.IndexOf( "Content-type:" );
740                         
741                         if ( index != -1 )
742                         {
743                                 index += 13; // Length of string "Content-type:"
744                                 
745                                 global_result = String.Empty;
746                                 
747                                 while ( search_string[ index ] != ';' )
748                                 {
749                                         global_result += search_string[ index++ ];
750                                 }
751                                 
752                                 global_result.Trim( );
753                                 
754                                 return true;
755                         }
756                         
757                         // convert string to byte array
758                         byte[] string_byte = ( new ASCIIEncoding( ) ).GetBytes( search_string );
759                         
760                         System.Array.Clear( buffer, 0, buffer.Length );
761                         
762                         if ( string_byte.Length > buffer.Length )
763                         {
764                                 System.Array.Copy( string_byte, buffer, buffer.Length );
765                         }
766                         else
767                         {
768                                 System.Array.Copy( string_byte, buffer, string_byte.Length );
769                         }
770                         
771                         if ( CheckMatch80Plus( ) )
772                                 return true;
773                         
774                         if ( CheckMatchBelow80( ) )
775                                 return true;
776                         
777                         return false;
778                 }
779         }
780         
781         internal class FDOMimeConfigReader {
782                 bool fdo_mime_available = false;
783                 StringCollection shared_mime_paths = new StringCollection ();
784                 BinaryReader br;
785                 
786                 int max_offset_and_range = 0;
787                 
788                 public int Init ()
789                 {
790                         CheckFDOMimePaths ();
791                         
792                         if (!fdo_mime_available)
793                                 return -1;
794                         
795                         ReadMagicData ();
796                         
797                         ReadGlobsData ();
798                         
799                         ReadSubclasses ();
800                         
801                         ReadAliases ();
802                         
803                         shared_mime_paths = null;
804                         br = null;
805                         
806                         return max_offset_and_range;
807                 }
808                 
809                 private void CheckFDOMimePaths ()
810                 {
811                         if (Directory.Exists ("/usr/share/mime"))
812                                 shared_mime_paths.Add ("/usr/share/mime/");
813                         else
814                         if (Directory.Exists ("/usr/local/share/mime"))
815                                 shared_mime_paths.Add ("/usr/local/share/mime/");
816                         
817                         if (Directory.Exists (System.Environment.GetFolderPath (Environment.SpecialFolder.Personal) + "/.local/share/mime"))
818                                 shared_mime_paths.Add (System.Environment.GetFolderPath (Environment.SpecialFolder.Personal) + "/.local/share/mime/");
819                         
820                         if (shared_mime_paths.Count == 0)
821                                 return;
822                         
823                         fdo_mime_available = true;
824                 }
825                 
826                 private void ReadMagicData ()
827                 {
828                         foreach (string path in shared_mime_paths) {
829                                 if (!File.Exists (path + "/magic"))
830                                         continue;
831                                 
832                                 try {
833                                         FileStream fs = File.OpenRead (path + "/magic");
834                                         br = new BinaryReader (fs);
835                                         
836                                         if (CheckMagicHeader ()) {
837                                                 MakeMatches ();
838                                         }
839                                         
840                                         br.Close ();
841                                         fs.Close ();
842                                 } catch (Exception ) {
843                                 }
844                         }
845                 }
846                 
847                 private void MakeMatches ()
848                 {
849                         Matchlet[] matchlets = new Matchlet [30];
850                         
851                         while (br.PeekChar () != -1) {
852                                 int priority = -1;
853                                 string mime_type = ReadPriorityAndMimeType (ref priority);
854                                 
855                                 if (mime_type != null) {
856                                         Match match = new Match ();
857                                         match.Priority = priority;
858                                         match.MimeType = mime_type;
859                                         
860                                         while (true) {
861                                                 int indent = 0;
862                                                 // indent
863                                                 char c;
864                                                 if (br.PeekChar () != '>') {
865                                                         string indent_string = String.Empty;
866                                                         while (true) {
867                                                                 if (br.PeekChar () == '>')
868                                                                         break;
869                                                                 
870                                                                 c = br.ReadChar ();
871                                                                 indent_string += c;
872                                                         }
873                                                         indent = Convert.ToInt32 (indent_string);
874                                                 }
875                                                 
876                                                 int offset = 0;
877                                                 
878                                                 // offset
879                                                 if (br.PeekChar () == '>') {
880                                                         br.ReadChar ();
881                                                         offset = ReadValue ();
882                                                 }
883                                                 
884                                                 int value_length = 0;
885                                                 byte[] value = null;
886                                                 // value length and value
887                                                 if (br.PeekChar () == '=') {
888                                                         br.ReadChar ();
889                                                         
890                                                         // read 2 bytes value length (always big endian)
891                                                         byte first = br.ReadByte ();
892                                                         byte second = br.ReadByte ();
893                                                         
894                                                         value_length = first * 256 + second;
895                                                         
896                                                         value = br.ReadBytes (value_length);
897                                                 }
898                                                 
899                                                 // mask
900                                                 byte[] mask = null;
901                                                 
902                                                 if (br.PeekChar () == '&') {
903                                                         br.ReadChar ();
904                                                         
905                                                         mask = br.ReadBytes (value_length);
906                                                 }
907                                                 
908                                                 // word_size
909                                                 int word_size = 1;
910                                                 if (br.PeekChar () == '~') {
911                                                         br.ReadChar ();
912                                                         
913                                                         c = br.ReadChar ();
914                                                         
915                                                         word_size = Convert.ToInt32 (c - 0x30);
916                                                         
917                                                         // data is stored in big endian format. 
918                                                         if (word_size > 1 && System.BitConverter.IsLittleEndian) {
919                                                                 //convert the value and, if available, the mask data to little endian
920                                                                 if (word_size == 2) {
921                                                                         if (value != null) {
922                                                                                 for (int i = 0; i < value.Length; i += 2) {
923                                                                                         byte one = value [i];
924                                                                                         byte two = value [i + 1];
925                                                                                         value [i] = two;
926                                                                                         value [i + 1] = one;
927                                                                                 }
928                                                                         }
929                                                                         if (mask != null) {
930                                                                                 for (int i = 0; i < mask.Length; i += 2) {
931                                                                                         byte one = mask [i];
932                                                                                         byte two = mask [i + 1];
933                                                                                         mask [i] = two;
934                                                                                         mask [i + 1] = one;
935                                                                                 }
936                                                                         }
937                                                                 } else if (word_size == 4) {
938                                                                         if (value != null) {
939                                                                                 for (int i = 0; i < value.Length; i += 4) {
940                                                                                         byte one = value [i];
941                                                                                         byte two = value [i + 1];
942                                                                                         byte three = value [i + 2];
943                                                                                         byte four = value [i + 3];
944                                                                                         value [i] = four;
945                                                                                         value [i + 1] = three;
946                                                                                         value [i + 2] = two;
947                                                                                         value [i + 3] = one;
948                                                                                 }
949                                                                         }
950                                                                         if (mask != null) {
951                                                                                 for (int i = 0; i < mask.Length; i += 4) {
952                                                                                         byte one = mask [i];
953                                                                                         byte two = mask [i + 1];
954                                                                                         byte three = mask [i + 2];
955                                                                                         byte four = mask [i + 3];
956                                                                                         mask [i] = four;
957                                                                                         mask [i + 1] = three;
958                                                                                         mask [i + 2] = two;
959                                                                                         mask [i + 3] = one;
960                                                                                         
961                                                                                 }
962                                                                         }
963                                                                 }
964                                                         }
965                                                 }
966                                                 
967                                                 // range length
968                                                 int range_length = 1;
969                                                 if (br.PeekChar () == '+') {
970                                                         br.ReadChar ();
971                                                         range_length = ReadValue ();
972                                                 }
973                                                 
974                                                 // read \n
975                                                 br.ReadChar ();
976                                                 
977                                                 // create the matchlet
978                                                 matchlets [indent] = new Matchlet ();
979                                                 matchlets [indent].Offset = offset;
980                                                 matchlets [indent].OffsetLength = range_length;
981                                                 matchlets [indent].ByteValue = value;
982                                                 if (mask != null)
983                                                         matchlets [indent].Mask = mask;
984                                                 
985                                                 if (indent == 0) {
986                                                         match.Matchlets.Add (matchlets [indent]);
987                                                 } else {
988                                                         matchlets [indent - 1].Matchlets.Add (matchlets [indent]);
989                                                 }
990                                                 
991                                                 if (max_offset_and_range < matchlets [indent].Offset + matchlets [indent].OffsetLength + matchlets [indent].ByteValue.Length + 1)
992                                                         max_offset_and_range = matchlets [indent].Offset + matchlets [indent].OffsetLength + matchlets [indent].ByteValue.Length  + 1;
993                                                 
994                                                 // if '[' move to next mime type
995                                                 if (br.PeekChar () == '[')
996                                                         break;
997                                         }
998                                         
999                                         if (priority < 80)
1000                                                 Mime.MatchesBelow80.Add (match);
1001                                         else
1002                                                 Mime.Matches80Plus.Add (match);
1003                                 }
1004                         }
1005                 }
1006                 
1007                 private void ReadGlobsData ()
1008                 {
1009                         foreach (string path in shared_mime_paths) {
1010                                 if (!File.Exists (path + "/globs"))
1011                                         continue;
1012                                 
1013                                 try {
1014                                         StreamReader sr = new StreamReader (path + "/globs");
1015                                         
1016                                         while (sr.Peek () != -1) {
1017                                                 string line = sr.ReadLine ().Trim ();
1018                                                 
1019                                                 if (line.StartsWith ("#"))
1020                                                         continue;
1021                                                 
1022                                                 string[] split = line.Split (new char [] {':'});
1023                                                 
1024                                                 if (split [1].IndexOf ('*') > -1 && split [1].IndexOf ('.') == -1) {
1025                                                         Mime.GlobalSufPref.Add (split [1], split [0]);
1026                                                 } else if (split [1]. IndexOf ('*') == -1) {
1027                                                         Mime.GlobalLiterals.Add (split [1], split [0]);
1028                                                 } else {
1029                                                         string[] split2 = split [1].Split (new char [] {'.'});
1030                                                         
1031                                                         if (split2.Length > 2) {
1032                                                                 // more than one dot
1033                                                                 Mime.GlobalPatternsLong.Add (split [1].Remove(0, 1), split [0]);
1034                                                         } else {
1035                                                                 // normal
1036                                                                 Mime.GlobalPatternsShort.Add (split [1].Remove(0, 1), split [0]);
1037                                                         }
1038                                                 }
1039                                         }
1040                                         
1041                                         sr.Close ();
1042                                 } catch (Exception ) {
1043                                 }
1044                         }
1045                 }
1046                 
1047                 private void ReadSubclasses ()
1048                 {
1049                         foreach (string path in shared_mime_paths) {
1050                                 if (!File.Exists (path + "/subclasses"))
1051                                         continue;
1052                                 
1053                                 try {
1054                                         StreamReader sr = new StreamReader (path + "/subclasses");
1055                                         
1056                                         while (sr.Peek () != -1) {
1057                                                 string line = sr.ReadLine ().Trim ();
1058                                                 
1059                                                 if (line.StartsWith ("#"))
1060                                                         continue;
1061                                                 
1062                                                 string[] split = line.Split (new char [] {' '});
1063                                                 
1064                                                 Mime.SubClasses.Add (split [0], split [1]);
1065                                         }
1066                                         
1067                                         sr.Close ();
1068                                 } catch (Exception ) {
1069                                 }
1070                         }
1071                 }
1072                 
1073                 private void ReadAliases ()
1074                 {
1075                         foreach (string path in shared_mime_paths) {
1076                                 if (!File.Exists (path + "/aliases"))
1077                                         continue;
1078                                 
1079                                 try {
1080                                         StreamReader sr = new StreamReader (path + "/aliases");
1081                                         
1082                                         while (sr.Peek () != -1) {
1083                                                 string line = sr.ReadLine ().Trim ();
1084                                                 
1085                                                 if (line.StartsWith ("#"))
1086                                                         continue;
1087                                                 
1088                                                 string[] split = line.Split (new char [] {' '});
1089                                                 
1090                                                 Mime.Aliases.Add (split [0], split [1]);
1091                                         }
1092                                         
1093                                         sr.Close ();
1094                                 } catch (Exception ) {
1095                                 }
1096                         }
1097                 }
1098                 
1099                 private int ReadValue ()
1100                 {
1101                         string result_string = String.Empty;
1102                         int result = 0;
1103                         char c;
1104                         
1105                         while (true) {
1106                                 if (br.PeekChar () == '=' || br.PeekChar () == '\n')
1107                                         break;
1108                                 
1109                                 c = br.ReadChar ();
1110                                 result_string += c;
1111                         }
1112                         
1113                         result = Convert.ToInt32 (result_string);
1114                         
1115                         return result;
1116                 }
1117                 
1118                 private string ReadPriorityAndMimeType (ref int priority)
1119                 {
1120                         if (br.ReadChar () == '[') {
1121                                 string priority_string = String.Empty;
1122                                 while (true) {
1123                                         char c = br.ReadChar ();
1124                                         if (c == ':')
1125                                                 break;
1126                                         priority_string += c;
1127                                 }
1128                                 
1129                                 priority = System.Convert.ToInt32 (priority_string);
1130                                 
1131                                 string mime_type_result = String.Empty;
1132                                 while (true) {
1133                                         char c = br.ReadChar ();
1134                                         if (c == ']')
1135                                                 break;
1136                                         
1137                                         mime_type_result += c;
1138                                 }
1139                                 
1140                                 if (br.ReadChar () == '\n')
1141                                         return mime_type_result;
1142                         }
1143                         return null;
1144                 }
1145                 
1146                 private bool CheckMagicHeader ()
1147                 {
1148                         char[] chars = br.ReadChars (10);
1149                         string magic_header = new String (chars);
1150                         
1151                         if (magic_header != "MIME-Magic")
1152                                 return false;
1153                         
1154                         if (br.ReadByte () != 0)
1155                                 return false;
1156                         if (br.ReadChar () != '\n')
1157                                 return false;
1158                         
1159                         return true;
1160                 }
1161         }
1162         
1163         internal class Match {
1164                 string mimeType;
1165                 int priority;
1166                 ArrayList matchlets = new ArrayList();
1167                 
1168                 public string MimeType {
1169                         set {
1170                                 mimeType = value;
1171                         }
1172                         
1173                         get {
1174                                 return mimeType;
1175                         }
1176                 }
1177                 
1178                 public int Priority {
1179                         set {
1180                                 priority = value;
1181                         }
1182                         
1183                         get {
1184                                 return priority;
1185                         }
1186                 }
1187                 
1188                 public ArrayList Matchlets {
1189                         get {
1190                                 return matchlets;
1191                         }
1192                 }
1193         }
1194         
1195         internal class Matchlet {
1196                 byte[] byteValue;
1197                 byte[] mask = null;
1198                 
1199                 int offset;
1200                 int offsetLength;
1201                 int wordSize = 1;
1202                 
1203                 ArrayList matchlets = new ArrayList ();
1204                 
1205                 public byte[] ByteValue {
1206                         set {
1207                                 byteValue = value;
1208                         }
1209                         
1210                         get {
1211                                 return byteValue;
1212                         }
1213                 }
1214                 
1215                 public byte[] Mask {
1216                         set {
1217                                 mask = value;
1218                         }
1219                         
1220                         get {
1221                                 return mask;
1222                         }
1223                 }
1224                 
1225                 public int Offset {
1226                         set {
1227                                 offset = value;
1228                         }
1229                         
1230                         get {
1231                                 return offset;
1232                         }
1233                 }
1234                 
1235                 public int OffsetLength {
1236                         set {
1237                                 offsetLength = value;
1238                         }
1239                         
1240                         get {
1241                                 return offsetLength;
1242                         }
1243                 }
1244                 
1245                 public int WordSize {
1246                         set {
1247                                 wordSize = value;
1248                         }
1249                         
1250                         get {
1251                                 return wordSize;
1252                         }
1253                 }
1254                 
1255                 public ArrayList Matchlets {
1256                         get {
1257                                 return matchlets;
1258                         }
1259                 }
1260         }
1261 }
1262