<commit_before>use std::result;

use env::Env;
use library::{self, Nullable};
use nameutil::crate_name;
use super::conversion_type::ConversionType;
use traits::*;

pub type Result = result::Result<String, String>;

impl AsStr for Result {
    #[inline]
    fn as_str(&self) -> &str {
        self.as_ref().unwrap_or_else(|s| s)
    }
}

pub fn rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), false)
}

fn rust_type_full(env: &Env, type_id: library::TypeId, nullable: Nullable, by_ref: bool) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let mut skip_option = false;
    let type_ = env.library.type_(type_id);
    let mut rust_type = match *type_ {
        Fundamental(fund) => {
            let ok = |s: &str| Ok(s.into());
            let err = |s: &str| Err(s.into());
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Float => ok("f32"),
                Double => ok("f64"),

                Utf8 => if by_ref { ok("str") } else { ok("String") },
                Filename => if by_ref { ok("str") } else { ok("String") },

                Type => ok("glib::types::Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },
        Alias(ref alias) => rust_type_full(env, alias.typ, nullable, by_ref)
                .map_any(|_| alias.name.clone()),

        Enumeration(ref enum_) => Ok(enum_.name.clone()),
        Bitfield(ref bitfield) => Ok(bitfield.name.clone()),
        Record(ref record) => Ok(record.name.clone()),
        Interface(ref interface) => Ok(interface.name.clone()),
        Class(ref klass) => Ok(klass.name.clone()),
        List(inner_tid) => {
            skip_option = true;
            rust_type(env, inner_tid)
                .map_any(|s| if by_ref { format!("[{}]", s) } else { format!("Vec<{}>", s) })
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    };

    if type_id.ns_id != library::MAIN_NAMESPACE && type_id.ns_id != library::INTERNAL_NAMESPACE {
        let rust_type_with_prefix = rust_type.map(|s| format!("{}::{}",
            crate_name(&env.library.namespace(type_id.ns_id).name), s));
        if env.type_status(&type_id.full_name(&env.library)).ignored() {
            rust_type = Err(rust_type_with_prefix.as_str().into());
        } else {
            rust_type = rust_type_with_prefix;
        }
    }
    if by_ref {
        rust_type = rust_type.map_any(|s| format!("&{}", s));
    }
    if *nullable && !skip_option {
        match ConversionType::of(&env.library, type_id) {
            ConversionType::Pointer => rust_type = rust_type.map_any(|s| format!("Option<{}>", s)),
            _ => (),
        }
    }

    rust_type
}

pub fn used_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    match *env.library.type_(type_id) {
        Fundamental(library::Fundamental::Type) |
            Alias(..) |
            Bitfield(..) |
            Record(..) |
            Class(..) |
            Enumeration(..) |
            Interface(..) => rust_type(env, type_id),
        List(inner_tid) => used_rust_type(env, inner_tid),
        _ => Err("Don't need use".into()),
    }
}

pub fn parameter_rust_type(env: &Env, type_id:library::TypeId,
        direction: library::ParameterDirection, nullable: Nullable) -> Result {
    use library::Type::*;
    let type_ = env.library.type_(type_id);
    let by_ref = use_by_ref(&env.library, type_, direction);
    let rust_type = rust_type_full(env, type_id, nullable, by_ref);
    match *type_ {
        Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 || fund == library::Fundamental::Filename {
                match direction {
                    library::ParameterDirection::In |
                        library::ParameterDirection::Return => rust_type,
                    _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
                }
            } else {
                format_parameter(rust_type, direction)
            }
        }
        Alias(ref alias) => {
            let res = format_parameter(rust_type, direction);
            if parameter_rust_type(env, alias.typ, direction, nullable).is_ok() {
                res
            } else {
                res.and_then(|s| Err(s))
            }
        }

        Enumeration(..) |
            Bitfield(..) => format_parameter(rust_type, direction),

        Record(..) => {
            if env.type_status(&type_id.full_name(&env.library)).ignored() {
                Err(format!("/*Ignored*/{}", rust_type.as_str()))
            } else if direction == library::ParameterDirection::InOut {
                Err(format!("/*Unimplemented*/{}", rust_type.as_str()))
            } else {
                rust_type
            }
        }

        Class(..) |
            Interface (..) => {
            match direction {
                _ if env.type_status(&type_id.full_name(&env.library)).ignored() => {
                    Err(format!("/*Ignored*/{}", rust_type.as_str()))
                }
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }

        List(..) => {
            match direction {
                library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    }
}

#[inline]
fn format_parameter(rust_type: Result, direction: library::ParameterDirection) -> Result {
    if direction.is_out() {
        rust_type.map(|s| format!("&mut {}", s))
    } else {
        rust_type
    }
}

#[inline]
fn use_by_ref(library: &library::Library, type_: &library::Type, direction: library::ParameterDirection) -> bool {
    use library::Type::*;
    match *type_ {
        Fundamental(library::Fundamental::Utf8) |
            Fundamental(library::Fundamental::Filename) |
            Record(..) |
            Class(..) |
            Interface(..) |
            List(..) => direction == library::ParameterDirection::In,
        Alias(ref alias) => {
            let type_ = library.type_(alias.typ);
            use_by_ref(library, type_, direction)
        }
        _ => false,
    }
}
<commit_msg>Use Rectangle without gdk::<commit_after>use std::result;

use env::Env;
use library::{self, Nullable};
use nameutil::crate_name;
use super::conversion_type::ConversionType;
use traits::*;

pub type Result = result::Result<String, String>;

impl AsStr for Result {
    #[inline]
    fn as_str(&self) -> &str {
        self.as_ref().unwrap_or_else(|s| s)
    }
}

pub fn rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), false)
}

