Converting string from email - python

I am trying to write a program that will scan my email inbox and take further action if the first word in the email is "Password". However, my specific condition if message_str[:9].lower() == 'password': always defaults to false, even though the string being compared is clearly 'password' as I have tested by making print statements. Why is this so?
code:
import imapclient, pyzmail, time, sys
while True:
imapObj = imapclient.IMAPClient('imap.gmail.com', ssl=True)
imapObj.login('xxxxxxxx#gmail.com', sys.argv[1])
imapObj.select_folder('INBOX', readonly=False)
UIDs = imapObj.search(['SINCE', '01-Mar-2021', 'FROM', 'xxxxxxxx#gmail.com'])
rawMessages = imapObj.fetch(UIDs, ['BODY[]'])
for i in UIDs:
message = pyzmail.PyzMessage.factory(rawMessages[i][b'BODY[]'])
if message.text_part:
message_str = message.text_part.get_payload().decode(message.text_part.charset)
a = (repr(message_str))
if message_str[:9].lower() == 'password':
continue

'password' has 8 not 9 letters, so you need to use message_str[:8].lower()

Related

LDAP python 3 (not working) but in PHP (working) where is the problem?

Python 3 code - not working:
import ldap3 as ldap
SERVER_NAME = 'serverip'
DN = 'dc=mydomain,dc=com,dc=br,ou=people'
USERNAME = 'no-reply'
PASSWORD = 'mypassword'
server = ldap.Server(SERVER_NAME,port=389, get_info='ALL')
connection = ldap.Connection(server, user='{}\\{}'.format(DN, USERNAME), password=PASSWORD)
connection.open()
if connection.bind():
print('Authenticated!')
else:
print('Not Authenticated')
print(connection.result)
This is print result
Not Authenticated
{'result': 34, 'description': 'invalidDNSyntax', 'dn': '', 'message': >'invalid DN', 'referrals': None, 'saslCreds': None, 'type': 'bindResponse'}
PHP code - working:
<?php
$username="no-reply";
$pass="mypassword";
$ldapconfig['host'] = 'serverip';
$ldapconfig['port'] = '389';
$ldapconfig['basedn'] = 'dc=mydomain,dc=com,dc=br';
$ldapconfig['usersdn'] = 'ou=people';
$ds=ldap_connect($ldapconfig['host'], $ldapconfig['port']);
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
$dn="uid=".$username.",".$ldapconfig['usersdn'].",".$ldapconfig['basedn'];
if ($bind=ldap_bind($ds, $dn, $pass)) {
$ldap_result = #ldap_search($ds, $dn, "uid=".$username);
$result = ldap_get_entries($ds, $ldap_result);
print_r($result);
} else {
print_r(new Exception("Error"));
}
?>
I'm writing Python 3 code to use LDAP, but authentication is not working. However, my old code written in PHP works successfully. Where is the problem in my Python code?
The key words in the error message is 'invalid DN'. It's complaining about the format of the username in the call to ldap.Connection. You're doing:
user='{}\\{}'.format(DN, USERNAME)
Which would result in this string:
"dc=mydomain,dc=com,dc=br,ou=people\no-reply"
But that's not a valid format. Because you're using the distinguished name (DN) of the domain, it's assuming the whole string is a DN, but then the \no-reply at the end throws it off.
Your PHP code is doing this:
$dn = "uid=" . $username . "," . $ldapconfig['usersdn'] . "," . $ldapconfig['basedn'];
Which will result in this string:
"uid=no-reply,ou=people,dc=mydomain,dc=com,dc=br"
So modify your Python code to use the same value for the username.
You may even get away with passing just the username ("no-reply"), depending on what your LDAP server is. It may or may not work:
connection = ldap.Connection(server, user=USERNAME, password=PASSWORD)
I managed to solve the problem, it was necessary to rewrite the code simplifying the commands.
from ldap3 import Server, Connection, ALL, SUBTREE
username = 'no-reply'
password = 'mypassword'
serverName = 'ip_address'
DC = 'ou=people,dc=domain,dc=com,dc=contry'
server = Server(serverName, get_info=ALL)
conn = Connection(server, 'uid='+username+','+DC, password, auto_bind=True)
conn.search(search_base = DC,
search_filter = '(uid='+username+')',
search_scope = SUBTREE,
attributes = ['cn', 'givenName', 'company', 'title' ],
paged_size = 5)
print(conn.entries)

How to create a function to send emails formatting html message with a variable list?

