<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#  king_phisher/client/mailer.py
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are
#  met:
#
#  * Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#  * Redistributions in binary form must reproduce the above
#    copyright notice, this list of conditions and the following disclaimer
#    in the documentation and/or other materials provided with the
#    distribution.
#  * Neither the name of the project nor the names of its
#    contributors may be used to endorse or promote products derived from
#    this software without specific prior written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
#  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
#  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
#  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
#  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
#  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
#  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

# disable this warning for the email.mime.* modules that have to be imported
# pylint: disable=unused-import

import codecs
import collections
import csv
import datetime
import email.encoders as encoders
import email.mime as mime
import email.mime.base
import email.mime.image
import email.mime.multipart
import email.mime.text
import email.utils
import logging
import mimetypes
import os
import smtplib
import socket
import sys
import threading
import time

from king_phisher import errors
from king_phisher import ics
from king_phisher import ipaddress
from king_phisher import its
from king_phisher import templates
from king_phisher import utilities
from king_phisher.client import gui_utilities
from king_phisher.client.dialogs import ssh_host_key
from king_phisher.constants import ConnectionErrorReason
from king_phisher.ssh_forward import SSHTCPForwarder

from gi.repository import GLib
import paramiko
import smoke_zephyr.utilities

if sys.version_info[0] < 3:
	import urllib
	import urlparse
	urllib.parse = urlparse
else:
	import urllib.parse  # pylint: disable=ungrouped-imports

__all__ = (
	'guess_smtp_server_address',
	'MailSenderThread',
	'render_message_template'
)

template_environment = templates.MessageTemplateEnvironment()

MessageAttachments = collections.namedtuple('MessageAttachments', ('files', 'images'))
"""A named tuple for holding both image and file attachments for a message."""

MIME_TEXT_PLAIN = 'This message requires an HTML aware email agent to be properly viewed.'
"""The static string to place in MIME message as a text/plain part. This is shown by email clients that do not support HTML."""

def _iterate_targets_file(target_file):
	target_file_h = open(target_file, 'rU')
	csv_reader = csv.DictReader(target_file_h, ('first_name', 'last_name', 'email_address', 'department'))
	for line_no, raw_target in enumerate(csv_reader, 1):
		if its.py_v2:
			# this will intentionally cause a UnicodeDecodeError to be raised as is the behaviour in python 3.x
			# when csv.DictReader is initialized
			raw_target = dict((k, (v if v is None else v.decode('utf-8'))) for k, v in raw_target.items())
		target = MessageTarget(line=line_no, **raw_target)
		# the caller needs to catch and process the missing fields appropriately
		yield target
	target_file_h.close()

def count_targets_file(target_file):
	"""
	Count the number of valid targets that the specified file contains. This
	skips lines which are missing fields or where the email address is invalid.

	:param str target_file: The path the the target CSV file on disk.
	:return: The number of valid targets.
	:rtype: int
	"""
	count = 0
	for target in _iterate_targets_file(target_file):
		if target.missing_fields:
			continue<commit_msg>Fix a bug when extra CSV fields are present<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#  king_phisher/client/mailer.py
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are
#  met:
#
#  * Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#  * Redistributions in binary form must reproduce the above
#    copyright notice, this list of conditions and the following disclaimer
#    in the documentation and/or other materials provided with the
#    distribution.
#  * Neither the name of the project nor the names of its
#    contributors may be used to endorse or promote products derived from
#    this software without specific prior written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
#  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
#  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
#  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
#  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
#  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
#  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

# disable this warning for the email.mime.* modules that have to be imported
# pylint: disable=unused-import

import codecs
import collections
import csv
import datetime
import email.encoders as encoders
import email.mime as mime
import email.mime.base
import email.mime.image
import email.mime.multipart
import email.mime.text
import email.utils
import logging
import mimetypes
import os
import smtplib
import socket
import sys
import threading
import time

from king_phisher import errors
from king_phisher import ics
from king_phisher import ipaddress
from king_phisher import its
from king_phisher import templates
from king_phisher import utilities
from king_phisher.client import gui_utilities
from king_phisher.client.dialogs import ssh_host_key
from king_phisher.constants import ConnectionErrorReason
from king_phisher.ssh_forward import SSHTCPForwarder

from gi.repository import GLib
import paramiko
import smoke_zephyr.utilities

if sys.version_info[0] < 3:
	import urllib
	import urlparse
	urllib.parse = urlparse
else:
	import urllib.parse  # pylint: disable=ungrouped-imports

__all__ = (
	'guess_smtp_server_address',
	'MailSenderThread',
	'render_message_template'
)

template_environment = templates.MessageTemplateEnvironment()

MessageAttachments = collections.namedtuple('MessageAttachments', ('files', 'images'))
"""A named tuple for holding both image and file attachments for a message."""

MIME_TEXT_PLAIN = 'This message requires an HTML aware email agent to be properly viewed.'
"""The static string to place in MIME message as a text/plain part. This is shown by email clients that do not support HTML."""

def _iterate_targets_file(target_file):
	target_file_h = open(target_file, 'rU')
	csv_reader = csv.DictReader(target_file_h, ('first_name', 'last_name', 'email_address', 'department'))
	for line_no, raw_target in enumerate(csv_reader, 1):
		if None in raw_target:
			# remove the additional fields
			del raw_target[None]
		if its.py_v2:
			# this will intentionally cause a UnicodeDecodeError to be raised as is the behaviour in python 3.x
			# when csv.DictReader is initialized
			raw_target = dict((k, (v if v is None else v.decode('utf-8'))) for k, v in raw_target.items())
		target = MessageTarget(line=line_no, **raw_target)
		# the caller needs to catch and process the missing fields appropriately
		yield target
	target_file_h.close()

def count_targets_file(target_file):
	"""
	Count the number of valid targets that the specified file contains. This
	skips lines which are missing fields or where the email address is invalid.

	:param str target_file: The path the the target CSV file on disk.
	:return: The number of valid targets.
	:rtype: int
	"""
	count = 0
	for target in _iterate_targets_file(target_file):
		if target.missing_fields:
			continue
{'fraction_non_alphanumeric': 0.061380490587564174, 'fraction_numerical': 0.0018254420992584142, 'mean_word_length': 4.907008086253369, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6586688', 'n_tokens_mistral': 2584, 'n_tokens_neox': 2407, 'n_words': 1057}