I have a very simple piece of code that I used in previous versions of Python without issues (version 2.5 and prior). Now with 3.0, the following code give the error on the login line "argument 1 must be string or buffer, not str".
import smtplib
smtpserver = 'mail.somedomain.com'
AUTHREQUIRED = 1 # if you need to use SMTP AUTH set to 1
smtpuser = 'admin#somedomain.com' # for SMTP AUTH, set SMTP username here
smtppass = 'somepassword' # for SMTP AUTH, set SMTP password here
msg = "Some message to send"
RECIPIENTS = ['admin#somedomain.com']
SENDER = 'someone#someotherdomain.net'
session = smtplib.SMTP(smtpserver)
if AUTHREQUIRED:
session.login(smtpuser, smtppass)
smtpresult = session.sendmail(SENDER, RECIPIENTS, msg)
Google shows there are some issues with that error not being clear, but I still can't figure out what I need to try to make it work. Suggestions included defining the username as b"username", but that doesn't seem to work either.
UPDATE: just noticed from a look at the bug tracker there's a suggested fix also:
Edit smtplib.py and replace the existing encode_plain() definition with this:
def encode_plain(user, password):
s = "\0%s\0%s" % (user, password)
return encode_base64(s.encode('ascii'), eol='')
Tested here on my installation and it works properly.
Traceback (most recent call last):
File "smtptest.py", line 18, in <module>
session.login(smtpuser, smtppass)
File "c:\Python30\lib\smtplib.py", line 580, in login
AUTH_PLAIN + " " + encode_plain(user, password))
File "c:\Python30\lib\smtplib.py", line 545, in encode_plain
return encode_base64("\0%s\0%s" % (user, password))
File "c:\Python30\lib\email\base64mime.py", line 96, in body_encode
enc = b2a_base64(s[i:i + max_unencoded]).decode("ascii")
TypeError: b2a_base64() argument 1 must be bytes or buffer, not str
Your code is correct. This is a bug in smtplib or in the base64mime.py.
You can track the issue here:
http://bugs.python.org/issue5259
Hopefully the devs will post a patch soon.
As a variation on Jay's answer, rather than edit smtplib.py you could "monkey patch" it at run time.
Put this somewhere in your code:
def encode_plain(user, password):
s = "\0%s\0%s" % (user, password)
return encode_base64(s.encode('ascii'), eol='')
import smtplib
encode_plain.func_globals = vars(smtplib)
smtplib.encode_plain = encode_plain
This is kind of ugly but useful if you want to deploy your code onto other systems without making changes to their python libraries.
This issue has been addressed in Python3.1. Get the update at http://www.python.org/download/releases/3.1/
Related
I am new to Python, and I am attempting to write code to get me into my gmail inbox, following along with a Udemy course. I have successfully installed imapclient, and importing impapclient does not return any error so I think I am okay on that.
When I go to write my next line of code conn = imapclient.IMAPClient('imap.gmail.com', ssl=True) and enter it, it gives me a Type Error
Traceback (most recent call last):
File "<pyshell#15>", line 1, in <module>
conn = imapclient.IMAPClient('imap.gmail.com', ssl=True)
File "C:\Users\china\AppData\Local\Programs\Python\Python39\lib\site-packages\imapclient\imapclient.py", line 254, in __init__
self._imap = self._create_IMAP4()
File "C:\Users\china\AppData\Local\Programs\Python\Python39\lib\site-packages\imapclient\imapclient.py", line 288, in _create_IMAP4
return tls.IMAP4_TLS(self.host, self.port, self.ssl_context,
File "C:\Users\china\AppData\Local\Programs\Python\Python39\lib\site-packages\imapclient\tls.py", line 44, in __init__
imaplib.IMAP4.__init__(self, host, port)
File "C:\Users\china\AppData\Local\Programs\Python\Python39\lib\imaplib.py", line 202, in __init__
self.open(host, port, timeout)
TypeError: open() takes 3 positional arguments but 4 were given
I know that both ssl=True and 'imap.gmail.com' are both required arguments, so I have not tried taking either out. I only have two lines of code, and I am following the exact code on the Udemy course, so I am not sure how to resolve this myself.
I had exactly the same problem.
The problem is that imapclient is only officially supported until Python 3.7, so you could try using imapclient with Python 3.7.
Or you could use imaplib module. It is a bit more complicated, but I tried using imaplib and pyzmail module (to make the text more readable)
import imaplib
import pyzmail
server = imaplib.IMAP4_SSL('IMAP_SERVER')
server.login('email#example.com', 'password')
server.select("INBOX")
typ, data = server.search(None, "ALL")
for num in data[0].split():
typ, data = server.fetch(num, '(RFC822)')
message = pyzmail.PyzMessage.factory(data[0][1])
print('THIS IS MESSAGE NO. ' + str(num))
if(message.text_part != None):
print(message.text_part.get_payload().decode(message.text_part.charset))
elif(message.html_part != None):
print(message.html_part.get_payload().decode(message.html_part.charset))
server.close()
server.logout()
im not an expert at Python, so using pyzmail might not be the optimal solution, but it worked for me using Python 3.9
for IMAP_SERVER you have to insert the right IMAP server domain name (like imap.gmail.com for gmail accounts)
and you have to enter your real email address and password (or get the password with input())
I got the same issue. And it has been fixed in IMAPClient 2.2.0.
https://imapclient.readthedocs.io/en/2.2.0/releases.html
I have a list of emails(mine) that I want to test against a list of passwords(All valid and some none valid of course) using imaplib library. Whenever I test the program ordinarily like in the code below, it works perfectly no errors.
import sys
import imaplib
# connect to host using SSL
imap_host = 'imap.server.com'
imap_port = '993'
imap_user = 'username#email'
imap_pass = 'RightPassword'
imap = imaplib.IMAP4_SSL(imap_host, imap_port)
## login to server
try:
login = imap.login(imap_user, imap_pass)
if login:
print login
except imaplib.IMAP4.error as error:
print error
#
But whenever I run the code such as to parsing credentials through a function to handle the authentication protocols such as the following code below, I get an error saying
"LOGIN command error: BAD ['Missing \'"\'']".
I have tried all sort of things I could find using google and non seem to handle it properly.
"""
E-mail Tester
NB: This is for educational purpose only.
"""
import sys
import imaplib
EMAILS_FILE = open('email_list.txt', 'r')
PASSWORD_FILE = open('pass_list.txt', 'r')
SUCCESS_FILE = open('success.txt', 'a')
EMAILS_FILE_LIST = []
def set_check(_emails):
email = str(_emails)
PASSWORD_FILE.seek(0)
for passwords in PASSWORD_FILE:
password = str(passwords)
# connect to host using SSL
imap_host = 'imap.server.com'
imap_port = '993'
imap = imaplib.IMAP4_SSL(imap_host, imap_port)
## login to server
try:
# print "%s%s" % (email,password)
# print "I got here so far"
# sys.exit()
print "Testing <--> E-mail: %s - Password: %s" % (email, password)
login = imap.login("%s","%s" % (email, password))
if login:
print login
print "OK <---> E-mail: %s\nPassword: %s" % (email, password)
except imaplib.IMAP4.error as error:
print error
for emails in EMAILS_FILE:
EMAILS_FILE_LIST.append(emails)
for email_count in range(0, len(EMAILS_FILE_LIST)):
set_check(EMAILS_FILE_LIST[email_count])
I have tried all kind of suggestions I could find on the internet but non has worked thus far.
I expect imap.login to handle the authentication without the mysterious error output
"LOGIN command error: BAD ['Missing \'"\'']"
login = imap.login("%s","%s" % (email, password))
does not work. It throws an error in Python: TypeError: not all arguments converted during string formatting, because you're providing two strings to one %s.
Why don't you just use imap.login(email, password)? It has the same effect as what you're trying to do.
And what does your password file look like? What is it actually sending? Please provide the log line before it crashes. (anonymizing if necessary, but leaving any punctuation in for help diagnosing)
Okay, so I actually got this fixed by removing trail lines from my strings.
email = str(_emails).rstrip()
PASSWORD_FILE.seek(0)
for passwords in PASSWORD_FILE:
password = str(passwords).rstrip()
the error is caused by trail lines in the strings.
Hello I try to make a small monitoring, and in the monitoring.py, it should send a email with function sendmail.sende(). But i think, i got issues with variables. Could you give me some tips, what i do wrong please?
This is the sendmail.py
def sende():
import smtplib
import configparser
from email.message import Message
config = configparser.ConfigParser()
config.read('config.ini')
absender=(config['SMTP']['absender'])
password=(config['SMTP']['password'])
empfaenger=(config['SMTP']['empfaenger'])
smtphost=(config['SMTP']['smtphost'])
port=int(config['SMTP']['port'])
def readLastLog():
log=open('log', 'r')
for line in log:
last= line
print (last)
log.close()
#check for if variables is correct
print("Email Config:")
print(absender, '\n',empfaenger,'\n',smtphost,'\n',port)
server = smtplib.SMTP_SSL(host=smtphost, port=port)
#do I need realy need this?
#server.esmtp_features['auth'] = 'LOGIN PLAIN'
nachricht = Message()
nachricht.set_payload(readLastLog())
nachricht["Subject"] = "Kritische Warnung - Hardlimit erreicht"
nachricht["FROM"] = absender
nachricht["To"] = empfaenger
server.login(absender, password)
server.sendmail(absender, empfaenger, nachricht.as_string())
server.quit()
sende()
Error:
Traceback (most recent call last):
File "./sendmail.py", line 42, in <module>
sende()
File "./sendmail.py", line 38, in sende
server.login(absender, password)
File "/usr/lib/python3.5/smtplib.py", line 729, in login
raise last_exception
File "/usr/lib/python3.5/smtplib.py", line 720, in login
initial_response_ok=initial_response_ok)
File "/usr/lib/python3.5/smtplib.py", line 641, in auth
raise SMTPAuthenticationError(code, resp)
smtplib.SMTPAuthenticationError: (535, b'5.7.8 Error: authentication failed: authentication failure')
You did not describe what is going wrong, so just guessing. But the function readLastLog is wrong:
it only print to stdout and does not return anything - so actually returns None
it is later used to initialize the payload which gives only nachricht.set_payload(None)
Some other immediate improvements:
declaring a function inside another one is not wrong and have real use cases, but here I really wonder why readLastLog is defined inside sende body...
as you have built a Message with its To: and From: header, you could use the send_message function:
...
server.send_message(nachricht)
server.quit()
So i changed the readLastLog with a return, and made a seperate function,
Anyway that hasnt fixed the skript, so problem here was maybe with my #web.de mail.
Changed to a own mailserver with other email. Now i do reciving emails.
Sorry for taking ur time.
def readLastLog():
log=open('log', 'r')
for line in log:
lastlog= line
print (lastlog)
log.close()
return lastlog
def sende():
import smtplib
import configparser
from email.message import Message
config = configparser.ConfigParser()
config.read('config.ini')
absender=(config['SMTP']['absender'])
password=(config['SMTP']['password'])
empfaenger=(config['SMTP']['empfaenger'])
smtphost=(config['SMTP']['smtphost'])
port=int(config['SMTP']['port'])
#check for if variables is correct
print("Email Config:")
print(absender, '\n',empfaenger,'\n',smtphost,'\n',port)
server = smtplib.SMTP_SSL(host=smtphost, port=port)
#do I need realy need this?
#server.esmtp_features['auth'] = 'LOGIN PLAIN'
nachricht = Message()
nachricht.set_payload(readLastLog())
nachricht["Subject"] = "Kritische Warnung - Hardlimit erreicht"
nachricht["FROM"] = absender
nachricht["To"] = empfaenger
server.login(absender, password)
server.sendmail(absender, empfaenger, nachricht.as_string())
server.quit()
I have a python script that gets executed from a php script.
This script sends and email and it is several orders of magnitude faster than php with swiftmailer, but i have a problem. I don't really understand much about python or charachter encoding, and sometimes an error is generated about the encoding from the MIMEText library of python. I googled and found a solution, but i still want to have the php script as a fallback in case of failure in the python script.
The problem is, in the except loop it seems i am unable to pass the data back to php before the script terminates? any ideas on what i'm doing wrong here? You can see in the except block that i say result = {type: 'failed'} then attempt to json encode it but the data doesn't get back to the php script.
import sys, json, os, smtplib, socket, logging
LOG_FILE='/tmp/mandrill.out'
logging.basicConfig(filename=LOG_FILE,level=logging.DEBUG)
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.header import Header
from email.utils import formataddr
try:
data = json.loads(sys.argv[1])
msg = MIMEMultipart('alternative')
msg['Subject'] = data['subject']
msg['From'] = formataddr((str(Header(u'Business Name', 'utf-8')), data['from']))
msg['To'] = data['to']
html = data['body']
email = MIMEText(html, 'html','utf-8')
username = 'username'
password = 'password'
msg.attach(email)
s = smtplib.SMTP('smtp.example.com', 587, socket.getfqdn(), 3)
s.login(username, password)
s.sendmail(msg['From'], msg['To'], msg.as_string())
s.quit()
result = {'type': 'success'}
print json.dumps(result)
except:
result = {'type': 'error'}
logging.exception('Got exception on main handler')
raise
return json.dumps(result)
EDIT TO INCLUDE TRACEBACK
ERROR:root:Got exception on main handler
Traceback (most recent call last):
File "/usr/share/nginx/mandrill/mandrill.py", line 16, in <module>
email = MIMEText(html, 'html')
File "/usr/lib/python2.7/email/mime/text.py", line 30, in __init__
self.set_payload(_text, _charset)
File "/usr/lib/python2.7/email/message.py", line 226, in set_payload
self.set_charset(charset)
File "/usr/lib/python2.7/email/message.py", line 262, in set_charset
self._payload = self._payload.encode(charset.output_charset)
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2019' in position 9580: ordinal not in range(128)
You can't use return to return data to the PHP calling script, you'll need to print it as you are doing in the successful case.
Also, calling raise will propagate the exception up to the main part of your script, thus preventing execution of the return json.dumps(result) (which should be a print). You don't need to raise the exception again (your script will terminate anyway).
You might also (or instead) want to set a return code from the python script via sys.exit(), then your PHP script can check the return code to detect errors. The convention is to set a return code of 0 for success and anything else for failure.
Also, you are using a bare except clause which will catch all exceptions - what it the actual exception that you are seeing? Can you update your question with the full traceback?
The raise causes the Python script to terminate, so it never reaches the return.
The next problem is that return can only be used inside Python to return a value to the calling function. You probably want to print the JSON string instead (but this somewhat depends on how you are calling Python from PHP, which you have not documented here).
The traceback indicates that
email = MIMEText(html, 'html','utf-8')
is not actually true; you are lacking the 'utf-8' parameter in the code which caused the error traceback.
You are killing the program with sys.exit(1).
If you want to return data back to something, I would exchange the sys.exit(1) line with
return json.dumps(result)
I want to use the credentials of the logged-in Windows user to authenticate an SMTP connection to an Exchange server using NTLM.
I'm aware of the python-ntlm module and the two patches that enable NTLM authentication for SMTP, however I want to use the current user's security token and not have to supply a username and password.
Very similar problem to Windows Authentication with Python and urllib2.
Although the solution below only uses the Python Win32 extensions (the sspi example code included with the Python Win32 extensions was very helpful), the python-ntlm IMAP & SMTP patches mentioned in the question also served as useful guides.
from smtplib import SMTPException, SMTPAuthenticationError
import string
import base64
import sspi
# NTLM Guide -- http://curl.haxx.se/rfc/ntlm.html
SMTP_EHLO_OKAY = 250
SMTP_AUTH_CHALLENGE = 334
SMTP_AUTH_OKAY = 235
def asbase64(msg):
# encoding the message then convert to string
return base64.b64encode(msg).decode("utf-8")
def connect_to_exchange_as_current_user(smtp):
"""Example:
>>> import smtplib
>>> smtp = smtplib.SMTP("my.smtp.server")
>>> connect_to_exchange_as_current_user(smtp)
"""
# Send the SMTP EHLO command
code, response = smtp.ehlo()
if code != SMTP_EHLO_OKAY:
raise SMTPException("Server did not respond as expected to EHLO command")
sspiclient = sspi.ClientAuth('NTLM')
# Generate the NTLM Type 1 message
sec_buffer=None
err, sec_buffer = sspiclient.authorize(sec_buffer)
ntlm_message = asbase64(sec_buffer[0].Buffer)
# Send the NTLM Type 1 message -- Authentication Request
code, response = smtp.docmd("AUTH", "NTLM " + ntlm_message)
# Verify the NTLM Type 2 response -- Challenge Message
if code != SMTP_AUTH_CHALLENGE:
raise SMTPException("Server did not respond as expected to NTLM negotiate message")
# Generate the NTLM Type 3 message
err, sec_buffer = sspiclient.authorize(base64.decodebytes(response))
ntlm_message = asbase64(sec_buffer[0].Buffer)
# Send the NTLM Type 3 message -- Response Message
code, response = smtp.docmd(ntlm_message)
if code != SMTP_AUTH_OKAY:
raise SMTPAuthenticationError(code, response)
Great answer but as an update for python 3
def asbase64(msg):
# encoding the message then convert to string
return base64.b64encode(msg).decode("utf-8")
Python 2.7.x will fail on sending the NTLM Type 3 message due to the blank cmd specified:
code, response = smtp.docmd("", ntlm_message)
This ends up sending the correct response back to the server, however it pre-pends a space due to the nature of docmd() calling putcmd().
smtplib.py:
def putcmd(self, cmd, args=""):
"""Send a command to the server."""
if args == "":
str = '%s%s' % (cmd, CRLF)
else:
str = '%s %s%s' % (cmd, args, CRLF)
self.send(str)
# ...
def docmd(self, cmd, args=""):
"""Send a command, and return its response code."""
self.putcmd(cmd, args)
return self.getreply()
which as a result takes the path of the else condition, thereby sending str(' ' + ntlm_message + CRLF) which results in (501, 'Syntax error in parameters or arguments').
As such the fix is simply to send the NTLM message as the cmd.
code, response = smtp.docmd(ntlm_message)
A fix to the above answer was submitted, though who knows when it will be reviewed/accepted.