sn0int 0.20.1

Semi-automatic OSINT framework and package manager
Documentation
use crate::errors::*;
use serde::{Serialize, Deserialize};
use crate::fmt::colors::*;
use diesel::prelude::*;
use crate::models::*;
use chrono::NaiveDateTime;

#[derive(Identifiable, Queryable, Serialize, Deserialize, PartialEq, Debug)]
#[table_name="devices"]
pub struct Device {
    pub id: i32,
    pub value: String,
    pub name: Option<String>,
    pub hostname: Option<String>,
    pub vendor: Option<String>,
    pub unscoped: bool,
    pub last_seen: Option<NaiveDateTime>,
}

impl Model for Device {
    type ID = str;

    fn to_string(&self) -> String {
        self.value.to_owned()
    }

    fn list(db: &Database) -> Result<Vec<Self>> {
        use crate::schema::devices::dsl::*;

        let results = devices.load::<Self>(db.db())?;

        Ok(results)
    }

    fn filter(db: &Database, filter: &Filter) -> Result<Vec<Self>> {
        use crate::schema::devices::dsl::*;

        let query = devices.filter(filter.sql());
        let results = query.load::<Self>(db.db())?;

        Ok(results)
    }

    fn delete(db: &Database, filter: &Filter) -> Result<usize> {
        use crate::schema::devices::dsl::*;

        diesel::delete(devices.filter(filter.sql()))
            .execute(db.db())
            .map_err(Error::from)
    }

    fn delete_id(db: &Database, my_id: i32) -> Result<usize> {
        use crate::schema::devices::dsl::*;

        diesel::delete(devices.filter(id.eq(my_id)))
            .execute(db.db())
            .map_err(Error::from)
    }

    fn id(&self) -> i32 {
        self.id
    }

    fn value(&self) -> &Self::ID {
        &self.value
    }

    fn by_id(db: &Database, my_id: i32) -> Result<Self> {
        use crate::schema::devices::dsl::*;

        let domain = devices.filter(id.eq(my_id))
            .first::<Self>(db.db())?;

        Ok(domain)
    }

    fn get(db: &Database, query: &Self::ID) -> Result<Self> {
        use crate::schema::devices::dsl::*;

        let domain = devices.filter(value.eq(query))
            .first::<Self>(db.db())?;

        Ok(domain)
    }

    fn get_opt(db: &Database, query: &Self::ID) -> Result<Option<Self>> {
        use crate::schema::devices::dsl::*;

        let domain = devices.filter(value.eq(query))
            .first::<Self>(db.db())
            .optional()?;

        Ok(domain)
    }
}

impl Scopable for Device {
    fn scoped(&self) -> bool {
        !self.unscoped
    }

    fn scope(db: &Database, filter: &Filter) -> Result<usize> {
        use crate::schema::devices::dsl::*;

        diesel::update(devices.filter(filter.sql()))
            .set(unscoped.eq(false))
            .execute(db.db())
            .map_err(Error::from)
    }

    fn noscope(db: &Database, filter: &Filter) -> Result<usize> {
        use crate::schema::devices::dsl::*;

        diesel::update(devices.filter(filter.sql()))
            .set(unscoped.eq(true))
            .execute(db.db())
            .map_err(Error::from)
    }
}

impl Device {
    fn network_device(&self, db: &Database) -> Result<Option<NetworkDevice>> {
        NetworkDevice::belonging_to(self)
            .order(network_devices::last_seen.desc())
            .first::<NetworkDevice>(db.db())
            .optional()
            .map_err(Error::from)
    }
}

pub struct PrintableDevice {
    value: String,
}

impl fmt::Display for PrintableDevice {
    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
        write!(w, "{:?}", self.value)
    }
}

impl Printable<PrintableDevice> for Device {
    fn printable(&self, _db: &Database) -> Result<PrintableDevice> {
        Ok(PrintableDevice {
            value: self.value.to_string(),
        })
    }
}

pub struct DetailedDevice {
    id: i32,
    value: String,
    name: Option<String>,
    hostname: Option<String>,
    vendor: Option<String>,
    ipaddr: Option<String>,
    network: Option<String>,
    unscoped: bool,
    last_seen: Option<NaiveDateTime>,
}

impl DisplayableDetailed for DetailedDevice {
    #[inline]
    fn scoped(&self) -> bool {
        !self.unscoped
    }

    #[inline]
    fn print(&self, w: &mut fmt::DetailFormatter) -> fmt::Result {
        w.id(self.id)?;
        w.debug::<Green, _>(&self.value)?;

        w.start_group();
        w.opt_debug_label::<Yellow, _>("name", &self.name)?;
        w.opt_debug_label::<Yellow, _>("hostname", &self.hostname)?;
        w.opt_debug_label::<Yellow, _>("vendor", &self.vendor)?;
        w.opt_debug_label::<Yellow, _>("last_seen", &self.last_seen)?;
        w.end_group()?;

        w.start_group();
        w.opt_debug_label::<Yellow, _>("network", &self.network)?;
        w.opt_debug_label::<Yellow, _>("ipaddr", &self.ipaddr)?;
        w.end_group()?;

        Ok(())
    }