fn rust_type_full(env: &Env, type_id: library::TypeId, nullable: Nullable, by_ref: bool) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let mut skip_option = false;
    let type_ = env.library.type_(type_id);
    let mut rust_type = match *type_ {
        Fundamental(fund) => {
            let ok = |s: &str| Ok(s.into());
            let err = |s: &str| Err(s.into());
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Float => ok("f32"),
                Double => ok("f64"),

                Utf8 => if by_ref { ok("str") } else { ok("String") },
                Filename => if by_ref { ok("str") } else { ok("String") },

                Type => ok("glib::types::Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },
        Alias(ref alias) => rust_type_full(env, alias.typ, nullable, by_ref)
                .map_any(|_| alias.name.clone()),

        Enumeration(ref enum_) => Ok(enum_.name.clone()),
        Bitfield(ref bitfield) => Ok(bitfield.name.clone()),
        Record(ref record) => Ok(record.name.clone()),
        Interface(ref interface) => Ok(interface.name.clone()),
        Class(ref klass) => Ok(klass.name.clone()),
        List(inner_tid) => {
            skip_option = true;
            rust_type(env, inner_tid)
                .map_any(|s| if by_ref { format!("[{}]", s) } else { format!("Vec<{}>", s) })
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    };

    if type_id.ns_id != library::MAIN_NAMESPACE && type_id.ns_id != library::INTERNAL_NAMESPACE
        && !implemented_in_main_namespace(&env.library, type_id) {
        let rust_type_with_prefix = rust_type.map(|s| format!("{}::{}",
            crate_name(&env.library.namespace(type_id.ns_id).name), s));
        if env.type_status(&type_id.full_name(&env.library)).ignored() {
            rust_type = Err(rust_type_with_prefix.as_str().into());
        } else {
            rust_type = rust_type_with_prefix;
        }
    }
    if by_ref {
        rust_type = rust_type.map_any(|s| format!("&{}", s));
    }
    if *nullable && !skip_option {
        match ConversionType::of(&env.library, type_id) {
            ConversionType::Pointer => rust_type = rust_type.map_any(|s| format!("Option<{}>", s)),
            _ => (),
        }
    }

    rust_type
}

pub fn used_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    match *env.library.type_(type_id) {
        Fundamental(library::Fundamental::Type) |
            Alias(..) |
            Bitfield(..) |
            Record(..) |
            Class(..) |
            Enumeration(..) |
            Interface(..) => rust_type(env, type_id),
        List(inner_tid) => used_rust_type(env, inner_tid),
        _ => Err("Don't need use".into()),
    }
}

pub fn parameter_rust_type(env: &Env, type_id:library::TypeId,
        direction: library::ParameterDirection, nullable: Nullable) -> Result {
    use library::Type::*;
    let type_ = env.library.type_(type_id);
    let by_ref = use_by_ref(&env.library, type_, direction);
    let rust_type = rust_type_full(env, type_id, nullable, by_ref);
    match *type_ {
        Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 || fund == library::Fundamental::Filename {
                match direction {
                    library::ParameterDirection::In |
                        library::ParameterDirection::Return => rust_type,
                    _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
                }
            } else {
                format_parameter(rust_type, direction)
            }
        }
        Alias(ref alias) => {
            let res = format_parameter(rust_type, direction);
            if parameter_rust_type(env, alias.typ, direction, nullable).is_ok() {
                res
            } else {
                res.and_then(|s| Err(s))
            }
        }

        Enumeration(..) |
            Bitfield(..) => format_parameter(rust_type, direction),

        Record(..) => {
            if env.type_status(&type_id.full_name(&env.library)).ignored() {
                Err(format!("/*Ignored*/{}", rust_type.as_str()))
            } else if direction == library::ParameterDirection::InOut {
                Err(format!("/*Unimplemented*/{}", rust_type.as_str()))
            } else {
                rust_type
            }
        }

        Class(..) |
            Interface (..) => {
            match direction {
                _ if env.type_status(&type_id.full_name(&env.library)).ignored() => {
                    Err(format!("/*Ignored*/{}", rust_type.as_str()))
                }
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }

        List(..) => {
            match direction {
                library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    }
}

#[inline]
fn format_parameter(rust_type: Result, direction: library::ParameterDirection) -> Result {
    if direction.is_out() {
        rust_type.map(|s| format!("&mut {}", s))
    } else {
        rust_type
    }
}

#[inline]
fn use_by_ref(library: &library::Library, type_: &library::Type, direction: library::ParameterDirection) -> bool {
    use library::Type::*;
    match *type_ {
        Fundamental(library::Fundamental::Utf8) |
            Fundamental(library::Fundamental::Filename) |
            Record(..) |
            Class(..) |
            Interface(..) |
            List(..) => direction == library::ParameterDirection::In,
        Alias(ref alias) => {
            let type_ = library.type_(alias.typ);
            use_by_ref(library, type_, direction)
        }
        _ => false,
    }
}

fn implemented_in_main_namespace(library: &library::Library, type_id: library::TypeId) -> bool {
    if library.namespace(library::MAIN_NAMESPACE).name != "Gtk" {
        return false;
    }
    match &*type_id.full_name(library) {
        "Gdk.Rectangle" => true,
        _ => false,
    }
}
<|endoftext|>


<commit_before>use std::{cell::RefCell, os::unix::io::RawFd, rc::Rc};

pub use wayland_protocols::unstable::linux_dmabuf::v1::server::zwp_linux_buffer_params_v1::Flags;
use wayland_protocols::unstable::linux_dmabuf::v1::server::{
    zwp_linux_buffer_params_v1::{
        Error as ParamError, RequestHandler as ParamRequestHandler, ZwpLinuxBufferParamsV1 as BufferParams,
    },
    zwp_linux_dmabuf_v1,
};
use wayland_server::{protocol::wl_buffer, Display, Global, NewResource};

/// Representation of a Dmabuf format, as advertized to the client
pub struct Format {
    /// The format identifier
    pub format: u32,
    /// High part of the supported modifiers
    pub modifier_hi: u32,
    /// Low part of the supported modifiers
    pub modifier_lo: u32,
    /// Number of planes used by this format
    pub plane_count: u32,
}

/// A plane send by the client
pub struct Plane {
    /// The file descriptor
    pub fd: RawFd,
    /// The plane index
    pub plane_idx: u32,
    /// Offset from the start of the Fd
    pub offset: u32,
    /// Stride for this plane
    pub stride: u32,
    /// High part of the modifiers for this plane
    pub modifier_hi: u32,
    /// Low part of the modifiers for this plane
    pub modifier_lo: u32,
}

/// The complete information provided by the client to create a dmabuf buffer
pub struct BufferInfo {
    /// The submitted planes
    pub planes: Vec<Plane>,
    /// The width of this buffer
    pub width: i32,
    /// The height of this buffer
    pub height: i32,
    /// The format in use
    pub format: u32,
    /// The flags applied to it
    ///
    /// This is a bitflag, to be compared with the `Flags` enum reexported by this module.
    pub flags: u32,
}

/// Handler trait for dmabuf validation
///
/// You need to provide an implementation of this trait that will validate the parameters provided by the
/// client and import it as a dmabuf.
pub trait DmabufHandler {
    /// The data of a successfully imported dmabuf.
    ///
    /// This will be stored as the `user_data` of the `WlBuffer` associated with this dmabuf.
    type BufferData: 'static;
    /// Validate a dmabuf
    ///
    /// From the information provided by the client, you need to validate and/or import the buffer.
    ///
    /// You can then store any information your compositor will need to handle it later, when the client has
    /// submitted the buffer by returning `Ok(BufferData)` where `BufferData` is the associated type of this,
    /// trait, a type of your choosing.
    ///
    /// If the buffer could not be imported, whatever the reason, return `Err(())`.
    fn validate_dmabuf(&mut self, info: BufferInfo) -> Result<Self::BufferData, ()>;
    /// Create a buffer from validated buffer data.
    ///
    /// This method is pre-implemented for you by storing the provided `BufferData` as the `user_data` of the
    /// provided `WlBuffer`. By default it assumes that your `BufferData` is not threadsafe.
    ///
    /// You can override it if you need your `BufferData` to be threadsafe, or which to register a destructor
    /// for the `WlBuffer` for example.
    fn create_buffer(
        &mut self,
        data: Self::BufferData,
        buffer: NewResource<wl_buffer::WlBuffer>,
    ) -> wl_buffer::WlBuffer {
        buffer.implement_closure(|_, _| {}, None::<fn(_)>, data)
    }
}

/// Initialize a dmabuf global.
///
/// You need to provide a vector of the supported formats, as well as an implementation fo the `DmabufHandler`
/// trait, which will receive the buffer creation requests from the clients.
pub fn init_dmabuf_global<H, L>(
    display: &mut Display,
    formats: Vec<Format>,
    handler: H,
    logger: L,
) -> Global<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1>
where
    L: Into<Option<::slog::Logger>>,
    H: DmabufHandler + 'static,
{
    let log = crate::slog_or_stdlog(logger);

    let max_planes = formats.iter().map(|f| f.plane_count).max().unwrap_or(0);
    let formats = Rc::new(formats);
    let handler = Rc::new(RefCell::new(handler));

    display.create_global(3, move |new_dmabuf, version| {
        let dma_formats = formats.clone();
        let dma_handler = handler.clone();
        let dmabuf: zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1 = new_dmabuf.implement_closure(
            move |req, _| match req {
                zwp_linux_dmabuf_v1::Request::CreateParams { params_id } => {
                    params_id.implement(
                        ParamsHandler {
                            pending_planes: Vec::new(),
                            max_planes,
                            used: false,
                            formats: dma_formats.clone(),
                            handler: dma_handler.clone(),
                        },
                        None::<fn(_)>,
                        (),
                    );
                }
                _ => (),
            },
            None::<fn(_)>,
            (),
        );

        // send the supported formats
        for f in &*formats {
            dmabuf.format(f.format);
            if version >= 3 {
                dmabuf.modifier(f.format, f.modifier_hi, f.modifier_lo);
            }
        }
    })
}

struct ParamsHandler<H: DmabufHandler> {
    pending_planes: Vec<Plane>,
    max_planes: u32,
    used: bool,
    formats: Rc<Vec<Format>>,
    handler: Rc<RefCell<H>>,
}

impl<H: DmabufHandler> ParamRequestHandler for ParamsHandler<H> {
    fn add(
        &mut self,
        params: BufferParams,
        fd: RawFd,
        plane_idx: u32,
        offset: u32,
        stride: u32,
        modifier_hi: u32,
        modifier_lo: u32,
    ) {
        // protocol checks:
        // Cannot reuse a params:
        if self.used {
            params.as_ref().post_error(
                ParamError::AlreadyUsed as u32,
                "This buffer_params has already been used to create a buffer.".into(),
            );
            return;
        }
        // plane_idx is not too large
        if plane_idx >= self.max_planes {
            // plane_idx starts at 0
            params.as_ref().post_error(
                ParamError::PlaneIdx as u32,
                format!("Plane index {} is out of bounds.", plane_idx),
            );
            return;
        }
        // plane_idx has already been set
        if self.pending_planes.iter().any(|d| d.plane_idx == plane_idx) {
            params.as_ref().post_error(
                ParamError::PlaneSet as u32,
                format!("Plane index {} is already set.", plane_idx),
            );
            return;
        }
        // all checks passed, store the plane
        self.pending_planes.push(Plane {
            fd,
            plane_idx,
            offset,
            stride,
            modifier_hi,
            modifier_lo,
        });
    }

    fn create(&mut self, params: BufferParams, width: i32, height: i32, format: u32, flags: u32) {
        // Cannot reuse a params:
        if self.used {
            params.as_ref().post_error(
                ParamError::AlreadyUsed as u32,
                "This buffer_params has already been used to create a buffer.".into(),
            );
            return;
        }
        self.used = true;
        if !buffer_basic_checks(
            &self.formats,
            &self.pending_planes,
            &params,
            format,
            width,
            height,
        ) {
            return;
        }
        let info = BufferInfo {
            planes: ::std::mem::replace(&mut self.pending_planes, Vec::new()),
            width,
            height,
            format,
            flags,
        };
        let mut handler = self.handler.borrow_mut();
        if let Ok(data) = handler.validate_dmabuf(info) {
            if let Some(buffer) = params
                .as_ref()
                .client()
                .and_then(|c| c.create_resource::<wl_buffer::WlBuffer>(1))
            {
                let buffer = handler.create_buffer(data, buffer);
                params.created(&buffer);
            }
        } else {
            params.failed();
        }
    }

    fn create_immed(
        &mut self,
        params: BufferParams,
        buffer_id: NewResource<wl_buffer::WlBuffer>,
        width: i32,
        height: i32,
        format: u32,
        flags: u32,
    ) {
        // Cannot reuse a params:
        if self.used {
            params.as_ref().post_error(
                ParamError::AlreadyUsed as u32,
                "This buffer_params has already been used to create a buffer.".into(),
            );
            return;
        }
        self.used = true;
        if !buffer_basic_checks(
            &self.formats,
            &self.pending_planes,
            &params,
            format,
            width,
            height,
        ) {
            return;
        }
        let info = BufferInfo {
            planes: ::std::mem::replace(&mut self.pending_planes, Vec::new()),
            width,
            height,
            format,
            flags,
        };
        let mut handler = self.handler.borrow_mut();
        if let Ok(data) = handler.validate_dmabuf(info) {
            handler.create_buffer(data, buffer_id);
        } else {
            params.as_ref().post_error(
                ParamError::InvalidWlBuffer as u32,
                "create_immed resulted in an invalid buffer.".into(),
            );
        }
    }
}

fn buffer_basic_checks(
    formats: &[Format],
    pending_planes: &[Plane],
    params: &BufferParams,
    format: u32,
    width: i32,
    height: i32,
) -> bool {
    // protocol_checks:
    // This must be a known format
    let format = match formats.iter().find(|f| f.format == format) {
        Some(f) => f,
        None => {
            params.as_ref().post_error(
                ParamError::InvalidFormat as u32,
                format!("Format {:x} is not supported.", format),
            );
            return false;;
        }
    };
    // The number of planes set must match what the format expects
    let max_plane_set = pending_planes.iter().map(|d| d.plane_idx + 1).max().unwrap_or(0);
    if max_plane_set != format.plane_count || pending_planes.len() < format.plane_count as usize {
        params.as_ref().post_error(
            ParamError::Incomplete as u32,
            format!(
                "Format {:x} requires {} planes but got {}.",
                format.format, format.plane_count, max_plane_set
            ),
        );
        return false;
    }
    // Width and height must be positivie
    if width < 1 || height < 1 {
        params.as_ref().post_error(
            ParamError::InvalidDimensions as u32,
            format!("Dimensions ({},{}) are not valid.", width, height),
        );
        return false;
    }
    // check the size of each plane buffer
    for plane in pending_planes {
        // check size for overflow
        let end = match plane
            .stride
            .checked_mul(height as u32)
            .and_then(|o| o.checked_add(plane.offset))
        {
            None => {
                params.as_ref().post_error(
                    ParamError::OutOfBounds as u32,
                    format!("Size overflow for plane {}.", plane.plane_idx),
                );
                return false;
            }
            Some(e) => e,
        };
        if let Ok(size) = ::nix::unistd::lseek(plane.fd, 0, ::nix::unistd::Whence::SeekEnd) {
            if plane.offset as i64 > size {
                params.as_ref().post_error(
                    ParamError::OutOfBounds as u32,
                    format!("Invalid offset {} for plane {}.", plane.offset, plane.plane_idx),
                );
                return false;
            }
            if (plane.offset + plane.stride) as i64 > size {
                params.as_ref().post_error(
                    ParamError::OutOfBounds as u32,
                    format!("Invalid stride {} for plane {}.", plane.stride, plane.plane_idx),
                );
                return false;
            }
            // Planes > 0 can be subsampled, in which case 'size' will be smaller
            // than expected.
            if plane.plane_idx == 0 && end as i64 > size {
                params.as_ref().post_error(
                    ParamError::OutOfBounds as u32,
                    format!(
                        "Invalid stride ({}) or height ({}) for plane {}.",
                        plane.stride, height, plane.plane_idx
                    ),
                );
                return false;
            }
        }
    }
    return true;
}
<commit_msg>wayland.dmabuf: module docs & some logging<commit_after>//! Linux DMABUF protocol
//!
//! This module provides helper to handle the linux-dmabuf protocol, which allows clients to submit their
//! contents as dmabuf file descriptors. These handlers automate the aggregation of the metadata associated
//! with a dma buffer, and do some basic checking of the sanity of what the client sends.
//!
//! ## How to use
//!
//! To setup the dmabuf global, you will need to provide 2 things:
//!
//! - a list of the dmabuf formats you wish to support
//! - an implementation of the `DmabufHandler` trait
//!
//! The list of supported format is just a `Vec<Format>`, where you will enter all the (format, modifier)
//! couples you support.
//!
//! The implementation of the `DmabufHandler` trait will be called whenever a client has finished setting up
//! a dma buffer. You will be handled the full details of the sclient's submission as a `BufferInfo` struct,
//! and you need to validate it and maybe import it into your rendered. The `BufferData` associated type
//! allows you to store any metadata of handle to the resource you need into the created `wl_buffer`,
//! user data, to then retrieve it when it is attached to a surface to re-identify the dmabuf.
//!
//! ```
//! # extern crate wayland_server;
//! # extern crate smithay;
//! use smithay::wayland::dmabuf::{DmabufHandler, BufferInfo, init_dmabuf_global};
//!
//! struct MyDmabufHandler;
//!
//! struct MyBufferData {
//!     /* ... */
//! }
//!
//! impl Drop for MyBufferData {
//!     fn drop(&mut self) {
//!         // This is called when all handles to this buffer have been dropped,
//!         // both client-side and server side.
//!         // You can now free the associated resource in your renderer.
//!     }
//! }
//!
//! impl DmabufHandler for MyDmabufHandler {
//!     type BufferData = MyBufferData;
//!     fn validate_dmabuf(&mut self, info: BufferInfo) -> Result<Self::BufferData, ()> {
//!         /* validate the dmabuf and import it into your renderer state */
//!         Ok(MyBufferData { /* ... */ })
//!     }
//! }
//!
//! // Once this is defined, you can in your setup initialize the dmabuf global:
//!
//! # fn main() {
//! # let mut event_loop = wayland_server::calloop::EventLoop::<()>::new().unwrap();
//! # let mut display = wayland_server::Display::new(event_loop.handle());
//! // define your supported formats
//! let formats = vec![
//!     /* ... */
//! ];
//! let dmabuf_global = init_dmabuf_global(
//!     &mut display,
//!     formats,
//!     MyDmabufHandler,
//!     None // we don't provide a logger in this example
//! );
//! # }
//! ```

use std::{cell::RefCell, os::unix::io::RawFd, rc::Rc};

pub use wayland_protocols::unstable::linux_dmabuf::v1::server::zwp_linux_buffer_params_v1::Flags;
use wayland_protocols::unstable::linux_dmabuf::v1::server::{
    zwp_linux_buffer_params_v1::{
        Error as ParamError, RequestHandler as ParamRequestHandler, ZwpLinuxBufferParamsV1 as BufferParams,
    },
    zwp_linux_dmabuf_v1,
};
use wayland_server::{protocol::wl_buffer, Display, Global, NewResource};

/// Representation of a Dmabuf format, as advertized to the client
pub struct Format {
    /// The format identifier.
    ///
    /// It must be a `DRM_FORMAT` code, as defined by the libdrm's drm_fourcc.h. The Linux kernel's DRM
    /// sub-system is the authoritative source on how the format codes should work.
    pub format: u32,
    /// The supported dmabuf layout modifier.
    ///
    /// This is an opaque token. Drivers use this token to express tiling, compression, etc. driver-specific
    /// modifications to the base format defined by the DRM fourcc code.
    pub modifier: u64,
    /// Number of planes used by this format
    pub plane_count: u32,
}

/// A plane send by the client
pub struct Plane {
    /// The file descriptor
    pub fd: RawFd,
    /// The plane index
    pub plane_idx: u32,
    /// Offset from the start of the Fd
    pub offset: u32,
    /// Stride for this plane
    pub stride: u32,
    /// Modifier for this plane
    pub modifier: u64,
}

/// The complete information provided by the client to create a dmabuf buffer
pub struct BufferInfo {
    /// The submitted planes
    pub planes: Vec<Plane>,
    /// The width of this buffer
    pub width: i32,
    /// The height of this buffer
    pub height: i32,
    /// The format in use
    pub format: u32,
    /// The flags applied to it
    ///
    /// This is a bitflag, to be compared with the `Flags` enum reexported by this module.
    pub flags: u32,
}

/// Handler trait for dmabuf validation
///
/// You need to provide an implementation of this trait that will validate the parameters provided by the
/// client and import it as a dmabuf.
pub trait DmabufHandler {
    /// The data of a successfully imported dmabuf.
    ///
    /// This will be stored as the `user_data` of the `WlBuffer` associated with this dmabuf. If it has a
    /// destructor, it will be run when the client has destroyed the buffer and your compositor has dropped
    /// all of its `WlBuffer` handles to it.
    type BufferData: 'static;
    /// Validate a dmabuf
    ///
    /// From the information provided by the client, you need to validate and/or import the buffer.
    ///
    /// You can then store any information your compositor will need to handle it later, when the client has
    /// submitted the buffer by returning `Ok(BufferData)` where `BufferData` is the associated type of this,
    /// trait, a type of your choosing.
    ///
    /// If the buffer could not be imported, whatever the reason, return `Err(())`.
    fn validate_dmabuf(&mut self, info: BufferInfo) -> Result<Self::BufferData, ()>;
    /// Create a buffer from validated buffer data.
    ///
    /// This method is pre-implemented for you by storing the provided `BufferData` as the `user_data` of the
    /// provided `WlBuffer`. By default it assumes that your `BufferData` is not threadsafe.
    ///
    /// You can override it if you need your `BufferData` to be threadsafe, or which to register a destructor
    /// for the `WlBuffer` for example.
    fn create_buffer(
        &mut self,
        data: Self::BufferData,
        buffer: NewResource<wl_buffer::WlBuffer>,
    ) -> wl_buffer::WlBuffer {
        buffer.implement_closure(|_, _| {}, None::<fn(_)>, data)
    }
}

/// Initialize a dmabuf global.
///
/// You need to provide a vector of the supported formats, as well as an implementation fo the `DmabufHandler`
/// trait, which will receive the buffer creation requests from the clients.
pub fn init_dmabuf_global<H, L>(
    display: &mut Display,
    formats: Vec<Format>,
    handler: H,
    logger: L,
) -> Global<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1>
where
    L: Into<Option<::slog::Logger>>,
    H: DmabufHandler + 'static,
{
    let log = crate::slog_or_stdlog(logger).new(o!("smithay_module" => "dmabuf_handler"));

    let max_planes = formats.iter().map(|f| f.plane_count).max().unwrap_or(0);
    let formats = Rc::new(formats);
    let handler = Rc::new(RefCell::new(handler));

    trace!(
        log,
        "Initializing DMABUF handler with {} supported formats",
        formats.len()
    );

    display.create_global(3, move |new_dmabuf, version| {
        let dma_formats = formats.clone();
        let dma_handler = handler.clone();
        let dma_log = log.clone();
        let dmabuf: zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1 = new_dmabuf.implement_closure(
            move |req, _| match req {
                zwp_linux_dmabuf_v1::Request::CreateParams { params_id } => {
                    params_id.implement(
                        ParamsHandler {
                            pending_planes: Vec::new(),
                            max_planes,
                            used: false,
                            formats: dma_formats.clone(),
                            handler: dma_handler.clone(),
                            log: dma_log.clone(),
                        },
                        None::<fn(_)>,
                        (),
                    );
                }
                _ => (),
            },
            None::<fn(_)>,
            (),
        );

        // send the supported formats
        for f in &*formats {
            dmabuf.format(f.format);
            if version >= 3 {
                dmabuf.modifier(f.format, (f.modifier >> 32) as u32, f.modifier as u32);
            }
        }
    })
}

struct ParamsHandler<H: DmabufHandler> {
    pending_planes: Vec<Plane>,
    max_planes: u32,
    used: bool,
    formats: Rc<Vec<Format>>,
    handler: Rc<RefCell<H>>,
    log: ::slog::Logger,
}

impl<H: DmabufHandler> ParamRequestHandler for ParamsHandler<H> {
    fn add(
        &mut self,
        params: BufferParams,
        fd: RawFd,
        plane_idx: u32,
        offset: u32,
        stride: u32,
        modifier_hi: u32,
        modifier_lo: u32,
    ) {
        // protocol checks:
        // Cannot reuse a params:
        if self.used {
            params.as_ref().post_error(
                ParamError::AlreadyUsed as u32,
                "This buffer_params has already been used to create a buffer.".into(),
            );
            return;
        }
        // plane_idx is not too large
        if plane_idx >= self.max_planes {
            // plane_idx starts at 0
            params.as_ref().post_error(
                ParamError::PlaneIdx as u32,
                format!("Plane index {} is out of bounds.", plane_idx),
            );
            return;
        }
        // plane_idx has already been set
        if self.pending_planes.iter().any(|d| d.plane_idx == plane_idx) {
            params.as_ref().post_error(
                ParamError::PlaneSet as u32,
                format!("Plane index {} is already set.", plane_idx),
            );
            return;
        }
        // all checks passed, store the plane
        self.pending_planes.push(Plane {
            fd,
            plane_idx,
            offset,
            stride,
            modifier: ((modifier_hi as u64) << 32) + (modifier_lo as u64),
        });
    }

    fn create(&mut self, params: BufferParams, width: i32, height: i32, format: u32, flags: u32) {
        // Cannot reuse a params:
        if self.used {
            params.as_ref().post_error(
                ParamError::AlreadyUsed as u32,
                "This buffer_params has already been used to create a buffer.".into(),
            );
            return;
        }
        self.used = true;
        if !buffer_basic_checks(
            &self.formats,
            &self.pending_planes,
            &params,
            format,
            width,
            height,
        ) {
            trace!(self.log, "Killing client providing bogus dmabuf buffer params.");
            return;
        }
        let info = BufferInfo {
            planes: ::std::mem::replace(&mut self.pending_planes, Vec::new()),
            width,
            height,
            format,
            flags,
        };
        let mut handler = self.handler.borrow_mut();
        if let Ok(data) = handler.validate_dmabuf(info) {
            if let Some(buffer) = params
                .as_ref()
                .client()
                .and_then(|c| c.create_resource::<wl_buffer::WlBuffer>(1))
            {
                let buffer = handler.create_buffer(data, buffer);
                trace!(self.log, "Creating a new validated dma wl_buffer.");
                params.created(&buffer);
            }
        } else {
            trace!(self.log, "Refusing creation of an invalid dma wl_buffer.");
            params.failed();
        }
    }

    fn create_immed(
        &mut self,
        params: BufferParams,
        buffer_id: NewResource<wl_buffer::WlBuffer>,
        width: i32,
        height: i32,
        format: u32,
        flags: u32,
    ) {
        // Cannot reuse a params:
        if self.used {
            params.as_ref().post_error(
                ParamError::AlreadyUsed as u32,
                "This buffer_params has already been used to create a buffer.".into(),
            );
            return;
        }
        self.used = true;
        if !buffer_basic_checks(
            &self.formats,
            &self.pending_planes,
            &params,
            format,
            width,
            height,
        ) {
            trace!(self.log, "Killing client providing bogus dmabuf buffer params.");
            return;
        }
        let info = BufferInfo {
            planes: ::std::mem::replace(&mut self.pending_planes, Vec::new()),
            width,
            height,
            format,
            flags,
        };
        let mut handler = self.handler.borrow_mut();
        if let Ok(data) = handler.validate_dmabuf(info) {
            trace!(self.log, "Creating a new validated immediate dma wl_buffer.");
            handler.create_buffer(data, buffer_id);
        } else {
            trace!(
                self.log,
                "Refusing creation of an invalid immediate dma wl_buffer, killing client."
            );
            params.as_ref().post_error(
                ParamError::InvalidWlBuffer as u32,
                "create_immed resulted in an invalid buffer.".into(),
            );
        }
    }
}

fn buffer_basic_checks(
    formats: &[Format],
    pending_planes: &[Plane],
    params: &BufferParams,
    format: u32,
    width: i32,
    height: i32,
) -> bool {
    // protocol_checks:
    // This must be a known format
    let format = match formats.iter().find(|f| f.format == format) {
        Some(f) => f,
        None => {
            params.as_ref().post_error(
                ParamError::InvalidFormat as u32,
                format!("Format {:x} is not supported.", format),
            );
            return false;;
        }
    };
    // The number of planes set must match what the format expects
    let max_plane_set = pending_planes.iter().map(|d| d.plane_idx + 1).max().unwrap_or(0);
    if max_plane_set != format.plane_count || pending_planes.len() < format.plane_count as usize {
        params.as_ref().post_error(
            ParamError::Incomplete as u32,
            format!(
                "Format {:x} requires {} planes but got {}.",
                format.format, format.plane_count, max_plane_set
            ),
        );
        return false;
    }
    // Width and height must be positivie
    if width < 1 || height < 1 {
        params.as_ref().post_error(
            ParamError::InvalidDimensions as u32,
            format!("Dimensions ({},{}) are not valid.", width, height),
        );
        return false;
    }
    // check the size of each plane buffer
    for plane in pending_planes {
        // check size for overflow
        let end = match plane
            .stride
            .checked_mul(height as u32)
            .and_then(|o| o.checked_add(plane.offset))
        {
            None => {
                params.as_ref().post_error(
                    ParamError::OutOfBounds as u32,
                    format!("Size overflow for plane {}.", plane.plane_idx),
                );
                return false;
            }
            Some(e) => e,
        };
        if let Ok(size) = ::nix::unistd::lseek(plane.fd, 0, ::nix::unistd::Whence::SeekEnd) {
            if plane.offset as i64 > size {
                params.as_ref().post_error(
                    ParamError::OutOfBounds as u32,
                    format!("Invalid offset {} for plane {}.", plane.offset, plane.plane_idx),
                );
                return false;
            }
            if (plane.offset + plane.stride) as i64 > size {
                params.as_ref().post_error(
                    ParamError::OutOfBounds as u32,
                    format!("Invalid stride {} for plane {}.", plane.stride, plane.plane_idx),
                );
                return false;
            }
            // Planes > 0 can be subsampled, in which case 'size' will be smaller
            // than expected.
            if plane.plane_idx == 0 && end as i64 > size {
                params.as_ref().post_error(
                    ParamError::OutOfBounds as u32,
                    format!(
                        "Invalid stride ({}) or height ({}) for plane {}.",
                        plane.stride, height, plane.plane_idx
                    ),
                );
                return false;
            }
        }
    }
    return true;
}
<|endoftext|>


<commit_before>use super::header::{
    FromHeader,
    ToHeader,
};
use super::rfc2045::Rfc2045Parser;
use super::rfc2047::decode_q_encoding;
use super::results::{ParsingResult,ParsingError};

use std::ascii::OwnedAsciiExt;
use std::collections::HashMap;
use rustc_serialize::base64::FromBase64;

/// Content-Type string, major/minor as the first and second elements
/// respectively.
#[stable]
pub type MimeContentType = (String, String);

/// Special header type for the Content-Type header.
#[stable]
pub struct MimeContentTypeHeader {
    /// The content type presented by this header
    pub content_type: MimeContentType,
    /// Parameters of this header
    pub params: HashMap<String, String>,
}

impl FromHeader for MimeContentTypeHeader {
    fn from_header(value: String) -> ParsingResult<MimeContentTypeHeader> {
        let mut parser = Rfc2045Parser::new(value.as_slice());
        let (value, params) = parser.consume_all();

        let mime_parts: Vec<&str> = value.as_slice().splitn(2, '/').collect();

        if mime_parts.len() == 2 {
            Ok(MimeContentTypeHeader {
                content_type: (mime_parts[0].to_string(), mime_parts[1].to_string()),
                params: params
            })
        } else {
            Err(ParsingError::new("Too many mime parts.".to_string()))  // FIXME
        }
    }
}

impl ToHeader for MimeContentTypeHeader {
    fn to_header(value: MimeContentTypeHeader) -> ParsingResult<String> {
        let (mime_major, mime_minor) = value.content_type;
        let mut result = format!("{}/{}", mime_major, mime_minor);
        for (key, val) in value.params.iter() {
            result = format!("{} {}={};", result, key, val);
        }
        Ok(result)
    }
}

/// Special header type for the Content-Transfer-Encoding header.
#[derive(Debug,PartialEq,Eq,Copy)]
#[stable]
pub enum MimeContentTransferEncoding {
    /// Message content is not encoded in any way.
    Identity,
    /// Content transfered using the quoted-printable encoding.
    ///
    /// This encoding is defined in RFC 2045 Section 6.7
    QuotedPrintable,
    /// Content transfered as BASE64
    ///
    /// This encoding is defined in RFC 2045 Section 6.8
    Base64,
}

impl MimeContentTransferEncoding {
    /// Decode the input string with this transfer encoding.
    ///
    /// Note that this will return a clone of the input's bytes if the
    /// transfer encoding is the Identity encoding.
    #[unstable]
    pub fn decode(&self, input: &String) -> Option<Vec<u8>> {
        match *self {
            MimeContentTransferEncoding::Identity => Some(input.clone().into_bytes()),
            MimeContentTransferEncoding::QuotedPrintable => decode_q_encoding(input.as_slice()).ok(),
            MimeContentTransferEncoding::Base64 => input.as_slice().from_base64().ok(),
        }
    }
}

impl FromHeader for MimeContentTransferEncoding {
    fn from_header(value: String) -> ParsingResult<MimeContentTransferEncoding> {
        let lower = value.into_ascii_lowercase();
        match lower.as_slice() {
            "7bit" | "8bit" | "binary" => Ok(MimeContentTransferEncoding::Identity),
            "quoted-printable" => Ok(MimeContentTransferEncoding::QuotedPrintable),
            "base64" => Ok(MimeContentTransferEncoding::Base64),
            x => Err(ParsingError::new(format!("Invalid encoding: {}", x)))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::super::results::ParsingResult;
    use super::super::header::Header;

    use std::collections::HashMap;

    struct ContentTypeParseTestResult<'a> {
        major_type: &'a str,
        minor_type: &'a str,
        params: Vec<(&'a str, &'a str)>,
    }

    struct ContentTypeParseTest<'a> {
        input: &'a str,
        result: Option<ContentTypeParseTestResult<'a>>,
    }

    #[test]
    fn test_content_type_parse() {
        let tests = vec![
            ContentTypeParseTest {
                input: "text/plain",
                result: Some(ContentTypeParseTestResult {
                    major_type: "text",
                    minor_type: "plain",
                    params: vec![],
                }),
            },
            ContentTypeParseTest {
                input: "text/plain; charset=us-ascii",
                result: Some(ContentTypeParseTestResult {
                    major_type: "text",
                    minor_type: "plain",
                    params: vec![
                        ("charset", "us-ascii"),
                    ],
                }),
            },
            ContentTypeParseTest {
                input: "application/octet-stream; charset=us-ascii; param=value",
                result: Some(ContentTypeParseTestResult {
                    major_type: "application",
                    minor_type: "octet-stream",
                    params: vec![
                        ("charset", "us-ascii"),
                        ("param", "value"),
                    ],
                }),
            },
        ];

        for test in tests.into_iter() {
            let header = Header::new("Content-Type".to_string(), test.input.to_string());
            let parsed_header: Option<MimeContentTypeHeader> = header.get_value().ok();

            let result = match (parsed_header, test.result) {
                (Some(given_result), Some(expected_result)) => {
                    let (given_major, given_minor) = given_result.content_type;
                    let mut expected_params = HashMap::new();
                    for &(param_name, param_value) in expected_result.params.iter() {
                        expected_params.insert(param_name.to_string(), param_value.to_string());
                    }
                    given_major == expected_result.major_type.to_string() &&
                        given_minor == expected_result.minor_type.to_string() &&
                        given_result.params == expected_params
                },
                (None, None) => true,
                (_, _) => false,
            };
            assert!(result, format!("Content-Type parse: '{}'", test.input));
        }
    }

    #[test]
    fn test_content_transfer_parse() {
        let tests = vec![
            ("base64", Some(MimeContentTransferEncoding::Base64)),
            ("quoted-printable", Some(MimeContentTransferEncoding::QuotedPrintable)),
            ("7bit", Some(MimeContentTransferEncoding::Identity)),
            ("8bit", Some(MimeContentTransferEncoding::Identity)),
            ("binary", Some(MimeContentTransferEncoding::Identity)),
            // Check for case insensitivity
            ("BASE64", Some(MimeContentTransferEncoding::Base64)),
            // Check for fail case
            ("lkasjdl", None),
        ];

        for (test, expected) in tests.into_iter() {
            let header = Header::new("Content-Transfer-Encoding".to_string(), test.to_string());
            let parsed: Option<MimeContentTransferEncoding> = header.get_value().ok();
            assert_eq!(parsed, expected);
        }
    }

    struct ContentTransferDecodeTest<'s> {
        encoding: MimeContentTransferEncoding,
        input: &'s str,
        output: Option<Vec<u8>>,
    }

    #[test]
    fn test_content_transfer_decode() {
        let tests = vec![
            ContentTransferDecodeTest {
                encoding: MimeContentTransferEncoding::Identity,
                input: "foo",
                output: Some(vec![102, 111, 111]),
            },
            ContentTransferDecodeTest {
                encoding: MimeContentTransferEncoding::QuotedPrintable,
                input: "foo=\r\nbar\r\nbaz",
                output: Some(vec![
                    102, 111, 111, 98, 97, 114, 13, 10,   // foobar
                    98, 97, 122,                          // baz
                ]),
            },
            ContentTransferDecodeTest {
                encoding: MimeContentTransferEncoding::Base64,
                input: "Zm9vCmJhcgpi\r\nYXoKcXV4Cg==",
                output: Some(vec![
                    102, 111, 111, 10, // foo
                    98, 97, 114, 10,   // bar
                    98, 97, 122, 10,   // baz
                    113, 117, 120, 10, // qux
                ]),
            },
            // Bad base64 content
            ContentTransferDecodeTest {
                encoding: MimeContentTransferEncoding::Base64,
                input: "/?#",
                output: None,
            },
        ];

        for test in tests.into_iter() {
            let result = test.encoding.decode(&test.input.to_string());
            assert_eq!(result, test.output);
        }
    }
}
<commit_msg>Correct error message<commit_after>use super::header::{
    FromHeader,
    ToHeader,
};
use super::rfc2045::Rfc2045Parser;
use super::rfc2047::decode_q_encoding;
use super::results::{ParsingResult,ParsingError};

use std::ascii::OwnedAsciiExt;
use std::collections::HashMap;
use rustc_serialize::base64::FromBase64;

/// Content-Type string, major/minor as the first and second elements
/// respectively.
#[stable]
pub type MimeContentType = (String, String);

/// Special header type for the Content-Type header.
#[stable]
pub struct MimeContentTypeHeader {
    /// The content type presented by this header
    pub content_type: MimeContentType,
    /// Parameters of this header
    pub params: HashMap<String, String>,
}

impl FromHeader for MimeContentTypeHeader {
    fn from_header(value: String) -> ParsingResult<MimeContentTypeHeader> {
        let mut parser = Rfc2045Parser::new(value.as_slice());
        let (value, params) = parser.consume_all();

        let mime_parts: Vec<&str> = value.as_slice().splitn(2, '/').collect();

        if mime_parts.len() == 2 {
            Ok(MimeContentTypeHeader {
                content_type: (mime_parts[0].to_string(), mime_parts[1].to_string()),
                params: params
            })
        } else {
            Err(ParsingError::new(format!("Invalid mimetype: {}", value)))
        }
    }
}

impl ToHeader for MimeContentTypeHeader {
    fn to_header(value: MimeContentTypeHeader) -> ParsingResult<String> {
        let (mime_major, mime_minor) = value.content_type;
        let mut result = format!("{}/{}", mime_major, mime_minor);
        for (key, val) in value.params.iter() {
            result = format!("{} {}={};", result, key, val);
        }
        Ok(result)
    }
}

/// Special header type for the Content-Transfer-Encoding header.
#[derive(Debug,PartialEq,Eq,Copy)]
#[stable]
pub enum MimeContentTransferEncoding {
    /// Message content is not encoded in any way.
    Identity,
    /// Content transfered using the quoted-printable encoding.
    ///
    /// This encoding is defined in RFC 2045 Section 6.7
    QuotedPrintable,
    /// Content transfered as BASE64
    ///
    /// This encoding is defined in RFC 2045 Section 6.8
    Base64,
}

impl MimeContentTransferEncoding {
    /// Decode the input string with this transfer encoding.
    ///
    /// Note that this will return a clone of the input's bytes if the
    /// transfer encoding is the Identity encoding.
    #[unstable]
    pub fn decode(&self, input: &String) -> Option<Vec<u8>> {
        match *self {
            MimeContentTransferEncoding::Identity => Some(input.clone().into_bytes()),
            MimeContentTransferEncoding::QuotedPrintable => decode_q_encoding(input.as_slice()).ok(),
            MimeContentTransferEncoding::Base64 => input.as_slice().from_base64().ok(),
        }
    }
}

impl FromHeader for MimeContentTransferEncoding {
    fn from_header(value: String) -> ParsingResult<MimeContentTransferEncoding> {
        let lower = value.into_ascii_lowercase();
        match lower.as_slice() {
            "7bit" | "8bit" | "binary" => Ok(MimeContentTransferEncoding::Identity),
            "quoted-printable" => Ok(MimeContentTransferEncoding::QuotedPrintable),
            "base64" => Ok(MimeContentTransferEncoding::Base64),
            x => Err(ParsingError::new(format!("Invalid encoding: {}", x)))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::super::results::ParsingResult;
    use super::super::header::Header;

    use std::collections::HashMap;

    struct ContentTypeParseTestResult<'a> {
        major_type: &'a str,
        minor_type: &'a str,
        params: Vec<(&'a str, &'a str)>,
    }

    struct ContentTypeParseTest<'a> {
        input: &'a str,
        result: Option<ContentTypeParseTestResult<'a>>,
    }

    #[test]
    fn test_content_type_parse() {
        let tests = vec![
            ContentTypeParseTest {
                input: "text/plain",
                result: Some(ContentTypeParseTestResult {
                    major_type: "text",
                    minor_type: "plain",
                    params: vec![],
                }),
            },
            ContentTypeParseTest {
                input: "text/plain; charset=us-ascii",
                result: Some(ContentTypeParseTestResult {
                    major_type: "text",
                    minor_type: "plain",
                    params: vec![
                        ("charset", "us-ascii"),
                    ],
                }),
            },
            ContentTypeParseTest {
                input: "application/octet-stream; charset=us-ascii; param=value",
                result: Some(ContentTypeParseTestResult {
                    major_type: "application",
                    minor_type: "octet-stream",
                    params: vec![
                        ("charset", "us-ascii"),
                        ("param", "value"),
                    ],
                }),
            },
        ];

        for test in tests.into_iter() {
            let header = Header::new("Content-Type".to_string(), test.input.to_string());
            let parsed_header: Option<MimeContentTypeHeader> = header.get_value().ok();

            let result = match (parsed_header, test.result) {
                (Some(given_result), Some(expected_result)) => {
                    let (given_major, given_minor) = given_result.content_type;
                    let mut expected_params = HashMap::new();
                    for &(param_name, param_value) in expected_result.params.iter() {
                        expected_params.insert(param_name.to_string(), param_value.to_string());
                    }
                    given_major == expected_result.major_type.to_string() &&
                        given_minor == expected_result.minor_type.to_string() &&
                        given_result.params == expected_params
                },
                (None, None) => true,
                (_, _) => false,
            };
            assert!(result, format!("Content-Type parse: '{}'", test.input));
        }
    }

    #[test]
    fn test_content_transfer_parse() {
        let tests = vec![
            ("base64", Some(MimeContentTransferEncoding::Base64)),
            ("quoted-printable", Some(MimeContentTransferEncoding::QuotedPrintable)),
            ("7bit", Some(MimeContentTransferEncoding::Identity)),
            ("8bit", Some(MimeContentTransferEncoding::Identity)),
            ("binary", Some(MimeContentTransferEncoding::Identity)),
            // Check for case insensitivity
            ("BASE64", Some(MimeContentTransferEncoding::Base64)),
            // Check for fail case
            ("lkasjdl", None),
        ];

        for (test, expected) in tests.into_iter() {
            let header = Header::new("Content-Transfer-Encoding".to_string(), test.to_string());
            let parsed: Option<MimeContentTransferEncoding> = header.get_value().ok();
            assert_eq!(parsed, expected);
        }
    }

    struct ContentTransferDecodeTest<'s> {
        encoding: MimeContentTransferEncoding,
        input: &'s str,
        output: Option<Vec<u8>>,
    }

    #[test]
    fn test_content_transfer_decode() {
        let tests = vec![
            ContentTransferDecodeTest {
                encoding: MimeContentTransferEncoding::Identity,
                input: "foo",
                output: Some(vec![102, 111, 111]),
            },
            ContentTransferDecodeTest {
                encoding: MimeContentTransferEncoding::QuotedPrintable,
                input: "foo=\r\nbar\r\nbaz",
                output: Some(vec![
                    102, 111, 111, 98, 97, 114, 13, 10,   // foobar
                    98, 97, 122,                          // baz
                ]),
            },
            ContentTransferDecodeTest {
                encoding: MimeContentTransferEncoding::Base64,
                input: "Zm9vCmJhcgpi\r\nYXoKcXV4Cg==",
                output: Some(vec![
                    102, 111, 111, 10, // foo
                    98, 97, 114, 10,   // bar
                    98, 97, 122, 10,   // baz
                    113, 117, 120, 10, // qux
                ]),
            },
            // Bad base64 content
            ContentTransferDecodeTest {
                encoding: MimeContentTransferEncoding::Base64,
                input: "/?#",
                output: None,
            },
        ];

        for test in tests.into_iter() {
            let result = test.encoding.decode(&test.input.to_string());
            assert_eq!(result, test.output);
        }
    }
}
<|endoftext|>


<commit_before>use diesel::prelude::*;

use crate::app::App;
use crate::github::{github_api, team_url};
use crate::util::errors::{cargo_err, AppResult, NotFound};

use oauth2::AccessToken;

use crate::models::{Crate, CrateOwner, Owner, OwnerKind, User};
use crate::schema::{crate_owners, teams};
use crate::views::EncodableTeam;

/// For now, just a Github Team. Can be upgraded to other teams
/// later if desirable.
#[derive(Queryable, Identifiable, Serialize, Deserialize, Debug)]
pub struct Team {
    /// Unique table id
    pub id: i32,
    /// "github:org:team"
    /// An opaque unique ID, that was at one point parsed out to query Github.
    /// We only query membership with github using the github_id, though.
    /// This is the only name we should ever talk to Cargo about.
    pub login: String,
    /// The GitHub API works on team ID numbers. This can change, if a team
    /// is deleted and then recreated with the same name!!!
    pub github_id: i32,
    /// Sugary goodness
    pub name: Option<String>,
    pub avatar: Option<String>,
}

#[derive(Insertable, AsChangeset, Debug)]
#[table_name = "teams"]
pub struct NewTeam<'a> {
    pub login: &'a str,
    pub github_id: i32,
    pub name: Option<String>,
    pub avatar: Option<String>,
}

