prey/prey-node-client

View on GitHub
lib/conf/gui/linux/prey-config3.py

Summary

Maintainability
F
1 wk
Test Coverage
#!/usr/bin/env python3
################################################
# Prey Configurator for Linux
# By Tomas Pollak
# (c) 2012-2014 - Fork Ltd. (usefork.com)
################################################

# if having trouble with the GTK theme as root, do this:
# sudo ln -s ~/.themes/ /root/.themes

################################################
# base includes
################################################

APP_NAME = "prey-config"
LANG_PATH = "lang"

import sys
import os
import re
import json
import gettext
import shlex
from subprocess import Popen, PIPE, STDOUT

import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from gi.repository import Gdk

# locale.setlocale(locale.LC_ALL, '')
# locale.bindtextdomain(app_name, lang_path)
gettext.bindtextdomain(APP_NAME, LANG_PATH)
gettext.textdomain(APP_NAME)
_ = gettext.gettext

################################################
# vars and such
################################################

FORCE_CONFIG = len(sys.argv) > 1 and (sys.argv[1] == "-f" or sys.argv[1] == "--force")

OUT = STDOUT  # None

SCRIPT_PATH = os.sys.path[0]
PACKAGE_PATH = SCRIPT_PATH + "/../../../.."
PREY_BIN = PACKAGE_PATH + "/bin/prey"
PREY_CONFIG = PREY_BIN + " config"

PACKAGE_JSON = open(PACKAGE_PATH + "/package.json", "r")
PACKAGE_INFO = json.loads(PACKAGE_JSON.read())
VERSION = PACKAGE_INFO["version"]

PAGES = ["control_panel_options", "new_user", "existing_user", "existing_device"]
EMAIL_REGEX = "^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,7}|[0-9]{1,3})(\\]?)$"


