<commit_before>use std::env;
use std::io::ErrorKind;
use std::process;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;
use std::net::SocketAddr;
use hex;
use time;

use byteorder::{LittleEndian, WriteBytesExt};

use mio::net::UdpSocket;
use mio::{Events, Poll, PollOpt, Ready, Token};
use mio_extras::timer::Timer;

use config;
use config::ServerConfig;
use kms;
use key::{LongTermKey, OnlineKey};
use merkle::MerkleTree;
use {Error, RtMessage, Tag};
use {MIN_REQUEST_LENGTH, VERSION};

macro_rules! check_ctrlc {
    ($keep_running:expr) => {
        if !$keep_running.load(Ordering::Acquire) {
            warn!("Ctrl-C caught, exiting...");
            return true;
        }
    }
}

const MESSAGE: Token = Token(0);
const STATUS: Token = Token(1);

fn make_response(srep: &RtMessage, cert_bytes: &[u8], path: &[u8], idx: u32) -> RtMessage {
    let mut index = [0; 4];
    (&mut index as &mut [u8])
        .write_u32::<LittleEndian>(idx)
        .unwrap();

    let sig_bytes = srep.get_field(Tag::SIG).unwrap();
    let srep_bytes = srep.get_field(Tag::SREP).unwrap();

    let mut response = RtMessage::new(5);
    response.add_field(Tag::SIG, sig_bytes).unwrap();
    response.add_field(Tag::PATH, path).unwrap();
    response.add_field(Tag::SREP, srep_bytes).unwrap();
    response.add_field(Tag::CERT, cert_bytes).unwrap();
    response.add_field(Tag::INDX, &index).unwrap();

    response
}

// extract the client's nonce from its request
fn nonce_from_request(buf: &[u8], num_bytes: usize) -> Result<&[u8], Error> {
    if num_bytes < MIN_REQUEST_LENGTH as usize {
        return Err(Error::RequestTooShort);
    }

    let tag_count = &buf[..4];
    let expected_nonc = &buf[8..12];
    let expected_pad = &buf[12..16];

    let tag_count_is_2 = tag_count == [0x02, 0x00, 0x00, 0x00];
    let tag1_is_nonc = expected_nonc == Tag::NONC.wire_value();
    let tag2_is_pad = expected_pad == Tag::PAD.wire_value();

    if tag_count_is_2 && tag1_is_nonc && tag2_is_pad {
        Ok(&buf[0x10..0x50])
    } else {
        Err(Error::InvalidRequest)
    }
}

pub struct Server {
    config: Box<ServerConfig>,
    online_key: OnlineKey,
    cert_bytes: Vec<u8>,

    response_counter: AtomicUsize,
    num_bad_requests: u64,

    socket: UdpSocket,
    fake_client_socket: UdpSocket,
    keep_running: Arc<AtomicBool>,
    poll_duration: Option<Duration>,
    timer: Timer<()>,
    poll: Poll,
    events: Events,
    merkle: MerkleTree,
    requests: Vec<(Vec<u8>, SocketAddr)>,
    buf: [u8; 65_536],

    public_key: String,
}

impl Server {
    pub fn new(config: Box<ServerConfig>) -> Server {

        let mut online_key = OnlineKey::new();
        let public_key: String;

        let cert_bytes = {
            let seed = kms::load_seed(&config).unwrap();
            let mut long_term_key = LongTermKey::new(&seed);
            public_key = hex::encode(long_term_key.public_key());

            long_term_key.make_cert(&online_key).encode().unwrap()
        };

        let response_counter = AtomicUsize::new(0);
        let keep_running = Arc::new(AtomicBool::new(true));

        let sock_addr = config.socket_addr().expect("");
        let socket = UdpSocket::bind(&sock_addr).expect("failed to bind to socket");
        let fake_client_socket = UdpSocket::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();

        let poll_duration = Some(Duration::from_millis(100));

        let mut timer: Timer<()> = Timer::default();
        //timer.set_timeout(config.status_interval(), ());

        let poll = Poll::new().unwrap();
        poll.register(&socket, MESSAGE, Ready::readable(), PollOpt::edge())
            .unwrap();
        //poll.register(&timer, STATUS, Ready::readable(), PollOpt::edge())
        //    .unwrap();

        let mut merkle = MerkleTree::new();
        let mut requests = Vec::with_capacity(config.batch_size() as usize);

        Server {
            config,
            online_key,
            cert_bytes,

            response_counter,
            num_bad_requests: 0,
            socket,
            fake_client_socket,
            keep_running,
            poll_duration,
            timer,
            poll,
            events: Events::with_capacity(32),
            merkle,
            requests,
            buf: [0u8; 65_536],

            public_key
        }

    }

    pub fn get_keep_running(&self) -> Arc<AtomicBool> {
        return self.keep_running.clone()
    }

    pub fn process_events(&mut self) -> bool {
        self.poll.poll(&mut self.events, self.poll_duration).expect("poll failed");

        for event in self.events.iter() {
            match event.token() {
                MESSAGE => {
                    let mut done = false;

                    'process_batch: loop {
                        check_ctrlc!(self.keep_running);

                        let resp_start = self.response_counter.load(Ordering::SeqCst);

                        for i in 0..self.config.batch_size() {
                            match self.socket.recv_from(&mut self.buf) {
                                Ok((num_bytes, src_addr)) => {
                                    info!("Read bytes: {}", num_bytes);
                                    match nonce_from_request(&self.buf, num_bytes) {
                                        Ok(nonce) => {
                                            self.requests.push((Vec::from(nonce), src_addr));
                                            self.merkle.push_leaf(nonce);
                                        },
                                        Err(e) => {
                                           self.num_bad_requests += 1;

                                             info!(
                                                "Invalid request: '{:?}' ({} bytes) from {} (#{} in batch, resp #{})",
                                                e, num_bytes, src_addr, i, resp_start + i as usize
                                            );
                                        }
                                    }
                                },
                                Err(e) => match e.kind() {
                                    ErrorKind::WouldBlock => {
                                        done = true;
                                        break;
                                    }
                                    _ => {
                                        error!(
                                            "Error receiving from socket: {:?}: {:?}",
                                            e.kind(),
                                            e
                                        );
                                        break;
                                    }
                                },
                            };
                        }

                        if self.requests.is_empty() {
                            break 'process_batch;
                        }

                        let merkle_root = self.merkle.compute_root();
                        let srep = self.online_key.make_srep(time::get_time(), &merkle_root);

                        for (i, &(ref nonce, ref src_addr)) in self.requests.iter().enumerate() {
                            let paths = self.merkle.get_paths(i);

                            let resp = make_response(&srep, &self.cert_bytes, &paths, i as u32);
                            let resp_bytes = resp.encode().unwrap();

                            let bytes_sent = self.socket
                                .send_to(&resp_bytes, &src_addr)
                                .expect("send_to failed");
                            let num_responses = self.response_counter.fetch_add(1, Ordering::SeqCst);

                            info!(
                                "Responded {} bytes to {} for '{}..' (#{} in batch, resp #{})",
                                bytes_sent,
                                src_addr,
                                hex::encode(&nonce[0..4]),
                                i,
                                num_responses
                            );
                        }

                        self.merkle.reset();
                        self.requests.clear();

                        if done {
                            break 'process_batch;
                        }
                    }
                }

                STATUS => {
                    info!(
                        "responses {}, invalid requests {}",
                        self.response_counter.load(Ordering::SeqCst),
                        self.num_bad_requests
                    );

                    self.timer.set_timeout(self.config.status_interval(), ());
                }

                _ => unreachable!(),
            }
        }
        false
    }

    pub fn send_to_self(&mut self, data: &[u8]) {
        self.response_counter.store(0,  Ordering::SeqCst);;
        self.num_bad_requests = 0;
        let res = self.fake_client_socket.send_to(data, &self.socket.local_addr().unwrap());
        info!("Sent to self: {:?}", res);
    }

    pub fn get_public_key(&self) -> &str {
        return &self.public_key
    }

    pub fn get_online_key(&self) -> &OnlineKey {
        return &self.online_key
    }

    pub fn get_config(&self) -> &Box<ServerConfig> {
        return &self.config
    }
}
<commit_msg>Move more things into Server struct<commit_after>use std::env;
use std::io::ErrorKind;
use std::process;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;
use std::net::SocketAddr;
use hex;
use time;

use byteorder::{LittleEndian, WriteBytesExt};

use mio::net::UdpSocket;
use mio::{Events, Poll, PollOpt, Ready, Token};
use mio_extras::timer::Timer;

use config;
use config::ServerConfig;
use kms;
use key::{LongTermKey, OnlineKey};
use merkle::MerkleTree;
use {Error, RtMessage, Tag};
use {MIN_REQUEST_LENGTH, VERSION};

macro_rules! check_ctrlc {
    ($keep_running:expr) => {
        if !$keep_running.load(Ordering::Acquire) {
            warn!("Ctrl-C caught, exiting...");
            return true;
        }
    }
}

const MESSAGE: Token = Token(0);
const STATUS: Token = Token(1);

pub struct Server {
    config: Box<ServerConfig>,
    online_key: OnlineKey,
    cert_bytes: Vec<u8>,

    response_counter: AtomicUsize,
    num_bad_requests: u64,

    socket: UdpSocket,
    fake_client_socket: UdpSocket,
    keep_running: Arc<AtomicBool>,
    poll_duration: Option<Duration>,
    timer: Timer<()>,
    poll: Poll,
    events: Events,
    merkle: MerkleTree,
    requests: Vec<(Vec<u8>, SocketAddr)>,
    buf: [u8; 65_536],

    public_key: String,
}

impl Server {
    pub fn new(config: Box<ServerConfig>) -> Server {

        let mut online_key = OnlineKey::new();
        let public_key: String;

        let cert_bytes = {
            let seed = kms::load_seed(&config).unwrap();
            let mut long_term_key = LongTermKey::new(&seed);
            public_key = hex::encode(long_term_key.public_key());

            long_term_key.make_cert(&online_key).encode().unwrap()
        };

        let response_counter = AtomicUsize::new(0);
        let keep_running = Arc::new(AtomicBool::new(true));

        let sock_addr = config.socket_addr().expect("");
        let socket = UdpSocket::bind(&sock_addr).expect("failed to bind to socket");
        let fake_client_socket = UdpSocket::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();

        let poll_duration = Some(Duration::from_millis(100));

        let mut timer: Timer<()> = Timer::default();
        //timer.set_timeout(config.status_interval(), ());

        let poll = Poll::new().unwrap();
        poll.register(&socket, MESSAGE, Ready::readable(), PollOpt::edge())
            .unwrap();
        //poll.register(&timer, STATUS, Ready::readable(), PollOpt::edge())
        //    .unwrap();

        let mut merkle = MerkleTree::new();
        let mut requests = Vec::with_capacity(config.batch_size() as usize);

        Server {
            config,
            online_key,
            cert_bytes,

            response_counter,
            num_bad_requests: 0,
            socket,
            fake_client_socket,
            keep_running,
            poll_duration,
            timer,
            poll,
            events: Events::with_capacity(32),
            merkle,
            requests,
            buf: [0u8; 65_536],

            public_key
        }

    }