impl<'a> NewTeam<'a> {
    pub fn new(
        login: &'a str,
        github_id: i32,
        name: Option<String>,
        avatar: Option<String>,
    ) -> Self {
        NewTeam {
            login,
            github_id,
            name,
            avatar,
        }
    }

    pub fn create_or_update(&self, conn: &PgConnection) -> QueryResult<Team> {
        use crate::schema::teams::dsl::*;
        use diesel::insert_into;

        insert_into(teams)
            .values(self)
            .on_conflict(login)
            .do_update()
            .set(self)
            .get_result(conn)
    }
}

impl Team {
    /// Tries to create the Team in the DB (assumes a `:` has already been found).
    ///
    /// # Panics
    ///
    /// This function will panic if login contains less than 2 `:` characters.
    pub fn create_or_update(
        app: &App,
        conn: &PgConnection,
        login: &str,
        req_user: &User,
    ) -> AppResult<Self> {
        // must look like system:xxxxxxx
        let mut chunks = login.split(':');
        // unwrap is okay, split on an empty string still has 1 chunk
        match chunks.next().unwrap() {
            // github:rust-lang:owners
            "github" => {
                // unwrap is documented above as part of the calling contract
                let org = chunks.next().unwrap();
                let team = chunks.next().ok_or_else(|| {
                    cargo_err(
                        "missing github team argument; \
                         format is github:org:team",
                    )
                })?;
                Team::create_or_update_github_team(
                    app,
                    conn,
                    &login.to_lowercase(),
                    org,
                    team,
                    req_user,
                )
            }
            _ => Err(cargo_err(
                "unknown organization handler, \
                 only 'github:org:team' is supported",
            )),
        }
    }

