def send_response(self, code, message=None): super(KingPhisherRequestHandler, self).send_response(code, message) signals.send_safe('response-sent', self.logger, self, code=code, message=message)
def on_session_after_flush(session, flush_context): for signal, session_attribute in _flush_signal_map: objs = collections.defaultdict(list) for obj in getattr(session, session_attribute): objs[obj.__tablename__].append(obj) for table, targets in objs.items(): signals.send_safe(signal, logger, table, targets=tuple(targets), session=session)
def _handle_page_visit_creds(self, session, visit_id): username, password = self.get_query_creds() if username is None: return cred_count = 0 query = session.query(db_models.Credential) query = query.filter_by(message_id=self.message_id, username=username, password=password) if query.count() == 0: cred = db_models.Credential(campaign_id=self.campaign_id, message_id=self.message_id, visit_id=visit_id) cred.username = username cred.password = password session.add(cred) session.commit() self.logger.debug( "credential id: {0} created for message id: {1}".format( cred.id, cred.message_id)) campaign = db_manager.get_row_by_id(session, db_models.Campaign, self.campaign_id) cred_count = len(campaign.credentials) if cred_count > 0 and ((cred_count in [1, 5, 10]) or ((cred_count % 25) == 0)): self.server.job_manager.job_run( self.issue_alert, (self.campaign_id, 'credentials', cred_count)) signals.send_safe('credentials-received', self.logger, self, username=username, password=password)
def forward_signal_update(mapper, connection, target): signals.send_safe('db-table-update', logger, target.__tablename__, mapper=mapper, connection=connection, target=target)
def forward_signal_insert(mapper, connection, target): signals.send_safe('db-table-insert', logger, target.__tablename__, mapper=mapper, connection=connection, target=target)
def handle_email_opened(self, query): # image size: 43 Bytes img_data = '47494638396101000100800100000000ffffff21f90401000001002c00000000' img_data += '010001000002024c01003b' img_data = binascii.a2b_hex(img_data) self.send_response(200) self.send_header('Content-Type', 'image/gif') self.send_header('Content-Length', str(len(img_data))) self.end_headers() self.wfile.write(img_data) msg_id = self.get_query('id') if not msg_id: return self.semaphore_acquire() query = self._session.query(db_models.Message) query = query.filter_by(id=msg_id, opened=None) message = query.first() if message and not message.campaign.has_expired: message.opened = db_models.current_timestamp() message.opener_ip = self.get_client_ip() message.opener_user_agent = self.headers.get('user-agent', None) self._session.commit() signals.send_safe('email-opened', self.logger, self) self.semaphore_release()
def on_session_after_flush(session, flush_context): for signal, session_attribute in _flush_signal_map: objs = collections.defaultdict(list) for obj in getattr(session, session_attribute): objs[obj.__tablename__].append(obj) for table, targets in objs.items(): signals.send_safe(signal, logger, table, targets=tuple(targets), session=session)
def wrapper(handler_instance, *args, **kwargs): if log_call and rpc_logger.isEnabledFor(logging.DEBUG): args_repr = ', '.join(map(repr, args)) if kwargs: for key, value in sorted(kwargs.items()): args_repr += ", {0}={1!r}".format(key, value) msg = "calling RPC method {0}({1})".format( function.__name__, args_repr) rpc_logger.debug(msg) signals.send_safe('rpc-method-call', rpc_logger, path[1:-1], request_handler=handler_instance, args=args, kwargs=kwargs) if database_access: session = db_manager.Session() try: result = function(handler_instance, session, *args, **kwargs) finally: session.close() else: result = function(handler_instance, *args, **kwargs) signals.send_safe('rpc-method-called', rpc_logger, path[1:-1], request_handler=handler_instance, args=args, kwargs=kwargs, retval=result) return result
def handle_email_opened(self, query): # image size: 43 Bytes img_data = '47494638396101000100800100000000ffffff21f90401000001002c00000000' img_data += '010001000002024c01003b' img_data = binascii.a2b_hex(img_data) self.send_response(200) self.send_header('Content-Type', 'image/gif') self.send_header('Content-Length', str(len(img_data))) self.end_headers() self.wfile.write(img_data) msg_id = self.get_query('id') if not msg_id: return self.semaphore_acquire() session = db_manager.Session() query = session.query(db_models.Message) query = query.filter_by(id=msg_id, opened=None) message = query.first() if message and not message.campaign.has_expired: message.opened = db_models.current_timestamp() message.opener_ip = self.get_client_ip() message.opener_user_agent = self.headers.get('user-agent', None) session.commit() session.close() signals.send_safe('email-opened', self.logger, self) self.semaphore_release()
def _handle_page_visit_creds(self, campaign, visit_id): query_creds = self.get_query_creds() if query_creds.username is None: return cred_count = 0 cred = self._get_db_creds(query_creds) if cred is None: if db_manager.get_row_by_id(self._session, db_models.Visit, self._visit_id) is None: self.logger.warning('discarding credentials because there is no visit associated with the request') return cred = db_models.Credential( campaign_id=campaign.id, message_id=self.message_id, visit_id=visit_id, **query_creds._asdict() ) cred.regex_validated = db_validation.validate_credential(cred, campaign) self._session.add(cred) self._session.commit() self.logger.debug("credential id: {0} created for message id: {1}".format(cred.id, cred.message_id)) campaign = db_manager.get_row_by_id(self._session, db_models.Campaign, self.campaign_id) cred_count = len(campaign.credentials) if cred_count > 0 and ((cred_count in [1, 5, 10]) or ((cred_count % 25) == 0)): self.server.job_manager.job_run(self.issue_alert, (self.campaign_id, 'credentials', cred_count)) signals.send_safe('credentials-received', self.logger, self, username=query_creds.username, password=query_creds.password)
def rpc_logout(handler): rpc_session = handler.rpc_session if rpc_session.event_socket is not None: rpc_session.event_socket.close() handler.server.session_manager.remove(handler.rpc_session_id) logger = logging.getLogger('KingPhisher.Server.Authentication') logger.info("successful logout request from {0} for user {1}".format(handler.client_address[0], rpc_session.user)) signals.send_safe('rpc-user-logged-out', logger, handler, session=handler.rpc_session_id, name=rpc_session.user)
def rpc_logout(handler): rpc_session = handler.rpc_session if rpc_session.event_socket is not None: rpc_session.event_socket.close() handler.server.session_manager.remove(handler.rpc_session_id) logger = logging.getLogger('KingPhisher.Server.Authentication') logger.info("successful logout request from {0} for user {1}".format(handler.client_address[0], rpc_session.user)) signals.send_safe('rpc-user-logged-out', logger, handler, session=handler.rpc_session_id, name=rpc_session.user)
def rpc_login(handler, session, username, password, otp=None): logger = logging.getLogger('KingPhisher.Server.Authentication') if not ipaddress.ip_address(handler.client_address[0]).is_loopback: logger.warning( "failed login request from {0} for user {1}, (invalid source address)" .format(handler.client_address[0], username)) raise ValueError('invalid source address for login') fail_default = (False, ConnectionErrorReason.ERROR_INVALID_CREDENTIALS, None) fail_otp = (False, ConnectionErrorReason.ERROR_INVALID_OTP, None) if not (username and password): logger.warning( "failed login request from {0} for user {1}, (missing username or password)" .format(handler.client_address[0], username)) return fail_default if not handler.server.forked_authenticator.authenticate( username, password): logger.warning( "failed login request from {0} for user {1}, (authentication failed)" .format(handler.client_address[0], username)) return fail_default user = db_manager.get_row_by_id(session, db_models.User, username) if not user: logger.info('creating new user object with id: ' + username) user = db_models.User(id=username) session.add(user) session.commit() elif user.otp_secret: if otp is None: logger.debug( "failed login request from {0} for user {1}, (missing otp)". format(handler.client_address[0], username)) return fail_otp if not (isinstance(otp, str) and len(otp) == 6 and otp.isdigit()): logger.warning( "failed login request from {0} for user {1}, (invalid otp)". format(handler.client_address[0], username)) return fail_otp totp = pyotp.TOTP(user.otp_secret) now = datetime.datetime.now() if not otp in (totp.at(now + datetime.timedelta(seconds=offset)) for offset in (0, -30, 30)): logger.warning( "failed login request from {0} for user {1}, (invalid otp)". format(handler.client_address[0], username)) return fail_otp session_id = handler.server.session_manager.put(username) logger.info("successful login request from {0} for user {1}".format( handler.client_address[0], username)) signals.send_safe('rpc-user-logged-in', logger, handler, session=session_id, name=username) return True, ConnectionErrorReason.SUCCESS, session_id
def wrapper(handler_instance, *args, **kwargs): if log_call: _log_rpc_call(handler_instance, function.__name__, *args, **kwargs) signals.send_safe('rpc-method-call', rpc_logger, path[1:-1], request_handler=handler_instance, args=args, kwargs=kwargs) if database_access: session = db_manager.Session() try: result = function(handler_instance, session, *args, **kwargs) finally: session.close() else: result = function(handler_instance, *args, **kwargs) signals.send_safe('rpc-method-called', rpc_logger, path[1:-1], request_handler=handler_instance, args=args, kwargs=kwargs, retval=result) return result
def issue_alert(self, campaign_id, table, count): """ Send a campaign alert for the specified table. :param int campaign_id: The campaign subscribers to send the alert to. :param str table: The type of event to use as the sender when it is forwarded. :param int count: The number associated with the event alert. """ session = db_manager.Session() campaign = db_manager.get_row_by_id(session, db_models.Campaign, campaign_id) alert_subscriptions = tuple(subscription for subscription in campaign.alert_subscriptions if not subscription.has_expired) if not alert_subscriptions: self.server.logger.debug("no active alert subscriptions are present for campaign id: {0} ({1})".format(campaign.id, campaign.name)) session.close() return if not signals.campaign_alert.receivers: self.server.logger.warning('users are subscribed to campaign alerts, and no signal handlers are connected') session.close() return if not signals.campaign_alert.has_receivers_for(table): self.server.logger.info('users are subscribed to campaign alerts, and no signal handlers are connected for sender: ' + table) session.close() return for subscription in alert_subscriptions: results = signals.send_safe('campaign-alert', self.server.logger, table, alert_subscription=subscription, count=count) if any((result for (_, result) in results)): continue self.server.logger.warning("user {0} is subscribed to campaign alerts, and no signal handlers succeeded to send an alert".format(subscription.user.name)) session.close() return
def _send_safe_campaign_alerts(campaign, signal_name, sender, **kwargs): alert_subscriptions = tuple( subscription for subscription in campaign.alert_subscriptions if not subscription.has_expired) logger = logging.getLogger('KingPhisher.Server.CampaignAlerts') logger.debug( "dispatching campaign alerts for '{0}' (sender: {1!r}) to {2:,} active subscriptions" .format(signal_name, sender, len(alert_subscriptions))) if not alert_subscriptions: return signal = blinker.signal(signal_name) if not signal.receivers: logger.warning( "users are subscribed to '{0}', and no signal handlers are connected" .format(signal_name)) return if not signal.has_receivers_for(sender): logger.info( "users are subscribed to '{0}', and no signal handlers are connected for sender: {1}" .format(signal_name, sender)) return for subscription in alert_subscriptions: results = signals.send_safe(signal_name, logger, sender, alert_subscription=subscription, **kwargs) if any((result for (_, result) in results)): continue logger.warning( "user {0} is subscribed to '{1}', and no signal handlers succeeded to send an alert" .format(subscription.user.name, signal_name))
def on_init(self): self.config = self.server.config regex_prefix = '^' if self.config.get('server.vhost_directories'): regex_prefix += r'[\w\.\-]+\/' for path, handler in self.handler_map.items(): if path.startswith(rest_api.REST_API_BASE): del self.handler_map[path] self.handler_map[regex_prefix + path] = handler self.handler_map[regex_prefix + 'kpdd$'] = self.handle_deaddrop_visit self.handler_map[regex_prefix + 'kp\\.js$'] = self.handle_javascript_hook self.web_socket_handler = self.server.ws_manager.dispatch tracking_image = self.config.get('server.tracking_image') tracking_image = tracking_image.replace('.', '\\.') self.handler_map[regex_prefix + tracking_image + '$'] = self.handle_email_opened signals.send_safe('request-received', self.logger, self)
def on_init(self): self.config = self.server.config regex_prefix = '^' if self.config.get('server.vhost_directories'): regex_prefix += r'[\w\.\-]+\/' for path, handler in self.handler_map.items(): if path.startswith(rest_api.REST_API_BASE): del self.handler_map[path] self.handler_map[regex_prefix + path] = handler self.handler_map[regex_prefix + 'kpdd$'] = self.handle_deaddrop_visit self.handler_map[regex_prefix + 'kp\\.js$'] = self.handle_javascript_hook self.web_socket_handler = self.server.ws_manager.dispatch tracking_image = self.config.get('server.tracking_image') tracking_image = tracking_image.replace('.', '\\.') self.handler_map[regex_prefix + tracking_image + '$'] = self.handle_email_opened signals.send_safe('request-received', self.logger, self)
def _handle_page_visit_creds(self, session, visit_id): username, password = self.get_query_creds() if username is None: return cred_count = 0 query = session.query(db_models.Credential) query = query.filter_by(message_id=self.message_id, username=username, password=password) if query.count() == 0: cred = db_models.Credential(campaign_id=self.campaign_id, message_id=self.message_id, visit_id=visit_id) cred.username = username cred.password = password session.add(cred) session.commit() self.logger.debug("credential id: {0} created for message id: {1}".format(cred.id, cred.message_id)) campaign = db_manager.get_row_by_id(session, db_models.Campaign, self.campaign_id) cred_count = len(campaign.credentials) if cred_count > 0 and ((cred_count in [1, 5, 10]) or ((cred_count % 25) == 0)): self.server.job_manager.job_run(self.issue_alert, (self.campaign_id, 'credentials', cred_count)) signals.send_safe('credentials-received', self.logger, self, username=username, password=password)
def _maintenance(self, interval): """ Execute periodic maintenance related tasks. :param int interval: The interval of time (in seconds) at which this method is being executed. """ self.logger.debug('running periodic maintenance tasks') now = db_models.current_timestamp() session = db_manager.Session() campaigns = session.query(db_models.Campaign).filter( db_models.Campaign.expiration != None).filter( db_models.Campaign.expiration < now).filter( db_models.Campaign.expiration >= now - datetime.timedelta(seconds=interval)) for campaign in campaigns: signals.send_safe('campaign-expired', self.logger, campaign) _send_safe_campaign_alerts(campaign, 'campaign-alert-expired', campaign) session.close()
def wrapper(handler_instance, *args, **kwargs): if log_call and rpc_logger.isEnabledFor(logging.DEBUG): args_repr = ', '.join(map(repr, args)) if kwargs: for key, value in sorted(kwargs.items()): args_repr += ", {0}={1!r}".format(key, value) msg = "calling RPC method {0}({1})".format(function.__name__, args_repr) rpc_logger.debug(msg) signals.send_safe('rpc-method-call', rpc_logger, path[1:-1], request_handler=handler_instance, args=args, kwargs=kwargs) if database_access: session = db_manager.Session() try: result = function(handler_instance, session, *args, **kwargs) finally: session.close() else: result = function(handler_instance, *args, **kwargs) signals.send_safe('rpc-method-called', rpc_logger, path[1:-1], request_handler=handler_instance, args=args, kwargs=kwargs, retval=result) return result
def _maintenance(self, interval): """ Execute periodic maintenance related tasks. :param int interval: The interval of time (in seconds) at which this method is being executed. """ self.logger.debug('running periodic maintenance tasks') now = db_models.current_timestamp() session = db_manager.Session() campaigns = session.query(db_models.Campaign).filter( db_models.Campaign.expiration != None ).filter( db_models.Campaign.expiration < now ).filter( db_models.Campaign.expiration >= now - datetime.timedelta(seconds=interval) ) for campaign in campaigns: signals.send_safe('campaign-expired', self.logger, campaign) _send_safe_campaign_alerts(campaign, 'campaign-alert-expired', campaign) session.close()
def rpc_login(handler, session, username, password, otp=None): logger = logging.getLogger('KingPhisher.Server.Authentication') if not ipaddress.ip_address(handler.client_address[0]).is_loopback: logger.warning("failed login request from {0} for user {1}, (invalid source address)".format(handler.client_address[0], username)) raise ValueError('invalid source address for login') fail_default = (False, ConnectionErrorReason.ERROR_INVALID_CREDENTIALS, None) fail_otp = (False, ConnectionErrorReason.ERROR_INVALID_OTP, None) if not (username and password): logger.warning("failed login request from {0} for user {1}, (missing username or password)".format(handler.client_address[0], username)) return fail_default if not handler.server.forked_authenticator.authenticate(username, password): logger.warning("failed login request from {0} for user {1}, (authentication failed)".format(handler.client_address[0], username)) return fail_default user = session.query(db_models.User).filter_by(name=username).first() if not user: logger.info('creating new user object with name: ' + username) user = db_models.User(name=username) elif user.has_expired: logger.warning("failed login request from {0} for user {1}, (user has expired)".format(handler.client_address[0], username)) return fail_default elif user.otp_secret: if otp is None: logger.debug("failed login request from {0} for user {1}, (missing otp)".format(handler.client_address[0], username)) return fail_otp if not (isinstance(otp, str) and len(otp) == 6 and otp.isdigit()): logger.warning("failed login request from {0} for user {1}, (invalid otp)".format(handler.client_address[0], username)) return fail_otp totp = pyotp.TOTP(user.otp_secret) now = datetime.datetime.now() if otp not in (totp.at(now + datetime.timedelta(seconds=offset)) for offset in (0, -30, 30)): logger.warning("failed login request from {0} for user {1}, (invalid otp)".format(handler.client_address[0], username)) return fail_otp user.last_login = db_models.current_timestamp() session.add(user) session.commit() session_id = handler.server.session_manager.put(user.id) logger.info("successful login request from {0} for user {1}".format(handler.client_address[0], username)) signals.send_safe('rpc-user-logged-in', logger, handler, session=session_id, name=username) return True, ConnectionErrorReason.SUCCESS, session_id
def _handle_page_visit_creds(self, campaign, visit_id): query_creds = self.get_query_creds() if query_creds.username is None: return cred_count = 0 cred = self._get_db_creds(query_creds) if cred is None: cred = db_models.Credential( campaign_id=campaign.id, message_id=self.message_id, visit_id=visit_id, **query_creds._asdict() ) cred.regex_validated = db_validation.validate_credential(cred, campaign) self._session.add(cred) self._session.commit() self.logger.debug("credential id: {0} created for message id: {1}".format(cred.id, cred.message_id)) campaign = db_manager.get_row_by_id(self._session, db_models.Campaign, self.campaign_id) cred_count = len(campaign.credentials) if cred_count > 0 and ((cred_count in [1, 5, 10]) or ((cred_count % 25) == 0)): self.server.job_manager.job_run(self.issue_alert, (self.campaign_id, 'credentials', cred_count)) signals.send_safe('credentials-received', self.logger, self, username=query_creds.username, password=query_creds.password)
def issue_alert(self, campaign_id, table, count): """ Send a campaign alert for the specified table. :param int campaign_id: The campaign subscribers to send the alert to. :param str table: The type of event to use as the sender when it is forwarded. :param int count: The number associated with the event alert. """ session = db_manager.Session() campaign = db_manager.get_row_by_id(session, db_models.Campaign, campaign_id) now = datetime.datetime.utcnow() alert_subscriptions = tuple( subscription for subscription in campaign.alert_subscriptions if subscription.mute_timestamp is None or subscription.mute_timestamp > now) if not alert_subscriptions: self.server.logger.debug( "no active alert subscriptions are present for campaign id: {0} ({1})" .format(campaign.id, campaign.name)) session.close() return if not signals.campaign_alert.receivers: self.server.logger.warning( 'users are subscribed to campaign alerts, and no signal handlers are connected' ) session.close() return if not signals.campaign_alert.has_receivers_for(table): self.server.logger.info( 'users are subscribed to campaign alerts, and no signal handlers are connected for sender: ' + table) session.close() return for subscription in alert_subscriptions: results = signals.send_safe('campaign-alert', self.server.logger, table, alert_subscription=subscription, count=count) if any((result for (_, result) in results)): continue self.server.logger.warning( "user {0} is subscribed to campaign alerts, and no signal handlers succeeded to send an alert" .format(subscription.user.id)) session.close() return
def _send_safe_campaign_alerts(campaign, signal_name, sender, **kwargs): alert_subscriptions = tuple(subscription for subscription in campaign.alert_subscriptions if not subscription.has_expired) logger = logging.getLogger('KingPhisher.Server.CampaignAlerts') logger.debug("dispatching campaign alerts for '{0}' (sender: {1!r}) to {2:,} active subscriptions".format(signal_name, sender, len(alert_subscriptions))) if not alert_subscriptions: return signal = blinker.signal(signal_name) if not signal.receivers: logger.warning("users are subscribed to '{0}', and no signal handlers are connected".format(signal_name)) return if not signal.has_receivers_for(sender): logger.info("users are subscribed to '{0}', and no signal handlers are connected for sender: {1}".format(signal_name, sender)) return for subscription in alert_subscriptions: results = signals.send_safe(signal_name, logger, sender, alert_subscription=subscription, **kwargs) if any((result for (_, result) in results)): continue logger.warning("user {0} is subscribed to '{1}', and no signal handlers succeeded to send an alert".format(subscription.user.name, signal_name))
def forward_signal_delete(mapper, connection, target): signals.send_safe('db-table-delete', logger, target.__tablename__, mapper=mapper, connection=connection, target=target)
def handle_page_visit(self): if not self.message_id: return if self.message_id == self.config.get('server.secret_id'): return if not self.campaign_id: return client_ip = self.get_client_ip() headers = [] campaign = db_manager.get_row_by_id(self._session, db_models.Campaign, self.campaign_id) if campaign.has_expired: self.logger.info("ignoring page visit for expired campaign id: {0} from IP address: {1}".format(self.campaign_id, client_ip)) return self.logger.info("handling a page visit for campaign id: {0} from IP address: {1}".format(self.campaign_id, client_ip)) message = db_manager.get_row_by_id(self._session, db_models.Message, self.message_id) if message.opened is None and self.config.get('server.set_message_opened_on_visit'): message.opened = db_models.current_timestamp() message.opener_ip = self.get_client_ip() message.opener_user_agent = self.headers.get('user-agent', None) query = self._session.query(db_models.LandingPage) query = query.filter_by(campaign_id=self.campaign_id, hostname=self.vhost, page=self.request_path[1:]) landing_page = query.first() set_new_visit = True visit_id = None if self.visit_id: visit_id = self.visit_id set_new_visit = False if landing_page: visit = db_manager.get_row_by_id(self._session, db_models.Visit, self.visit_id) if visit.message_id == self.message_id: visit.count += 1 visit.last_seen = db_models.current_timestamp() self._session.commit() else: set_new_visit = True visit_id = None if visit_id is None: visit_id = utilities.make_visit_uid() if landing_page and set_new_visit: kp_cookie_name = self.config.get('server.cookie_name') cookie = "{0}={1}; Path=/; HttpOnly".format(kp_cookie_name, visit_id) headers.append(('Set-Cookie', cookie)) visit = db_models.Visit(id=visit_id, campaign_id=self.campaign_id, message_id=self.message_id) visit.ip = client_ip visit.first_landing_page_id = landing_page.id visit.user_agent = self.headers.get('user-agent', '') self._session.add(visit) self._session.commit() self.logger.debug("visit id: {0} created for message id: {1}".format(visit_id, self.message_id)) visit_count = len(campaign.visits) if visit_count > 0 and ((visit_count in (1, 10, 25)) or ((visit_count % 50) == 0)): self.server.job_manager.job_run(self.issue_alert, (self.campaign_id, 'visits', visit_count)) signals.send_safe('visit-received', self.logger, self) self._handle_page_visit_creds(campaign, visit_id) trained = self.get_query('trained') if isinstance(trained, str) and trained.lower() in ['1', 'true', 'yes']: message.trained = True self._session.commit() return headers
def send_response(self, code, message=None): super(KingPhisherRequestHandler, self).send_response(code, message) signals.send_safe('response-sent', self.logger, self, code=code, message=message)
def forward_signal_insert(mapper, connection, target): signals.send_safe('db-table-insert', logger, target.__tablename__, mapper=mapper, connection=connection, target=target)
def handle_page_visit(self): if not self.message_id: return if self.message_id == self.config.get('server.secret_id'): return if not self.campaign_id: return client_ip = self.get_client_ip() session = db_manager.Session() campaign = db_manager.get_row_by_id(session, db_models.Campaign, self.campaign_id) if campaign.has_expired: self.logger.info("ignoring page visit for expired campaign id: {0} from IP address: {1}".format(self.campaign_id, client_ip)) session.close() return self.logger.info("handling a page visit for campaign id: {0} from IP address: {1}".format(self.campaign_id, client_ip)) message = db_manager.get_row_by_id(session, db_models.Message, self.message_id) if message.opened is None and self.config.get('server.set_message_opened_on_visit'): message.opened = db_models.current_timestamp() message.opener_ip = self.get_client_ip() message.opener_user_agent = self.headers.get('user-agent', None) query = session.query(db_models.LandingPage) query = query.filter_by(campaign_id=self.campaign_id, hostname=self.vhost, page=self.request_path[1:]) landing_page = query.first() set_new_visit = True visit_id = None if self.visit_id: visit_id = self.visit_id set_new_visit = False if landing_page: visit = db_manager.get_row_by_id(session, db_models.Visit, self.visit_id) if visit.message_id == self.message_id: visit.count += 1 visit.last_seen = db_models.current_timestamp() session.commit() else: set_new_visit = True visit_id = None if visit_id is None: visit_id = utilities.make_visit_uid() if landing_page and set_new_visit: kp_cookie_name = self.config.get('server.cookie_name') cookie = "{0}={1}; Path=/; HttpOnly".format(kp_cookie_name, visit_id) self.send_header('Set-Cookie', cookie) visit = db_models.Visit(id=visit_id, campaign_id=self.campaign_id, message_id=self.message_id) visit.ip = client_ip visit.first_landing_page_id = landing_page.id visit.user_agent = self.headers.get('user-agent', '') session.add(visit) session.commit() self.logger.debug("visit id: {0} created for message id: {1}".format(visit_id, self.message_id)) visit_count = len(campaign.visits) if visit_count > 0 and ((visit_count in (1, 10, 25)) or ((visit_count % 50) == 0)): self.server.job_manager.job_run(self.issue_alert, (self.campaign_id, 'visits', visit_count)) signals.send_safe('visit-received', self.logger, self) self._handle_page_visit_creds(session, visit_id) trained = self.get_query('trained') if isinstance(trained, str) and trained.lower() in ['1', 'true', 'yes']: message.trained = True session.commit() session.close()