summaryrefslogtreecommitdiff
path: root/src/main.rs
blob: b536464b21b637e47a2a169e940b7c656728acff (plain)
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
use std::env;
use std::iter::repeat;
use std::sync::Arc;
use std::thread;

extern crate ascii;
extern crate crypto;
extern crate getopts;
#[macro_use] extern crate log;
extern crate tiny_http;
extern crate time;
extern crate rand;
extern crate rustc_serialize;
extern crate simple_logger;
extern crate url;

use crypto::digest::Digest;
use crypto::sha1::Sha1;
use getopts::Options;
use rand::{thread_rng, Rng};
use log::LogLevel;

mod apachelog;
mod handler;
mod message;
mod sendxmpp;
mod token;

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("j", "jid", "bot jid", "JID");
    opts.optopt("p", "password", "bot password", "PASSWORD");
    opts.optopt("s", "secret", "server secret for token generation", "SECRET");
    opts.optopt("t", "time", "Validity of the token in hours (default 48)", "HOURS");
    opts.optopt("o", "port", "TCP Port to listen on", "PORT");
    opts.optflag("d", "debug", "Use loglevel Debug instead of Warn");
    opts.optflag("n", "nosend", "Don't send XMPP message, just print debug infos");
    opts.optflag("h", "help", "print this help menu");
    let matches = opts.parse(&args[1..]).unwrap_or_else(|f| panic!(f.to_string()));

    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }

    if !(matches.opt_present("j") && matches.opt_present("p")) && !matches.opt_present("n") {
        print_usage(&program, opts);
        panic!("Missing jid or password");
    }

    simple_logger::init_with_level(if matches.opt_present("d") { LogLevel::Debug } else { LogLevel::Warn }).unwrap();

    let mut hasher = Sha1::new();
    let mut secret: Vec<u8> = repeat(0).take((hasher.output_bits() + 7) / 8).collect();
    matches.opt_str("s").and_then(|s| {
        hasher.input_str(s.as_str());
        hasher.result(&mut secret);
        Some(())
    }).unwrap_or_else(|| {
        println!("No secret (-s/--secret) given, using random value");
        thread_rng().fill_bytes(&mut secret);
    });
    let secret = secret.into_iter().take(16).collect::<Vec<u8>>();
    let validity: i64 = matches.opt_str("t").unwrap_or(String::from("48")).parse()
        .unwrap_or_else(|_| { panic!("Failed to parse time") });
    let port = matches.opt_str("o").unwrap_or(String::from("8080")).parse()
        .unwrap_or_else(|_| { panic!("Failed to parse port number") });

    let handler = handler::AuthHandler::make(matches.opt_str("j").unwrap_or(String::from("<jid>")),
                                             matches.opt_str("p").unwrap_or(String::from("<password>")),
                                             time::Duration::hours(validity),
                                             secret,
                                             matches.opt_present("n"));
    let handler = Arc::new(handler);
    let server = Arc::new(tiny_http::Server::http(("0.0.0.0", port)).unwrap());

    let mut threads = Vec::new();

    for _ in 0..2 {
        let server = server.clone();
        let handler = handler.clone();
        threads.push(thread::spawn(move || {
            for request in server.incoming_requests() {
                let response = handler.call(&request);
                let _ = request.respond(response);
            }
        }));
    }
    for h in threads {
        h.join().unwrap();
    }
}