<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Utilities for the implementation of JSAPI proxy handlers.

#![deny(missing_docs)]

use dom::bindings::conversions::is_dom_proxy;
use dom::bindings::utils::delete_property_by_id;
use js::glue::{GetProxyHandler, GetProxyHandlerFamily, SetProxyExtra};
use js::glue::GetProxyExtra;
use js::glue::InvokeGetOwnPropertyDescriptor;
use js::jsapi::{DOMProxyShadowsResult, JSContext, JSObject, JSPROP_GETTER, PropertyDescriptor};
use js::jsapi::{Handle, HandleId, HandleObject, MutableHandle, ObjectOpResult};
use js::jsapi::{JSErrNum, JS_AlreadyHasOwnPropertyById, JS_StrictPropertyStub};
use js::jsapi::{JS_DefinePropertyById, JS_NewObjectWithGivenProto, SetDOMProxyInformation};
use js::jsapi::GetObjectProto;
use js::jsapi::GetStaticPrototype;
use js::jsapi::JS_GetPropertyDescriptorById;
use js::jsapi::MutableHandleObject;
use js::jsval::ObjectValue;
use libc;
use std::{mem, ptr};

static JSPROXYSLOT_EXPANDO: u32 = 0;

/// Determine if this id shadows any existing properties for this proxy.
pub unsafe extern "C" fn shadow_check_callback(cx: *mut JSContext,
                                               object: HandleObject,
                                               id: HandleId)
                                               -> DOMProxyShadowsResult {
    // TODO: support OverrideBuiltins when #12978 is fixed.

    rooted!(in(cx) let mut expando = ptr::null_mut());
    get_expando_object(object, expando.handle_mut());
    if !expando.get().is_null() {
        let mut has_own = false;
        if !JS_AlreadyHasOwnPropertyById(cx, expando.handle(), id, &mut has_own) {
            return DOMProxyShadowsResult::ShadowCheckFailed;
        }

        if has_own {
            return DOMProxyShadowsResult::ShadowsViaDirectExpando;
        }
    }

    // Our expando, if any, didn't shadow, so we're not shadowing at all.
    DOMProxyShadowsResult::DoesntShadow
}

/// Initialize the infrastructure for DOM proxy objects.
pub unsafe fn init() {
    SetDOMProxyInformation(GetProxyHandlerFamily(),
                           JSPROXYSLOT_EXPANDO,
                           Some(shadow_check_callback));
}

/// Invoke the [[GetOwnProperty]] trap (`getOwnPropertyDescriptor`) on `proxy`,
/// with argument `id` and return the result, if it is not `undefined`.
/// Otherwise, walk along the prototype chain to find a property with that
/// name.
pub unsafe extern "C" fn get_property_descriptor(cx: *mut JSContext,
                                                 proxy: HandleObject,
                                                 id: HandleId,
                                                 desc: MutableHandle<PropertyDescriptor>)
                                                 -> bool {
    let handler = GetProxyHandler(proxy.get());
    if !InvokeGetOwnPropertyDescriptor(handler, cx, proxy, id, desc) {
        return false;
    }
    if !desc.obj.is_null() {
        return true;
    }

    rooted!(in(cx) let mut proto = ptr::null_mut());
    if !GetObjectProto(cx, proxy, proto.handle_mut()) {
        // FIXME(#11868) Should assign to desc.obj, desc.get() is a copy.
        desc.get().obj = ptr::null_mut();
        return true;
    }

    JS_GetPropertyDescriptorById(cx, proto.handle(), id, desc)
}

/// Defines an expando on the given `proxy`.
pub unsafe extern "C" fn define_property(cx: *mut JSContext,
                                         proxy: HandleObject,
                                         id: HandleId,
                                         desc: Handle<PropertyDescriptor>,
                                         result: *mut ObjectOpResult)
                                         -> bool {
    // FIXME: Workaround for https://github.com/rust-lang/rfcs/issues/718
    let setter: *const libc::c_void = mem::transmute(desc.get().setter);
    let setter_stub: unsafe extern fn(_, _, _, _, _) -> _ = JS_StrictPropertyStub;
    let setter_stub: *const libc::c_void = mem::transmute(setter_stub);
    if (desc.get().attrs & JSPROP_GETTER) != 0 && setter == setter_stub {
        (*result).code_ = JSErrNum::JSMSG_GETTER_ONLY as ::libc::uintptr_t;
        return true;
    }

    rooted!(in(cx) let mut expando = ptr::null_mut());
    ensure_expando_object(cx, proxy, expando.handle_mut());
    JS_DefinePropertyById(cx, expando.handle(), id, desc, result)
}

/// Deletes an expando off the given `proxy`.
pub unsafe extern "C" fn delete(cx: *mut JSContext,
                                proxy: HandleObject,
                                id: HandleId,
                                bp: *mut ObjectOpResult)
                                -> bool {
    rooted!(in(cx) let mut expando = ptr::null_mut());
    get_expando_object(proxy, expando.handle_mut());
    if expando.is_null() {
        (*bp).code_ = 0 /* OkCode */;
        return true;
    }

    delete_property_by_id(cx, expando.handle(), id, bp)
}

/// Controls whether the Extensible bit can be changed
pub unsafe extern "C" fn prevent_extensions(_cx: *mut JSContext,
                                            _proxy: HandleObject,
                                            result: *mut ObjectOpResult)
                                            -> bool {
    (*result).code_ = JSErrNum::JSMSG_CANT_PREVENT_EXTENSIONS as ::libc::uintptr_t;
    true
}

/// Reports whether the object is Extensible
pub unsafe extern "C" fn is_extensible(_cx: *mut JSContext,
                                       _proxy: HandleObject,
                                       succeeded: *mut bool)
                                       -> bool {
    *succeeded = true;
    true
}

/// If `proxy` (underneath any functionally-transparent wrapper proxies) has as
/// its `[[GetPrototypeOf]]` trap the ordinary `[[GetPrototypeOf]]` behavior
/// defined for ordinary objects, set `*is_ordinary` to true and store `obj`'s
/// prototype in `proto`.  Otherwise set `*isOrdinary` to false. In case of
/// error, both outparams have unspecified value.
///
/// This implementation always handles the case of the ordinary
/// `[[GetPrototypeOf]]` behavior. An alternative implementation will be
/// necessary for the Location object.
pub unsafe extern "C" fn get_prototype_if_ordinary(_: *mut JSContext,
                                                   proxy: HandleObject,
                                                   is_ordinary: *mut bool,
                                                   proto: MutableHandleObject)
                                                   -> bool {
    *is_ordinary = true;
    proto.set(GetStaticPrototype(proxy.get()));
    true
}

/// Get the expando object, or null if there is none.
pub fn get_expando_object(obj: HandleObject, expando: MutableHandleObject) {
    unsafe {
        assert!(is_dom_proxy(obj.get()));
        let val = GetProxyExtra(obj.get(), JSPROXYSLOT_EXPANDO);
        expando.set(if val.is_undefined() {
            ptr::null_mut()
        } else {
            val.to_object()
        });
    }
}

/// Get the expando object, or create it if it doesn't exist yet.
/// Fails on JSAPI failure.
pub fn ensure_expando_object(cx: *mut JSContext, obj: HandleObject, expando: MutableHandleObject) {
    unsafe {
        assert!(is_dom_proxy(obj.get()));
        get_expando_object(obj, expando);
        if expando.is_null() {
            expando.set(JS_NewObjectWithGivenProto(cx, ptr::null_mut(), HandleObject::null()));
            assert!(!expando.is_null());

            SetProxyExtra(obj.get(), JSPROXYSLOT_EXPANDO, &ObjectValue(expando.get()));
        }
    }
}

/// Set the property descriptor's object to `obj` and set it to enumerable,
/// and writable if `readonly` is true.
pub fn fill_property_descriptor(mut desc: MutableHandle<PropertyDescriptor>,
                                obj: *mut JSObject,
                                attrs: u32) {
    desc.obj = obj;
    desc.attrs = attrs;
    desc.getter = None;
    desc.setter = None;
}
<commit_msg>Make {get,ensure}_expando_object unsafe.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Utilities for the implementation of JSAPI proxy handlers.

#![deny(missing_docs)]

use dom::bindings::conversions::is_dom_proxy;
use dom::bindings::utils::delete_property_by_id;
use js::glue::{GetProxyHandler, GetProxyHandlerFamily, SetProxyExtra};
use js::glue::GetProxyExtra;
use js::glue::InvokeGetOwnPropertyDescriptor;
use js::jsapi::{DOMProxyShadowsResult, JSContext, JSObject, JSPROP_GETTER, PropertyDescriptor};
use js::jsapi::{Handle, HandleId, HandleObject, MutableHandle, ObjectOpResult};
use js::jsapi::{JSErrNum, JS_AlreadyHasOwnPropertyById, JS_StrictPropertyStub};
use js::jsapi::{JS_DefinePropertyById, JS_NewObjectWithGivenProto, SetDOMProxyInformation};
use js::jsapi::GetObjectProto;
use js::jsapi::GetStaticPrototype;
use js::jsapi::JS_GetPropertyDescriptorById;
use js::jsapi::MutableHandleObject;
use js::jsval::ObjectValue;
use libc;
use std::{mem, ptr};

static JSPROXYSLOT_EXPANDO: u32 = 0;

/// Determine if this id shadows any existing properties for this proxy.
pub unsafe extern "C" fn shadow_check_callback(cx: *mut JSContext,
                                               object: HandleObject,
                                               id: HandleId)
                                               -> DOMProxyShadowsResult {
    // TODO: support OverrideBuiltins when #12978 is fixed.

    rooted!(in(cx) let mut expando = ptr::null_mut());
    get_expando_object(object, expando.handle_mut());
    if !expando.get().is_null() {
        let mut has_own = false;
        if !JS_AlreadyHasOwnPropertyById(cx, expando.handle(), id, &mut has_own) {
            return DOMProxyShadowsResult::ShadowCheckFailed;
        }

        if has_own {
            return DOMProxyShadowsResult::ShadowsViaDirectExpando;
        }
    }

    // Our expando, if any, didn't shadow, so we're not shadowing at all.
    DOMProxyShadowsResult::DoesntShadow
}

/// Initialize the infrastructure for DOM proxy objects.
pub unsafe fn init() {
    SetDOMProxyInformation(GetProxyHandlerFamily(),
                           JSPROXYSLOT_EXPANDO,
                           Some(shadow_check_callback));
}

/// Invoke the [[GetOwnProperty]] trap (`getOwnPropertyDescriptor`) on `proxy`,
/// with argument `id` and return the result, if it is not `undefined`.
/// Otherwise, walk along the prototype chain to find a property with that
/// name.
pub unsafe extern "C" fn get_property_descriptor(cx: *mut JSContext,
                                                 proxy: HandleObject,
                                                 id: HandleId,
                                                 desc: MutableHandle<PropertyDescriptor>)
                                                 -> bool {
    let handler = GetProxyHandler(proxy.get());
    if !InvokeGetOwnPropertyDescriptor(handler, cx, proxy, id, desc) {
        return false;
    }
    if !desc.obj.is_null() {
        return true;
    }

    rooted!(in(cx) let mut proto = ptr::null_mut());
    if !GetObjectProto(cx, proxy, proto.handle_mut()) {
        // FIXME(#11868) Should assign to desc.obj, desc.get() is a copy.
        desc.get().obj = ptr::null_mut();
        return true;
    }

    JS_GetPropertyDescriptorById(cx, proto.handle(), id, desc)
}

