Changeset 93
- Timestamp:
- 06/18/07 18:58:13 (2 years ago)
- Files:
-
- src/jmc/jabber/component.py (modified) (3 diffs)
- src/jmc/jabber/tests/component.py (modified) (4 diffs)
- src/jmc/model/account.py (modified) (28 diffs)
- src/jmc/model/tests/account.py (modified) (7 diffs)
- src/jmc/model/tests/server.py (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
src/jmc/jabber/component.py
r91 r93 244 244 to_node = message.get_to().node 245 245 to_email = to_node.replace('%', '@', 1) 246 accounts[0].send_email(to_email, 247 message.get_subject(), 248 message.get_body()) 246 accounts[0].send_email(\ 247 accounts[0].create_email(accounts[0].default_from, 248 to_email, 249 message.get_subject(), 250 message.get_body())) 249 251 return self.send_mail_result(message, lang_class, to_email) 250 252 … … 254 256 def __init__(self): 255 257 SendMailMessageHandler.__init__(self) 256 self.to_regexp = re.compile("^\s*(to|TO )\s*:\s*(?P<to_email>.*)")258 self.to_regexp = re.compile("^\s*(to|TO|To)\s*:\s*(?P<to_email>.*)") 257 259 self.__logger = logging.getLogger(\ 258 260 "jmc.jabber.component.RootSendMailMessageHandler") … … 286 288 message_body.extend(lines) 287 289 if to_email is not None: 288 accounts[0].send_email(to_email, message.get_subject(), 289 "\n".join(message_body)) 290 accounts[0].send_email(\ 291 accounts[0].create_email(accounts[0].default_from, 292 to_email, 293 message.get_subject(), 294 "\n".join(message_body))) 290 295 return self.send_mail_result(message, lang_class, to_email) 291 296 else: src/jmc/jabber/tests/component.py
r91 r93 143 143 MockMailAccount._init(self) 144 144 145 class MockSMTPAccount(object): 146 def __init__(self): 147 self.default_from = "user1@test.com" 148 149 def create_email(self, from_email, to_email, subject, body): 150 return (from_email, to_email, subject, body) 151 152 def send_email(self, email): 153 self.email = email 154 145 155 class MailComponent_TestCase(unittest.TestCase): 146 156 def setUp(self): … … 526 536 subject="message subject", 527 537 body="message body") 528 class MockSMTPAccount(object):529 def send_email(self, to_email, subject, body):530 self.to_email = to_email531 self.subject = subject532 self.body = body533 538 accounts = [MockSMTPAccount()] 534 539 result = self.handler.handle(message, Lang.en, accounts) 535 self.assertEquals(accounts[0]. to_email, "user@test.com")536 self.assertEquals(accounts[0]. subject, "message subject")537 self.assertEquals(accounts[0]. body, "message body")540 self.assertEquals(accounts[0].email[1], "user@test.com") 541 self.assertEquals(accounts[0].email[2], "message subject") 542 self.assertEquals(accounts[0].email[3], "message body") 538 543 self.assertEquals(len(result), 1) 539 544 self.assertEquals(result[0].stanza_type, "message") … … 633 638 body="to: user@test.com\n" \ 634 639 "message body\nother line") 635 class MockSMTPAccount(object):636 def send_email(self, to_email, subject, body):637 self.to_email = to_email638 self.subject = subject639 self.body = body640 640 accounts = [MockSMTPAccount()] 641 641 result = self.handler.handle(message, Lang.en, accounts) 642 self.assertEquals(accounts[0].to_email, "user@test.com") 643 self.assertEquals(accounts[0].subject, "message subject") 644 self.assertEquals(accounts[0].body, "message body\nother line") 642 self.assertEquals(accounts[0].email[1], "user@test.com") 643 self.assertEquals(accounts[0].email[2], "message subject") 644 self.assertEquals(accounts[0].email[3], 645 "message body\nother line") 645 646 self.assertEquals(len(result), 1) 646 647 self.assertEquals(result[0].get_type(), None) … … 657 658 subject="message subject", 658 659 body="message body") 659 class MockSMTPAccount(object):660 def send_email(self, to_email, subject, body):661 self.to_email = to_email662 self.subject = subject663 self.body = body664 660 accounts = [MockSMTPAccount()] 665 661 result = self.handler.handle(message, Lang.en, accounts) src/jmc/model/account.py
r92 r93 5 5 ## Started on Fri Jan 19 18:21:44 2007 David Rousselie 6 6 ## $Id$ 7 ## 7 ## 8 8 ## Copyright (C) 2007 David Rousselie 9 9 ## This program is free software; you can redistribute it and/or modify … … 11 11 ## the Free Software Foundation; either version 2 of the License, or 12 12 ## (at your option) any later version. 13 ## 13 ## 14 14 ## This program is distributed in the hope that it will be useful, 15 15 ## but WITHOUT ANY WARRANTY; without even the implied warranty of 16 16 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 17 ## GNU General Public License for more details. 18 ## 18 ## 19 19 ## You should have received a copy of the GNU General Public License 20 20 ## along with this program; if not, write to the Free Software … … 25 25 import logging 26 26 import email 27 import email.Header 27 from email.Header import Header 28 from email.MIMEText import MIMEText 29 from email.MIMEMultipart import MIMEMultipart 28 30 import traceback 29 31 … … 31 33 import imaplib 32 34 import socket 35 import smtplib 33 36 34 37 from sqlobject.inheritance import InheritableSQLObject … … 81 84 msg = "getaddrinfo returns an empty list" 82 85 self.sock = None 83 for res in socket.getaddrinfo(self.host, 84 self.port, 85 0, 86 for res in socket.getaddrinfo(self.host, 87 self.port, 88 0, 86 89 socket.SOCK_STREAM): 87 90 af, socktype, proto, canonname, sa = res … … 104 107 105 108 class MYPOP3_SSL(poplib.POP3_SSL): 106 def __init__(self, host, port=poplib.POP3_SSL_PORT, keyfile=None, 109 def __init__(self, host, port=poplib.POP3_SSL_PORT, keyfile=None, 107 110 certfile=None): 108 111 self.host = host … … 113 116 msg = "getaddrinfo returns an empty list" 114 117 self.sock = None 115 for res in socket.getaddrinfo(self.host, self.port, 0, 118 for res in socket.getaddrinfo(self.host, self.port, 0, 116 119 socket.SOCK_STREAM): 117 120 af, socktype, proto, canonname, sa = res … … 155 158 store_password = BoolCol(default=True) 156 159 live_email_only = BoolCol(default=False) 157 160 158 161 lastcheck = IntCol(default=0) 159 162 waiting_password_reply = BoolCol(default=False) 160 163 first_check = BoolCol(default=True) 161 164 162 165 def _init(self, *args, **kw): 163 166 """MailAccount init … … 168 171 self.connected = False 169 172 self.default_lang_class = Lang.en 170 173 171 174 def _get_register_fields(cls, real_class=None): 172 175 """See Account._get_register_fields … … 205 208 account.int_post_func, 206 209 lambda bare_from_jid: 5)] 207 210 208 211 get_register_fields = classmethod(_get_register_fields) 209 212 … … 211 214 return 42 212 215 get_default_port = classmethod(_get_default_port) 213 216 214 217 def _get_presence_actions_fields(cls): 215 218 """See PresenceAccount._get_presence_actions_fields … … 227 230 'offline_action': (cls.possibles_actions, 228 231 PresenceAccount.DO_NOTHING)} 229 232 230 233 get_presence_actions_fields = classmethod(_get_presence_actions_fields) 231 234 … … 256 259 257 260 return result 258 261 259 262 def format_message(self, email_msg, include_body = True): 260 263 from_decoded = email.Header.decode_header(email_msg["From"]) … … 313 316 "".join(traceback.format_exception 314 317 (type, value, stack, 5)) 315 318 316 319 result += u"\n\n" 317 320 … … 330 333 def format_message_summary(self, email_msg): 331 334 return self.format_message(email_msg, False) 332 335 333 336 def get_status_msg(self): 334 337 return self.get_type() + "://" + self.login + "@" + self.host + ":" + \ … … 372 375 account.default_post_func, 373 376 lambda bare_from_jid: "INBOX")] 374 377 375 378 get_register_fields = classmethod(_get_register_fields) 376 379 … … 378 381 """Return default IMAP server port""" 379 382 return 143 380 383 381 384 get_default_port = classmethod(_get_default_port) 382 385 … … 389 392 return "imaps" 390 393 return "imap" 391 394 392 395 def get_status(self): 393 396 return MailAccount.get_status(self) + "/" + self.mailbox … … 427 430 email.message_from_string(data[0][1])) 428 431 return u"Error while fetching mail " + str(index) 429 432 430 433 def get_mail_summary(self, index): 431 434 self.__logger.debug("Getting mail summary " + str(index)) … … 445 448 def mark_all_as_read(self): 446 449 self.get_mail_list() 447 450 448 451 type = property(get_type) 449 452 … … 451 454 nb_mail = IntCol(default=0) 452 455 lastmail = IntCol(default=0) 453 456 454 457 def _init(self, *args, **kw): 455 458 MailAccount._init(self, *args, **kw) … … 459 462 """Return default POP3 server port""" 460 463 return 110 461 464 462 465 get_default_port = classmethod(_get_default_port) 463 466 … … 471 474 def connect(self): 472 475 self.__logger.debug("Connecting to POP3 server " 473 + self.login + "@" + self.host + ":" + 476 + self.login + "@" + self.host + ":" + 474 477 str(self.port) + ". SSL=" + str(self.ssl)) 475 478 if self.ssl: … … 483 486 self.connection.pass_(self.password) 484 487 self.connected = True 485 488 486 489 487 490 def disconnect(self): … … 494 497 self.__logger.debug("Getting mail list") 495 498 count, size = self.connection.stat() 496 self.nb_mail = count 499 self.nb_mail = count 497 500 return [str(i) for i in range(1, count + 1)] 498 501 … … 536 539 self.get_mail_list() 537 540 self.lastmail = self.nb_mail 538 541 539 542 540 543 class SMTPAccount(Account): … … 560 563 """Return default SMTP server port""" 561 564 return 25 562 565 563 566 get_default_port = classmethod(_get_default_port) 564 567 … … 629 632 default_account_post_func, 630 633 default_account_default_func)] 631 634 632 635 get_register_fields = classmethod(_get_register_fields) 633 636 634 def send_email(self, to_email, subject, body): 637 def create_email(self, from_email, to_email, subject, body): 638 """Create new email""" 639 email = MIMEText(body) 640 email['Subject'] = Header(str(subject)) 641 email['From'] = Header(str(from_email)) 642 email['To'] = Header(str(to_email)) 643 return email 644 645 def __say_hello(self, connection): 646 if not (200 <= connection.ehlo()[0] <= 299): 647 (code, resp) = connection.helo() 648 if not (200 <= code <= 299): 649 raise SMTPHeloError(code, resp) 650 651 def send_email(self, email): 652 """Send email according to current account parameters""" 635 653 self.__logger.debug("Sending email:\n" 636 "From: " + self.default_from + "\n" + 637 "To: " + to_email + "\n" + 638 "Subject: " + subject + "\n\n" + 639 body) 654 + str(email)) 655 smtp_connection = smtplib.SMTP() 656 if self.__logger.getEffectiveLevel() == logging.DEBUG: 657 smtp_connection.set_debuglevel(1) 658 smtp_connection.connect(self.host, self.port) 659 self.__say_hello(smtp_connection) 660 if self.tls: 661 smtp_connection.starttls() 662 self.__say_hello(smtp_connection) 663 if self.login is not None and len(self.login) > 0: 664 auth_methods = smtp_connection.esmtp_features["auth"].split() 665 auth_methods.reverse() 666 current_error = None 667 for auth_method in auth_methods: 668 self.__logger.debug("Trying to authenticate using " 669 + auth_method + " method") 670 smtp_connection.esmtp_features["auth"] = auth_method 671 try: 672 smtp_connection.login(self.login, self.password) 673 current_error = None 674 self.__logger.debug("Successfuly to authenticate using " 675 + auth_method + " method") 676 break 677 except smtplib.SMTPAuthenticationError, error: 678 self.__logger.debug("Failed to authenticate using " 679 + auth_method + " method") 680 current_error = error 681 if current_error is not None: 682 raise current_error 683 smtp_connection.sendmail(str(email['From']), str(email['To']), 684 email.as_string()) 685 smtp_connection.quit() src/jmc/model/tests/account.py
r88 r93 5 5 ## Started on Wed Feb 14 08:23:17 2007 David Rousselie 6 6 ## $Id$ 7 ## 7 ## 8 8 ## Copyright (C) 2007 David Rousselie 9 9 ## This program is free software; you can redistribute it and/or modify … … 11 11 ## the Free Software Foundation; either version 2 of the License, or 12 12 ## (at your option) any later version. 13 ## 13 ## 14 14 ## This program is distributed in the hope that it will be useful, 15 15 ## but WITHOUT ANY WARRANTY; without even the implied warranty of 16 16 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 17 ## GNU General Public License for more details. 18 ## 18 ## 19 19 ## You should have received a copy of the GNU General Public License 20 20 ## along with this program; if not, write to the Free Software … … 169 169 del account.hub.threadConnection 170 170 self.account_class = POP3Account 171 171 172 172 def tearDown(self): 173 173 account.hub.threadConnection = connectionForURI('sqlite://' + self.db_url) … … 211 211 212 212 test_connection = make_test() 213 213 214 214 test_get_mail_list = \ 215 215 make_test(["+OK 2 20\r\n"], \ … … 341 341 self.failUnless(self.server.verify_queries()) 342 342 return inner 343 343 344 344 test_connection = make_test() 345 345 … … 434 434 self.assertTrue(value) 435 435 del account.hub.threadConnection 436 436 437 437 def test_default_account_post_func_true(self): 438 438 account.hub.threadConnection = connectionForURI('sqlite://' + self.db_url) … … 467 467 del account.hub.threadConnection 468 468 469 def test_create_email(self): 470 account.hub.threadConnection = connectionForURI('sqlite://' + self.db_url) 471 account11 = SMTPAccount(user_jid="user1@test.com", 472 name="account11", 473 jid="account11@jmc.test.com") 474 del account.hub.threadConnection 475 email = account11.create_email("from@test.com", 476 "to@test.com", 477 "subject", 478 "body") 479 self.assertEqual(email['From'], "from@test.com") 480 self.assertEqual(email['To'], "to@test.com") 481 self.assertEqual(email['Subject'], "subject") 482 self.assertEqual(email.get_payload(), "body") 483 484 def make_test(self, responses=None, queries=None, core=None): 485 def inner(): 486 self.server = server.DummyServer("localhost", 1025) 487 thread.start_new_thread(self.server.serve, ()) 488 self.server.responses = [] 489 if responses: 490 self.server.responses += responses 491 self.server.responses += ["221 localhost closing connection\r\n"] 492 self.server.queries = [] 493 if queries: 494 self.server.queries += queries 495 self.server.queries += ["quit\r\n"] 496 if core: 497 account.hub.threadConnection = connectionForURI('sqlite://' 498 + self.db_url) 499 core(self) 500 del account.hub.threadConnection 501 self.failUnless(self.server.verify_queries()) 502 return inner 503 504 def test_send_email_esmtp_no_auth(self): 505 account.hub.threadConnection = connectionForURI('sqlite://' 506 + self.db_url) 507 smtp_account = SMTPAccount(user_jid="user1@test.com", 508 name="account11", 509 jid="account11@jmc.test.com") 510 smtp_account.host = "localhost" 511 smtp_account.port = 1025 512 del account.hub.threadConnection 513 email = smtp_account.create_email("from@test.com", 514 "to@test.com", 515 "subject", 516 "body") 517 test_func = self.make_test(["220 localhost ESMTP\r\n", 518 "250-localhost Hello 127.0.0.1\r\n" 519 + "250-SIZE 52428800\r\n" 520 + "250-PIPELINING\r\n" 521 + "250 HELP\r\n", 522 "250 OK\r\n", 523 "250 Accepted\r\n", 524 "354 Enter message\r\n", 525 None, None, None, None, 526 None, None, None, None, 527 "250 OK\r\n"], 528 ["ehlo \[127.0.0.1\]\r\n", 529 "mail FROM:<" + str(email['From']) + ">.*", 530 "rcpt TO:<" + str(email['To']) + ">\r\n", 531 "data\r\n"] + 532 email.as_string().split("\n") + [".\r\n"], 533 lambda self: \ 534 smtp_account.send_email(email)) 535 test_func() 536 537 def test_send_email_no_auth(self): 538 account.hub.threadConnection = connectionForURI('sqlite://' 539 + self.db_url) 540 smtp_account = SMTPAccount(user_jid="user1@test.com", 541 name="account11", 542 jid="account11@jmc.test.com") 543 smtp_account.host = "localhost" 544 smtp_account.port = 1025 545 del account.hub.threadConnection 546 email = smtp_account.create_email("from@test.com", 547 "to@test.com", 548 "subject", 549 "body") 550 test_func = self.make_test(["220 localhost SMTP\r\n", 551 "504 ESMTP not supported\r\n", 552 "250-localhost Hello 127.0.0.1\r\n" 553 + "250-SIZE 52428800\r\n" 554 + "250-PIPELINING\r\n" 555 + "250 HELP\r\n", 556 "250 OK\r\n", 557 "250 Accepted\r\n", 558 "354 Enter message\r\n", 559 None, None, None, None, 560 None, None, None, None, 561 "250 OK\r\n"], 562 ["ehlo \[127.0.0.1\]\r\n", 563 "helo \[127.0.0.1\]\r\n", 564 "mail FROM:<" + str(email['From']) + ">.*", 565 "rcpt TO:<" + str(email['To']) + ">\r\n", 566 "data\r\n"] + 567 email.as_string().split("\n") + [".\r\n"], 568 lambda self: \ 569 smtp_account.send_email(email)) 570 test_func() 571 572 def test_send_email_esmtp_auth(self): 573 account.hub.threadConnection = connectionForURI('sqlite://' 574 + self.db_url) 575 smtp_account = SMTPAccount(user_jid="user1@test.com", 576 name="account11", 577 jid="account11@jmc.test.com") 578 smtp_account.host = "localhost" 579 smtp_account.port = 1025 580 smtp_account.login = "user" 581 smtp_account.password = "pass" 582 del account.hub.threadConnection 583 email = smtp_account.create_email("from@test.com", 584 "to@test.com", 585 "subject", 586 "body") 587 test_func = self.make_test(["220 localhost ESMTP\r\n", 588 "250-localhost Hello 127.0.0.1\r\n" 589 + "250-SIZE 52428800\r\n" 590 + "250-AUTH PLAIN LOGIN CRAM-MD5\r\n" 591 + "250-PIPELINING\r\n" 592 + "250 HELP\r\n", 593 "334 ZGF4IDNmNDM2NzY0YzBhNjgyMTQ1MzhhZGNiMjE2YTYxZjRm\r\n", 594 "235 Authentication succeeded\r\n", 595 "250 OK\r\n", 596 "250 Accepted\r\n", 597 "354 Enter message\r\n", 598 None, None, None, None, 599 None, None, None, None, 600 "250 OK\r\n"], 601 ["ehlo \[127.0.0.1\]\r\n", 602 "AUTH CRAM-MD5\r\n", 603 ".*\r\n", 604 "mail FROM:<" + str(email['From']) + ">.*", 605 "rcpt TO:<" + str(email['To']) + ">\r\n", 606 "data\r\n"] + 607 email.as_string().split("\n") + [".\r\n"], 608 lambda self: \ 609 smtp_account.send_email(email)) 610 test_func() 611 612 def test_send_email_esmtp_auth_method2(self): 613 account.hub.threadConnection = connectionForURI('sqlite://' 614 + self.db_url) 615 smtp_account = SMTPAccount(user_jid="user1@test.com", 616 name="account11", 617 jid="account11@jmc.test.com") 618 smtp_account.host = "localhost" 619 smtp_account.port = 1025 620 smtp_account.login = "user" 621 smtp_account.password = "pass" 622 del account.hub.threadConnection 623 email = smtp_account.create_email("from@test.com", 624 "to@test.com", 625 "subject", 626 "body") 627 test_func = self.make_test(["220 localhost ESMTP\r\n", 628 "250-localhost Hello 127.0.0.1\r\n" 629 + "250-SIZE 52428800\r\n" 630 + "250-AUTH PLAIN LOGIN CRAM-MD5\r\n" 631 + "250-PIPELINING\r\n" 632 + "250 HELP\r\n", 633 "334 ZGF4IDNmNDM2NzY0YzBhNjgyMTQ1MzhhZGNiMjE2YTYxZjRm\r\n", 634 "535 Incorrect Authentication data\r\n", 635 "334 asd235r4\r\n", 636 "235 Authentication succeeded\r\n", 637 "250 OK\r\n", 638 "250 Accepted\r\n", 639 "354 Enter message\r\n", 640 None, None, None, None, 641 None, None, None, None, 642 "250 OK\r\n"], 643 ["ehlo \[127.0.0.1\]\r\n", 644 "AUTH CRAM-MD5\r\n", 645 ".*\r\n", 646 "AUTH LOGIN .*\r\n", 647 ".*\r\n", 648 "mail FROM:<" + str(email['From']) + ">.*", 649 "rcpt TO:<" + str(email['To']) + ">\r\n", 650 "data\r\n"] + 651 email.as_string().split("\n") + [".\r\n"], 652 lambda self: \ 653 smtp_account.send_email(email)) 654 test_func() 655 656 469 657 def suite(): 470 658 suite = unittest.TestSuite() src/jmc/model/tests/server.py
r68 r93 2 2 ## dummy_server.py 3 3 ## Login : David Rousselie <david.rousselie@happycoders.org> 4 ## Started on Fri May 13 12:53:17 2005 4 ## Started on Fri May 13 12:53:17 2005 5 5 ## $Id: dummy_server.py,v 1.1 2005/07/11 20:39:31 dax Exp $ 6 ## 7 ## Copyright (C) 2005 6 ## 7 ## Copyright (C) 2005 8 8 ## This program is free software; you can redistribute it and/or modify 9 9 ## it under the terms of the GNU General Public License as published by 10 10 ## the Free Software Foundation; either version 2 of the License, or 11 11 ## (at your option) any later version. 12 ## 12 ## 13 13 ## This program is distributed in the hope that it will be useful, 14 14 ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 15 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 16 ## GNU General Public License for more details. 17 ## 17 ## 18 18 ## You should have received a copy of the GNU General Public License 19 19 ## along with this program; if not, write to the Free Software … … 76 76 self.queries = None 77 77 self.real_queries = [] 78 78 79 79 def serve(self): 80 80 conn = None … … 151 151 except: 152 152 type, value, stack = sys.exc_info() 153 print "".join (traceback.format_exception 153 print "".join (traceback.format_exception 154 154 (type, value, stack, 5)) 155 155 raise … … 176 176 except: 177 177 type, value, stack = sys.exc_info() 178 print "".join (traceback.format_exception 178 print "".join (traceback.format_exception 179 179 (type, value, stack, 5)) 180 180 raise … … 206 206 if __name__ == '__main__': 207 207 test() 208 209 208 209