class PreyConfigurator(object):

    ################################################
    # helper functions
    ################################################

    def get(self, name):
        return self.root.get_object(name)

    def text(self, name):
        return self.get(name).get_text()

    ################################################
    # validations
    ################################################

    def valid_email_regex(self, string):
        if len(string) > 7:
            if re.match(EMAIL_REGEX, string) is not None:
                return True
        return False

    def validate_email(self, email_field):
        if not self.valid_email_regex(self.text(email_field)):
            self.show_alert(
                _("Invalid email"),
                _("Please make sure the email address you typed is valid."),
            )
            return False

        return True

    def validate_password(self, password_field):
        if len(self.text(password_field)) < 6:
            self.show_alert(
                _("Bad password"), _("Password should contain at least 6 chars.")
            )
            return False

        return True

    def validate_existing_user_fields(self):
        if self.text("existing_email") == "":
            self.show_alert(_("Empty email!"), _("Please type in your email."))
            return False
        if self.text("existing_password") == "":
            self.show_alert(_("Empty password!"), _("Please type in your password."))
            return False

        return True

    def validate_new_user_fields(self):
        if self.text("user_name") == "":
            self.show_alert(_("Empty name!"), _("Please type in your name."))
            return False
        if self.text("email") == "":
            self.show_alert(_("Empty email!"), _("Please type in your email."))
            return False
        if self.text("password") == "":
            self.show_alert(_("Empty password!"), _("Please type in your password."))
            return False
        elif self.text("password") != self.text("password_confirm"):
            self.show_alert(
                _("Passwords don't match"), _("Please make sure both passwords match.")
            )
            return False
        if not self.get("check_terms_conds").get_active():
            self.show_alert(
                _("Error"),
                _(
                    "You need to accept the Terms & Conditions and Privacy Policy to continue"
                ),
            )
            return False
        if not self.get("check_age").get_active():
            self.show_alert(
                _("Error"), _("You must be older than 16 years old to use Prey")
            )
            return False
        return True

    ################################################
    # dialogs
    ################################################

    def show_alert(self, title, message, quit=False):
        dialog = Gtk.MessageDialog(
            parent=None,
            flags=Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
            type=Gtk.MessageType.INFO,
            buttons=Gtk.ButtonsType.OK,
            message_format=message,
        )
        dialog.set_title(title)
        if quit is True:
            dialog.connect("response", lambda dialog, response: Gtk.main_quit())
        else:
            dialog.connect("response", lambda dialog, response: dialog.destroy())
        self.center_dialog(dialog)
        dialog.show()

    def show_about(self):
        dialog = self.get("about_prey_config")
        self.center_dialog(dialog)
        dialog.show()

    def close_about(self, dialog, response):
        dialog.hide()

    def center_dialog(self, dialog):
        if "window" in self.__dict__:
            dialog.set_transient_for(self.window)
        dialog.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)

    ################################################
    # window and widget management
    ################################################

    def get_page_name(self):
        return PAGES[self.pages.get_current_page()]

    def toggle_pg3_next_apply(self, button):
        button_next = self.get("button_next")
        button_apply = self.get("button_apply")

        if self.get("use_existing_device").get_active() is False:
            button_next.hide()
            button_apply.show()
            button_apply.grab_default()
        else:
            button_apply.hide()
            button_next.show()
            button_next.grab_default()

    def next_page(self, button):
        page_name = self.get_page_name()
        increment = 1

        if (
            page_name == "control_panel_options"
            and self.get("new_user_option").get_active() is False
        ):
            increment = 2

        self.pages.set_current_page(self.pages.get_current_page() + increment)
        self.toggle_buttons(button, None, 1)

    def prev_page(self, button):
        page_name = self.get_page_name()
        decrement = 1

        if page_name == "existing_user":
            decrement = 2
        # elif page_name == 'standalone_options':
        # decrement = 5

        if self.pages.get_current_page() != 0:
            self.pages.set_current_page(self.pages.get_current_page() - decrement)

        self.toggle_buttons(button, None, 1)

    def toggle_buttons(self, button, tab, tab_number):

        button_prev = self.get("button_prev")
        button_next = self.get("button_next")
        button_apply = self.get("button_apply")

        if self.get_page_name() == "control_panel_options":
            button_prev.hide()
            button_apply.hide()
            button_next.show()
            button_next.grab_default()
        else:
            button_prev.show()
            button_next.hide()
            button_apply.show()
            button_apply.grab_default()

    def set_default_action(self, button, ctrl):
        button_cancel = self.get("button_cancel")
        cancel_has_default = button_cancel.flags() & Gtk.HAS_DEFAULT
        button_prev = self.get("button_prev")
        prev_has_default = button_prev.flags() & Gtk.HAS_DEFAULT
        button_next = self.get("button_next")
        button_apply = self.get("button_apply")

        if not cancel_has_default and not prev_has_default:
            if button_next.flags() & Gtk.VISIBLE:
                button_next.grab_default()
            else:
                button_apply.grab_default()

    # ensure the widget focused is visible in the scroll window
    def ensure_visible(self, widget, event):
        widget_name = widget.get_name()
        internal_height = self.get("control_panel_options").get_size()[1]
        widget_posn = widget.allocation.y
        widget_height = widget.allocation.height
        return True

    # show about dialog on F1 keypress
    def key_pressed(self, widget, event):
        if (
            (event.keyval == Gdk.KEY_F1)
            and (event.get_state() & Gdk.ModifierType.CONTROL_MASK) == 0
            and (event.get_state() & Gdk.ModifierType.SHIFT_MASK) == 0
        ):
            self.show_about()
            return True

        return False

    ################################################
    # setting settings
    ################################################

    def apply_settings(self, button):
        self.get("button_apply").set_label(_("Saving..."))

        page_name = self.get_page_name()
        if page_name == "new_user":
            if self.validate_new_user_fields():
                self.create_user()
        elif page_name == "existing_user":
            if self.validate_existing_user_fields():
                self.get_existing_user(False)

        self.get("button_apply").set_label("gtk-apply")

    def client_configured(self):
        self.call_prey_config("account verify", "--current")
        return self.result == 0

    def create_user(self):
        name = self.text("user_name")
        email = self.text("email")
        password = self.text("password")
        check_terms = self.get("check_terms_conds").get_active()
        check_age = self.get("check_age").get_active()
        terms = "no"
        age = "no"

        if check_terms is True:
            terms = "yes"
        if check_age is True:
            age = "yes"

        password = re.escape(password)

        self.call_prey_config(
            "account signup",
            '-n "'
            + name
            + '" -e "'
            + email
            + '" -p '
            + password
            + ' -t "'
            + terms
            + '" -a "'
            + age
            + '"',
        )
        self.error_or_exit()

    def get_existing_user(self, show_devices):
        email = self.text("existing_email")
        password = self.text("existing_password")
        password = re.escape(password)

        self.call_prey_config("account authorize", '-e "' + email + '" -p ' + password)
        self.error_or_exit()

    def call_prey_config(self, action, opts):
        return self.run_command(PREY_CONFIG + " " + action + " " + opts)

    def run_prey(self):
        return self.run_command(PREY_BIN)

    def run_command(self, cmd):
        args = shlex.split(cmd)
        proc = Popen(args, stdout=PIPE, shell=False)
        self.out = proc.communicate()[0]
        self.result = proc.returncode
        return proc

    def parse_error(self, line):
        if line.find("been taken") != -1:
            return "Email has been taken. Seems you already signed up!"
        elif line.find("Unexpected status code: 401") != -1:
            return "Invalid account credentials. Please try again."

        return line

    def error_or_exit(self):
        if self.result == 0:
            return self.exit_ok()
        #      self.run_prey()
        #      if self.result == 0:
        #        self.exit_ok()
        #      else:
        #        self.show_alert('Error', 'Something went wrong while running Prey. Please check the logfile for details.')
        else:
            lines = self.out.strip()
            message = self.parse_error(lines)
            self.show_error(message)

    def show_error(self, message):
        self.show_alert(_("Hold on!"), _(message), False)

    def exit_ok(self):
        self.show_alert(
            _("Success"),
            _(
                "Sweet! Your computer is now protected by Prey. To try it out or to start tracking it, please visit preyproject.com."
            ),
            True,
        )

    def __init__(self):
        if not FORCE_CONFIG and self.client_configured():
            return self.exit_ok()

        builder = Gtk.Builder()
        builder.set_translation_domain(APP_NAME)
        builder.add_from_file(SCRIPT_PATH + "/prey-config.glade")
        builder.connect_signals(
            {
                "on_window_destroy": Gtk.main_quit,
                "prev_page": self.prev_page,
                "next_page": self.next_page,
                "toggle_buttons": self.toggle_buttons,
                "apply_settings": self.apply_settings,
                "toggle_pg3_next_apply": self.toggle_pg3_next_apply,
                "set_default_action": self.set_default_action,
                "ensure_visible": self.ensure_visible,
                "key_pressed": self.key_pressed,
                "close_about": self.close_about,
            }
        )

        self.window = builder.get_object("window")
        self.window.set_title(self.window.get_title() + " (v" + VERSION + ")")
        # self.window.get_settings().set_string_property('gtk-font-name', 'sans normal 11','');
        self.pages = builder.get_object("reporting_mode_tabs")
        self.root = builder

        about = self.get("about_prey_config")
        about.set_version(VERSION)


if __name__ == "__main__":
    app = PreyConfigurator()
    Gtk.main()