    pub fn get_keep_running(&self) -> Arc<AtomicBool> {
        return self.keep_running.clone()
    }

    // extract the client's nonce from its request
    fn nonce_from_request(buf: &[u8], num_bytes: usize) -> Result<&[u8], Error> {
        if num_bytes < MIN_REQUEST_LENGTH as usize {
            return Err(Error::RequestTooShort);
        }

        let tag_count = &buf[..4];
        let expected_nonc = &buf[8..12];
        let expected_pad = &buf[12..16];

        let tag_count_is_2 = tag_count == [0x02, 0x00, 0x00, 0x00];
        let tag1_is_nonc = expected_nonc == Tag::NONC.wire_value();
        let tag2_is_pad = expected_pad == Tag::PAD.wire_value();

        if tag_count_is_2 && tag1_is_nonc && tag2_is_pad {
            Ok(&buf[0x10..0x50])
        } else {
            Err(Error::InvalidRequest)
        }
    }

    fn make_response(srep: &RtMessage, cert_bytes: &[u8], path: &[u8], idx: u32) -> RtMessage {
        let mut index = [0; 4];
        (&mut index as &mut [u8])
            .write_u32::<LittleEndian>(idx)
            .unwrap();

        let sig_bytes = srep.get_field(Tag::SIG).unwrap();
        let srep_bytes = srep.get_field(Tag::SREP).unwrap();

        let mut response = RtMessage::new(5);
        response.add_field(Tag::SIG, sig_bytes).unwrap();
        response.add_field(Tag::PATH, path).unwrap();
        response.add_field(Tag::SREP, srep_bytes).unwrap();
        response.add_field(Tag::CERT, cert_bytes).unwrap();
        response.add_field(Tag::INDX, &index).unwrap();

        response
    }

    pub fn process_events(&mut self) -> bool {
        self.poll.poll(&mut self.events, self.poll_duration).expect("poll failed");

        for event in self.events.iter() {
            match event.token() {
                MESSAGE => {
                    let mut done = false;

                    'process_batch: loop {
                        check_ctrlc!(self.keep_running);

                        let resp_start = self.response_counter.load(Ordering::SeqCst);

                        for i in 0..self.config.batch_size() {
                            match self.socket.recv_from(&mut self.buf) {
                                Ok((num_bytes, src_addr)) => {
                                    info!("Read bytes: {}", num_bytes);
                                    match self.nonce_from_request(&self.buf, num_bytes) {
                                        Ok(nonce) => {
                                            self.requests.push((Vec::from(nonce), src_addr));
                                            self.merkle.push_leaf(nonce);
                                        },
                                        Err(e) => {
                                           self.num_bad_requests += 1;

                                             info!(
                                                "Invalid request: '{:?}' ({} bytes) from {} (#{} in batch, resp #{})",
                                                e, num_bytes, src_addr, i, resp_start + i as usize
                                            );
                                        }
                                    }
                                },
                                Err(e) => match e.kind() {
                                    ErrorKind::WouldBlock => {
                                        done = true;
                                        break;
                                    }
                                    _ => {
                                        error!(
                                            "Error receiving from socket: {:?}: {:?}",
                                            e.kind(),
                                            e
                                        );
                                        break;
                                    }
                                },
                            };
                        }

                        if self.requests.is_empty() {
                            break 'process_batch;
                        }

                        let merkle_root = self.merkle.compute_root();
                        let srep = self.online_key.make_srep(time::get_time(), &merkle_root);

                        for (i, &(ref nonce, ref src_addr)) in self.requests.iter().enumerate() {
                            let paths = self.merkle.get_paths(i);

                            let resp = make_response(&srep, &self.cert_bytes, &paths, i as u32);
                            let resp_bytes = resp.encode().unwrap();

                            let bytes_sent = self.socket
                                .send_to(&resp_bytes, &src_addr)
                                .expect("send_to failed");
                            let num_responses = self.response_counter.fetch_add(1, Ordering::SeqCst);

                            info!(
                                "Responded {} bytes to {} for '{}..' (#{} in batch, resp #{})",
                                bytes_sent,
                                src_addr,
                                hex::encode(&nonce[0..4]),
                                i,
                                num_responses
                            );
                        }

                        self.merkle.reset();
                        self.requests.clear();

                        if done {
                            break 'process_batch;
                        }
                    }
                }

                STATUS => {
                    info!(
                        "responses {}, invalid requests {}",
                        self.response_counter.load(Ordering::SeqCst),
                        self.num_bad_requests
                    );

                    self.timer.set_timeout(self.config.status_interval(), ());
                }

                _ => unreachable!(),
            }
        }
        false
    }

    #[cfg(fuzzing)]
    pub fn send_to_self(&mut self, data: &[u8]) {
        self.response_counter.store(0,  Ordering::SeqCst);;
        self.num_bad_requests = 0;
        let res = self.fake_client_socket.send_to(data, &self.socket.local_addr().unwrap());
        info!("Sent to self: {:?}", res);
    }

    pub fn get_public_key(&self) -> &str {
        return &self.public_key
    }

    pub fn get_online_key(&self) -> &OnlineKey {
        return &self.online_key
    }

    pub fn get_config(&self) -> &Box<ServerConfig> {
        return &self.config
    }
}
<|endoftext|>


<commit_before>// The Base64-encoded content in this file (7.txt)has been encrypted
// via AES-128 in ECB mode under the key

// "YELLOW SUBMARINE".
// (case-sensitive, without the quotes; exactly 16 characters;
// I like "YELLOW SUBMARINE" because it's exactly 16 bytes long,
// and now you do too).

// Decrypt it. You know the key, after all.

// Easiest way: use OpenSSL::Cipher and give it AES-128-ECB as the cipher.

#![allow(unused_features)]

#![feature(core)]
#![feature(env)]
#![feature(old_io)]
#![feature(old_path)]

#[macro_use]
extern crate log;
extern crate openssl;

use std::ascii::AsciiExt;
use std::iter::range_step;
use std::num::Int;
use std::str;

#[cfg(not(test))]
use std::env;
#[cfg(not(test))]
use std::old_io::BufferedReader;
#[cfg(not(test))]
use std::old_io::File;

#[cfg(not(test))]
fn main() {

	println!("Decoding...");

	if env::args().count() < 2 {
		panic!("Must pass a file to decode")
	}

	let arg = match env::args().nth(1) {
		Some(s) => s,
		None => panic!("No input argument given")
	};

	let output = decrypt_aes_ecb_128_file("YELLOW SUBMARINE", &arg);
	println!("output: {:?}", output);
}

#[cfg(not(test))]
fn decrypt_aes_ecb_128_file(key: &str, path: &str) -> String {
	let path = Path::new(path);
	let mut file = BufferedReader::new(File::open(&path));
	let lines: Vec<_> = file.lines()
		.map(|x| x.unwrap())
		.collect();

	decrypt_base64_aes_ecb_128(key, lines)
}

fn decrypt_base64_aes_ecb_128(key: &str, lines: Vec<String>) -> String {
	let base64_decoded = base64_lines_to_hex(&lines);
	let nums = convert_hex_string_to_decimal_pairs(&base64_decoded);

	let t = openssl::crypto::symm::decrypt(openssl::crypto::symm::Type::AES_128_ECB, key.as_bytes(), vec!(), &nums);

	match str::from_utf8(&t) {
		Ok(s) => return s.to_string(),
		Err(_) => panic!("Result couldn't be converted to u8")
	}
}

fn convert_hex_string_to_decimal_pairs(string: &String) -> Vec<u8> {
	let string_lower = string.to_ascii_lowercase();
	let bytes = string_lower.as_bytes();

	let mut decimal_values = vec!();
	let mut tick = 1;
	let mut current_byte = 0;

	for &x in bytes.iter() {
		current_byte += convert_hex_char_to_decimal(x) * 16.pow(tick);
		tick = tick ^ 1;
		if tick == 1 {
			decimal_values.push(current_byte);
			current_byte = 0;
		}
	}

	decimal_values
}

fn convert_hex_char_to_decimal(character: u8) -> u8 {
	// if x is in the ascii range for numbers, subtract 48,
	// which is '0' in ascii. Otherwise, it should be a lowercase letter.
	// 'a' is 97, so subtract 87.
	match character < 58 {
		true => character - 48,
		false => character - 87
	}
}

// BASE 64 TO HEX

fn base64_lines_to_hex(lines: &Vec<String>) -> String {
	let mut output = String::new();
	for line in lines {
		output.push_str(&base64_to_hex(&line.trim()));
	}
	output
}

fn base64_to_hex(input: &str) -> String {
	let bytes = input.as_bytes();
	let mut nums = vec!();
	for index in range_step(0, bytes.len(), 4) {
		let slice = &bytes[index..index+4];
		debug!("{:?}", index);
		let byte0 = base64_ascii_to_index(slice[0]);
		let byte1 = base64_ascii_to_index(slice[1]);
		let byte2 = base64_ascii_to_index(slice[2]);
		let byte3 = base64_ascii_to_index(slice[3]);

		match (byte0, byte1, byte2, byte3) {
			(Some(b0), Some(b1), Some(b2), Some(b3)) => {
				nums.push(b0 >> 2);
				nums.push(((b0 & 3) << 2) + (b1 >> 4));
				nums.push(b1 & 15);
				nums.push((b2 & 60) >> 2);
				nums.push(((b2 & 3) << 2) + (b3 >> 4));
				nums.push(b3 & 15);
			},
			(Some(b0), Some(b1), Some(b2), None) => {
				nums.push(b0 >> 2);
				nums.push(((b0 & 3) << 2) + (b1 >> 4));
				nums.push(b1 & 15);
				nums.push((b2 & 60) >> 2);
			},
			(Some(b0), Some(b1), None, None) => {
				nums.push(b0 >> 2);
				nums.push(((b0 & 3) << 2) + (b1 >> 4));
			},
			_ => panic!("Only the last two bytes may be unused")
		};

	}

	let hex: Vec<u8> = nums.iter().map(|&x| convert_num_to_hex_char(x)).collect();

	match str::from_utf8(hex.as_slice()) {
	    Ok(v) => {
	        debug!("output string: {:?}", v);
	        return v.to_string();
	    }
	    Err(e) => {
	        panic!("error parsing string: {:?}", e);
    	}
	}

}

