def import_message_data(self): """ Process a previously exported message archive file and restore the message data, settings, and applicable files from it. This function wraps the emission of the ``message-data-import`` signal. :return: Whether or not the message archive file was loaded from disk. :rtype: bool """ config_tab = self.tabs.get('config') if not config_tab: self.logger.warning('attempted to import message data while the config tab was unavailable') return False config_tab.objects_save_to_config() dialog = extras.FileChooserDialog('Import Message Configuration', self.parent) dialog.quick_add_filter('King Phisher Message Files', '*.kpm') dialog.quick_add_filter('All Files', '*') response = dialog.run_quick_open() dialog.destroy() if not response: return False target_file = response['target_path'] dialog = extras.FileChooserDialog('Destination Directory', self.parent) response = dialog.run_quick_select_directory() dialog.destroy() if not response: return False dest_dir = response['target_path'] if not self.emit('message-data-import', target_file, dest_dir): return False gui_utilities.show_dialog_info('Success', self.parent, 'Successfully imported the message.') return True
def import_message_data(self): """ Process a previously exported message archive file and restore the message data, settings, and applicable files from it. """ config_tab = self.tabs.get('config') if not config_tab: self.logger.warning('attempted to import message data while the config tab was unavailable') return config_prefix = config_tab.config_prefix config_tab.objects_save_to_config() dialog = extras.FileChooserDialog('Import Message Configuration', self.parent) dialog.quick_add_filter('King Phisher Message Files', '*.kpm') dialog.quick_add_filter('All Files', '*') response = dialog.run_quick_open() dialog.destroy() if not response: return target_file = response['target_path'] dialog = extras.FileChooserDialog('Destination Directory', self.parent) response = dialog.run_quick_select_directory() dialog.destroy() if not response: return dest_dir = response['target_path'] try: message_data = export.message_data_from_kpm(target_file, dest_dir) except KingPhisherInputValidationError as error: gui_utilities.show_dialog_error('Import Error', self.parent, error.message.capitalize() + '.') return config_keys = set(key for key in self.config.keys() if key.startswith(config_prefix)) config_types = dict(zip(config_keys, [type(self.config[key]) for key in config_keys])) for key, value in message_data.items(): key = config_prefix + key if not key in config_keys: continue self.config[key] = value config_keys.remove(key) for unset_key in config_keys: config_type = config_types[unset_key] if not config_type in (bool, dict, int, list, str, tuple): continue self.config[unset_key] = config_type() # set missing defaults for backwards compatibility if not self.config.get('mailer.message_type'): self.config['mailer.message_type'] = 'email' if not self.config.get('mailer.target_type'): self.config['mailer.target_type'] = 'file' config_tab.objects_load_from_config() gui_utilities.show_dialog_info('Success', self.parent, 'Successfully imported the message.')
def save_html_file(self, force_prompt=False): """ Save the contents from the editor into an HTML file if one is configured otherwise prompt to user to select a file to save as. The user may abort the operation by declining to select a file to save as if they are prompted to do so. :param force_prompt: Force prompting the user to select the file to save as. :rtype: bool :return: Whether the contents were saved or not. """ html_file = self.config.get('mailer.html_file') if not html_file or force_prompt: if html_file: current_name = os.path.basename(html_file) else: current_name = 'message.html' dialog = extras.FileChooserDialog('Save HTML File', self.parent) response = dialog.run_quick_save(current_name=current_name) dialog.destroy() if not response: return False html_file = response['target_path'] self.config['mailer.html_file'] = html_file text = self.textbuffer.get_text(self.textbuffer.get_start_iter(), self.textbuffer.get_end_iter(), False) with open(html_file, 'w') as file_h: file_h.write(text) self.toolbutton_save_html_file.set_sensitive(True) return True
def signal_kpm_select_clicked(self, _): dialog = extras.FileChooserDialog('Import Message Configuration', self.parent) dialog.quick_add_filter('King Phisher Message Files', '*.kpm') dialog.quick_add_filter('All Files', '*') response = dialog.run_quick_open() dialog.destroy() if not response: return False target_path = response['target_path'] self.gobjects['entry_kpm_file'].set_text(target_path) self._set_page_complete(self._get_kpm_path().is_valid) if not _kpm_file_path_is_valid(target_path): return # open the KPM for reading to extract the target URL for the assistant, # ignore the directory to allow the user to optionally only import the URL kpm = archive.ArchiveFile(target_path, 'r') if not kpm.has_file('message_config.json'): self.logger.warning( 'the kpm archive is missing the message_config.json file') return message_config = kpm.get_json('message_config.json') webserver_url = message_config.get('webserver_url') if not webserver_url: return self._set_webserver_url(webserver_url)
def export_message_data(self, path=None): """ Gather and prepare the components of the mailer tab to be exported into a King Phisher message (KPM) archive file suitable for restoring at a later point in time. If *path* is not specified, the user will be prompted to select one and failure to do so will prevent the message data from being exported. This function wraps the emission of the ``message-data-export`` signal. :param str path: An optional path of where to save the archive file to. :return: Whether or not the message archive file was written to disk. :rtype: bool """ config_tab = self.tabs.get('config') if not config_tab: self.logger.warning('attempted to export message data while the config tab was unavailable') return False if path is None: dialog = extras.FileChooserDialog('Export Message Configuration', self.parent) response = dialog.run_quick_save('message.kpm') dialog.destroy() if not response: return False path = response['target_path'] if not self.emit('message-data-export', path): return False gui_utilities.show_dialog_info('Success', self.parent, 'Successfully exported the message.') return True
def make_preview(self, _): mailer_tab = self.application.main_tabs['mailer'] config_tab = mailer_tab.tabs['config'] config_tab.objects_save_to_config() input_path = self.application.config['mailer.attachment_file'] if not (os.path.isfile(input_path) and os.access(input_path, os.R_OK)): gui_utilities.show_dialog_error( 'PDF Build Error', self.application.get_active_window(), 'Attachment path is invalid or is not readable.' ) return dialog = extras.FileChooserDialog('Save Generated PDF File', self.application.get_active_window()) response = dialog.run_quick_save('PDF Preview.pdf') dialog.destroy() if response is None: return output_path = response['target_path'] if not self.process_attachment_file(input_path, output_path): return gui_utilities.show_dialog_info( 'PDF Created', self.application.get_active_window(), 'Successfully created the PDF file.' )
def prompt_and_generate(self): config = self.config dialog_txt = 'Would you like to generate research data to submit to SecureState?' if not gui_utilities.show_dialog_yes_no( 'Phishing Data Collection', self.application.get_active_window(), dialog_txt): return get_stats = StatsGenerator(self.application.rpc) stats = get_stats.generate_stats() dialog = extras.FileChooserDialog('Save Anonymized Data', self.application.get_active_window()) file_name = 'anonymized_phishing_data.txt' response = dialog.run_quick_save(file_name) dialog.destroy() if not response['target_path']: return with open(response['target_path'], 'w') as file_h: file_h.write(stats) gui_utilities.show_dialog_info( 'Successfully Generated Data', self.application.get_active_window(), "Please review and email:\n{}\nto [email protected]". format(response['target_path'])) config['last_date'] = datetime.datetime.utcnow()
def signal_kpm_dest_folder_clicked(self, _): dialog = extras.FileChooserDialog('Destination Directory', self.parent) response = dialog.run_quick_select_directory() dialog.destroy() if not response: return False self.gobjects['entry_kpm_dest_folder'].set_text(response['target_path']) self._update_completion_status()
def signal_activate_export_credentials_msf_txt(self, _): dialog = extras.FileChooserDialog('Export Credentials', self.application.get_active_window()) file_name = self.config['campaign_name'] + '.txt' response = dialog.run_quick_save(file_name) dialog.destroy() if not response: return destination_file = response['target_path'] export.campaign_credentials_to_msf_txt(self.application.rpc, self.config['campaign_id'], destination_file)
def signal_menuitem_activate_config(self, _): dialog = extras.FileChooserDialog('Import Configuration File', self.dialog) response = dialog.run_quick_open() dialog.destroy() if response is None: return self.application.merge_config(response['target_path'], strict=False) self.objects_load_from_config()
def signal_activate_popup_menu_export(self, action): dialog = extras.FileChooserDialog('Export Graph', self.application.get_active_window()) file_name = self.config['campaign_name'] + '.png' response = dialog.run_quick_save(file_name) dialog.destroy() if not response: return destination_file = response['target_path'] self.figure.savefig(destination_file, format='png')
def export_campaign_xml(self): """Export the current campaign to an XML data file.""" dialog = extras.FileChooserDialog('Export Campaign XML Data', self) file_name = self.config['campaign_name'] + '.xml' response = dialog.run_quick_save(file_name) dialog.destroy() if not response: return destination_file = response['target_path'] export.campaign_to_xml(self.rpc, self.config['campaign_id'], destination_file)
def signal_kpm_select_clicked(self, _): dialog = extras.FileChooserDialog('Import Message Configuration', self.parent) dialog.quick_add_filter('King Phisher Message Files', '*.kpm') dialog.quick_add_filter('All Files', '*') response = dialog.run_quick_open() dialog.destroy() if not response: return False self.gobjects['entry_kpm_file'].set_text(response['target_path']) self._update_completion_status()
def signal_toolbutton_open(self, button): dialog = extras.FileChooserDialog('Choose File', self.parent) dialog.quick_add_filter('HTML Files', ['*.htm', '*.html']) dialog.quick_add_filter('All Files', '*') response = dialog.run_quick_open() dialog.destroy() if not response: return False self.config['mailer.html_file'] = response['target_path'] self.show_tab() return True
def signal_activate_popup_menu_insert_image(self, widget): dialog = extras.FileChooserDialog('Choose Image', self.parent) dialog.quick_add_filter('Images', ['*.gif', '*.jpeg', '*.jpg', '*.png']) dialog.quick_add_filter('All Files', '*') response = dialog.run_quick_open() dialog.destroy() if not response: return target_path = response['target_path'] target_path = escape_single_quote(target_path) text = "{{{{ inline_image('{0}') }}}}".format(target_path) return self.signal_activate_popup_menu_insert(widget, text)
def export_campaign_visit_geojson(self): """ Export the current campaign visit information to a GeoJSON data file. """ dialog = extras.FileChooserDialog('Export Campaign Visit GeoJSON Data', self) file_name = self.config['campaign_name'] + '.geojson' response = dialog.run_quick_save(file_name) dialog.destroy() if not response: return destination_file = response['target_path'] export.campaign_visits_to_geojson(self.rpc, self.config['campaign_id'], destination_file)
def signal_entry_activate_open_file(self, entry): dialog = extras.FileChooserDialog('Choose File', self.parent) if entry == self.gobjects.get('entry_html_file'): dialog.quick_add_filter('HTML Files', ['*.htm', '*.html']) elif entry == self.gobjects.get('entry_target_file'): dialog.quick_add_filter('CSV Files', '*.csv') dialog.quick_add_filter('All Files', '*') response = dialog.run_quick_open() dialog.destroy() if not response: return False entry.set_text(response['target_path']) return True
def signal_menuitem_activate_import_config(self, _): dialog = extras.FileChooserDialog('Import Configuration File', self.dialog) response = dialog.run_quick_open() dialog.destroy() if response is None: return config_path = response['target_path'] try: self.application.merge_config(config_path, strict=False) except Exception: self.logger.warning('failed to merge configuration file: ' + config_path, exc_info=True) gui_utilities.show_dialog_error('Invalid Configuration File', self.dialog, 'Could not import the configuration file.') else: self.objects_load_from_config()
def select_xml_campaign(self): """ Prompts the user with a file dialog window to select the King Phisher Campaign XML file to import. Validates the file to make sure it is a Campaign exported from King Phisher and is the correct version to import. """ dialog = extras.FileChooserDialog('Import Campaign from XML', self.window) dialog.quick_add_filter('King Phisher XML Campaign', '*.xml') dialog.quick_add_filter('All Files', '*') response = dialog.run_quick_open() dialog.destroy() if not response: return target_file = response['target_path'] self.entry_path.set_text(target_file) try: campaign_xml = ET.parse(target_file) except ET.ParseError as error: self.logger.error( "cannot import campaign: {0} is not a valid XML file".format( target_file), error) raise KingPhisherInputValidationError( "{0} is not a valid xml file".format(target_file)) root = campaign_xml.getroot() if root.tag != 'king_phisher': raise KingPhisherInputValidationError( 'File not a King Phisher Campaign XML Export') meta_data = root.find('metadata') if meta_data.find('version').text < '1.3': raise KingPhisherInputValidationError( 'Can only import XML Campaign data version 1.3 or higher') self.campaign_info = root.find('campaign') if not self.campaign_info: raise KingPhisherInputValidationError( 'XML file does not contain any campaign information') self.db_campaigns = self.rpc.graphql( "{ db { campaigns { edges { node { id, name } } } } }" )['db']['campaigns']['edges'] self.entry_campaign_name.set_text(self.campaign_info.find('name').text) self.thread_import_campaign = None if not self._check_campaign_name(self.campaign_info.find('name').text, verbose=True): self.button_import_campaign.set_sensitive(False) return self.button_import_campaign.set_sensitive(True)
def export_table_to_csv(self): """Export the data represented by the view to a CSV file.""" if not self._export_lock(): return dialog = extras.FileChooserDialog('Export Data', self.parent) file_name = self.config['campaign_name'] + '.csv' response = dialog.run_quick_save(file_name) dialog.destroy() if not response: self.loader_thread_lock.release() return destination_file = response['target_path'] store = self.gobjects['treeview_campaign'].get_model() columns = dict(enumerate(('UID', ) + self.view_columns)) export.liststore_to_csv(store, destination_file, columns) self.loader_thread_lock.release()
def export_campaign_xlsx(self): """Export the current campaign to an Excel compatible XLSX workbook.""" dialog = extras.FileChooserDialog('Export Campaign To Excel', self) file_name = self.config['campaign_name'] + '.xlsx' response = dialog.run_quick_save(file_name) dialog.destroy() if not response: return destination_file = response['target_path'] campaign_tab = self.tabs['campaign'] workbook = xlsxwriter.Workbook(destination_file) title_format = workbook.add_format({'bold': True, 'size': 18}) for tab_name, tab in campaign_tab.tabs.items(): if not isinstance(tab, CampaignViewGenericTableTab): continue tab.export_table_to_xlsx_worksheet(workbook.add_worksheet(tab_name), title_format) workbook.close()
def export_table_to_csv(self): """Export the data represented by the view to a CSV file.""" if not self.loader_thread_lock.acquire(False) or (isinstance(self.loader_thread, threading.Thread) and self.loader_thread.is_alive()): gui_utilities.show_dialog_warning('Can Not Export Rows While Loading', self.parent) return dialog = extras.FileChooserDialog('Export Data', self.parent) file_name = self.config['campaign_name'] + '.csv' response = dialog.run_quick_save(file_name) dialog.destroy() if not response: self.loader_thread_lock.release() return destination_file = response['target_path'] store = self.gobjects['treeview_campaign'].get_model() columns = dict(enumerate(('UID',) + self.view_columns)) export.liststore_to_csv(store, destination_file, columns) self.loader_thread_lock.release()
def select_path(self, application, entry_widget): dialog = extras.FileChooserDialog('Select ' + self.path_type.capitalize(), application.get_active_window()) if self.path_type.startswith('file-') and self.file_filters: for name, patterns in self.file_filters: dialog.quick_add_filter(name, patterns) dialog.quick_add_filter('All Files', '*') if self.path_type == 'directory': result = dialog.run_quick_select_directory() elif self.path_type == 'file-open': result = dialog.run_quick_open() elif self.path_type == 'file-save': result = dialog.run_quick_save() else: dialog.destroy() raise ValueError('path_type must be either \'directory\', \'file-open\', or \'file-save\'') dialog.destroy() if result is None: return entry_widget.set_text(result['target_path'])
def export_message_data(self): """ Gather and prepare the components of the mailer tab to be exported into a message archive file suitable for restoring at a later point in time. """ config_tab = self.tabs.get('config') if not config_tab: self.logger.warning('attempted to export message data while the config tab was unavailable') return config_prefix = config_tab.config_prefix config_tab.objects_save_to_config() dialog = extras.FileChooserDialog('Export Message Configuration', self.parent) response = dialog.run_quick_save('message.kpm') dialog.destroy() if not response: return message_config = {} config_keys = (key for key in self.config.keys() if key.startswith(config_prefix)) for config_key in config_keys: message_config[config_key[7:]] = self.config[config_key] export.message_data_to_kpm(message_config, response['target_path'])
def make_preview(self, _): input_path = self.application.config['mailer.attachment_file'] if not os.path.isfile(input_path) and os.access(input_path, os.R_OK): gui_utilities.show_dialog_error( 'PDF Build Error', self.application.get_active_window(), 'An invalid attachment file is specified.') return dialog = extras.FileChooserDialog('Save Generated PDF File', self.application.get_active_window()) response = dialog.run_quick_save('preview.pdf') dialog.destroy() if response is None: return output_path = response['target_path'] if not self.process_attachment_file(input_path, output_path): gui_utilities.show_dialog_error( 'PDF Build Error', self.application.get_active_window(), 'Failed to create the PDF file.') return gui_utilities.show_dialog_info('PDF Created', self.application.get_active_window(), 'Successfully created the PDF file.')
def signal_multi_set_directory(self, _): dialog = extras.FileChooserDialog('Destination Directory', self.dialog) response = dialog.run_quick_select_directory() dialog.destroy() if response: self.entry_directory.set_text(response['target_path'])
def select_xml_campaign(self): """ Prompts the user with a file dialog window to select the King Phisher Campaign XML file to import. Validates the file to make sure it is a Campaign exported from King Phisher and is the correct version to import. """ dialog = extras.FileChooserDialog('Import Campaign from XML', self.window) dialog.quick_add_filter('King Phisher XML Campaign', '*.xml') dialog.quick_add_filter('All Files', '*') response = dialog.run_quick_open() dialog.destroy() if not response: return target_file = response['target_path'] self.entry_path.set_text(target_file) try: campaign_xml = ET.parse(target_file) except ET.ParseError as error: self.logger.error( "cannot import campaign file: {0} (not a valid xml file)". format(target_file)) gui_utilities.show_dialog_error('Improper Format', self.window, 'File is not valid XML') return root = campaign_xml.getroot() if root.tag != 'king_phisher': self.logger.error( "cannot import campaign file: {0} (invalid root xml tag)". format(target_file)) gui_utilities.show_dialog_error( 'Improper Format', self.window, 'File is not a valid King Phisher XML campaign file') return meta_data = root.find('metadata') if meta_data.find('version').text < '1.3': self.logger.error( "cannot import campaign file: {0} (incompatible version)". format(target_file)) gui_utilities.show_dialog_error( 'Invalid Version', self.window, 'Cannot import XML campaign data less then version 1.3') return self.campaign_info = root.find('campaign') if not self.campaign_info: self.logger.error( "cannot import campaign file: {0} (no campaign data found)". format(target_file)) gui_utilities.show_dialog_error('No Campaign Data', self.window, 'No campaign data to import') return self.db_campaigns = self.rpc.graphql( "{ db { campaigns { edges { node { id, name } } } } }" )['db']['campaigns']['edges'] self.entry_campaign_name.set_text(self.campaign_info.find('name').text) self.thread_import_campaign = None if not self._check_campaign_name(self.campaign_info.find('name').text, verbose=True): self.button_import_campaign.set_sensitive(False) return self.button_import_campaign.set_sensitive(True)