Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate client UI and actions better part one #222

Merged
merged 2 commits into from
Apr 4, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 41 additions & 35 deletions idaplugin/rematch/actions/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,37 @@
import idc


class Action(idaapi.action_handler_t):
"""Actions are objects registered to IDA's interface and added to the
rematch menu and toolbar"""
dialog = None

class Action():
reject_handler = None
finish_handler = None
submit_handler = None
response_handler = None
exception_handler = None

def __init__(self):
def __init__(self, ui_class):
self.ui_class = ui_class
self.ui = None

def __repr__(self):
return "<Action: {}>".format(self.ui_class)

def running(self):
return self.ui is not None


class IDAAction(idaapi.action_handler_t, Action):
"""Actions are objects registered to IDA's interface and added to the
rematch menu and toolbar"""

def __init__(self, *args, **kwargs):
super(IDAAction, self).__init__(*args, **kwargs)
self._icon = None
self.dlg = None

def __repr__(self):
return "<Action: {}>".format(self.get_id())
return "<Action: {}, {}>".format(self.get_id(), self.ui_class)

def __del__(self):
super(IDAAction, self).__del__()
if self._icon:
idaapi.free_custom_icon(self._icon)

Expand Down Expand Up @@ -81,23 +93,20 @@ def get_action_path(self):

return '/'.join(t)

@classmethod
def register(cls):
action = cls()
r = idaapi.register_action(action.get_desc())
def register(self):
r = idaapi.register_action(self.get_desc())
if not r:
log('actions').warn("failed registering %s: %s", cls, r)
log('actions').warn("failed registering %s: %s", self, r)
return
idaapi.attach_action_to_menu(
action.get_action_path(),
action.get_id(),
self.get_action_path(),
self.get_id(),
idaapi.SETMENU_APP)
r = idaapi.attach_action_to_toolbar(
"AnalysisToolBar",
action.get_id())
self.get_id())
if not r:
log('actions').warn("registration of %s failed: %s", cls, r)
return action
log('actions').warn("registration of %s failed: %s", self, r)

def update(self, ctx):
return idaapi.AST_ENABLE if self.enabled(ctx) else idaapi.AST_DISABLE
Expand All @@ -107,25 +116,22 @@ def activate(self, ctx):
if self.running():
return

if callable(self.dialog):
self.dlg = self.dialog(reject_handler=self.reject_handler,
submit_handler=self.submit_handler,
response_handler=self.response_handler,
exception_handler=self.exception_handler)
if callable(self.ui_class):
self.ui = self.ui_class(reject_handler=self.reject_handler,
submit_handler=self.submit_handler,
response_handler=self.response_handler,
exception_handler=self.exception_handler)
if self.finish_handler:
self.dlg.finished.connect(self.finish_handler)
self.dlg.finished.connect(self.close_dialog)
self.dlg.finished.connect(self.force_update)
self.dlg.show()
self.ui.finished.connect(self.finish_handler)
self.ui.finished.connect(self.close_dialog)
self.ui.finished.connect(self.force_update)
self.ui.show()
else:
log('actions').warn("%s: no activation", self.__class__)

def running(self):
return self.dlg is not None

def close_dialog(self):
del self.dlg
self.dlg = None
del self.ui
self.ui = None

@staticmethod
def force_update():
Expand All @@ -136,23 +142,23 @@ def force_update():
idaapi.request_refresh(iwid_all)


class IdbAction(Action):
class IdbAction(IDAAction):
"""This action is only available when an idb file is loaded"""
@staticmethod
def enabled(ctx):
del ctx
return bool(idc.GetIdbPath())


class UnauthAction(Action):
class UnauthAction(IDAAction):
"""This action is only available when a user is logged off"""
@staticmethod
def enabled(ctx):
del ctx
return not bool(user['is_authenticated'])


class AuthAction(Action):
class AuthAction(IDAAction):
"""This action is only available when a user is logged in"""
@staticmethod
def enabled(ctx):
Expand Down
21 changes: 9 additions & 12 deletions idaplugin/rematch/actions/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@
from .. import config
from .. import exceptions

