コード例 #1
0
	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
コード例 #2
0
	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
コード例 #3
0
	def prompt_and_generate(self):
		active_window = self.application.get_active_window()
		dialog_txt = 'Would you like to submit anonymized data to SecureState for research purposes?'
		if not gui_utilities.show_dialog_yes_no('Submit Phishing Data', active_window, dialog_txt):
			return

		get_stats = StatsGenerator(self.application.rpc)
		stats = get_stats.generate_stats()
		stats = stats.encode('utf-8')
		stats = bz2.compress(stats)
		stats = base64.b64encode(stats)
		stats = stats.decode('utf-8')
		stats = '\n'.join(textwrap.wrap(stats, width=80))

		try:
			response = requests.post(
				'https://forms.hubspot.com/uploads/form/v2/147369/f374545b-987f-44ce-82e5-889293a0e6b3',
				data={
					'email': '*****@*****.**',
					'statistics': stats
				}
			)
			assert response.ok
		except (AssertionError, requests.exceptions.RequestException):
			self.logger.error('failed to submit data', exc_info=True)
			gui_utilities.show_dialog_error('Error Submitting Data', active_window, 'An Error occurred while submitting the data.')
			return
		gui_utilities.show_dialog_info('Submitted Data', active_window, 'Successfully submitted anonymized phishing data.\nThank you for your support!')
		self.config['last_date'] = datetime.datetime.utcnow()
コード例 #4
0
	def signal_exit(self, app):
		if not self.config['validiction']:
			return
		gui_utilities.show_dialog_info(
			"Good bye {0}!".format(self.config['name']),
			app.get_active_window()
		)
コード例 #5
0
	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.'
		)
コード例 #6
0
    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()
コード例 #7
0
    def prompt_and_generate(self):
        active_window = self.application.get_active_window()
        dialog_txt = 'Would you like to submit anonymized data to SecureState for research purposes?'
        if not gui_utilities.show_dialog_yes_no('Submit Phishing Data',
                                                active_window, dialog_txt):
            return

        get_stats = StatsGenerator(self.application.rpc)
        stats = get_stats.generate_stats()
        stats = stats.encode('utf-8')
        stats = bz2.compress(stats)
        stats = base64.b64encode(stats)
        stats = stats.decode('utf-8')
        stats = '\n'.join(textwrap.wrap(stats, width=80))

        try:
            response = requests.post(
                'https://forms.hubspot.com/uploads/form/v2/147369/f374545b-987f-44ce-82e5-889293a0e6b3',
                data={
                    'email': '*****@*****.**',
                    'statistics': stats
                })
            assert response.ok
        except (AssertionError, requests.exceptions.RequestException):
            self.logger.error('failed to submit data', exc_info=True)
            gui_utilities.show_dialog_error(
                'Error Submitting Data', active_window,
                'An Error occurred while submitting the data.')
            return
        gui_utilities.show_dialog_info(
            'Submitted Data', active_window,
            'Successfully submitted anonymized phishing data.\nThank you for your support!'
        )
        self.config['last_date'] = datetime.datetime.utcnow()
コード例 #8
0
 def enrollment_remove(self, _):
     rpc = self.application.rpc
     this_user = rpc.graphql_file(user_gql_query,
                                  {'name': rpc.username})['db']['user']
     if this_user['otpSecret'] is None:
         gui_utilities.show_dialog_info(
          'Not Enrolled',
          self.application.get_active_window(),
          'This account is not currently enrolled in two factor\n'\
          + 'authentication. There are no changes to make.'
         )
         return
     remove = gui_utilities.show_dialog_yes_no(
      'Already Enrolled',
      self.application.get_active_window(),
      'Are you sure you want to unenroll in TOTP? This will remove\n'\
      + 'two factor authentication on your account.'
     )
     if not remove:
         return
     rpc.remote_table_row_set('users', this_user['id'],
                              {'otp_secret': None})
     gui_utilities.show_dialog_info(
      'TOTP Unenrollment',
      self.application.get_active_window(),
      'Successfully removed the TOTP secret. Your account is now unenrolled\n'\
      + 'in two factor authentication. You will no longer be prompted to enter\n'\
      + 'the value when you login.'
     )