    #[inline]
    fn children(&self, _w: &mut fmt::DetailFormatter) -> fmt::Result {
        Ok(())
    }
}

display_detailed!(DetailedDevice);

impl Detailed for Device {
    type T = DetailedDevice;

    fn detailed(&self, db: &Database) -> Result<Self::T> {
        let network_device = self.network_device(db)?;

        let (ipaddr, network) = match network_device {
            Some(network_device) => {
                let network = network_device.network(db)?;
                (network_device.ipaddr, Some(network.value))
            },
            _ => (None, None),
        };

        Ok(DetailedDevice {
            id: self.id,
            value: self.value.to_string(),
            name: self.name.clone(),
            hostname: self.hostname.clone(),
            vendor: self.vendor.clone(),
            ipaddr,
            network,
            unscoped: self.unscoped,
            last_seen: self.last_seen,
        })
    }
}

#[derive(Debug, Clone, Insertable, Serialize, Deserialize)]
#[table_name="devices"]
pub struct NewDevice {
    pub value: String,
    pub name: Option<String>,
    pub hostname: Option<String>,
    pub vendor: Option<String>,
    pub last_seen: Option<NaiveDateTime>,
    pub unscoped: bool,
}

impl InsertableStruct<Device> for NewDevice {
    fn value(&self) -> &str {
        &self.value
    }

    fn set_scoped(&mut self, scoped: bool) {
        self.unscoped = !scoped;
    }

    fn insert(&self, db: &Database) -> Result<()> {
        diesel::insert_into(devices::table)
            .values(self)
            .execute(db.db())?;
        Ok(())
    }
}

impl Upsertable<Device> for NewDevice {
    type Update = DeviceUpdate;

    fn upsert(self, existing: &Device) -> Self::Update {
        Self::Update {
            id: existing.id,
            name: Self::upsert_opt(self.name, &existing.name),
            hostname: Self::upsert_opt(self.hostname, &existing.hostname),
            vendor: Self::upsert_opt(self.vendor, &existing.vendor),
            last_seen: Self::upsert_opt(self.last_seen, &existing.last_seen),
        }
    }
}

impl Printable<PrintableDevice> for NewDevice {
    fn printable(&self, _db: &Database) -> Result<PrintableDevice> {
        Ok(PrintableDevice {
            value: self.value.to_string(),
        })
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct InsertDevice {
    pub value: String,
    pub name: Option<String>,
    pub hostname: Option<String>,
    pub vendor: Option<String>,
    pub last_seen: Option<NaiveDateTime>,
}

impl InsertToNew for InsertDevice {
    type Target = NewDevice;

    fn try_into_new(self) -> Result<NewDevice> {
        let value = self.value.to_lowercase();
        Ok(NewDevice {
            value,
            name: self.name,
            hostname: self.hostname,
            vendor: self.vendor,
            last_seen: self.last_seen,

            unscoped: false,
        })
    }
}

#[derive(Identifiable, AsChangeset, Serialize, Deserialize, Debug)]
#[table_name="devices"]
pub struct DeviceUpdate {
    pub id: i32,
    pub name: Option<String>,
    pub hostname: Option<String>,
    pub vendor: Option<String>,
    pub last_seen: Option<NaiveDateTime>,
}

impl Upsert for DeviceUpdate {
    fn is_dirty(&self) -> bool {
        self.name.is_some() ||
        self.hostname.is_some() ||
        self.vendor.is_some() ||
        self.last_seen.is_some()
    }

    fn generic(self) -> Update {
        Update::Device(self)
    }

    fn apply(&self, db: &Database) -> Result<i32> {
        db.update_device(self)
    }
}

impl Updateable<Device> for DeviceUpdate {
    fn changeset(&mut self, existing: &Device) {
        Self::clear_if_equal(&mut self.name, &existing.name);
        Self::clear_if_equal(&mut self.hostname, &existing.hostname);
        Self::clear_if_equal(&mut self.vendor, &existing.vendor);
        Self::clear_if_equal(&mut self.last_seen, &existing.last_seen);
    }

    fn fmt(&self, updates: &mut Vec<String>, colors: bool) {
        Self::push_value(updates, "name", &self.name, colors);
        Self::push_value(updates, "hostname", &self.hostname, colors);
        Self::push_value(updates, "vendor", &self.vendor, colors);
        Self::push_value(updates, "last_seen", &self.last_seen, colors);
    }
}