    /// Tries to create or update a Github Team. Assumes `org` and `team` are
    /// correctly parsed out of the full `name`. `name` is passed as a
    /// convenience to avoid rebuilding it.
    fn create_or_update_github_team(
        app: &App,
        conn: &PgConnection,
        login: &str,
        org_name: &str,
        team_name: &str,
        req_user: &User,
    ) -> AppResult<Self> {
        // GET orgs/:org/teams
        // check that `team` is the `slug` in results, and grab its data

        // "sanitization"
        fn whitelist(c: char) -> bool {
            match c {
                'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_' => false,
                _ => true,
            }
        }

        if let Some(c) = org_name.chars().find(|c| whitelist(*c)) {
            return Err(cargo_err(&format_args!(
                "organization cannot contain special \
                 characters like {}",
                c
            )));
        }

        #[derive(Deserialize)]
        struct GithubTeam {
            id: i32,              // unique GH id (needed for membership queries)
            name: Option<String>, // Pretty name
        }

        let url = format!("/orgs/{}/teams/{}", org_name, team_name);
        let token = AccessToken::new(req_user.gh_access_token.clone());
        let team = github_api::<GithubTeam>(app, &url, &token).map_err(|_| {
            cargo_err(&format_args!(
                "could not find the github team {}/{}",
                org_name, team_name
            ))
        })?;

        if !team_with_gh_id_contains_user(app, team.id, req_user)? {
            return Err(cargo_err("only members of a team can add it as an owner"));
        }

        #[derive(Deserialize)]
        struct Org {
            avatar_url: Option<String>,
        }

        let url = format!("/orgs/{}", org_name);
        let org = github_api::<Org>(app, &url, &token)?;

        NewTeam::new(&login.to_lowercase(), team.id, team.name, org.avatar_url)
            .create_or_update(conn)
            .map_err(Into::into)
    }