I am trying to create a function in django to send my HTML email templates with certain variables.
Here's what I have so far:
from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.core.mail import get_connection
from django.utils.html import strip_tags
from otr import settings
def send_email(subject, email_template, recipient, username, password, variable_list):
html_message = render_to_string(email_template, {'context': 'values'})
for variable in variable_list:
html_message.format(variable)
plain_message = strip_tags(html_message)
recipient = recipient
with get_connection(
host=settings.EMAIL_HOST,
port=settings.EMAIL_PORT,
username=username,
password=password,
use_tls=True
) as connection:
send_mail(subject, plain_message, 'Me', [recipient], fail_silently=False,
html_message=html_message, connection=connection)
Then I want to call this function to be used in different django views like:
subject = "My subject"
email_template = "emails/followup_email.html"
recipient = queryset.customer_email
username = username_variable
password = password_variable
customer_name = "Bob"
dear_user = "my_name"
phone_number = "1111111111"
sincerely_user = "my_name"
variable_list = [customer_name, dear_user, phone_number, sincerely_user]
send_email(subject, email_template, recipient, username, password, variable_list)
I keep getting "Replacement index 1 out of range for positional args tuple", so I am guessing I can't do a for loop in this way to format the HTML email with a list of variables. Does anyone know how to accomplish this a different way?
Assuming everything else is correct Replacement index 1 out of range for positional args tuple error you get is because of the for loop in send_email function. Replace those two lines with -
html_message.format(*variable_list)
Note: The html_message string should contain same number of placeholders {} as the number of items in variable_list i.e., 4 in this case for format to work correctly.