fn base64_ascii_to_index(ascii: u8) -> Option<u8> {
	match ascii {
		65...90 => Some(ascii - 65), // uppercase
		97...122 => Some(ascii - 71), // lowercase
		48...57 => Some(ascii + 4), // numbers
		43 => Some(ascii + 19), // +
		47 => Some(ascii + 16), // /
		// equals means empty byte. need some way of
		// differentiating from 'A'.
		61 => None, // =

		_ => panic!("{:?}: Not in base64 range", ascii)
	}
}

fn convert_num_to_hex_char(num: u8) -> u8 {
	match num {
		0...9 => num + 48,
		10...15 => num + 87,
		_ => panic!("Not a valid hex char")
	}
}

#[cfg(test)]
mod set1challenge7 {

	#[test]
	fn decrypt_aes_ecb_128() {
		let input = "o3VBEciqmzUQswmiEMLdfPuhlv1XK0i0ww26jHAiaeY=".to_string();
		let original = "This is encrypt.";
		let key = "TESTTESTTESTTEST";

		let output = super::decrypt_base64_aes_ecb_128(key, vec!(input));
		assert_eq!(output, original);
	}

}<commit_msg>Updated challenge 7 to latest Rust<commit_after>// The Base64-encoded content in this file (7.txt)has been encrypted
// via AES-128 in ECB mode under the key

// "YELLOW SUBMARINE".
// (case-sensitive, without the quotes; exactly 16 characters;
// I like "YELLOW SUBMARINE" because it's exactly 16 bytes long,
// and now you do too).

// Decrypt it. You know the key, after all.

// Easiest way: use OpenSSL::Cipher and give it AES-128-ECB as the cipher.

#![feature(step_by)]

#[macro_use]
extern crate log;
extern crate openssl;

use std::ascii::AsciiExt;
use std::str;

#[cfg(not(test))]
use std::env;
#[cfg(not(test))]
use std::io::BufReader;
#[cfg(not(test))]
use std::fs::File;
#[cfg(not(test))]
use std::path::Path;
#[cfg(not(test))]
use std::io::prelude::*;

#[cfg(not(test))]
fn main() {

	println!("Decoding...");

	if env::args().count() < 2 {
		panic!("Must pass a file to decode")
	}

	let arg = match env::args().nth(1) {
		Some(s) => s,
		None => panic!("No input argument given")
	};

	let output = decrypt_aes_ecb_128_file("YELLOW SUBMARINE", &arg);
	println!("output: {:?}", output);
}

#[cfg(not(test))]
fn decrypt_aes_ecb_128_file(key: &str, path: &str) -> String {
	let path = Path::new(path);
	let file = BufReader::new(File::open(&path).unwrap());
	let lines: Vec<_> = file.lines()
		.map(|x| x.unwrap())
		.collect();

	decrypt_base64_aes_ecb_128(key, lines)
}

fn decrypt_base64_aes_ecb_128(key: &str, lines: Vec<String>) -> String {
	let base64_decoded = base64_lines_to_hex(&lines);
	let nums = convert_hex_string_to_decimal_pairs(&base64_decoded);

	let t = openssl::crypto::symm::decrypt(openssl::crypto::symm::Type::AES_128_ECB, key.as_bytes(), vec!(), &nums);

	match str::from_utf8(&t) {
		Ok(s) => return s.to_string(),
		Err(_) => panic!("Result couldn't be converted to u8")
	}
}

fn convert_hex_string_to_decimal_pairs(string: &String) -> Vec<u8> {
	let string_lower = string.to_ascii_lowercase();
	let bytes = string_lower.as_bytes();

	let mut decimal_values = vec!();
	let mut tick = 1;
	let mut current_byte = 0;

	for &x in bytes.iter() {
		current_byte += convert_hex_char_to_decimal(x) * 16u8.pow(tick);
		tick = tick ^ 1;
		if tick == 1 {
			decimal_values.push(current_byte);
			current_byte = 0;
		}
	}

	decimal_values
}

fn convert_hex_char_to_decimal(character: u8) -> u8 {
	// if x is in the ascii range for numbers, subtract 48,
	// which is '0' in ascii. Otherwise, it should be a lowercase letter.
	// 'a' is 97, so subtract 87.
	match character < 58 {
		true => character - 48,
		false => character - 87
	}
}

// BASE 64 TO HEX

fn base64_lines_to_hex(lines: &Vec<String>) -> String {
	let mut output = String::new();
	for line in lines {
		output.push_str(&base64_to_hex(&line.trim()));
	}
	output
}

fn base64_to_hex(input: &str) -> String {
	let bytes = input.as_bytes();
	let mut nums = vec!();
	for index in (0..bytes.len()).step_by(4) {
		let slice = &bytes[index..index+4];
		debug!("{:?}", index);
		let byte0 = base64_ascii_to_index(slice[0]);
		let byte1 = base64_ascii_to_index(slice[1]);
		let byte2 = base64_ascii_to_index(slice[2]);
		let byte3 = base64_ascii_to_index(slice[3]);

		match (byte0, byte1, byte2, byte3) {
			(Some(b0), Some(b1), Some(b2), Some(b3)) => {
				nums.push(b0 >> 2);
				nums.push(((b0 & 3) << 2) + (b1 >> 4));
				nums.push(b1 & 15);
				nums.push((b2 & 60) >> 2);
				nums.push(((b2 & 3) << 2) + (b3 >> 4));
				nums.push(b3 & 15);
			},
			(Some(b0), Some(b1), Some(b2), None) => {
				nums.push(b0 >> 2);
				nums.push(((b0 & 3) << 2) + (b1 >> 4));
				nums.push(b1 & 15);
				nums.push((b2 & 60) >> 2);
			},
			(Some(b0), Some(b1), None, None) => {
				nums.push(b0 >> 2);
				nums.push(((b0 & 3) << 2) + (b1 >> 4));
			},
			_ => panic!("Only the last two bytes may be unused")
		};

	}

	let hex: Vec<u8> = nums.iter().map(|&x| convert_num_to_hex_char(x)).collect();

	match str::from_utf8(&hex) {
	    Ok(v) => {
	        debug!("output string: {:?}", v);
	        return v.to_string();
	    }
	    Err(e) => {
	        panic!("error parsing string: {:?}", e);
    	}
	}

}

fn base64_ascii_to_index(ascii: u8) -> Option<u8> {
	match ascii {
		65...90 => Some(ascii - 65), // uppercase
		97...122 => Some(ascii - 71), // lowercase
		48...57 => Some(ascii + 4), // numbers
		43 => Some(ascii + 19), // +
		47 => Some(ascii + 16), // /
		// equals means empty byte. need some way of
		// differentiating from 'A'.
		61 => None, // =

		_ => panic!("{:?}: Not in base64 range", ascii)
	}
}

fn convert_num_to_hex_char(num: u8) -> u8 {
	match num {
		0...9 => num + 48,
		10...15 => num + 87,
		_ => panic!("Not a valid hex char")
	}
}

#[cfg(test)]
mod set1challenge7 {

	#[test]
	fn decrypt_aes_ecb_128() {
		let input = "o3VBEciqmzUQswmiEMLdfPuhlv1XK0i0ww26jHAiaeY=".to_string();
		let original = "This is encrypt.";
		let key = "TESTTESTTESTTEST";

		let output = super::decrypt_base64_aes_ecb_128(key, vec!(input));
		assert_eq!(output, original);
	}

}<|endoftext|>


<commit_before>pub extern crate dbus;

use std::result::Result;

use self::dbus::{Connection, BusType, Message, Path};
use self::dbus::arg::Variant;

use provider::service::inline::InlineProvider;
use provider::Output;
use provider::error::Error;

#[derive(Clone, Debug)]
pub struct Systemd;

impl InlineProvider for Systemd {
    fn is_running(&self, name: &str) -> Result<Output, Error> {
        let state = try!(self.get_active_state(name));
        Ok(Output::Bool(state == "active"))
    }

    fn is_enabled(&self, name: &str) -> Result<Output, Error> {
        let state = try!(self.get_unit_file_state(name));
        Ok(Output::Bool(state == "enabled"))
    }

    fn box_clone(&self) -> Box<InlineProvider> {
        Box::new((*self).clone())
    }
}

impl Systemd {
    fn get_active_state(&self, name: &str) -> Result<String, Error> {
        let c = try!(Connection::get_private(BusType::System));

        let object_path = try!(self.get_object_path(name));

        let m = try!(Message::new_method_call("org.freedesktop.systemd1",
                                              object_path,
                                              "org.freedesktop.DBus.Properties",
                                              "Get"))
            .append2("org.freedesktop.systemd1.Unit", "ActiveState");

        let r = try!(c.send_with_reply_and_block(m, 2000));
        let s: Variant<&str> = try!(r.read1());
        let active_state = s.0;

        Ok(active_state.to_string())
    }

    fn get_unit_file_state(&self, name: &str) -> Result<String, Error> {
        let c = try!(Connection::get_private(BusType::System));

        let object_path = try!(self.get_object_path(name));

        let m = try!(Message::new_method_call("org.freedesktop.systemd1",
                                              object_path,
                                              "org.freedesktop.DBus.Properties",
                                              "Get"))
            .append2("org.freedesktop.systemd1.Unit", "UnitFileState");

        let r = try!(c.send_with_reply_and_block(m, 2000));
        let s: Variant<&str> = try!(r.read1());
        let unit_file_state = s.0;
        println!("{}", unit_file_state);
        Ok(unit_file_state.to_string())
    }

    fn get_object_path(&self, name: &str) -> Result<Path, Error> {
        let c = try!(Connection::get_private(BusType::System));

        let service: String;
        if name.ends_with(".service") {
            service = name.to_string()
        } else {
            service = name.to_string() + ".service"
        }

        let m = try!(Message::new_method_call("org.freedesktop.systemd1",
                                              "/org/freedesktop/systemd1",
                                              "org.freedesktop.systemd1.Manager",
                                              "GetUnit"))
            .append1(service);

        let r = try!(c.send_with_reply_and_block(m, 2000));
        let object_path: Path = try!(r.read1());
        Ok(object_path)
    }
}
<commit_msg>Fix get_unit_file_state()<commit_after>pub extern crate dbus;