    /// Phones home to Github to ask if this User is a member of the given team.
    /// Note that we're assuming that the given user is the one interested in
    /// the answer. If this is not the case, then we could accidentally leak
    /// private membership information here.
    pub fn contains_user(&self, app: &App, user: &User) -> AppResult<bool> {
        team_with_gh_id_contains_user(app, self.github_id, user)
    }

    pub fn owning(krate: &Crate, conn: &PgConnection) -> QueryResult<Vec<Owner>> {
        let base_query = CrateOwner::belonging_to(krate).filter(crate_owners::deleted.eq(false));
        let teams = base_query
            .inner_join(teams::table)
            .select(teams::all_columns)
            .filter(crate_owners::owner_kind.eq(OwnerKind::Team as i32))
            .load(conn)?
            .into_iter()
            .map(Owner::Team);

        Ok(teams.collect())
    }

    pub fn encodable(self) -> EncodableTeam {
        let Team {
            id,
            name,
            login,
            avatar,
            ..
        } = self;
        let url = team_url(&login);

        EncodableTeam {
            id,
            login,
            name,
            avatar,
            url: Some(url),
        }
    }
}

fn team_with_gh_id_contains_user(app: &App, github_id: i32, user: &User) -> AppResult<bool> {
    // GET teams/:team_id/memberships/:user_name
    // check that "state": "active"

    #[derive(Deserialize)]
    struct Membership {
        state: String,
    }

    let url = format!("/teams/{}/memberships/{}", &github_id, &user.gh_login);
    let token = AccessToken::new(user.gh_access_token.clone());
    let membership = match github_api::<Membership>(app, &url, &token) {
        // Officially how `false` is returned
        Err(ref e) if e.is::<NotFound>() => return Ok(false),
        x => x?,
    };

    // There is also `state: pending` for which we could possibly give
    // some feedback, but it's not obvious how that should work.
    Ok(membership.state == "active")
}
<commit_msg>Auto merge of #2645 - JohnTitor:matches, r=jtgeibel<commit_after>use diesel::prelude::*;

