aboutsummaryrefslogtreecommitdiffstats
path: root/mumd/src/main.rs
blob: c923857236fe1e27f3b9f701f1ab92edbe1907b5 (plain) (blame)
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
mod audio;
mod network;
mod command;
mod state;

use crate::network::ConnectionInfo;
use crate::command::{Command, CommandResponse};
use crate::state::State;

use argparse::ArgumentParser;
use argparse::Store;
use argparse::StoreTrue;
use colored::*;
use tokio::sync::oneshot;
use futures::{join, select};
use log::*;
use mumble_protocol::control::ControlPacket;
use mumble_protocol::crypt::ClientCryptState;
use mumble_protocol::voice::Serverbound;
use std::sync::{Arc, Mutex};
use tokio::sync::{mpsc, watch};
use std::thread;
use std::time::Duration;
use tokio::stream::StreamExt;
use futures::FutureExt;

#[tokio::main]
async fn main() {
    // setup logger
    fern::Dispatch::new()
        .format(|out, message, record| {
            let message = message.to_string();
            out.finish(format_args!(
                "{} {}:{}{}{}",
                //TODO runtime flag that disables color
                match record.level() {
                    Level::Error => "ERROR".red(),
                    Level::Warn  => "WARN ".yellow(),
                    Level::Info  => "INFO ".normal(),
                    Level::Debug => "DEBUG".green(),
                    Level::Trace => "TRACE".normal(),
                },
                record.file().unwrap(),
                record.line().unwrap(),
                if message.chars().any(|e| e == '\n') { "\n" } else { " " },
                message
            ))
        })
        .level(log::LevelFilter::Debug)
        .chain(std::io::stderr())
        .apply().unwrap();

    // Handle command line arguments
    let mut server_host = "".to_string();
    let mut server_port = 64738u16;
    let mut username = "EchoBot".to_string();
    let mut accept_invalid_cert = false;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Run the echo client example");
        ap.refer(&mut server_host)
            .add_option(&["--host"], Store, "Hostname of mumble server")
            .required();
        ap.refer(&mut server_port)
            .add_option(&["--port"], Store, "Port of mumble server");
        ap.refer(&mut username)
            .add_option(&["--username"], Store, "User name used to connect");
        ap.refer(&mut accept_invalid_cert).add_option(
            &["--accept-invalid-cert"],
            StoreTrue,
            "Accept invalid TLS certificates",
        );
        ap.parse_args_or_exit();
    }

    // Oneshot channel for setting UDP CryptState from control task
    // For simplicity we don't deal with re-syncing, real applications would have to.
    let (crypt_state_sender, crypt_state_receiver) = oneshot::channel::<ClientCryptState>();
    let (packet_sender, packet_receiver) = mpsc::unbounded_channel::<ControlPacket<Serverbound>>();
    let (command_sender, command_receiver) = mpsc::unbounded_channel::<Command>();
    let (command_response_sender, command_response_receiver) = mpsc::unbounded_channel::<Result<Option<CommandResponse>, ()>>();
    let (connection_info_sender, connection_info_receiver) = watch::channel::<Option<ConnectionInfo>>(None);

    command_sender.send(Command::ChannelList).unwrap();
    command_sender.send(Command::ServerConnect{host: server_host, port: server_port, username: username.clone(), accept_invalid_cert});
    //command_sender.send(Command::ChannelJoin{channel_id: 1}).unwrap();
    command_sender.send(Command::ChannelList).unwrap();
    let state = State::new(packet_sender, command_sender.clone(), connection_info_sender, username);
    let state = Arc::new(Mutex::new(state));

    // Run it
    join!(
        network::tcp::handle(
            Arc::clone(&state),
            connection_info_receiver.clone(),
            crypt_state_sender,
            packet_receiver,
        ),
        network::udp::handle(
            Arc::clone(&state),
            connection_info_receiver.clone(),
            crypt_state_receiver,
        ),
        command::handle(
            state,
            command_receiver,
            command_response_sender,
        ),
        send_commands(
            command_sender
        ),
        receive_command_responses(
            command_response_receiver,
        ),
    );
}

async fn send_commands(command_sender: mpsc::UnboundedSender<Command>) {
    tokio::time::delay_for(Duration::from_secs(5)).await;
    command_sender.send(Command::ServerDisconnect);

    debug!("Finished sending commands");
}

async fn receive_command_responses(
    mut command_response_receiver: mpsc::UnboundedReceiver<Result<Option<CommandResponse>, ()>>,
) {
    while let Some(command_response) = command_response_receiver.recv().await {
        debug!("{:?}", command_response);
    }

    debug!("Finished receiving commands");
}