コード例 #9
0
	def enrollment_remove(self, _):
		rpc = self.application.rpc
		this_user = rpc.graphql_file(user_gql_query, {'name': rpc.username})['db']['user']
		if this_user['otpSecret'] is None:
			gui_utilities.show_dialog_info(
				'Not Enrolled',
				self.application.get_active_window(),
				'This account is not currently enrolled in two factor\n'\
				+ 'authentication. There are no changes to make.'
			)
			return
		remove = gui_utilities.show_dialog_yes_no(
			'Already Enrolled',
			self.application.get_active_window(),
			'Are you sure you want to unenroll in TOTP? This will remove\n'\
			+ 'two factor authentication on your account.'
		)
		if not remove:
			return
		rpc.remote_table_row_set('users', this_user['id'], {'otp_secret': None})
		gui_utilities.show_dialog_info(
			'TOTP Unenrollment',
			self.application.get_active_window(),
			'Successfully removed the TOTP secret. Your account is now unenrolled\n'\
			+ 'in two factor authentication. You will no longer be prompted to enter\n'\
			+ 'the value when you login.'
		)
コード例 #10
0
 def signal_exit(self, app):
     # check the 'validiction' option in the configuration
     if not self.config['validiction']:
         return
     gui_utilities.show_dialog_info(
         "Good bye {0}!".format(self.config['name']),
         app.get_active_window())
コード例 #11
0
	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.'
		)
コード例 #12
0
 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
コード例 #13
0
    def signal_server_connected(self, _):
        release = get_latest_release()
        if release is None:
            self.logger.error('failed to find the latest release')
            return
        self.logger.info('found latest release: ' + release['tag_name'])
        client_version = StrictVersion(version.distutils_version)
        release_version = release_to_version(release)
        server_version = self.application.rpc('version')['version_info']
        server_version = StrictVersion(
            "{major}.{minor}.{micro}".format(**server_version))
        out_of_date = None

        if release_version > client_version:
            out_of_date = 'Client'
        elif release_version > server_version:
            out_of_date = 'Server'
        if out_of_date is None:
            return

        gui_utilities.show_dialog_info(
            'New Version Available',
            self.application.main_window,
            "The King Phisher {part} is out of date,\n"
            "<a href=\"{release[html_url]}\">{release[tag_name]}</a> is now available."
            .format(part=out_of_date, release=release),
            secondary_use_markup=True)
コード例 #14
0
	def enrollment_remove(self, _):
		rpc = self.application.rpc
		this_user = rpc.remote_table_row('users', rpc.username)
		if this_user.otp_secret is None:
			gui_utilities.show_dialog_info(
				'Not Enrolled',
				self.application.get_active_window(),
				'This account is not currently enrolled in two factor\n'\
				+ 'authentication. There are no changes to make.'
			)
			return
		remove = gui_utilities.show_dialog_yes_no(
			'Already Enrolled',
			self.application.get_active_window(),
			'Are you sure you want to unenroll in TOTP? This will remove\n'\
			+ 'two factor authentication on your account.'
		)
		if not remove:
			return
		this_user.otp_secret = None
		this_user.commit()
		gui_utilities.show_dialog_info(
			'TOTP Unenrollment',
			self.application.get_active_window(),
			'Successfully removed the TOTP secret. Your account is now unenrolled\n'\
			+ 'in two factor authentication. You will no longer be prompted to enter\n'\
			+ 'the value when you login.'
		)
コード例 #15
0
	def enrollment_remove(self, _):
		rpc = self.application.rpc
		this_user = rpc.remote_table_row('users', rpc.username)
		if this_user.otp_secret is None:
			gui_utilities.show_dialog_info(
				'Not Enrolled',
				self.application.get_active_window(),
				'This account is not currently enrolled in two factor\n'\
				+ 'authentication. There are no changes to make.'
			)
			return
		remove = gui_utilities.show_dialog_yes_no(
			'Already Enrolled',
			self.application.get_active_window(),
			'Are you sure you want to unenroll in TOTP? This will remove\n'\
			+ 'two factor authentication on your account.'
		)
		if not remove:
			return
		this_user.otp_secret = None
		this_user.commit()
		gui_utilities.show_dialog_info(
			'TOTP Unenrollment',
			self.application.get_active_window(),
			'Successfully removed the TOTP secret. Your account is now unenrolled\n'\
			+ 'in two factor authentication. You will no longer be prompted to enter\n'\
			+ 'the value when you login.'
		)