How do I fix SMTPRecipientsRefused: {'': (421, b'4.7.0 Too many protocol errors (6) on this connection, closing transmission channel error Python?

I get this error when sending emails via smptplib in Python to a certain list
SMTPRecipientsRefused: {'': (421, b'4.7.0 Too many protocol errors (6) on this connection, closing transmission channel.')}?
I'm using Office365 SMTP details and a snippet of the code is below:-
import smtplib, ssl
from email.message import EmailMessage
import getpass
ids = df['IDs']
emails_to = df['Emails']
namesofcompanies = df["CompanyNames"]
sendfrom = df["SenderList"]
date_7days = (datetime.now() + timedelta(days=7)).strftime('%d/%m/%Y')
date_14days = (datetime.now() + timedelta(days=13)).strftime('%d/%m/%Y')
email_pass = input() #Office 365 password
context=ssl.create_default_context()
for i in range(len(emails_to)): # iterate through the records
# for every record get the name and the email addresses
ID = str(ids[i])
Emaitstosendto = emails_to[i]
companynames = namesofcompanies[i]
tosendfrom = sendfrom[i]
if my_files_dict.get(ID): #Looks for attachments in the same folder with same name as the corresponding record
smtp_ssl_host = 'smtp.office365.com'
smtp_ssl_port = 587
email_login = "xxx#xxx.com" #Office 365 email
email_from = tosendfrom
email_to = Emaitstosendto
msg = MIMEMultipart()
msg['Subject'] = "Received Emails between "+date_7days+" - "+date_14days
msg['From'] = email_from
msg['To'] = email_to
msg['X-Priority'] = '2'
text = ("XXXX,\n"
f"xxxxxx\n\n")
msg.attach(MIMEText(text))
filename = my_files_dict.get(ID)#Files in the folder matching the ID
fo = open(filename,'rb')
s2 = smtplib.SMTP(smtp_ssl_host, smtp_ssl_port)
s2.starttls(context=context)
s2.login(email_login, email_pass)
attachment = email.mime.application.MIMEApplication(fo.read(),_subtype="xlsx")
fo.close()
attachment.add_header('Content-Disposition','attachment',filename=filename)
msg.attach(attachment)
s2.send_message(msg)
s2.quit()
On average, I will be sending emails to a list separated by a semi-colon(;) of about 8 emails per record. This means that for each attachment, I'll send to about 8 emails and I'm to do it for about 70 of such contacts. In total, that will be about 560 emails. Nothing gets sent out I get the above error the moment I log in. On the contrary, when I try test sending it to a list of 3 emails in the test emails column, the same emails go out very well. Can anyone point to where I may not be getting it right? I suspect either the list of emails is too long or is something with the email addresses thus the protocol error? Is this an SMTPlib limitation?
What you specify in MIMEMultipart() is what appears in the message header, but this is not always equal to the list of recipients. You can try to change server.send_message(msg) to server.sendmail(sender,recipients,msg.as_string())
Keep in mind that sendmail() requires a list of recipients, whereas msg['To'] should be set as one string, so if your variable email_to is comma separated, you should write it like:
s2.sendmail(email_from,email_to.split(','),msg.as_string())
More details about sendmail() can be found here.
I resolved this error by creating a list and joined all addresses from a tuple into a string, using a comma character as separator.
family = the list of all recipients' email addresses
family = [
'name1#example.com',
'name2#example.com',
'name3#example.com',
'name4#example.com',
'name5#example.com',
'name6#example.com',
'name7#example.com',
'name8#example.com'
]
msg['To'] =', '.join(family)

Code times out when trying to run as a lambda function in AWS

Below is my code and I am hoping someone can help me with the cleaning up the code and making it more effiencient. Basically, the code should iterate through all the volumes in my AWS account and then list all untagged volumes and then send out an email. However, it times out when running it as a lambda function in AWS but if i run it locally, it will take over 30 mins to complete (however it does complete). Im sure its iterating through things it doesnt need.
Also if I print the ec2_instances list, I can see duplicate values, so I want to only have unique values so that its not repeating the script for each ec2 instance.
import logging
import boto3
from smtplib import SMTP, SMTPException
from email.mime.text import MIMEText
logger = logging.getLogger()
logger.setLevel(logging.INFO)
session = boto3.Session(profile_name="prod")
client = session.client('ec2')
untagged_volumes = []
detached_volumes = []
ec2_instances = []
response = client.describe_volumes()
for volume in response['Volumes']:
if 'Tags' in str(volume):
continue
else:
if 'available' in str(volume):
detached_volumes.append(volume['VolumeId'])
else:
untagged_volumes.append(volume['VolumeId'])
untagged_volumes.append(volume['Attachments'][0]['InstanceId'])
ec2_instances.append(volume['Attachments'][0]['InstanceId'])
unique_instances = list(set(ec2_instances))
# Create the msg body.
msg_body_list = []
for instance in unique_instances:
desc_instance = client.describe_instances()
# append to the msg_body_list the lines that we would like to show on the email
msg_body_list.append("VolumeID: {}".format(desc_instance['Reservations'][0]['Instances'][0]['BlockDeviceMappings'][0]['Ebs']['VolumeId']))
msg_body_list.append("Attached Instance: {}".format(desc_instance['Reservations'][0]['Instances'][0]['InstanceId']))
# if there are tags, we will append it as singles lines as far we have tags
if 'Tags' in desc_instance['Reservations'][0]['Instances'][0]:
msg_body_list.append("Tags:")
for tag in desc_instance['Reservations'][0]['Instances'][0]['Tags']:
msg_body_list.append(" Key: {} | Value: {}".format(tag['Key'], tag['Value']))
# in case we don't have tags, just append no tags.
else:
msg_body_list.append("Tags: no tags")
msg_body_list.append("--------------------")
# send email
mail_from = "xxx#xxx.com"
mail_to = 'xxx#xxx.com'
msg = MIMEText("\n".join(msg_body_list))
msg["Subject"] = "EBS Tagged Instance Report for"
msg["From"] = mail_from
msg["To"] = mail_to
try:
server = SMTP('xxx.xxx.xxx.xxx', 'xx')
server.sendmail(mail_from, mail_to.split(','), msg.as_string())
server.quit()
print('Email sent')
except SMTPException:
print('ERROR! Unable to send mail')
Lambda functions have a time limit of 15 minutes. That is the reason for the timeout - if you need to run scripts for longer, look up AWS Fargate.

How to deal with flooded unseen messages

I have written an email parsing mechanism in python.
It finds a new email and passes the data correctly. I am 99.999% certain that my code is functioning correctly, so there should be no issue there. The problem is that occasionally, the Gmail inbox will get flooded with messages that are considered "unseen". At this point, there is nothing that my code can do.
It fails with:
imaplib.error: FETCH command error: BAD ['Could not parse command']
This is distressing, and I would love to have either
a way to check whether the unseen messages have overflown to this state, or
a way to manually (via imaplib) mark all messages as read, including a way to detect this particular error.
Any thoughts on how to accomplish this?
Here is my code:
#!/usr/bin/env python
import imaplib, re, sys, time, OSC, threading, os
iparg = 'localhost'
oportarg = 9000
iportarg = 9002
usern = 'myusrname#gmail.com'
gpass = 'mypass'
kill_program = False
server = imaplib.IMAP4_SSL('imap.googlemail.com', 993)
oclient = OSC.OSCClient()
email_interval = 2.0
def login():
server.login(usern, gpass)
oclient.connect((iparg, oportarg))
def logout_handle(addr, tags, stuff, source):
print 'received kill call'
global kill_program
kill_program = True
def filter_signature(s): #so annoying; wish i didn't have to do this
try:
a_sig = re.sub(r'Sent|--Sent', '', s)
b_sig = re.sub(r'using SMS-to-email. Reply to this email to text the sender back and', '', a_sig)
c_sig = re.sub(r'save on SMS fees.', '', b_sig)
d_sig = re.sub(r'https://www.google.com/voice', '', c_sig)
no_lines = re.sub(r'\n|=|\r?', '', d_sig) #add weird characters to this as needed
except:
nolines = s
return no_lines
def parse_email(interval):
while True:
server.select('INBOX')
status, ids = server.search(None, 'UnSeen')
print 'status is: ', status
if not ids or ids[0] is '':
print 'no new messages'
else:
try:
print 'found a message; attempting to parse...'
latest_id = ids[0]
status, msg_data = server.fetch(latest_id, '(UID BODY[TEXT])')
raw_data = msg_data[0][1]
raw_filter = raw_data
print 'message result: ', raw_filter
time.sleep(interval)
#execute main block
while not kill_program:
login()
parse_email(email_interval)
st.kill()
sys.exit()
Based upon the error, I would very carefully check the parameters that you're passing to fetch. Gmail is telling you that it could not parse the command that you sent to it.
Also, you can do a STORE +FLAGS \SEEN to mark the messages as read.

Categories