def save_proxy_settings(self): proxy_url = urllib.parse.urlparse( self.gtk_builder_get('entry_proxy_url').get_text().strip()) proxy_username = self.gtk_builder_get( 'entry_proxy_username').get_text().strip() proxy_password = self.gtk_builder_get( 'entry_proxy_password').get_text().strip() if not proxy_url.geturl(): self.config['proxy.url'] = None os.environ.pop('HTTP_PROXY', None) os.environ.pop('HTTPS_PROXY', None) return if not (proxy_url.hostname and proxy_url.scheme): gui_utilities.show_dialog_warning( 'Invalid Proxy Settings', self.parent, 'The proxy url you have submitted is not valid.') return try: proxy_url.port except ValueError: gui_utilities.show_dialog_warning( 'Invalid Proxy Settings', self.parent, 'The port must be an integer between 1-65535 inclusive.') return netloc = proxy_url.netloc if proxy_username: netloc = '{}:{}@{}'.format(proxy_username, proxy_password, proxy_url.netloc) formatted_proxy_url = urllib.parse.urlunparse( (proxy_url.scheme, netloc, proxy_url.path, '', '', '')) self.config['proxy.url'] = formatted_proxy_url os.environ['HTTP_PROXY'] = formatted_proxy_url os.environ['HTTPS_PROXY'] = formatted_proxy_url
def _sender_precheck_attachment(self): attachment = self.config.get('mailer.attachment_file') if not attachment: return True if not os.path.isfile(attachment): gui_utilities.show_dialog_warning('Invalid Attachment', self.parent, 'The specified attachment file does not exist.') return False if not os.access(attachment, os.R_OK): gui_utilities.show_dialog_warning('Invalid Attachment', self.parent, 'The specified attachment file can not be read.') return False self.text_insert("File '{0}' will be attached to sent messages.\n".format(os.path.basename(attachment))) _, extension = os.path.splitext(attachment) extension = extension[1:] if self.config['remove_attachment_metadata'] and extension in ('docm', 'docx', 'pptm', 'pptx', 'xlsm', 'xlsx'): scrubber.remove_office_metadata(attachment) self.text_insert("Attachment file detected as MS Office 2007+, metadata has been removed.\n") md5 = hashlib.new('md5') sha1 = hashlib.new('sha1') with open(attachment, 'rb') as file_h: data = True while data: data = file_h.read(1024) md5.update(data) sha1.update(data) self.text_insert(" MD5: {0}\n".format(md5.hexdigest())) self.text_insert(" SHA1: {0}\n".format(sha1.hexdigest())) return True
def _sender_precheck_settings(self): required_settings = { 'mailer.webserver_url': 'Web Server URL', 'mailer.subject': 'Subject', 'mailer.html_file': 'Message HTML File' } target_type = self.config.get('mailer.target_type') if target_type == 'file': required_settings['mailer.target_file'] = 'Target CSV File' elif target_type == 'single': required_settings['mailer.target_email_address'] = 'Target Email Address' required_settings['mailer.target_name'] = 'Target Name' else: gui_utilities.show_dialog_warning('Invalid Target Type', self.parent, 'Please specify a target file or name and email address.') return False message_type = self.config.get('mailer.message_type') if not message_type in ('email', 'calendar_invite'): gui_utilities.show_dialog_warning('Invalid Message Type', self.parent, 'Please select a valid message type.') return False for setting, setting_name in required_settings.items(): if not self.config.get(setting): gui_utilities.show_dialog_warning("Missing Required Option: '{0}'".format(setting_name), self.parent, 'Return to the Config tab and set all required options') return if not setting.endswith('_file'): continue file_path = self.config[setting] if not (os.path.isfile(file_path) and os.access(file_path, os.R_OK)): gui_utilities.show_dialog_warning('Invalid Option Configuration', self.parent, "Setting: '{0}'\nReason: the file could not be read.".format(setting_name)) return False if not self.config.get('smtp_server'): gui_utilities.show_dialog_warning('Missing SMTP Server Setting', self.parent, 'Please configure the SMTP server') return False return True
def start_sftp_client(self): """ Start the client's preferred sftp client application. """ if not self.config['sftp_client']: gui_utilities.show_dialog_warning( 'Invalid SFTP Configuration', self, 'An SFTP client is not configured') return False command = str(self.config['sftp_client']) sftp_bin = shlex.split(command)[0] if not utilities.which(sftp_bin): self.logger.warning('could not locate the sftp binary: ' + sftp_bin) gui_utilities.show_dialog_warning( 'Invalid SFTP Configuration', self, "Could not find the SFTP binary '{0}'".format(sftp_bin)) return False try: command = command.format( server=self.config['server'], username=self.config['server_username'], web_root=self.config['server_config']['server.web_root']) except KeyError: pass self.logger.debug("starting sftp client command: {0}".format(command)) utilities.start_process(command, wait=False) return
def save_alert_settings(self): email_address = gui_utilities.gobject_get_value( self.gobjects['entry_email_address']) phone_number = gui_utilities.gobject_get_value( self.gobjects['entry_sms_phone_number']) sms_carrier = gui_utilities.gobject_get_value( self.gobjects['combobox_sms_carrier']) server_user = self.application.server_user if email_address and not utilities.is_valid_email_address( email_address): gui_utilities.show_dialog_warning( 'Invalid Email Address', self.parent, 'The email address you have entered is not valid.') return if phone_number: phone_number = ''.join(d for d in phone_number if d in string.digits) if len(phone_number) > 11: gui_utilities.show_dialog_warning( 'Invalid Phone Number', self.parent, 'The phone number must not contain more than 11 digits') return email_address = utilities.nonempty_string(email_address) phone_number = utilities.nonempty_string(phone_number) sms_carrier = utilities.nonempty_string(sms_carrier) self.application.rpc( 'db/table/set', 'users', server_user.id, ('email_address', 'phone_number', 'phone_carrier'), (email_address, phone_number, sms_carrier))
def _sender_precheck_settings(self): required_settings = { 'mailer.webserver_url': 'Web Server URL', 'mailer.subject': 'Subject', 'mailer.html_file': 'Message HTML File' } target_type = self.config.get('mailer.target_type') if target_type == 'file': required_settings['mailer.target_file'] = 'Target CSV File' elif target_type == 'single': required_settings['mailer.target_email_address'] = 'Target Email Address' required_settings['mailer.target_name'] = 'Target Name' else: gui_utilities.show_dialog_warning('Invalid Target Type', self.parent, 'Please specify a target file or name and email address.') return False message_type = self.config.get('mailer.message_type') if not message_type in ('email', 'calendar_invite'): gui_utilities.show_dialog_warning('Invalid Message Type', self.parent, 'Please select a valid message type.') return False for setting, setting_name in required_settings.items(): if not self.config.get(setting): gui_utilities.show_dialog_warning("Missing Required Option: '{0}'".format(setting_name), self.parent, 'Return to the Config tab and set all required options') return if not setting.endswith('_file'): continue file_path = self.config[setting] if not (os.path.isfile(file_path) and os.access(file_path, os.R_OK)): gui_utilities.show_dialog_warning('Invalid Option Configuration', self.parent, "Setting: '{0}'\nReason: the file could not be read.".format(setting_name)) return False if not self.config.get('smtp_server'): gui_utilities.show_dialog_warning('Missing SMTP Server Setting', self.parent, 'Please configure the SMTP server') return False return True
def _sender_precheck_attachment(self): attachment = self.config.get('mailer.attachment_file') if not attachment: return True if not os.path.isfile(attachment): gui_utilities.show_dialog_warning('Invalid Attachment', self.parent, 'The specified attachment file does not exist.') return False if not os.access(attachment, os.R_OK): gui_utilities.show_dialog_warning('Invalid Attachment', self.parent, 'The specified attachment file can not be read.') return False self.text_insert("File '{0}' will be attached to sent messages.\n".format(os.path.basename(attachment))) _, extension = os.path.splitext(attachment) extension = extension[1:] if self.config['remove_attachment_metadata'] and extension in ('docm', 'docx', 'pptm', 'pptx', 'xlsm', 'xlsx'): scrubber.remove_office_metadata(attachment) self.text_insert("Attachment file detected as MS Office 2007+, metadata has been removed.\n") md5 = hashlib.new('md5') sha1 = hashlib.new('sha1') with open(attachment, 'rb') as file_h: data = True while data: data = file_h.read(1024) md5.update(data) sha1.update(data) self.text_insert(" MD5: {0}\n".format(md5.hexdigest())) self.text_insert(" SHA1: {0}\n".format(sha1.hexdigest())) return True
def signal_button_clicked_verify(self, button): target_url = self.gobjects['entry_webserver_url'].get_text() error_description = None try: response = test_webserver_url(target_url, self.config['server_config']['server.secret_id']) except requests.exceptions.RequestException as error: if isinstance(error, requests.exceptions.ConnectionError): self.logger.warning('verify url attempt failed, could not connect') error_description = 'Could not connect to the server' elif isinstance(error, requests.exceptions.Timeout): self.logger.warning('verify url attempt failed, a timeout occurred') error_description = 'The HTTP request timed out' else: self.logger.warning('unknown verify url exception: ' + repr(error)) error_description = 'An unknown verify URL exception occurred' else: if response.status_code < 200 or response.status_code > 299: self.logger.warning("verify url HTTP error: {0} {1}".format(response.status_code, response.reason)) error_description = "HTTP status {0} {1}".format(response.status_code, response.reason) else: self.logger.debug("verify url HTTP status: {0} {1}".format(response.status_code, response.reason)) if error_description: gui_utilities.show_dialog_warning('Unable To Open The Web Server URL', self.parent, error_description) else: gui_utilities.show_dialog_info('Successfully Opened The Web Server URL', self.parent) return
def signal_treeview_key_pressed(self, widget, event): if event.type != Gdk.EventType.KEY_PRESS: return treeview = self.gobjects['treeview_campaigns'] keyval = event.get_keyval()[1] if event.get_state() == Gdk.ModifierType.CONTROL_MASK: if keyval == Gdk.KEY_c: gui_utilities.gtk_treeview_selection_to_clipboard(treeview) elif keyval == Gdk.KEY_F5: self.load_campaigns() self._highlight_campaign(self.config.get('campaign_name')) elif keyval == Gdk.KEY_Delete: treeview_selection = treeview.get_selection() (model, tree_iter) = treeview_selection.get_selected() if not tree_iter: return campaign_id = model.get_value(tree_iter, 0) if self.config.get('campaign_id') == campaign_id: gui_utilities.show_dialog_warning('Can Not Delete Campaign', self.dialog, 'Can not delete the current campaign.') return if not gui_utilities.show_dialog_yes_no('Delete This Campaign?', self.dialog, 'This action is irreversible, all campaign data will be lost.'): return self.parent.rpc('campaign/delete', campaign_id) self.load_campaigns() self._highlight_campaign(self.config.get('campaign_name'))
def _sender_precheck_campaign(self): campaign = self.application.rpc.remote_table_row("campaigns", self.config["campaign_id"]) if campaign.expiration and campaign.expiration < datetime.datetime.utcnow(): gui_utilities.show_dialog_warning( "Campaign Is Expired", self.parent, "The current campaign has already expired" ) return False return True
def signal_button_clicked(self, button): if self._creation_assistant is not None: gui_utilities.show_dialog_warning('Campaign Creation Assistant', self.dialog, 'The campaign creation assistant is already active.') return assistant = CampaignAssistant(self.application) assistant.assistant.set_transient_for(self.dialog) assistant.assistant.set_modal(True) assistant.assistant.connect('destroy', self.signal_assistant_destroy, assistant) assistant.interact() self._creation_assistant = assistant
def export_table_to_xlsx_worksheet(self, worksheet): """Export the data represented by the view to a XLSX worksheet.""" 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 store = self.gobjects['treeview_campaign'].get_model() columns = dict(enumerate(('UID',) + self.view_columns)) worksheet.set_column(0, len(columns), 30) export.liststore_to_xlsx_worksheet(store, worksheet, columns) self.loader_thread_lock.release()
def export_table_to_xlsx_worksheet(self, worksheet): """Export the data represented by the view to a XLSX worksheet.""" 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 store = self.gobjects['treeview_campaign'].get_model() columns = dict(enumerate(('UID',) + self.view_columns)) worksheet.set_column(0, len(columns), 30) export.liststore_to_xlsx_worksheet(store, worksheet, columns) self.loader_thread_lock.release()
def save_sms_settings(self): phone_number = gui_utilities.gobject_get_value(self.gobjects['entry_sms_phone_number']) sms_carrier = gui_utilities.gobject_get_value(self.gobjects['combobox_sms_carrier']) server_user = self.application.server_user if phone_number: phone_number = ''.join(d for d in phone_number if d in string.digits) if len(phone_number) > 11: gui_utilities.show_dialog_warning('Invalid Phone Number', self.parent, 'The phone number must not contain more than 11 digits') return phone_number = utilities.nonempty_string(phone_number) sms_carrier = utilities.nonempty_string(sms_carrier) self.application.rpc('db/table/set', 'users', server_user.id, ('phone_number', 'phone_carrier'), (phone_number, sms_carrier))
def signal_button_clicked_export(self, button): if 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 = gui_utilities.UtilityFileChooser('Export Data', self.parent) file_name = self.config['campaign_name'] + '.csv' response = dialog.run_quick_save(file_name) dialog.destroy() if not response: return destination_file = response['target_path'] export.treeview_liststore_to_csv(self.gobjects['treeview_campaign'], destination_file)
def signal_button_clicked_export(self, button): if isinstance(self.row_loader_thread, threading.Thread) and self.row_loader_thread.is_alive(): gui_utilities.show_dialog_warning('Can Not Export Rows While Loading', self.parent) return dialog = gui_utilities.UtilityFileChooser('Export Data', self.parent) file_name = self.config['campaign_name'] + '.csv' response = dialog.run_quick_save(file_name) dialog.destroy() if not response: return destination_file = response['target_filename'] export.treeview_liststore_to_csv(self.gobjects['treeview_campaign'], destination_file)
def signal_popup_menu_activate_show_documentation(self, _): named_row = self._selected_named_row if named_row is None or named_row.type != _ROW_TYPE_PLUGIN: return if not named_row.installed: gui_utilities.show_dialog_warning('No Documentation', self.window, 'This plugin has no documentation.') return try: PluginDocumentationWindow(self.application, named_row.id) except FileNotFoundError as error: self.logger.warning(error.strerror) gui_utilities.show_dialog_warning('No Documentation', self.window, error.strerror.capitalize() + '.')
def _prompt_to_delete_row(self, treeview, selection): (model, tree_iter) = selection.get_selected() if not tree_iter: return campaign_id = model.get_value(tree_iter, 0) if self.config.get('campaign_id') == campaign_id: gui_utilities.show_dialog_warning('Can Not Delete Campaign', self.dialog, 'Can not delete the current campaign.') return if not gui_utilities.show_dialog_yes_no('Delete This Campaign?', self.dialog, 'This action is irreversible, all campaign data will be lost.'): return self.application.emit('campaign-delete', campaign_id) self.load_campaigns() self._highlight_campaign(self.config.get('campaign_name'))
def _prompt_to_delete_row(self, treeview, selection): (model, tree_iter) = selection.get_selected() if not tree_iter: return campaign_id = model.get_value(tree_iter, 0) if self.config.get('campaign_id') == campaign_id: gui_utilities.show_dialog_warning('Can Not Delete Campaign', self.dialog, 'Can not delete the current campaign.') return if not gui_utilities.show_dialog_yes_no('Delete This Campaign?', self.dialog, 'This action is irreversible, all campaign data will be lost.'): return self.application.emit('campaign-delete', campaign_id) self.load_campaigns() self._highlight_campaign(self.config.get('campaign_name'))
def signal_button_clicked(self, button): campaign_name_entry = self.gobjects['entry_new_campaign_name'] campaign_name = campaign_name_entry.get_property('text') if not campaign_name: gui_utilities.show_dialog_warning('Invalid Campaign Name', self.dialog, 'Please specify a new campaign name') return try: self.parent.rpc('campaign/new', campaign_name) except AdvancedHTTPServer.AdvancedHTTPServerRPCError: gui_utilities.show_dialog_error('Failed To Create New Campaign', self.dialog, 'Encountered an error creating the new campaign') return campaign_name_entry.set_property('text', '') self.load_campaigns() self._highlight_campaign(campaign_name)
def signal_button_clicked(self, button): campaign_name_entry = self.gobjects['entry_new_campaign_name'] campaign_name = campaign_name_entry.get_property('text') if not campaign_name: gui_utilities.show_dialog_warning('Invalid Campaign Name', self.dialog, 'Please specify a new campaign name') return try: self.parent.rpc('campaign/new', campaign_name) except: gui_utilities.show_dialog_error('Failed To Create New Campaign', self.dialog, 'Encountered an error creating the new campaign') return campaign_name_entry.set_property('text', '') self.load_campaigns() self._highlight_campaign(campaign_name)
def signal_activate_popup_menu_delete(self, action): if isinstance(self.row_loader_thread, threading.Thread) and self.row_loader_thread.is_alive(): gui_utilities.show_dialog_warning('Can Not Delete Rows While Loading', self.parent) return treeview = self.gobjects['treeview_campaign'] selection = treeview.get_selection() (model, tree_iter) = selection.get_selected() if not tree_iter: return row_id = model.get_value(tree_iter, 0) if not gui_utilities.show_dialog_yes_no('Delete This Row?', self.parent, 'This information will be lost'): return self.parent.rpc(self.remote_table_name + '/delete', row_id) self.load_campaign_information(force=True)
def signal_button_clicked_verify(self, button): target_url = self.gobjects['entry_webserver_url'].get_text() try: parsed_url = urlparse.urlparse(target_url) query = urlparse.parse_qs(parsed_url.query) query['id'] = [self.config['server_config']['server.secret_id']] query = urllib.urlencode(query, True) target_url = urlparse.urlunparse((parsed_url.scheme, parsed_url.netloc, parsed_url.path, parsed_url.params, query, parsed_url.fragment)) urllib2.urlopen(target_url, timeout=5) except: gui_utilities.show_dialog_warning('Unable To Open The Web Server URL', self.parent) return gui_utilities.show_dialog_info('Successfully Opened The Web Server URL', self.parent) return
def _prompt_to_delete_row(self, treeview, _): if isinstance(self.loader_thread, threading.Thread) and self.loader_thread.is_alive(): gui_utilities.show_dialog_warning('Can Not Delete Rows While Loading', self.parent) return model = treeview.get_model() row_ids = [model.get_value(ti, 0) for ti in gui_utilities.gtk_treeview_selection_iterate(treeview)] if len(row_ids) == 0: return elif len(row_ids) == 1: message = 'Delete This Row?' else: message = "Delete These {0:,} Rows?".format(len(row_ids)) if not gui_utilities.show_dialog_yes_no(message, self.parent, 'This information will be lost.'): return self.application.emit(self.table_name[:-1] + '-delete', row_ids)
def _prompt_to_delete_row(self, treeview, _): if isinstance(self.loader_thread, threading.Thread) and self.loader_thread.is_alive(): gui_utilities.show_dialog_warning('Can Not Delete Rows While Loading', self.parent) return model = treeview.get_model() row_ids = [model.get_value(ti, 0) for ti in gui_utilities.gtk_treeview_selection_iterate(treeview)] if len(row_ids) == 0: return elif len(row_ids) == 1: message = 'Delete This Row?' else: message = "Delete These {0:,} Rows?".format(len(row_ids)) if not gui_utilities.show_dialog_yes_no(message, self.parent, 'This information will be lost.'): return self.application.emit(self.table_name[:-1] + '-delete', row_ids)
def export_table_to_xlsx_worksheet(self, worksheet, title_format): """ Export the data represented by the view to an XLSX worksheet. :param worksheet: The destination sheet for the store's data. :type worksheet: :py:class:`xlsxwriter.worksheet.Worksheet` :param title_format: The formatting to use for the title row. :type title_format: :py:class:`xlsxwriter.format.Format` """ 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 store = self.gobjects['treeview_campaign'].get_model() columns = dict(enumerate(('UID',) + self.view_columns)) export.liststore_to_xlsx_worksheet(store, worksheet, columns, title_format) self.loader_thread_lock.release()
def _sender_precheck_settings(self): required_settings = { "mailer.webserver_url": "Web Server URL", "mailer.subject": "Subject", "mailer.html_file": "Message HTML File", } target_type = self.config.get("mailer.target_type") if target_type == "file": required_settings["mailer.target_file"] = "Target CSV File" elif target_type == "single": required_settings["mailer.target_email_address"] = "Target Email Address" required_settings["mailer.target_name"] = "Target Name" else: gui_utilities.show_dialog_warning( "Invalid Target Type", self.parent, "Please specify a target file or name and email address." ) return False message_type = self.config.get("mailer.message_type") if not message_type in ("email", "calendar_invite"): gui_utilities.show_dialog_warning( "Invalid Message Type", self.parent, "Please select a valid message type." ) return False for setting, setting_name in required_settings.items(): if not self.config.get(setting): gui_utilities.show_dialog_warning( "Missing Required Option: '{0}'".format(setting_name), self.parent, "Return to the Config tab and set all required options", ) return if not setting.endswith("_file"): continue file_path = self.config[setting] if not (os.path.isfile(file_path) and os.access(file_path, os.R_OK)): gui_utilities.show_dialog_warning( "Invalid Option Configuration", self.parent, "Setting: '{0}'\nReason: the file could not be read.".format(setting_name), ) return False if not self.config.get("smtp_server"): gui_utilities.show_dialog_warning( "Missing SMTP Server Setting", self.parent, "Please configure the SMTP server" ) return False return True
def save_alert_settings(self): email_address = gui_utilities.gobject_get_value(self.gobjects['entry_email_address']) phone_number = gui_utilities.gobject_get_value(self.gobjects['entry_sms_phone_number']) sms_carrier = gui_utilities.gobject_get_value(self.gobjects['combobox_sms_carrier']) server_user = self.application.server_user if email_address and not utilities.is_valid_email_address(email_address): gui_utilities.show_dialog_warning('Invalid Email Address', self.parent, 'The email address you have entered is not valid.') return if phone_number: phone_number = ''.join(d for d in phone_number if d in string.digits) if len(phone_number) > 11: gui_utilities.show_dialog_warning('Invalid Phone Number', self.parent, 'The phone number must not contain more than 11 digits') return email_address = utilities.nonempty_string(email_address) phone_number = utilities.nonempty_string(phone_number) sms_carrier = utilities.nonempty_string(sms_carrier) self.application.rpc('db/table/set', 'users', server_user.id, ('email_address', 'phone_number', 'phone_carrier'), (email_address, phone_number, sms_carrier))
def verify_sms_settings(self): phone_number = gui_utilities.gobject_get_value(self.gobjects['entry_sms_phone_number']) phone_number_set = bool(phone_number) sms_carrier_set = bool(self.gobjects['combobox_sms_carrier'].get_active() > 0) if phone_number_set ^ sms_carrier_set: gui_utilities.show_dialog_warning('Missing Information', self.parent, 'Both a phone number and a valid carrier must be specified') if 'sms_phone_number' in self.config: del self.config['sms_phone_number'] if 'sms_carrier' in self.config: del self.config['sms_carrier'] elif phone_number_set and sms_carrier_set: phone_number = ''.join(d for d in phone_number if d in string.digits) if len(phone_number) != 10: gui_utilities.show_dialog_warning('Invalid Phone Number', self.parent, 'The phone number must contain exactly 10 digits') return username = self.config['server_username'] self.application.rpc('users/set', username, ('phone_number', 'phone_carrier'), (phone_number, self.config['sms_carrier']))
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 check_totp(self, _, window, entry, new_otp, this_user): if not new_otp.verify(entry.get_text().strip()): gui_utilities.show_dialog_warning( 'Incorrect TOTP', self.application.get_active_window(), 'The specified TOTP code is invalid. Make sure your time\n'\ + 'is correct, rescan the QR code and try again.' ) return self.application.rpc.remote_table_row_set('users', this_user['id'], {'otp_secret': new_otp.secret}) gui_utilities.show_dialog_info( 'TOTP Enrollment', self.application.get_active_window(), 'Successfully set the TOTP secret. Your account is now enrolled\n'\ + 'in two factor authentication. You will be prompted to enter the\n' + 'value the next time you login.' ) window.destroy()
def _export_lock(self): show_dialog_warning = lambda: gui_utilities.show_dialog_warning('Export Failed', self.parent, 'Can not export data while loading.') if not self.loader_thread_lock.acquire(False): show_dialog_warning() return False if isinstance(self.loader_thread, threading.Thread) and self.loader_thread.is_alive(): self.loader_thread_lock.release() show_dialog_warning() return False return True
def _export_lock(self): show_dialog_warning = lambda: gui_utilities.show_dialog_warning('Export Failed', self.parent, 'Can not export data while loading.') if not self.loader_thread_lock.acquire(False): show_dialog_warning() return False if isinstance(self.loader_thread, threading.Thread) and self.loader_thread.is_alive(): self.loader_thread_lock.release() show_dialog_warning() return False return True
def check_totp(self, _, window, entry, new_otp, this_user): if not new_otp.verify(entry.get_text().strip()): gui_utilities.show_dialog_warning( 'Incorrect TOTP', self.application.get_active_window(), 'The specified TOTP code is invalid. Make sure your time\n'\ + 'is correct, rescan the QR code and try again.' ) return rpc.remote_table_row_set('users', this_user['id'], {'otp_secret': new_otp.secret}) gui_utilities.show_dialog_info( 'TOTP Enrollment', self.application.get_active_window(), 'Successfully set the TOTP secret. Your account is now enrolled\n'\ + 'in two factor authentication. You will be prompted to enter the\n' + 'value the next time you login.' ) window.destroy()
def _prompt_to_delete_row(self, treeview, selection): if isinstance(self.loader_thread, threading.Thread) and self.loader_thread.is_alive(): gui_utilities.show_dialog_warning('Can Not Delete Rows While Loading', self.parent) return (model, tree_paths) = selection.get_selected_rows() if not tree_paths: return tree_iters = map(model.get_iter, tree_paths) row_ids = [model.get_value(ti, 0) for ti in tree_iters] if len(row_ids) == 1: message = 'Delete This Row?' else: message = "Delete These {0:,} Rows?".format(len(row_ids)) if not gui_utilities.show_dialog_yes_no(message, self.parent, 'This information will be lost.'): return if len(row_ids) == 1: self.rpc('db/table/delete', self.remote_table_name, row_ids[0]) else: self.rpc('db/table/delete/multi', self.remote_table_name, row_ids) self.load_campaign_information()
def signal_button_clicked(self, button): campaign_name_entry = self.gobjects['entry_new_campaign_name'] campaign_name = campaign_name_entry.get_property('text') if not campaign_name: gui_utilities.show_dialog_warning('Invalid Campaign Name', self.dialog, 'Please specify a new campaign name') return try: self.parent.rpc('campaign/new', campaign_name) except: gui_utilities.show_dialog_error('Failed To Create New Campaign', self.dialog, 'Encountered an error creating the new campaign') return campaign_name_entry.set_property('text', '') self.load_campaigns() treeview = self.gobjects['treeview_campaigns'] store = treeview.get_model() store_iter = store.get_iter_first() while store_iter: if campaign_name == store.get_value(store_iter, 1): treeview.set_cursor(store.get_path(store_iter), None, False) break store_iter = store.iter_next(store_iter)
def signal_button_clicked_verify_spf(self, button): sender_email = self.gobjects['entry_source_email_smtp'].get_text() if not utilities.is_valid_email_address(sender_email): gui_utilities.show_dialog_warning('Warning', self.parent, 'Can not check SPF records for an invalid source email address.\n') return True spf_test_ip = mailer.guess_smtp_server_address(self.config['smtp_server'], (self.config['ssh_server'] if self.config['smtp_ssh_enable'] else None)) if not spf_test_ip: gui_utilities.show_dialog_warning('Warning', self.parent, 'Skipping spf policy check because the smtp server address could not be reliably detected') return True spf_test_sender, spf_test_domain = sender_email.split('@') try: spf_test = spf.SenderPolicyFramework(spf_test_ip, spf_test_domain, spf_test_sender) spf_result = spf_test.check_host() except spf.SPFError as error: gui_utilities.show_dialog_warning('Warning', self.parent, "Done, encountered exception: {0}.\n".format(error.__class__.__name__)) return True if not spf_result: gui_utilities.show_dialog_info('SPF Check Results', self.parent, 'No SPF records found.') else: if spf_result is 'fail': gui_utilities.show_dialog_info('SPF Check Results:', self.parent, 'SPF exists with a hard fail. Your messages will probably be blocked.') elif spf_result is 'softfail': gui_utilities.show_dialog_info('SPF Check Results', self.parent, 'SPF Exists with a soft fail. Your messages have strong possiblity of being blocked. Check your logs.') return True return True
def _sender_precheck_settings(self): required_settings = { 'mailer.webserver_url': 'Web Server URL', 'mailer.company_name': 'Company Name', 'mailer.subject': 'Subject', 'mailer.html_file': 'Message HTML File', 'mailer.target_file': 'Target CSV File' } for setting, setting_name in required_settings.items(): if not self.config.get(setting): gui_utilities.show_dialog_warning( "Missing Required Option: '{0}'".format(setting_name), self.parent, 'Return to the Config tab and set all required options') return if not setting.endswith('_file'): continue file_path = self.config[setting] if not (os.path.isfile(file_path) and os.access(file_path, os.R_OK)): gui_utilities.show_dialog_warning( 'Invalid Option Configuration', self.parent, "Setting: '{0}'\nReason: the file could not be read.". format(setting_name)) return False if not self.config.get('smtp_server'): gui_utilities.show_dialog_warning( 'Missing SMTP Server Setting', self.parent, 'Please configure the SMTP server') return False return True
def signal_button_clicked_verify_spf(self, button): sender_email = self.gobjects['entry_source_email_smtp'].get_text() if not utilities.is_valid_email_address(sender_email): gui_utilities.show_dialog_warning('Warning', self.parent, 'Can not check SPF records for an invalid source email address.\n') return True spf_test_ip = mailer.guess_smtp_server_address(self.config['smtp_server'], (self.config['ssh_server'] if self.config['smtp_ssh_enable'] else None)) if not spf_test_ip: gui_utilities.show_dialog_warning('Warning', self.parent, 'Skipping spf policy check because the smtp server address could not be reliably detected') return True spf_test_sender, spf_test_domain = sender_email.split('@') try: spf_test = spf.SenderPolicyFramework(spf_test_ip, spf_test_domain, spf_test_sender) spf_result = spf_test.check_host() except spf.SPFError as error: gui_utilities.show_dialog_warning('Warning', self.parent, "Done, encountered exception: {0}.\n".format(error.__class__.__name__)) return True if not spf_result: gui_utilities.show_dialog_info('SPF Check Results', self.parent, 'No SPF records found.') else: if spf_result is 'fail': gui_utilities.show_dialog_info('SPF Check Results:', self.parent, 'SPF exists with a hard fail. Your messages will probably be blocked.') elif spf_result is 'softfail': gui_utilities.show_dialog_info('SPF Check Results', self.parent, 'SPF Exists with a soft fail. Your messages have strong possiblity of being blocked. Check your logs.') return True return True
def _prompt_to_delete_row(self): selection = self.gobjects['treeview_campaign'].get_selection() if not selection.count_selected_rows(): return if isinstance(self.loader_thread, threading.Thread) and self.loader_thread.is_alive(): gui_utilities.show_dialog_warning('Can Not Delete Rows While Loading', self.parent) return (model, tree_paths) = selection.get_selected_rows() if not tree_paths: return tree_iters = map(model.get_iter, tree_paths) row_ids = map(lambda ti: model.get_value(ti, 0), tree_iters) if len(row_ids) == 1: message = 'Delete This Row?' else: message = "Delete These {0:,} Rows?".format(len(row_ids)) if not gui_utilities.show_dialog_yes_no(message, self.parent, 'This information will be lost.'): return for row_id in row_ids: self.parent.rpc(self.remote_table_name + '/delete', row_id) self.load_campaign_information(force=True)
def signal_button_clicked_sender_start(self, button): required_settings = { 'mailer.webserver_url': 'Web Server URL', 'mailer.company_name': 'Company Name', 'mailer.source_email': 'Source Email', 'mailer.subject': 'Friendly Alias', 'mailer.html_file': 'Message HTML File', 'mailer.target_file': 'Target CSV File' } for setting, setting_name in required_settings.items(): if not self.config.get(setting): gui_utilities.show_dialog_warning("Missing Required Option: '{0}'".format(setting_name), self.parent, 'Return to the Config tab and set all required options') return if not setting.endswith('_file'): continue file_path = self.config[setting] if not (os.path.isfile(file_path) and os.access(file_path, os.R_OK)): gui_utilities.show_dialog_warning('Invalid Option Configuration', self.parent, "Setting: '{0}'\nReason: File could not be read".format(setting_name)) return if not self.config.get('smtp_server'): gui_utilities.show_dialog_warning('Missing SMTP Server Setting', self.parent, 'Please configure the SMTP server') return if self.sender_thread: return self.parent.save_config() self.gobjects['button_mail_sender_start'].set_sensitive(False) self.gobjects['button_mail_sender_stop'].set_sensitive(True) self.progressbar.set_fraction(0) self.sender_thread = MailSenderThread(self.config, self.config['mailer.target_file'], self, self.parent.rpc) # Connect to the SMTP server if self.config['smtp_ssh_enable']: while True: self.text_insert('Connecting To SSH... ') login_dialog = KingPhisherClientSSHLoginDialog(self.config, self.parent) login_dialog.objects_load_from_config() response = login_dialog.interact() if response == Gtk.ResponseType.CANCEL: self.sender_start_failure(text='Canceled.\n') return if self.sender_thread.server_ssh_connect(): self.text_insert('Done.\n') break self.sender_start_failure('Failed to connect to SSH', 'Failed.\n') self.text_insert('Connecting To SMTP Server... ') if not self.sender_thread.server_smtp_connect(): self.sender_start_failure('Failed to connect to SMTP', 'Failed.\n') return self.text_insert('Done.\n') parsed_target_url = urlparse.urlparse(self.config['mailer.webserver_url']) landing_page_hostname = parsed_target_url.netloc landing_page = parsed_target_url.path landing_page = landing_page.lstrip('/') self.parent.rpc('campaign/landing_page/new', self.config['campaign_id'], landing_page_hostname, landing_page) self.sender_thread.start() self.gobjects['togglebutton_mail_sender_pause'].set_sensitive(True)
def signal_button_clicked_verify(self, button): target_url = self.gobjects['entry_webserver_url'].get_text() try: test_webserver_url(target_url, self.config['server_config']['server.secret_id']) except Exception as error: error_description = None if isinstance(error, urllib.request.URLError) and hasattr(error, 'reason') and isinstance(error.reason, Exception): error = error.reason if isinstance(error, urllib.request.HTTPError) and error.getcode(): self.logger.warning("verify url HTTPError: {0} {1}".format(error.getcode(), error.reason)) error_description = "HTTP status {0} {1}".format(error.getcode(), error.reason) elif isinstance(error, socket.gaierror): self.logger.warning('verify url attempt failed, socket.gaierror') error_description = error.args[-1] elif isinstance(error, socket.timeout): self.logger.warning('verify url attempt failed, connection timeout occurred') error_description = 'Connection timed out' else: self.logger.warning('unknown verify url exception: ' + repr(error)) gui_utilities.show_dialog_warning('Unable To Open The Web Server URL', self.parent, error_description) return gui_utilities.show_dialog_info('Successfully Opened The Web Server URL', self.parent) return
def signal_button_clicked_verify(self, button): target_url = self.gobjects["entry_webserver_url"].get_text() error_description = None if re.match(r"^\s+", target_url): target_url = target_url.strip() self.gobjects["entry_webserver_url"].set_text(target_url) for _ in range(1): if not target_url.strip().startswith("http"): error_description = "The web server URL is invalid" break try: response = test_webserver_url(target_url, self.config["server_config"]["server.secret_id"]) except (requests.exceptions.ConnectionError, requests.exceptions.RequestException) as error: if isinstance(error, requests.exceptions.ConnectionError): self.logger.warning("verify url attempt failed, could not connect") error_description = "Could not connect to the server" elif isinstance(error, requests.exceptions.Timeout): self.logger.warning("verify url attempt failed, a timeout occurred") error_description = "The HTTP request timed out" else: self.logger.warning("unknown verify url exception: " + repr(error)) error_description = "An unknown verify URL exception occurred" break if not response.ok: self.logger.warning("verify url HTTP error: {0} {1}".format(response.status_code, response.reason)) error_description = "HTTP status {0} {1}".format(response.status_code, response.reason) break self.logger.debug("verify url HTTP status: {0} {1}".format(response.status_code, response.reason)) if error_description: gui_utilities.show_dialog_warning("Unable To Open The Web Server URL", self.parent, error_description) else: gui_utilities.show_dialog_info("Successfully Opened The Web Server URL", self.parent) return
def signal_button_clicked_verify(self, button): target_url = self.gobjects['entry_webserver_url'].get_text() error_description = None if re.match(r'^\s+', target_url): target_url = target_url.strip() self.gobjects['entry_webserver_url'].set_text(target_url) for _ in range(1): if not target_url.strip().startswith('http'): error_description = 'The web server URL is invalid' break try: response = test_webserver_url(target_url, self.config['server_config']['server.secret_id']) except (requests.exceptions.ConnectionError, requests.exceptions.RequestException) as error: if isinstance(error, requests.exceptions.ConnectionError): self.logger.warning('verify url attempt failed, could not connect') error_description = 'Could not connect to the server' elif isinstance(error, requests.exceptions.Timeout): self.logger.warning('verify url attempt failed, a timeout occurred') error_description = 'The HTTP request timed out' else: self.logger.warning('unknown verify url exception: ' + repr(error)) error_description = 'An unknown verify URL exception occurred' break if not response.ok: self.logger.warning("verify url HTTP error: {0} {1}".format(response.status_code, response.reason)) error_description = "HTTP status {0} {1}".format(response.status_code, response.reason) break self.logger.debug("verify url HTTP status: {0} {1}".format(response.status_code, response.reason)) if error_description: gui_utilities.show_dialog_warning('Unable To Open The Web Server URL', self.parent, error_description) else: gui_utilities.show_dialog_info('Successfully Opened The Web Server URL', self.parent) return
def start_sftp_client(self): """ Start the client's preferred sftp client application. """ if not self.config['sftp_client']: gui_utilities.show_dialog_warning('Invalid SFTP Configuration', self, 'An SFTP client is not configured') return False command = str(self.config['sftp_client']) sftp_bin = shlex.split(command)[0] if not utilities.which(sftp_bin): self.logger.warning('could not locate the sftp binary: ' + sftp_bin) gui_utilities.show_dialog_warning('Invalid SFTP Configuration', self, "Could not find the SFTP binary '{0}'".format(sftp_bin)) return False try: command = command.format( server=self.config['server'], username=self.config['server_username'], web_root=self.config['server_config']['server.web_root'] ) except KeyError: pass self.logger.debug("starting sftp client command: {0}".format(command)) utilities.start_process(command, wait=False) return
def _sender_precheck_settings(self): required_settings = { 'mailer.webserver_url': 'Web Server URL', 'mailer.subject': 'Subject', 'mailer.html_file': 'Message HTML File', 'mailer.target_file': 'Target CSV File' } for setting, setting_name in required_settings.items(): if not self.config.get(setting): gui_utilities.show_dialog_warning("Missing Required Option: '{0}'".format(setting_name), self.parent, 'Return to the Config tab and set all required options') return if not setting.endswith('_file'): continue file_path = self.config[setting] if not (os.path.isfile(file_path) and os.access(file_path, os.R_OK)): gui_utilities.show_dialog_warning('Invalid Option Configuration', self.parent, "Setting: '{0}'\nReason: the file could not be read.".format(setting_name)) return False if not self.config.get('smtp_server'): gui_utilities.show_dialog_warning('Missing SMTP Server Setting', self.parent, 'Please configure the SMTP server') return False return True
def _sender_precheck_campaign(self): campaign = self.application.rpc.remote_table_row('campaigns', self.config['campaign_id']) if campaign.expiration and campaign.expiration < datetime.datetime.utcnow(): gui_utilities.show_dialog_warning('Campaign Is Expired', self.parent, 'The current campaign has already expired') return False return True
def _show_dialog_busy(self): gui_utilities.show_dialog_warning('Currently Busy', self.window, 'An operation is already running.')