/// Defines an expando on the given `proxy`.
pub unsafe extern "C" fn define_property(cx: *mut JSContext,
                                         proxy: HandleObject,
                                         id: HandleId,
                                         desc: Handle<PropertyDescriptor>,
                                         result: *mut ObjectOpResult)
                                         -> bool {
    // FIXME: Workaround for https://github.com/rust-lang/rfcs/issues/718
    let setter: *const libc::c_void = mem::transmute(desc.get().setter);
    let setter_stub: unsafe extern fn(_, _, _, _, _) -> _ = JS_StrictPropertyStub;
    let setter_stub: *const libc::c_void = mem::transmute(setter_stub);
    if (desc.get().attrs & JSPROP_GETTER) != 0 && setter == setter_stub {
        (*result).code_ = JSErrNum::JSMSG_GETTER_ONLY as ::libc::uintptr_t;
        return true;
    }

    rooted!(in(cx) let mut expando = ptr::null_mut());
    ensure_expando_object(cx, proxy, expando.handle_mut());
    JS_DefinePropertyById(cx, expando.handle(), id, desc, result)
}

/// Deletes an expando off the given `proxy`.
pub unsafe extern "C" fn delete(cx: *mut JSContext,
                                proxy: HandleObject,
                                id: HandleId,
                                bp: *mut ObjectOpResult)
                                -> bool {
    rooted!(in(cx) let mut expando = ptr::null_mut());
    get_expando_object(proxy, expando.handle_mut());
    if expando.is_null() {
        (*bp).code_ = 0 /* OkCode */;
        return true;
    }

    delete_property_by_id(cx, expando.handle(), id, bp)
}

/// Controls whether the Extensible bit can be changed
pub unsafe extern "C" fn prevent_extensions(_cx: *mut JSContext,
                                            _proxy: HandleObject,
                                            result: *mut ObjectOpResult)
                                            -> bool {
    (*result).code_ = JSErrNum::JSMSG_CANT_PREVENT_EXTENSIONS as ::libc::uintptr_t;
    true
}

/// Reports whether the object is Extensible
pub unsafe extern "C" fn is_extensible(_cx: *mut JSContext,
                                       _proxy: HandleObject,
                                       succeeded: *mut bool)
                                       -> bool {
    *succeeded = true;
    true
}

/// If `proxy` (underneath any functionally-transparent wrapper proxies) has as
/// its `[[GetPrototypeOf]]` trap the ordinary `[[GetPrototypeOf]]` behavior
/// defined for ordinary objects, set `*is_ordinary` to true and store `obj`'s
/// prototype in `proto`.  Otherwise set `*isOrdinary` to false. In case of
/// error, both outparams have unspecified value.
///
/// This implementation always handles the case of the ordinary
/// `[[GetPrototypeOf]]` behavior. An alternative implementation will be
/// necessary for the Location object.
pub unsafe extern "C" fn get_prototype_if_ordinary(_: *mut JSContext,
                                                   proxy: HandleObject,
                                                   is_ordinary: *mut bool,
                                                   proto: MutableHandleObject)
                                                   -> bool {
    *is_ordinary = true;
    proto.set(GetStaticPrototype(proxy.get()));
    true
}

/// Get the expando object, or null if there is none.
pub unsafe fn get_expando_object(obj: HandleObject, expando: MutableHandleObject) {
    assert!(is_dom_proxy(obj.get()));
    let val = GetProxyExtra(obj.get(), JSPROXYSLOT_EXPANDO);
    expando.set(if val.is_undefined() {
        ptr::null_mut()
    } else {
        val.to_object()
    });
}

/// Get the expando object, or create it if it doesn't exist yet.
/// Fails on JSAPI failure.
pub unsafe fn ensure_expando_object(cx: *mut JSContext, obj: HandleObject, expando: MutableHandleObject) {
    assert!(is_dom_proxy(obj.get()));
    get_expando_object(obj, expando);
    if expando.is_null() {
        expando.set(JS_NewObjectWithGivenProto(cx, ptr::null_mut(), HandleObject::null()));
        assert!(!expando.is_null());

        SetProxyExtra(obj.get(), JSPROXYSLOT_EXPANDO, &ObjectValue(expando.get()));
    }
}