from ..dialogs.login import LoginDialog


class LoginAction(base.UnauthAction):
name = "&Login"
group = "User"
dialog = LoginDialog

def __init__(self):
super(LoginAction, self).__init__()
def __init__(self, *args, **kwargs):
super(LoginAction, self).__init__(*args, **kwargs)
self.username = None
self.password = None
self.server = None
Expand All @@ -34,8 +31,8 @@ def submit_handler(self, username, password, server, remember):
def handle_login(self, response):
del response

self.dlg.statusLbl.setText("Connected!")
self.dlg.statusLbl.setStyleSheet("color: green;")
self.ui.statusLbl.setText("Connected!")
self.ui.statusLbl.setStyleSheet("color: green;")

config['login']['username'] = self.username
config['login']['server'] = self.server
Expand All @@ -45,17 +42,17 @@ def handle_login(self, response):
config['login']['password'] = ""
config.save()

self.dlg.accept()
self.ui.accept()

def handle_exception(self, exception):
if isinstance(exception, (exceptions.ConnectionException,
exceptions.ServerException)):
self.dlg.statusLbl.setText("Connection to server failed.")
self.dlg.statusLbl.setStyleSheet("color: blue;")
self.ui.statusLbl.setText("Connection to server failed.")
self.ui.statusLbl.setStyleSheet("color: blue;")
elif isinstance(exception, (exceptions.QueryException,
exceptions.AuthenticationException)):
self.dlg.statusLbl.setText("Invalid user name or password.")
self.dlg.statusLbl.setStyleSheet("color: red;")
self.ui.statusLbl.setText("Invalid user name or password.")
self.ui.statusLbl.setStyleSheet("color: red;")


class LogoutAction(base.AuthAction):
Expand Down
3 changes: 0 additions & 3 deletions idaplugin/rematch/actions/project.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from . import base
from ..dialogs.project import AddProjectDialog, AddFileDialog

from .. import netnode
from .. import network
Expand All @@ -8,7 +7,6 @@
class AddProjectAction(base.AuthAction):
name = "&Add project"
group = "Project"
dialog = AddProjectDialog

@staticmethod
def submit_handler(name, description, private, bind_current):
Expand All @@ -31,7 +29,6 @@ def response_handler(cls, response):
class AddFileAction(base.UnboundFileAction):
name = "&Add file"
group = "Project"
dialog = AddFileDialog

@staticmethod
def submit_handler(project, name, md5hash, description, shareidb):
Expand Down
5 changes: 1 addition & 4 deletions idaplugin/rematch/actions/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@
from . import base
from .. import config

from ..dialogs.settings import SettingsDialog


class SettingsAction(base.Action):
class SettingsAction(base.IDAAction):
name = "&Settings"
dialog = SettingsDialog

@staticmethod
def submit_handler(autocheck, autoupdate, autologin, autologout, debug):
Expand Down
14 changes: 8 additions & 6 deletions idaplugin/rematch/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from . import config, user
from . import actions
from . import dialogs
from . import update


Expand Down Expand Up @@ -47,15 +48,16 @@ def setup(self):
self.menu = QtWidgets.QMenu("Rematch")
self.get_mainwindow().menuWidget().addMenu(self.menu)

actions.login.LoginAction.register()
actions.login.LogoutAction.register()
actions.login.LoginAction(dialogs.login.LoginDialog).register()
actions.login.LogoutAction(None).register()

actions.project.AddProjectAction.register()
actions.project.AddFileAction.register()
actions.project.AddProjectAction(dialogs.project.
AddProjectDialog).register()
actions.project.AddFileAction(dialogs.project.AddFileDialog).register()

actions.match.MatchAction.register()
actions.match.MatchAction(dialogs.match.MatchDialog).register()

actions.settings.SettingsAction.register()
actions.settings.SettingsAction(dialogs.settings.SettingsDialog).register()

# set up status bar
self.statusbar_label = QtWidgets.QLabel("Rematch loaded")
Expand Down