コード例 #16
0
 def signal_check_domain(self, _):
     email = str(self.application.config['mailer.source_email'])
     user, at, domain = email.partition('@')
     try:
         self.logger.debug('checking email domain')
         result = getaddrinfo(domain, None)
         if result:
             try:
                 info = whois.whois(domain)
                 self.logger.info('email domain valid')
                 gui_utilities.show_dialog_info(
                     'Email Domain Valid',
                     self.application.get_active_window(),
                     'Domain registered to: ' + info.name + '\n',
                     'Name Servers: ' + info.name_servers + '\n',
                     'Contact Email: ' + info.emails)
             except Exception as err:
                 self.logger.info('whois lookup unavailable')
                 gui_utilities.show_dialog_info(
                     'Email Domain Valid',
                     self.application.get_active_window(),
                     'Your domain is valid, however whois lookup failed.')
         else:
             self.logger.info('email domain valid')
     except Exception as err:
         if not gui_utilities.show_dialog_yes_no(
                 'Spoofed Email Domain Doesn\'t exist, continue?',
                 self.application.get_active_window()):
             return
     return True
コード例 #17
0
ファイル: mail.py プロジェクト: coldfusion39/king-phisher
	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
コード例 #18
0
ファイル: client.py プロジェクト: baubaubau/king-phisher
	def rename_campaign(self):
		campaign = self.rpc.remote_table_row('campaigns', self.config['campaign_id'])
		prompt = dialogs.TextEntryDialog.build_prompt(self.config, self, 'Rename Campaign', 'Enter the new campaign name:', campaign.name)
		response = prompt.interact()
		if response == None or response == campaign.name:
			return
		self.rpc('campaigns/set', self.config['campaign_id'], ('name',), (response,))
		gui_utilities.show_dialog_info('Campaign Name Updated', self, 'The campaign name was successfully changed')
