using System.Collections;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
+using System.Text;
// Usage:
// - for files:
// TODO:
// - optimize
-// - some stuff returns more then one mime type (means result string has more then one mime type) -> fix me
// - little/big endian stuff for TypeHostXX
-// - inode/... ?
// - async callback ?!?
// - freedesktop org file extensions can have regular expressions also, resolve them too
// - sort match collections by magic priority ( higher = first )
// - MimeGenerated: use indexes to point to mime type name strings instead of repeating the name string each time (in match, subclass, etc.) !?!
-// - shrink mime_file_cache after some time ?!?
-// - buffer is currently hard coded to 8192, value should be determined by MimeGenerated
+// - buffer is currently hard coded to size 8192, value should be determined by MimeGenerated
namespace System.Windows.Forms
{
- internal class MimeType
- {
- private string comment;
- private Hashtable commentsLanguage = new Hashtable();
-
- public string Comment
- {
- get {
- return comment;
- }
- set {
- comment = value;
- }
- }
-
- public Hashtable CommentsLanguage
- {
- get {
- return commentsLanguage;
- }
- set {
- commentsLanguage = value;
- }
- }
- public string GetCommentForLanguage( string language )
- {
- return commentsLanguage[ language ] as string;
- }
- }
-
- internal enum MatchTypes
- {
- TypeString,
- TypeHost16,
- TypeHost32,
- TypeBig16,
- TypeBig32,
- TypeLittle16,
- TypeLittle32,
- TypeByte
- }
-
- internal class Match
- {
- string mimeType;
- byte[] byteValue;
- byte[] mask = null;
- int priority;
- int offset;
- int offsetLength;
- int wordSize = 1;
- MatchTypes matchType;
- ArrayList matches = new ArrayList();
-
- public string MimeType
- {
- set {
- mimeType = value;
- }
-
- get {
- return mimeType;
- }
- }
-
- public byte[] ByteValue
- {
- set {
- byteValue = value;
- }
-
- get {
- return byteValue;
- }
- }
-
- public byte[] Mask
- {
- set {
- mask = value;
- }
-
- get {
- return mask;
- }
- }
-
- public int Priority
- {
- set {
- priority = value;
- }
-
- get {
- return priority;
- }
- }
-
- public ArrayList Matches
- {
- set {
- matches = value;
- }
-
- get {
- return matches;
- }
- }
-
- public int Offset
- {
- set {
- offset = value;
- }
-
- get {
- return offset;
- }
- }
-
- public int OffsetLength
- {
- set {
- offsetLength = value;
- }
-
- get {
- return offsetLength;
- }
- }
-
- public int WordSize
- {
- set {
- wordSize = value;
- }
-
- get {
- return wordSize;
- }
- }
-
- public MatchTypes MatchType
- {
- set {
- matchType = value;
- }
-
- get {
- return matchType;
- }
- }
- }
-
public class Mime
{
public static Mime Instance = new Mime();
private const string octet_stream = "application/octet-stream";
private const string text_plain = "text/plain";
+ private const string zero_file = "application/x-zerosize";
private StringDictionary mime_file_cache = new StringDictionary();
+ private const int mime_file_cache_max_size = 5000;
+
private string search_string;
private static object lock_object = new Object();
+ private int platform = (int) Environment.OSVersion.Platform;
+
+ private bool is_zero_file = false;
+
public Mime( )
{
MimeGenerated.Init( );
return Instance.global_result;
}
- // not tested
public static string GetMimeTypeForString( string input )
{
lock ( lock_object )
}
current_file_name = filename;
- global_result = octet_stream;
+ is_zero_file = false;
- GoByFileName( );
+ if ( !CheckForInode( ) )
+ {
+ global_result = octet_stream;
+
+ GoByFileName( );
+ }
if ( !mime_file_cache.ContainsKey( current_file_name ) )
mime_file_cache.Add( current_file_name, global_result );
+
+ // not tested
+ if ( mime_file_cache.Count > mime_file_cache_max_size )
+ {
+ IEnumerator enumerator = mime_file_cache.GetEnumerator( );
+
+ for ( int i = 0; i < mime_file_cache_max_size - 1000; i++ )
+ {
+ mime_file_cache.Remove( enumerator.Current.ToString( ) );
+ }
+ }
}
private void StartDataLookup( byte[] data )
return;
}
+ private bool CheckForInode( )
+ {
+ if ( ( platform == 4 ) || ( platform == 128 ) )
+ {
+ // *nix platform
+ Mono.Unix.UnixFileInfo ufi = new Mono.Unix.UnixFileInfo( current_file_name );
+
+ if ( ufi.IsFile )
+ {
+ return false;
+ }
+ else
+ if ( ufi.IsDirectory )
+ {
+ global_result = "inode/directory";
+ return true;
+ }
+ else
+ if ( ufi.IsBlockDevice )
+ {
+ global_result = "inode/blockdevice";
+ return true;
+ }
+ else
+ if ( ufi.IsSocket )
+ {
+ global_result = "inode/socket";
+ return true;
+ }
+ else
+ if ( ufi.IsSymbolicLink )
+ {
+ global_result = "inode/symlink";
+ return true;
+ }
+ else
+ if ( ufi.IsCharacterDevice )
+ {
+ global_result = "inode/chardevice";
+ return true;
+ }
+ else
+ if ( ufi.IsFIFO )
+ {
+ global_result = "inode/fifo";
+ return true;
+ }
+ }
+ else
+ {
+ // TODO!!!!
+ // windows platform
+ }
+
+ return false;
+ }
+
private void GoByFileName( )
{
// check if we can open the file
return;
}
- // check for matches with a priority >= 80
- if ( CheckMatch80Plus( ) )
- return;
+ if ( !is_zero_file )
+ {
+ // check for matches with a priority >= 80
+ if ( CheckMatch80Plus( ) )
+ return;
+ }
// check global patterns, aka file extensions...
+ // this should be done for zero size files also,
+ // for example zero size file trash.ccc~ should return
+ // application/x-trash instead of application/x-zerosize
if ( CheckGlobalPatterns( ) )
return;
+ // if file size is zero, no other checks are needed
+ if ( is_zero_file )
+ return;
+
// ok, still nothing matches then try matches with a priority < 80
if ( CheckMatchBelow80( ) )
return;
return false;
}
+ private void CheckGlobalResult( )
+ {
+ int comma_index = global_result.IndexOf( "," );
+
+ if ( comma_index != -1 )
+ {
+ global_result = global_result.Substring( 0, comma_index );
+ }
+ }
+
private bool CheckGlobalPatterns( )
{
string filename = Path.GetFileName( current_file_name );
if ( key.Equals( filename ) )
{
global_result = MimeGenerated.GlobalLiterals[ i ];
+ CheckGlobalResult( );
return true;
}
}
if ( Regex.IsMatch( filename, key ) )
{
global_result = MimeGenerated.GlobalLiterals[ i ];
+ CheckGlobalResult( );
return true;
}
}
if ( filename.EndsWith( key ) )
{
global_result = MimeGenerated.GlobalPatternsLong[ i ];
+ CheckGlobalResult( );
return true;
}
else
if ( filename_lower.EndsWith( key ) )
{
global_result = MimeGenerated.GlobalPatternsLong[ i ];
+ CheckGlobalResult( );
return true;
}
}
if ( global_result != null )
{
+ CheckGlobalResult( );
return true;
}
if ( global_result != null )
{
+ CheckGlobalResult( );
return true;
}
}
if ( filename.EndsWith( key.Replace( "*", "" ) ) )
{
global_result = MimeGenerated.GlobalSufPref[ i ];
+ CheckGlobalResult( );
return true;
}
}
if ( filename.StartsWith( key.Replace( "*", "" ) ) )
{
global_result = MimeGenerated.GlobalSufPref[ i ];
+ CheckGlobalResult( );
return true;
}
}
// using a simple brute force search algorithm
// compare each (masked) value from the buffer with the (masked) value from the match
// TODO:
- // - to find some more speed, maybe use unsafe code
+ // - to find some more speed, maybe we should use unsafe code
// - check if buffer[0] and buffer[lastmatchbyte] match ByteValue[0] and ByteValue[lastmatchbyte] in a match
for ( int offset_counter = 0; offset_counter < match.OffsetLength; offset_counter++ )
file_stream = new FileStream( current_file_name, FileMode.Open, FileAccess.Read ); // FileShare ??? use BinaryReader ???
- file_stream.Read( buffer, 0, buffer.Length );
+ if ( file_stream.Length == 0 )
+ {
+ global_result = zero_file;
+ is_zero_file = true;
+ }
+ else
+ {
+ file_stream.Read( buffer, 0, buffer.Length );
+ }
file_stream.Close( );
-
}
catch (Exception e)
{
return true;
}
- // TODO: conver string to byte array and check matches also.
+ // convert string to byte array
+ byte[] string_byte = ( new ASCIIEncoding( ) ).GetBytes( search_string );
+
+ System.Array.Clear( buffer, 0, buffer.Length );
+
+ if ( string_byte.Length > buffer.Length )
+ {
+ System.Array.Copy( string_byte, buffer, buffer.Length );
+ }
+ else
+ {
+ System.Array.Copy( string_byte, buffer, string_byte.Length );
+ }
+
+ if ( CheckMatch80Plus( ) )
+ return true;
+
+ if ( CheckMatchBelow80( ) )
+ return true;
return false;
}
}
+
+ internal class MimeType
+ {
+ private string comment;
+ private Hashtable commentsLanguage = new Hashtable();
+
+ public string Comment
+ {
+ get {
+ return comment;
+ }
+ set {
+ comment = value;
+ }
+ }
+
+ public Hashtable CommentsLanguage
+ {
+ get {
+ return commentsLanguage;
+ }
+ set {
+ commentsLanguage = value;
+ }
+ }
+ public string GetCommentForLanguage( string language )
+ {
+ return commentsLanguage[ language ] as string;
+ }
+ }
+
+ internal enum MatchTypes
+ {
+ TypeString,
+ TypeHost16,
+ TypeHost32,
+ TypeBig16,
+ TypeBig32,
+ TypeLittle16,
+ TypeLittle32,
+ TypeByte
+ }
+
+ internal class Match
+ {
+ string mimeType;
+ byte[] byteValue;
+ byte[] mask = null;
+ int priority;
+ int offset;
+ int offsetLength;
+ int wordSize = 1;
+ MatchTypes matchType;
+ ArrayList matches = new ArrayList();
+
+ public string MimeType
+ {
+ set {
+ mimeType = value;
+ }
+
+ get {
+ return mimeType;
+ }
+ }
+
+ public byte[] ByteValue
+ {
+ set {
+ byteValue = value;
+ }
+
+ get {
+ return byteValue;
+ }
+ }
+
+ public byte[] Mask
+ {
+ set {
+ mask = value;
+ }
+
+ get {
+ return mask;
+ }
+ }
+
+ public int Priority
+ {
+ set {
+ priority = value;
+ }
+
+ get {
+ return priority;
+ }
+ }
+
+ public ArrayList Matches
+ {
+ set {
+ matches = value;
+ }
+
+ get {
+ return matches;
+ }
+ }
+
+ public int Offset
+ {
+ set {
+ offset = value;
+ }
+
+ get {
+ return offset;
+ }
+ }
+
+ public int OffsetLength
+ {
+ set {
+ offsetLength = value;
+ }
+
+ get {
+ return offsetLength;
+ }
+ }
+
+ public int WordSize
+ {
+ set {
+ wordSize = value;
+ }
+
+ get {
+ return wordSize;
+ }
+ }
+
+ public MatchTypes MatchType
+ {
+ set {
+ matchType = value;
+ }
+
+ get {
+ return matchType;
+ }
+ }
+ }
}