/// Set the property descriptor's object to `obj` and set it to enumerable,
/// and writable if `readonly` is true.
pub fn fill_property_descriptor(mut desc: MutableHandle<PropertyDescriptor>,
                                obj: *mut JSObject,
                                attrs: u32) {
    desc.obj = obj;
    desc.attrs = attrs;
    desc.getter = None;
    desc.setter = None;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2015-2016 Torrie Fischer <tdfischer@hackerbots.net>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use rustc_serialize::json::Json;
use rustc_serialize::json;
use matrix::json as mjson;
use matrix::model;

#[derive(Debug)]
pub enum MembershipAction {
    Join,
    Leave,
    Ban,
    Invite,
}

impl MembershipAction {
    pub fn from_str(s: &str) -> Self {
        match s {
            "join" => MembershipAction::Join,
            "leave" => MembershipAction::Leave,
            "ban" => MembershipAction::Ban,
            "invite" => MembershipAction::Invite,
            _ => panic!("unknown membership action {:?}", s)
        }
    }
}

#[derive(Debug)]
pub enum RoomEvent {
    CanonicalAlias(String),
    JoinRules(String),
    Membership(model::UserID, MembershipAction),
    HistoryVisibility(String),
    Create,
    Aliases(Vec<String>),
    Message(model::UserID, String),
    PowerLevels,
    Name(model::UserID, String),
    Avatar(model::UserID, String),
    Topic(model::UserID, String),
    Unknown(String, Json)
}

#[derive(Debug)]
pub struct TypingEvent {
    pub users: Vec<model::UserID>,
    pub room: model::RoomID,
}

#[derive(Debug)]
pub enum EventData {
    Room(model::RoomID, RoomEvent),
    Typing(TypingEvent),
    Presence(PresenceEvent),
    Unknown(String, Json),
    EndOfSync
}

impl EventData {
    pub fn type_str(&self) -> String {
        match self {
            &EventData::Room(_, RoomEvent::Message(_, _)) =>
                "m.room.message".to_string(),
            &EventData::Room(_, RoomEvent::CanonicalAlias(_)) =>
                "m.room.canonical_alias".to_string(),
            &EventData::Room(_, RoomEvent::JoinRules(_)) =>
                "m.room.join_rules".to_string(),
            &EventData::Room(_, RoomEvent::Membership(_, _)) =>
                "m.room.member".to_string(),
            &EventData::Room(_, RoomEvent::HistoryVisibility(_)) =>
                "m.room.history_visibility".to_string(),
            &EventData::Room(_, RoomEvent::Create )=>
                "m.room.create".to_string(),
            &EventData::Room(_, RoomEvent::Aliases(_)) =>
                "m.room.aliases".to_string(),
            &EventData::Room(_, RoomEvent::PowerLevels) =>
                "m.room.power_levels".to_string(),
            &EventData::Room(_, RoomEvent::Name(_, _)) =>
                "m.room.name".to_string(),
            &EventData::Room(_, RoomEvent::Avatar(_, _)) =>
                "m.room.avatar".to_string(),
            &EventData::Room(_, RoomEvent::Topic(_, _)) =>
                "m.room.topic".to_string(),
            &EventData::Room(_, RoomEvent::Unknown(ref unknown_type, _)) =>
                format!("m.room.{}", unknown_type),
            &EventData::Typing(_) =>
                "m.typing".to_string(),
            &EventData::Presence(_) =>
                "m.presence".to_string(),
            &EventData::Unknown(ref unknown_type, _) => unknown_type.clone(),
            &EventData::EndOfSync => panic!("EndOfSync is a special value")
        }
    }

    pub fn to_json(&self) -> json::Json {
        let mut ret = json::Object::new();
        match self {
            &EventData::Room(ref _id, ref evt) => {
                match evt {
                    &RoomEvent::Message(_, ref text) => {
                        ret.insert("msgtype".to_string(), json::Json::String("m.text".to_string()));
                        ret.insert("body".to_string(), json::Json::String(text.clone()));
                    },
                    _ => panic!("Can only serialize m.room.message events :(")
                }
            },
            _ => panic!("Can only serialize m.room.message events :(")
        }
        json::Json::Object(ret)
    }
}

#[derive(Debug)]
pub struct Event {
    pub id: Option<model::EventID>,
    pub data: EventData
}

#[derive(Debug)]
pub struct PresenceEvent {
    pub presence: String,
    pub user: model::UserID
}

impl Event {
    pub fn from_json(json: &Json) -> Self {
        let tokens: Vec<&str> = mjson::string(json, "type").split(".").collect();
        let id = match json.as_object().unwrap().get("event_id") {
            Some(i) => Some(model::EventID::from_str(i.as_string().unwrap())),
            None => None
        };
        if tokens[0] != "m" {
            Event {
                id: id,
                data: EventData::Unknown(json.as_object().unwrap().get("type").unwrap().as_string().unwrap().to_string(), json.clone()),
            }
        } else {
            Event {
                id: id,
                data: match tokens[1] {
                    "room" =>
                        Self::from_room_json(tokens[2], json),
                    "typing" =>
                        EventData::Typing(TypingEvent {
                            users: vec![],
                            room: model::RoomID::from_str(mjson::string(json, "room_id"))
                        }),
                    "presence" =>
                        EventData::Presence(PresenceEvent{
                            presence: mjson::string(json, "content.presence").to_string(),
                            user: model::UserID::from_str(mjson::string(json, "content.sender"))
                        }),
                    e =>
                        EventData::Unknown(e.to_string(), json.clone())
                }
            }
        }
    }

    fn from_room_json(event_type: &str, json: &Json) -> EventData {
        EventData::Room(
            model::RoomID::from_str(mjson::string(json, "room_id")),
            match event_type {
                "canonical_alias" =>
                    RoomEvent::CanonicalAlias(mjson::string(json, "content.alias").to_string()),
                "join_rules" => {
                        if json.find_path(&["content", "join_rules"]) == None {
                            RoomEvent::JoinRules(mjson::string(json, "content.join_rule").to_string())
                        } else {
                            RoomEvent::JoinRules(mjson::string(json, "content.join_rules").to_string())
                        }
                    },
                "member" =>
                    RoomEvent::Membership(model::UserID::from_str(mjson::string(json, "sender")), MembershipAction::from_str(mjson::string(json, "content.membership"))),
                "history_visibility" =>
                    RoomEvent::HistoryVisibility(mjson::string(json, "content.history_visibility").to_string()),
                "create" =>
                    RoomEvent::Create,
                "aliases" => {
                    let aliases = mjson::array(json, "content.aliases");
                    let mut alias_list: Vec<String> = vec![];
                    for alias in aliases {
                        alias_list.push(alias.as_string().unwrap().to_string());
                    }
                    RoomEvent::Aliases(alias_list)
                },
                "power_levels" =>
                    RoomEvent::PowerLevels,
                "message" =>
                    RoomEvent::Message(model::UserID::from_str(mjson::string(json, "sender")), mjson::string(json, "content.body").to_string()),
                "name" =>
                    RoomEvent::Name(model::UserID::from_str(mjson::string(json, "sender")), mjson::string(json, "content.name").to_string()),
                "topic" =>
                    RoomEvent::Topic(model::UserID::from_str(mjson::string(json, "sender")), mjson::string(json, "content.topic").to_string()),
                "avatar" =>
                    RoomEvent::Avatar(model::UserID::from_str(mjson::string(json, "sender")), mjson::string(json, "content.url").to_string()),
                unknown_type => RoomEvent::Unknown(unknown_type.to_string(), json.clone())
            }
        )
    }
}
<commit_msg>matrix: events: update schema for presence events<commit_after>/*
 * Copyright 2015-2016 Torrie Fischer <tdfischer@hackerbots.net>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use rustc_serialize::json::Json;
use rustc_serialize::json;
use matrix::json as mjson;
use matrix::model;

#[derive(Debug)]
pub enum MembershipAction {
    Join,
    Leave,
    Ban,
    Invite,
}

impl MembershipAction {
    pub fn from_str(s: &str) -> Self {
        match s {
            "join" => MembershipAction::Join,
            "leave" => MembershipAction::Leave,
            "ban" => MembershipAction::Ban,
            "invite" => MembershipAction::Invite,
            _ => panic!("unknown membership action {:?}", s)
        }
    }
}

#[derive(Debug)]
pub enum RoomEvent {
    CanonicalAlias(String),
    JoinRules(String),
    Membership(model::UserID, MembershipAction),
    HistoryVisibility(String),
    Create,
    Aliases(Vec<String>),
    Message(model::UserID, String),
    PowerLevels,
    Name(model::UserID, String),
    Avatar(model::UserID, String),
    Topic(model::UserID, String),
    Unknown(String, Json)
}

#[derive(Debug)]
pub struct TypingEvent {
    pub users: Vec<model::UserID>,
    pub room: model::RoomID,
}

#[derive(Debug)]
pub enum EventData {
    Room(model::RoomID, RoomEvent),
    Typing(TypingEvent),
    Presence(PresenceEvent),
    Unknown(String, Json),
    EndOfSync
}

impl EventData {
    pub fn type_str(&self) -> String {
        match self {
            &EventData::Room(_, RoomEvent::Message(_, _)) =>
                "m.room.message".to_string(),
            &EventData::Room(_, RoomEvent::CanonicalAlias(_)) =>
                "m.room.canonical_alias".to_string(),
            &EventData::Room(_, RoomEvent::JoinRules(_)) =>
                "m.room.join_rules".to_string(),
            &EventData::Room(_, RoomEvent::Membership(_, _)) =>
                "m.room.member".to_string(),
            &EventData::Room(_, RoomEvent::HistoryVisibility(_)) =>
                "m.room.history_visibility".to_string(),
            &EventData::Room(_, RoomEvent::Create )=>
                "m.room.create".to_string(),
            &EventData::Room(_, RoomEvent::Aliases(_)) =>
                "m.room.aliases".to_string(),
            &EventData::Room(_, RoomEvent::PowerLevels) =>
                "m.room.power_levels".to_string(),
            &EventData::Room(_, RoomEvent::Name(_, _)) =>
                "m.room.name".to_string(),
            &EventData::Room(_, RoomEvent::Avatar(_, _)) =>
                "m.room.avatar".to_string(),
            &EventData::Room(_, RoomEvent::Topic(_, _)) =>
                "m.room.topic".to_string(),
            &EventData::Room(_, RoomEvent::Unknown(ref unknown_type, _)) =>
                format!("m.room.{}", unknown_type),
            &EventData::Typing(_) =>
                "m.typing".to_string(),
            &EventData::Presence(_) =>
                "m.presence".to_string(),
            &EventData::Unknown(ref unknown_type, _) => unknown_type.clone(),
            &EventData::EndOfSync => panic!("EndOfSync is a special value")
        }
    }

    pub fn to_json(&self) -> json::Json {
        let mut ret = json::Object::new();
        match self {
            &EventData::Room(ref _id, ref evt) => {
                match evt {
                    &RoomEvent::Message(_, ref text) => {
                        ret.insert("msgtype".to_string(), json::Json::String("m.text".to_string()));
                        ret.insert("body".to_string(), json::Json::String(text.clone()));
                    },
                    _ => panic!("Can only serialize m.room.message events :(")
                }
            },
            _ => panic!("Can only serialize m.room.message events :(")
        }
        json::Json::Object(ret)
    }
}

#[derive(Debug)]
pub struct Event {
    pub id: Option<model::EventID>,
    pub data: EventData
}

#[derive(Debug)]
pub struct PresenceEvent {
    pub presence: String,
    pub user: model::UserID
}

impl Event {
    pub fn from_json(json: &Json) -> Self {
        let tokens: Vec<&str> = mjson::string(json, "type").split(".").collect();
        let id = match json.as_object().unwrap().get("event_id") {
            Some(i) => Some(model::EventID::from_str(i.as_string().unwrap())),
            None => None
        };
        if tokens[0] != "m" {
            Event {
                id: id,
                data: EventData::Unknown(json.as_object().unwrap().get("type").unwrap().as_string().unwrap().to_string(), json.clone()),
            }
        } else {
            Event {
                id: id,
                data: match tokens[1] {
                    "room" =>
                        Self::from_room_json(tokens[2], json),
                    "typing" =>
                        EventData::Typing(TypingEvent {
                            users: vec![],
                            room: model::RoomID::from_str(mjson::string(json, "room_id"))
                        }),
                    "presence" =>
                        EventData::Presence(PresenceEvent{
                            presence: mjson::string(json, "content.presence").to_string(),
                            user: model::UserID::from_str(mjson::string(json, "sender"))
                        }),
                    e =>
                        EventData::Unknown(e.to_string(), json.clone())
                }
            }
        }
    }

    fn from_room_json(event_type: &str, json: &Json) -> EventData {
        EventData::Room(
            model::RoomID::from_str(mjson::string(json, "room_id")),
            match event_type {
                "canonical_alias" =>
                    RoomEvent::CanonicalAlias(mjson::string(json, "content.alias").to_string()),
                "join_rules" => {
                        if json.find_path(&["content", "join_rules"]) == None {
                            RoomEvent::JoinRules(mjson::string(json, "content.join_rule").to_string())
                        } else {
                            RoomEvent::JoinRules(mjson::string(json, "content.join_rules").to_string())
                        }
                    },
                "member" =>
                    RoomEvent::Membership(model::UserID::from_str(mjson::string(json, "sender")), MembershipAction::from_str(mjson::string(json, "content.membership"))),
                "history_visibility" =>
                    RoomEvent::HistoryVisibility(mjson::string(json, "content.history_visibility").to_string()),
                "create" =>
                    RoomEvent::Create,
                "aliases" => {
                    let aliases = mjson::array(json, "content.aliases");
                    let mut alias_list: Vec<String> = vec![];
                    for alias in aliases {
                        alias_list.push(alias.as_string().unwrap().to_string());
                    }
                    RoomEvent::Aliases(alias_list)
                },
                "power_levels" =>
                    RoomEvent::PowerLevels,
                "message" =>
                    RoomEvent::Message(model::UserID::from_str(mjson::string(json, "sender")), mjson::string(json, "content.body").to_string()),
                "name" =>
                    RoomEvent::Name(model::UserID::from_str(mjson::string(json, "sender")), mjson::string(json, "content.name").to_string()),
                "topic" =>
                    RoomEvent::Topic(model::UserID::from_str(mjson::string(json, "sender")), mjson::string(json, "content.topic").to_string()),
                "avatar" =>
                    RoomEvent::Avatar(model::UserID::from_str(mjson::string(json, "sender")), mjson::string(json, "content.url").to_string()),
                unknown_type => RoomEvent::Unknown(unknown_type.to_string(), json.clone())
            }
        )
    }
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Macros to make our custom address types require a lot less repetitive code.

macro_rules! Addr {
    (($size:ty) $(pub)* enum $name:ident $($tail:tt)*) => {
        Addr! { @impl $name, $size }
    };
    (($size:ty) $(pub)* struct $name:ident $($tail:tt)*) => {
        Addr! { @impl $name, $size }
    };
    (@impl $ty:ident, $size:ty) => {

        impl Addr for $ty {
            type Repr = $size;
        }

        impl $crate::core::fmt::Debug for $ty {
            fn fmt(&self, f: &mut $crate::core::fmt::Formatter)
                  -> $crate::core::fmt::Result {
                write!(f, "{}({:#x})", stringify!($ty), self.0)
            }
        }

        impl $crate::core::convert::Into<$size> for $ty {
            #[inline] fn into(self) -> $size { self.0 }
        }

        impl $crate::core::convert::From<$size> for $ty {
            #[inline] fn from(n: $size) -> Self { $ty(n) }
        }

        impl<T> $crate::core::convert::From<*mut T> for $ty {
            #[inline] fn from(ptr: *mut T) -> Self { $ty(ptr as $size) }
        }

        impl<T> $crate::core::convert::From<*const T> for $ty {
            #[inline] fn from(ptr: *const T) -> Self { $ty(ptr as $size) }
        }

        impl $crate::core::ops::Deref for $ty {
            type Target = $size;
            #[inline] fn deref(&self) -> &Self::Target { &self.0 }
        }

        impl $ty {
            #[inline(always)]
            pub const fn as_mut_ptr<T>(&self) -> *mut T { self.0 as *mut _ }
            #[inline(always)]
            pub const fn as_ptr<T>(&self) -> *const T { self.0 as *const _ }
        }

        impl_ops! {
            Add, add, + for $ty, $size
            Sub, sub, - for $ty, $size
            Div, div, / for $ty, $size
            Mul, mul, * for $ty, $size
            Shl, shl, >> for $ty, $size
            Shr, shr, << for $ty, $size
            Rem, rem, % for $ty, $size
            BitAnd, bitand, & for $ty, $size
            BitOr, bitor, | for $ty, $size
            BitXor, bitxor, ^ for $ty, $size

        }

        impl_assign_ops! {
            AddAssign, add_assign, += for $ty, $size
            SubAssign, sub_assign, -= for $ty, $size
            DivAssign, div_assign, /= for $ty, $size
            MulAssign, mul_assign, *= for $ty, $size
            ShlAssign, shl_assign, >>= for $ty, $size
            ShrAssign, shr_assign, <<= for $ty, $size
            RemAssign, rem_assign, %= for $ty, $size
            BitAndAssign, bitand_assign, &= for $ty, $size
            BitOrAssign, bitor_assign, |= for $ty, $size
            BitXorAssign, bitxor_assign, ^= for $ty, $size
        }

        impl_fmt! {
            Binary for $ty
            Display for $ty
            Octal for $ty
            LowerHex for $ty
            UpperHex for $ty
        }

    }
}

macro_rules! impl_page_ops {
    ($($name:ident, $fun:ident, $op:tt for $ty:ident, $size:ty)*) => {$(
        impl $crate::core::ops::$name<$ty> for $ty {
            type Output = Self;

            #[inline] fn $fun(self, rhs: $ty) -> Self {
                expr!( $ty { number: expr!(self.number $op rhs.number) })
            }
        }
        impl $crate::core::ops::$name<$size> for $ty {
            type Output = $ty;

            #[inline] fn $fun(self, rhs: $size) -> Self {
                expr!($ty{ number: expr!(self.number $op rhs) })
            }
        }

        forward_ref_binop! {
            $name, $fun for $ty, $ty
        }
        forward_ref_binop! {
            $name, $fun for $ty, $size
        }
    )*}
}

macro_rules! impl_page_assign_ops {
    ($($name:ident, $fun:ident, $op:tt for $ty:ident, $size:ty)*) => {$(
        impl $crate::core::ops::$name<$ty> for $ty {

            #[inline] fn $fun(&mut self, rhs: $ty) {
                expr!(self.number $op rhs.number);
            }
        }
        impl $crate::core::ops::$name<$size> for $ty {

            #[inline] fn $fun(&mut self, rhs: $size) {
                expr!(self.number $op rhs);
            }
        }
    )*}
}

macro_rules! Page {
    (($addr:ty) $(pub)* struct $name:ident $($tail:tt)*) => {
        Page! { @impl $name, $addr }
    };
    (@impl $ty:ident, $addr:ty) => {

        impl Page for $ty {
            type Address = $addr;

            /// Create a new `Page` containing the given address.
            //  TODO: rewrite this as `up`/`down` using the page shift, instead.
            fn containing(addr: $addr) -> Self {
                use $crate::memory::PAGE_SHIFT;
                assert!( *addr < 0x0000_8000_0000_0000 ||
                         *addr >= 0xffff_8000_0000_0000
                       , "invalid address: 0x{:x}", addr );
                $ty { number: *addr >> PAGE_SHIFT as <Self::Address as Addr>::Repr }
            }

            /// Return the start virtual address of this page
            #[inline]
            fn base(&self) -> $addr {
                Self::Address::from(
                    self.number << PAGE_SHIFT as <Self::Address as Addr>::Repr )
            }

            #[inline] fn number(&self) -> usize {
                self.number as usize
            }
        }

        impl $crate::core::num::One for $ty {
            #[inline] fn one() -> Self {
                $ty { number:
                    <<$addr as Addr>::Repr as $crate::core::num::One>::one()
                }
            }
        }

        impl $crate::core::iter::Step for $ty {
            #[inline]
            fn step(&self, by: &Self) -> Option<Self> {
                self.number
                    .step(&by.number)
                    .map(|s| $ty { number: s})
            }

            #[inline]
            #[allow(trivial_numeric_casts)]
            fn steps_between(start: &$ty, end: &$ty, by: &$ty)
                            -> Option<usize> {
                use $crate::core::iter::Step;
                <<$addr as Addr>::Repr as Step>::steps_between( &start.number
                                                              , &end.number
                                                              , &by.number
                                                              )
            }
        }

        impl_page_ops! {
            Add, add, + for $ty, <<$ty as Page>::Address as Addr>::Repr
            Sub, sub, - for $ty, <<$ty as Page>::Address as Addr>::Repr
            Div, div, / for $ty, <<$ty as Page>::Address as Addr>::Repr
            Mul, mul, * for $ty, <<$ty as Page>::Address as Addr>::Repr
            Shl, shl, >> for $ty,<<$ty as Page>::Address as Addr>::Repr
            Shr, shr, << for $ty, <<$ty as Page>::Address as Addr>::Repr
            Rem, rem, % for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitAnd, bitand, & for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitOr, bitor, | for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitXor, bitxor, ^ for $ty, <<$ty as Page>::Address as Addr>::Repr

        }

        impl_page_assign_ops! {
            AddAssign, add_assign, += for $ty, <<$ty as Page>::Address as Addr>::Repr
            SubAssign, sub_assign, -= for $ty, <<$ty as Page>::Address as Addr>::Repr
            DivAssign, div_assign, /= for $ty, <<$ty as Page>::Address as Addr>::Repr
            MulAssign, mul_assign, *= for $ty, <<$ty as Page>::Address as Addr>::Repr
            ShlAssign, shl_assign, >>= for $ty, <<$ty as Page>::Address as Addr>::Repr
            ShrAssign, shr_assign, <<= for $ty, <<$ty as Page>::Address as Addr>::Repr
            RemAssign, rem_assign, %= for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitAndAssign, bitand_assign, &= for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitOrAssign, bitor_assign, |= for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitXorAssign, bitxor_assign, ^= for $ty, <<$ty as Page>::Address as Addr>::Repr
        }
    }
}
<commit_msg>[kernel] add function for checking if addresses are page aligned<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Macros to make our custom address types require a lot less repetitive code.

macro_rules! Addr {
    (($size:ty) $(pub)* enum $name:ident $($tail:tt)*) => {
        Addr! { @impl $name, $size }
    };
    (($size:ty) $(pub)* struct $name:ident $($tail:tt)*) => {
        Addr! { @impl $name, $size }
    };
    (@impl $ty:ident, $size:ty) => {

        impl $crate::core::fmt::Debug for $ty {
            fn fmt(&self, f: &mut $crate::core::fmt::Formatter)
                  -> $crate::core::fmt::Result {
                write!(f, "{}({:#x})", stringify!($ty), self.0)
            }
        }

        impl $crate::core::convert::Into<$size> for $ty {
            #[inline] fn into(self) -> $size { self.0 }
        }

        impl $crate::core::convert::From<$size> for $ty {
            #[inline] fn from(n: $size) -> Self { $ty(n) }
        }

        impl<T> $crate::core::convert::From<*mut T> for $ty {
            #[inline] fn from(ptr: *mut T) -> Self { $ty(ptr as $size) }
        }

        impl<T> $crate::core::convert::From<*const T> for $ty {
            #[inline] fn from(ptr: *const T) -> Self { $ty(ptr as $size) }
        }

        impl $crate::core::ops::Deref for $ty {
            type Target = $size;
            #[inline] fn deref(&self) -> &Self::Target { &self.0 }
        }

        impl $ty {
            #[inline(always)]
            pub const fn as_mut_ptr<T>(&self) -> *mut T { self.0 as *mut _ }

            #[inline(always)]
            pub const fn as_ptr<T>(&self) -> *const T { self.0 as *const _ }

            /// Returns true if this address is aligned on a page boundary.
            #[inline]
            pub fn is_page_aligned(&self) -> bool {
                **self % PAGE_SIZE as <Self as Addr>::Repr == 0 as <Self as Addr>::Repr
            }
        }

        impl_ops! {
            Add, add, + for $ty, $size
            Sub, sub, - for $ty, $size
            Div, div, / for $ty, $size
            Mul, mul, * for $ty, $size
            Shl, shl, >> for $ty, $size
            Shr, shr, << for $ty, $size
            Rem, rem, % for $ty, $size
            BitAnd, bitand, & for $ty, $size
            BitOr, bitor, | for $ty, $size
            BitXor, bitxor, ^ for $ty, $size

        }

        impl_assign_ops! {
            AddAssign, add_assign, += for $ty, $size
            SubAssign, sub_assign, -= for $ty, $size
            DivAssign, div_assign, /= for $ty, $size
            MulAssign, mul_assign, *= for $ty, $size
            ShlAssign, shl_assign, >>= for $ty, $size
            ShrAssign, shr_assign, <<= for $ty, $size
            RemAssign, rem_assign, %= for $ty, $size
            BitAndAssign, bitand_assign, &= for $ty, $size
            BitOrAssign, bitor_assign, |= for $ty, $size
            BitXorAssign, bitxor_assign, ^= for $ty, $size
        }

        impl_fmt! {
            Binary for $ty
            Display for $ty
            Octal for $ty
            LowerHex for $ty
            UpperHex for $ty
        }

        impl Addr for $ty {
            type Repr = $size;
        }

    }
}

macro_rules! impl_page_ops {
    ($($name:ident, $fun:ident, $op:tt for $ty:ident, $size:ty)*) => {$(
        impl $crate::core::ops::$name<$ty> for $ty {
            type Output = Self;

            #[inline] fn $fun(self, rhs: $ty) -> Self {
                expr!( $ty { number: expr!(self.number $op rhs.number) })
            }
        }
        impl $crate::core::ops::$name<$size> for $ty {
            type Output = $ty;

            #[inline] fn $fun(self, rhs: $size) -> Self {
                expr!($ty{ number: expr!(self.number $op rhs) })
            }
        }

        forward_ref_binop! {
            $name, $fun for $ty, $ty
        }
        forward_ref_binop! {
            $name, $fun for $ty, $size
        }
    )*}
}

macro_rules! impl_page_assign_ops {
    ($($name:ident, $fun:ident, $op:tt for $ty:ident, $size:ty)*) => {$(
        impl $crate::core::ops::$name<$ty> for $ty {

            #[inline] fn $fun(&mut self, rhs: $ty) {
                expr!(self.number $op rhs.number);
            }
        }
        impl $crate::core::ops::$name<$size> for $ty {

            #[inline] fn $fun(&mut self, rhs: $size) {
                expr!(self.number $op rhs);
            }
        }
    )*}
}

macro_rules! Page {
    (($addr:ty) $(pub)* struct $name:ident $($tail:tt)*) => {
        Page! { @impl $name, $addr }
    };
    (@impl $ty:ident, $addr:ty) => {

        impl Page for $ty {
            type Address = $addr;

            /// Create a new `Page` containing the given address.
            //  TODO: rewrite this as `up`/`down` using the page shift, instead.
            fn containing(addr: $addr) -> Self {
                use $crate::memory::PAGE_SHIFT;
                assert!( *addr < 0x0000_8000_0000_0000 ||
                         *addr >= 0xffff_8000_0000_0000
                       , "invalid address: 0x{:x}", addr );
                $ty { number: *addr >> PAGE_SHIFT as <Self::Address as Addr>::Repr }
            }

            /// Return the start virtual address of this page
            #[inline]
            fn base(&self) -> $addr {
                Self::Address::from(
                    self.number << PAGE_SHIFT as <Self::Address as Addr>::Repr )
            }

            #[inline] fn number(&self) -> usize {
                self.number as usize
            }
        }

        impl $crate::core::num::One for $ty {
            #[inline] fn one() -> Self {
                $ty { number:
                    <<$addr as Addr>::Repr as $crate::core::num::One>::one()
                }
            }
        }

        impl $crate::core::iter::Step for $ty {
            #[inline]
            fn step(&self, by: &Self) -> Option<Self> {
                self.number
                    .step(&by.number)
                    .map(|s| $ty { number: s})
            }

            #[inline]
            #[allow(trivial_numeric_casts)]
            fn steps_between(start: &$ty, end: &$ty, by: &$ty)
                            -> Option<usize> {
                use $crate::core::iter::Step;
                <<$addr as Addr>::Repr as Step>::steps_between( &start.number
                                                              , &end.number
                                                              , &by.number
                                                              )
            }
        }

        impl_page_ops! {
            Add, add, + for $ty, <<$ty as Page>::Address as Addr>::Repr
            Sub, sub, - for $ty, <<$ty as Page>::Address as Addr>::Repr
            Div, div, / for $ty, <<$ty as Page>::Address as Addr>::Repr
            Mul, mul, * for $ty, <<$ty as Page>::Address as Addr>::Repr
            Shl, shl, >> for $ty,<<$ty as Page>::Address as Addr>::Repr
            Shr, shr, << for $ty, <<$ty as Page>::Address as Addr>::Repr
            Rem, rem, % for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitAnd, bitand, & for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitOr, bitor, | for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitXor, bitxor, ^ for $ty, <<$ty as Page>::Address as Addr>::Repr

        }

        impl_page_assign_ops! {
            AddAssign, add_assign, += for $ty, <<$ty as Page>::Address as Addr>::Repr
            SubAssign, sub_assign, -= for $ty, <<$ty as Page>::Address as Addr>::Repr
            DivAssign, div_assign, /= for $ty, <<$ty as Page>::Address as Addr>::Repr
            MulAssign, mul_assign, *= for $ty, <<$ty as Page>::Address as Addr>::Repr
            ShlAssign, shl_assign, >>= for $ty, <<$ty as Page>::Address as Addr>::Repr
            ShrAssign, shr_assign, <<= for $ty, <<$ty as Page>::Address as Addr>::Repr
            RemAssign, rem_assign, %= for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitAndAssign, bitand_assign, &= for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitOrAssign, bitor_assign, |= for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitXorAssign, bitxor_assign, ^= for $ty, <<$ty as Page>::Address as Addr>::Repr
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::path::Path;

use common::ui::UI;
use hcore::crypto::SigKeyPair;
use hcore::env;
use hcore::package::ident;
use hcore::Error::InvalidOrigin;

use {AUTH_TOKEN_ENVVAR, ORIGIN_ENVVAR};
use analytics;
use command;
use config;
use error::Result;

pub fn start(ui: &mut UI, cache_path: &Path, analytics_path: &Path) -> Result<()> {
    let mut generated_origin = false;

    ui.br()?;
    ui.title("Habitat CLI Setup")?;
    ui.para("Welcome to hab setup. Let's get started.")?;

    ui.heading("Set up a default origin")?;
    ui.para(
        "Every package in Habitat belongs to an origin, which indicates the person or \
               organization responsible for maintaining that package. Each origin also has \
               a key used to cryptographically sign packages in that origin.",
    )?;
    ui.para(
        "Selecting a default origin tells package building operations such as 'hab \
                  pkg build' what key should be used to sign the packages produced. If you \
                  do not set a default origin now, you will have to tell package building \
                  commands each time what origin to use.",
    )?;
    ui.para(
        "For more information on origins and how they are used in building packages, \
               please consult the docs at https://www.habitat.sh/docs/create-packages-build/",
    )?;
    if ask_default_origin(ui)? {
        ui.br()?;
        ui.para(
            "Enter the name of your origin. If you plan to publish your packages \
                      publicly, we recommend that you select one that is not already in use \
                      on the Habitat build service found at https://bldr.habitat.sh/.",
        )?;
        ui.para( &format!("{}", InvalidOrigin) )?;
        let mut origin = prompt_origin(ui)?;

        while !ident::is_valid_origin_name(&origin) {
            ui.br()?;
            ui.fatal( &format!("{}", InvalidOrigin) )?;
            ui.br()?;

            origin = prompt_origin(ui)?;
        }
        write_cli_config_origin(&origin)?;
        ui.br()?;
        if is_origin_in_cache(&origin, cache_path) {
            ui.para(&format!(
                "You already have an origin key for {} created and \
                                   installed. Great work!",
                &origin
            ))?;
        } else {
            ui.heading("Create origin key pair")?;
            ui.para(&format!(
                "It doesn't look like you have a signing key for the origin \
                                `{}'. Without it, you won't be able to build new packages \
                                successfully.",
                &origin
            ))?;
            ui.para(
                "You can either create a new signing key now, or, if you are building \
                       packages for an origin that already exists, ask the owner to give \
                       you the signing key.",
            )?;
            ui.para(
                "For more information on the use of origin keys, please consult \
                          the documentation at \
                          https://www.habitat.sh/docs/concepts-keys/#origin-keys",
            )?;
            if ask_create_origin(ui, &origin)? {
                create_origin(ui, &origin, cache_path)?;
                generated_origin = true;
            } else {
                ui.para(&format!(
                    "You might want to create an origin key later with: \
                                       `hab origin key generate {}'",
                    &origin
                ))?;
            }
        }
    } else {
        ui.para("Okay, maybe another time.")?;
    }
    ui.heading("GitHub Access Token")?;
    ui.para(
        "While you can build and run Habitat packages without sharing them on the public \
               depot, doing so allows you to collaborate with the Habitat community. In \
               addition, it is how you can perform continuous deployment with Habitat.",
    )?;
    ui.para(
        "The depot uses GitHub authentication by personal access token with the \
                  user:email and read:org scopes (https://help.github.\
                  com/articles/creating-an-access-token-for-command-line-use/).",
    )?;
    ui.para(
        "If you would like to share your packages on the depot, please enter your GitHub \
               access token. Otherwise, just enter No.",
    )?;
    ui.para(
        "For more information on sharing packages on the depot, please read the \
          documentation at https://www.habitat.sh/docs/share-packages-overview/",
    )?;
    if ask_default_auth_token(ui)? {
        ui.br()?;
        ui.para("Enter your GitHub access token.")?;
        let auth_token = prompt_auth_token(ui)?;
        write_cli_config_auth_token(&auth_token)?;
    } else {
        ui.para("Okay, maybe another time.")?;
    }
    ui.heading("Analytics")?;
    ui.para(
        "The `hab` command-line tool will optionally send anonymous usage data to \
               Habitat's Google Analytics account. This is a strictly opt-in activity and \
               no tracking will occur unless you respond affirmatively to the question \
               below.",
    )?;
    ui.para(
        "We collect this data to help improve Habitat's user experience. For example, we \
               would like to know the category of tasks users are performing, and which ones \
               they are having trouble with (e.g. mistyping command line arguments).",
    )?;
    ui.para(
        "To see what kinds of data are sent and how they are anonymized, please read more \
               about our analytics here: https://www.habitat.sh/docs/about-analytics/",
    )?;
    if ask_enable_analytics(ui, analytics_path)? {
        opt_in_analytics(ui, analytics_path, generated_origin)?;
    } else {
        opt_out_analytics(ui, analytics_path)?;
    }
    ui.heading("CLI Setup Complete")?;
    ui.para("That's all for now. Thanks for using Habitat!")?;
    Ok(())
}

fn ask_default_origin(ui: &mut UI) -> Result<bool> {
    Ok(ui.prompt_yes_no("Set up a default origin?", Some(true))?)
}

fn ask_create_origin(ui: &mut UI, origin: &str) -> Result<bool> {
    Ok(ui.prompt_yes_no(
        &format!("Create an origin key for `{}'?", origin),
        Some(true),
    )?)
}

fn write_cli_config_origin(origin: &str) -> Result<()> {
    let mut config = config::load()?;
    config.origin = Some(origin.to_string());
    config::save(&config)
}

fn write_cli_config_auth_token(auth_token: &str) -> Result<()> {
    let mut config = config::load()?;
    config.auth_token = Some(auth_token.to_string());
    config::save(&config)
}

fn is_origin_in_cache(origin: &str, cache_path: &Path) -> bool {
    match SigKeyPair::get_latest_pair_for(origin, cache_path, None) {
        Ok(pair) => {
            match pair.secret() {
                Ok(_) => true,
                _ => false,
            }
        }
        _ => false,
    }
}

fn create_origin(ui: &mut UI, origin: &str, cache_path: &Path) -> Result<()> {
    let result = command::origin::key::generate::start(ui, &origin, cache_path);
    ui.br()?;
    result
}

fn prompt_origin(ui: &mut UI) -> Result<String> {
    let config = config::load()?;
    let default = match config.origin {
        Some(o) => {
            ui.para(&format!(
                "You already have a default origin set up as `{}', but feel \
                                free to change it if you wish.",
                &o
            ))?;
            Some(o)
        }
        None => env::var(ORIGIN_ENVVAR).or(env::var("USER")).ok(),
    };
    Ok(ui.prompt_ask(
        "Default origin name",
        default.as_ref().map(|x| &**x),
    )?)
}

fn ask_default_auth_token(ui: &mut UI) -> Result<bool> {
    Ok(ui.prompt_yes_no(
        "Set up a default GitHub access token?",
        Some(true),
    )?)
}

fn prompt_auth_token(ui: &mut UI) -> Result<String> {
    let config = config::load()?;
    let default = match config.auth_token {
        Some(o) => {
            ui.para(
                "You already have a default auth token set up, but feel free to change it \
                       if you wish.",
            )?;
            Some(o)
        }
        None => env::var(AUTH_TOKEN_ENVVAR).ok(),
    };
    Ok(ui.prompt_ask(
        "GitHub access token",
        default.as_ref().map(|x| &**x),
    )?)
}

fn ask_enable_analytics(ui: &mut UI, analytics_path: &Path) -> Result<bool> {
    let default = match analytics::is_opted_in(analytics_path) {
        Some(val) => Some(val),
        None => Some(true),
    };
    Ok(ui.prompt_yes_no("Enable analytics?", default)?)
}

fn opt_in_analytics(ui: &mut UI, analytics_path: &Path, generated_origin: bool) -> Result<()> {
    let result = analytics::opt_in(ui, analytics_path, generated_origin);
    ui.br()?;
    result
}

fn opt_out_analytics(ui: &mut UI, analytics_path: &Path) -> Result<()> {
    let result = analytics::opt_out(ui, analytics_path);
    ui.br()?;
    result
}
<commit_msg>Fix lint<commit_after>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::path::Path;

use common::ui::UI;
use hcore::crypto::SigKeyPair;
use hcore::env;
use hcore::package::ident;
use hcore::Error::InvalidOrigin;

use {AUTH_TOKEN_ENVVAR, ORIGIN_ENVVAR};
use analytics;
use command;
use config;
use error::Result;

pub fn start(ui: &mut UI, cache_path: &Path, analytics_path: &Path) -> Result<()> {
    let mut generated_origin = false;

    ui.br()?;
    ui.title("Habitat CLI Setup")?;
    ui.para("Welcome to hab setup. Let's get started.")?;

    ui.heading("Set up a default origin")?;
    ui.para(
        "Every package in Habitat belongs to an origin, which indicates the person or \
               organization responsible for maintaining that package. Each origin also has \
               a key used to cryptographically sign packages in that origin.",
    )?;
    ui.para(
        "Selecting a default origin tells package building operations such as 'hab \
                  pkg build' what key should be used to sign the packages produced. If you \
                  do not set a default origin now, you will have to tell package building \
                  commands each time what origin to use.",
    )?;
    ui.para(
        "For more information on origins and how they are used in building packages, \
               please consult the docs at https://www.habitat.sh/docs/create-packages-build/",
    )?;
    if ask_default_origin(ui)? {
        ui.br()?;
        ui.para(
            "Enter the name of your origin. If you plan to publish your packages \
                      publicly, we recommend that you select one that is not already in use \
                      on the Habitat build service found at https://bldr.habitat.sh/.",
        )?;
        ui.para(&format!("{}", InvalidOrigin))?;
        let mut origin = prompt_origin(ui)?;

        while !ident::is_valid_origin_name(&origin) {
            ui.br()?;
            ui.fatal(&format!("{}", InvalidOrigin))?;
            ui.br()?;

            origin = prompt_origin(ui)?;
        }
        write_cli_config_origin(&origin)?;
        ui.br()?;
        if is_origin_in_cache(&origin, cache_path) {
            ui.para(&format!(
                "You already have an origin key for {} created and \
                                   installed. Great work!",
                &origin
            ))?;
        } else {
            ui.heading("Create origin key pair")?;
            ui.para(&format!(
                "It doesn't look like you have a signing key for the origin \
                                `{}'. Without it, you won't be able to build new packages \
                                successfully.",
                &origin
            ))?;
            ui.para(
                "You can either create a new signing key now, or, if you are building \
                       packages for an origin that already exists, ask the owner to give \
                       you the signing key.",
            )?;
            ui.para(
                "For more information on the use of origin keys, please consult \
                          the documentation at \
                          https://www.habitat.sh/docs/concepts-keys/#origin-keys",
            )?;
            if ask_create_origin(ui, &origin)? {
                create_origin(ui, &origin, cache_path)?;
                generated_origin = true;
            } else {
                ui.para(&format!(
                    "You might want to create an origin key later with: \
                                       `hab origin key generate {}'",
                    &origin
                ))?;
            }
        }
    } else {
        ui.para("Okay, maybe another time.")?;
    }
    ui.heading("GitHub Access Token")?;
    ui.para(
        "While you can build and run Habitat packages without sharing them on the public \
               depot, doing so allows you to collaborate with the Habitat community. In \
               addition, it is how you can perform continuous deployment with Habitat.",
    )?;
    ui.para(
        "The depot uses GitHub authentication by personal access token with the \
                  user:email and read:org scopes (https://help.github.\
                  com/articles/creating-an-access-token-for-command-line-use/).",
    )?;
    ui.para(
        "If you would like to share your packages on the depot, please enter your GitHub \
               access token. Otherwise, just enter No.",
    )?;
    ui.para(
        "For more information on sharing packages on the depot, please read the \
          documentation at https://www.habitat.sh/docs/share-packages-overview/",
    )?;
    if ask_default_auth_token(ui)? {
        ui.br()?;
        ui.para("Enter your GitHub access token.")?;
        let auth_token = prompt_auth_token(ui)?;
        write_cli_config_auth_token(&auth_token)?;
    } else {
        ui.para("Okay, maybe another time.")?;
    }
    ui.heading("Analytics")?;
    ui.para(
        "The `hab` command-line tool will optionally send anonymous usage data to \
               Habitat's Google Analytics account. This is a strictly opt-in activity and \
               no tracking will occur unless you respond affirmatively to the question \
               below.",
    )?;
    ui.para(
        "We collect this data to help improve Habitat's user experience. For example, we \
               would like to know the category of tasks users are performing, and which ones \
               they are having trouble with (e.g. mistyping command line arguments).",
    )?;
    ui.para(
        "To see what kinds of data are sent and how they are anonymized, please read more \
               about our analytics here: https://www.habitat.sh/docs/about-analytics/",
    )?;
    if ask_enable_analytics(ui, analytics_path)? {
        opt_in_analytics(ui, analytics_path, generated_origin)?;
    } else {
        opt_out_analytics(ui, analytics_path)?;
    }
    ui.heading("CLI Setup Complete")?;
    ui.para("That's all for now. Thanks for using Habitat!")?;
    Ok(())
}

fn ask_default_origin(ui: &mut UI) -> Result<bool> {
    Ok(ui.prompt_yes_no("Set up a default origin?", Some(true))?)
}

fn ask_create_origin(ui: &mut UI, origin: &str) -> Result<bool> {
    Ok(ui.prompt_yes_no(
        &format!("Create an origin key for `{}'?", origin),
        Some(true),
    )?)
}

fn write_cli_config_origin(origin: &str) -> Result<()> {
    let mut config = config::load()?;
    config.origin = Some(origin.to_string());
    config::save(&config)
}

fn write_cli_config_auth_token(auth_token: &str) -> Result<()> {
    let mut config = config::load()?;
    config.auth_token = Some(auth_token.to_string());
    config::save(&config)
}

fn is_origin_in_cache(origin: &str, cache_path: &Path) -> bool {
    match SigKeyPair::get_latest_pair_for(origin, cache_path, None) {
        Ok(pair) => {
            match pair.secret() {
                Ok(_) => true,
                _ => false,
            }
        }
        _ => false,
    }
}

fn create_origin(ui: &mut UI, origin: &str, cache_path: &Path) -> Result<()> {
    let result = command::origin::key::generate::start(ui, &origin, cache_path);
    ui.br()?;
    result
}

fn prompt_origin(ui: &mut UI) -> Result<String> {
    let config = config::load()?;
    let default = match config.origin {
        Some(o) => {
            ui.para(&format!(
                "You already have a default origin set up as `{}', but feel \
                                free to change it if you wish.",
                &o
            ))?;
            Some(o)
        }
        None => env::var(ORIGIN_ENVVAR).or(env::var("USER")).ok(),
    };
    Ok(ui.prompt_ask(
        "Default origin name",
        default.as_ref().map(|x| &**x),
    )?)
}

fn ask_default_auth_token(ui: &mut UI) -> Result<bool> {
    Ok(ui.prompt_yes_no(
        "Set up a default GitHub access token?",
        Some(true),
    )?)
}

fn prompt_auth_token(ui: &mut UI) -> Result<String> {
    let config = config::load()?;
    let default = match config.auth_token {
        Some(o) => {
            ui.para(
                "You already have a default auth token set up, but feel free to change it \
                       if you wish.",
            )?;
            Some(o)
        }
        None => env::var(AUTH_TOKEN_ENVVAR).ok(),
    };
    Ok(ui.prompt_ask(
        "GitHub access token",
        default.as_ref().map(|x| &**x),
    )?)
}

fn ask_enable_analytics(ui: &mut UI, analytics_path: &Path) -> Result<bool> {
    let default = match analytics::is_opted_in(analytics_path) {
        Some(val) => Some(val),
        None => Some(true),
    };
    Ok(ui.prompt_yes_no("Enable analytics?", default)?)
}

fn opt_in_analytics(ui: &mut UI, analytics_path: &Path, generated_origin: bool) -> Result<()> {
    let result = analytics::opt_in(ui, analytics_path, generated_origin);
    ui.br()?;
    result
}

fn opt_out_analytics(ui: &mut UI, analytics_path: &Path) -> Result<()> {
    let result = analytics::opt_out(ui, analytics_path);
    ui.br()?;
    result
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::path::Path;

#[cfg(windows)]
use std::ptr;
#[cfg(windows)]
use user32;
#[cfg(windows)]
use widestring::WideCString;
#[cfg(windows)]
use winapi;
#[cfg(windows)]
use winreg::enums::{HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS, KEY_READ};
#[cfg(windows)]
use winreg::RegKey;

use common::ui::{UIReader, UIWriter, UI};
use hcore::crypto::SigKeyPair;
use hcore::env as henv;
use hcore::package::ident;
use hcore::Error::InvalidOrigin;

use analytics;
use command;
use config;
use error::Result;
use {AUTH_TOKEN_ENVVAR, CTL_SECRET_ENVVAR, ORIGIN_ENVVAR};

pub fn start(
    ui: &mut UI,
    cache_path: &Path,
    analytics_path: &Path,
    binlink_path: &Path,
) -> Result<()> {
    let mut generated_origin = false;

    ui.br()?;
    ui.title("Habitat CLI Setup")?;
    ui.para("Welcome to hab setup. Let's get started.")?;

    ui.heading("Set up a default origin")?;
    ui.para(
        "Every package in Habitat belongs to an origin, which indicates the person or \
         organization responsible for maintaining that package. Each origin also has \
         a key used to cryptographically sign packages in that origin.",
    )?;
    ui.para(
        "Selecting a default origin tells package building operations such as 'hab \
         pkg build' what key should be used to sign the packages produced. If you \
         do not set a default origin now, you will have to tell package building \
         commands each time what origin to use.",
    )?;
    ui.para(
        "For more information on origins and how they are used in building packages, \
         please consult the docs at https://www.habitat.sh/docs/create-packages-build/",
    )?;
    if ask_default_origin(ui)? {
        ui.br()?;
        ui.para(
            "Enter the name of your origin. If you plan to publish your packages \
             publicly, we recommend that you select one that is not already in use \
             on the Habitat build service found at https://bldr.habitat.sh/.",
        )?;
        ui.para(&format!(
            "Origins must begin with a lowercase letter or number. \
             Allowed characters include lowercase letters, numbers, _, -. \
             No more than 255 characters."
        ))?;
        let mut origin = prompt_origin(ui)?;

        while !ident::is_valid_origin_name(&origin) {
            ui.br()?;
            ui.fatal(&format!("{}", InvalidOrigin(origin)))?;
            ui.br()?;

            origin = prompt_origin(ui)?;
        }
        write_cli_config_origin(&origin)?;
        ui.br()?;
        if is_origin_in_cache(&origin, cache_path) {
            ui.para(&format!(
                "You already have an origin key for {} created and \
                 installed. Great work!",
                &origin
            ))?;
        } else {
            ui.heading("Create origin key pair")?;
            ui.para(&format!(
                "It doesn't look like you have a signing key for the origin \
                 `{}'. Without it, you won't be able to build new packages \
                 successfully.",
                &origin
            ))?;
            ui.para(
                "You can either create a new signing key now, or, if you are building \
                 packages for an origin that already exists, ask the owner to give \
                 you the signing key.",
            )?;
            ui.para(
                "For more information on the use of origin keys, please consult \
                 the documentation at \
                 https://www.habitat.sh/docs/concepts-keys/#origin-keys",
            )?;
            if ask_create_origin(ui, &origin)? {
                create_origin(ui, &origin, cache_path)?;
                generated_origin = true;
            } else {
                ui.para(&format!(
                    "You might want to create an origin key later with: \
                     `hab origin key generate {}'",
                    &origin
                ))?;
            }
        }
    } else {
        ui.para("Okay, maybe another time.")?;
    }
    ui.heading("Habitat Personal Access Token")?;
    ui.para(
        "While you can perform tasks like building and running Habitat packages without needing \
         to authenticate with Builder, some operations like uploading your packages to \
         Builder, or checking status of your build jobs from the Habitat client will require \
         you to use an access token.",
    )?;
    ui.para(
        "The Habitat Personal Access Token can be generated via the Builder  \
         Profile page (https://bldr.habitat.sh/#/profile). Once you have \
         generated your token, you can enter it here.",
    )?;
    ui.para(
        "If you would like to save your token for use by the Habitat client, please enter \
         your access token. Otherwise, just enter No.",
    )?;
    ui.para(
        "For more information on using Builder, please read the \
         documentation at https://www.habitat.sh/docs/using-builder/",
    )?;
    if ask_default_auth_token(ui)? {
        ui.br()?;
        ui.para("Enter your Habitat Personal Access Token.")?;
        let auth_token = prompt_auth_token(ui)?;
        write_cli_config_auth_token(auth_token)?;
    } else {
        ui.para("Okay, maybe another time.")?;
    }
    if ask_default_ctl_secret(ui)? {
        ui.br()?;
        ui.para("Enter your Habitat Supervisor CtlGateway secret.")?;
        let ctl_secret = prompt_ctl_secret(ui)?;
        write_cli_config_ctl_secret(ctl_secret)?;
    } else {
        ui.para("Okay, maybe another time.")?;
    }
    if cfg!(windows) {
        ui.heading("Habitat Binlink Path")?;
        ui.para(
            "The `hab` command-line tool can create binlinks for package binaries \
             in the 'PATH' when using the 'pkg binlink' or 'pkg install --binlink' \
             commands. By default, Habitat will create these binlinks in the '/hab/bin' \
             directory. This directory will always be included in your 'PATH' when \
             inside a Studio environment. However, you will want this directory on your \
             machine's persistent 'PATH' in order to access binlinks outside of a Studio.",
        )?;
        if ui.prompt_yes_no("Add binlink directory to PATH?", Some(true))? {
            set_binlink_path(&binlink_path)?;
            ui.para(&format!(
                "{} has been added to your path. You will need to open a \
                 new console window for this added entry to take effect.",
                binlink_path.display()
            ))?;
        } else {
            ui.para("Okay, maybe another time.")?;
        }
    }
    ui.heading("Analytics")?;
    ui.para(
        "The `hab` command-line tool will optionally send anonymous usage data to \
         Habitat's Google Analytics account. This is a strictly opt-in activity and \
         no tracking will occur unless you respond affirmatively to the question \
         below.",
    )?;
    ui.para(
        "We collect this data to help improve Habitat's user experience. For example, we \
         would like to know the category of tasks users are performing, and which ones \
         they are having trouble with (e.g. mistyping command line arguments).",
    )?;
    ui.para(
        "To see what kinds of data are sent and how they are anonymized, please read more \
         about our analytics here: https://www.habitat.sh/docs/about-analytics/",
    )?;
    if ask_enable_analytics(ui, analytics_path)? {
        opt_in_analytics(ui, analytics_path, generated_origin)?;
    } else {
        opt_out_analytics(ui, analytics_path)?;
    }
    ui.heading("CLI Setup Complete")?;
    ui.para("That's all for now. Thanks for using Habitat!")?;
    Ok(())
}

fn ask_default_origin(ui: &mut UI) -> Result<bool> {
    Ok(ui.prompt_yes_no("Set up a default origin?", Some(true))?)
}

fn ask_create_origin(ui: &mut UI, origin: &str) -> Result<bool> {
    Ok(ui.prompt_yes_no(
        &format!("Create an origin key for `{}'?", origin),
        Some(true),
    )?)
}

fn write_cli_config_origin<T>(origin: T) -> Result<()>
where
    T: ToString,
{
    let mut config = config::load()?;
    config.origin = Some(origin.to_string());
    config::save(&config)
}

fn write_cli_config_auth_token<T>(auth_token: T) -> Result<()>
where
    T: ToString,
{
    let mut config = config::load()?;
    config.auth_token = Some(auth_token.to_string());
    config::save(&config)
}

fn write_cli_config_ctl_secret<T>(value: T) -> Result<()>
where
    T: ToString,
{
    let mut config = config::load()?;
    config.ctl_secret = Some(value.to_string());
    config::save(&config)
}

fn is_origin_in_cache(origin: &str, cache_path: &Path) -> bool {
    match SigKeyPair::get_latest_pair_for(origin, cache_path, None) {
        Ok(pair) => match pair.secret() {
            Ok(_) => true,
            _ => false,
        },
        _ => false,
    }
}

fn create_origin(ui: &mut UI, origin: &str, cache_path: &Path) -> Result<()> {
    let result = command::origin::key::generate::start(ui, &origin, cache_path);
    ui.br()?;
    result
}

fn prompt_origin(ui: &mut UI) -> Result<String> {
    let config = config::load()?;
    let default = match config.origin {
        Some(o) => {
            ui.para(&format!(
                "You already have a default origin set up as `{}', but feel \
                 free to change it if you wish.",
                &o
            ))?;
            Some(o)
        }
        None => henv::var(ORIGIN_ENVVAR).or(henv::var("USER")).ok(),
    };
    Ok(ui.prompt_ask("Default origin name", default.as_ref().map(|x| &**x))?)
}

fn ask_default_auth_token(ui: &mut UI) -> Result<bool> {
    Ok(ui.prompt_yes_no(
        "Set up a default Habitat personal access token?",
        Some(true),
    )?)
}

fn ask_default_ctl_secret(ui: &mut UI) -> Result<bool> {
    Ok(ui.prompt_yes_no(
        "Set up a default Habitat Supervisor CtlGateway secret?",
        Some(true),
    )?)
}

fn prompt_auth_token(ui: &mut UI) -> Result<String> {
    let config = config::load()?;
    let default = match config.auth_token {
        Some(o) => {
            ui.para(
                "You already have a default auth token set up, but feel free to change it \
                 if you wish.",
            )?;
            Some(o)
        }
        None => henv::var(AUTH_TOKEN_ENVVAR).ok(),
    };
    Ok(ui.prompt_ask(
        "Habitat personal access token",
        default.as_ref().map(|x| &**x),
    )?)
}

fn prompt_ctl_secret(ui: &mut UI) -> Result<String> {
    let config = config::load()?;
    let default = match config.ctl_secret {
        Some(o) => {
            ui.para(
                "You already have a default CtlGateway secret set up, but feel free to change it
                if you wish.",
            )?;
            Some(o)
        }
        None => henv::var(CTL_SECRET_ENVVAR).ok(),
    };
    Ok(ui.prompt_ask(
        "Habitat Supervisor CtlGateway secret",
        default.as_ref().map(|x| &**x),
    )?)
}

fn ask_enable_analytics(ui: &mut UI, analytics_path: &Path) -> Result<bool> {
    let default = match analytics::is_opted_in(analytics_path) {
        Some(val) => Some(val),
        None => Some(true),
    };
    Ok(ui.prompt_yes_no("Enable analytics?", default)?)
}

fn opt_in_analytics(ui: &mut UI, analytics_path: &Path, generated_origin: bool) -> Result<()> {
    let result = analytics::opt_in(ui, analytics_path, generated_origin);
    ui.br()?;
    result
}

fn opt_out_analytics(ui: &mut UI, analytics_path: &Path) -> Result<()> {
    let result = analytics::opt_out(ui, analytics_path);
    ui.br()?;
    result
}

#[cfg(windows)]
fn binlink_is_on_path(binlink_path: &Path) -> bool {
    match RegKey::predef(HKEY_LOCAL_MACHINE).open_subkey_with_flags(
        r"System\CurrentControlSet\Control\Session Manager\Environment",
        KEY_READ,
    ) {
        Ok(env) => {
            let path: String = env
                .get_value("path")
                .expect("could not find a machine PATH");
            env::split_paths(&path).any(|p| p == binlink_path)
        }
        _ => false,
    }
}

/// this sets the permanent machine PATH and not
/// the path of this process. These are maintained
/// in the Windows registry
#[cfg(windows)]
fn set_binlink_path(binlink_path: &Path) -> Result<()> {
    if !binlink_is_on_path(binlink_path) {
        let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
        let env = hklm.open_subkey_with_flags(
            r"System\CurrentControlSet\Control\Session Manager\Environment",
            KEY_ALL_ACCESS,
        )?;
        let path: String = env.get_value("path")?;
        let new_paths = [binlink_path, Path::new(&path)];
        env.set_value(
            "path",
            &env::join_paths(new_paths.iter())?.to_str().unwrap(),
        )?;

        // After altering the machine environment, we must broadcast
        // a WM_SETTINGCHANGE message to all windows so the user
        // will not need to sign out/in for the new path to take effect
        unsafe {
            user32::SendMessageTimeoutW(
                winapi::HWND_BROADCAST,
                winapi::WM_SETTINGCHANGE,
                0,
                WideCString::from_str("Environment").unwrap().as_ptr() as winapi::LPARAM,
                winapi::SMTO_ABORTIFHUNG,
                5000,
                ptr::null_mut(),
            );
        }
    }
    Ok(())
}

#[cfg(not(windows))]
fn set_binlink_path(_binlink_path: &Path) -> Result<()> {
    unreachable!()
}
<commit_msg>Add std::env for windows only<commit_after>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[cfg(windows)]
use std::env;
use std::path::Path;

#[cfg(windows)]
use std::ptr;
#[cfg(windows)]
use user32;
#[cfg(windows)]
use widestring::WideCString;
#[cfg(windows)]
use winapi;
#[cfg(windows)]
use winreg::enums::{HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS, KEY_READ};
#[cfg(windows)]
use winreg::RegKey;

use common::ui::{UIReader, UIWriter, UI};
use hcore::crypto::SigKeyPair;
use hcore::env as henv;
use hcore::package::ident;
use hcore::Error::InvalidOrigin;

use analytics;
use command;
use config;
use error::Result;
use {AUTH_TOKEN_ENVVAR, CTL_SECRET_ENVVAR, ORIGIN_ENVVAR};

pub fn start(
    ui: &mut UI,
    cache_path: &Path,
    analytics_path: &Path,
    binlink_path: &Path,
) -> Result<()> {
    let mut generated_origin = false;

    ui.br()?;
    ui.title("Habitat CLI Setup")?;
    ui.para("Welcome to hab setup. Let's get started.")?;

    ui.heading("Set up a default origin")?;
    ui.para(
        "Every package in Habitat belongs to an origin, which indicates the person or \
         organization responsible for maintaining that package. Each origin also has \
         a key used to cryptographically sign packages in that origin.",
    )?;
    ui.para(
        "Selecting a default origin tells package building operations such as 'hab \
         pkg build' what key should be used to sign the packages produced. If you \
         do not set a default origin now, you will have to tell package building \
         commands each time what origin to use.",
    )?;
    ui.para(
        "For more information on origins and how they are used in building packages, \
         please consult the docs at https://www.habitat.sh/docs/create-packages-build/",
    )?;
    if ask_default_origin(ui)? {
        ui.br()?;
        ui.para(
            "Enter the name of your origin. If you plan to publish your packages \
             publicly, we recommend that you select one that is not already in use \
             on the Habitat build service found at https://bldr.habitat.sh/.",
        )?;
        ui.para(&format!(
            "Origins must begin with a lowercase letter or number. \
             Allowed characters include lowercase letters, numbers, _, -. \
             No more than 255 characters."
        ))?;
        let mut origin = prompt_origin(ui)?;

        while !ident::is_valid_origin_name(&origin) {
            ui.br()?;
            ui.fatal(&format!("{}", InvalidOrigin(origin)))?;
            ui.br()?;

            origin = prompt_origin(ui)?;
        }
        write_cli_config_origin(&origin)?;
        ui.br()?;
        if is_origin_in_cache(&origin, cache_path) {
            ui.para(&format!(
                "You already have an origin key for {} created and \
                 installed. Great work!",
                &origin
            ))?;
        } else {
            ui.heading("Create origin key pair")?;
            ui.para(&format!(
                "It doesn't look like you have a signing key for the origin \
                 `{}'. Without it, you won't be able to build new packages \
                 successfully.",
                &origin
            ))?;
            ui.para(
                "You can either create a new signing key now, or, if you are building \
                 packages for an origin that already exists, ask the owner to give \
                 you the signing key.",
            )?;
            ui.para(
                "For more information on the use of origin keys, please consult \
                 the documentation at \
                 https://www.habitat.sh/docs/concepts-keys/#origin-keys",
            )?;
            if ask_create_origin(ui, &origin)? {
                create_origin(ui, &origin, cache_path)?;
                generated_origin = true;
            } else {
                ui.para(&format!(
                    "You might want to create an origin key later with: \
                     `hab origin key generate {}'",
                    &origin
                ))?;
            }
        }
    } else {
        ui.para("Okay, maybe another time.")?;
    }
    ui.heading("Habitat Personal Access Token")?;
    ui.para(
        "While you can perform tasks like building and running Habitat packages without needing \
         to authenticate with Builder, some operations like uploading your packages to \
         Builder, or checking status of your build jobs from the Habitat client will require \
         you to use an access token.",
    )?;
    ui.para(
        "The Habitat Personal Access Token can be generated via the Builder  \
         Profile page (https://bldr.habitat.sh/#/profile). Once you have \
         generated your token, you can enter it here.",
    )?;
    ui.para(
        "If you would like to save your token for use by the Habitat client, please enter \
         your access token. Otherwise, just enter No.",
    )?;
    ui.para(
        "For more information on using Builder, please read the \
         documentation at https://www.habitat.sh/docs/using-builder/",
    )?;
    if ask_default_auth_token(ui)? {
        ui.br()?;
        ui.para("Enter your Habitat Personal Access Token.")?;
        let auth_token = prompt_auth_token(ui)?;
        write_cli_config_auth_token(auth_token)?;
    } else {
        ui.para("Okay, maybe another time.")?;
    }
    if ask_default_ctl_secret(ui)? {
        ui.br()?;
        ui.para("Enter your Habitat Supervisor CtlGateway secret.")?;
        let ctl_secret = prompt_ctl_secret(ui)?;
        write_cli_config_ctl_secret(ctl_secret)?;
    } else {
        ui.para("Okay, maybe another time.")?;
    }
    if cfg!(windows) {
        ui.heading("Habitat Binlink Path")?;
        ui.para(
            "The `hab` command-line tool can create binlinks for package binaries \
             in the 'PATH' when using the 'pkg binlink' or 'pkg install --binlink' \
             commands. By default, Habitat will create these binlinks in the '/hab/bin' \
             directory. This directory will always be included in your 'PATH' when \
             inside a Studio environment. However, you will want this directory on your \
             machine's persistent 'PATH' in order to access binlinks outside of a Studio.",
        )?;
        if ui.prompt_yes_no("Add binlink directory to PATH?", Some(true))? {
            set_binlink_path(&binlink_path)?;
            ui.para(&format!(
                "{} has been added to your path. You will need to open a \
                 new console window for this added entry to take effect.",
                binlink_path.display()
            ))?;
        } else {
            ui.para("Okay, maybe another time.")?;
        }
    }
    ui.heading("Analytics")?;
    ui.para(
        "The `hab` command-line tool will optionally send anonymous usage data to \
         Habitat's Google Analytics account. This is a strictly opt-in activity and \
         no tracking will occur unless you respond affirmatively to the question \
         below.",
    )?;
    ui.para(
        "We collect this data to help improve Habitat's user experience. For example, we \
         would like to know the category of tasks users are performing, and which ones \
         they are having trouble with (e.g. mistyping command line arguments).",
    )?;
    ui.para(
        "To see what kinds of data are sent and how they are anonymized, please read more \
         about our analytics here: https://www.habitat.sh/docs/about-analytics/",
    )?;
    if ask_enable_analytics(ui, analytics_path)? {
        opt_in_analytics(ui, analytics_path, generated_origin)?;
    } else {
        opt_out_analytics(ui, analytics_path)?;
    }
    ui.heading("CLI Setup Complete")?;
    ui.para("That's all for now. Thanks for using Habitat!")?;
    Ok(())
}

fn ask_default_origin(ui: &mut UI) -> Result<bool> {
    Ok(ui.prompt_yes_no("Set up a default origin?", Some(true))?)
}

fn ask_create_origin(ui: &mut UI, origin: &str) -> Result<bool> {
    Ok(ui.prompt_yes_no(
        &format!("Create an origin key for `{}'?", origin),
        Some(true),
    )?)
}

fn write_cli_config_origin<T>(origin: T) -> Result<()>
where
    T: ToString,
{
    let mut config = config::load()?;
    config.origin = Some(origin.to_string());
    config::save(&config)
}

fn write_cli_config_auth_token<T>(auth_token: T) -> Result<()>
where
    T: ToString,
{
    let mut config = config::load()?;
    config.auth_token = Some(auth_token.to_string());
    config::save(&config)
}

fn write_cli_config_ctl_secret<T>(value: T) -> Result<()>
where
    T: ToString,
{
    let mut config = config::load()?;
    config.ctl_secret = Some(value.to_string());
    config::save(&config)
}

fn is_origin_in_cache(origin: &str, cache_path: &Path) -> bool {
    match SigKeyPair::get_latest_pair_for(origin, cache_path, None) {
        Ok(pair) => match pair.secret() {
            Ok(_) => true,
            _ => false,
        },
        _ => false,
    }
}

fn create_origin(ui: &mut UI, origin: &str, cache_path: &Path) -> Result<()> {
    let result = command::origin::key::generate::start(ui, &origin, cache_path);
    ui.br()?;
    result
}

fn prompt_origin(ui: &mut UI) -> Result<String> {
    let config = config::load()?;
    let default = match config.origin {
        Some(o) => {
            ui.para(&format!(
                "You already have a default origin set up as `{}', but feel \
                 free to change it if you wish.",
                &o
            ))?;
            Some(o)
        }
        None => henv::var(ORIGIN_ENVVAR).or(henv::var("USER")).ok(),
    };
    Ok(ui.prompt_ask("Default origin name", default.as_ref().map(|x| &**x))?)
}

fn ask_default_auth_token(ui: &mut UI) -> Result<bool> {
    Ok(ui.prompt_yes_no(
        "Set up a default Habitat personal access token?",
        Some(true),
    )?)
}

fn ask_default_ctl_secret(ui: &mut UI) -> Result<bool> {
    Ok(ui.prompt_yes_no(
        "Set up a default Habitat Supervisor CtlGateway secret?",
        Some(true),
    )?)
}

fn prompt_auth_token(ui: &mut UI) -> Result<String> {
    let config = config::load()?;
    let default = match config.auth_token {
        Some(o) => {
            ui.para(
                "You already have a default auth token set up, but feel free to change it \
                 if you wish.",
            )?;
            Some(o)
        }
        None => henv::var(AUTH_TOKEN_ENVVAR).ok(),
    };
    Ok(ui.prompt_ask(
        "Habitat personal access token",
        default.as_ref().map(|x| &**x),
    )?)
}

fn prompt_ctl_secret(ui: &mut UI) -> Result<String> {
    let config = config::load()?;
    let default = match config.ctl_secret {
        Some(o) => {
            ui.para(
                "You already have a default CtlGateway secret set up, but feel free to change it
                if you wish.",
            )?;
            Some(o)
        }
        None => henv::var(CTL_SECRET_ENVVAR).ok(),
    };
    Ok(ui.prompt_ask(
        "Habitat Supervisor CtlGateway secret",
        default.as_ref().map(|x| &**x),
    )?)
}

fn ask_enable_analytics(ui: &mut UI, analytics_path: &Path) -> Result<bool> {
    let default = match analytics::is_opted_in(analytics_path) {
        Some(val) => Some(val),
        None => Some(true),
    };
    Ok(ui.prompt_yes_no("Enable analytics?", default)?)
}

fn opt_in_analytics(ui: &mut UI, analytics_path: &Path, generated_origin: bool) -> Result<()> {
    let result = analytics::opt_in(ui, analytics_path, generated_origin);
    ui.br()?;
    result
}

fn opt_out_analytics(ui: &mut UI, analytics_path: &Path) -> Result<()> {
    let result = analytics::opt_out(ui, analytics_path);
    ui.br()?;
    result
}

#[cfg(windows)]
fn binlink_is_on_path(binlink_path: &Path) -> bool {
    match RegKey::predef(HKEY_LOCAL_MACHINE).open_subkey_with_flags(
        r"System\CurrentControlSet\Control\Session Manager\Environment",
        KEY_READ,
    ) {
        Ok(env) => {
            let path: String = env
                .get_value("path")
                .expect("could not find a machine PATH");
            env::split_paths(&path).any(|p| p == binlink_path)
        }
        _ => false,
    }
}

/// this sets the permanent machine PATH and not
/// the path of this process. These are maintained
/// in the Windows registry
#[cfg(windows)]
fn set_binlink_path(binlink_path: &Path) -> Result<()> {
    if !binlink_is_on_path(binlink_path) {
        let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
        let env = hklm.open_subkey_with_flags(
            r"System\CurrentControlSet\Control\Session Manager\Environment",
            KEY_ALL_ACCESS,
        )?;
        let path: String = env.get_value("path")?;
        let new_paths = [binlink_path, Path::new(&path)];
        env.set_value(
            "path",
            &env::join_paths(new_paths.iter())?.to_str().unwrap(),
        )?;

        // After altering the machine environment, we must broadcast
        // a WM_SETTINGCHANGE message to all windows so the user
        // will not need to sign out/in for the new path to take effect
        unsafe {
            user32::SendMessageTimeoutW(
                winapi::HWND_BROADCAST,
                winapi::WM_SETTINGCHANGE,
                0,
                WideCString::from_str("Environment").unwrap().as_ptr() as winapi::LPARAM,
                winapi::SMTO_ABORTIFHUNG,
                5000,
                ptr::null_mut(),
            );
        }
    }
    Ok(())
}

#[cfg(not(windows))]
fn set_binlink_path(_binlink_path: &Path) -> Result<()> {
    unreachable!()
}
<|endoftext|>