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