Logo Search packages:      
Sourcecode: galaxium version File versions  Download package

MsnObject.cs

/*
 * Galaxium Messenger
 * Copyright (C) 2005-2007 Philippe Durand <draekz@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Xml;

using Anculus.Core;

using Galaxium.Core;

namespace Galaxium.Protocol.Msn
{
      public class MsnObject
      {
            static string _cacheDir;
            static Dictionary<string, P2PObjectTransfer> _objectTransfers;
            
            public delegate void WinkRequestDelegate ();
            
            byte[] _data = new byte[0];
            MemoryStream _updateStream = new MemoryStream ();
            MsnSession _session;
            XmlElement _xml;
            
            public event EventHandler DataChanged;

            public MsnSession Session
            {
                  get { return _session; }
            }
            
            protected XmlElement Xml
            {
                  get { return _xml; }
            }
            
            public virtual string Context
            {
                  get
                  {
                        string context = _xml.OuterXml;
                        
                        // aMSN doesn't recognise the object if we include the space...
                        // (Gives error "Couln't find filename for context")
                        if (context.EndsWith (" />"))
                              context = context.Substring (0, context.Length - 3) + "/>";
                        
                        return context;
                  }
                  set
                  {
                        try
                        {
                              ThrowUtility.ThrowIfEmpty ("context", value);
                        
                              // Decode context if needed
                              string context = EncodingUtility.UrlDecodeSafe (value).Trim ();
                              
                              if (context == "0")
                              {
                                    SetDefaults ();
                                    return;
                              }
                              
                              // Ugly workaround for kmess which doesn't close the xml tag
                              if (context.StartsWith ("<msnobj", StringComparison.InvariantCultureIgnoreCase) && (!context.EndsWith ("/>")))
                              {
                                    if (context.EndsWith ("/"))
                                          context += ">";
                                    else
                                          context += "/>";
                              }
                        
                              XmlDocument xmlDoc = new XmlDocument ();
                              xmlDoc.LoadXml (context);
                        
                              ThrowUtility.ThrowIfFalse ("No msnobject XML DocumentElement", xmlDoc.DocumentElement.Name.Equals ("msnobj", StringComparison.InvariantCultureIgnoreCase));
                        
                              _xml = xmlDoc.DocumentElement;
                        
                              LoadCache ();
                        }
                        catch (Exception ex)
                        {
                              Log.Warn (ex, "Invalid MsnObject Context {0}", value);
                              SetDefaults ();
                        }
                  }
            }
            
            public string ContextEncoded
            {
                  get { return EncodingUtility.UrlEncode (Context); }
            }
            
            public byte[] Data
            {
                  get { return _data; }
                  set
                  {
                        _data = value;
                        _xml.SetAttribute ("Size", _data.Length.ToString ());
                        
                        UpdateHash ();
                        CacheData ();
                        OnDataChanged ();
                  }
            }
            
            public string Checksum
            {
                  get { return _xml.GetAttribute ("SHA1C"); }
            }

            public IMsnEntity Creator
            {
                  get
                  {
                        if (string.IsNullOrEmpty (_xml.GetAttribute ("Creator")))
                              return null;
                        
                        return _session.FindEntity (_xml.GetAttribute ("Creator")) as IMsnEntity;
                  }
                  set
                  {
                        _xml.SetAttribute ("Creator", value.UniqueIdentifier);
                        UpdateChecksum ();
                  }
            }
            
            public int Size
            {
                  get
                  {
                        if (string.IsNullOrEmpty (_xml.GetAttribute ("Size")))
                              return 0;
                        
                        return int.Parse (_xml.GetAttribute ("Size"));
                  }
            }
            
            public MsnObjectType Type
            {
                  get
                  {
                        if (string.IsNullOrEmpty (_xml.GetAttribute ("Type")))
                              return MsnObjectType.Unknown;
                        
                        return (MsnObjectType)int.Parse (_xml.GetAttribute ("Type"));
                  }
                  set
                  {
                        if (Type != value)
                        {
                              _xml.SetAttribute ("Type", ((int)value).ToString ());
                              UpdateChecksum ();
                        }
                  }
            }
            
            public string Location
            {
                  get { return _xml.GetAttribute ("Location"); }
                  set
                  {
                        _xml.SetAttribute ("Location", value);
                        UpdateChecksum ();
                  }
            }
            
            public string Friendly
            {
                  get
                  {
                        string str = Encoding.Unicode.GetString (Convert.FromBase64String (_xml.GetAttribute("Friendly")));
                        
                        if (str.Contains ("\0"))
                              str = str.Substring (0, str.IndexOf ("\0"));
                        
                        return str;
                  }
                  set
                  {
                        _xml.SetAttribute("Friendly", Convert.ToBase64String (Encoding.Unicode.GetBytes (value + "\0")));
                        UpdateChecksum ();
                  }
            }
            
            public string Sha
            {
                  get   { return _xml.GetAttribute("SHA1D"); }
                  internal set
                  {
                        _xml.SetAttribute ("SHA1D", value);
                        UpdateChecksum ();
                  }
            }
            
            public Stream UpdateStream
            {
                  get { return _updateStream; }
            }
            
            public string CacheFilename
            {
                  get
                  {
                        if (_session == null)
                              return null;
                        
                        return Path.Combine (GetCacheDirectory (Creator, Type), EncodingUtility.Base64Encode (Sha));
                  }
            }
            
            static MsnObject ()
            {
                  _cacheDir = Path.Combine (Path.Combine (CoreUtility.GetConfigurationSubDirectory ("Cache"), "MSN"), "ObjectData");
                  BaseUtility.CreateDirectoryIfNeeded (_cacheDir);
                  
                  _objectTransfers = new Dictionary<string, P2PObjectTransfer> ();
            }
            
            public static string GetCacheDirectory (IMsnEntity entity, MsnObjectType type)
            {
                  string dir = Path.Combine (Path.Combine (_cacheDir, entity.UniqueIdentifier), type.ToString ());
                  
                  BaseUtility.CreateDirectoryIfNeeded (dir);
                  
                  return dir;
            }
            
            protected MsnObject (MsnSession session, string context)
            {
                  ThrowUtility.ThrowIfNull ("session", session);
                  ThrowUtility.ThrowIfEmpty ("context", context);
                  
                  _session = session;
                  Context = context;
            }
            
            public MsnObject (MsnSession session)
            {
                  ThrowUtility.ThrowIfNull ("session", session);
            
                  _session = session;
                  
                  SetDefaults ();
            }
            
            void SetDefaults ()
            {
                  XmlDocument xmlDoc = new XmlDocument ();
                  _xml = xmlDoc.CreateElement ("msnobj");
                  xmlDoc.AppendChild (_xml);
                  
                  if (_session != null)
                        Creator = _session.Account as IMsnEntity;
                  
                  Friendly = string.Empty;
                  Location = "0";
                  Type = MsnObjectType.Unknown;
                  _xml.SetAttribute ("Size", "0");
            }
            
            protected virtual void UpdateHash ()
            {
                  _xml.SetAttribute ("SHA1D", CalculateHash ());
                  UpdateChecksum ();
            }
            
            protected virtual string CalculateHash ()
            {
                  HashAlgorithm hash = new SHA1Managed ();              
                  byte[] hashBytes = hash.ComputeHash (_data);
                  return Convert.ToBase64String (hashBytes);
            }
            
            protected virtual void UpdateChecksum ()
            {
                  _xml.SetAttribute ("SHA1C", CalculateChecksum ());
            }
            
            protected virtual string CalculateChecksum ()
            {
                  string checksum = string.Format ("Creator{0}Size{1}Type{2}Location{3}Friendly{4}SHA1D{5}",
                                                   _xml.GetAttribute ("Creator"),
                                                   _xml.GetAttribute ("Size"),
                                                   _xml.GetAttribute ("Type"),
                                                   _xml.GetAttribute ("Location"),
                                                   _xml.GetAttribute ("Friendly"),
                                                   _xml.GetAttribute ("SHA1D"));

                  HashAlgorithm shaAlg = new SHA1Managed ();
                  return Convert.ToBase64String (shaAlg.ComputeHash (Encoding.ASCII.GetBytes (checksum)));
            }
            
            public virtual void UpdateFromStream ()
            {
                  byte[] newData = new byte[_updateStream.Length];
                  _updateStream.Seek (0, SeekOrigin.Begin);
                  _updateStream.Read (newData, 0, (int)_updateStream.Length);
                  
                  Data = newData;
            }
            
            protected virtual void OnDataChanged ()
            {
                  if (DataChanged != null)
                        DataChanged (this, EventArgs.Empty);
            }
            
            protected virtual void CacheData ()
            {
                  File.WriteAllBytes (CacheFilename, _data);
            }
            
            protected virtual void LoadCache ()
            {
                  if ((!string.IsNullOrEmpty (CacheFilename)) && File.Exists (CacheFilename))
                  {
                        _data = File.ReadAllBytes (CacheFilename);
                        OnDataChanged ();
                  }
                  else
                        _data = new byte[0];
            }
            
            public void Request (WinkRequestDelegate callback)
            {
                  if (_data.Length > 0)
                  {
                        if (callback != null)
                              callback ();
                        
                        return;
                  }
                  
                  if (string.IsNullOrEmpty (Sha))
                  {
                        Log.Debug ("Not requesting MsnObject with empty Sha: {0}", Context);
                        return;
                  }
                  
                  if (!(Creator is MsnContact))
                  {
                        Log.Debug ("Not requesting MsnObject with non-MsnContact creator: {0}", Context);
                        return;
                  }
                  
                  // We can't request objects from offline contacts or those on other networks (eg. yahoo)
                  if ((Creator.Presence == MsnPresence.Offline) || (Creator.Network != Network.WindowsLive))
                  {
                        //Log.Debug ("Not requesting MsnObject with offline or non-msn creator: {0}", Context);
                        return;
                  }
                  
                  // We're already transferring the object
                  if (_objectTransfers.ContainsKey (Sha))
                  {
                        // Once the transfer is finished the data should be cached & we can load it
                        _objectTransfers[Sha].Complete += delegate
                        {
                              LoadCache ();
                              
                              if (callback != null)
                                    callback ();
                        };
                        
                        return;
                  }
                  
                  if (_data.Length == 0)
                  {
                        // See if we have the data cached
                        LoadCache ();
                        
                        // We don't, request it
                        if (_data.Length == 0)
                        {
                              P2PObjectTransfer transfer = new P2PObjectTransfer (this);
                              
                              _objectTransfers.Add (Sha, transfer);
                              MsnP2PUtility.Add (transfer);
                              
                              transfer.Complete += delegate
                              {
                                    _objectTransfers.Remove (Sha);
                                    
                                    if (callback != null)
                                          callback ();
                              };
                        }
                        else if (callback != null)
                              callback ();
                  }
            }
            
            public void Request ()
            {
                  Request (null);
            }
            
            public static MsnObject Load (MsnSession session, string context)
            {
                  if (string.IsNullOrEmpty (context) || (context == "0"))
                        return null;
                  
                  MsnObject tmp = new MsnObject (session, context);
                  
                  switch (tmp.Type)
                  {
                  case MsnObjectType.UserDisplay:
                        return new MsnDisplayImage (session, context);
                  case MsnObjectType.DynamicDisplay:
                        return new MsnDynamicDisplayPicture (session, context);
                  case MsnObjectType.Emoticon:
                        return new MsnEmoticon (session, context);
                  case MsnObjectType.Wink:
                        return new MsnWink (session, context);
                  case MsnObjectType.VoiceClip:
                        return new MsnVoiceClip (session, context);
                  default:
                        return tmp;
                  }
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index