Hello,

In keeping with the idea to make the less Perl-ish and more OOP style
(in hope to get more Pythonic). I thought it was a simple place to
start off with.

Any suggestions and changes for the patch are welcome.

The following patch:
- creates a class for prompt questions moving away from Perl hash hack
for the purpose.
- moves some functions to the methods for that class
- fix options being incorrectly passed to questionPrompt in aa-mergeprof

Due to the functions being moved around the patch is a bit long.

Basic testing done with logprof.

Regards,
Kshitij Gupta
=== modified file 'utils/aa-mergeprof'
--- utils/aa-mergeprof	2014-09-03 23:49:47 +0000
+++ utils/aa-mergeprof	2014-09-10 09:46:48 +0000
@@ -26,7 +26,7 @@
 from apparmor.translations import init_translation
 _ = init_translation()
 
-parser = argparse.ArgumentParser(description=_('Perform a 2-way or 3-way merge on the given profiles'), 
+parser = argparse.ArgumentParser(description=_('Perform a 2-way or 3-way merge on the given profiles'),
     epilog='WARNING: the arguments will change in a future version!')
 parser.add_argument('mine', type=str, help=_('your profile'))
 parser.add_argument('base', type=str, help=_('base profile'))
@@ -61,20 +61,20 @@
 
         mergeprofiles.ask_the_questions('base')
 
-        q = apparmor.aa.hasher()
-        q['title'] = 'Changed Local Profiles'
-        q['headers'] = []
-        q['explanation'] = _('The following local profiles were changed. Would you like to save them?')
-        q['functions'] = ['CMD_SAVE_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ABORT']
-        q['default'] = 'CMD_VIEW_CHANGES'
-        q['options'] = []
-        q['selected'] = 0
+        q = aaui.PromptQuestion()
+        q.title = _('Changed Local Profiles')
+        q.explanation = _('The following local profiles were changed. Would you like to save them?')
+        q.functions = ['CMD_SAVE_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ABORT']
+        q.default = 'CMD_VIEW_CHANGES'
+        q.options = []
+        q.selected = 0
+
         ans = ''
         arg = None
         programs = list(mergeprofiles.user.aa.keys())
         program = programs[0]
         while ans != 'CMD_SAVE_CHANGES':
-            ans, arg = aaui.UI_PromptUser(q)
+            ans, arg = q.promptUser()
             if ans == 'CMD_SAVE_CHANGES':
                 apparmor.aa.write_profile_ui_feedback(program)
                 apparmor.aa.reload_base(program)
@@ -144,17 +144,17 @@
             if conflict_x & set('X'):
                 conflict_x.remove('X')
             if len(conflict_x) > 1:
-                q = apparmor.aa.hasher()
-                q['headers'] = [_('Path'), path]
-                q['headers'] += [_('Select the appropriate mode'), '']
+                q = aaui.PromptQuestion()
+                q.headers = [_('Path'), path]
+                q.headers += [_('Select the appropriate mode'), '']
                 options = []
                 options.append('%s: %s' %(mode, apparmor.aa.mode_to_str_user(new_mode)))# - (old_mode & conflict_x))))
                 options.append('%s: %s' %(mode, apparmor.aa.mode_to_str_user(old_mode)))#(old_mode | new_mode) - (new_mode & conflict_x))))
-                q['options'] = options
-                q['functions'] = ['CMD_ALLOW', 'CMD_ABORT']
+                q.options = options
+                q.functions = ['CMD_ALLOW', 'CMD_ABORT']
                 done = False
                 while not done:
-                    ans, selected = aaui.UI_PromptUser(q)
+                    ans, selected = q.promptUser()
                     if ans == 'CMD_ALLOW':
                         if selected == 0:
                             self.user.aa[profile][hat][allow]['path'][path][mode] = m#apparmor.aa.owner_flatten_mode(new_mode)#(old_mode | new_mode) - (old_mode & conflict_x)
@@ -176,15 +176,17 @@
         #Add the file-wide includes from the other profile to the user profile
         done = False
         options = list(map(lambda inc: '#include <%s>' %inc, sorted(other.filelist[other.filename]['include'].keys())))
-        q = apparmor.aa.hasher()
-        q['options'] = options
         default_option = 1
-        q['selected'] = default_option - 1
-        q['headers'] = [_('File includes'), _('Select the ones you wish to add')]
-        q['functions'] = ['CMD_ALLOW', 'CMD_IGNORE_ENTRY', 'CMD_ABORT', 'CMD_FINISHED']
-        q['default'] = 'CMD_ALLOW'
+
+        q = aaui.PromptQuestion()
+        q.options = options
+        q.selected = default_option - 1
+        q.headers = [_('File includes'), _('Select the ones you wish to add')]
+        q.functions = ['CMD_ALLOW', 'CMD_IGNORE_ENTRY', 'CMD_ABORT', 'CMD_FINISHED']
+        q.default = 'CMD_ALLOW'
+
         while not done and options:
-            ans, selected = aaui.UI_PromptUser(q)
+            ans, selected = q.PromptUser()
             if ans == 'CMD_IGNORE_ENTRY':
                 done = True
             elif ans == 'CMD_ALLOW':
@@ -202,15 +204,17 @@
                 #Add the includes from the other profile to the user profile
                 done = False
                 options = list(map(lambda inc: '#include <%s>' %inc, sorted(other.aa[profile][hat]['include'].keys())))
-                q = apparmor.aa.hasher()
-                q['options'] = options
                 default_option = 1
-                q['selected'] = default_option - 1
-                q['headers'] = [_('File includes'), _('Select the ones you wish to add')]
-                q['functions'] = ['CMD_ALLOW', 'CMD_IGNORE_ENTRY', 'CMD_ABORT', 'CMD_FINISHED']
-                q['default'] = 'CMD_ALLOW'
+
+                q = aaui.PromptQuestion()
+                q.options = options
+                q.selected = default_option - 1
+                q.headers = [_('File includes'), _('Select the ones you wish to add')]
+                q.functions = ['CMD_ALLOW', 'CMD_IGNORE_ENTRY', 'CMD_ABORT', 'CMD_FINISHED']
+                q.default = 'CMD_ALLOW'
+
                 while not done and options:
-                    ans, selected = aaui.UI_PromptUser(q)
+                    ans, selected = q.promptUser()
                     if ans == 'CMD_IGNORE_ENTRY':
                         done = True
                     elif ans == 'CMD_ALLOW':
@@ -232,28 +236,28 @@
                         default_option = 1
                         options = []
                         newincludes = apparmor.aa.match_cap_includes(self.user.aa[profile][hat], capability)
-                        q = apparmor.aa.hasher()
+                        q = aaui.PromptQuestion()
                         if newincludes:
                             options += list(map(lambda inc: '#include <%s>' %inc, sorted(set(newincludes))))
 
                         if options:
                             options.append('capability %s' % capability)
-                            q['options'] = [options]
-                            q['selected'] = default_option - 1
+                            q.options = options
+                            q.selected = default_option - 1
 
-                        q['headers'] = [_('Profile'), apparmor.aa.combine_name(profile, hat)]
-                        q['headers'] += [_('Capability'), capability]
-                        q['headers'] += [_('Severity'), severity]
+                        q.headers = [_('Profile'), apparmor.aa.combine_name(profile, hat)]
+                        q.headers += [_('Capability'), capability]
+                        q.headers += [_('Severity'), severity]
 
                         audit_toggle = 0
 
-                        q['functions'] = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_ABORT', 'CMD_FINISHED']
+                        q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_ABORT', 'CMD_FINISHED']
 
-                        q['default'] = 'CMD_ALLOW'
+                        q.default = 'CMD_ALLOW'
 
                         done = False
                         while not done:
-                            ans, selected = aaui.UI_PromptUser(q)
+                            ans, selected = q.promptUser()
                             # Ignore the log entry
                             if ans == 'CMD_IGNORE_ENTRY':
                                 done = True
@@ -425,8 +429,8 @@
                                 owner_toggle = apparmor.aa.cfg['settings']['default_owner_prompt']
                             done = False
                             while not done:
-                                q =  apparmor.aa.hasher()
-                                q['headers'] = [_('Profile'), apparmor.aa.combine_name(profile, hat),
+                                q =  aaui.PromptQuestion()
+                                q.headers = [_('Profile'), apparmor.aa.combine_name(profile, hat),
                                                 _('Path'), path]
 
                                 if allow_mode:
@@ -456,7 +460,7 @@
                                     else:
                                         s = apparmor.aa.mode_to_str_user(prompt_mode) + tail
 
-                                    q['headers'] += [_('Old Mode'), apparmor.aa.mode_to_str_user(allow_mode),
+                                    q.headers += [_('Old Mode'), apparmor.aa.mode_to_str_user(allow_mode),
                                                      _('New Mode'), s]
 
                                 else:
@@ -475,19 +479,19 @@
                                         tail = '     ' + _('(force perms to owner)')
 
                                     s = apparmor.aa.mode_to_str_user(prompt_mode)
-                                    q['headers'] += [_('Mode'), s]
+                                    q.headers += [_('Mode'), s]
 
-                                q['headers'] += [_('Severity'), severity]
-                                q['options'] = options
-                                q['selected'] = default_option - 1
-                                q['functions'] = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_GLOB',
+                                q.headers += [_('Severity'), severity]
+                                q.options = options
+                                q.selected = default_option - 1
+                                q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_GLOB',
                                                   'CMD_GLOBEXT', 'CMD_NEW', 'CMD_ABORT',
                                                   'CMD_FINISHED', 'CMD_OTHER']
 
-                                q['default'] = 'CMD_ALLOW'
-
-
-                                ans, selected = aaui.UI_PromptUser(q)
+                                q.default = 'CMD_ALLOW'
+
+
+                                ans, selected = q.promptUser()
 
                                 if ans == 'CMD_IGNORE_ENTRY':
                                     done = True
@@ -618,27 +622,27 @@
                             default_option = 1
                             options = []
                             newincludes = apparmor.aa.match_net_includes(self.user.aa[profile][hat], family, sock_type)
-                            q = apparmor.aa.hasher()
+                            q = aaui.PromptQuestion()
                             if newincludes:
                                 options += list(map(lambda s: '#include <%s>'%s, sorted(set(newincludes))))
                             if True:#options:
                                 options.append('network %s %s' % (family, sock_type))
-                                q['options'] = options
-                                q['selected'] = default_option - 1
+                                q.options = options
+                                q.selected = default_option - 1
 
-                            q['headers'] = [_('Profile'), apparmor.aa.combine_name(profile, hat)]
-                            q['headers'] += [_('Network Family'), family]
-                            q['headers'] += [_('Socket Type'), sock_type]
+                            q.headers = [_('Profile'), apparmor.aa.combine_name(profile, hat)]
+                            q.headers += [_('Network Family'), family]
+                            q.headers += [_('Socket Type'), sock_type]
 
                             audit_toggle = 0
-                            q['functions'] = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_AUDIT_NEW',
+                            q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_AUDIT_NEW',
                                               'CMD_ABORT', 'CMD_FINISHED']
 
-                            q['default'] = 'CMD_ALLOW'
+                            q.default = 'CMD_ALLOW'
 
                             done = False
                             while not done:
-                                ans, selected = aaui.UI_PromptUser(q)
+                                ans, selected = q.promptUser()
                                 if ans == 'CMD_IGNORE_ENTRY':
                                     done = True
                                     break
@@ -648,14 +652,14 @@
                                     audit = ''
                                     if audit_toggle:
                                         audit = 'audit'
-                                        q['functions'] = ['CMD_ALLOW', 'CMD_DENY', 'CMD_AUDIT_OFF',
+                                        q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_AUDIT_OFF',
                                                           'CMD_ABORT', 'CMD_FINISHED']
                                     else:
-                                        q['functions'] = ['CMD_ALLOW', 'CMD_DENY', 'CMD_AUDIT_NEW',
+                                        q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_AUDIT_NEW',
                                                           'CMD_ABORT', 'CMD_FINISHED']
-                                    q['headers'] = [_('Profile'), apparmor.aa.combine_name(profile, hat)]
-                                    q['headers'] += [_('Network Family'), audit + family]
-                                    q['headers'] += [_('Socket Type'), sock_type]
+                                    q.headers = [_('Profile'), apparmor.aa.combine_name(profile, hat)]
+                                    q.headers += [_('Network Family'), audit + family]
+                                    q.headers += [_('Socket Type'), sock_type]
 
                                 elif ans == 'CMD_ALLOW':
                                     #print(options, selected)

=== modified file 'utils/apparmor/aa.py'
--- utils/apparmor/aa.py	2014-09-08 12:31:18 +0000
+++ utils/apparmor/aa.py	2014-09-10 07:07:21 +0000
@@ -495,13 +495,13 @@
         options.append(preferred_user)
     options += tmp_list
 
-    q = dict()
-    q['headers'] = ['Profile', prof_name]
-    q['functions'] = ['CMD_VIEW_PROFILE', 'CMD_USE_PROFILE', 'CMD_CREATE_PROFILE',
+    q = aaui.PromptQuestion()
+    q.headers = ['Profile', prof_name]
+    q.functions = ['CMD_VIEW_PROFILE', 'CMD_USE_PROFILE', 'CMD_CREATE_PROFILE',
                       'CMD_ABORT', 'CMD_FINISHED']
-    q['default'] = "CMD_VIEW_PROFILE"
-    q['options'] = options
-    q['selected'] = 0
+    q.default = "CMD_VIEW_PROFILE"
+    q.options = options
+    q.selected = 0
 
     ans = ''
     while 'CMD_USE_PROFILE' not in ans and 'CMD_CREATE_PROFILE' not in ans:
@@ -509,9 +509,9 @@
             save_profiles()
             return
 
-        ans, arg = aaui.UI_PromptUser(q)
+        ans, arg = q.promptUser()
         p = profile_hash[options[arg]]
-        q['selected'] = options.index(options[arg])
+        q.selected = options.index(options[arg])
         if ans == 'CMD_VIEW_PROFILE':
             if aaui.UI_mode == 'yast':
                 SendDataToYast({'type': 'dialogue-view-profile',
@@ -845,18 +845,18 @@
 def console_select_and_upload_profiles(title, message, profiles_up):
     url = cfg['repository']['url']
     profs = profiles_up[:]
-    q = hasher()
-    q['title'] = title
-    q['headers'] = ['Repository', url]
-    q['explanation'] = message
-    q['functions'] = ['CMD_UPLOAD_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ASK_LATER',
+    q = aaui.PromptQuestion()
+    q.title = title
+    q.headers = ['Repository', url]
+    q.explanation = message
+    qfunctions = ['CMD_UPLOAD_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ASK_LATER',
                       'CMD_ASK_NEVER', 'CMD_ABORT']
-    q['default'] = 'CMD_VIEW_CHANGES'
-    q['options'] = [i[0] for i in profs]
-    q['selected'] = 0
+    q.default = 'CMD_VIEW_CHANGES'
+    q.options = [i[0] for i in profs]
+    q.selected = 0
     ans = ''
     while 'CMD_UPLOAD_CHANGES' not in ans and 'CMD_ASK_NEVER' not in ans and 'CMD_ASK_LATER' not in ans:
-        ans, arg = aaui.UI_PromptUser(q)
+        ans, arg = q.promptUser()
         if ans == 'CMD_VIEW_CHANGES':
             display_changes(profs[arg][2], profs[arg][1])
     if ans == 'CMD_NEVER_ASK':
@@ -987,28 +987,26 @@
                 ans = transitions.get(context, 'XXXINVALIDXXX')
 
                 while ans not in ['CMD_ADDHAT', 'CMD_USEDEFAULT', 'CMD_DENY']:
-                    q = hasher()
-                    q['headers'] = []
-                    q['headers'] += [_('Profile'), profile]
-
-                    if default_hat:
-                        q['headers'] += [_('Default Hat'), default_hat]
-
-                    q['headers'] += [_('Requested Hat'), uhat]
-
-                    q['functions'] = []
-                    q['functions'].append('CMD_ADDHAT')
-                    if default_hat:
-                        q['functions'].append('CMD_USEDEFAULT')
-                    q['functions'] += ['CMD_DENY', 'CMD_ABORT', 'CMD_FINISHED']
-
-                    q['default'] = 'CMD_DENY'
+                    q = aaui.PromptQuestion()
+                    q.headers += [_('Profile'), profile]
+
+                    if default_hat:
+                        q.headers += [_('Default Hat'), default_hat]
+
+                    q.headers += [_('Requested Hat'), uhat]
+
+                    q.functions.append('CMD_ADDHAT')
+                    if default_hat:
+                        q.functions.append('CMD_USEDEFAULT')
+                    q.functions += ['CMD_DENY', 'CMD_ABORT', 'CMD_FINISHED']
+
+                    q.default = 'CMD_DENY'
                     if aamode == 'PERMITTING':
-                        q['default'] = 'CMD_ADDHAT'
+                        q.default = 'CMD_ADDHAT'
 
                     seen_events += 1
 
-                    ans = aaui.UI_PromptUser(q)
+                    ans = q.promptUser()
 
                     if ans == 'CMD_FINISHED':
                         save_profiles()
@@ -1247,20 +1245,19 @@
                         severity = sev_db.rank(exec_target, 'x')
 
                         # Prompt portion starts
-                        q = hasher()
-                        q['headers'] = []
-                        q['headers'] += [_('Profile'), combine_name(profile, hat)]
+                        q = aaui.PromptQuestion()
+
+                        q.headers += [_('Profile'), combine_name(profile, hat)]
                         if prog and prog != 'HINT':
-                            q['headers'] += [_('Program'), prog]
+                            q.headers += [_('Program'), prog]
 
                         # to_name should not exist here since, transitioning is already handeled
-                        q['headers'] += [_('Execute'), exec_target]
-                        q['headers'] += [_('Severity'), severity]
+                        q.headers += [_('Execute'), exec_target]
+                        q.headers += [_('Severity'), severity]
 
-                        q['functions'] = []
                         # prompt = '\n%s\n' % context_new  # XXX
                         exec_toggle = False
-                        q['functions'] += build_x_functions(default, options, exec_toggle)
+                        q.functions += build_x_functions(default, options, exec_toggle)
 
                         # options = '|'.join(options)
                         seen_events += 1
@@ -1268,11 +1265,11 @@
 
                         ans = ''
                         while not regex_options.search(ans):
-                            ans = aaui.UI_PromptUser(q)[0].strip()
+                            ans = q.promptUser()[0].strip()
                             if ans.startswith('CMD_EXEC_IX_'):
                                 exec_toggle = not exec_toggle
-                                q['functions'] = []
-                                q['functions'] += build_x_functions(default, options, exec_toggle)
+                                q.functions = []
+                                q.functions += build_x_functions(default, options, exec_toggle)
                                 ans = ''
                                 continue
 
@@ -1549,36 +1546,36 @@
                     default_option = 1
                     options = []
                     newincludes = match_cap_includes(aa[profile][hat], capability)
-                    q = hasher()
+                    q = aaui.PromptQuestion()
 
                     if newincludes:
                         options += list(map(lambda inc: '#include <%s>' % inc, sorted(set(newincludes))))
 
                     if options:
                         options.append('capability %s' % capability)
-                        q['options'] = options
-                        q['selected'] = default_option - 1
+                        q.options = options
+                        q.selected = default_option - 1
 
-                    q['headers'] = [_('Profile'), combine_name(profile, hat)]
-                    q['headers'] += [_('Capability'), capability]
-                    q['headers'] += [_('Severity'), severity]
+                    q.headers = [_('Profile'), combine_name(profile, hat)]
+                    q.headers += [_('Capability'), capability]
+                    q.headers += [_('Severity'), severity]
 
                     audit_toggle = 0
 
-                    q['functions'] = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_AUDIT_NEW',
+                    q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_AUDIT_NEW',
                                       'CMD_ABORT', 'CMD_FINISHED']
 
                     # In complain mode: events default to allow
                     # In enforce mode: events default to deny
-                    q['default'] = 'CMD_DENY'
+                    q.default = 'CMD_DENY'
                     if aamode == 'PERMITTING':
-                        q['default'] = 'CMD_ALLOW'
+                        q.default = 'CMD_ALLOW'
 
                     seen_events += 1
 
                     done = False
                     while not done:
-                        ans, selected = aaui.UI_PromptUser(q)
+                        ans, selected = q.promptUser()
 
                         if ans == 'CMD_FINISHED':
                             save_profiles()
@@ -1593,14 +1590,14 @@
                             audit_toggle = not audit_toggle
                             audit = ''
                             if audit_toggle:
-                                q['functions'] = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_AUDIT_OFF',
+                                q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_AUDIT_OFF',
                                                   'CMD_ABORT', 'CMD_FINISHED']
                                 audit = 'audit'
                             else:
-                                q['functions'] = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_AUDIT_NEW',
+                                q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_AUDIT_NEW',
                                                   'CMD_ABORT', 'CMD_FINISHED', ]
 
-                            q['headers'] = [_('Profile'), combine_name(profile, hat),
+                            q.headers = [_('Profile'), combine_name(profile, hat),
                                             _('Capability'), audit + capability,
                                             _('Severity'), severity]
 
@@ -1762,8 +1759,8 @@
                             owner_toggle = cfg['settings']['default_owner_prompt']
                         done = False
                         while not done:
-                            q = hasher()
-                            q['headers'] = [_('Profile'), combine_name(profile, hat),
+                            q = aaui.PromptQuestion()
+                            q.headers = [_('Profile'), combine_name(profile, hat),
                                             _('Path'), path]
 
                             if allow_mode:
@@ -1793,7 +1790,7 @@
                                 else:
                                     s = mode_to_str_user(prompt_mode) + tail
 
-                                q['headers'] += [_('Old Mode'), mode_to_str_user(allow_mode),
+                                q.headers += [_('Old Mode'), mode_to_str_user(allow_mode),
                                                  _('New Mode'), s]
 
                             else:
@@ -1812,21 +1809,21 @@
                                     tail = '     ' + _('(force perms to owner)')
 
                                 s = mode_to_str_user(prompt_mode)
-                                q['headers'] += [_('Mode'), s]
+                                q.headers += [_('Mode'), s]
 
-                            q['headers'] += [_('Severity'), severity]
-                            q['options'] = options
-                            q['selected'] = default_option - 1
-                            q['functions'] = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_GLOB',
+                            q.headers += [_('Severity'), severity]
+                            q.options = options
+                            q.selected = default_option - 1
+                            q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_GLOB',
                                               'CMD_GLOBEXT', 'CMD_NEW', 'CMD_ABORT',
                                               'CMD_FINISHED', 'CMD_OTHER']
-                            q['default'] = 'CMD_DENY'
+                            q.default = 'CMD_DENY'
                             if aamode == 'PERMITTING':
-                                q['default'] = 'CMD_ALLOW'
+                                q.default = 'CMD_ALLOW'
 
                             seen_events += 1
 
-                            ans, selected = aaui.UI_PromptUser(q)
+                            ans, selected = q.promptUser()
 
                             if ans == 'CMD_FINISHED':
                                 save_profiles()
@@ -1947,31 +1944,31 @@
                         default_option = 1
                         options = []
                         newincludes = match_net_includes(aa[profile][hat], family, sock_type)
-                        q = hasher()
+                        q = aaui.PromptQuestion()
                         if newincludes:
                             options += list(map(lambda s: '#include <%s>' % s, sorted(set(newincludes))))
                         if options:
                             options.append('network %s %s' % (family, sock_type))
-                            q['options'] = options
-                            q['selected'] = default_option - 1
+                            q.options = options
+                            q.selected = default_option - 1
 
-                        q['headers'] = [_('Profile'), combine_name(profile, hat)]
-                        q['headers'] += [_('Network Family'), family]
-                        q['headers'] += [_('Socket Type'), sock_type]
+                        q.headers = [_('Profile'), combine_name(profile, hat)]
+                        q.headers += [_('Network Family'), family]
+                        q.headers += [_('Socket Type'), sock_type]
 
                         audit_toggle = 0
-                        q['functions'] = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_AUDIT_NEW',
+                        q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_AUDIT_NEW',
                                           'CMD_ABORT', 'CMD_FINISHED']
-                        q['default'] = 'CMD_DENY'
+                        q.default = 'CMD_DENY'
 
                         if aamode == 'PERMITTING':
-                            q['default'] = 'CMD_ALLOW'
+                            q.default = 'CMD_ALLOW'
 
                         seen_events += 1
 
                         done = False
                         while not done:
-                            ans, selected = aaui.UI_PromptUser(q)
+                            ans, selected = q.promptUser()
 
                             if ans == 'CMD_FINISHED':
                                 save_profiles()
@@ -1986,14 +1983,14 @@
                                 audit = ''
                                 if audit_toggle:
                                     audit = 'audit'
-                                    q['functions'] = ['CMD_ALLOW', 'CMD_DENY', 'CMD_AUDIT_OFF',
+                                    q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_AUDIT_OFF',
                                                       'CMD_ABORT', 'CMD_FINISHED']
                                 else:
-                                    q['functions'] = ['CMD_ALLOW', 'CMD_DENY', 'CMD_AUDIT_NEW',
+                                    q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_AUDIT_NEW',
                                                       'CMD_ABORT', 'CMD_FINISHED']
-                                q['headers'] = [_('Profile'), combine_name(profile, hat)]
-                                q['headers'] += [_('Network Family'), audit + family]
-                                q['headers'] += [_('Socket Type'), sock_type]
+                                q.headers = [_('Profile'), combine_name(profile, hat)]
+                                q.headers += [_('Network Family'), audit + family]
+                                q.headers += [_('Socket Type'), sock_type]
 
                             elif ans == 'CMD_ALLOW':
                                 selection = options[selected]
@@ -2339,20 +2336,19 @@
                     reload_base(profile_name)
 
         else:
-            q = hasher()
-            q['title'] = 'Changed Local Profiles'
-            q['headers'] = []
-            q['explanation'] = _('The following local profiles were changed. Would you like to save them?')
-            q['functions'] = ['CMD_SAVE_CHANGES', 'CMD_SAVE_SELECTED', 'CMD_VIEW_CHANGES', 'CMD_VIEW_CHANGES_CLEAN', 'CMD_ABORT']
-            q['default'] = 'CMD_VIEW_CHANGES'
-            q['options'] = changed
-            q['selected'] = 0
+            q = aaui.PromptQuestion()
+            q.title = 'Changed Local Profiles'
+            q.explanation = _('The following local profiles were changed. Would you like to save them?')
+            q.functions = ['CMD_SAVE_CHANGES', 'CMD_SAVE_SELECTED', 'CMD_VIEW_CHANGES', 'CMD_VIEW_CHANGES_CLEAN', 'CMD_ABORT']
+            q.default = 'CMD_VIEW_CHANGES'
+            q.options = changed
+            q.selected = 0
             ans = ''
             arg = None
             while ans != 'CMD_SAVE_CHANGES':
                 if not changed:
                     return
-                ans, arg = aaui.UI_PromptUser(q)
+                ans, arg = q.promptUser()
                 if ans == 'CMD_SAVE_SELECTED':
                     profile_name = list(changed.keys())[arg]
                     write_profile_ui_feedback(profile_name)

=== modified file 'utils/apparmor/tools.py'
--- utils/apparmor/tools.py	2014-03-06 19:54:38 +0000
+++ utils/apparmor/tools.py	2014-09-10 09:34:19 +0000
@@ -234,18 +234,17 @@
 
         if filename:
             if not self.silent:
-                q = apparmor.hasher()
-                q['title'] = 'Changed Local Profiles'
-                q['headers'] = []
-                q['explanation'] = _('The local profile for %s in file %s was changed. Would you like to save it?') % (program, filename)
-                q['functions'] = ['CMD_SAVE_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ABORT']
-                q['default'] = 'CMD_VIEW_CHANGES'
-                q['options'] = []
-                q['selected'] = 0
+                q = aaui.PromptQuestion()
+                q.title = 'Changed Local Profiles'
+                q.explanation = _('The local profile for %s in file %s was changed. Would you like to save it?') % (program, filename)
+                q.functions = ['CMD_SAVE_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ABORT']
+                q.default = 'CMD_VIEW_CHANGES'
+                q.options = []
+                q.selected = 0
                 ans = ''
                 arg = None
                 while ans != 'CMD_SAVE_CHANGES':
-                    ans, arg = aaui.UI_PromptUser(q)
+                    ans, arg = q.promptUser()
                     if ans == 'CMD_SAVE_CHANGES':
                         apparmor.write_profile_ui_feedback(program)
                         apparmor.reload_base(program)

=== modified file 'utils/apparmor/ui.py'
--- utils/apparmor/ui.py	2014-08-06 17:07:41 +0000
+++ utils/apparmor/ui.py	2014-09-10 06:59:16 +0000
@@ -282,22 +282,166 @@
         'CMD_IGNORE_ENTRY': _('(I)gnore')
         }
 
-def UI_PromptUser(q, params=''):
-    cmd = None
-    arg = None
-    if UI_mode == 'text':
-        cmd, arg = Text_PromptUser(q)
-    else:
-        q['type'] = 'wizard'
-        SendDataToYast(q)
-        ypath, yarg = GetDataFromYast()
-        if not cmd:
-            cmd = 'CMD_ABORT'
-        arg = yarg['selected']
-    if cmd == 'CMD_ABORT':
-        confirm_and_abort()
-        cmd = 'XXXINVALIDXXX'
-    return (cmd, arg)
+class PromptQuestion(object):
+    title = None
+    headers = None
+    explanation = None
+    functions = None
+    options = None
+    default = None
+    selected = None
+    helptext = None
+
+    def __init__(self):
+        self.headers = list()
+        self.functions = list()
+        self.selected = 0
+
+    def promptUser(self, params=''):
+        cmd = None
+        arg = None
+        if UI_mode == 'text':
+            cmd, arg = self.Text_PromptUser()
+        else:
+            q.type = 'wizard'
+            SendDataToYast(q)
+            ypath, yarg = GetDataFromYast()
+            if not cmd:
+                cmd = 'CMD_ABORT'
+            arg = yarg['selected']
+        if cmd == 'CMD_ABORT':
+            confirm_and_abort()
+            cmd = 'XXXINVALIDXXX'
+        return (cmd, arg)
+
+    def Text_PromptUser(self):
+        title = self.title
+        explanation = self.explanation
+        headers = self.headers
+        functions = self.functions
+
+        default = self.default
+        options = self.options
+        selected = self.selected
+        helptext = self.helptext
+
+        if helptext:
+            functions.append('CMD_HELP')
+
+        menu_items = list()
+        keys = dict()
+
+        for cmd in functions:
+            if not CMDS.get(cmd, False):
+                raise AppArmorException(_('PromptUser: Unknown command %s') % cmd)
+
+            menutext = CMDS[cmd]
+
+            key = get_translated_hotkey(menutext).lower()
+            # Duplicate hotkey
+            if keys.get(key, False):
+                raise AppArmorException(_('PromptUser: Duplicate hotkey for %s: %s ') % (cmd, menutext))
+
+            keys[key] = cmd
+
+            if default and default == cmd:
+                menutext = '[%s]' % menutext
+
+            menu_items.append(menutext)
+
+        default_key = 0
+        if default and CMDS[default]:
+            defaulttext = CMDS[default]
+            defmsg = _('PromptUser: Invalid hotkey in default item')
+
+            default_key = get_translated_hotkey(defaulttext, defmsg).lower()
+
+            if not keys.get(default_key, False):
+                raise AppArmorException(_('PromptUser: Invalid default %s') % default)
+
+        widest = 0
+        header_copy = headers[:]
+        while header_copy:
+            header = header_copy.pop(0)
+            header_copy.pop(0)
+            if len(header) > widest:
+                widest = len(header)
+        widest += 1
+
+        formatstr = '%-' + str(widest) + 's %s\n'
+
+        function_regexp = '^('
+        function_regexp += '|'.join(keys.keys())
+        if options:
+            function_regexp += '|\d'
+        function_regexp += ')$'
+
+        ans = 'XXXINVALIDXXX'
+        while not re.search(function_regexp, ans, flags=re.IGNORECASE):
+
+            prompt = '\n'
+            if title:
+                prompt += '= %s =\n\n' % title
+
+            if headers:
+                header_copy = headers[:]
+                while header_copy:
+                    header = header_copy.pop(0)
+                    value = header_copy.pop(0)
+                    prompt += formatstr % (header + ':', value)
+                prompt += '\n'
+
+            if explanation:
+                prompt += explanation + '\n\n'
+
+            if options:
+                for index, option in enumerate(options):
+                    if selected == index:
+                        format_option = ' [%s - %s]'
+                    else:
+                        format_option = '  %s - %s '
+                    prompt += format_option % (index + 1, option)
+                    prompt += '\n'
+
+            prompt += ' / '.join(menu_items)
+
+            sys.stdout.write(prompt + '\n')
+
+            ans = getkey().lower()
+
+            if ans:
+                if ans == 'up':
+                    if options and selected > 0:
+                        selected -= 1
+                    ans = 'XXXINVALIDXXX'
+
+                elif ans == 'down':
+                    if options and selected < len(options) - 1:
+                        selected += 1
+                    ans = 'XXXINVALIDXXX'
+
+    #             elif keys.get(ans, False) == 'CMD_HELP':
+    #                 sys.stdout.write('\n%s\n' %helptext)
+    #                 ans = 'XXXINVALIDXXX'
+
+                elif is_number(ans) == 10:
+                    # If they hit return choose default option
+                    ans = default_key
+
+                elif options and re.search('^\d$', ans):
+                    ans = int(ans)
+                    if ans > 0 and ans <= len(options):
+                        selected = ans - 1
+                    ans = 'XXXINVALIDXXX'
+
+            if keys.get(ans, False) == 'CMD_HELP':
+                sys.stdout.write('\n%s\n' % helptext)
+                ans = 'again'
+
+        if keys.get(ans, False):
+            ans = keys[ans]
+
+        return ans, selected
 
 def confirm_and_abort():
     ans = UI_YesNo(_('Are you sure you want to abandon this set of profile changes and exit?'), 'n')
@@ -323,134 +467,6 @@
     ypath, yarg = GetDataFromYast()
 
 
-def Text_PromptUser(question):
-    title = question['title']
-    explanation = question['explanation']
-    headers = question['headers']
-    functions = question['functions']
-
-    default = question['default']
-    options = question['options']
-    selected = question.get('selected', 0)
-    helptext = question['helptext']
-    if helptext:
-        functions.append('CMD_HELP')
-
-    menu_items = []
-    keys = dict()
-
-    for cmd in functions:
-        if not CMDS.get(cmd, False):
-            raise AppArmorException(_('PromptUser: Unknown command %s') % cmd)
-
-        menutext = CMDS[cmd]
-
-        key = get_translated_hotkey(menutext).lower()
-        # Duplicate hotkey
-        if keys.get(key, False):
-            raise AppArmorException(_('PromptUser: Duplicate hotkey for %s: %s ') % (cmd, menutext))
-
-        keys[key] = cmd
-
-        if default and default == cmd:
-            menutext = '[%s]' % menutext
-
-        menu_items.append(menutext)
-
-    default_key = 0
-    if default and CMDS[default]:
-        defaulttext = CMDS[default]
-        defmsg = _('PromptUser: Invalid hotkey in default item')
-
-        default_key = get_translated_hotkey(defaulttext, defmsg).lower()
-
-        if not keys.get(default_key, False):
-            raise AppArmorException(_('PromptUser: Invalid default %s') % default)
-
-    widest = 0
-    header_copy = headers[:]
-    while header_copy:
-        header = header_copy.pop(0)
-        header_copy.pop(0)
-        if len(header) > widest:
-            widest = len(header)
-    widest += 1
-
-    formatstr = '%-' + str(widest) + 's %s\n'
-
-    function_regexp = '^('
-    function_regexp += '|'.join(keys.keys())
-    if options:
-        function_regexp += '|\d'
-    function_regexp += ')$'
-
-    ans = 'XXXINVALIDXXX'
-    while not re.search(function_regexp, ans, flags=re.IGNORECASE):
-
-        prompt = '\n'
-        if title:
-            prompt += '= %s =\n\n' % title
-
-        if headers:
-            header_copy = headers[:]
-            while header_copy:
-                header = header_copy.pop(0)
-                value = header_copy.pop(0)
-                prompt += formatstr % (header + ':', value)
-            prompt += '\n'
-
-        if explanation:
-            prompt += explanation + '\n\n'
-
-        if options:
-            for index, option in enumerate(options):
-                if selected == index:
-                    format_option = ' [%s - %s]'
-                else:
-                    format_option = '  %s - %s '
-                prompt += format_option % (index + 1, option)
-                prompt += '\n'
-
-        prompt += ' / '.join(menu_items)
-
-        sys.stdout.write(prompt + '\n')
-
-        ans = getkey().lower()
-
-        if ans:
-            if ans == 'up':
-                if options and selected > 0:
-                    selected -= 1
-                ans = 'XXXINVALIDXXX'
-
-            elif ans == 'down':
-                if options and selected < len(options) - 1:
-                    selected += 1
-                ans = 'XXXINVALIDXXX'
-
-#             elif keys.get(ans, False) == 'CMD_HELP':
-#                 sys.stdout.write('\n%s\n' %helptext)
-#                 ans = 'XXXINVALIDXXX'
-
-            elif is_number(ans) == 10:
-                # If they hit return choose default option
-                ans = default_key
-
-            elif options and re.search('^\d$', ans):
-                ans = int(ans)
-                if ans > 0 and ans <= len(options):
-                    selected = ans - 1
-                ans = 'XXXINVALIDXXX'
-
-        if keys.get(ans, False) == 'CMD_HELP':
-            sys.stdout.write('\n%s\n' % helptext)
-            ans = 'again'
-
-    if keys.get(ans, False):
-        ans = keys[ans]
-
-    return ans, selected
-
 def is_number(number):
     try:
         return int(number)

-- 
AppArmor mailing list
[email protected]
Modify settings or unsubscribe at: 
https://lists.ubuntu.com/mailman/listinfo/apparmor

Reply via email to