irc/client/
mod.rs

1//! A simple, thread-safe, and async-friendly IRC client library.
2//!
3//! This API provides the ability to connect to an IRC server via the
4//! [`Client`] type. The [`Client`] trait that
5//! [`Client`] implements provides methods for communicating with the
6//! server.
7//!
8//! # Examples
9//!
10//! Using these APIs, we can connect to a server and send a one-off message (in this case,
11//! identifying with the server).
12//!
13//! ```no_run
14//! # extern crate irc;
15//! use irc::client::prelude::Client;
16//!
17//! # #[tokio::main]
18//! # async fn main() -> irc::error::Result<()> {
19//! let client = Client::new("config.toml").await?;
20//! client.identify()?;
21//! # Ok(())
22//! # }
23//! ```
24//!
25//! We can then use functions from [`Client`] to receive messages from the
26//! server in a blocking fashion and perform any desired actions in response. The following code
27//! performs a simple call-and-response when the bot's name is mentioned in a channel.
28//!
29//! ```no_run
30//! use irc::client::prelude::*;
31//! use futures::*;
32//!
33//! # #[tokio::main]
34//! # async fn main() -> irc::error::Result<()> {
35//! let mut client = Client::new("config.toml").await?;
36//! let mut stream = client.stream()?;
37//! client.identify()?;
38//!
39//! while let Some(message) = stream.next().await.transpose()? {
40//!     if let Command::PRIVMSG(channel, message) = message.command {
41//!         if message.contains(client.current_nickname()) {
42//!             client.send_privmsg(&channel, "beep boop").unwrap();
43//!         }
44//!     }
45//! }
46//! # Ok(())
47//! # }
48//! ```
49
50#[cfg(feature = "ctcp")]
51use chrono::prelude::*;
52use futures_util::{
53    future::{FusedFuture, Future},
54    ready,
55    stream::{FusedStream, Stream},
56};
57use futures_util::{
58    sink::Sink as _,
59    stream::{SplitSink, SplitStream, StreamExt as _},
60};
61use parking_lot::RwLock;
62use std::{
63    collections::HashMap,
64    fmt,
65    path::Path,
66    pin::Pin,
67    sync::Arc,
68    task::{Context, Poll},
69};
70use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
71
72use crate::{
73    client::{
74        conn::Connection,
75        data::{Config, User},
76    },
77    error,
78    proto::{
79        mode::ModeType,
80        CapSubCommand::{END, LS, REQ},
81        Capability, ChannelMode, Command,
82        Command::{
83            ChannelMODE, AUTHENTICATE, CAP, INVITE, JOIN, KICK, KILL, NICK, NICKSERV, NOTICE, OPER,
84            PART, PASS, PONG, PRIVMSG, QUIT, SAMODE, SANICK, TOPIC, USER,
85        },
86        Message, Mode, NegotiationVersion, Response,
87    },
88};
89
90pub mod conn;
91pub mod data;
92mod mock;
93pub mod prelude;
94pub mod transport;
95
96macro_rules! pub_state_base {
97    () => {
98        /// Changes the modes for the specified target.
99        pub fn send_mode<S, T>(&self, target: S, modes: &[Mode<T>]) -> error::Result<()>
100        where
101            S: fmt::Display,
102            T: ModeType,
103        {
104            self.send(T::mode(&target.to_string(), modes))
105        }
106
107        /// Joins the specified channel or chanlist.
108        pub fn send_join<S>(&self, chanlist: S) -> error::Result<()>
109        where
110            S: fmt::Display,
111        {
112            self.send(JOIN(chanlist.to_string(), None, None))
113        }
114
115        /// Joins the specified channel or chanlist using the specified key or keylist.
116        pub fn send_join_with_keys<S1, S2>(&self, chanlist: S1, keylist: S2) -> error::Result<()>
117        where
118            S1: fmt::Display,
119            S2: fmt::Display,
120        {
121            self.send(JOIN(chanlist.to_string(), Some(keylist.to_string()), None))
122        }
123
124        /// Sends a notice to the specified target.
125        pub fn send_notice<S1, S2>(&self, target: S1, message: S2) -> error::Result<()>
126        where
127            S1: fmt::Display,
128            S2: fmt::Display,
129        {
130            let message = message.to_string();
131            for line in message.split("\r\n") {
132                self.send(NOTICE(target.to_string(), line.to_string()))?
133            }
134            Ok(())
135        }
136    };
137}
138
139macro_rules! pub_sender_base {
140    () => {
141        /// Sends a request for a list of server capabilities for a specific IRCv3 version.
142        pub fn send_cap_ls(&self, version: NegotiationVersion) -> error::Result<()> {
143            self.send(Command::CAP(
144                None,
145                LS,
146                match version {
147                    NegotiationVersion::V301 => None,
148                    NegotiationVersion::V302 => Some("302".to_owned()),
149                },
150                None,
151            ))
152        }
153
154        /// Sends an IRCv3 capabilities request for the specified extensions.
155        pub fn send_cap_req(&self, extensions: &[Capability]) -> error::Result<()> {
156            let append = |mut s: String, c| {
157                s.push_str(c);
158                s.push(' ');
159                s
160            };
161            let mut exts = extensions
162                .iter()
163                .map(|c| c.as_ref())
164                .fold(String::new(), append);
165            let len = exts.len() - 1;
166            exts.truncate(len);
167            self.send(CAP(None, REQ, None, Some(exts)))
168        }
169
170        /// Sends a SASL AUTHENTICATE message with the specified data.
171        pub fn send_sasl<S: fmt::Display>(&self, data: S) -> error::Result<()> {
172            self.send(AUTHENTICATE(data.to_string()))
173        }
174
175        /// Sends a SASL AUTHENTICATE request to use the PLAIN mechanism.
176        pub fn send_sasl_plain(&self) -> error::Result<()> {
177            self.send_sasl("PLAIN")
178        }
179
180        /// Sends a SASL AUTHENTICATE request to use the EXTERNAL mechanism.
181        pub fn send_sasl_external(&self) -> error::Result<()> {
182            self.send_sasl("EXTERNAL")
183        }
184
185        /// Sends a SASL AUTHENTICATE request to abort authentication.
186        pub fn send_sasl_abort(&self) -> error::Result<()> {
187            self.send_sasl("*")
188        }
189
190        /// Sends a PONG with the specified message.
191        pub fn send_pong<S>(&self, msg: S) -> error::Result<()>
192        where
193            S: fmt::Display,
194        {
195            self.send(PONG(msg.to_string(), None))
196        }
197
198        /// Parts the specified channel or chanlist.
199        pub fn send_part<S>(&self, chanlist: S) -> error::Result<()>
200        where
201            S: fmt::Display,
202        {
203            self.send(PART(chanlist.to_string(), None))
204        }
205
206        /// Attempts to oper up using the specified username and password.
207        pub fn send_oper<S1, S2>(&self, username: S1, password: S2) -> error::Result<()>
208        where
209            S1: fmt::Display,
210            S2: fmt::Display,
211        {
212            self.send(OPER(username.to_string(), password.to_string()))
213        }
214
215        /// Sends a message to the specified target. If the message contains IRC newlines (`\r\n`), it
216        /// will automatically be split and sent as multiple separate `PRIVMSG`s to the specified
217        /// target. If you absolutely must avoid this behavior, you can do
218        /// `client.send(PRIVMSG(target, message))` directly.
219        pub fn send_privmsg<S1, S2>(&self, target: S1, message: S2) -> error::Result<()>
220        where
221            S1: fmt::Display,
222            S2: fmt::Display,
223        {
224            let message = message.to_string();
225            for line in message.split("\r\n") {
226                self.send(PRIVMSG(target.to_string(), line.to_string()))?
227            }
228            Ok(())
229        }
230
231        /// Sets the topic of a channel or requests the current one.
232        /// If `topic` is an empty string, it won't be included in the message.
233        pub fn send_topic<S1, S2>(&self, channel: S1, topic: S2) -> error::Result<()>
234        where
235            S1: fmt::Display,
236            S2: fmt::Display,
237        {
238            let topic = topic.to_string();
239            self.send(TOPIC(
240                channel.to_string(),
241                if topic.is_empty() { None } else { Some(topic) },
242            ))
243        }
244
245        /// Kills the target with the provided message.
246        pub fn send_kill<S1, S2>(&self, target: S1, message: S2) -> error::Result<()>
247        where
248            S1: fmt::Display,
249            S2: fmt::Display,
250        {
251            self.send(KILL(target.to_string(), message.to_string()))
252        }
253
254        /// Kicks the listed nicknames from the listed channels with a comment.
255        /// If `message` is an empty string, it won't be included in the message.
256        pub fn send_kick<S1, S2, S3>(
257            &self,
258            chanlist: S1,
259            nicklist: S2,
260            message: S3,
261        ) -> error::Result<()>
262        where
263            S1: fmt::Display,
264            S2: fmt::Display,
265            S3: fmt::Display,
266        {
267            let message = message.to_string();
268            self.send(KICK(
269                chanlist.to_string(),
270                nicklist.to_string(),
271                if message.is_empty() {
272                    None
273                } else {
274                    Some(message)
275                },
276            ))
277        }
278
279        /// Changes the mode of the target by force.
280        /// If `modeparams` is an empty string, it won't be included in the message.
281        pub fn send_samode<S1, S2, S3>(
282            &self,
283            target: S1,
284            mode: S2,
285            modeparams: S3,
286        ) -> error::Result<()>
287        where
288            S1: fmt::Display,
289            S2: fmt::Display,
290            S3: fmt::Display,
291        {
292            let modeparams = modeparams.to_string();
293            self.send(SAMODE(
294                target.to_string(),
295                mode.to_string(),
296                if modeparams.is_empty() {
297                    None
298                } else {
299                    Some(modeparams)
300                },
301            ))
302        }
303
304        /// Forces a user to change from the old nickname to the new nickname.
305        pub fn send_sanick<S1, S2>(&self, old_nick: S1, new_nick: S2) -> error::Result<()>
306        where
307            S1: fmt::Display,
308            S2: fmt::Display,
309        {
310            self.send(SANICK(old_nick.to_string(), new_nick.to_string()))
311        }
312
313        /// Invites a user to the specified channel.
314        pub fn send_invite<S1, S2>(&self, nick: S1, chan: S2) -> error::Result<()>
315        where
316            S1: fmt::Display,
317            S2: fmt::Display,
318        {
319            self.send(INVITE(nick.to_string(), chan.to_string()))
320        }
321
322        /// Quits the server entirely with a message.
323        /// This defaults to `Powered by Rust.` if none is specified.
324        pub fn send_quit<S>(&self, msg: S) -> error::Result<()>
325        where
326            S: fmt::Display,
327        {
328            let msg = msg.to_string();
329            self.send(QUIT(Some(if msg.is_empty() {
330                "Powered by Rust.".to_string()
331            } else {
332                msg
333            })))
334        }
335
336        /// Sends a CTCP-escaped message to the specified target.
337        /// This requires the CTCP feature to be enabled.
338        #[cfg(feature = "ctcp")]
339        pub fn send_ctcp<S1, S2>(&self, target: S1, msg: S2) -> error::Result<()>
340        where
341            S1: fmt::Display,
342            S2: fmt::Display,
343        {
344            let msg = msg.to_string();
345            for line in msg.split("\r\n") {
346                self.send(PRIVMSG(
347                    target.to_string(),
348                    format!("\u{001}{}\u{001}", line),
349                ))?
350            }
351            Ok(())
352        }
353
354        /// Sends an action command to the specified target.
355        /// This requires the CTCP feature to be enabled.
356        #[cfg(feature = "ctcp")]
357        pub fn send_action<S1, S2>(&self, target: S1, msg: S2) -> error::Result<()>
358        where
359            S1: fmt::Display,
360            S2: fmt::Display,
361        {
362            self.send_ctcp(target, &format!("ACTION {}", msg.to_string())[..])
363        }
364
365        /// Sends a finger request to the specified target.
366        /// This requires the CTCP feature to be enabled.
367        #[cfg(feature = "ctcp")]
368        pub fn send_finger<S: fmt::Display>(&self, target: S) -> error::Result<()>
369        where
370            S: fmt::Display,
371        {
372            self.send_ctcp(target, "FINGER")
373        }
374
375        /// Sends a version request to the specified target.
376        /// This requires the CTCP feature to be enabled.
377        #[cfg(feature = "ctcp")]
378        pub fn send_version<S>(&self, target: S) -> error::Result<()>
379        where
380            S: fmt::Display,
381        {
382            self.send_ctcp(target, "VERSION")
383        }
384
385        /// Sends a source request to the specified target.
386        /// This requires the CTCP feature to be enabled.
387        #[cfg(feature = "ctcp")]
388        pub fn send_source<S>(&self, target: S) -> error::Result<()>
389        where
390            S: fmt::Display,
391        {
392            self.send_ctcp(target, "SOURCE")
393        }
394
395        /// Sends a user info request to the specified target.
396        /// This requires the CTCP feature to be enabled.
397        #[cfg(feature = "ctcp")]
398        pub fn send_user_info<S>(&self, target: S) -> error::Result<()>
399        where
400            S: fmt::Display,
401        {
402            self.send_ctcp(target, "USERINFO")
403        }
404
405        /// Sends a finger request to the specified target.
406        /// This requires the CTCP feature to be enabled.
407        #[cfg(feature = "ctcp")]
408        pub fn send_ctcp_ping<S>(&self, target: S) -> error::Result<()>
409        where
410            S: fmt::Display,
411        {
412            let time = Local::now();
413            self.send_ctcp(target, &format!("PING {}", time.timestamp())[..])
414        }
415
416        /// Sends a time request to the specified target.
417        /// This requires the CTCP feature to be enabled.
418        #[cfg(feature = "ctcp")]
419        pub fn send_time<S>(&self, target: S) -> error::Result<()>
420        where
421            S: fmt::Display,
422        {
423            self.send_ctcp(target, "TIME")
424        }
425    };
426}
427
428/// A stream of `Messages` received from an IRC server via an `Client`.
429///
430/// Interaction with this stream relies on the `futures` API, but is only expected for less
431/// traditional use cases. To learn more, you can view the documentation for the
432/// [`futures`](https://docs.rs/futures/) crate, or the tutorials for
433/// [`tokio`](https://tokio.rs/docs/getting-started/futures/).
434#[derive(Debug)]
435pub struct ClientStream {
436    state: Arc<ClientState>,
437    stream: SplitStream<Connection>,
438    // In case the client stream also handles outgoing messages.
439    outgoing: Option<Outgoing>,
440}
441
442impl ClientStream {
443    /// collect stream and collect all messages available.
444    pub async fn collect(mut self) -> error::Result<Vec<Message>> {
445        let mut output = Vec::new();
446
447        while let Some(message) = self.next().await {
448            match message {
449                Ok(message) => output.push(message),
450                Err(e) => return Err(e),
451            }
452        }
453
454        Ok(output)
455    }
456}
457
458impl FusedStream for ClientStream {
459    fn is_terminated(&self) -> bool {
460        false
461    }
462}
463
464impl Stream for ClientStream {
465    type Item = Result<Message, error::Error>;
466
467    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
468        if let Some(outgoing) = self.as_mut().outgoing.as_mut() {
469            match Pin::new(outgoing).poll(cx) {
470                Poll::Ready(Ok(())) => {
471                    // assure that we wake up again to check the incoming stream.
472                    cx.waker().wake_by_ref();
473                    return Poll::Ready(None);
474                }
475                Poll::Ready(Err(e)) => {
476                    cx.waker().wake_by_ref();
477                    return Poll::Ready(Some(Err(e)));
478                }
479                Poll::Pending => (),
480            }
481        }
482
483        match ready!(Pin::new(&mut self.as_mut().stream).poll_next(cx)) {
484            Some(Ok(msg)) => {
485                self.state.handle_message(&msg)?;
486                Poll::Ready(Some(Ok(msg)))
487            }
488            other => Poll::Ready(other),
489        }
490    }
491}
492
493/// Thread-safe internal state for an IRC server connection.
494#[derive(Debug)]
495struct ClientState {
496    sender: Sender,
497    /// The configuration used with this connection.
498    config: Config,
499    /// A thread-safe map of channels to the list of users in them.
500    chanlists: RwLock<HashMap<String, Vec<User>>>,
501    /// A thread-safe index to track the current alternative nickname being used.
502    alt_nick_index: RwLock<usize>,
503    /// Default ghost sequence to send if one is required but none is configured.
504    default_ghost_sequence: Vec<String>,
505}
506
507impl ClientState {
508    fn new(sender: Sender, config: Config) -> ClientState {
509        ClientState {
510            sender,
511            config,
512            chanlists: RwLock::new(HashMap::new()),
513            alt_nick_index: RwLock::new(0),
514            default_ghost_sequence: vec![String::from("GHOST")],
515        }
516    }
517
518    fn config(&self) -> &Config {
519        &self.config
520    }
521
522    fn send<M: Into<Message>>(&self, msg: M) -> error::Result<()> {
523        let msg = msg.into();
524        self.handle_sent_message(&msg)?;
525        self.sender.send(msg)
526    }
527
528    /// Gets the current nickname in use.
529    fn current_nickname(&self) -> &str {
530        let alt_nicks = self.config().alternate_nicknames();
531        let index = self.alt_nick_index.read();
532
533        match *index {
534            0 => self
535                .config()
536                .nickname()
537                .expect("current_nickname should not be callable if nickname is not defined."),
538            i => alt_nicks[i - 1].as_str(),
539        }
540    }
541
542    /// Handles sent messages internally for basic client functionality.
543    fn handle_sent_message(&self, msg: &Message) -> error::Result<()> {
544        log::trace!("[SENT] {}", msg.to_string());
545
546        if let PART(ref chan, _) = msg.command {
547            let _ = self.chanlists.write().remove(chan);
548        }
549
550        Ok(())
551    }
552
553    /// Handles received messages internally for basic client functionality.
554    fn handle_message(&self, msg: &Message) -> error::Result<()> {
555        log::trace!("[RECV] {}", msg.to_string());
556        match msg.command {
557            JOIN(ref chan, _, _) => self.handle_join(msg.source_nickname().unwrap_or(""), chan),
558            PART(ref chan, _) => self.handle_part(msg.source_nickname().unwrap_or(""), chan),
559            KICK(ref chan, ref user, _) => self.handle_part(user, chan),
560            QUIT(_) => self.handle_quit(msg.source_nickname().unwrap_or("")),
561            NICK(ref new_nick) => {
562                self.handle_nick_change(msg.source_nickname().unwrap_or(""), new_nick)
563            }
564            ChannelMODE(ref chan, ref modes) => self.handle_mode(chan, modes),
565            PRIVMSG(ref target, ref body) => {
566                if body.starts_with('\u{001}') {
567                    let tokens: Vec<_> = {
568                        let end = if body.ends_with('\u{001}') && body.len() > 1 {
569                            body.len() - 1
570                        } else {
571                            body.len()
572                        };
573                        body[1..end].split(' ').collect()
574                    };
575                    if target.starts_with('#') {
576                        self.handle_ctcp(target, &tokens)?
577                    } else if let Some(user) = msg.source_nickname() {
578                        self.handle_ctcp(user, &tokens)?
579                    }
580                }
581            }
582            Command::Response(Response::RPL_NAMREPLY, ref args) => self.handle_namreply(args),
583            Command::Response(Response::RPL_ENDOFMOTD, _)
584            | Command::Response(Response::ERR_NOMOTD, _) => {
585                self.send_nick_password()?;
586                self.send_umodes()?;
587
588                let config_chans = self.config().channels();
589                for chan in config_chans {
590                    match self.config().channel_key(chan) {
591                        Some(key) => self.send_join_with_keys::<&str, &str>(chan, key)?,
592                        None => self.send_join(chan)?,
593                    }
594                }
595                let joined_chans = self.chanlists.read();
596                for chan in joined_chans
597                    .keys()
598                    .filter(|x| !config_chans.iter().any(|c| c == *x))
599                {
600                    self.send_join(chan)?
601                }
602            }
603            Command::Response(Response::ERR_NICKNAMEINUSE, _)
604            | Command::Response(Response::ERR_ERRONEOUSNICKNAME, _) => {
605                let alt_nicks = self.config().alternate_nicknames();
606                let mut index = self.alt_nick_index.write();
607
608                if *index >= alt_nicks.len() {
609                    return Err(error::Error::NoUsableNick);
610                } else {
611                    self.send(NICK(alt_nicks[*index].to_owned()))?;
612                    *index += 1;
613                }
614            }
615            _ => (),
616        }
617        Ok(())
618    }
619
620    fn send_nick_password(&self) -> error::Result<()> {
621        if self.config().nick_password().is_empty() {
622            Ok(())
623        } else {
624            let mut index = self.alt_nick_index.write();
625
626            if self.config().should_ghost() && *index != 0 {
627                let seq = match self.config().ghost_sequence() {
628                    Some(seq) => seq,
629                    None => &*self.default_ghost_sequence,
630                };
631
632                for s in seq {
633                    self.send(NICKSERV(vec![
634                        s.to_string(),
635                        self.config().nickname()?.to_string(),
636                        self.config().nick_password().to_string(),
637                    ]))?;
638                }
639                *index = 0;
640                self.send(NICK(self.config().nickname()?.to_owned()))?
641            }
642
643            self.send(NICKSERV(vec![
644                "IDENTIFY".to_string(),
645                self.config().nick_password().to_string(),
646            ]))
647        }
648    }
649
650    fn send_umodes(&self) -> error::Result<()> {
651        if self.config().umodes().is_empty() {
652            Ok(())
653        } else {
654            self.send_mode(
655                self.current_nickname(),
656                &Mode::as_user_modes(
657                    self.config()
658                        .umodes()
659                        .split(' ')
660                        .collect::<Vec<_>>()
661                        .as_ref(),
662                )
663                .map_err(|e| error::Error::InvalidMessage {
664                    string: format!(
665                        "MODE {} {}",
666                        self.current_nickname(),
667                        self.config().umodes()
668                    ),
669                    cause: e,
670                })?,
671            )
672        }
673    }
674
675    #[cfg(not(feature = "channel-lists"))]
676    fn handle_join(&self, _: &str, _: &str) {}
677
678    #[cfg(feature = "channel-lists")]
679    fn handle_join(&self, src: &str, chan: &str) {
680        if let Some(vec) = self.chanlists.write().get_mut(&chan.to_owned()) {
681            if !src.is_empty() {
682                vec.push(User::new(src))
683            }
684        }
685    }
686
687    #[cfg(not(feature = "channel-lists"))]
688    fn handle_part(&self, _: &str, _: &str) {}
689
690    #[cfg(feature = "channel-lists")]
691    fn handle_part(&self, src: &str, chan: &str) {
692        if let Some(vec) = self.chanlists.write().get_mut(&chan.to_owned()) {
693            if !src.is_empty() {
694                if let Some(n) = vec.iter().position(|x| x.get_nickname() == src) {
695                    vec.swap_remove(n);
696                }
697            }
698        }
699    }
700
701    #[cfg(not(feature = "channel-lists"))]
702    fn handle_quit(&self, _: &str) {}
703
704    #[cfg(feature = "channel-lists")]
705    fn handle_quit(&self, src: &str) {
706        if src.is_empty() {
707            return;
708        }
709
710        for vec in self.chanlists.write().values_mut() {
711            if let Some(p) = vec.iter().position(|x| x.get_nickname() == src) {
712                vec.swap_remove(p);
713            }
714        }
715    }
716
717    #[cfg(not(feature = "channel-lists"))]
718    fn handle_nick_change(&self, _: &str, _: &str) {}
719
720    #[cfg(feature = "channel-lists")]
721    fn handle_nick_change(&self, old_nick: &str, new_nick: &str) {
722        if old_nick.is_empty() || new_nick.is_empty() {
723            return;
724        }
725
726        for (_, vec) in self.chanlists.write().iter_mut() {
727            if let Some(n) = vec.iter().position(|x| x.get_nickname() == old_nick) {
728                let new_entry = User::new(new_nick);
729                vec[n] = new_entry;
730            }
731        }
732    }
733
734    #[cfg(not(feature = "channel-lists"))]
735    fn handle_mode(&self, _: &str, _: &[Mode<ChannelMode>]) {}
736
737    #[cfg(feature = "channel-lists")]
738    fn handle_mode(&self, chan: &str, modes: &[Mode<ChannelMode>]) {
739        for mode in modes {
740            match *mode {
741                Mode::Plus(_, Some(ref user)) | Mode::Minus(_, Some(ref user)) => {
742                    if let Some(vec) = self.chanlists.write().get_mut(chan) {
743                        if let Some(n) = vec.iter().position(|x| x.get_nickname() == user) {
744                            vec[n].update_access_level(mode)
745                        }
746                    }
747                }
748                _ => (),
749            }
750        }
751    }
752
753    #[cfg(not(feature = "channel-lists"))]
754    fn handle_namreply(&self, _: &[String]) {}
755
756    #[cfg(feature = "channel-lists")]
757    fn handle_namreply(&self, args: &[String]) {
758        if args.len() == 4 {
759            let chan = &args[2];
760            for user in args[3].split(' ') {
761                self.chanlists
762                    .write()
763                    .entry(chan.clone())
764                    .or_insert_with(Vec::new)
765                    .push(User::new(user))
766            }
767        }
768    }
769
770    #[cfg(feature = "ctcp")]
771    fn handle_ctcp(&self, resp: &str, tokens: &[&str]) -> error::Result<()> {
772        if tokens.is_empty() {
773            return Ok(());
774        }
775        if tokens[0].eq_ignore_ascii_case("FINGER") {
776            self.send_ctcp_internal(
777                resp,
778                &format!(
779                    "FINGER :{} ({})",
780                    self.config().real_name(),
781                    self.config().username()
782                ),
783            )
784        } else if tokens[0].eq_ignore_ascii_case("VERSION") {
785            self.send_ctcp_internal(resp, &format!("VERSION {}", self.config().version()))
786        } else if tokens[0].eq_ignore_ascii_case("SOURCE") {
787            self.send_ctcp_internal(resp, &format!("SOURCE {}", self.config().source()))
788        } else if tokens[0].eq_ignore_ascii_case("PING") && tokens.len() > 1 {
789            self.send_ctcp_internal(resp, &format!("PING {}", tokens[1]))
790        } else if tokens[0].eq_ignore_ascii_case("TIME") {
791            self.send_ctcp_internal(resp, &format!("TIME :{}", Local::now().to_rfc2822()))
792        } else if tokens[0].eq_ignore_ascii_case("USERINFO") {
793            self.send_ctcp_internal(resp, &format!("USERINFO :{}", self.config().user_info()))
794        } else {
795            Ok(())
796        }
797    }
798
799    #[cfg(feature = "ctcp")]
800    fn send_ctcp_internal(&self, target: &str, msg: &str) -> error::Result<()> {
801        self.send_notice(target, format!("\u{001}{}\u{001}", msg))
802    }
803
804    #[cfg(not(feature = "ctcp"))]
805    fn handle_ctcp(&self, _: &str, _: &[&str]) -> error::Result<()> {
806        Ok(())
807    }
808
809    pub_state_base!();
810}
811
812/// Thread-safe sender that can be used with the client.
813#[derive(Debug, Clone)]
814pub struct Sender {
815    tx_outgoing: UnboundedSender<Message>,
816}
817
818impl Sender {
819    /// Send a single message to the unbounded queue.
820    pub fn send<M: Into<Message>>(&self, msg: M) -> error::Result<()> {
821        Ok(self.tx_outgoing.send(msg.into())?)
822    }
823
824    pub_state_base!();
825    pub_sender_base!();
826}
827
828/// Future to handle outgoing messages.
829///
830/// Note: this is essentially the same as a version of [SendAll](https://github.com/rust-lang-nursery/futures-rs/blob/master/futures-util/src/sink/send_all.rs) that owns it's sink and stream.
831#[derive(Debug)]
832pub struct Outgoing {
833    sink: SplitSink<Connection, Message>,
834    stream: UnboundedReceiver<Message>,
835    buffered: Option<Message>,
836}
837
838impl Outgoing {
839    fn try_start_send(
840        &mut self,
841        cx: &mut Context<'_>,
842        message: Message,
843    ) -> Poll<Result<(), error::Error>> {
844        debug_assert!(self.buffered.is_none());
845
846        match Pin::new(&mut self.sink).poll_ready(cx)? {
847            Poll::Ready(()) => Poll::Ready(Pin::new(&mut self.sink).start_send(message)),
848            Poll::Pending => {
849                self.buffered = Some(message);
850                Poll::Pending
851            }
852        }
853    }
854}
855
856impl FusedFuture for Outgoing {
857    fn is_terminated(&self) -> bool {
858        // NB: outgoing stream never terminates.
859        // TODO: should it terminate if rx_outgoing is terminated?
860        false
861    }
862}
863
864impl Future for Outgoing {
865    type Output = error::Result<()>;
866
867    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
868        let this = &mut *self;
869
870        if let Some(message) = this.buffered.take() {
871            ready!(this.try_start_send(cx, message))?
872        }
873
874        loop {
875            match this.stream.poll_recv(cx) {
876                Poll::Ready(Some(message)) => ready!(this.try_start_send(cx, message))?,
877                Poll::Ready(None) => {
878                    ready!(Pin::new(&mut this.sink).poll_flush(cx))?;
879                    return Poll::Ready(Ok(()));
880                }
881                Poll::Pending => {
882                    ready!(Pin::new(&mut this.sink).poll_flush(cx))?;
883                    return Poll::Pending;
884                }
885            }
886        }
887    }
888}
889
890/// The canonical implementation of a connection to an IRC server.
891///
892/// For a full example usage, see [`irc::client`].
893#[derive(Debug)]
894pub struct Client {
895    /// The internal, thread-safe server state.
896    state: Arc<ClientState>,
897    incoming: Option<SplitStream<Connection>>,
898    outgoing: Option<Outgoing>,
899    sender: Sender,
900    #[cfg(test)]
901    /// A view of the logs for a mock connection.
902    view: Option<self::transport::LogView>,
903}
904
905impl Client {
906    /// Creates a new `Client` from the configuration at the specified path, connecting
907    /// immediately. This function is short-hand for loading the configuration and then calling
908    /// `Client::from_config` and consequently inherits its behaviors.
909    ///
910    /// # Example
911    /// ```no_run
912    /// # use irc::client::prelude::*;
913    /// # #[tokio::main]
914    /// # async fn main() -> irc::error::Result<()> {
915    /// let client = Client::new("config.toml").await?;
916    /// # Ok(())
917    /// # }
918    /// ```
919    pub async fn new<P: AsRef<Path>>(config: P) -> error::Result<Client> {
920        Client::from_config(Config::load(config)?).await
921    }
922
923    /// Creates a `Future` of an `Client` from the specified configuration and on the event loop
924    /// corresponding to the given handle. This can be used to set up a number of `Clients` on a
925    /// single, shared event loop. It can also be used to take more control over execution and error
926    /// handling. Connection will not occur until the event loop is run.
927    pub async fn from_config(config: Config) -> error::Result<Client> {
928        let (tx_outgoing, rx_outgoing) = mpsc::unbounded_channel();
929        let conn = Connection::new(&config, tx_outgoing.clone()).await?;
930
931        #[cfg(test)]
932        let view = conn.log_view();
933
934        let (sink, incoming) = conn.split();
935
936        let sender = Sender { tx_outgoing };
937
938        Ok(Client {
939            sender: sender.clone(),
940            state: Arc::new(ClientState::new(sender, config)),
941            incoming: Some(incoming),
942            outgoing: Some(Outgoing {
943                sink,
944                stream: rx_outgoing,
945                buffered: None,
946            }),
947            #[cfg(test)]
948            view,
949        })
950    }
951
952    /// Gets the log view from the internal transport. Only used for unit testing.
953    #[cfg(test)]
954    fn log_view(&self) -> &self::transport::LogView {
955        self.view
956            .as_ref()
957            .expect("there should be a log during testing")
958    }
959
960    /// Take the outgoing future in order to drive it yourself.
961    ///
962    /// Must be called before `stream` if you intend to drive this future
963    /// yourself.
964    pub fn outgoing(&mut self) -> Option<Outgoing> {
965        self.outgoing.take()
966    }
967
968    /// Get access to a thread-safe sender that can be used with the client.
969    pub fn sender(&self) -> Sender {
970        self.sender.clone()
971    }
972
973    /// Gets the configuration being used with this `Client`.
974    fn config(&self) -> &Config {
975        &self.state.config
976    }
977
978    /// Gets a stream of incoming messages from the `Client`'s connection. This is only necessary
979    /// when trying to set up more complex clients, and requires use of the `futures` crate. Most
980    /// You can find some examples of setups using `stream` in the
981    /// [GitHub repository](https://github.com/aatxe/irc/tree/stable/examples).
982    ///
983    /// **Note**: The stream can only be returned once. Subsequent attempts will cause a panic.
984    // FIXME: when impl traits stabilize, we should change this return type.
985    pub fn stream(&mut self) -> error::Result<ClientStream> {
986        let stream = self
987            .incoming
988            .take()
989            .ok_or(error::Error::StreamAlreadyConfigured)?;
990
991        Ok(ClientStream {
992            state: Arc::clone(&self.state),
993            stream,
994            outgoing: self.outgoing.take(),
995        })
996    }
997
998    /// Gets a list of currently joined channels. This will be `None` if tracking is disabled
999    /// altogether by disabling the `channel-lists` feature.
1000    #[cfg(feature = "channel-lists")]
1001    pub fn list_channels(&self) -> Option<Vec<String>> {
1002        Some(
1003            self.state
1004                .chanlists
1005                .read()
1006                .keys()
1007                .map(|k| k.to_owned())
1008                .collect(),
1009        )
1010    }
1011
1012    /// Always returns `None` since `channel-lists` feature is disabled.
1013    #[cfg(not(feature = "channel-lists"))]
1014    pub fn list_channels(&self) -> Option<Vec<String>> {
1015        None
1016    }
1017
1018    /// Gets a list of [`Users`] in the specified channel. If the
1019    /// specified channel hasn't been joined or the `channel-lists` feature is disabled, this function
1020    /// will return `None`.
1021    ///
1022    /// For best results, be sure to request `multi-prefix` support from the server. This will allow
1023    /// for more accurate tracking of user rank (e.g. oper, half-op, etc.).
1024    /// # Requesting multi-prefix support
1025    /// ```no_run
1026    /// # use irc::client::prelude::*;
1027    /// use irc::proto::caps::Capability;
1028    ///
1029    /// # #[tokio::main]
1030    /// # async fn main() -> irc::error::Result<()> {
1031    /// # let client = Client::new("config.toml").await?;
1032    /// client.send_cap_req(&[Capability::MultiPrefix])?;
1033    /// client.identify()?;
1034    /// # Ok(())
1035    /// # }
1036    /// ```
1037    #[cfg(feature = "channel-lists")]
1038    pub fn list_users(&self, chan: &str) -> Option<Vec<User>> {
1039        self.state.chanlists.read().get(&chan.to_owned()).cloned()
1040    }
1041
1042    /// Always returns `None` since `channel-lists` feature is disabled.
1043    #[cfg(not(feature = "channel-lists"))]
1044    pub fn list_users(&self, _: &str) -> Option<Vec<User>> {
1045        None
1046    }
1047
1048    /// Gets the current nickname in use. This may be the primary username set in the configuration,
1049    /// or it could be any of the alternative nicknames listed as well. As a result, this is the
1050    /// preferred way to refer to the client's nickname.
1051    pub fn current_nickname(&self) -> &str {
1052        self.state.current_nickname()
1053    }
1054
1055    /// Sends a [`Command`] as this `Client`. This is the
1056    /// core primitive for sending messages to the server.
1057    ///
1058    /// # Example
1059    /// ```no_run
1060    /// # use irc::client::prelude::*;
1061    /// # #[tokio::main]
1062    /// # async fn main() {
1063    /// # let client = Client::new("config.toml").await.unwrap();
1064    /// client.send(Command::NICK("example".to_owned())).unwrap();
1065    /// client.send(Command::USER("user".to_owned(), "0".to_owned(), "name".to_owned())).unwrap();
1066    /// # }
1067    /// ```
1068    pub fn send<M: Into<Message>>(&self, msg: M) -> error::Result<()> {
1069        self.state.send(msg)
1070    }
1071
1072    /// Sends a CAP END, NICK and USER to identify.
1073    pub fn identify(&self) -> error::Result<()> {
1074        // Send a CAP END to signify that we're IRCv3-compliant (and to end negotiations!).
1075        self.send(CAP(None, END, None, None))?;
1076        if self.config().password() != "" {
1077            self.send(PASS(self.config().password().to_owned()))?;
1078        }
1079        self.send(NICK(self.config().nickname()?.to_owned()))?;
1080        self.send(USER(
1081            self.config().username().to_owned(),
1082            "0".to_owned(),
1083            self.config().real_name().to_owned(),
1084        ))?;
1085        Ok(())
1086    }
1087
1088    pub_state_base!();
1089    pub_sender_base!();
1090}
1091
1092#[cfg(test)]
1093mod test {
1094    use std::{collections::HashMap, default::Default, thread, time::Duration};
1095
1096    use super::Client;
1097    #[cfg(feature = "channel-lists")]
1098    use crate::client::data::User;
1099    use crate::{
1100        client::data::Config,
1101        error::Error,
1102        proto::{
1103            command::Command::{Raw, PRIVMSG},
1104            ChannelMode, IrcCodec, Mode, UserMode,
1105        },
1106    };
1107    use anyhow::Result;
1108    use futures::prelude::*;
1109
1110    pub fn test_config() -> Config {
1111        Config {
1112            owners: vec!["test".to_string()],
1113            nickname: Some("test".to_string()),
1114            alt_nicks: vec!["test2".to_string()],
1115            server: Some("irc.test.net".to_string()),
1116            channels: vec!["#test".to_string(), "#test2".to_string()],
1117            user_info: Some("Testing.".to_string()),
1118            use_mock_connection: true,
1119            ..Default::default()
1120        }
1121    }
1122
1123    pub fn get_client_value(client: Client) -> String {
1124        // We sleep here because of synchronization issues.
1125        // We can't guarantee that everything will have been sent by the time of this call.
1126        thread::sleep(Duration::from_millis(100));
1127        client
1128            .log_view()
1129            .sent()
1130            .unwrap()
1131            .iter()
1132            .fold(String::new(), |mut acc, msg| {
1133                // NOTE: we have to sanitize here because sanitization happens in IrcCodec after the
1134                // messages are converted into strings, but our transport logger catches messages before
1135                // they ever reach that point.
1136                acc.push_str(&IrcCodec::sanitize(msg.to_string()));
1137                acc
1138            })
1139    }
1140
1141    #[tokio::test]
1142    async fn stream() -> Result<()> {
1143        let exp = "PRIVMSG test :Hi!\r\nPRIVMSG test :This is a test!\r\n\
1144                   :test!test@test JOIN #test\r\n";
1145
1146        let mut client = Client::from_config(Config {
1147            mock_initial_value: Some(exp.to_owned()),
1148            ..test_config()
1149        })
1150        .await?;
1151
1152        client.stream()?.collect().await?;
1153        // assert_eq!(&messages[..], exp);
1154        Ok(())
1155    }
1156
1157    #[tokio::test]
1158    async fn handle_message() -> Result<()> {
1159        let value = ":irc.test.net 376 test :End of /MOTD command.\r\n";
1160        let mut client = Client::from_config(Config {
1161            mock_initial_value: Some(value.to_owned()),
1162            ..test_config()
1163        })
1164        .await?;
1165        client.stream()?.collect().await?;
1166        assert_eq!(
1167            &get_client_value(client)[..],
1168            "JOIN #test\r\nJOIN #test2\r\n"
1169        );
1170        Ok(())
1171    }
1172
1173    #[tokio::test]
1174    async fn handle_end_motd_with_nick_password() -> Result<()> {
1175        let value = ":irc.test.net 376 test :End of /MOTD command.\r\n";
1176        let mut client = Client::from_config(Config {
1177            mock_initial_value: Some(value.to_owned()),
1178            nick_password: Some("password".to_string()),
1179            channels: vec!["#test".to_string(), "#test2".to_string()],
1180            ..test_config()
1181        })
1182        .await?;
1183        client.stream()?.collect().await?;
1184        assert_eq!(
1185            &get_client_value(client)[..],
1186            "NICKSERV IDENTIFY password\r\nJOIN #test\r\n\
1187             JOIN #test2\r\n"
1188        );
1189        Ok(())
1190    }
1191
1192    #[tokio::test]
1193    async fn handle_end_motd_with_chan_keys() -> Result<()> {
1194        let value = ":irc.test.net 376 test :End of /MOTD command\r\n";
1195        let mut client = Client::from_config(Config {
1196            mock_initial_value: Some(value.to_owned()),
1197            nickname: Some("test".to_string()),
1198            channels: vec!["#test".to_string(), "#test2".to_string()],
1199            channel_keys: {
1200                let mut map = HashMap::new();
1201                map.insert("#test2".to_string(), "password".to_string());
1202                map
1203            },
1204            ..test_config()
1205        })
1206        .await?;
1207        client.stream()?.collect().await?;
1208        assert_eq!(
1209            &get_client_value(client)[..],
1210            "JOIN #test\r\nJOIN #test2 password\r\n"
1211        );
1212        Ok(())
1213    }
1214
1215    #[tokio::test]
1216    async fn handle_end_motd_with_ghost() -> Result<()> {
1217        let value = ":irc.test.net 433 * test :Nickname is already in use.\r\n\
1218                     :irc.test.net 376 test2 :End of /MOTD command.\r\n";
1219        let mut client = Client::from_config(Config {
1220            mock_initial_value: Some(value.to_owned()),
1221            nickname: Some("test".to_string()),
1222            alt_nicks: vec!["test2".to_string()],
1223            nick_password: Some("password".to_string()),
1224            channels: vec!["#test".to_string(), "#test2".to_string()],
1225            should_ghost: true,
1226            ..test_config()
1227        })
1228        .await?;
1229        client.stream()?.collect().await?;
1230        assert_eq!(
1231            &get_client_value(client)[..],
1232            "NICK test2\r\nNICKSERV GHOST test password\r\n\
1233             NICK test\r\nNICKSERV IDENTIFY password\r\nJOIN #test\r\nJOIN #test2\r\n"
1234        );
1235        Ok(())
1236    }
1237
1238    #[tokio::test]
1239    async fn handle_end_motd_with_ghost_seq() -> Result<()> {
1240        let value = ":irc.test.net 433 * test :Nickname is already in use.\r\n\
1241                     :irc.test.net 376 test2 :End of /MOTD command.\r\n";
1242        let mut client = Client::from_config(Config {
1243            mock_initial_value: Some(value.to_owned()),
1244            nickname: Some("test".to_string()),
1245            alt_nicks: vec!["test2".to_string()],
1246            nick_password: Some("password".to_string()),
1247            channels: vec!["#test".to_string(), "#test2".to_string()],
1248            should_ghost: true,
1249            ghost_sequence: Some(vec!["RECOVER".to_string(), "RELEASE".to_string()]),
1250            ..test_config()
1251        })
1252        .await?;
1253        client.stream()?.collect().await?;
1254        assert_eq!(
1255            &get_client_value(client)[..],
1256            "NICK test2\r\nNICKSERV RECOVER test password\
1257             \r\nNICKSERV RELEASE test password\r\nNICK test\r\nNICKSERV IDENTIFY password\
1258             \r\nJOIN #test\r\nJOIN #test2\r\n"
1259        );
1260        Ok(())
1261    }
1262
1263    #[tokio::test]
1264    async fn handle_end_motd_with_umodes() -> Result<()> {
1265        let value = ":irc.test.net 376 test :End of /MOTD command.\r\n";
1266        let mut client = Client::from_config(Config {
1267            mock_initial_value: Some(value.to_owned()),
1268            nickname: Some("test".to_string()),
1269            umodes: Some("+B".to_string()),
1270            channels: vec!["#test".to_string(), "#test2".to_string()],
1271            ..test_config()
1272        })
1273        .await?;
1274        client.stream()?.collect().await?;
1275        assert_eq!(
1276            &get_client_value(client)[..],
1277            "MODE test +B\r\nJOIN #test\r\nJOIN #test2\r\n"
1278        );
1279        Ok(())
1280    }
1281
1282    #[tokio::test]
1283    async fn nickname_in_use() -> Result<()> {
1284        let value = ":irc.test.net 433 * test :Nickname is already in use.\r\n";
1285        let mut client = Client::from_config(Config {
1286            mock_initial_value: Some(value.to_owned()),
1287            ..test_config()
1288        })
1289        .await?;
1290        client.stream()?.collect().await?;
1291        assert_eq!(&get_client_value(client)[..], "NICK test2\r\n");
1292        Ok(())
1293    }
1294
1295    #[tokio::test]
1296    async fn ran_out_of_nicknames() -> Result<()> {
1297        let value = ":irc.test.net 433 * test :Nickname is already in use.\r\n\
1298                     :irc.test.net 433 * test2 :Nickname is already in use.\r\n";
1299        let mut client = Client::from_config(Config {
1300            mock_initial_value: Some(value.to_owned()),
1301            ..test_config()
1302        })
1303        .await?;
1304        let res = client.stream()?.try_collect::<Vec<_>>().await;
1305        if let Err(Error::NoUsableNick) = res {
1306        } else {
1307            panic!("expected error when no valid nicks were specified")
1308        }
1309        Ok(())
1310    }
1311
1312    #[tokio::test]
1313    async fn send() -> Result<()> {
1314        let mut client = Client::from_config(test_config()).await?;
1315        assert!(client
1316            .send(PRIVMSG("#test".to_string(), "Hi there!".to_string()))
1317            .is_ok());
1318        client.stream()?.collect().await?;
1319        assert_eq!(
1320            &get_client_value(client)[..],
1321            "PRIVMSG #test :Hi there!\r\n"
1322        );
1323        Ok(())
1324    }
1325
1326    #[tokio::test]
1327    async fn send_no_newline_injection() -> Result<()> {
1328        let mut client = Client::from_config(test_config()).await?;
1329        assert!(client
1330            .send(PRIVMSG(
1331                "#test".to_string(),
1332                "Hi there!\r\nJOIN #bad".to_string()
1333            ))
1334            .is_ok());
1335        client.stream()?.collect().await?;
1336        assert_eq!(
1337            &get_client_value(client)[..],
1338            "PRIVMSG #test :Hi there!\r\n"
1339        );
1340        Ok(())
1341    }
1342
1343    #[tokio::test]
1344    async fn send_raw_is_really_raw() -> Result<()> {
1345        let mut client = Client::from_config(test_config()).await?;
1346        assert!(client
1347            .send(Raw("PASS".to_owned(), vec!["password".to_owned()]))
1348            .is_ok());
1349        assert!(client
1350            .send(Raw("NICK".to_owned(), vec!["test".to_owned()]))
1351            .is_ok());
1352        client.stream()?.collect().await?;
1353        assert_eq!(
1354            &get_client_value(client)[..],
1355            "PASS password\r\nNICK test\r\n"
1356        );
1357        Ok(())
1358    }
1359
1360    #[tokio::test]
1361    #[cfg(feature = "channel-lists")]
1362    async fn channel_tracking_names() -> Result<()> {
1363        let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n";
1364        let mut client = Client::from_config(Config {
1365            mock_initial_value: Some(value.to_owned()),
1366            ..test_config()
1367        })
1368        .await?;
1369        client.stream()?.collect().await?;
1370        assert_eq!(client.list_channels().unwrap(), vec!["#test".to_owned()]);
1371        Ok(())
1372    }
1373
1374    #[tokio::test]
1375    #[cfg(feature = "channel-lists")]
1376    async fn channel_tracking_names_part() -> Result<()> {
1377        use crate::proto::command::Command::PART;
1378
1379        let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n";
1380        let mut client = Client::from_config(Config {
1381            mock_initial_value: Some(value.to_owned()),
1382            ..test_config()
1383        })
1384        .await?;
1385
1386        client.stream()?.collect().await?;
1387
1388        assert_eq!(client.list_channels(), Some(vec!["#test".to_owned()]));
1389        // we ignore the result, as soon as we queue an outgoing message we
1390        // update client state, regardless if the queue is available or not.
1391        let _ = client.send(PART("#test".to_string(), None));
1392        assert_eq!(client.list_channels(), Some(vec![]));
1393        Ok(())
1394    }
1395
1396    #[tokio::test]
1397    #[cfg(feature = "channel-lists")]
1398    async fn user_tracking_names() -> Result<()> {
1399        let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n";
1400        let mut client = Client::from_config(Config {
1401            mock_initial_value: Some(value.to_owned()),
1402            ..test_config()
1403        })
1404        .await?;
1405        client.stream()?.collect().await?;
1406        assert_eq!(
1407            client.list_users("#test").unwrap(),
1408            vec![User::new("test"), User::new("~owner"), User::new("&admin")]
1409        );
1410        Ok(())
1411    }
1412
1413    #[tokio::test]
1414    #[cfg(feature = "channel-lists")]
1415    async fn user_tracking_names_join() -> Result<()> {
1416        let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n\
1417                     :test2!test@test JOIN #test\r\n";
1418        let mut client = Client::from_config(Config {
1419            mock_initial_value: Some(value.to_owned()),
1420            ..test_config()
1421        })
1422        .await?;
1423        client.stream()?.collect().await?;
1424        assert_eq!(
1425            client.list_users("#test").unwrap(),
1426            vec![
1427                User::new("test"),
1428                User::new("~owner"),
1429                User::new("&admin"),
1430                User::new("test2"),
1431            ]
1432        );
1433        Ok(())
1434    }
1435
1436    #[tokio::test]
1437    #[cfg(feature = "channel-lists")]
1438    async fn user_tracking_names_kick() -> Result<()> {
1439        let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n\
1440                     :owner!test@test KICK #test test\r\n";
1441        let mut client = Client::from_config(Config {
1442            mock_initial_value: Some(value.to_owned()),
1443            ..test_config()
1444        })
1445        .await?;
1446        client.stream()?.collect().await?;
1447        assert_eq!(
1448            client.list_users("#test").unwrap(),
1449            vec![User::new("&admin"), User::new("~owner"),]
1450        );
1451        Ok(())
1452    }
1453
1454    #[tokio::test]
1455    #[cfg(feature = "channel-lists")]
1456    async fn user_tracking_names_part() -> Result<()> {
1457        let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n\
1458                     :owner!test@test PART #test\r\n";
1459        let mut client = Client::from_config(Config {
1460            mock_initial_value: Some(value.to_owned()),
1461            ..test_config()
1462        })
1463        .await?;
1464        client.stream()?.collect().await?;
1465        assert_eq!(
1466            client.list_users("#test").unwrap(),
1467            vec![User::new("test"), User::new("&admin")]
1468        );
1469        Ok(())
1470    }
1471
1472    #[tokio::test]
1473    #[cfg(feature = "channel-lists")]
1474    async fn user_tracking_names_mode() -> Result<()> {
1475        let value = ":irc.test.net 353 test = #test :+test ~owner &admin\r\n\
1476                     :test!test@test MODE #test +o test\r\n";
1477        let mut client = Client::from_config(Config {
1478            mock_initial_value: Some(value.to_owned()),
1479            ..test_config()
1480        })
1481        .await?;
1482        client.stream()?.collect().await?;
1483        assert_eq!(
1484            client.list_users("#test").unwrap(),
1485            vec![User::new("@test"), User::new("~owner"), User::new("&admin")]
1486        );
1487        let mut exp = User::new("@test");
1488        exp.update_access_level(&Mode::Plus(ChannelMode::Voice, None));
1489        assert_eq!(
1490            client.list_users("#test").unwrap()[0].highest_access_level(),
1491            exp.highest_access_level()
1492        );
1493        // The following tests if the maintained user contains the same entries as what is expected
1494        // but ignores the ordering of these entries.
1495        let mut levels = client.list_users("#test").unwrap()[0].access_levels();
1496        levels.retain(|l| exp.access_levels().contains(l));
1497        assert_eq!(levels.len(), exp.access_levels().len());
1498        Ok(())
1499    }
1500
1501    #[tokio::test]
1502    #[cfg(not(feature = "channel-lists"))]
1503    async fn no_user_tracking() -> Result<()> {
1504        let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n";
1505        let mut client = Client::from_config(Config {
1506            mock_initial_value: Some(value.to_owned()),
1507            ..test_config()
1508        })
1509        .await?;
1510        client.stream()?.collect().await?;
1511        assert!(client.list_users("#test").is_none());
1512        Ok(())
1513    }
1514
1515    #[tokio::test]
1516    async fn handle_single_soh() -> Result<()> {
1517        let value = ":test!test@test PRIVMSG #test :\u{001}\r\n";
1518        let mut client = Client::from_config(Config {
1519            mock_initial_value: Some(value.to_owned()),
1520            nickname: Some("test".to_string()),
1521            channels: vec!["#test".to_string(), "#test2".to_string()],
1522            ..test_config()
1523        })
1524        .await?;
1525        client.stream()?.collect().await?;
1526        Ok(())
1527    }
1528
1529    #[tokio::test]
1530    #[cfg(feature = "ctcp")]
1531    async fn finger_response() -> Result<()> {
1532        let value = ":test!test@test PRIVMSG test :\u{001}FINGER\u{001}\r\n";
1533        let mut client = Client::from_config(Config {
1534            mock_initial_value: Some(value.to_owned()),
1535            ..test_config()
1536        })
1537        .await?;
1538        client.stream()?.collect().await?;
1539        assert_eq!(
1540            &get_client_value(client)[..],
1541            "NOTICE test :\u{001}FINGER :test (test)\u{001}\r\n"
1542        );
1543        Ok(())
1544    }
1545
1546    #[tokio::test]
1547    #[cfg(feature = "ctcp")]
1548    async fn version_response() -> Result<()> {
1549        let value = ":test!test@test PRIVMSG test :\u{001}VERSION\u{001}\r\n";
1550        let mut client = Client::from_config(Config {
1551            mock_initial_value: Some(value.to_owned()),
1552            ..test_config()
1553        })
1554        .await?;
1555        client.stream()?.collect().await?;
1556        assert_eq!(
1557            &get_client_value(client)[..],
1558            &format!(
1559                "NOTICE test :\u{001}VERSION {}\u{001}\r\n",
1560                crate::VERSION_STR,
1561            )
1562        );
1563        Ok(())
1564    }
1565
1566    #[tokio::test]
1567    #[cfg(feature = "ctcp")]
1568    async fn source_response() -> Result<()> {
1569        let value = ":test!test@test PRIVMSG test :\u{001}SOURCE\u{001}\r\n";
1570        let mut client = Client::from_config(Config {
1571            mock_initial_value: Some(value.to_owned()),
1572            ..test_config()
1573        })
1574        .await?;
1575        client.stream()?.collect().await?;
1576        assert_eq!(
1577            &get_client_value(client)[..],
1578            "NOTICE test :\u{001}SOURCE https://github.com/aatxe/irc\u{001}\r\n"
1579        );
1580        Ok(())
1581    }
1582
1583    #[tokio::test]
1584    #[cfg(feature = "ctcp")]
1585    async fn ctcp_ping_response() -> Result<()> {
1586        let value = ":test!test@test PRIVMSG test :\u{001}PING test\u{001}\r\n";
1587        let mut client = Client::from_config(Config {
1588            mock_initial_value: Some(value.to_owned()),
1589            ..test_config()
1590        })
1591        .await?;
1592        client.stream()?.collect().await?;
1593        assert_eq!(
1594            &get_client_value(client)[..],
1595            "NOTICE test :\u{001}PING test\u{001}\r\n"
1596        );
1597        Ok(())
1598    }
1599
1600    #[tokio::test]
1601    #[cfg(feature = "ctcp")]
1602    async fn time_response() -> Result<()> {
1603        let value = ":test!test@test PRIVMSG test :\u{001}TIME\u{001}\r\n";
1604        let mut client = Client::from_config(Config {
1605            mock_initial_value: Some(value.to_owned()),
1606            ..test_config()
1607        })
1608        .await?;
1609        client.stream()?.collect().await?;
1610        let val = get_client_value(client);
1611        assert!(val.starts_with("NOTICE test :\u{001}TIME :"));
1612        assert!(val.ends_with("\u{001}\r\n"));
1613        Ok(())
1614    }
1615
1616    #[tokio::test]
1617    #[cfg(feature = "ctcp")]
1618    async fn user_info_response() -> Result<()> {
1619        let value = ":test!test@test PRIVMSG test :\u{001}USERINFO\u{001}\r\n";
1620        let mut client = Client::from_config(Config {
1621            mock_initial_value: Some(value.to_owned()),
1622            ..test_config()
1623        })
1624        .await?;
1625        client.stream()?.collect().await?;
1626        assert_eq!(
1627            &get_client_value(client)[..],
1628            "NOTICE test :\u{001}USERINFO :Testing.\u{001}\
1629             \r\n"
1630        );
1631        Ok(())
1632    }
1633
1634    #[tokio::test]
1635    #[cfg(feature = "ctcp")]
1636    async fn ctcp_ping_no_timestamp() -> Result<()> {
1637        let value = ":test!test@test PRIVMSG test \u{001}PING\u{001}\r\n";
1638        let mut client = Client::from_config(Config {
1639            mock_initial_value: Some(value.to_owned()),
1640            ..test_config()
1641        })
1642        .await?;
1643        client.stream()?.collect().await?;
1644        assert_eq!(&get_client_value(client)[..], "");
1645        Ok(())
1646    }
1647
1648    #[tokio::test]
1649    async fn identify() -> Result<()> {
1650        let mut client = Client::from_config(test_config()).await?;
1651        client.identify()?;
1652        client.stream()?.collect().await?;
1653        assert_eq!(
1654            &get_client_value(client)[..],
1655            "CAP END\r\nNICK test\r\n\
1656             USER test 0 * test\r\n"
1657        );
1658        Ok(())
1659    }
1660
1661    #[tokio::test]
1662    async fn identify_with_password() -> Result<()> {
1663        let mut client = Client::from_config(Config {
1664            nickname: Some("test".to_string()),
1665            password: Some("password".to_string()),
1666            ..test_config()
1667        })
1668        .await?;
1669        client.identify()?;
1670        client.stream()?.collect().await?;
1671        assert_eq!(
1672            &get_client_value(client)[..],
1673            "CAP END\r\nPASS password\r\nNICK test\r\n\
1674             USER test 0 * test\r\n"
1675        );
1676        Ok(())
1677    }
1678
1679    #[tokio::test]
1680    async fn send_pong() -> Result<()> {
1681        let mut client = Client::from_config(test_config()).await?;
1682        client.send_pong("irc.test.net")?;
1683        client.stream()?.collect().await?;
1684        assert_eq!(&get_client_value(client)[..], "PONG irc.test.net\r\n");
1685        Ok(())
1686    }
1687
1688    #[tokio::test]
1689    async fn send_join() -> Result<()> {
1690        let mut client = Client::from_config(test_config()).await?;
1691        client.send_join("#test,#test2,#test3")?;
1692        client.stream()?.collect().await?;
1693        assert_eq!(
1694            &get_client_value(client)[..],
1695            "JOIN #test,#test2,#test3\r\n"
1696        );
1697        Ok(())
1698    }
1699
1700    #[tokio::test]
1701    async fn send_part() -> Result<()> {
1702        let mut client = Client::from_config(test_config()).await?;
1703        client.send_part("#test")?;
1704        client.stream()?.collect().await?;
1705        assert_eq!(&get_client_value(client)[..], "PART #test\r\n");
1706        Ok(())
1707    }
1708
1709    #[tokio::test]
1710    async fn send_oper() -> Result<()> {
1711        let mut client = Client::from_config(test_config()).await?;
1712        client.send_oper("test", "test")?;
1713        client.stream()?.collect().await?;
1714        assert_eq!(&get_client_value(client)[..], "OPER test test\r\n");
1715        Ok(())
1716    }
1717
1718    #[tokio::test]
1719    async fn send_privmsg() -> Result<()> {
1720        let mut client = Client::from_config(test_config()).await?;
1721        client.send_privmsg("#test", "Hi, everybody!")?;
1722        client.stream()?.collect().await?;
1723        assert_eq!(
1724            &get_client_value(client)[..],
1725            "PRIVMSG #test :Hi, everybody!\r\n"
1726        );
1727        Ok(())
1728    }
1729
1730    #[tokio::test]
1731    async fn send_notice() -> Result<()> {
1732        let mut client = Client::from_config(test_config()).await?;
1733        client.send_notice("#test", "Hi, everybody!")?;
1734        client.stream()?.collect().await?;
1735        assert_eq!(
1736            &get_client_value(client)[..],
1737            "NOTICE #test :Hi, everybody!\r\n"
1738        );
1739        Ok(())
1740    }
1741
1742    #[tokio::test]
1743    async fn send_topic_no_topic() -> Result<()> {
1744        let mut client = Client::from_config(test_config()).await?;
1745        client.send_topic("#test", "")?;
1746        client.stream()?.collect().await?;
1747        assert_eq!(&get_client_value(client)[..], "TOPIC #test\r\n");
1748        Ok(())
1749    }
1750
1751    #[tokio::test]
1752    async fn send_topic() -> Result<()> {
1753        let mut client = Client::from_config(test_config()).await?;
1754        client.send_topic("#test", "Testing stuff.")?;
1755        client.stream()?.collect().await?;
1756        assert_eq!(
1757            &get_client_value(client)[..],
1758            "TOPIC #test :Testing stuff.\r\n"
1759        );
1760        Ok(())
1761    }
1762
1763    #[tokio::test]
1764    async fn send_kill() -> Result<()> {
1765        let mut client = Client::from_config(test_config()).await?;
1766        client.send_kill("test", "Testing kills.")?;
1767        client.stream()?.collect().await?;
1768        assert_eq!(
1769            &get_client_value(client)[..],
1770            "KILL test :Testing kills.\r\n"
1771        );
1772        Ok(())
1773    }
1774
1775    #[tokio::test]
1776    async fn send_kick_no_message() -> Result<()> {
1777        let mut client = Client::from_config(test_config()).await?;
1778        client.send_kick("#test", "test", "")?;
1779        client.stream()?.collect().await?;
1780        assert_eq!(&get_client_value(client)[..], "KICK #test test\r\n");
1781        Ok(())
1782    }
1783
1784    #[tokio::test]
1785    async fn send_kick() -> Result<()> {
1786        let mut client = Client::from_config(test_config()).await?;
1787        client.send_kick("#test", "test", "Testing kicks.")?;
1788        client.stream()?.collect().await?;
1789        assert_eq!(
1790            &get_client_value(client)[..],
1791            "KICK #test test :Testing kicks.\r\n"
1792        );
1793        Ok(())
1794    }
1795
1796    #[tokio::test]
1797    async fn send_mode_no_modeparams() -> Result<()> {
1798        let mut client = Client::from_config(test_config()).await?;
1799        client.send_mode("#test", &[Mode::Plus(ChannelMode::InviteOnly, None)])?;
1800        client.stream()?.collect().await?;
1801        assert_eq!(&get_client_value(client)[..], "MODE #test +i\r\n");
1802        Ok(())
1803    }
1804
1805    #[tokio::test]
1806    async fn send_mode() -> Result<()> {
1807        let mut client = Client::from_config(test_config()).await?;
1808        client.send_mode(
1809            "#test",
1810            &[
1811                Mode::Plus(ChannelMode::Oper, Some("test".to_owned())),
1812                Mode::Minus(ChannelMode::Oper, Some("test2".to_owned())),
1813            ],
1814        )?;
1815        client.stream()?.collect().await?;
1816        assert_eq!(
1817            &get_client_value(client)[..],
1818            "MODE #test +o-o test test2\r\n"
1819        );
1820        Ok(())
1821    }
1822
1823    #[tokio::test]
1824    async fn send_umode() -> Result<()> {
1825        let mut client = Client::from_config(test_config()).await?;
1826        client.send_mode(
1827            "test",
1828            &[
1829                Mode::Plus(UserMode::Invisible, None),
1830                Mode::Plus(UserMode::MaskedHost, None),
1831            ],
1832        )?;
1833        client.stream()?.collect().await?;
1834        assert_eq!(&get_client_value(client)[..], "MODE test +i+x\r\n");
1835        Ok(())
1836    }
1837
1838    #[tokio::test]
1839    async fn send_samode_no_modeparams() -> Result<()> {
1840        let mut client = Client::from_config(test_config()).await?;
1841        client.send_samode("#test", "+i", "")?;
1842        client.stream()?.collect().await?;
1843        assert_eq!(&get_client_value(client)[..], "SAMODE #test +i\r\n");
1844        Ok(())
1845    }
1846
1847    #[tokio::test]
1848    async fn send_samode() -> Result<()> {
1849        let mut client = Client::from_config(test_config()).await?;
1850        client.send_samode("#test", "+o", "test")?;
1851        client.stream()?.collect().await?;
1852        assert_eq!(&get_client_value(client)[..], "SAMODE #test +o test\r\n");
1853        Ok(())
1854    }
1855
1856    #[tokio::test]
1857    async fn send_sanick() -> Result<()> {
1858        let mut client = Client::from_config(test_config()).await?;
1859        client.send_sanick("test", "test2")?;
1860        client.stream()?.collect().await?;
1861        assert_eq!(&get_client_value(client)[..], "SANICK test test2\r\n");
1862        Ok(())
1863    }
1864
1865    #[tokio::test]
1866    async fn send_invite() -> Result<()> {
1867        let mut client = Client::from_config(test_config()).await?;
1868        client.send_invite("test", "#test")?;
1869        client.stream()?.collect().await?;
1870        assert_eq!(&get_client_value(client)[..], "INVITE test #test\r\n");
1871        Ok(())
1872    }
1873
1874    #[tokio::test]
1875    #[cfg(feature = "ctcp")]
1876    async fn send_ctcp() -> Result<()> {
1877        let mut client = Client::from_config(test_config()).await?;
1878        client.send_ctcp("test", "LINE1\r\nLINE2\r\nLINE3")?;
1879        client.stream()?.collect().await?;
1880        assert_eq!(
1881            &get_client_value(client)[..],
1882            "PRIVMSG test \u{001}LINE1\u{001}\r\nPRIVMSG test \u{001}LINE2\u{001}\r\nPRIVMSG test \u{001}LINE3\u{001}\r\n"
1883        );
1884        Ok(())
1885    }
1886
1887    #[tokio::test]
1888    #[cfg(feature = "ctcp")]
1889    async fn send_action() -> Result<()> {
1890        let mut client = Client::from_config(test_config()).await?;
1891        client.send_action("test", "tests.")?;
1892        client.stream()?.collect().await?;
1893        assert_eq!(
1894            &get_client_value(client)[..],
1895            "PRIVMSG test :\u{001}ACTION tests.\u{001}\r\n"
1896        );
1897        Ok(())
1898    }
1899
1900    #[tokio::test]
1901    #[cfg(feature = "ctcp")]
1902    async fn send_finger() -> Result<()> {
1903        let mut client = Client::from_config(test_config()).await?;
1904        client.send_finger("test")?;
1905        client.stream()?.collect().await?;
1906        assert_eq!(
1907            &get_client_value(client)[..],
1908            "PRIVMSG test \u{001}FINGER\u{001}\r\n"
1909        );
1910        Ok(())
1911    }
1912
1913    #[tokio::test]
1914    #[cfg(feature = "ctcp")]
1915    async fn send_version() -> Result<()> {
1916        let mut client = Client::from_config(test_config()).await?;
1917        client.send_version("test")?;
1918        client.stream()?.collect().await?;
1919        assert_eq!(
1920            &get_client_value(client)[..],
1921            "PRIVMSG test \u{001}VERSION\u{001}\r\n"
1922        );
1923        Ok(())
1924    }
1925
1926    #[tokio::test]
1927    #[cfg(feature = "ctcp")]
1928    async fn send_source() -> Result<()> {
1929        let mut client = Client::from_config(test_config()).await?;
1930        client.send_source("test")?;
1931        client.stream()?.collect().await?;
1932        assert_eq!(
1933            &get_client_value(client)[..],
1934            "PRIVMSG test \u{001}SOURCE\u{001}\r\n"
1935        );
1936        Ok(())
1937    }
1938
1939    #[tokio::test]
1940    #[cfg(feature = "ctcp")]
1941    async fn send_user_info() -> Result<()> {
1942        let mut client = Client::from_config(test_config()).await?;
1943        client.send_user_info("test")?;
1944        client.stream()?.collect().await?;
1945        assert_eq!(
1946            &get_client_value(client)[..],
1947            "PRIVMSG test \u{001}USERINFO\u{001}\r\n"
1948        );
1949        Ok(())
1950    }
1951
1952    #[tokio::test]
1953    #[cfg(feature = "ctcp")]
1954    async fn send_ctcp_ping() -> Result<()> {
1955        let mut client = Client::from_config(test_config()).await?;
1956        client.send_ctcp_ping("test")?;
1957        client.stream()?.collect().await?;
1958        let val = get_client_value(client);
1959        println!("{}", val);
1960        assert!(val.starts_with("PRIVMSG test :\u{001}PING "));
1961        assert!(val.ends_with("\u{001}\r\n"));
1962        Ok(())
1963    }
1964
1965    #[tokio::test]
1966    #[cfg(feature = "ctcp")]
1967    async fn send_time() -> Result<()> {
1968        let mut client = Client::from_config(test_config()).await?;
1969        client.send_time("test")?;
1970        client.stream()?.collect().await?;
1971        assert_eq!(
1972            &get_client_value(client)[..],
1973            "PRIVMSG test \u{001}TIME\u{001}\r\n"
1974        );
1975        Ok(())
1976    }
1977}