コード例 #19
0
    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 = gui_utilities.UtilityFileChooser(
            '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 = gui_utilities.UtilityFileChooser('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(
            filter(lambda k: k.startswith(config_prefix), self.config.keys()))
        config_types = dict(zip(config_keys, map(type, 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()
        config_tab.objects_load_from_config()
        gui_utilities.show_dialog_info('Successfully imported the message',
                                       self.parent)
コード例 #20
0
 def make_preview(self, _):
     if self.missing_options():
         return False
     if not self.build_pdf():
         gui_utilities.show_dialog_info(
             '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.')
コード例 #21
0
ファイル: mail.py プロジェクト: collin123/king-phisher
    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.")
コード例 #22
0
	def campaign_rename(self):
		"""
		Show a dialog prompting the user to for the a new name to assign to the
		currently selected campaign.
		"""
		campaign = self.rpc.remote_table_row('campaigns', self.config['campaign_id'])
		prompt = dialogs.TextEntryDialog.build_prompt(self, 'Rename Campaign', 'Enter the new campaign name:', campaign.name)
		response = prompt.interact()
		if response is None or response == campaign.name:
			return
		self.rpc('db/table/set', 'campaigns', self.config['campaign_id'], 'name', response)
		gui_utilities.show_dialog_info('Campaign Name Updated', self.get_active_window(), 'The campaign name was successfully changed.')
コード例 #23
0
	def campaign_rename(self):
		"""
		Show a dialog prompting the user to for the a new name to assign to the
		currently selected campaign.
		"""
		campaign = self.rpc.remote_table_row('campaigns', self.config['campaign_id'])
		prompt = dialogs.TextEntryDialog.build_prompt(self, 'Rename Campaign', 'Enter the new campaign name:', campaign.name)
		response = prompt.interact()
		if response == None or response == campaign.name:
			return
		self.rpc('db/table/set', 'campaigns', self.config['campaign_id'], 'name', response)
		gui_utilities.show_dialog_info('Campaign Name Updated', self.get_active_window(), 'The campaign name was successfully changed.')
コード例 #24
0
	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
コード例 #25
0
 def rename_campaign(self):
     campaign = self.rpc.remote_table_row('campaigns',
                                          self.config['campaign_id'])
     prompt = dialogs.TextEntryDialog.build_prompt(
         self.config, self, 'Rename Campaign',
         'Enter the new campaign name:', campaign.name)
     response = prompt.interact()
     if response == None or response == campaign.name:
         return
     self.rpc('campaigns/set', self.config['campaign_id'], ('name', ),
              (response, ))
     gui_utilities.show_dialog_info(
         'Campaign Name Updated', self,
         'The campaign name was successfully changed')
コード例 #26
0
	def make_preview(self, _):
		if self.missing_options():
			return False
		if not self.build_pdf():
			gui_utilities.show_dialog_info(
				'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.'
		)
コード例 #27
0
	def _finialize_settings_dashboard(self):
		dashboard_changed = False
		for dash_position in ['top_left', 'top_right', 'bottom']:
			combobox = self.gtk_builder_get('combobox_dashboard_' + dash_position)
			ti = combobox.get_active_iter()
			if not ti:
				continue
			graph_providers = combobox.get_model()
			graph_name = graph_providers[ti][1]
			if self.config.get('dashboard.' + dash_position) == graph_name:
				continue
			self.config['dashboard.' + dash_position] = graph_name
			dashboard_changed = True
		if dashboard_changed:
			gui_utilities.show_dialog_info('The dashboard layout has been updated.', self.parent, 'The new settings will be applied the next time the application starts.')
コード例 #28
0
ファイル: configuration.py プロジェクト: kn9/king-phisher
	def _finialize_settings_dashboard(self):
		dashboard_changed = False
		for dash_position in ['top_left', 'top_right', 'bottom']:
			combobox = self.gtk_builder_get('combobox_dashboard_' + dash_position)
			ti = combobox.get_active_iter()
			if not ti:
				continue
			graph_providers = combobox.get_model()
			graph_name = graph_providers[ti][1]
			if self.config.get('dashboard.' + dash_position) == graph_name:
				continue
			self.config['dashboard.' + dash_position] = graph_name
			dashboard_changed = True
		if dashboard_changed:
			gui_utilities.show_dialog_info('The dashboard layout has been updated.', self.parent, 'The new settings will be applied the next time the application starts.')
コード例 #29
0
ファイル: mail.py プロジェクト: coldfusion39/king-phisher
	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 = gui_utilities.FileChooser('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 = gui_utilities.FileChooser('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, map(type, 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()
		config_tab.objects_load_from_config()
		gui_utilities.show_dialog_info('Success', self.parent, 'Successfully imported the message.')
コード例 #30
0
	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()
コード例 #31
0
 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()
コード例 #32
0
ファイル: mail.py プロジェクト: jacobj10/king-phisher
	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
コード例 #33
0
	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
コード例 #34
0
	def signal_server_connected(self, _):
		release = get_latest_release()
		self.logger.info('found latest release: ' + release['tag_name'])
		client_version = StrictVersion(version.distutils_version)
		release_version = release_to_version(release)
		server_version = self.application.rpc('version')['version_info']
		server_version = StrictVersion("{major}.{minor}.{micro}".format(**server_version))
		out_of_date = None

		if release_version > client_version:
			out_of_date = 'Client'
		elif release_version > server_version:
			out_of_date = 'Server'
		if out_of_date is None:
			return

		gui_utilities.show_dialog_info(
			'New Version Available',
			self.application.main_window,
			"The King Phisher {part} is out of date,\n"
			"<a href=\"{release[html_url]}\">{release[tag_name]}</a> is now available.".format(part=out_of_date, release=release),
			secondary_use_markup=True
		)
コード例 #35
0
	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()
コード例 #36
0
ファイル: mail.py プロジェクト: baubaubau/king-phisher
	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
コード例 #37
0
ファイル: mail.py プロジェクト: collin123/king-phisher
    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
コード例 #38
0
ファイル: mail.py プロジェクト: l1k3m/king-phisher
	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
コード例 #39
0
    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.')
コード例 #40
0
    def signal_precheck(self, mailer_tab):
        email = str(self.application.config['mailer.source_email'])
        user, _, domain = email.partition('@')
        self.logger.debug("checking email domain: {0}".format(domain))

        if not domain_has_mx_record(domain):
            response = gui_utilities.show_dialog_yes_no(
                'Invalid Email Domain', self.application.get_active_window(),
                'The source email domain does not exist. Continue?')
            if not response:
                return False

        text_insert = mailer_tab.tabs['send_messages'].text_insert
        text_insert(
            "Checking the WHOIS record for domain '{0}'... ".format(domain))
        try:
            info = whois.whois(domain)
        except Exception as error:
            text_insert("done, encountered exception: {0}.\n".format(
                error.__class__.__name__))
            self.logger.error(
                "whois lookup failed for domain: {0}".format(domain),
                exc_info=True)
            response = gui_utilities.show_dialog_info(
                'Whois Lookup Failed', self.application.get_active_window(),
                'The domain is valid, however the whois lookup failed. Continue?'
            )
            return response

        if any(info.values()):
            text_insert('done, record found.\nWHOIS Record Overview:\n')
            text_insert("  Domain registered to: {0!r}\n".format(info.name))
            if info.name_servers:
                text_insert("  Name Servers:         {0}\n".format(', '.join(
                    info.name_servers)))
            if info.emails:
                text_insert("  Contact Email:        {0}\n".format(
                    info.emails))
        else:
            text_insert('done, no record found.\n')
        return True
コード例 #41
0
	def signal_precheck(self, mailer_tab):
		email = str(self.application.config['mailer.source_email'])
		user, _, domain = email.partition('@')
		self.logger.debug("checking email domain: {0}".format(domain))

		if not domain_has_mx_record(domain):
			response = gui_utilities.show_dialog_yes_no(
				'Invalid Email Domain',
				self.application.get_active_window(),
				'The source email domain does not exist. Continue?'
			)
			if not response:
				return False

		text_insert = mailer_tab.tabs['send_messages'].text_insert
		text_insert("Checking the WHOIS record for domain '{0}'... ".format(domain))
		try:
			info = whois.whois(domain)
		except Exception as error:
			text_insert("done, encountered exception: {0}.\n".format(error.__class__.__name__))
			self.logger.error("whois lookup failed for domain: {0}".format(domain), exc_info=True)
			response = gui_utilities.show_dialog_info(
				'Whois Lookup Failed',
				self.application.get_active_window(),
				'The domain is valid, however the whois lookup failed. Continue?'
			)
			return response

		if any(info.values()):
			text_insert('done, record found.\nWHOIS Record Overview:\n')
			text_insert("  Domain registered to: {0!r}\n".format(info.name))
			if info.name_servers:
				text_insert("  Name Servers:         {0}\n".format(', '.join(info.name_servers)))
			if info.emails:
				text_insert("  Contact Email:        {0}\n".format(info.emails))
		else:
			text_insert('done, no record found.\n')
		return True
コード例 #42
0
ファイル: campaign.py プロジェクト: xy-sec/king-phisher
    def signal_assistant_apply(self, _):
        self._close_ready = False
        # have to do it this way because the next page will be selected when the apply signal is complete
        set_current_page = lambda page_name: self.assistant.set_current_page(
            max(0, self._page_titles[page_name] - 1))

        # get and validate the campaign name
        campaign_name = self.gobjects['entry_campaign_name'].get_text()
        campaign_name = campaign_name.strip()
        if not campaign_name:
            gui_utilities.show_dialog_error(
                'Invalid Campaign Name', self.parent,
                'A valid campaign name must be specified.')
            set_current_page('Basic Settings')
            return True
        campaign = self._get_campaign_by_name(campaign_name)
        if campaign and campaign['id'] != self.campaign_id:
            gui_utilities.show_dialog_error(
                'Invalid Campaign Name', self.parent,
                'A unique campaign name must be specified.')
            set_current_page('Basic Settings')
            return True

        properties = {}
        # validate the credential validation regular expressions
        for field in ('username', 'password', 'mfa_token'):
            regex = self.gobjects['entry_validation_regex_' + field].get_text()
            if regex:
                try:
                    re.compile(regex)
                except re.error:
                    label = self.gobjects['label_validation_regex_' +
                                          field].get_text()
                    gui_utilities.show_dialog_error(
                        'Invalid Regex', self.parent,
                        "The '{0}' regular expression is invalid.".format(
                            label))
                    return True
            else:
                regex = None  # keep empty strings out of the database
            properties['credential_regex_' + field] = regex

        # validate the company
        company_id = None
        if self.gobjects['radiobutton_company_existing'].get_active():
            company_id = self._get_company_existing_id()
            if company_id is None:
                gui_utilities.show_dialog_error(
                    'Invalid Company', self.parent,
                    'A valid existing company must be specified.')
                set_current_page('Company')
                return True
        elif self.gobjects['radiobutton_company_new'].get_active():
            company_id = self._get_company_new_id()
            if company_id is None:
                gui_utilities.show_dialog_error(
                    'Invalid Company', self.parent,
                    'The new company settings are invalid.')
                set_current_page('Company')
                return True

        # get and validate the campaign expiration
        expiration = None
        if self.gobjects['checkbutton_expire_campaign'].get_property('active'):
            expiration = datetime.datetime.combine(
                gui_utilities.gtk_calendar_get_pydate(
                    self.gobjects['calendar_campaign_expiration']),
                self._expiration_time.time)
            expiration = utilities.datetime_local_to_utc(expiration)
            if self.is_new_campaign and expiration <= datetime.datetime.now():
                gui_utilities.show_dialog_error(
                    'Invalid Campaign Expiration', self.parent,
                    'The expiration date is set in the past.')
                set_current_page('Expiration')
                return True

        # point of no return
        campaign_description = self.get_entry_value('campaign_description')
        if self.campaign_id:
            properties['name'] = self.campaign_name
            properties['description'] = campaign_description
            cid = self.campaign_id
        else:
            try:
                cid = self.application.rpc('campaign/new',
                                           campaign_name,
                                           description=campaign_description)
                properties['name'] = campaign_name
            except advancedhttpserver.RPCError as error:
                if not error.is_remote_exception:
                    raise error
                if not error.remote_exception[
                        'name'] == 'exceptions.ValueError':
                    raise error
                error_message = error.remote_exception.get(
                    'message', 'an unknown error occurred').capitalize() + '.'
                gui_utilities.show_dialog_error('Failed To Create Campaign',
                                                self.parent, error_message)
                set_current_page('Basic Settings')
                return True
            self.application.emit('campaign-created', cid)

        properties['campaign_type_id'] = self._get_tag_from_combobox(
            self.gobjects['combobox_campaign_type'], 'campaign_types')
        properties['company_id'] = company_id
        properties['expiration'] = expiration
        properties['max_credentials'] = (
            1 if self.gobjects['checkbutton_reject_after_credentials'].
            get_property('active') else None)
        self.application.rpc('db/table/set', 'campaigns', cid,
                             tuple(properties.keys()),
                             tuple(properties.values()))

        should_subscribe = self.gobjects[
            'checkbutton_alert_subscribe'].get_property('active')
        if should_subscribe != self.application.rpc(
                'campaign/alerts/is_subscribed', cid):
            if should_subscribe:
                self.application.rpc('campaign/alerts/subscribe', cid)
            else:
                self.application.rpc('campaign/alerts/unsubscribe', cid)

        self.application.emit('campaign-changed', cid)

        old_cid = self.config.get('campaign_id')
        self.config['campaign_id'] = cid
        self.config['campaign_name'] = properties['name']

        _kpm_pathing = self._get_kpm_path()
        if all(_kpm_pathing):
            if not self.application.main_tabs['mailer'].emit(
                    'message-data-import', _kpm_pathing.kpm_file,
                    _kpm_pathing.destination_folder):
                gui_utilities.show_dialog_info(
                    'Failure', self.parent,
                    'Failed to import the message configuration.')
            else:
                gui_utilities.show_dialog_info(
                    'Success', self.parent,
                    'Successfully imported the message configuration.')

        if self._ssl_status['hasSni']:
            combobox_url_scheme = self.gobjects['combobox_url_scheme']
            active = combobox_url_scheme.get_active()
            if active != -1:
                url_scheme = _ModelURLScheme(
                    *combobox_url_scheme.get_model()[active])
                if url_scheme.name == 'https':
                    hostname = gui_utilities.gtk_combobox_get_entry_text(
                        self.gobjects['combobox_url_hostname'])
                    if not self.application.rpc('ssl/sni_hostnames/load',
                                                hostname):
                        gui_utilities.show_dialog_error(
                            'Failure', self.parent,
                            'Failed to load an SSL certificate for the specified hostname.'
                        )

        self.config['mailer.webserver_url'] = self._get_webserver_url()
        self.application.emit('campaign-set', old_cid, cid)
        self._close_ready = True
        return
コード例 #43
0
ファイル: campaign.py プロジェクト: securestate/king-phisher
	def signal_assistant_apply(self, _):
		self._close_ready = False
		# have to do it this way because the next page will be selected when the apply signal is complete
		set_current_page = lambda page_name: self.assistant.set_current_page(max(0, self._page_titles[page_name] - 1))

		# get and validate the campaign name
		campaign_name = self.gobjects['entry_campaign_name'].get_text()
		campaign_name = campaign_name.strip()
		if not campaign_name:
			gui_utilities.show_dialog_error('Invalid Campaign Name', self.parent, 'A unique and valid campaign name must be specified.')
			set_current_page('Basic Settings')
			return True

		properties = {}
		# validate the credential validation regular expressions
		for field in ('username', 'password', 'mfa_token'):
			regex = self.gobjects['entry_validation_regex_' + field].get_text()
			if regex:
				try:
					re.compile(regex)
				except re.error:
					label = self.gobjects['label_validation_regex_' + field].get_text()
					gui_utilities.show_dialog_error('Invalid Regex', self.parent, "The '{0}' regular expression is invalid.".format(label))
					return True
			else:
				regex = None  # keep empty strings out of the database
			properties['credential_regex_' + field] = regex

		# validate the company
		company_id = None
		if self.gobjects['radiobutton_company_existing'].get_active():
			company_id = self._get_company_existing_id()
			if company_id is None:
				gui_utilities.show_dialog_error('Invalid Company', self.parent, 'A valid existing company must be specified.')
				set_current_page('Company')
				return True
		elif self.gobjects['radiobutton_company_new'].get_active():
			company_id = self._get_company_new_id()
			if company_id is None:
				gui_utilities.show_dialog_error('Invalid Company', self.parent, 'The new company settings are invalid.')
				set_current_page('Company')
				return True

		# get and validate the campaign expiration
		expiration = None
		if self.gobjects['checkbutton_expire_campaign'].get_property('active'):
			expiration = datetime.datetime.combine(
				gui_utilities.gtk_calendar_get_pydate(self.gobjects['calendar_campaign_expiration']),
				self._expiration_time.time
			)
			expiration = utilities.datetime_local_to_utc(expiration)
			if self.is_new_campaign and expiration <= datetime.datetime.now():
				gui_utilities.show_dialog_error('Invalid Campaign Expiration', self.parent, 'The expiration date is set in the past.')
				set_current_page('Expiration')
				return True

		# point of no return
		campaign_description = self.get_entry_value('campaign_description')
		if self.campaign_id:
			properties['name'] = self.campaign_name
			properties['description'] = campaign_description
			cid = self.campaign_id
		else:
			try:
				cid = self.application.rpc('campaign/new', campaign_name, description=campaign_description)
				properties['name'] = campaign_name
			except advancedhttpserver.RPCError as error:
				if not error.is_remote_exception:
					raise error
				if not error.remote_exception['name'] == 'exceptions.ValueError':
					raise error
				error_message = error.remote_exception.get('message', 'an unknown error occurred').capitalize() + '.'
				gui_utilities.show_dialog_error('Failed To Create Campaign', self.parent, error_message)
				set_current_page('Basic Settings')
				return True
			self.application.emit('campaign-created', cid)

		properties['campaign_type_id'] = self._get_tag_from_combobox(self.gobjects['combobox_campaign_type'], 'campaign_types')
		properties['company_id'] = company_id
		properties['expiration'] = expiration
		properties['max_credentials'] = (1 if self.gobjects['checkbutton_reject_after_credentials'].get_property('active') else None)
		self.application.rpc('db/table/set', 'campaigns', cid, tuple(properties.keys()), tuple(properties.values()))

		should_subscribe = self.gobjects['checkbutton_alert_subscribe'].get_property('active')
		if should_subscribe != self.application.rpc('campaign/alerts/is_subscribed', cid):
			if should_subscribe:
				self.application.rpc('campaign/alerts/subscribe', cid)
			else:
				self.application.rpc('campaign/alerts/unsubscribe', cid)

		self.application.emit('campaign-changed', cid)

		old_cid = self.config.get('campaign_id')
		self.config['campaign_id'] = cid
		self.config['campaign_name'] = properties['name']

		_kpm_pathing = self._get_kpm_path()
		if all(_kpm_pathing):
			if not self.application.main_tabs['mailer'].emit('message-data-import', _kpm_pathing.kpm_file, _kpm_pathing.destination_folder):
				gui_utilities.show_dialog_info('Failure', self.parent, 'Failed to import the message configuration.')
			else:
				gui_utilities.show_dialog_info('Success', self.parent, 'Successfully imported the message configuration.')

		url_model, url_iter = self.gobjects['treeselection_url_selector'].get_selected()
		if url_iter:
			selected_row = []
			for column_n in range(0, url_model.get_n_columns()):
				selected_row.append(url_model.get_value(url_iter, column_n))
			selected_row = _ModelNamedRow(*selected_row)
			self.config['mailer.webserver_url'] = selected_row.url

		self.application.emit('campaign-set', old_cid, cid)
		self._close_ready = True
		return