1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
use futures::{sink::SinkExt, stream::StreamExt};
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_util::codec::Framed;
use xmpp_parsers::{ns, Element, Jid};

use crate::xmpp_codec::{Packet, XmppCodec};
use crate::xmpp_stream::XMPPStream;
use crate::{Error, ProtocolError};

/// Sends a `<stream:stream>`, then wait for one from the server, and
/// construct an XMPPStream.
pub async fn start<S: AsyncRead + AsyncWrite + Unpin>(
    mut stream: Framed<S, XmppCodec>,
    jid: Jid,
    ns: String,
) -> Result<XMPPStream<S>, Error> {
    let attrs = [
        ("to".to_owned(), jid.domain().to_string()),
        ("version".to_owned(), "1.0".to_owned()),
        ("xmlns".to_owned(), ns.clone()),
        ("xmlns:stream".to_owned(), ns::STREAM.to_owned()),
    ]
    .iter()
    .cloned()
    .collect();
    stream.send(Packet::StreamStart(attrs)).await?;

    let stream_attrs;
    loop {
        match stream.next().await {
            Some(Ok(Packet::StreamStart(attrs))) => {
                stream_attrs = attrs;
                break;
            }
            Some(Ok(_)) => {}
            Some(Err(e)) => return Err(e.into()),
            None => return Err(Error::Disconnected),
        }
    }

    let stream_ns = stream_attrs
        .get("xmlns")
        .ok_or(ProtocolError::NoStreamNamespace)?
        .clone();
    let stream_id = stream_attrs
        .get("id")
        .ok_or(ProtocolError::NoStreamId)?
        .clone();
    let stream = if stream_ns == "jabber:client" && stream_attrs.get("version").is_some() {
        let stream_features;
        loop {
            match stream.next().await {
                Some(Ok(Packet::Stanza(stanza))) if stanza.is("features", ns::STREAM) => {
                    stream_features = stanza;
                    break;
                }
                Some(Ok(_)) => {}
                Some(Err(e)) => return Err(e.into()),
                None => return Err(Error::Disconnected),
            }
        }
        XMPPStream::new(jid, stream, ns, stream_id, stream_features)
    } else {
        // FIXME: huge hack, shouldn’t be an element!
        XMPPStream::new(
            jid,
            stream,
            ns,
            stream_id.clone(),
            Element::builder(stream_id, ns::STREAM).build(),
        )
    };
    Ok(stream)
}