This repository has been archived by the owner on Nov 30, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 214
Added python email client script #101
Merged
powerexploit
merged 2 commits into
powerexploit:master
from
PatelKeviin:email-client-issue94
Aug 20, 2020
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Email Client using Python for automating email shooting process | ||
Python email client that can be used to automate email sending process. | ||
|
||
## Pre-requisites :rotating_light: | ||
[![GitHub top language](https://img.shields.io/github/languages/top/vinitshahdeo/PortScanner?logo=python&logoColor=white)](https://www.python.org/) | ||
- **Python** `>= v3.7.x` | ||
- Install Python from [here](https://www.python.org/). | ||
- **Pip** `>= v20.0.x` | ||
- Install pip from [here](https://pip.pypa.io/en/stable/installing/). | ||
|
||
## How to run? :rocket: | ||
### To run Email Client: (Via Terminal) | ||
- Update `email_client.py` file to enter Email credentials for the sender account as well as the recipient's address. | ||
- **Open terminal** and type **`python Email_CLient/email_client.py`**. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
import os | ||
import smtplib | ||
from email.mime.multipart import MIMEMultipart | ||
from email.mime.text import MIMEText | ||
from email.mime.base import MIMEBase | ||
from email.utils import formatdate | ||
from email import encoders | ||
from pathlib import Path | ||
|
||
|
||
class EmailClient: | ||
""" An email client to send emails. | ||
""" | ||
|
||
def __init__(self, server: str, server_port: int, user_name: str, password: str): | ||
""" Class constructor to initialise EmailClient | ||
|
||
Parameters: | ||
server (str): Email server used by the user | ||
server_port (int): Port number of the email server | ||
user_name (str): Email address | ||
password (str): Email password | ||
""" | ||
|
||
# Email credentials | ||
self.user_name = user_name | ||
self.__password = password | ||
|
||
# used to add plain texts/HTML parts in the email | ||
self.__email_content = MIMEMultipart() | ||
# add sender's info | ||
self.__email_content['From'] = user_name | ||
# uses SMTP email protocol to communicate with email service provider | ||
self.__mail_server = smtplib.SMTP( | ||
host=server, port=server_port) | ||
|
||
# necessary for internal workings | ||
self.__is_subject_added = False | ||
self.__is_body_added = False | ||
self.__is_attached = False | ||
self.__is_signature_added = False | ||
|
||
def set_subject(self, subject: str): | ||
""" Method to set subject for the email (optional). | ||
|
||
Parameters: | ||
subject (str): Email subject to set | ||
""" | ||
|
||
self.__is_subject_added = (subject is not None and subject != '') | ||
if self.__is_subject_added: | ||
self.__email_content['Subject'] = subject | ||
|
||
def set_body(self, body: str): | ||
""" Method to set body for the email (optional). | ||
|
||
Parameters: | ||
body (str): Email body to set | ||
""" | ||
|
||
self.__is_body_added = (body is not None and body != '') | ||
if self.__is_body_added: | ||
self.__email_content.attach(MIMEText(body, 'plain')) | ||
|
||
def set_signature(self, signature: str): | ||
""" Method to set signature for the email (optional). | ||
|
||
Parameters: | ||
signature (str): Email signature to set | ||
""" | ||
|
||
self.__is_signature_added = (signature is not None and signature != '') | ||
if self.__is_signature_added: | ||
self.__email_content.attach(MIMEText(signature, 'plain')) | ||
|
||
def add_attachment(self, attachment_path: str): | ||
""" Method to attach attachments in the email (optional). | ||
|
||
Parameters: | ||
attachment_path (str): Path of attachment | ||
""" | ||
|
||
attachment = MIMEBase('application', "octet-stream") | ||
|
||
with open(attachment_path, 'rb') as file: | ||
attachment.set_payload(file.read()) | ||
|
||
encoders.encode_base64(attachment) | ||
attachment.add_header('Content-Disposition', | ||
'attachment; filename="{}"'.format(Path(attachment_path).name)) | ||
|
||
self.__email_content.attach(attachment) | ||
|
||
# added attachment | ||
self.__is_attached = True | ||
|
||
def send(self, recipient: str) -> bool: | ||
""" Method to send email message. | ||
|
||
Parameters: | ||
recipient (str): Recipient's email address | ||
|
||
Returns: | ||
bool: Determines success of email being sent | ||
""" | ||
if self.__is_attached and not self.__is_subject_added: | ||
print('Error: Subject is empty. Please add a subject and send again.') | ||
return False | ||
|
||
if not self.__is_subject_added and not self.__is_body_added and not self.__is_signature_added: | ||
print('Error: Cannot send empty email message. Please add at least one from subject, body or signature.') | ||
return False | ||
|
||
self.__email_content['To'] = recipient | ||
self.__email_content['Date'] = formatdate(localtime=True) | ||
|
||
try: | ||
self.__mail_server.starttls() # start a secure TLS connection | ||
# login with user credentials on email server | ||
self.__mail_server.login(self.user_name, self.__password) | ||
|
||
# send email message | ||
self.__mail_server.send_message(self.__email_content) | ||
|
||
return True | ||
except Exception as e: | ||
print('Something went wrong :(\n', e) | ||
|
||
return False | ||
finally: | ||
# close connection with email server | ||
self.__mail_server.quit() | ||
|
||
def reset_email(self): | ||
""" Resets all email content except for the initialisation details. | ||
""" | ||
|
||
# used to add plain texts/HTML parts in the email | ||
self.__email_content = MIMEMultipart() | ||
# add sender's info | ||
self.__email_content['From'] = self.user_name | ||
|
||
# necessary for internal workings | ||
self.__is_subject_added = False | ||
self.__is_body_added = False | ||
self.__is_attached = False | ||
self.__is_signature_added = False | ||
|
||
|
||
# driver code | ||
if __name__ == "__main__": | ||
|
||
# NOTE: if you're using Gmail account for sending email, you may have to turn off one of Google account's setting. | ||
# link: https://myaccount.google.com/lesssecureapps | ||
# More about this issue: https://stackoverflow.com/questions/16512592/login-credentials-not-working-with-gmail-smtp | ||
|
||
# using Google mail server | ||
mail_server = 'smtp.gmail.com' | ||
port_number = 587 | ||
# sender's credentials | ||
username = '[email protected]' # enter your gmail address | ||
with open('password.txt', 'r') as pass_file: | ||
email_password = pass_file.read() | ||
# recipient's email address | ||
email_recipient = '[email protected]' # enter sender's email address | ||
|
||
# Email message content | ||
# Email subject | ||
email_subject = 'Hello, world!' | ||
|
||
# Email body | ||
email_body = 'Hello there, \n\nThis is my automated email. Please do not reply.' | ||
|
||
# Email signature | ||
email_signature = '\n\nKind regards,\n{}'.format(username) | ||
|
||
# using 'EmailClient' class for sending email messages | ||
email_client = EmailClient( | ||
mail_server, port_number, username, email_password) | ||
email_client.set_subject(email_subject) | ||
email_client.set_body(email_body) | ||
email_client.set_signature(email_signature) | ||
# email_client.add_attachment('file_to_attach.txt') | ||
|
||
# sending email | ||
if email_client.send(email_recipient): | ||
print('Email sent.') | ||
else: | ||
print('Failed :(') |
111 changes: 111 additions & 0 deletions
111
System-Automation-Scripts/Email_Client/email_client_test.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
from email_client import EmailClient | ||
import unittest | ||
|
||
|
||
class EmailClientTest(unittest.TestCase): | ||
""" Tests for EmailClient class | ||
""" | ||
|
||
# credentials | ||
# using Google mail server | ||
__server = 'smtp.gmail.com' | ||
__port_number = 587 | ||
# sender's credentials | ||
__username = '[email protected]' | ||
with open('password.txt', 'r') as pass_file: | ||
__password = pass_file.read() | ||
# recipient's email addr | ||
__recipient = '[email protected]' | ||
|
||
# test case #1 - testing sending functionality of EmailClient class | ||
def test_email_client_send(self): | ||
# Email message content | ||
# Email subject | ||
subject = 'Testing' | ||
# Email body | ||
body = 'Hi,\n\nI am Kevin. How are you?' | ||
# Email signature | ||
signature = '\n\nKind regards,\n{}'.format(self.__username) | ||
|
||
# sending Email | ||
email_client = EmailClient( | ||
self.__server, self.__port_number, self.__username, self.__password) | ||
email_client.set_subject(subject) | ||
email_client.set_body(body) | ||
email_client.set_signature(signature) | ||
email_client.add_attachment('test.txt') | ||
|
||
# testing | ||
sent = True | ||
self.assertEqual(email_client.send(self.__recipient), sent) | ||
|
||
# test case #2 - EmailClient Should not send empty emails | ||
def test_empty_email(self): | ||
# Empty Email message content | ||
# Email subject | ||
subject = '' | ||
# Email body | ||
body = '' | ||
# Email signature | ||
signature = '' | ||
|
||
# sending Email | ||
email_client = EmailClient( | ||
self.__server, self.__port_number, self.__username, self.__password) | ||
email_client.set_subject(subject) | ||
email_client.set_body(body) | ||
email_client.set_signature(signature) | ||
|
||
# testing | ||
sent = False | ||
self.assertEqual(email_client.send(self.__recipient), sent) | ||
|
||
# test case #3 - testing reset functionality in EmailClient class | ||
def test_email_client_reset(self): | ||
# Email message content | ||
# Email subject | ||
subject = 'Testing Reset Method' | ||
# Email body | ||
body = 'Email body has not been reset. Please check its functionality.' | ||
# Email signature | ||
signature = '\n\nKind regards,\[email protected]' | ||
|
||
# initialising Email | ||
email_client = EmailClient( | ||
self.__server, self.__port_number, self.__username, self.__password) | ||
email_client.set_subject(subject) | ||
email_client.set_body(body) | ||
email_client.set_signature(signature) | ||
email_client.add_attachment('test.txt') | ||
|
||
# resetting email content | ||
email_client.reset_email() | ||
|
||
# adding Email body | ||
new_body = 'Email body has been reset.' | ||
email_client.set_body(new_body) | ||
|
||
# testing | ||
sent = True | ||
self.assertEqual(email_client.send(self.__recipient), sent) | ||
|
||
# test case #4 - testing sending functionality when special case of attachment added but not subject | ||
def test_email_client_send_misc(self): | ||
# Email message content | ||
# Email subject | ||
subject = '' | ||
|
||
# sending Email | ||
email_client = EmailClient( | ||
self.__server, self.__port_number, self.__username, self.__password) | ||
email_client.set_subject(subject) | ||
# add Email attachment | ||
email_client.add_attachment('test.txt') | ||
|
||
# testing | ||
sent = False | ||
self.assertEqual(email_client.send(self.__recipient), sent) | ||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() # running tests on EmailClient |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<your password> |
Empty file.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will recommend you to elaborate the installation process briefly along with what's already present in README file.
Rest, LGTM 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @Neilblaze, I have updated the changes as advised. Also, thank you very much for your kind words!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Neilblaze as all the required modules are part of Python Standard Libraries, nothing in particular is required in terms of set-up as long as the user has Python installed on his/her machine :)