use crate::app::App;
use crate::github::{github_api, team_url};
use crate::util::errors::{cargo_err, AppResult, NotFound};

use oauth2::AccessToken;

use crate::models::{Crate, CrateOwner, Owner, OwnerKind, User};
use crate::schema::{crate_owners, teams};
use crate::views::EncodableTeam;

/// For now, just a Github Team. Can be upgraded to other teams
/// later if desirable.
#[derive(Queryable, Identifiable, Serialize, Deserialize, Debug)]
pub struct Team {
    /// Unique table id
    pub id: i32,
    /// "github:org:team"
    /// An opaque unique ID, that was at one point parsed out to query Github.
    /// We only query membership with github using the github_id, though.
    /// This is the only name we should ever talk to Cargo about.
    pub login: String,
    /// The GitHub API works on team ID numbers. This can change, if a team
    /// is deleted and then recreated with the same name!!!
    pub github_id: i32,
    /// Sugary goodness
    pub name: Option<String>,
    pub avatar: Option<String>,
}

#[derive(Insertable, AsChangeset, Debug)]
#[table_name = "teams"]
pub struct NewTeam<'a> {
    pub login: &'a str,
    pub github_id: i32,
    pub name: Option<String>,
    pub avatar: Option<String>,
}

impl<'a> NewTeam<'a> {
    pub fn new(
        login: &'a str,
        github_id: i32,
        name: Option<String>,
        avatar: Option<String>,
    ) -> Self {
        NewTeam {
            login,
            github_id,
            name,
            avatar,
        }
    }

