1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
/*
This file is responsible for handling config IO.
Since the config is stored in the binary of the program,
a few hacks have to be made to get it to work.
*/

// use crate::helpers::SliceBetween;
use std::env;
use std::fs::{self, File};
use std::io::{self, Write};

/* impl SliceBetween for String {
    fn slice_between(&self, start: &str, end: &str) -> Option<&str> {
        Some(&self[self.find(start)? + start.len()..self.find(end)? + end.len()])
    }
} */

// The reference string cannot be its own constant since it could change the data section,
// and it cannot be used with the concat! macro.

const CONFIG: &'static str = "----CONFIG-REF-START----00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";

// Just to be safe (making sure it doesn't go into the data section) I am returning a non-static string
pub fn get_config() -> io::Result<&'static str> {
    let mut len = None;
    for (i, c) in CONFIG[24 /* length of reference */..].chars().enumerate() {
        if c == '0' {
            if len.is_none() {
                len = Some(i);
            }
        } else if len.is_some() {
            len = None;
        }
    }

    if let Some(i) = len {
        return Ok(&CONFIG[24..24 + i]);
    }

    Err(io::Error::new(
        io::ErrorKind::Other,
        "Could not locate config.",
    ))
}

pub fn new_config(config: &str) -> io::Result<()> {
    // let mut new = String::new();
    // This SHOULDN'T be a const to avoid potentially mixing it with the original reference
    if config.len() > 2024 {
        return Err(io::Error::new(
            io::ErrorKind::Other,
            "Config is too large. It must be no more than 2024 bytes.",
        ));
    }

    let conf_text = "----CONFIG-REF-START----";

    if config.contains(conf_text) {
        return Err(io::Error::new(
            io::ErrorKind::Other,
            format!("Config contains invalid string '{}'.", conf_text),
        ));
    }

    let exe_loc = env::current_exe()?;
    let current = fs::read(&exe_loc)?;
    let mut new = Vec::new();
    let mut index = None;
    for i in 0..current.len() {
        // 24 is the length of the ----CONFIG-REF-START---- text
        if current[i..i + 24] == *conf_text.as_bytes() {
            index = Some(i);
            break;
        }
    }

    if let Some(i) = index {
        // println!("i: {}", i);
        new.extend_from_slice(&current[0..i]);
        let new_out = conf_text.to_owned()
            + config
            + &"0".repeat(2024 /* Number of bytes avaliable to the config */ - config.len());
        // println!("New out len: {}", new_out.len());
        new.extend_from_slice(new_out.as_bytes());
        new.extend_from_slice(&current[i + 2048..]);

        /* if new.len() != current.len() {
            panic!()
        } */

        fs::rename(&exe_loc, exe_loc.to_str().unwrap().to_owned() + ".old")?;
        File::create(exe_loc)?.write_all(&new)?;

        return Ok(());
    }

    Err(io::Error::new(
        io::ErrorKind::Other,
        "Could not locate config.",
    ))
}