use std::result::Result;

use self::dbus::{Connection, BusType, Message, Path};
use self::dbus::arg::Variant;

use provider::service::inline::InlineProvider;
use provider::Output;
use provider::error::Error;

#[derive(Clone, Debug)]
pub struct Systemd;

impl InlineProvider for Systemd {
    fn is_running(&self, name: &str) -> Result<Output, Error> {
        let state = try!(self.get_active_state(name));
        Ok(Output::Bool(state == "active"))
    }

    fn is_enabled(&self, name: &str) -> Result<Output, Error> {
        let state = try!(self.get_unit_file_state(name));
        Ok(Output::Bool(state == "enabled"))
    }

    fn box_clone(&self) -> Box<InlineProvider> {
        Box::new((*self).clone())
    }
}

impl Systemd {
    fn get_active_state(&self, name: &str) -> Result<String, Error> {
        let c = try!(Connection::get_private(BusType::System));

        let object_path = try!(self.get_object_path(name));

        let m = try!(Message::new_method_call("org.freedesktop.systemd1",
                                              object_path,
                                              "org.freedesktop.DBus.Properties",
                                              "Get"))
            .append2("org.freedesktop.systemd1.Unit", "ActiveState");

        let r = try!(c.send_with_reply_and_block(m, 2000));
        let s: Variant<&str> = try!(r.read1());
        let active_state = s.0;

        Ok(active_state.to_string())
    }

    fn get_unit_file_state(&self, name: &str) -> Result<String, Error> {
        let c = try!(Connection::get_private(BusType::System));

        let service: String;
        if name.ends_with(".service") {
            service = name.to_string()
        } else {
            service = name.to_string() + ".service"
        }

        let m = try!(Message::new_method_call("org.freedesktop.systemd1",
                                              "/org/freedesktop/systemd1",
                                              "org.freedesktop.systemd1.Manager",
                                              "GetUnitFileState"))
            .append1(service);

        let r = try!(c.send_with_reply_and_block(m, 2000));
        let unit_file_state: &str = try!(r.read1());
        Ok(unit_file_state.to_string())
    }

    fn get_object_path(&self, name: &str) -> Result<Path, Error> {
        let c = try!(Connection::get_private(BusType::System));

        let service: String;
        if name.ends_with(".service") {
            service = name.to_string()
        } else {
            service = name.to_string() + ".service"
        }

        let m = try!(Message::new_method_call("org.freedesktop.systemd1",
                                              "/org/freedesktop/systemd1",
                                              "org.freedesktop.systemd1.Manager",
                                              "GetUnit"))
            .append1(service);

        let r = try!(c.send_with_reply_and_block(m, 2000));
        let object_path: Path = try!(r.read1());
        Ok(object_path)
    }
}
<|endoftext|>


<commit_before>use lazy_static::lazy_static;
use pulldown_cmark as cmark;
use regex::Regex;
use syntect::easy::HighlightLines;
use syntect::html::{
    start_highlighted_html_snippet, styled_line_to_highlighted_html, IncludeBackground,
};

use crate::context::RenderContext;
use crate::table_of_contents::{make_table_of_contents, Heading};
use config::highlighting::{get_highlighter, SYNTAX_SET, THEME_SET};
use errors::{Error, Result};
use front_matter::InsertAnchor;
use utils::site::resolve_internal_link;
use utils::slugs::maybe_slugify_anchors;
use utils::vec::InsertMany;

use self::cmark::{Event, LinkType, Options, Parser, Tag};

const CONTINUE_READING: &str =
    "

\n";
const ANCHOR_LINK_TEMPLATE: &str = "anchor-link.html";

#[derive(Debug)]
pub struct Rendered {
    pub body: String,
    pub summary_len: Option<usize>,
    pub toc: Vec<Heading>,
    pub internal_links_with_anchors: Vec<(String, String)>,
    pub external_links: Vec<String>,
}

// tracks a heading in a slice of pulldown-cmark events
#[derive(Debug)]
struct HeadingRef {
    start_idx: usize,
    end_idx: usize,
    level: u32,
    id: Option<String>,
}

impl HeadingRef {
    fn new(start: usize, level: u32) -> HeadingRef {
        HeadingRef { start_idx: start, end_idx: 0, level, id: None }
    }
}

// We might have cases where the slug is already present in our list of anchor
// for example an article could have several titles named Example
// We add a counter after the slug if the slug is already present, which
// means we will have example, example-1, example-2 etc
fn find_anchor(anchors: &[String], name: String, level: u8) -> String {
    if level == 0 && !anchors.contains(&name) {
        return name;
    }

    let new_anchor = format!("{}-{}", name, level + 1);
    if !anchors.contains(&new_anchor) {
        return new_anchor;
    }

    find_anchor(anchors, name, level + 1)
}

// Returns whether the given string starts with a schema.
//
// Although there exists [a list of registered URI schemes][uri-schemes], a link may use arbitrary,
// private schemes. This function checks if the given string starts with something that just looks
// like a scheme, i.e., a case-insensitive identifier followed by a colon.
//
// [uri-schemes]: https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml
fn starts_with_schema(s: &str) -> bool {
    lazy_static! {
        static ref PATTERN: Regex = Regex::new(r"^[0-9A-Za-z\-]+:").unwrap();
    }

    PATTERN.is_match(s)
}

// Colocated asset links refers to the files in the same directory,
// there it should be a filename only
fn is_colocated_asset_link(link: &str) -> bool {
    !link.contains('/')  // http://, ftp://, ../ etc
        && !starts_with_schema(link)
}

// Returns whether a link starts with an HTTP(s) scheme.
fn is_external_link(link: &str) -> bool {
    link.starts_with("http:") || link.starts_with("https:")
}

fn fix_link(
    link_type: LinkType,
    link: &str,
    context: &RenderContext,
    internal_links_with_anchors: &mut Vec<(String, String)>,
    external_links: &mut Vec<String>,
) -> Result<String> {
    if link_type == LinkType::Email {
        return Ok(link.to_string());
    }

    // TODO: remove me in a few versions when people have upgraded
    if link.starts_with("./") && link.contains(".md") {
        println!("It looks like the link `{}` is using the previous syntax for internal links: start with @/ instead", link);
    }

    // A few situations here:
    // - it could be a relative link (starting with `@/`)
    // - it could be a link to a co-located asset
    // - it could be a normal link
    let result = if link.starts_with("@/") {
        match resolve_internal_link(&link, context.permalinks) {
            Ok(resolved) => {
                if resolved.anchor.is_some() {
                    internal_links_with_anchors
                        .push((resolved.md_path.unwrap(), resolved.anchor.unwrap()));
                }
                resolved.permalink
            }
            Err(_) => {
                return Err(format!("Relative link {} not found.", link).into());
            }
        }
    } else if is_colocated_asset_link(&link) {
        format!("{}{}", context.current_page_permalink, link)
    } else {
        if is_external_link(link) {
            external_links.push(link.to_owned());
        }
        link.to_string()
    };
    Ok(result)
}

/// get only text in a slice of events
fn get_text(parser_slice: &[Event]) -> String {
    let mut title = String::new();

    for event in parser_slice.iter() {
        match event {
            Event::Text(text) | Event::Code(text) => title += text,
            _ => continue,
        }
    }

    title
}

fn get_heading_refs(events: &[Event]) -> Vec<HeadingRef> {
    let mut heading_refs = vec![];

    for (i, event) in events.iter().enumerate() {
        match event {
            Event::Start(Tag::Heading(level)) => {
                heading_refs.push(HeadingRef::new(i, *level));
            }
            Event::End(Tag::Heading(_)) => {
                let msg = "Heading end before start?";
                heading_refs.last_mut().expect(msg).end_idx = i;
            }
            _ => (),
        }
    }

    heading_refs
}

pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Rendered> {
    // the rendered html
    let mut html = String::with_capacity(content.len());
    // Set while parsing
    let mut error = None;

    let mut background = IncludeBackground::Yes;
    let mut highlighter: Option<(HighlightLines, bool)> = None;

    let mut inserted_anchors: Vec<String> = vec![];
    let mut headings: Vec<Heading> = vec![];
    let mut internal_links_with_anchors = Vec::new();
    let mut external_links = Vec::new();

    let mut opts = Options::empty();
    let mut has_summary = false;
    opts.insert(Options::ENABLE_TABLES);
    opts.insert(Options::ENABLE_FOOTNOTES);

    {
        let mut events = Parser::new_ext(content, opts)
            .map(|event| {
                match event {
                    Event::Text(text) => {
                        // if we are in the middle of a code block
                        if let Some((ref mut highlighter, in_extra)) = highlighter {
                            let highlighted = if in_extra {
                                if let Some(ref extra) = context.config.extra_syntax_set {
                                    highlighter.highlight(&text, &extra)
                                } else {
                                    unreachable!(
                                        "Got a highlighter from extra syntaxes but no extra?"
                                    );
                                }
                            } else {
                                highlighter.highlight(&text, &SYNTAX_SET)
                            };
                            //let highlighted = &highlighter.highlight(&text, ss);
                            let html = styled_line_to_highlighted_html(&highlighted, background);
                            return Event::Html(html.into());
                        }

                        // Business as usual
                        Event::Text(text)
                    }
                    Event::Start(Tag::CodeBlock(ref info)) => {
                        if !context.config.highlight_code {
                            return Event::Html("
".into());
                        }

                        let theme = &THEME_SET.themes[&context.config.highlight_theme];
                        highlighter = Some(get_highlighter(info, &context.config));
                        // This selects the background color the same way that start_coloured_html_snippet does
                        let color = theme
                            .settings
                            .background
                            .unwrap_or(::syntect::highlighting::Color::WHITE);
                        background = IncludeBackground::IfDifferent(color);
                        let snippet = start_highlighted_html_snippet(theme);
                        Event::Html(snippet.0.into())
                    }
                    Event::End(Tag::CodeBlock(_)) => {
                        if !context.config.highlight_code {
                            return Event::Html("
\n".into());
                        }
                        // reset highlight and close the code block
                        highlighter = None;
                        Event::Html("
".into())
                    }
                    Event::Start(Tag::Image(link_type, src, title)) => {
                        if is_colocated_asset_link(&src) {
                            let link = format!("{}{}", context.current_page_permalink, &*src);
                            return Event::Start(Tag::Image(link_type, link.into(), title));
                        }

                        Event::Start(Tag::Image(link_type, src, title))
                    }
                    Event::Start(Tag::Link(link_type, link, title)) if link.is_empty() => {
                        error = Some(Error::msg("There is a link that is missing a URL"));
                        Event::Start(Tag::Link(link_type, "#".into(), title))
                    }
                    Event::Start(Tag::Link(link_type, link, title)) => {
                        let fixed_link = match fix_link(
                            link_type,
                            &link,
                            context,
                            &mut internal_links_with_anchors,
                            &mut external_links,
                        ) {
                            Ok(fixed_link) => fixed_link,
                            Err(err) => {
                                error = Some(err);
                                return Event::Html("".into());
                            }
                        };

                        Event::Start(Tag::Link(link_type, fixed_link.into(), title))
                    }
                    Event::Html(ref markup) if markup.contains("") => {
                        has_summary = true;
                        Event::Html(CONTINUE_READING.into())
                    }
                    _ => event,
                }
            })
            .collect::<Vec<_>>(); // We need to collect the events to make a second pass

        let mut heading_refs = get_heading_refs(&events);

        let mut anchors_to_insert = vec![];

        // First heading pass: look for a manually-specified IDs, e.g. `# Heading text {#hash}`
        // (This is a separate first pass so that auto IDs can avoid collisions with manual IDs.)
        for heading_ref in heading_refs.iter_mut() {
            let end_idx = heading_ref.end_idx;
            if let Event::Text(ref mut text) = events[end_idx - 1] {
                if text.as_bytes().last() == Some(&b'}') {
                    if let Some(mut i) = text.find("{#") {
                        let id = text[i + 2..text.len() - 1].to_owned();
                        inserted_anchors.push(id.clone());
                        while i > 0 && text.as_bytes()[i - 1] == b' ' {
                            i -= 1;
                        }
                        heading_ref.id = Some(id);
                        *text = text[..i].to_owned().into();
                    }
                }
            }
        }

        // Second heading pass: auto-generate remaining IDs, and emit HTML
        for heading_ref in heading_refs {
            let start_idx = heading_ref.start_idx;
            let end_idx = heading_ref.end_idx;
            let title = get_text(&events[start_idx + 1..end_idx]);
            let id = heading_ref.id.unwrap_or_else(|| {
                find_anchor(
                    &inserted_anchors,
                    maybe_slugify_anchors(&title, context.config.slugify_paths),
                    0,
                )
            });
            inserted_anchors.push(id.clone());

            // insert `id` to the tag
            let html = format!("<h{lvl} id=\"{id}\">", lvl = heading_ref.level, id = id);
            events[start_idx] = Event::Html(html.into());

            // generate anchors and places to insert them
            if context.insert_anchor != InsertAnchor::None {
                let anchor_idx = match context.insert_anchor {
                    InsertAnchor::Left => start_idx + 1,
                    InsertAnchor::Right => end_idx,
                    InsertAnchor::None => 0, // Not important
                };
                let mut c = tera::Context::new();
                c.insert("id", &id);

                let anchor_link = utils::templates::render_template(
                    &ANCHOR_LINK_TEMPLATE,
                    context.tera,
                    c,
                    &None,
                )
                .map_err(|e| Error::chain("Failed to render anchor link template", e))?;
                anchors_to_insert.push((anchor_idx, Event::Html(anchor_link.into())));
            }

            // record heading to make table of contents
            let permalink = format!("{}#{}", context.current_page_permalink, id);
            let h =
                Heading { level: heading_ref.level, id, permalink, title, children: Vec::new() };
            headings.push(h);
        }

        if context.insert_anchor != InsertAnchor::None {
            events.insert_many(anchors_to_insert);
        }

        cmark::html::push_html(&mut html, events.into_iter());
    }

    if let Some(e) = error {
        Err(e)
    } else {
        Ok(Rendered {
            summary_len: if has_summary { html.find(CONTINUE_READING) } else { None },
            body: html,
            toc: make_table_of_contents(headings),
            internal_links_with_anchors,
            external_links,
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_starts_with_schema() {
        // registered
        assert!(starts_with_schema("https://example.com/"));
        assert!(starts_with_schema("ftp://example.com/"));
        assert!(starts_with_schema("mailto:user@example.com"));
        assert!(starts_with_schema("xmpp:node@example.com"));
        assert!(starts_with_schema("tel:18008675309"));
        assert!(starts_with_schema("sms:18008675309"));
        assert!(starts_with_schema("h323:user@example.com"));

        // arbitrary
        assert!(starts_with_schema("zola:post?content=hi"));

        // case-insensitive
        assert!(starts_with_schema("MailTo:user@example.com"));
        assert!(starts_with_schema("MAILTO:user@example.com"));
    }

    #[test]
    fn test_is_external_link() {
        assert!(is_external_link("http://example.com/"));
        assert!(is_external_link("https://example.com/"));
        assert!(is_external_link("https://example.com/index.html#introduction"));

        assert!(!is_external_link("mailto:user@example.com"));
        assert!(!is_external_link("tel:18008675309"));

        assert!(!is_external_link("#introduction"));

        assert!(!is_external_link("http.jpg"))
    }
}
<commit_msg>Add Markdown strikethrough rendering (#924)<commit_after>use lazy_static::lazy_static;
use pulldown_cmark as cmark;
use regex::Regex;
use syntect::easy::HighlightLines;
use syntect::html::{
    start_highlighted_html_snippet, styled_line_to_highlighted_html, IncludeBackground,
};

use crate::context::RenderContext;
use crate::table_of_contents::{make_table_of_contents, Heading};
use config::highlighting::{get_highlighter, SYNTAX_SET, THEME_SET};
use errors::{Error, Result};
use front_matter::InsertAnchor;
use utils::site::resolve_internal_link;
use utils::slugs::maybe_slugify_anchors;
use utils::vec::InsertMany;

use self::cmark::{Event, LinkType, Options, Parser, Tag};

const CONTINUE_READING: &str =
    "

\n";
const ANCHOR_LINK_TEMPLATE: &str = "anchor-link.html";

#[derive(Debug)]
pub struct Rendered {
    pub body: String,
    pub summary_len: Option<usize>,
    pub toc: Vec<Heading>,
    pub internal_links_with_anchors: Vec<(String, String)>,
    pub external_links: Vec<String>,
}

// tracks a heading in a slice of pulldown-cmark events
#[derive(Debug)]
struct HeadingRef {
    start_idx: usize,
    end_idx: usize,
    level: u32,
    id: Option<String>,
}

impl HeadingRef {
    fn new(start: usize, level: u32) -> HeadingRef {
        HeadingRef { start_idx: start, end_idx: 0, level, id: None }
    }
}

// We might have cases where the slug is already present in our list of anchor
// for example an article could have several titles named Example
// We add a counter after the slug if the slug is already present, which
// means we will have example, example-1, example-2 etc
fn find_anchor(anchors: &[String], name: String, level: u8) -> String {
    if level == 0 && !anchors.contains(&name) {
        return name;
    }

    let new_anchor = format!("{}-{}", name, level + 1);
    if !anchors.contains(&new_anchor) {
        return new_anchor;
    }

    find_anchor(anchors, name, level + 1)
}

// Returns whether the given string starts with a schema.
//
// Although there exists [a list of registered URI schemes][uri-schemes], a link may use arbitrary,
// private schemes. This function checks if the given string starts with something that just looks
// like a scheme, i.e., a case-insensitive identifier followed by a colon.
//
// [uri-schemes]: https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml
fn starts_with_schema(s: &str) -> bool {
    lazy_static! {
        static ref PATTERN: Regex = Regex::new(r"^[0-9A-Za-z\-]+:").unwrap();
    }

    PATTERN.is_match(s)
}

// Colocated asset links refers to the files in the same directory,
// there it should be a filename only
fn is_colocated_asset_link(link: &str) -> bool {
    !link.contains('/')  // http://, ftp://, ../ etc
        && !starts_with_schema(link)
}

// Returns whether a link starts with an HTTP(s) scheme.
fn is_external_link(link: &str) -> bool {
    link.starts_with("http:") || link.starts_with("https:")
}

fn fix_link(
    link_type: LinkType,
    link: &str,
    context: &RenderContext,
    internal_links_with_anchors: &mut Vec<(String, String)>,
    external_links: &mut Vec<String>,
) -> Result<String> {
    if link_type == LinkType::Email {
        return Ok(link.to_string());
    }

    // TODO: remove me in a few versions when people have upgraded
    if link.starts_with("./") && link.contains(".md") {
        println!("It looks like the link `{}` is using the previous syntax for internal links: start with @/ instead", link);
    }

    // A few situations here:
    // - it could be a relative link (starting with `@/`)
    // - it could be a link to a co-located asset
    // - it could be a normal link
    let result = if link.starts_with("@/") {
        match resolve_internal_link(&link, context.permalinks) {
            Ok(resolved) => {
                if resolved.anchor.is_some() {
                    internal_links_with_anchors
                        .push((resolved.md_path.unwrap(), resolved.anchor.unwrap()));
                }
                resolved.permalink
            }
            Err(_) => {
                return Err(format!("Relative link {} not found.", link).into());
            }
        }
    } else if is_colocated_asset_link(&link) {
        format!("{}{}", context.current_page_permalink, link)
    } else {
        if is_external_link(link) {
            external_links.push(link.to_owned());
        }
        link.to_string()
    };
    Ok(result)
}

/// get only text in a slice of events
fn get_text(parser_slice: &[Event]) -> String {
    let mut title = String::new();

    for event in parser_slice.iter() {
        match event {
            Event::Text(text) | Event::Code(text) => title += text,
            _ => continue,
        }
    }

    title
}

fn get_heading_refs(events: &[Event]) -> Vec<HeadingRef> {
    let mut heading_refs = vec![];

    for (i, event) in events.iter().enumerate() {
        match event {
            Event::Start(Tag::Heading(level)) => {
                heading_refs.push(HeadingRef::new(i, *level));
            }
            Event::End(Tag::Heading(_)) => {
                let msg = "Heading end before start?";
                heading_refs.last_mut().expect(msg).end_idx = i;
            }
            _ => (),
        }
    }

    heading_refs
}

pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Rendered> {
    // the rendered html
    let mut html = String::with_capacity(content.len());
    // Set while parsing
    let mut error = None;

    let mut background = IncludeBackground::Yes;
    let mut highlighter: Option<(HighlightLines, bool)> = None;

    let mut inserted_anchors: Vec<String> = vec![];
    let mut headings: Vec<Heading> = vec![];
    let mut internal_links_with_anchors = Vec::new();
    let mut external_links = Vec::new();

    let mut opts = Options::empty();
    let mut has_summary = false;
    opts.insert(Options::ENABLE_TABLES);
    opts.insert(Options::ENABLE_FOOTNOTES);
    opts.insert(Options::ENABLE_STRIKETHROUGH);

    {
        let mut events = Parser::new_ext(content, opts)
            .map(|event| {
                match event {
                    Event::Text(text) => {
                        // if we are in the middle of a code block
                        if let Some((ref mut highlighter, in_extra)) = highlighter {
                            let highlighted = if in_extra {
                                if let Some(ref extra) = context.config.extra_syntax_set {
                                    highlighter.highlight(&text, &extra)
                                } else {
                                    unreachable!(
                                        "Got a highlighter from extra syntaxes but no extra?"
                                    );
                                }
                            } else {
                                highlighter.highlight(&text, &SYNTAX_SET)
                            };
                            //let highlighted = &highlighter.highlight(&text, ss);
                            let html = styled_line_to_highlighted_html(&highlighted, background);
                            return Event::Html(html.into());
                        }

                        // Business as usual
                        Event::Text(text)
                    }
                    Event::Start(Tag::CodeBlock(ref info)) => {
                        if !context.config.highlight_code {
                            return Event::Html("
".into());
                        }

                        let theme = &THEME_SET.themes[&context.config.highlight_theme];
                        highlighter = Some(get_highlighter(info, &context.config));
                        // This selects the background color the same way that start_coloured_html_snippet does
                        let color = theme
                            .settings
                            .background
                            .unwrap_or(::syntect::highlighting::Color::WHITE);
                        background = IncludeBackground::IfDifferent(color);
                        let snippet = start_highlighted_html_snippet(theme);
                        Event::Html(snippet.0.into())
                    }
                    Event::End(Tag::CodeBlock(_)) => {
                        if !context.config.highlight_code {
                            return Event::Html("
\n".into());
                        }
                        // reset highlight and close the code block
                        highlighter = None;
                        Event::Html("
".into())
                    }
                    Event::Start(Tag::Image(link_type, src, title)) => {
                        if is_colocated_asset_link(&src) {
                            let link = format!("{}{}", context.current_page_permalink, &*src);
                            return Event::Start(Tag::Image(link_type, link.into(), title));
                        }

                        Event::Start(Tag::Image(link_type, src, title))
                    }
                    Event::Start(Tag::Link(link_type, link, title)) if link.is_empty() => {
                        error = Some(Error::msg("There is a link that is missing a URL"));
                        Event::Start(Tag::Link(link_type, "#".into(), title))
                    }
                    Event::Start(Tag::Link(link_type, link, title)) => {
                        let fixed_link = match fix_link(
                            link_type,
                            &link,
                            context,
                            &mut internal_links_with_anchors,
                            &mut external_links,
                        ) {
                            Ok(fixed_link) => fixed_link,
                            Err(err) => {
                                error = Some(err);
                                return Event::Html("".into());
                            }
                        };

                        Event::Start(Tag::Link(link_type, fixed_link.into(), title))
                    }
                    Event::Html(ref markup) if markup.contains("") => {
                        has_summary = true;
                        Event::Html(CONTINUE_READING.into())
                    }
                    _ => event,
                }
            })
            .collect::<Vec<_>>(); // We need to collect the events to make a second pass

        let mut heading_refs = get_heading_refs(&events);

        let mut anchors_to_insert = vec![];

        // First heading pass: look for a manually-specified IDs, e.g. `# Heading text {#hash}`
        // (This is a separate first pass so that auto IDs can avoid collisions with manual IDs.)
        for heading_ref in heading_refs.iter_mut() {
            let end_idx = heading_ref.end_idx;
            if let Event::Text(ref mut text) = events[end_idx - 1] {
                if text.as_bytes().last() == Some(&b'}') {
                    if let Some(mut i) = text.find("{#") {
                        let id = text[i + 2..text.len() - 1].to_owned();
                        inserted_anchors.push(id.clone());
                        while i > 0 && text.as_bytes()[i - 1] == b' ' {
                            i -= 1;
                        }
                        heading_ref.id = Some(id);
                        *text = text[..i].to_owned().into();
                    }
                }
            }
        }

        // Second heading pass: auto-generate remaining IDs, and emit HTML
        for heading_ref in heading_refs {
            let start_idx = heading_ref.start_idx;
            let end_idx = heading_ref.end_idx;
            let title = get_text(&events[start_idx + 1..end_idx]);
            let id = heading_ref.id.unwrap_or_else(|| {
                find_anchor(
                    &inserted_anchors,
                    maybe_slugify_anchors(&title, context.config.slugify_paths),
                    0,
                )
            });
            inserted_anchors.push(id.clone());

            // insert `id` to the tag
            let html = format!("<h{lvl} id=\"{id}\">", lvl = heading_ref.level, id = id);
            events[start_idx] = Event::Html(html.into());

            // generate anchors and places to insert them
            if context.insert_anchor != InsertAnchor::None {
                let anchor_idx = match context.insert_anchor {
                    InsertAnchor::Left => start_idx + 1,
                    InsertAnchor::Right => end_idx,
                    InsertAnchor::None => 0, // Not important
                };
                let mut c = tera::Context::new();
                c.insert("id", &id);

                let anchor_link = utils::templates::render_template(
                    &ANCHOR_LINK_TEMPLATE,
                    context.tera,
                    c,
                    &None,
                )
                .map_err(|e| Error::chain("Failed to render anchor link template", e))?;
                anchors_to_insert.push((anchor_idx, Event::Html(anchor_link.into())));
            }

            // record heading to make table of contents
            let permalink = format!("{}#{}", context.current_page_permalink, id);
            let h =
                Heading { level: heading_ref.level, id, permalink, title, children: Vec::new() };
            headings.push(h);
        }

        if context.insert_anchor != InsertAnchor::None {
            events.insert_many(anchors_to_insert);
        }

        cmark::html::push_html(&mut html, events.into_iter());
    }

    if let Some(e) = error {
        Err(e)
    } else {
        Ok(Rendered {
            summary_len: if has_summary { html.find(CONTINUE_READING) } else { None },
            body: html,
            toc: make_table_of_contents(headings),
            internal_links_with_anchors,
            external_links,
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_starts_with_schema() {
        // registered
        assert!(starts_with_schema("https://example.com/"));
        assert!(starts_with_schema("ftp://example.com/"));
        assert!(starts_with_schema("mailto:user@example.com"));
        assert!(starts_with_schema("xmpp:node@example.com"));
        assert!(starts_with_schema("tel:18008675309"));
        assert!(starts_with_schema("sms:18008675309"));
        assert!(starts_with_schema("h323:user@example.com"));

        // arbitrary
        assert!(starts_with_schema("zola:post?content=hi"));

        // case-insensitive
        assert!(starts_with_schema("MailTo:user@example.com"));
        assert!(starts_with_schema("MAILTO:user@example.com"));
    }

    #[test]
    fn test_is_external_link() {
        assert!(is_external_link("http://example.com/"));
        assert!(is_external_link("https://example.com/"));
        assert!(is_external_link("https://example.com/index.html#introduction"));

        assert!(!is_external_link("mailto:user@example.com"));
        assert!(!is_external_link("tel:18008675309"));

        assert!(!is_external_link("#introduction"));

        assert!(!is_external_link("http.jpg"))
    }
}
<|endoftext|>


<commit_before>extern crate libc;

use libc::{c_int, c_uchar};

pub mod ffi {
    use libc::{c_int, c_uchar, c_float, c_void};
    use std::ptr;

    pub struct FontInfo {
       userdata: *c_void,

       // pointer to .ttf file
       data: *c_uchar,

       // offset of start of font
       fontstart: c_int,

       // number of glyphs, needed for range checking
       numGlyphs: c_int,

       // table locations as offset from start of .ttf
       loca: c_int,
       head: c_int,
       glyf: c_int,
       hhea: c_int,
       hmtx: c_int,
       kern: c_int,

       // a cmap mapping for our chosen character encoding
       index_map: c_int,

       // format needed to map from glyph index to glyph
       indexToLocFormat: c_int,
    }

    impl FontInfo {
        pub fn new() -> FontInfo {
            FontInfo {
                userdata: ptr::null(),
                data: ptr::null(),
                fontstart: 0,
                numGlyphs: 0,
                loca: 0,
                head: 0,
                glyf: 0,
                hhea: 0,
                hmtx: 0,
                kern: 0,
                index_map: 0,
                indexToLocFormat: 0,
            }
        }
    }

    #[link(name = "stb_truetype")]
    extern {
        pub fn stbtt_GetFontOffsetForIndex(
            data: *c_uchar,
            index: c_int,
        ) -> c_int;

        pub fn stbtt_InitFont(
            info: *mut FontInfo,
            data2: *c_uchar,
            fontstart: c_int,
        );

        pub fn stbtt_GetFontVMetrics(
            info: *FontInfo,
            ascent: *mut c_int,
            descent: *mut c_int,
            lineGap: *mut c_int,
        );

        pub fn stbtt_FindGlyphIndex(
            info: *FontInfo,
            unicode_codepoint: c_int,
        ) -> c_int;

        pub fn stbtt_GetGlyphHMetrics(
            info: *FontInfo,
            glyph_index: c_int,
            advanceWidth: *mut c_int,
            eftSideBearing: *mut c_int,
        );

        pub fn stbtt_GetGlyphBitmapBox(
            font: *FontInfo,
            glyph: c_int,
            scale_x: c_float,
            scale_y: c_float,
            ix0: *mut c_int,
            iy0: *mut c_int,
            ix1: *mut c_int,
            iy1: *mut c_int,
        );

        pub fn stbtt_GetGlyphKernAdvance(
            info: *FontInfo,
            glyph1: c_int,
            glyph2: c_int,
        );

        pub fn stbtt_MakeGlyphBitmap(
            info: *FontInfo,
            output: *c_uchar,
            out_w: c_int,
            out_h: c_int,
            out_stride: c_int,
            scale_x: c_float,
            scale_y: c_float,
            glyph: c_int,
        );

        pub fn stbtt_ScaleForPixelHeight(
            info: *FontInfo,
            height: c_float,
        ) -> c_float;

        pub fn stbtt_GetGlyphBitmap(
            info: *FontInfo,
            scale_x: c_float,
            scale_y: c_float,
            glyph: c_int,
            width: *mut c_int,
            height: *mut c_int,
            xoff: *mut c_int,
            yoff: *mut c_int,
        ) -> *c_uchar;
    }
}

pub struct Font {
    font_info: ffi::FontInfo,
    data: Vec<c_uchar>,
    height: f32,
    scale: f32,
    ascent: i32,
    descent: i32,
    line_gap: i32,
}

impl Font {
    pub fn new(font_path: &Path, height: f32) -> Font {
        let data = {
            use std::io::{BufferedReader, File};
            if !font_path.exists() {
                fail!("Wrong font path: {}", font_path.display());
            }
            let file = File::open(font_path);
            let mut reader = BufferedReader::new(file);
            reader.read_to_end().unwrap()
        };
        let mut font_info = ffi::FontInfo::new();
        unsafe {
            let font_offset = ffi::stbtt_GetFontOffsetForIndex(data.get(0) as *u8, 0);
            ffi::stbtt_InitFont(&mut font_info, data.get(0) as *u8, font_offset);
        }
        let scale = unsafe {
            ffi::stbtt_ScaleForPixelHeight(&font_info, height)
        };
        let mut c_ascent: c_int = 0;
        let mut c_descent: c_int = 0;
        let mut c_line_gap: c_int = 0;
        unsafe {
            ffi::stbtt_GetFontVMetrics(&font_info,
                &mut c_ascent, &mut c_descent, &mut c_line_gap);
        }
        let ascent = (c_ascent as f32 * scale) as i32;
        let descent = (c_descent as f32 * scale) as i32;
        let line_gap = (c_line_gap as f32 * scale) as i32;
        Font {
            font_info: font_info,
            data: data,
            height: height,
            scale: scale,
            ascent: ascent,
            descent: descent,
            line_gap: line_gap,
        }
    }

    pub fn get_glyph(&self, glyph_index: i32) -> (Vec<c_uchar>, i32, i32, i32, i32) {
        use std::vec::raw::from_buf;
        let mut w = 0;
        let mut h = 0;
        let mut xoff = 0;
        let mut yoff = 0;
        let bitmap = unsafe {
            let scale_x = 0.0;
            let scale_y = self.scale;
            let buf = ffi::stbtt_GetGlyphBitmap(
                &self.font_info,
                scale_x,
                scale_y,
                glyph_index as c_int,
                &mut w,
                &mut h,
                &mut xoff,
                &mut yoff,
            );
            from_buf(buf, (w * h) as uint)
        };
        (bitmap, w as i32, h as i32, xoff as i32, yoff as i32)
    }

    pub fn find_glyph_index(&self, c: char) -> i32 {
        unsafe {
            ffi::stbtt_FindGlyphIndex(&self.font_info, c as c_int) as i32
        }
    }

    pub fn get_glyph_bitmap_box(&self, glyph_index: i32) -> (i32, i32, i32, i32) {
        let scale_x = 0.0;
        let scale_y = self.scale;
        let mut ix0 = 0;
        let mut iy0 = 0;
        let mut ix1 = 0;
        let mut iy1 = 0;
        unsafe {
            ffi::stbtt_GetGlyphBitmapBox(
                &self.font_info,
                glyph_index as c_int,
                scale_x,
                scale_y,
                &mut ix0,
                &mut iy0,
                &mut ix1,
                &mut iy1,
            );
        }
        (ix0 as i32, iy0 as i32, ix1 as i32, iy1 as i32)
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<commit_msg>Updated for Rust 0dfc90a: *const, *mut<commit_after>extern crate libc;

use libc::{c_int, c_uchar};

pub mod ffi {
    use libc::{c_int, c_uchar, c_float, c_void};
    use std::ptr;

    pub struct FontInfo {
       userdata: *const c_void,

       // pointer to .ttf file
       data: *const c_uchar,

       // offset of start of font
       fontstart: c_int,

       // number of glyphs, needed for range checking
       numGlyphs: c_int,

       // table locations as offset from start of .ttf
       loca: c_int,
       head: c_int,
       glyf: c_int,
       hhea: c_int,
       hmtx: c_int,
       kern: c_int,

       // a cmap mapping for our chosen character encoding
       index_map: c_int,

       // format needed to map from glyph index to glyph
       indexToLocFormat: c_int,
    }

    impl FontInfo {
        pub fn new() -> FontInfo {
            FontInfo {
                userdata: ptr::null(),
                data: ptr::null(),
                fontstart: 0,
                numGlyphs: 0,
                loca: 0,
                head: 0,
                glyf: 0,
                hhea: 0,
                hmtx: 0,
                kern: 0,
                index_map: 0,
                indexToLocFormat: 0,
            }
        }
    }

    #[link(name = "stb_truetype")]
    extern {
        pub fn stbtt_GetFontOffsetForIndex(
            data: *const c_uchar,
            index: c_int,
        ) -> c_int;

        pub fn stbtt_InitFont(
            info: *mut FontInfo,
            data2: *const c_uchar,
            fontstart: c_int,
        );

        pub fn stbtt_GetFontVMetrics(
            info: *const FontInfo,
            ascent: *mut c_int,
            descent: *mut c_int,
            lineGap: *mut c_int,
        );

        pub fn stbtt_FindGlyphIndex(
            info: *const FontInfo,
            unicode_codepoint: c_int,
        ) -> c_int;

        pub fn stbtt_GetGlyphHMetrics(
            info: *const FontInfo,
            glyph_index: c_int,
            advanceWidth: *mut c_int,
            eftSideBearing: *mut c_int,
        );

        pub fn stbtt_GetGlyphBitmapBox(
            font: *const FontInfo,
            glyph: c_int,
            scale_x: c_float,
            scale_y: c_float,
            ix0: *mut c_int,
            iy0: *mut c_int,
            ix1: *mut c_int,
            iy1: *mut c_int,
        );

        pub fn stbtt_GetGlyphKernAdvance(
            info: *const FontInfo,
            glyph1: c_int,
            glyph2: c_int,
        );

        pub fn stbtt_MakeGlyphBitmap(
            info: *const FontInfo,
            output: *const c_uchar,
            out_w: c_int,
            out_h: c_int,
            out_stride: c_int,
            scale_x: c_float,
            scale_y: c_float,
            glyph: c_int,
        );

        pub fn stbtt_ScaleForPixelHeight(
            info: *const FontInfo,
            height: c_float,
        ) -> c_float;

        pub fn stbtt_GetGlyphBitmap(
            info: *const FontInfo,
            scale_x: c_float,
            scale_y: c_float,
            glyph: c_int,
            width: *mut c_int,
            height: *mut c_int,
            xoff: *mut c_int,
            yoff: *mut c_int,
        ) -> *const c_uchar;
    }
}

pub struct Font {
    font_info: ffi::FontInfo,
    data: Vec<c_uchar>,
    height: f32,
    scale: f32,
    ascent: i32,
    descent: i32,
    line_gap: i32,
}

impl Font {
    pub fn new(font_path: &Path, height: f32) -> Font {
        let data = {
            use std::io::{BufferedReader, File};
            if !font_path.exists() {
                fail!("Wrong font path: {}", font_path.display());
            }
            let file = File::open(font_path);
            let mut reader = BufferedReader::new(file);
            reader.read_to_end().unwrap()
        };
        let mut font_info = ffi::FontInfo::new();
        unsafe {
            let font_offset = ffi::stbtt_GetFontOffsetForIndex(data.get(0) as *const u8, 0);
            ffi::stbtt_InitFont(&mut font_info, data.get(0) as *const u8, font_offset);
        }
        let scale = unsafe {
            ffi::stbtt_ScaleForPixelHeight(&font_info, height)
        };
        let mut c_ascent: c_int = 0;
        let mut c_descent: c_int = 0;
        let mut c_line_gap: c_int = 0;
        unsafe {
            ffi::stbtt_GetFontVMetrics(&font_info,
                &mut c_ascent, &mut c_descent, &mut c_line_gap);
        }
        let ascent = (c_ascent as f32 * scale) as i32;
        let descent = (c_descent as f32 * scale) as i32;
        let line_gap = (c_line_gap as f32 * scale) as i32;
        Font {
            font_info: font_info,
            data: data,
            height: height,
            scale: scale,
            ascent: ascent,
            descent: descent,
            line_gap: line_gap,
        }
    }

    pub fn get_glyph(&self, glyph_index: i32) -> (Vec<c_uchar>, i32, i32, i32, i32) {
        use std::vec::raw::from_buf;
        let mut w = 0;
        let mut h = 0;
        let mut xoff = 0;
        let mut yoff = 0;
        let bitmap = unsafe {
            let scale_x = 0.0;
            let scale_y = self.scale;
            let buf = ffi::stbtt_GetGlyphBitmap(
                &self.font_info,
                scale_x,
                scale_y,
                glyph_index as c_int,
                &mut w,
                &mut h,
                &mut xoff,
                &mut yoff,
            );
            from_buf(buf, (w * h) as uint)
        };
        (bitmap, w as i32, h as i32, xoff as i32, yoff as i32)
    }

    pub fn find_glyph_index(&self, c: char) -> i32 {
        unsafe {
            ffi::stbtt_FindGlyphIndex(&self.font_info, c as c_int) as i32
        }
    }

    pub fn get_glyph_bitmap_box(&self, glyph_index: i32) -> (i32, i32, i32, i32) {
        let scale_x = 0.0;
        let scale_y = self.scale;
        let mut ix0 = 0;
        let mut iy0 = 0;
        let mut ix1 = 0;
        let mut iy1 = 0;
        unsafe {
            ffi::stbtt_GetGlyphBitmapBox(
                &self.font_info,
                glyph_index as c_int,
                scale_x,
                scale_y,
                &mut ix0,
                &mut iy0,
                &mut ix1,
                &mut iy1,
            );
        }
        (ix0 as i32, iy0 as i32, ix1 as i32, iy1 as i32)
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<|endoftext|>


<commit_before>use std::io::{Reader, Writer, IoErrorKind, InvalidInput};
use std::string::{String};

/// The maximum line size as specified by RFC 5321.
pub static MAX_LINE_SIZE: uint = 512;

#[test]
fn test_static_vars() {
    assert_eq!(512, MAX_LINE_SIZE);
}

/// A stream specially made for reading SMTP commands.
///
/// It reads lines of input delimited by the <CRLF> sequence and with a maximum
/// size of 512 bytes, including the command word and the <CRLF> sequence. If
/// the input is not UTF8, non-UTF8 characters are replaced with `U+FFFD
/// REPLACEMENT CHARACTER` but no error is returned.
///
/// Returns `InvalidInput` if no line is found within 512 bytes of input.
///
/// # Example
/// ```no_run
/// use std::io::TcpStream;
/// use rsmtp::stream::SmtpStream;
///
/// let mut smtp = SmtpStream::new(TcpStream::connect("127.0.0.1", 2525).unwrap());
///
/// println!("{}", smtp.read_line().unwrap());
/// ```
pub struct SmtpStream<S> {
    stream: S,
    vec: Vec<u8>
}

impl<S> SmtpStream<S> {
    /// Create a new `SmtpStream` from another stream.
    pub fn new(inner: S) -> SmtpStream<S> {
        SmtpStream {
            stream: inner,
            vec: Vec::with_capacity(MAX_LINE_SIZE)
        }
    }

    /// Read the data section of an email. Ends with "<CRLF>.<CRLF>".
    pub fn read_data(&mut self) -> Result<String, IoErrorKind> {
        println!("At the moment, the DATA command is fake. Wanna help us out?");
        println!("https://github.com/conradkleinespel/rustastic-smtp");
        Ok("Hello world!".into_string())
    }
}

impl<R: Reader> SmtpStream<R> {
    /// Read one line of input.
    pub fn read_line(&mut self) -> Result<String, IoErrorKind> {
        self.vec.clear();
        loop {
            // If we have previously read 512 bytes and have not found a line,
            // stop here.
            if self.vec.len() == 512 {
                return Err(InvalidInput)
            }

            // Try to read one more byte and see if a line is formed.
            let byte_res = self.stream.read_byte();
            match byte_res {
                Ok(b) => {
                    self.vec.push(b);
                    // A line ends with \r\n (or 13/10 in decimal), so we
                    // check for these bytes at the end of our buffer.
                    let len = self.vec.len();
                    if len >= 2 {
                        if self.vec[len - 2] == 13 && self.vec[len - 1] == 10 {
                            return Ok(String::from_utf8_lossy(
                                self.vec.slice_to(len - 2)
                            ).into_string());
                        }
                    }
                },
                Err(e) => return Err(e.kind)
            }
        }
    }

}

impl<W: Writer> SmtpStream<W> {
    /// Write a line ended with CRLF.
    pub fn write_line(&mut self, s: &str) -> Result<(), ()> {
        match self.stream.write_str(format!("{}\r\n", s).as_slice()) {
            Ok(_) => Ok(()),
            Err(_) => Err(())
        }
    }
}

#[test]
fn test_reader() {
    let mut path: Path;
    let mut file: super::std::io::fs::File;
    let mut stream: SmtpStream<super::std::io::fs::File>;
    let mut expected: String;

    path = Path::new("tests/stream/0line1");
    file = super::std::io::fs::File::open(&path).unwrap();
    stream = SmtpStream::new(file);
    assert!(!stream.read_line().is_ok());

    path = Path::new("tests/stream/0line2");
    file = super::std::io::fs::File::open(&path).unwrap();
    stream = SmtpStream::new(file);
    assert!(!stream.read_line().is_ok());

    path = Path::new("tests/stream/0line3");
    file = super::std::io::fs::File::open(&path).unwrap();
    stream = SmtpStream::new(file);
    assert!(!stream.read_line().is_ok());

    path = Path::new("tests/stream/1line1");
    file = super::std::io::fs::File::open(&path).unwrap();
    stream = SmtpStream::new(file);
    assert_eq!(stream.read_line().unwrap().as_slice(), "hello world!");
    assert!(!stream.read_line().is_ok());

    path = Path::new("tests/stream/1line2");
    file = super::std::io::fs::File::open(&path).unwrap();
    stream = SmtpStream::new(file);
    assert_eq!(stream.read_line().unwrap().as_slice(), "hello world!");
    assert!(!stream.read_line().is_ok());

    path = Path::new("tests/stream/2lines1");
    file = super::std::io::fs::File::open(&path).unwrap();
    stream = SmtpStream::new(file);
    assert_eq!(stream.read_line().unwrap().as_slice(), "hello world!");
    assert_eq!(stream.read_line().unwrap().as_slice(), "bye bye world!");
    assert!(!stream.read_line().is_ok());

    expected = String::from_char(62, 'x');
    path = Path::new("tests/stream/xlines1");
    file = super::std::io::fs::File::open(&path).unwrap();
    stream = SmtpStream::new(file);
    assert_eq!(stream.read_line().unwrap(), expected);
    assert_eq!(stream.read_line().unwrap(), expected);
    assert_eq!(stream.read_line().unwrap(), expected);
    assert_eq!(stream.read_line().unwrap(), expected);
    assert_eq!(stream.read_line().unwrap(), expected);
    assert_eq!(stream.read_line().unwrap(), expected);
    assert_eq!(stream.read_line().unwrap(), expected);
    assert_eq!(stream.read_line().unwrap(), expected);
    assert_eq!(stream.read_line().unwrap(), expected);
    assert!(!stream.read_line().is_ok());
}
<commit_msg>escape <CRLF> in docs<commit_after>use std::io::{Reader, Writer, IoErrorKind, InvalidInput};
use std::string::{String};

/// The maximum line size as specified by RFC 5321.
static MAX_LINE_SIZE: uint = 512;

#[test]
fn test_static_vars() {
    assert_eq!(512, MAX_LINE_SIZE);
}

/// A stream specially made for reading SMTP commands.
///
/// It reads lines of input delimited by the <CRLF> sequence and with a maximum
/// size of 512 bytes, including the command word and the <CRLF> sequence. If
/// the input is not UTF8, non-UTF8 characters are replaced with `U+FFFD
/// REPLACEMENT CHARACTER` but no error is returned.
///
/// Returns `InvalidInput` if no line is found within 512 bytes of input.
///
/// # Example
/// ```no_run
/// use std::io::TcpStream;
/// use rsmtp::stream::SmtpStream;
///
/// let mut smtp = SmtpStream::new(TcpStream::connect("127.0.0.1", 2525).unwrap());
///
/// println!("{}", smtp.read_line().unwrap());
/// ```
pub struct SmtpStream<S> {
    stream: S,
    vec: Vec<u8>
}

impl<S> SmtpStream<S> {
    /// Create a new `SmtpStream` from another stream.
    pub fn new(inner: S) -> SmtpStream<S> {
        SmtpStream {
            stream: inner,
            vec: Vec::with_capacity(MAX_LINE_SIZE)
        }
    }
}

impl<R: Reader> SmtpStream<R> {
    /// Read the data section of an email. Ends with "<CRLF>.<CRLF>".
    pub fn read_data(&mut self) -> Result<String, IoErrorKind> {
        println!("At the moment, the DATA command is fake. Wanna help us out?");
        println!("https://github.com/conradkleinespel/rustastic-smtp");
        Ok("Hello world!".into_string())
    }

    /// Read one line of input.
    pub fn read_line(&mut self) -> Result<String, IoErrorKind> {
        self.vec.clear();
        loop {
            // If we have previously read 512 bytes and have not found a line,
            // stop here.
            if self.vec.len() == 512 {
                return Err(InvalidInput)
            }

            // Try to read one more byte and see if a line is formed.
            let byte_res = self.stream.read_byte();
            match byte_res {
                Ok(b) => {
                    self.vec.push(b);
                    // A line ends with \r\n (or 13/10 in decimal), so we
                    // check for these bytes at the end of our buffer.
                    let len = self.vec.len();
                    if len >= 2 {
                        if self.vec[len - 2] == 13 && self.vec[len - 1] == 10 {
                            return Ok(String::from_utf8_lossy(
                                self.vec.slice_to(len - 2)
                            ).into_string());
                        }
                    }
                },
                Err(e) => return Err(e.kind)
            }
        }
    }

}

impl<W: Writer> SmtpStream<W> {
    /// Write a line ended with <CRLF>.
    pub fn write_line(&mut self, s: &str) -> Result<(), ()> {
        match self.stream.write_str(format!("{}\r\n", s).as_slice()) {
            Ok(_) => Ok(()),
            Err(_) => Err(())
        }
    }
}

#[test]
fn test_reader() {
    let mut path: Path;
    let mut file: super::std::io::fs::File;
    let mut stream: SmtpStream<super::std::io::fs::File>;
    let mut expected: String;

    path = Path::new("tests/stream/0line1");
    file = super::std::io::fs::File::open(&path).unwrap();
    stream = SmtpStream::new(file);
    assert!(!stream.read_line().is_ok());

    path = Path::new("tests/stream/0line2");
    file = super::std::io::fs::File::open(&path).unwrap();
    stream = SmtpStream::new(file);
    assert!(!stream.read_line().is_ok());

    path = Path::new("tests/stream/0line3");
    file = super::std::io::fs::File::open(&path).unwrap();
    stream = SmtpStream::new(file);
    assert!(!stream.read_line().is_ok());

    path = Path::new("tests/stream/1line1");
    file = super::std::io::fs::File::open(&path).unwrap();
    stream = SmtpStream::new(file);
    assert_eq!(stream.read_line().unwrap().as_slice(), "hello world!");
    assert!(!stream.read_line().is_ok());

    path = Path::new("tests/stream/1line2");
    file = super::std::io::fs::File::open(&path).unwrap();
    stream = SmtpStream::new(file);
    assert_eq!(stream.read_line().unwrap().as_slice(), "hello world!");
    assert!(!stream.read_line().is_ok());

    path = Path::new("tests/stream/2lines1");
    file = super::std::io::fs::File::open(&path).unwrap();
    stream = SmtpStream::new(file);
    assert_eq!(stream.read_line().unwrap().as_slice(), "hello world!");
    assert_eq!(stream.read_line().unwrap().as_slice(), "bye bye world!");
    assert!(!stream.read_line().is_ok());

    expected = String::from_char(62, 'x');
    path = Path::new("tests/stream/xlines1");
    file = super::std::io::fs::File::open(&path).unwrap();
    stream = SmtpStream::new(file);
    assert_eq!(stream.read_line().unwrap(), expected);
    assert_eq!(stream.read_line().unwrap(), expected);
    assert_eq!(stream.read_line().unwrap(), expected);
    assert_eq!(stream.read_line().unwrap(), expected);
    assert_eq!(stream.read_line().unwrap(), expected);
    assert_eq!(stream.read_line().unwrap(), expected);
    assert_eq!(stream.read_line().unwrap(), expected);
    assert_eq!(stream.read_line().unwrap(), expected);
    assert_eq!(stream.read_line().unwrap(), expected);
    assert!(!stream.read_line().is_ok());
}
<|endoftext|>