    pub fn create_or_update(&self, conn: &PgConnection) -> QueryResult<Team> {
        use crate::schema::teams::dsl::*;
        use diesel::insert_into;

        insert_into(teams)
            .values(self)
            .on_conflict(login)
            .do_update()
            .set(self)
            .get_result(conn)
    }
}

impl Team {
    /// Tries to create the Team in the DB (assumes a `:` has already been found).
    ///
    /// # Panics
    ///
    /// This function will panic if login contains less than 2 `:` characters.
    pub fn create_or_update(
        app: &App,
        conn: &PgConnection,
        login: &str,
        req_user: &User,
    ) -> AppResult<Self> {
        // must look like system:xxxxxxx
        let mut chunks = login.split(':');
        // unwrap is okay, split on an empty string still has 1 chunk
        match chunks.next().unwrap() {
            // github:rust-lang:owners
            "github" => {
                // unwrap is documented above as part of the calling contract
                let org = chunks.next().unwrap();
                let team = chunks.next().ok_or_else(|| {
                    cargo_err(
                        "missing github team argument; \
                         format is github:org:team",
                    )
                })?;
                Team::create_or_update_github_team(
                    app,
                    conn,
                    &login.to_lowercase(),
                    org,
                    team,
                    req_user,
                )
            }
            _ => Err(cargo_err(
                "unknown organization handler, \
                 only 'github:org:team' is supported",
            )),
        }
    }

