public void Transfer(SISession session) { IEnumerable <Streamhost> source = null; if (this.ProxyAllowed) { try { source = this.GetProxyList(); } catch { } } try { if ((source != null) && (source.Count <Streamhost>() > 0)) { this.MediatedTransfer(session, source); } else { this.DirectTransfer(session); } } catch (Exception exception) { CommonConfig.Logger.WriteError("socket5传输过程出现异常!", exception); this.TransferAborted.Raise <TransferAbortedEventArgs>(this, new TransferAbortedEventArgs(session)); this.siFileTransfer.InvalidateSession(session.Sid); } }
private void Data(string sessionId, Iq stanza) { sessionId.ThrowIfNull <string>("sessionId"); stanza.ThrowIfNull <Iq>("stanza"); XmlElement element = stanza.Data["data"]; if (element == null) { throw new ArgumentException("Invalid stanza, missing data element."); } SISession session = this.siFileTransfer.GetSession(sessionId, stanza.From, base.im.Jid); if (session == null) { throw new ArgumentException("Invalid session-id."); } byte[] buffer = Convert.FromBase64String(element.InnerText); try { session.Stream.Write(buffer, 0, buffer.Length); } catch (Exception exception) { throw new IOException("The stream could not be written.", exception); } session.Count += buffer.Length; this.BytesTransferred.Raise <BytesTransferredEventArgs>(this, new BytesTransferredEventArgs(session)); }
/// <summary> /// Performs a mediated transfer, meaning we send the data over a proxy server. /// </summary> /// <param name="session">The SI session whose data to transfer.</param> /// <param name="proxies"></param> /// <exception cref="Socks5Exception">The SOCKS5 connection to the designated /// proxy server could not be established.</exception> /// <exception cref="XmppErrorException">The server returned an XMPP error code. /// Use the Error property of the XmppErrorException to obtain the specific /// error condition.</exception> /// <exception cref="XmppException">The server returned invalid data or another /// unspecified XMPP error occurred.</exception> void MediatedTransfer(SISession session, IEnumerable <Streamhost> proxies) { var proxy = NegotiateProxy(session, proxies); // Connect to the designated proxy. using (var client = new Socks5Client(proxy.Host, proxy.Port)) { // Send the SOCKS5 Connect command. string hostname = Sha1(session.Sid + session.From + session.To); SocksReply reply = client.Request(SocksCommand.Connect, hostname, 0); if (reply.Status != ReplyStatus.Succeeded) { throw new Socks5Exception("SOCKS5 Connect request failed."); } // Activate the bytetream. var xml = Xml.Element("query", "http://jabber.org/protocol/bytestreams") .Attr("sid", session.Sid).Child( Xml.Element("activate").Text(session.To.ToString())); Iq iq = im.IqRequest(IqType.Set, proxy.Jid, im.Jid, xml); if (iq.Type == IqType.Error) { throw Util.ExceptionFromError(iq, "Could not activate the bytestream."); } // Finally, go ahead and send the data to the proxy. SendData(session, client.GetStream()); } }
/// <summary> /// Waits for a client connection to be made and subsequently verifies it. /// </summary> /// <param name="session">The SI session whose data to transfer.</param> /// <param name="server">The instance of the SOCKS5 server to accept /// client connections on.</param> /// <param name="timeout">The number of milliseconds to wait for a client /// connection before returning to the caller, or -1 to wait /// indefinitely.</param> /// <exception cref="SocketException">An error occurred when accessing the /// underlying socket of the SOCKS5 server instance.</exception> /// <exception cref="Socks5Exception">The SOCKS5 negotiation with the client /// failed.</exception> /// <exception cref="TimeoutException">A timeout was specified and it /// expired.</exception> /// <exception cref="IOException">The stream could not be read, or the /// operation timed out.</exception> void AcceptClientConnection(SISession session, Socks5Server server, int timeout = -1) { var request = server.Accept(timeout); if (request.Command != SocksCommand.Connect) { throw new Socks5Exception("Unexpected SOCKS5 command: " + request.Command); } if (request.ATyp != ATyp.Domain) { throw new Socks5Exception("Unexpected ATyp: " + request.ATyp); } string hash = (string)request.Destination; // Calculate the SHA-1 hash and compare it with the one in the request. string calculated = Sha1(session.Sid + im.Jid + session.To); if (calculated != hash) { throw new Socks5Exception("Hostname hash mismatch."); } // We're good to go. server.Reply(ReplyStatus.Succeeded, hash, request.Port); }
private Streamhost NegotiateProxy(SISession session, IEnumerable <Streamhost> proxies) { XmlElement e = Xml.Element("query", "http://jabber.org/protocol/bytestreams").Attr("sid", session.Sid); foreach (Streamhost streamhost in proxies) { e.Child(Xml.Element("streamhost", null).Attr("jid", streamhost.Jid.ToString()).Attr("host", streamhost.Host).Attr("port", streamhost.Port.ToString())); } Iq errorIq = base.im.IqRequest(IqType.Set, session.To, base.im.Jid, e, null, -1, ""); if (errorIq.Type == IqType.Error) { throw Util.ExceptionFromError(errorIq, "The SOCKS5 negotiation failed."); } XmlElement element2 = errorIq.Data["query"]; if ((element2 == null) || (element2.NamespaceURI != "http://jabber.org/protocol/bytestreams")) { throw new XmppException("Erroneous response."); } XmlElement element3 = element2["streamhost-used"]; if (element3 == null) { throw new XmppException("Missing streamhost-used element."); } string proxyJid = element3.GetAttribute("jid"); Streamhost streamhost2 = proxies.FirstOrDefault <Streamhost>(proxy => proxy.Jid == proxyJid); if (streamhost2 == null) { throw new XmppException("Invalid streamhost JID."); } return(streamhost2); }
/// <summary> /// Processes an IBB 'data' request. /// </summary> /// <param name="sessionId">The mandatory session id attribute of the 'data' /// element.</param> /// <param name="stanza">The IQ stanza containing the request.</param> /// <exception cref="ArgumentNullException">The sessionId parameter or the /// stanza parameter is null.</exception> /// <exception cref="ArgumentException">The IQ stanza is missing the /// mandatory 'data' element, or the specified session id is not associated /// with a file-stream.</exception> /// <exception cref="FormatException">The data contained in the 'data' element /// is not a valid BASE64-encoded string.</exception> /// <exception cref="IOException">The data could not be written to the /// file-stream. Consult the InnerException property of the IOException object /// to obtain the specific reason.</exception> void Data(string sessionId, Iq stanza) { sessionId.ThrowIfNull("sessionId"); stanza.ThrowIfNull("stanza"); var data = stanza.Data["data"]; if (data == null) { throw new ArgumentException("Invalid stanza, missing data element."); } SISession session = siFileTransfer.GetSession(sessionId, stanza.From, im.Jid); if (session == null) { throw new ArgumentException("Invalid session-id."); } string base64 = data.InnerText; // Decode base64 string and write decoded binary data to file. byte[] bytes = Convert.FromBase64String(base64); try { session.Stream.Write(bytes, 0, bytes.Length); } catch (Exception e) { throw new IOException("The stream could not be written.", e); } // Update the byte count and raise the 'BytesTransferred' event. session.Count = session.Count + bytes.Length; BytesTransferred.Raise(this, new BytesTransferredEventArgs(session)); }
public void CancelFileTransfer(FileTransfer transfer) { transfer.ThrowIfNull <FileTransfer>("transfer"); SISession session = this.GetSession(transfer.SessionId, transfer.From, transfer.To); if (session == null) { throw new ArgumentException("The specified transfer instance does not represent an active data-transfer operation."); } session.Extension.CancelTransfer(session); }
private void Close(string sessionId, Iq stanza) { sessionId.ThrowIfNull <string>("sessionId"); stanza.ThrowIfNull <Iq>("stanza"); SISession session = this.siFileTransfer.GetSession(sessionId, stanza.From, stanza.To); if (session != null) { this.siFileTransfer.InvalidateSession(sessionId); if (session.Count < session.Size) { this.TransferAborted.Raise <TransferAbortedEventArgs>(this, new TransferAbortedEventArgs(session)); } } }
/// <summary> /// Performs the actual data-transfer implied by the specified /// SI session. /// </summary> /// <param name="session">The SI session whose data to transfer.</param> /// <exception cref="ArgumentNullException">The session parameter is /// null.</exception> /// <exception cref="NotSupportedException">The XMPP extension /// implementing this method is not supported by the intended recipient's /// XMPP client.</exception> /// <exception cref="XmppErrorException">The server or the XMPP entity /// with the specified JID returned an XMPP error code. Use the Error /// property of the XmppErrorException to obtain the specific error /// condition.</exception> /// <exception cref="XmppException">The server returned invalid data or /// another unspecified XMPP error occurred.</exception> public void Transfer(SISession session) { session.ThrowIfNull("session"); // Open the negotiated IBB. OpenStream(session.To, session.Sid); byte[] buf = new byte[blockSize]; // 'seq' is defined as 16-bit unsigned short value that wraps around. ushort seq = 0; long left = session.Size; try { while (left > 0) { int read = session.Stream.Read(buf, 0, blockSize); left = left - read; if (read <= 0) { break; } string b64 = Convert.ToBase64String(buf, 0, read); var data = Xml.Element("data", "http://jabber.org/protocol/ibb") .Attr("sid", session.Sid) .Attr("seq", seq.ToString()) .Text(b64); seq++; Iq response = im.IqRequest(IqType.Set, session.To, im.Jid, data); if (response.Type == IqType.Error) { throw Util.ExceptionFromError(response); } session.Count = session.Count + read; // Raise the 'BytesTransferred' event. BytesTransferred.Raise(this, new BytesTransferredEventArgs(session)); } } catch (ObjectDisposedException) { // This means the IO-stream has been disposed because we cancelled // the transfer. Just fall through. } catch { // The IQ response is of type 'error', the other site has cancelled // the transfer. TransferAborted.Raise(this, new TransferAbortedEventArgs(session)); // Rethrow. throw; } finally { // Gracefully close the IBB. CloseStream(session.To, session.Sid); } }
/// <summary> /// Performs the actual data-transfer implied by the specified /// SI session. /// </summary> /// <param name="session">The SI session whose data to transfer.</param> /// <exception cref="ArgumentNullException">The session parameter is /// null.</exception> /// <exception cref="NotSupportedException">The XMPP extension /// implementing this method is not supported by the intended recipient's /// XMPP client.</exception> /// <exception cref="XmppErrorException">The server or the XMPP entity /// with the specified JID returned an XMPP error code. Use the Error /// property of the XmppErrorException to obtain the specific error /// condition.</exception> /// <exception cref="XmppException">The server returned invalid data or /// another unspecified XMPP error occurred.</exception> public void Transfer(SISession session) { IEnumerable <Streamhost> proxies = null; // Determine if this is going to be a direct or a mediated transfer. if (ProxyAllowed) { try { var externalAddresses = GetExternalAddresses(); // If all of our external addresses are behind NATs, we may need a proxy. bool behindNAT = externalAddresses.All(addr => BehindNAT(addr)); if (behindNAT) { // Look for user-defined proxies first. if (Proxies.Count > 0) { proxies = Proxies; } else { // Otherwise query XMPP server for a list of proxies. proxies = GetProxyList(); } } } catch { // Retrieving the external addresses may fail as well as querying // the XMPP server for proxies. In these cases, we at least try // with a direct transfer. } } try { if (proxies != null && proxies.Count() > 0) { MediatedTransfer(session, proxies); } else { DirectTransfer(session); } } catch (Exception) { // Raise the 'TransferAborted' event. TransferAborted.Raise(this, new TransferAbortedEventArgs(session)); // Invalidate the session. siFileTransfer.InvalidateSession(session.Sid); } }
/// <summary> /// Negotiates with the target which of the specified SOCKS5 proxies to use. /// </summary> /// <param name="session">The SI session whose data to transfer.</param> /// <param name="proxies">An enumerable collection of SOCKS5 proxy servers /// to advertise to the target.</param> /// <returns>The proxy server to use for the data transfer.</returns> /// <exception cref="XmppErrorException">The server returned an XMPP error code. /// Use the Error property of the XmppErrorException to obtain the specific /// error condition.</exception> /// <exception cref="XmppException">The server returned invalid data or another /// unspecified XMPP error occurred.</exception> Streamhost NegotiateProxy(SISession session, IEnumerable <Streamhost> proxies) { // Compile XML containing our list of proxy servers. var xml = Xml.Element("query", "http://jabber.org/protocol/bytestreams") .Attr("sid", session.Sid); foreach (var proxy in proxies) { xml.Child(Xml.Element("streamhost") .Attr("jid", proxy.Jid.ToString()) .Attr("host", proxy.Host) .Attr("port", proxy.Port.ToString())); } // Wait for the other site to tell us which proxy server it selected. var iq = im.IqRequest(IqType.Set, session.To, im.Jid, xml); if (iq.Type == IqType.Error) { throw Util.ExceptionFromError(iq, "The SOCKS5 negotiation failed."); } var query = iq.Data["query"]; if (query == null || query.NamespaceURI != "http://jabber.org/protocol/bytestreams") { throw new XmppException("Erroneous response."); } if (query.GetAttribute("sid") != session.Sid) { throw new XmppException("Invalid session identifier."); } var used = query["streamhost-used"]; if (used == null) { throw new XmppException("Missing streamhost-used element."); } var proxyJid = used.GetAttribute("jid"); var streamhost = proxies.FirstOrDefault(proxy => proxy.Jid == proxyJid); if (streamhost == null) { throw new XmppException("Invalid streamhost JID."); } return(streamhost); }
public void Transfer(SISession session) { session.ThrowIfNull <SISession>("session"); this.OpenStream(session.To, session.Sid); byte[] buffer = new byte[0x1000]; ushort num = 0; long size = session.Size; try { while (size > 0L) { int length = session.Stream.Read(buffer, 0, 0x1000); size -= length; if (length <= 0) { return; } string text = Convert.ToBase64String(buffer, 0, length); XmlElement data = Xml.Element("data", "http://jabber.org/protocol/ibb").Attr("sid", session.Sid).Attr("seq", num.ToString()).Text(text); num = (ushort)(num + 1); Iq errorIq = base.im.IqRequest(IqType.Set, session.To, base.im.Jid, data, null, -1, ""); if (errorIq.Type == IqType.Error) { throw Util.ExceptionFromError(errorIq, null); } session.Count += length; this.BytesTransferred.Raise <BytesTransferredEventArgs>(this, new BytesTransferredEventArgs(session)); } } catch (ObjectDisposedException exception) { CommonConfig.Logger.WriteError(exception); } catch (Exception exception2) { CommonConfig.Logger.WriteError(exception2); this.TransferAborted.Raise <TransferAbortedEventArgs>(this, new TransferAbortedEventArgs(session)); throw; } finally { this.CloseStream(session.To, session.Sid); } }
/// <summary> /// Processes an IBB 'close' request. /// </summary> /// <param name="sessionId">The mandatory session id attribute of the 'close' /// element.</param> /// <param name="stanza">The IQ stanza containing the request.</param> /// <exception cref="ArgumentNullException">The sessionId parameter or the /// stanza parameter is null.</exception> void Close(string sessionId, Iq stanza) { sessionId.ThrowIfNull("sessionId"); stanza.ThrowIfNull("stanza"); SISession session = siFileTransfer.GetSession(sessionId, stanza.From, stanza.To); // We don't allow the other site to close a session that we opened. if (session != null) { siFileTransfer.InvalidateSession(sessionId); // Had all bytes been received when we got the 'close' request? // Otherwise, the other site cancelled the transfer prematurely. if (session.Count < session.Size) { TransferAborted.Raise(this, new TransferAbortedEventArgs(session)); } } }
private void AcceptClientConnection(SISession session, Socks5Server server, int timeout = -1) { SocksRequest request = server.Accept(timeout); if (request.Command != SocksCommand.Connect) { throw new Socks5Exception("Unexpected SOCKS5 command: " + request.Command); } if (request.ATyp != ATyp.Domain) { throw new Socks5Exception("Unexpected ATyp: " + request.ATyp); } string destination = (string)request.Destination; if (this.Sha1(session.Sid + base.im.Jid + session.To) != destination) { throw new Socks5Exception("Hostname hash mismatch."); } server.Reply(ReplyStatus.Succeeded, destination, request.Port); }
private void OnInitiationResult(InitiationResult result, Jid to, string name, Stream stream, long size, string description, Action <bool, FileTransfer> cb) { Exception exception; FileTransfer transfer = new FileTransfer(base.im.Jid, to, name, size, null, description, 0L); try { IDataStream extension = base.im.GetExtension(result.Method) as IDataStream; SISession session = new SISession(result.SessionId, stream, size, false, base.im.Jid, to, extension); this.siSessions.TryAdd(result.SessionId, session); this.metaData.TryAdd(result.SessionId, new FileMetaData(name, description)); if (cb != null) { cb(true, transfer); } try { CommonConfig.Logger.WriteInfo("开始执行Transfer文件传输方法"); extension.Transfer(session); } catch (Exception exception1) { exception = exception1; CommonConfig.Logger.WriteError("发送文件过程出错:" + exception.ToString(), exception); } } catch (Exception exception2) { exception = exception2; if (stream != null) { stream.Dispose(); } CommonConfig.Logger.WriteError("OnInitiationResult发送文件异常", exception); if (cb != null) { cb(false, transfer); } } }
private void MediatedTransfer(SISession session, IEnumerable <Streamhost> proxies) { Streamhost streamhost = this.NegotiateProxy(session, proxies); using (Socks5Client client = new Socks5Client(streamhost.Host, streamhost.Port, null, null)) { string domain = this.Sha1(session.Sid + session.From + session.To); if (client.Request(SocksCommand.Connect, domain, 0).Status != ReplyStatus.Succeeded) { CommonConfig.Logger.WriteInfo("SOCKS5 Connect request failed."); throw new Socks5Exception("SOCKS5 Connect request failed."); } CommonConfig.Logger.WriteInfo("SOCKS5 Connect successed."); XmlElement data = Xml.Element("query", "http://jabber.org/protocol/bytestreams").Attr("sid", session.Sid).Child(Xml.Element("activate", null).Text(session.To.ToString())); Iq errorIq = base.im.IqRequest(IqType.Set, streamhost.Jid, base.im.Jid, data, null, -1, ""); if (errorIq.Type == IqType.Error) { throw Util.ExceptionFromError(errorIq, "Could not activate the bytestream."); } this.SendData(session, client.GetStream()); } }
/// <summary> /// Receives the actual data after SOCKS5 negotiation has been completed. /// </summary> /// <param name="stanza">The original requesting IQ stanza.</param> /// <param name="sid">The session-id associated with the /// data-transfer.</param> /// <param name="stream">The stream from which to read the incoming /// data.</param> /// <exception cref="XmppException">The specifid SID does not denote a /// valid SI session.</exception> void ReceiveData(Iq stanza, string sid, Stream stream) { SISession session = siFileTransfer.GetSession(sid, stanza.From, stanza.To); if (session == null) { throw new XmppException("Invalid session-id: " + sid); } long left = session.Size; try { while (left > 0) { byte[] buffer = new byte[4096]; int read = stream.Read(buffer, 0, buffer.Length); if (read <= 0) { break; } left = left - read; session.Stream.Write(buffer, 0, read); // Update the byte count and raise the 'BytesTransferred' event. session.Count = session.Count + read; BytesTransferred.Raise(this, new BytesTransferredEventArgs(session)); } } catch (ObjectDisposedException) { // This means the IO-stream has been disposed because we cancelled // the transfer. Just fall through. } finally { // Tear down the SI session. siFileTransfer.InvalidateSession(sid); // If not all bytes have been transferred, the data-transfer must have // been aborted prematurely. if (session.Count < session.Size) { TransferAborted.Raise(this, new TransferAbortedEventArgs(session)); } } }
private void ReceiveData(Iq stanza, string sid, Stream stream) { SISession session = this.siFileTransfer.GetSession(sid, stanza.From, stanza.To); if (session == null) { throw new XmppException("Invalid session-id: " + sid); } long size = session.Size; try { while (size > 0L) { byte[] buffer = new byte[0x1000]; int count = stream.Read(buffer, 0, buffer.Length); if (count <= 0) { return; } size -= count; session.Stream.Write(buffer, 0, count); session.Count += count; this.BytesTransferred.Raise <BytesTransferredEventArgs>(this, new BytesTransferredEventArgs(session)); } } catch (ObjectDisposedException) { } finally { this.siFileTransfer.InvalidateSession(sid); if (session.Count < session.Size) { this.TransferAborted.Raise <TransferAbortedEventArgs>(this, new TransferAbortedEventArgs(session)); } } }
private void SendData(SISession session, Stream stream) { long size = session.Size; try { CommonConfig.Logger.WriteInfo("开始通过socket5方式传输流,大小为:" + size); while (size > 0L) { byte[] buffer = new byte[0x1000]; int count = session.Stream.Read(buffer, 0, (int)Math.Min(size, (long)buffer.Length)); if (count > 0) { stream.Write(buffer, 0, count); CommonConfig.Logger.WriteInfo("socket5传read=" + count.ToString()); } else { break; } size -= count; session.Count += count; this.BytesTransferred.Raise <BytesTransferredEventArgs>(this, new BytesTransferredEventArgs(session)); } CommonConfig.Logger.WriteInfo("传输完成,socket5方式共:" + session.Size); } catch (ObjectDisposedException) { } finally { this.siFileTransfer.InvalidateSession(session.Sid); if (session.Count < session.Size) { this.TransferAborted.Raise <TransferAbortedEventArgs>(this, new TransferAbortedEventArgs(session)); } } }
/// <summary> /// Invoked whenever a 'Stream Initiation' request for file transfers /// is received. /// </summary> /// <param name="from">The JID of the XMPP entity that wishes to initiate /// the data-stream.</param> /// <param name="si">The 'si' element of the request.</param> /// <returns>The response to the SI request or an error element to include /// in the IQ response.</returns> XmlElement OnStreamInitiationRequest(Jid from, XmlElement si) { try { string method = SelectStreamMethod(si["feature"]); // If the session-id is already in use, we cannot process the request. string sid = si.GetAttribute("id"); if (String.IsNullOrEmpty(sid) || siSessions.ContainsKey(sid)) { return(new XmppError(ErrorType.Cancel, ErrorCondition.Conflict).Data); } // Extract file information and hand to user. var file = si["file"]; string desc = file["desc"] != null ? file["desc"].InnerText : null, name = file.GetAttribute("name"); int size = Int32.Parse(file.GetAttribute("size")); FileTransfer transfer = new FileTransfer(from, im.Jid, name, size, sid, desc); string savePath = TransferRequest.Invoke(transfer); // User has rejected the request. if (savePath == null) { return(new XmppError(ErrorType.Cancel, ErrorCondition.NotAcceptable).Data); } // Create an SI session instance. SISession session = new SISession(sid, File.OpenWrite(savePath), size, true, from, im.Jid, im.GetExtension(method) as IDataStream); siSessions.TryAdd(sid, session); // Store the file's meta data. metaData.TryAdd(sid, new FileMetaData(name, desc)); // Construct and return the negotiation result. return(Xml.Element("si", "http://jabber.org/protocol/si").Child( FeatureNegotiation.Create(new SubmitForm( new ListField("stream-method", method))))); } catch { return(new XmppError(ErrorType.Cancel, ErrorCondition.BadRequest).Data); } }
/// <summary> /// Sends the actual data associated with the specified session over the /// specified stream. /// </summary> /// <param name="session">The SI session whose data to transfer.</param> /// <param name="stream">The (network) stream representing the connection /// to the client.</param> void SendData(SISession session, Stream stream) { long left = session.Size; try { while (left > 0) { byte[] buffer = new byte[4096]; int read = session.Stream.Read(buffer, 0, (int)Math.Min(left, buffer.Length)); if (read > 0) { stream.Write(buffer, 0, read); } else { break; } left = left - read; // Update the byte count and raise the 'BytesTransferred' event. session.Count = session.Count + read; BytesTransferred.Raise(this, new BytesTransferredEventArgs(session)); } } catch (ObjectDisposedException) { // This means the IO-stream has been disposed because we cancelled // the transfer. Just fall through. } finally { // Tear down the SI session. siFileTransfer.InvalidateSession(session.Sid); // If not all bytes have been transferred, the data-transfer must have // been aborted prematurely. if (session.Count < session.Size) { TransferAborted.Raise(this, new TransferAbortedEventArgs(session)); } } }
private XmlElement OnStreamInitiationRequest(Jid from, XmlElement si) { FileStream stream = null; try { string str = this.SelectStreamMethod(si["feature"]); string attribute = si.GetAttribute("id"); if (string.IsNullOrEmpty(attribute) || this.siSessions.ContainsKey(attribute)) { return(new XmppError(ErrorType.Cancel, ErrorCondition.Conflict, new XmlElement[0]).Data); } XmlElement element = si["file"]; string description = (element["desc"] != null) ? element["desc"].InnerText : null; string name = element.GetAttribute("name"); int num = int.Parse(element.GetAttribute("size")); FileTransfer transfer = new FileTransfer(from, base.im.Jid, name, (long)num, attribute, description, 0L); string path = this.TransferRequest(transfer); if (path == null) { return(new XmppError(ErrorType.Cancel, ErrorCondition.NotAcceptable, new XmlElement[0]).Data); } stream = File.OpenWrite(path); SISession session = new SISession(attribute, stream, (long)num, true, from, base.im.Jid, base.im.GetExtension(str) as IDataStream); this.siSessions.TryAdd(attribute, session); this.metaData.TryAdd(attribute, new FileMetaData(name, description)); return(Xml.Element("si", "http://jabber.org/protocol/si").Child(FeatureNegotiation.Create(new SubmitForm(new DataField[] { new ListField("stream-method", str) })))); } catch { if (stream != null) { stream.Close(); } return(new XmppError(ErrorType.Cancel, ErrorCondition.BadRequest, new XmlElement[0]).Data); } }
/// <summary> /// Invoked once the result of a pending stream-initiation operation has been /// received. /// </summary> /// <param name="result">The result of the stream-initiation operation. If /// this parameter is null, stream-initiation failed.</param> /// <param name="to">The JID of the XMPP user to offer the file to.</param> /// <param name="stream">The stream to read the file-data from.</param> /// <param name="name">The name of the file, as offered to the XMPP user /// with the specified JID.</param> /// <param name="size">The number of bytes to transfer.</param> /// <param name="cb">A callback method invoked once the other site has /// accepted or rejected the file-transfer request.</param> /// <param name="description">A description of the file so the receiver can /// better understand what is being sent.</param> /// <remarks>This is called in the context of an arbitrary thread.</remarks> void OnInitiationResult(InitiationResult result, Jid to, string name, Stream stream, long size, string description, Action <bool, FileTransfer> cb) { FileTransfer transfer = new FileTransfer(im.Jid, to, name, size, null, description); try { // Get the instance of the data-stream extension that the other site has // selected. IDataStream ext = im.GetExtension(result.Method) as IDataStream; // Register the session. SISession session = new SISession(result.SessionId, stream, size, false, im.Jid, to, ext); siSessions.TryAdd(result.SessionId, session); // Store the file's meta data. metaData.TryAdd(result.SessionId, new FileMetaData(name, description)); // Invoke user-provided callback. if (cb != null) { cb.Invoke(true, transfer); } // Perform the actual data-transfer. try { ext.Transfer(session); } catch (Exception e) { // Nothing to do here. } } catch { // Something went wrong. Invoke user-provided callback to let them know // the file-transfer can't be performed. if (cb != null) { cb.Invoke(false, transfer); } } }
/// <summary> /// Cancels the data-transfer implied by the specified SI session. /// </summary> /// <param name="session">The SI session whose data-transfer to /// cancel.</param> /// <exception cref="ArgumentNullException">The session parameter is /// null.</exception> public void CancelTransfer(SISession session) { session.ThrowIfNull("session"); siFileTransfer.InvalidateSession(session.Sid); }
/// <summary> /// Performs a direct transfer, meaning we act as a SOCKS5 server. /// </summary> /// <param name="session">The SI session whose data to transfer.</param> /// <exception cref="Socks5Exception">The SOCKS5 server could not be /// instantiated.</exception> /// <exception cref="XmppErrorException">The server returned an XMPP error code. /// Use the Error property of the XmppErrorException to obtain the specific /// error condition.</exception> /// <exception cref="XmppException">The server returned invalid data or another /// unspecified XMPP error occurred.</exception> void DirectTransfer(SISession session) { // Create the listening SOCKS5 server. Socks5Server socks5Server = null; try { socks5Server = CreateSocks5Server(serverPortFrom, serverPortTo); } catch (Exception e) { throw new Socks5Exception("The SOCKS5 server could not be created.", e); } IEnumerable <IPAddress> externalAddresses = null; try { externalAddresses = GetExternalAddresses(); // Check if we might need to forward the server port. if (externalAddresses.Any(addr => BehindNAT(addr)) && UseUPnP) { try { //UPnP.ForwardPort(socks5Server.Port, ProtocolType.Tcp, // "XMPP SOCKS5 File-transfer"); } catch (InvalidOperationException) { // If automatic port forwarding failed for whatever reason, just // go on normally. The user can still configure forwarding manually. } } } catch (NotSupportedException) { // Not much we can do. } // Waiting for a client connection is a blocking call and we need to // negotiate the SOCKS5 connection after we send the IQ request but // _before_ we wait for the IQ response. Task.Factory.StartNew(() => { try { AcceptClientConnection(session, socks5Server, acceptTimeout); SendData(session, socks5Server.GetStream()); } finally { socks5Server.Close(); } }); // Send target a list of streamhosts, one for each active network interface. var xml = Xml.Element("query", "http://jabber.org/protocol/bytestreams") .Attr("sid", session.Sid); // Compile a set of all our IP addresses that we can advertise. ISet <IPAddress> ips = new HashSet <IPAddress>(); if (externalAddresses != null) { ips.UnionWith(externalAddresses); } ips.UnionWith(GetIpAddresses()); foreach (var ip in ips) { xml.Child(Xml.Element("streamhost") .Attr("jid", im.Jid.ToString()) .Attr("host", ip.ToString()) .Attr("port", socks5Server.Port.ToString())); } // Send IQ with streamhosts to the target. var iq = im.IqRequest(IqType.Set, session.To, im.Jid, xml); if (iq.Type == IqType.Error) { throw Util.ExceptionFromError(iq, "The SOCKS5 connection could not " + "be established."); } }
/// <summary> /// Cancels the data-transfer implied by the specified SI session. /// </summary> /// <param name="session">The SI session whose data-transfer to /// cancel.</param> /// <exception cref="ArgumentNullException">The session parameter is /// null.</exception> public void CancelTransfer(SISession session) { session.ThrowIfNull("session"); siFileTransfer.InvalidateSession(session.Sid); TransferAborted.Raise(this, new TransferAbortedEventArgs(session)); }
public TransferAbortedEventArgs(SISession session) { session.ThrowIfNull <SISession>("session"); this.Session = session; }
internal FileTransfer(SISession session, string name, string description) : this(session.From, session.To, name, session.Size, session.Sid, description, session.Count) { }
/// <summary> /// Invoked whenever a 'Stream Initiation' request for file transfers /// is received. /// </summary> /// <param name="from">The JID of the XMPP entity that wishes to initiate /// the data-stream.</param> /// <param name="si">The 'si' element of the request.</param> /// <returns>The response to the SI request or an error element to include /// in the IQ response.</returns> XmlElement OnStreamInitiationRequest(Jid from, XmlElement si) { try { string method = SelectStreamMethod(si["feature"]); // If the session-id is already in use, we cannot process the request. string sid = si.GetAttribute("id"); if (String.IsNullOrEmpty(sid) || siSessions.ContainsKey(sid)) return new XmppError(ErrorType.Cancel, ErrorCondition.Conflict).Data; // Extract file information and hand to user. var file = si["file"]; string desc = file["desc"] != null ? file["desc"].InnerText : null, name = file.GetAttribute("name"); int size = Int32.Parse(file.GetAttribute("size")); FileTransfer transfer = new FileTransfer(from, im.Jid, name, size, sid, desc); string savePath = TransferRequest.Invoke(transfer); // User has rejected the request. if (savePath == null) return new XmppError(ErrorType.Cancel, ErrorCondition.NotAcceptable).Data; // Create an SI session instance. SISession session = new SISession(sid, File.OpenWrite(savePath), size, true, from, im.Jid, im.GetExtension(method) as IDataStream); siSessions.TryAdd(sid, session); // Store the file's meta data. metaData.TryAdd(sid, new FileMetaData(name, desc)); // Construct and return the negotiation result. return Xml.Element("si", "http://jabber.org/protocol/si").Child( FeatureNegotiation.Create(new SubmitForm( new ListField("stream-method", method)))); } catch { return new XmppError(ErrorType.Cancel, ErrorCondition.BadRequest).Data; } }
/// <summary> /// Initializes a new instance of the BytesTransferredEventArgs class. /// </summary> /// <param name="session">The session for which the event is raised.</param> /// <exception cref="ArgumentNullException">The session parameter /// is null.</exception> public TransferAbortedEventArgs(SISession session) { session.ThrowIfNull("session"); Session = session; }
/// <summary> /// Initializes a new instance of the BytesTransferredEventArgs class. /// </summary> /// <param name="session">The session for which the event is raised.</param> /// <exception cref="ArgumentNullException">The session parameter /// is null.</exception> public BytesTransferredEventArgs(SISession session) { session.ThrowIfNull("session"); Session = session; }
/// <summary> /// Invoked once the result of a pending stream-initiation operation has been /// received. /// </summary> /// <param name="result">The result of the stream-initiation operation. If /// this parameter is null, stream-initiation failed.</param> /// <param name="to">The JID of the XMPP user to offer the file to.</param> /// <param name="stream">The stream to read the file-data from.</param> /// <param name="name">The name of the file, as offered to the XMPP user /// with the specified JID.</param> /// <param name="size">The number of bytes to transfer.</param> /// <param name="cb">A callback method invoked once the other site has /// accepted or rejected the file-transfer request.</param> /// <param name="description">A description of the file so the receiver can /// better understand what is being sent.</param> /// <remarks>This is called in the context of an arbitrary thread.</remarks> void OnInitiationResult(InitiationResult result, Jid to, string name, Stream stream, long size, string description, Action<bool, FileTransfer> cb) { FileTransfer transfer = new FileTransfer(im.Jid, to, name, size, null, description); try { // Get the instance of the data-stream extension that the other site has // selected. IDataStream ext = im.GetExtension(result.Method) as IDataStream; // Register the session. SISession session = new SISession(result.SessionId, stream, size, false, im.Jid, to, ext); siSessions.TryAdd(result.SessionId, session); // Store the file's meta data. metaData.TryAdd(result.SessionId, new FileMetaData(name, description)); // Invoke user-provided callback. if (cb != null) cb.Invoke(true, transfer); // Perform the actual data-transfer. try { ext.Transfer(session); } catch(Exception e) { // Nothing to do here. } } catch { // Something went wrong. Invoke user-provided callback to let them know // the file-transfer can't be performed. if (cb != null) cb.Invoke(false, transfer); } }
public BytesTransferredEventArgs(SISession session) { session.ThrowIfNull <SISession>("session"); this.Session = session; }