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

P2PFileTransfer.cs

/*
 * Galaxium Messenger
 * 
 * Copyright (C) 2008 Paul Burton <paulburton89@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.IO;

using Anculus.Core;

using Galaxium.Client;

namespace Galaxium.Protocol.Msn
{
      [MsnP2PApplication (2, "5D3E02AB-6190-11D3-BBBB-00C04F795683")]
      public class P2PFileTransfer : AbstractMsnP2PSessionApplication
      {
            public event EventHandler Progressed;
            
            FTContext _context;
            Stream _data;
            bool _sending;
            bool _sendingData;
            P2PMessage _dataMsg;
            MsnFileTransfer _fileTransfer;
            
            public FTContext Context
            {
                  get { return _context; }
            }
            
            public Stream DataStream
            {
                  get { return _data; }
                  set { _data = value; }
            }
            
            public bool Sending
            {
                  get { return _sending; }
            }
            
            public long Transferred
            {
                  get
                  {
                        if (Sending)
                              return _data.Position;
                        
                        return _data.Length;
                  }
            }
            
            public P2PFileTransfer (MsnP2PSession p2pSession)
                  : base (p2pSession)
            {
                  _context = new FTContext (Convert.FromBase64String (P2PSession.Invite.MIMEBody["Context"].Value));
                  _sending = false;
                  
                  _data = File.OpenWrite (Path.Combine (FileTransferUtility.DestinationFolder, _context.Filename));
                  
                  _fileTransfer = new MsnFileTransfer (Session, Remote, this);
                  FileTransferUtility.Add (_fileTransfer);
                  
                  P2PSession.Waiting += P2PSessionWaiting;
            }
            
            public P2PFileTransfer (MsnContact remote, Stream data, string filename)
                  : base (remote.Session.Account as MsnAccount, remote)
            {
                  _context = new FTContext (filename, (ulong)data.Length);
                  _data = data;
                  _sending = true;
            }
            
            public P2PFileTransfer (MsnContact remote, string filename)
                  : this (remote, File.OpenRead (filename), Path.GetFileName (filename))
            {
            }
            
            public override void Dispose ()
            {
                  if (_data != null)
                        _data.Close ();
                  
                  base.Dispose ();
            }
            
            void P2PSessionWaiting (object sender, EventArgs args)
            {
                  if ((P2PSession.State == MsnP2PSessionState.WaitingForLocal) && (_fileTransfer != null))
                  {
                        P2PSession.Waiting -= P2PSessionWaiting;
                        
                        ActivityUtility.EmitActivity (this, new ReceivedFileActivity(Session, _fileTransfer));
                        Session.EmitTransferInvitationReceived (new FileTransferEventArgs (_fileTransfer));
                  }
            }
            
            protected void OnProgressed ()
            {
                  if (this.Progressed != null)
                        this.Progressed (this, EventArgs.Empty);
            }
            
            public override bool CheckInvite (SLPRequestMessage invite)
            {
                  try
                  {
                        FTContext context = new FTContext (Convert.FromBase64String (invite.MIMEBody["Context"].Value));
                        Log.Debug ("{0} ({1} bytes) (base {2})", context.Filename, context.FileSize, base.CheckInvite (invite));
                        // Invite is valid if it has a filename and the file size is greater than 0
                        return base.CheckInvite (invite) && (!string.IsNullOrEmpty (context.Filename)) && (context.FileSize > 0);
                  }
                  catch (Exception ex)
                  {
                        // We can't parse the context, so refuse the invite
                        Log.Error (ex, "Unable to parse file transfer invite");
                        Log.Debug ("{0}", invite);
                        
                        return false;
                  }
            }
            
            public override string CreateInviteContext ()
            {
                  return Convert.ToBase64String (_context.ToByteArray ());
            }
            
            public override void Begin ()
            {
                  base.Begin ();
                  
                  if (Sending)
                  {
                        _data.Seek (0, SeekOrigin.Begin);
                        
                        _dataMsg = new P2PMessage (Session);
                        _dataMsg.Header.MessageID = P2PSession.NextID ();
                        _dataMsg.Header.Flags = P2PHeaderFlag.FileData;
                        _dataMsg.Header.TotalSize = (ulong)_data.Length;
                        
                        _sendingData = true;
                        
                        SendChunk ();
                  }
            }
            
            protected override void ProcessOutQueue (bool forceReady)
            {
                  if (_sendingData && (Bridge.Ready || forceReady) && (OutQueue.Count == 0))
                        SendChunk ();
                  else
                        base.ProcessOutQueue (forceReady);
            }
            
            void SendChunk ()
            {
                  _dataMsg.Header.ChunkOffset = (ulong)_data.Position;
                  
                  byte[] data = new byte[Bridge.MaxDataSize];
                  int read = _data.Read (data, 0, data.Length);
                  
                  //Log.Debug ("Sending {0} bytes ({1} to {2} of {3})", read, _dataMsg.Header.ChunkOffset, _data.Position - 1, _data.Length);
                  
                  if (read < data.Length)
                  {
                        byte[] newData = new byte[read];
                        Array.Copy (data, newData, newData.Length);
                        data = newData;
                  }
                  
                  _dataMsg.Payload = data;
                  
                  /*Log.Debug ("SessionID: {0}", _dataMsg.Header.SessionID);
                  Log.Debug ("MessageID: {0}", _dataMsg.Header.MessageID);
                  Log.Debug ("Offset:    {0}", _dataMsg.Header.ChunkOffset);
                  Log.Debug ("TotalSize: {0}", _dataMsg.Header.TotalSize);
                  Log.Debug ("ChunkSize: {0}", _dataMsg.Header.ChunkSize);
                  Log.Debug ("Flags:     {0}", _dataMsg.Header.Flags);
                  Log.Debug ("AckID:     {0}", _dataMsg.Header.AckID);
                  Log.Debug ("AckUID:    {0}", _dataMsg.Header.AckUID);
                  Log.Debug ("AckSize:   {0}", _dataMsg.Header.AckSize);*/
                  
                  if (_data.Position == _data.Length)
                  {
                        // This is the last chunk of data, register the ackHandler
                        Send (_dataMsg, delegate
                        {
                              OnComplete ();
                        });
                        
                        _sendingData = false;
                  }
                  else
                        Send (_dataMsg, null);
                  
                  OnProgressed ();
            }
            
            public override bool ProcessMessage (IMsnP2PBridge bridge, P2PMessage msg)
            {
                  if (((msg.Header.Flags & P2PHeaderFlag.Data) == P2PHeaderFlag.Data) ||
                      (msg.Header.Flags == P2PHeaderFlag.Normal))
                  {
                        _data.Write (msg.Payload, 0, msg.Payload.Length);
                        
                        OnProgressed ();
                        
                        if (_data.Length == (long)_context.FileSize)
                        {
                              // Finished transfer
                              
                              Send (msg.CreateAck ());
                              
                              OnComplete ();
                              P2PSession.Close ();
                        }
                        
                        return true;
                  }
                  
#if debug
                  return false;
#else
                  // If we're not debugging then we should try to continue even if we don't understand this message
                  return true;
#endif
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index