    /// Tries to create or update a Github Team. Assumes `org` and `team` are
    /// correctly parsed out of the full `name`. `name` is passed as a
    /// convenience to avoid rebuilding it.
    fn create_or_update_github_team(
        app: &App,
        conn: &PgConnection,
        login: &str,
        org_name: &str,
        team_name: &str,
        req_user: &User,
    ) -> AppResult<Self> {
        // GET orgs/:org/teams
        // check that `team` is the `slug` in results, and grab its data

        // "sanitization"
        fn is_allowed_char(c: char) -> bool {
            matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_')
        }

        if let Some(c) = org_name.chars().find(|c| !is_allowed_char(*c)) {
            return Err(cargo_err(&format_args!(
                "organization cannot contain special \
                 characters like {}",
                c
            )));
        }

        #[derive(Deserialize)]
        struct GithubTeam {
            id: i32,              // unique GH id (needed for membership queries)
            name: Option<String>, // Pretty name
        }

        let url = format!("/orgs/{}/teams/{}", org_name, team_name);
        let token = AccessToken::new(req_user.gh_access_token.clone());
        let team = github_api::<GithubTeam>(app, &url, &token).map_err(|_| {
            cargo_err(&format_args!(
                "could not find the github team {}/{}",
                org_name, team_name
            ))
        })?;

        if !team_with_gh_id_contains_user(app, team.id, req_user)? {
            return Err(cargo_err("only members of a team can add it as an owner"));
        }

        #[derive(Deserialize)]
        struct Org {
            avatar_url: Option<String>,
        }

        let url = format!("/orgs/{}", org_name);
        let org = github_api::<Org>(app, &url, &token)?;

        NewTeam::new(&login.to_lowercase(), team.id, team.name, org.avatar_url)
            .create_or_update(conn)
            .map_err(Into::into)
    }

    /// Phones home to Github to ask if this User is a member of the given team.
    /// Note that we're assuming that the given user is the one interested in
    /// the answer. If this is not the case, then we could accidentally leak
    /// private membership information here.
    pub fn contains_user(&self, app: &App, user: &User) -> AppResult<bool> {
        team_with_gh_id_contains_user(app, self.github_id, user)
    }

    pub fn owning(krate: &Crate, conn: &PgConnection) -> QueryResult<Vec<Owner>> {
        let base_query = CrateOwner::belonging_to(krate).filter(crate_owners::deleted.eq(false));
        let teams = base_query
            .inner_join(teams::table)
            .select(teams::all_columns)
            .filter(crate_owners::owner_kind.eq(OwnerKind::Team as i32))
            .load(conn)?
            .into_iter()
            .map(Owner::Team);

        Ok(teams.collect())
    }

    pub fn encodable(self) -> EncodableTeam {
        let Team {
            id,
            name,
            login,
            avatar,
            ..
        } = self;
        let url = team_url(&login);

        EncodableTeam {
            id,
            login,
            name,
            avatar,
            url: Some(url),
        }
    }
}

fn team_with_gh_id_contains_user(app: &App, github_id: i32, user: &User) -> AppResult<bool> {
    // GET teams/:team_id/memberships/:user_name
    // check that "state": "active"

    #[derive(Deserialize)]
    struct Membership {
        state: String,
    }

    let url = format!("/teams/{}/memberships/{}", &github_id, &user.gh_login);
    let token = AccessToken::new(user.gh_access_token.clone());
    let membership = match github_api::<Membership>(app, &url, &token) {
        // Officially how `false` is returned
        Err(ref e) if e.is::<NotFound>() => return Ok(false),
        x => x?,
    };

    // There is also `state: pending` for which we could possibly give
    // some feedback, but it's not obvious how that should work.
    Ok(membership.state == "active")
}
<|endoftext|>


<commit_before>use std::process::{Command, Stdio};
use std::io::{BufRead, Write, BufReader};

pub fn startRDS() {
	let mut rdsd_child = Command::new("rdsd").stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null()).spawn().expect("rdsd command failed to start");
	let mut rdsquery_child = Command::new("rdsquery").arg("-s").arg("localhost").arg("-c").arg("0").arg("-t").arg("tmc").stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::null()).spawn().expect("rdsquery command failed to start");

	let mut buf_reader = BufReader::new(rdsquery_child.stdout.as_mut().unwrap());

	for line in buf_reader.lines() {
		if let Ok(content) = line {
			if content.starts_with("GS ") {
				println!("GS:: {}", content);
			} else if content.starts_with("GF ") {
				println!("GF:: {}", content);
			} else if content.starts_with("S ") {
				println!("S::  {}", content);
			}
		}
	}

	rdsd_child.kill();
}<commit_msg>begin analyzing tmc data in osm<commit_after>use std::process::{Command, Stdio};
use std::io::{BufRead, Write, BufReader};
use std::ffi::OsString;
use std::fs;
use std::fs::File;
use std::path::Path;

use osmpbfreader::OsmObj;
use osmpbfreader::Tags;
use osmpbfreader::OsmPbfReader;

pub fn startRDS() {
	readRDSTags();
}

pub fn readRDSQueryInput() {
	let mut rdsd_child = Command::new("rdsd").stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null()).spawn().expect("rdsd command failed to start");
	let mut rdsquery_child = Command::new("rdsquery").arg("-s").arg("localhost").arg("-c").arg("0").arg("-t").arg("tmc").stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::null()).spawn().expect("rdsquery command failed to start");

	let mut buf_reader = BufReader::new(rdsquery_child.stdout.as_mut().unwrap());

	for line in buf_reader.lines() {
		if let Ok(content) = line {
			if content.starts_with("GS ") {
				println!("GS:: {}", content);
			} else if content.starts_with("GF ") {
				println!("GF:: {}", content);
			} else if content.starts_with("S ") {
				println!("S::  {}", content);
			}
		}
	}

	rdsd_child.kill();
}

pub fn readRDSTags() {
	let filename = OsString::from("/home/jan/Downloads/baden-wuerttemberg-latest.osm.pbf".to_string());
	let pbf_file = File::open(&Path::new(&filename)).unwrap();

	let mut pbf = OsmPbfReader::new(pbf_file);

	for obj in pbf.iter() {
		match obj {
			OsmObj::Node(node) => {
				printTMCTags(node.tags, "node", node.id);
			}
			OsmObj::Relation(rel) => {
				printTMCTags(rel.tags, "rel", rel.id);
			}
			OsmObj::Way(way) => {
				printTMCTags(way.tags, "way", way.id);
			}
		}
	}
}

fn printTMCTags(tags: Tags, typ: &str, id: i64) {
	for (tag, val) in tags {
		if tag.contains("tmc") || tag.contains("TMC") {
			println!("{}->{} in {} {}", tag, val, typ, id);
		}
	}
}<|endoftext|>