/// <summary> /// Completes the initial connection to the proxy server /// </summary> /// <remarks>This method is used to complete the proxy server transaction for both /// Stage 1 sending and Stage 2 receiver-redirect proxy scenarios</remarks> private void InitProxyConnectFinished(IAsyncResult res) { RendezvousData rd = res.AsyncState as RendezvousData; DirectConnectInfo fti = rd.DirectConnection; try { fti.Transfer.EndConnect(res); ProxyInitializeSend(rd); RendezvousProxyPacket rpp = ReadProxyPacket(rd); if (rpp.Command == RendezvousProxyCommand.Acknowledge) { rd.Port = Marshal.ByteArrayToUshort(rpp.Data, 0); byte[] ipaddr = new byte[4]; Marshal.CopyArray(rpp.Data, ipaddr, 2); rd.ProxyIP = (new IPAddress(ipaddr)).ToString(); // Send the "send file" request on SNAC(04,06):02 // SNAC04.SendDirectConnectionRequest(_parent, rd); // Wait for the proxy to send its 12 byte READY sequence rd.DirectConnection.Transfer.BeginReceive(new byte[12], 0, 12, SocketFlags.None, new AsyncCallback(ProxyReceivedReady), rd); } else { int index = 0; ushort error = Marshal.ByteArrayToUshort(rpp.Data, ref index); if (error == 0x0004) { throw new Exception("Recipient not logged in"); } else if (error == 0x000D) { throw new Exception("Client sent bad request"); } throw new Exception("AOL proxy sent unknown error"); } } catch (Exception ex) { fti.CancelTransfer(ex.Message); } }
/// <summary> /// Constructs a new RendezvousData object for a file transfer /// </summary> private RendezvousData CreateSendFileData(string recipient, string filename) { // Set up a direct connection information structure RendezvousData rd = new RendezvousData(); rd.UserInfo = new UserInfo(); rd.UserInfo.ScreenName = recipient; rd.ParentSession = _parent; rd.Capability = Capabilities.SendFiles; rd.Type = 0x0000; DirectConnectInfo fti = new DirectConnectInfo(rd, DirectConnectType.FileTransfer); // Put the basename of the file into the structure int slashindex = filename.LastIndexOf("\\"); fti.Header.Name = filename.Substring(slashindex + 1, filename.Length - (slashindex + 1)); fti.LocalFileName = filename; // Get the size of the file to put in the structure and checksum it fti.Header.Size = (uint)(new FileInfo(filename)).Length; fti.Header.Checksum = Checksum(_parent, filename); // Couldn't locate the passed-in file if (fti.Header.Checksum == 0xFFFFFFFF) { return(null); } // Set the totals. Eventually OscarLib may support > 1 files at a time fti.TotalFiles = 1; fti.TotalParts = 1; fti.FilesLeft = 1; fti.TotalSize = fti.Header.Size; rd.DirectConnection = fti; return(rd); }
/// <summary> /// Constructs a new RendezvousData object for a file transfer /// </summary> private RendezvousData CreateSendFileData(string recipient, string filename) { // Set up a direct connection information structure RendezvousData rd = new RendezvousData(); rd.UserInfo = new UserInfo(); rd.UserInfo.ScreenName = recipient; rd.ParentSession = _parent; rd.Capability = Capabilities.SendFiles; rd.Type = 0x0000; DirectConnectInfo fti = new DirectConnectInfo(rd, DirectConnectType.FileTransfer); // Put the basename of the file into the structure int slashindex = filename.LastIndexOf("\\"); fti.Header.Name = filename.Substring(slashindex + 1, filename.Length - (slashindex + 1)); fti.LocalFileName = filename; // Get the size of the file to put in the structure and checksum it fti.Header.Size = (uint)(new FileInfo(filename)).Length; fti.Header.Checksum = Checksum(_parent, filename); // Couldn't locate the passed-in file if (fti.Header.Checksum == 0xFFFFFFFF) { return null; } // Set the totals. Eventually OscarLib may support > 1 files at a time fti.TotalFiles = 1; fti.TotalParts = 1; fti.FilesLeft = 1; fti.TotalSize = fti.Header.Size; rd.DirectConnection = fti; return rd; }
/// <summary> /// The callback function for socket reads during a file transfer /// </summary> /// <param name="res">An <see cref="IAsyncResult"/> object</param> private void SendFileTransferReceive(IAsyncResult res) { int bytesread = 0; RendezvousData rd = (RendezvousData)res.AsyncState; DirectConnectInfo fti = rd.DirectConnection; // End the async receive operation try { bytesread = fti.Transfer.EndReceive(res); if (bytesread == 0) { throw new Exception("Remote client cancelled transfer"); } } catch (Exception ex) { rd.DirectConnection.CancelTransfer(ex.Message); return; } // Write the received data out to the file try { fti.DataStream.Write(fti.DataChunk, 0, bytesread); fti.StreamPosition += (uint)bytesread; } catch (Exception ex) { rd.DirectConnection.CancelTransfer(ex.Message); return; } // Checksum the received chunk fti.Header.ReceivedChecksum = ChecksumChunk(fti.DataChunk, (uint)bytesread, fti.Header.ReceivedChecksum); // Signal progress to the parent session rd.ParentSession.OnFileTransferProgress( RendezvousManager.GetKeyFromCookie(rd.Cookie), rd.DirectConnection.StreamPosition, rd.DirectConnection.Header.Size); // Check to see if the transfer has finished if (fti.StreamPosition >= fti.Header.Size) { fti.DataChunk = null; fti.DataStream.Close(); // Send out the acknowledgement, compare the checksums, and finish up SendFileReceiverDone(rd); if (fti.Header.ReceivedChecksum != fti.Header.Checksum) { rd.DirectConnection.CancelTransfer("Received data does not match expected checksum"); return; } rd.DirectConnection.CompleteTransfer(); } else { // Keep receiving asynchronously fti.Transfer.BeginReceive(fti.DataChunk, 0, fti.DataChunk.Length, SocketFlags.None, new AsyncCallback(SendFileTransferReceive), rd); } }
/// <summary> /// Receives the PROMPT message and responds with an ACK message, then prepares the transfer socket to receive data /// </summary> /// <param name="rd">The <see cref="RendezvousData"/> object receiving a file</param> private void BeginReceiveFile(RendezvousData rd) { byte[] filetransferheader = null; int index = 0; // Read in 256 bytes, PROMPT type and blank cookie filetransferheader = new byte[256]; try { while (index < filetransferheader.Length) { index += rd.DirectConnection.Transfer.Receive( filetransferheader, index, filetransferheader.Length - index, SocketFlags.None); } } catch (Exception ex) { string message = "Error negotiating file transfer:" + Environ.NewLine + ex.Message; throw new Exception(message); } index = 8; DirectConnectInfo dci = rd.DirectConnection; //Marshal.ByteArrayToFTI(filetransferheader, ref index, ref dci); rd.DirectConnection = dci; // Just to be sure // Respond with the same header, but with the ACK type and the ICBM cookie set index = 6; Marshal.InsertUshort(filetransferheader, 0x0202, ref index); Marshal.CopyArray(rd.Cookie, filetransferheader, 0, ref index); index = 0; try { while (index < filetransferheader.Length) { index += rd.DirectConnection.Transfer.Send( filetransferheader, index, filetransferheader.Length - index, SocketFlags.None); } } catch (Exception ex) { string message = "Error negotiating file transfer:" + Environ.NewLine + ex.Message; throw new Exception(message); } // Open the file for writing try { rd.DirectConnection.DataChunk = new byte[8192]; rd.DirectConnection.DataStream = (new StreamWriter(rd.DirectConnection.LocalFileName, false)).BaseStream; rd.DirectConnection.StreamPosition = 0; } catch (Exception) { throw new Exception("Can't open target file for writing"); } // Signal the parent session that the file transfer has started rd.ParentSession.OnFileTransferProgress(RendezvousManager.GetKeyFromCookie(rd.Cookie), 0, rd.DirectConnection.Header.Size); // Start receiving data rd.DirectConnection.Transfer.BeginReceive(rd.DirectConnection.DataChunk, 0, rd.DirectConnection.DataChunk.Length, SocketFlags.None, new AsyncCallback(SendFileTransferReceive), rd); }