def test_sendsimplemail_with_disabled_user(self): c.user = M.User.by_username('test-admin') with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendsimplemail(fromaddr=str(c.user._id), toaddr='*****@*****.**', text=u'This is a test', reply_to=g.noreply, subject=u'Test subject', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') assert_in('From: "Test Admin" <*****@*****.**>', body) c.user.disabled = True ThreadLocalORMSession.flush_all() mail_tasks.sendsimplemail(fromaddr=str(c.user._id), toaddr='*****@*****.**', text=u'This is a test', reply_to=g.noreply, subject=u'Test subject', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 2) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') assert_in('From: %s' % g.noreply, body)
def test_email_sender_to_headers(self): c.user = M.User.by_username('test-admin') with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendsimplemail(fromaddr=str(c.user._id), toaddr='*****@*****.**', text=u'This is a test', reply_to=g.noreply, subject=u'Test subject', sender=u'*****@*****.**', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') assert_in('From: "Test Admin" <*****@*****.**>', body) assert_in('Sender: [email protected]', body) assert_in('To: [email protected]', body) _client.reset_mock() mail_tasks.sendmail(fromaddr=str(c.user._id), destinations=[str(c.user._id)], text=u'This is a test', reply_to=u'*****@*****.**', subject=u'Test subject', sender=u'*****@*****.**', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') assert_in('From: "Test Admin" <*****@*****.**>', body) assert_in('Sender: [email protected]', body) assert_in('To: [email protected]', body)
def test_email_references_header(self): c.user = M.User.by_username('test-admin') with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendsimplemail(fromaddr=str(c.user._id), toaddr='*****@*****.**', text=u'This is a test', reply_to=g.noreply, subject=u'Test subject', references=['a', 'b', 'c'], message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') assert_in('From: "Test Admin" <*****@*****.**>', body) assert_in('References: <a> <b> <c>', body) _client.reset_mock() mail_tasks.sendmail(fromaddr=str(c.user._id), destinations=[str(c.user._id)], text=u'This is a test', reply_to=g.noreply, subject=u'Test subject', references=u'ref', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') assert_in('From: "Test Admin" <*****@*****.**>', body) assert_in('References: <ref>', body)
def test_sendsimplemail_with_disabled_user(self): c.user = M.User.by_username('test-admin') with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendsimplemail( fromaddr=str(c.user._id), toaddr='*****@*****.**', text=u'This is a test', reply_to=g.noreply, subject=u'Test subject', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') assert_in('From: "Test Admin" <*****@*****.**>', body) c.user.disabled = True ThreadLocalORMSession.flush_all() mail_tasks.sendsimplemail( fromaddr=str(c.user._id), toaddr='*****@*****.**', text=u'This is a test', reply_to=g.noreply, subject=u'Test subject', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 2) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') assert_in('From: %s' % g.noreply, body)
def test_email_references_header(self): c.user = M.User.by_username('test-admin') with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendsimplemail( fromaddr=str(c.user._id), toaddr='*****@*****.**', text=u'This is a test', reply_to=g.noreply, subject=u'Test subject', references=['a', 'b', 'c'], message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') assert_in('From: "Test Admin" <*****@*****.**>', body) assert_in('References: <a> <b> <c>', body) _client.reset_mock() mail_tasks.sendmail( fromaddr=str(c.user._id), destinations=[str(c.user._id)], text=u'This is a test', reply_to=g.noreply, subject=u'Test subject', references=u'ref', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') assert_in('From: "Test Admin" <*****@*****.**>', body) assert_in('References: <ref>', body)
def test_email_sender_to_headers(self): c.user = M.User.by_username('test-admin') with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendsimplemail( fromaddr=str(c.user._id), toaddr='*****@*****.**', text=u'This is a test', reply_to=g.noreply, subject=u'Test subject', sender=u'*****@*****.**', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') assert_in('From: "Test Admin" <*****@*****.**>', body) assert_in('Sender: [email protected]', body) assert_in('To: [email protected]', body) _client.reset_mock() mail_tasks.sendmail( fromaddr=str(c.user._id), destinations=[str(c.user._id)], text=u'This is a test', reply_to=u'*****@*****.**', subject=u'Test subject', sender=u'*****@*****.**', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') assert_in('From: "Test Admin" <*****@*****.**>', body) assert_in('Sender: [email protected]', body) assert_in('To: [email protected]', body)
def clone( cloned_from_path, cloned_from_name, cloned_from_url, copy_hooks=False): try: from allura import model as M c.app.repo.init_as_clone( cloned_from_path, cloned_from_name, cloned_from_url, copy_hooks) M.Notification.post_user( c.user, c.app.repo, 'created', text='Repository %s/%s created' % ( c.project.shortname, c.app.config.options.mount_point)) if not c.project.suppress_emails: sendmail( destinations=[str(c.user._id)], fromaddr=u'SourceForge.net <*****@*****.**>', reply_to=u'*****@*****.**', subject=u'SourceForge Repo Clone Complete', message_id=h.gen_message_id(), text=u''.join([ u'Your cloned repository %s in project %s is now ready for use.\n\n', u'Old repository url: %s \n\n', u'New repository checkout command: %s \n\n', u'You and any other developers should do a fresh checkout using the ', u'new repository location.\n' ]) % (c.app.config.options.mount_point, c.project.shortname, cloned_from_url, c.app.repo.clone_command('rw'))) except: sendmail( destinations=['*****@*****.**'], fromaddr=u'SourceForge.net <*****@*****.**>', reply_to=u'*****@*****.**', subject=u'SourceForge Repo Clone Failure', message_id=h.gen_message_id(), text=u''.join([ u'Forking/cloning repo %s in project %s from %s failed.\n', u'\n', u'%s', ]) % (c.app.config.options.mount_point, c.project.shortname, cloned_from_url, traceback.format_exc())) if not c.project.suppress_emails: sendmail( destinations=[str(c.user._id)], fromaddr=u'SourceForge.net <*****@*****.**>', reply_to=u'*****@*****.**', subject=u'SourceForge Repo Clone Failed', message_id=h.gen_message_id(), text=u''.join([ u'Forking/cloning repo %s in project %s from %s failed. ', u'The SourceForge engineering team has been notified.\n', ]) % (c.app.config.options.mount_point, c.project.shortname, cloned_from_url))
def send_digest(self, user_id, from_address, subject, notifications, reply_to_address=None): if not notifications: return user = User.query.get(_id=ObjectId(user_id), disabled=False) if not user: log.debug("Skipping notification - enabled user %s not found " % user_id) return # Filter out notifications for which the user doesn't have read # permissions to the artifact. artifact = self.ref.artifact def perm_check(notification): return not (user and artifact) or \ security.has_access(artifact, 'read', user)() notifications = filter(perm_check, notifications) log.debug('Sending digest of notifications [%s] to user %s', ', '.join([n._id for n in notifications]), user_id) if reply_to_address is None: reply_to_address = from_address text = [ 'Digest of %s' % subject ] for n in notifications: text.append('From: %s' % n.from_address) text.append('Subject: %s' % (n.subject or '(no subject)')) text.append('Message-ID: %s' % n._id) text.append('') text.append(n.text or '-no text-') text.append(n.footer()) text = '\n'.join(text) allura.tasks.mail_tasks.sendmail.post( destinations=[str(user_id)], fromaddr=from_address, reply_to=reply_to_address, subject=subject, message_id=h.gen_message_id(), text=text)
def test_send_email_nonascii(self): with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendmail(fromaddr=u'"По" <*****@*****.**>', destinations=['*****@*****.**'], text=u'Громады стройные теснятся', reply_to=g.noreply, subject=u'По оживлённым берегам', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') assert_equal(rcpts, ['*****@*****.**']) assert_in('Reply-To: %s' % g.noreply, body) # The address portion must not be encoded, only the name portion can be. # Also it is apparently not necessary to have the double-quote separators present # when the name portion is encoded. That is, the encoding below is # just По and not "По" assert_in('From: =?utf-8?b?0J/Qvg==?= <*****@*****.**>', body) assert_in( 'Subject: =?utf-8?b?0J/QviDQvtC20LjQstC70ZHQvdC90YvQvCDQsdC10YDQtdCz0LDQvA==?=', body) assert_in('Content-Type: text/plain; charset="utf-8"', body) assert_in('Content-Transfer-Encoding: base64', body) assert_in(b64encode(u'Громады стройные теснятся'.encode('utf-8')), body)
def post(self, text, message_id=None, parent_id=None, timestamp=None, ignore_security=False, **kw): if not ignore_security: require_access(self, 'post') if self.ref_id and self.artifact: self.artifact.subscribe() if message_id is None: message_id = h.gen_message_id() parent = parent_id and self.post_class().query.get(_id=parent_id) slug, full_slug = self.post_class().make_slugs(parent, timestamp) kwargs = dict(discussion_id=self.discussion_id, full_slug=full_slug, slug=slug, thread_id=self._id, parent_id=parent_id, text=text, status='pending') if timestamp is not None: kwargs['timestamp'] = timestamp if message_id is not None: kwargs['_id'] = message_id post = self.post_class()(**kwargs) if ignore_security or not self.is_spam(post) and has_access( self, 'unmoderated_post')(): log.info('Auto-approving message from %s', c.user.username) file_info = kw.get('file_info', None) post.approve(file_info, notify=kw.get('notify', True)) else: self.notify_moderators(post) return post
def test_send_email_nonascii(self): with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendmail( fromaddr='"По" <*****@*****.**>', destinations=['*****@*****.**'], text='Громады стройные теснятся', reply_to=g.noreply, subject='По оживлённым берегам', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') assert_equal(rcpts, ['*****@*****.**']) assert_in('Reply-To: %s' % g.noreply, body) # The address portion must not be encoded, only the name portion can be. # Also py2 and py3 vary in handling of double-quote separators when the name portion is encoded unquoted_cyrillic_No = '=?utf-8?b?0J/Qvg==?=' # По quoted_cyrillic_No = '=?utf-8?b?ItCf0L4i?=' # "По" assert ('From: {} <*****@*****.**>'.format(quoted_cyrillic_No) in body or 'From: {} <*****@*****.**>'.format(unquoted_cyrillic_No) in body), body assert_in( 'Subject: =?utf-8?b?0J/QviDQvtC20LjQstC70ZHQvdC90YvQvCDQsdC10YDQtdCz0LDQvA==?=', body) assert_in('Content-Type: text/plain; charset="utf-8"', body) assert_in('Content-Transfer-Encoding: base64', body) assert_in(six.ensure_text(b64encode('Громады стройные теснятся'.encode('utf-8'))), body)
def send_system_mail_to_user(user_or_emailaddr, subject, text): ''' Sends a standard email from the Allura system itself, to a user. This is a helper function around sendsimplemail() that generates a new task :param user_or_emailaddr: an email address (str) or a User object :param subject: subject of the email :param text: text of the email (markdown) ''' if isinstance(user_or_emailaddr, basestring): toaddr = user_or_emailaddr else: toaddr = user_or_emailaddr._id email = { 'toaddr': toaddr, 'fromaddr': u'"{}" <{}>'.format( config['site_name'], config['forgemail.return_path'] ), 'sender': unicode(config['forgemail.return_path']), 'reply_to': unicode(config['forgemail.return_path']), 'message_id': h.gen_message_id(), 'subject': subject, 'text': text, } sendsimplemail.post(**email)
def test_send_email_long_lines_use_quoted_printable(self): with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendsimplemail(fromaddr=u'"По" <*****@*****.**>', toaddr='*****@*****.**', text=(u'0123456789' * 100) + u'\n\n' + (u'Громады стро ' * 100), reply_to=g.noreply, subject=u'По оживлённым берегам', message_id=h.gen_message_id()) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') for line in body: assert_less(len(line), 991) # plain text assert_in( '012345678901234567890123456789012345678901234567890123456789012345678901234=', body) assert_in( '=D0=93=D1=80=D0=BE=D0=BC=D0=B0=D0=B4=D1=8B =D1=81=D1=82=D1=80=D0=BE =D0=93=', body) # html assert_in( '<div class=3D"markdown_content"><p>0123456789012345678901234567890123456789=', body) assert_in( '<p>=D0=93=D1=80=D0=BE=D0=BC=D0=B0=D0=B4=D1=8B =D1=81=D1=82=D1=80=D0=BE =D0=', body)
def send_user_mention_notification(self, mentioned_by, artifact): """Send user mention notification to {self} user. """ tmpl = g.jinja2_env.get_template('allura:templates/mail/usermentions_email.md') subject = '[%s:%s] Your name was mentioned' % ( c.project.shortname, c.app.config.options.mount_point) item_url = artifact.url() if artifact.type_s == 'Post': item_url = artifact.url_paginated() tmpl_context = { 'site_domain': config['domain'], 'base_url': config['base_url'], 'user': c.user, 'artifact_link': h.absurl(item_url), 'artifact_linktext': artifact.link_text(), 'mentioned_by': mentioned_by } allura.tasks.mail_tasks.sendsimplemail.post( toaddr=self.get_pref('email_address'), fromaddr=g.noreply, reply_to=g.noreply, message_id=h.gen_message_id(), subject=subject, text=tmpl.render(tmpl_context))
def password_recovery_hash(self, email=None, **kw): provider = plugin.AuthenticationProvider.get(request) if not provider.forgotten_password_process: raise wexc.HTTPNotFound() if not email: redirect('/') user_record = M.User.by_email_address(email) hash = h.nonce(42) user_record.set_tool_data('AuthPasswordReset', hash=hash, hash_expiry=datetime.datetime.utcnow() + datetime.timedelta(seconds=int(config.get('auth.recovery_hash_expiry_period', 600)))) log.info('Sending password recovery link to %s', email) text = ''' To reset your password on %s, please visit the following URL: %s/auth/forgotten_password/%s ''' % (config['site_name'], config['base_url'], hash) allura.tasks.mail_tasks.sendmail.post( destinations=[email], fromaddr=config['forgemail.return_path'], reply_to=config['forgemail.return_path'], subject='Password recovery', message_id=h.gen_message_id(), text=text) flash('Email with instructions has been sent.') redirect('/')
def test_send_email_nonascii(self): with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendmail( fromaddr=u'"По" <*****@*****.**>', destinations=['*****@*****.**'], text=u'Громады стройные теснятся', reply_to=g.noreply, subject=u'По оживлённым берегам', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') assert_equal(rcpts, ['*****@*****.**']) assert_in('Reply-To: %s' % g.noreply, body) # The address portion must not be encoded, only the name portion can be. # Also it is apparently not necessary to have the double-quote separators present # when the name portion is encoded. That is, the encoding below is # just По and not "По" assert_in('From: =?utf-8?b?0J/Qvg==?= <*****@*****.**>', body) assert_in( 'Subject: =?utf-8?b?0J/QviDQvtC20LjQstC70ZHQvdC90YvQvCDQsdC10YDQtdCz0LDQvA==?=', body) assert_in('Content-Type: text/plain; charset="utf-8"', body) assert_in('Content-Transfer-Encoding: base64', body) assert_in( b64encode(u'Громады стройные теснятся'.encode('utf-8')), body)
def post(self, text, message_id=None, parent_id=None, timestamp=None, ignore_security=False, is_meta=False, **kw): if not ignore_security: require_access(self, 'post') if self.ref_id and self.artifact: self.artifact.subscribe() if message_id is None: message_id = h.gen_message_id() parent = parent_id and self.post_class().query.get(_id=parent_id) slug, full_slug = self.post_class().make_slugs(parent, timestamp) kwargs = dict( discussion_id=self.discussion_id, full_slug=full_slug, slug=slug, thread_id=self._id, parent_id=parent_id, text=text, status='pending', is_meta=is_meta) if timestamp is not None: kwargs['timestamp'] = timestamp if message_id is not None: kwargs['_id'] = message_id post = self.post_class()(**kwargs) if ignore_security or not self.is_spam(post) and has_access(self, 'unmoderated_post')(): log.info('Auto-approving message from %s', c.user.username) file_info = kw.get('file_info', None) post.approve(file_info, notify=kw.get('notify', True)) else: self.notify_moderators(post) return post
def process(self, project, tools, user, filename=None, send_email=True, with_attachments=False): export_filename = filename or project.bulk_export_filename() export_path = self.get_export_path(project.bulk_export_path(), export_filename) if not os.path.exists(export_path): os.makedirs(export_path) apps = [project.app_instance(tool) for tool in tools] exportable = self.filter_exportable(apps) results = [ self.export(export_path, app, with_attachments) for app in exportable ] exported = self.filter_successful(results) if exported: zipdir(export_path, os.path.join(os.path.dirname(export_path), export_filename)) shutil.rmtree(export_path) if not user: log.info('No user. Skipping notification.') return if not send_email: return tmpl = g.jinja2_env.get_template( 'allura:templates/mail/bulk_export.html') instructions = tg.config.get('bulk_export_download_instructions', '') instructions = instructions.format( project=project.shortname, filename=export_filename, c=c, ) exported_names = [a.config.options.mount_point for a in exported] tmpl_context = { 'instructions': instructions, 'project': project, 'tools': exported_names, 'not_exported_tools': list(set(tools) - set(exported_names)), } email = { 'toaddr': unicode(user._id), 'fromaddr': unicode(tg.config['forgemail.return_path']), 'sender': unicode(tg.config['forgemail.return_path']), 'reply_to': unicode(tg.config['forgemail.return_path']), 'message_id': h.gen_message_id(), 'subject': u'Bulk export for project %s completed' % project.shortname, 'text': tmpl.render(tmpl_context) } mail_tasks.sendsimplemail.post(**email)
def password_recovery_hash(self, email=None, **kw): provider = plugin.AuthenticationProvider.get(request) if not provider.forgotten_password_process: raise wexc.HTTPNotFound() if not email: redirect('/') user_record = M.User.by_email_address(email) allow_non_primary_email_reset = asbool( config.get('auth.allow_non_primary_email_password_reset', True)) if not re.match(r"[^@]+@[^@]+\.[^@]+", email): flash('Enter email in correct format!', 'error') redirect('/auth/forgotten_password') if not allow_non_primary_email_reset: message = 'If the given email address is on record, '\ 'a password reset email has been sent to the account\'s primary email address.' email_record = M.EmailAddress.get( email=provider.get_primary_email_address( user_record=user_record), confirmed=True) else: message = 'A password reset email has been sent, if the given email address is on record in our system.' email_record = M.EmailAddress.get(email=email, confirmed=True) if user_record and email_record and email_record.confirmed: hash = h.nonce(42) user_record.set_tool_data( 'AuthPasswordReset', hash=hash, hash_expiry=datetime.datetime.utcnow() + datetime.timedelta(seconds=int( config.get('auth.recovery_hash_expiry_period', 600)))) log.info('Sending password recovery link to %s', email_record.email) subject = '%s Password recovery' % config['site_name'] text = g.jinja2_env.get_template( 'allura:templates/mail/forgot_password.txt').render( dict( user=user_record, config=config, hash=hash, )) allura.tasks.mail_tasks.sendsimplemail.post( toaddr=email_record.email, fromaddr=config['forgemail.return_path'], reply_to=config['forgemail.return_path'], subject=subject, message_id=h.gen_message_id(), text=text) h.auditlog_user('Password recovery link sent to: %s', email, user=user_record) flash(message) redirect('/')
def post(self, text, message_id=None, parent_id=None, notify=True, notification_text=None, timestamp=None, ignore_security=False, is_meta=False, subscribe=False, **kw): if not ignore_security: require_access(self, 'post') if subscribe: self.primary().subscribe() if message_id is None: message_id = h.gen_message_id() parent = parent_id and self.post_class().query.get(_id=parent_id) slug, full_slug = self.post_class().make_slugs(parent, timestamp) kwargs = dict(discussion_id=self.discussion_id, full_slug=full_slug, slug=slug, thread_id=self._id, parent_id=parent_id, text=text, status='pending', is_meta=is_meta) if timestamp is not None: kwargs['timestamp'] = timestamp if message_id is not None: kwargs['_id'] = message_id post = self.post_class()(**kwargs) if ignore_security or is_meta: spammy = False else: spammy = self.is_spam(post) # unmoderated post -> autoapprove # unmoderated post but is spammy -> don't approve it, it goes into moderation # moderated post -> moderation # moderated post but is spammy -> mark as spam if ignore_security or (not spammy and has_access(self, 'unmoderated_post')): log.info('Auto-approving message from %s', c.user.username) file_info = kw.get('file_info', None) post.approve(file_info, notify=notify, notification_text=notification_text) elif not has_access(self, 'unmoderated_post') and spammy: post.spam( submit_spam_feedback=False ) # no feedback since we're marking as spam automatically not manually else: self.notify_moderators(post) return post
def reply(self): new_id = h.gen_message_id() slug, full_slug = self.make_slugs(self) new_args = dict(state(self).document, _id=new_id, slug=slug, full_slug=full_slug, parent_id=self._id, timestamp=datetime.utcnow(), author_id=c.user._id) return self.__class__(**new_args)
def _post_email(self, mailfrom, rcpttos, subject, msg): '''msg is MIME message object''' msg['Message-ID'] = '<' + h.gen_message_id() + '>' msg['From'] = mailfrom msg['To'] = ', '.join(rcpttos) msg['Subject'] = subject mail_tasks.route_email(peer='127.0.0.1', mailfrom=mailfrom, rcpttos=rcpttos, data=msg.as_string()) M.artifact_orm_session.flush()
def _post_email(self, mailfrom, rcpttos, subject, msg): '''msg is MIME message object''' msg['Message-ID'] = '<' + h.gen_message_id() + '>' msg['From'] = mailfrom msg['To'] = ', '.join(rcpttos) msg['Subject'] = subject mail_tasks.route_email( peer='127.0.0.1', mailfrom=mailfrom, rcpttos=rcpttos, data=msg.as_string()) M.artifact_orm_session.flush()
def reply(self): new_id = h.gen_message_id() slug, full_slug = self.make_slugs(self) new_args = dict( state(self).document, _id=new_id, slug=slug, full_slug=full_slug, parent_id=self._id, timestamp=datetime.utcnow(), author_id=c.user._id) return self.__class__(**new_args)
def test_fromaddr_objectid_not_str(self): c.user = M.User.by_username('test-admin') with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendsimplemail(fromaddr=c.user._id, toaddr='*****@*****.**', text=u'This is a test', reply_to=g.noreply, subject=u'Test subject', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] assert_in('From: "Test Admin" <*****@*****.**>', body)
def password_recovery_hash(self, email=None, **kw): provider = plugin.AuthenticationProvider.get(request) if not provider.forgotten_password_process: raise wexc.HTTPNotFound() if not email: redirect("/") user_record = M.User.by_email_address(email) allow_non_primary_email_reset = asbool(config.get("auth.allow_non_primary_email_password_reset", True)) if not re.match(r"[^@]+@[^@]+\.[^@]+", email): flash("Enter email in correct format!", "error") redirect("/auth/forgotten_password") if not allow_non_primary_email_reset: message = ( "If the given email address is on record, " "a password reset email has been sent to the account's primary email address." ) email_record = M.EmailAddress.get( email=provider.get_primary_email_address(user_record=user_record), confirmed=True ) else: message = "A password reset email has been sent, if the given email address is on record in our system." email_record = M.EmailAddress.get(email=email, confirmed=True) if user_record and email_record and email_record.confirmed: hash = h.nonce(42) user_record.set_tool_data( "AuthPasswordReset", hash=hash, hash_expiry=datetime.datetime.utcnow() + datetime.timedelta(seconds=int(config.get("auth.recovery_hash_expiry_period", 600))), ) log.info("Sending password recovery link to %s", email_record.email) subject = "%s Password recovery" % config["site_name"] text = g.jinja2_env.get_template("allura:templates/mail/forgot_password.txt").render( dict(user=user_record, config=config, hash=hash) ) allura.tasks.mail_tasks.sendsimplemail.post( toaddr=email_record.email, fromaddr=config["forgemail.return_path"], reply_to=config["forgemail.return_path"], subject=subject, message_id=h.gen_message_id(), text=text, ) h.auditlog_user("Password recovery link sent to: %s", email, user=user_record) flash(message) redirect("/")
def test_fromaddr_objectid_not_str(self): c.user = M.User.by_username('test-admin') with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendsimplemail( fromaddr=c.user._id, toaddr='*****@*****.**', text=u'This is a test', reply_to=g.noreply, subject=u'Test subject', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] assert_in('From: "Test Admin" <*****@*****.**>', body)
def test_send_email_with_disabled_destination_user(self): c.user = M.User.by_username('test-admin') destination_user = M.User.by_username('test-user-1') destination_user.preferences['email_address'] = '*****@*****.**' destination_user.disabled = True ThreadLocalORMSession.flush_all() with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendmail(fromaddr=str(c.user._id), destinations=[str(destination_user._id)], text=u'This is a test', reply_to=g.noreply, subject=u'Test subject', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 0)
def test_cc(self): c.user = M.User.by_username('test-admin') with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendsimplemail(fromaddr=str(c.user._id), toaddr='*****@*****.**', text=u'This is a test', reply_to=g.noreply, subject=u'Test subject', cc=u'*****@*****.**', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] assert_in('CC: [email protected]', body) assert_in('*****@*****.**', rcpts)
def test_send_email_with_disabled_destination_user(self): c.user = M.User.by_username('test-admin') destination_user = M.User.by_username('test-user-1') destination_user.preferences['email_address'] = '*****@*****.**' destination_user.disabled = True ThreadLocalORMSession.flush_all() with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendmail( fromaddr=str(c.user._id), destinations=[str(destination_user._id)], text=u'This is a test', reply_to=g.noreply, subject=u'Test subject', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 0)
def test_cc(self): c.user = M.User.by_username('test-admin') with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendsimplemail( fromaddr=str(c.user._id), toaddr='*****@*****.**', text=u'This is a test', reply_to=g.noreply, subject=u'Test subject', cc=u'*****@*****.**', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] assert_in('CC: [email protected]', body) assert_in('*****@*****.**', rcpts)
def password_recovery_hash(self, email=None, **kw): provider = plugin.AuthenticationProvider.get(request) if not provider.forgotten_password_process: raise wexc.HTTPNotFound() if not email: redirect('/') user_record = M.User.by_email_address(email) allow_non_primary_email_reset = asbool(config.get('auth.allow_non_primary_email_password_reset', True)) if not re.match(r"[^@]+@[^@]+\.[^@]+", email): flash('Enter email in correct format!','error') redirect('/auth/forgotten_password') if not allow_non_primary_email_reset: message = 'If the given email address is on record, '\ 'a password reset email has been sent to the account\'s primary email address.' email_record = M.EmailAddress.get(email=provider.get_primary_email_address(user_record=user_record), confirmed=True) else: message = 'A password reset email has been sent, if the given email address is on record in our system.' email_record = M.EmailAddress.get(email=email, confirmed=True) if user_record and email_record and email_record.confirmed: hash = h.nonce(42) user_record.set_tool_data('AuthPasswordReset', hash=hash, hash_expiry=datetime.datetime.utcnow() + datetime.timedelta(seconds=int(config.get('auth.recovery_hash_expiry_period', 600)))) log.info('Sending password recovery link to %s', email_record.email) subject = '%s Password recovery' % config['site_name'] text = g.jinja2_env.get_template('allura:templates/mail/forgot_password.txt').render(dict( user=user_record, config=config, hash=hash, )) allura.tasks.mail_tasks.sendsimplemail.post( toaddr=email_record.email, fromaddr=config['forgemail.return_path'], reply_to=config['forgemail.return_path'], subject=subject, message_id=h.gen_message_id(), text=text) h.auditlog_user('Password recovery link sent to: %s', email, user=user_record) flash(message) redirect('/')
def process(self, project, tools, user, filename=None, send_email=True, with_attachments=False): export_filename = filename or project.bulk_export_filename() export_path = self.get_export_path( project.bulk_export_path(), export_filename) if not os.path.exists(export_path): os.makedirs(export_path) apps = [project.app_instance(tool) for tool in tools] exportable = self.filter_exportable(apps) results = [self.export(export_path, app, with_attachments) for app in exportable] exported = self.filter_successful(results) if exported: zipdir(export_path, os.path.join(os.path.dirname(export_path), export_filename)) shutil.rmtree(export_path) if not user: log.info('No user. Skipping notification.') return if not send_email: return tmpl = g.jinja2_env.get_template( 'allura:templates/mail/bulk_export.html') instructions = tg.config.get('bulk_export_download_instructions', '') instructions = instructions.format( project=project.shortname, filename=export_filename, c=c, ) exported_names = [a.config.options.mount_point for a in exported] tmpl_context = { 'instructions': instructions, 'project': project, 'tools': exported_names, 'not_exported_tools': list(set(tools) - set(exported_names)), } email = { 'toaddr': unicode(user._id), 'fromaddr': unicode(tg.config['forgemail.return_path']), 'sender': unicode(tg.config['forgemail.return_path']), 'reply_to': unicode(tg.config['forgemail.return_path']), 'message_id': h.gen_message_id(), 'subject': u'Bulk export for project %s completed' % project.shortname, 'text': tmpl.render(tmpl_context) } mail_tasks.sendsimplemail.post(**email)
def parse_message(data): # Parse the email to its constituent parts # https://bugs.python.org/issue25545 says # > A unicode string has no RFC defintion as an email, so things do not work right... # > You do have to conditionalize your 2/3 code to use the bytes parser and generator if you are dealing with 8-bit # > messages. There's just no way around that. if six.PY2: parser = email.feedparser.FeedParser() parser.feed(data) msg = parser.close() else: # works the same as BytesFeedParser, and better than non-"Bytes" parsers for some messages parser = email.parser.BytesParser() msg = parser.parsebytes(data.encode('utf-8')) # Extract relevant data result = {} result['multipart'] = multipart = msg.is_multipart() result['headers'] = dict(msg) result['message_id'] = _parse_message_id(msg.get('Message-ID')) result['in_reply_to'] = _parse_message_id(msg.get('In-Reply-To')) result['references'] = _parse_message_id(msg.get('References')) if result['message_id'] == []: result['message_id'] = h.gen_message_id() else: result['message_id'] = result['message_id'][0] if multipart: result['parts'] = [] for part in msg.walk(): dpart = dict(headers=dict(part), message_id=result['message_id'], in_reply_to=result['in_reply_to'], references=result['references'], content_type=part.get_content_type(), filename=part.get_filename(None), payload=part.get_payload(decode=True)) # payload is sometimes already unicode (due to being saved in mongo?) if part.get_content_maintype() == 'text': dpart['payload'] = six.ensure_text(dpart['payload']) result['parts'].append(dpart) else: result['payload'] = msg.get_payload(decode=True) # payload is sometimes already unicode (due to being saved in mongo?) if msg.get_content_maintype() == 'text': result['payload'] = six.ensure_text(result['payload']) return result
def send_verification_link(self): self.nonce = sha256(os.urandom(10)).hexdigest() log.info('Sending verification link to %s', self._id) text = ''' To verify the email address %s belongs to the user %s, please visit the following URL: %s ''' % (self._id, self.claimed_by_user().username, g.url('/auth/verify_addr', a=self.nonce)) log.info('Verification email:\n%s', text) allura.tasks.mail_tasks.sendmail.post( destinations=[self._id], fromaddr=self._id, reply_to='', subject='Email address verification', message_id=h.gen_message_id(), text=text)
def send_summary(self, user_id, from_address, subject, notifications): if not notifications: return text = ['Digest of %s' % subject] for n in notifications: text.append('From: %s' % n.from_address) text.append('Subject: %s' % (n.subject or '(no subject)')) text.append('Message-ID: %s' % n._id) text.append('') text.append(h.text.truncate(n.text or '-no text-', 128)) text.append(n.footer()) text = '\n'.join(text) allura.tasks.mail_tasks.sendmail.post(destinations=[str(user_id)], fromaddr=from_address, reply_to=from_address, subject=subject, message_id=h.gen_message_id(), text=text)
def test_send_email_with_disabled_user(self): c.user = M.User.by_username('test-admin') c.user.disabled = True destination_user = M.User.by_username('test-user-1') destination_user.preferences['email_address'] = '*****@*****.**' ThreadLocalORMSession.flush_all() with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendmail(fromaddr=str(c.user._id), destinations=[str(destination_user._id)], text=u'This is a test', reply_to=u'*****@*****.**', subject=u'Test subject', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') assert_in('From: [email protected]', body)
def test_send_email_with_disabled_user(self): c.user = M.User.by_username('test-admin') c.user.disabled = True destination_user = M.User.by_username('test-user-1') destination_user.preferences['email_address'] = '*****@*****.**' ThreadLocalORMSession.flush_all() with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendmail( fromaddr=str(c.user._id), destinations=[ str(destination_user._id) ], text=u'This is a test', reply_to=u'*****@*****.**', subject=u'Test subject', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') assert_in('From: [email protected]', body)
def send_claim_attempt(self): confirmed_email = self.find(dict(email=self.email, confirmed=True)).all() if confirmed_email: log.info('Sending claim attempt email to %s', self.email) text = g.jinja2_env.get_template('allura:templates/mail/claimed_existing_email.txt').render(dict( email=self, user=confirmed_email[0].claimed_by_user(), config=config )) allura.tasks.mail_tasks.sendsimplemail.post( toaddr=self.email, fromaddr=config['forgemail.return_path'], reply_to=config['forgemail.return_path'], subject=u'%s - Email address claim attempt' % config['site_name'], message_id=h.gen_message_id(), text=text)
def send_summary(self, user_id, from_address, subject, notifications): if not notifications: return text = [ 'Digest of %s' % subject ] for n in notifications: text.append('From: %s' % n.from_address) text.append('Subject: %s' % (n.subject or '(no subject)')) text.append('Message-ID: %s' % n._id) text.append('') text.append(h.text.truncate(n.text or '-no text-', 128)) text.append(n.footer()) text = '\n'.join(text) allura.tasks.mail_tasks.sendmail.post( destinations=[str(user_id)], fromaddr=from_address, reply_to=from_address, subject=subject, message_id=h.gen_message_id(), text=text)
def send_verification_link(self): self.set_nonce_hash() log.info('Sending verification link to %s', self.email) text = ''' To verify the email address %s belongs to the user %s, please visit the following URL: %s ''' % (self.email, self.claimed_by_user(include_pending=True).username, g.url('/auth/verify_addr', a=self.nonce)) log.info('Verification email:\n%s', text) allura.tasks.mail_tasks.sendsimplemail.post( fromaddr=g.noreply, reply_to=g.noreply, toaddr=self.email, subject=u'%s - Email address verification' % config['site_name'], message_id=h.gen_message_id(), text=text)
def send_user_message(self, user, subject, message, cc): """Send a user message (email) to ``user``. """ tmpl = g.jinja2_env.get_template('allura:ext/user_profile/templates/message.html') tmpl_context = { 'message_text': message, 'site_name': config['site_name'], 'base_url': config['base_url'], 'username': c.user.username, } allura.tasks.mail_tasks.sendsimplemail.post( toaddr=user.get_pref('email_address'), fromaddr=self.get_pref('email_address'), reply_to=self.get_pref('email_address'), message_id=h.gen_message_id(), subject=subject, text=tmpl.render(tmpl_context), cc=cc) self.sent_user_message_times.append(datetime.utcnow())
def post(self, text, message_id=None, parent_id=None, notify=True, notification_text=None, timestamp=None, ignore_security=False, is_meta=False, **kw): if not ignore_security: require_access(self, 'post') if self.ref_id and self.artifact: self.artifact.subscribe() if message_id is None: message_id = h.gen_message_id() parent = parent_id and self.post_class().query.get(_id=parent_id) slug, full_slug = self.post_class().make_slugs(parent, timestamp) kwargs = dict( discussion_id=self.discussion_id, full_slug=full_slug, slug=slug, thread_id=self._id, parent_id=parent_id, text=text, status='pending', is_meta=is_meta) if timestamp is not None: kwargs['timestamp'] = timestamp if message_id is not None: kwargs['_id'] = message_id post = self.post_class()(**kwargs) # unmoderated post -> autoapprove # unmoderated post but is spammy -> don't approve it, it goes into moderation # moderated post -> moderation # moderated post but is spammy -> mark as spam spammy = self.is_spam(post) if ignore_security or (not spammy and has_access(self, 'unmoderated_post')): log.info('Auto-approving message from %s', c.user.username) file_info = kw.get('file_info', None) post.approve(file_info, notify=notify, notification_text=notification_text) elif not has_access(self, 'unmoderated_post') and spammy: post.spam(submit_spam_feedback=False) # no feedback since we're marking as spam automatically not manually else: self.notify_moderators(post) return post
def test_send_email_long_lines_use_quoted_printable(self): with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendsimplemail( fromaddr=u'"По" <*****@*****.**>', toaddr='*****@*****.**', text=(u'0123456789' * 100) + u'\n\n' + (u'Громады стро ' * 100), reply_to=g.noreply, subject=u'По оживлённым берегам', message_id=h.gen_message_id()) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') for line in body: assert_less(len(line), 991) # plain text assert_in('012345678901234567890123456789012345678901234567890123456789012345678901234=', body) assert_in('=D0=93=D1=80=D0=BE=D0=BC=D0=B0=D0=B4=D1=8B =D1=81=D1=82=D1=80=D0=BE =D0=93=', body) # html assert_in('<div class=3D"markdown_content"><p>0123456789012345678901234567890123456789=', body) assert_in('<p>=D0=93=D1=80=D0=BE=D0=BC=D0=B0=D0=B4=D1=8B =D1=81=D1=82=D1=80=D0=BE =D0=', body)
def send_user_message(self, user, subject, message, cc): """Send a user message (email) to ``user``. """ tmpl = g.jinja2_env.get_template( 'allura:ext/user_profile/templates/message.html') tmpl_context = { 'message_text': message, 'site_name': config['site_name'], 'base_url': config['base_url'], 'user': c.user, } allura.tasks.mail_tasks.sendsimplemail.post( toaddr=user.get_pref('email_address'), fromaddr=self.get_pref('email_address'), reply_to=self.get_pref('email_address'), message_id=h.gen_message_id(), subject=subject, text=tmpl.render(tmpl_context), cc=cc) self.sent_user_message_times.append(datetime.utcnow())
def parse_message(data): # Parse the email to its constituent parts parser = email.feedparser.FeedParser() parser.feed(data) msg = parser.close() # Extract relevant data result = {} result['multipart'] = multipart = msg.is_multipart() result['headers'] = dict(msg) result['message_id'] = _parse_message_id(msg.get('Message-ID')) result['in_reply_to'] = _parse_message_id(msg.get('In-Reply-To')) result['references'] = _parse_message_id(msg.get('References')) if result['message_id'] == []: result['message_id'] = h.gen_message_id() else: result['message_id'] = result['message_id'][0] if multipart: result['parts'] = [] for part in msg.walk(): dpart = dict( headers=dict(part), message_id=result['message_id'], in_reply_to=result['in_reply_to'], references=result['references'], content_type=part.get_content_type(), filename=part.get_filename(None), payload=part.get_payload(decode=True)) charset = part.get_content_charset() # payload is sometimes already unicode (due to being saved in mongo?) if isinstance(dpart['payload'], six.binary_type) and charset: dpart['payload'] = dpart['payload'].decode(charset) result['parts'].append(dpart) else: result['payload'] = msg.get_payload(decode=True) charset = msg.get_content_charset() # payload is sometimes already unicode (due to being saved in mongo?) if isinstance(result['payload'], six.binary_type) and charset: result['payload'] = result['payload'].decode(charset) return result
def test_send_email_ascii_with_user_lookup(self): c.user = M.User.by_username('test-admin') with mock.patch.object(mail_tasks.smtp_client, '_client') as _client: mail_tasks.sendmail( fromaddr=str(c.user._id), destinations=[ str(c.user._id) ], text=u'This is a test', reply_to=u'*****@*****.**', subject=u'Test subject', message_id=h.gen_message_id()) assert_equal(_client.sendmail.call_count, 1) return_path, rcpts, body = _client.sendmail.call_args[0] body = body.split('\n') assert_equal(rcpts, [c.user.get_pref('email_address')]) assert_in('Reply-To: [email protected]', body) assert_in('From: "Test Admin" <*****@*****.**>', body) assert_in('Subject: Test subject', body) # plain assert_in('This is a test', body) # html assert_in('<div class="markdown_content"><p>This is a test</p></div>', body)
def parse_message(data): # Parse the email to its constituent parts parser = email.feedparser.FeedParser() parser.feed(data) msg = parser.close() # Extract relevant data result = {} result["multipart"] = multipart = msg.is_multipart() result["headers"] = dict(msg) result["message_id"] = _parse_message_id(msg.get("Message-ID")) result["in_reply_to"] = _parse_message_id(msg.get("In-Reply-To")) result["references"] = _parse_message_id(msg.get("References")) if result["message_id"] == []: result["message_id"] = h.gen_message_id() else: result["message_id"] = result["message_id"][0] if multipart: result["parts"] = [] for part in msg.walk(): dpart = dict( headers=dict(part), message_id=result["message_id"], in_reply_to=result["in_reply_to"], references=result["references"], content_type=part.get_content_type(), filename=part.get_filename(None), payload=part.get_payload(decode=True), ) charset = part.get_content_charset() if charset: dpart["payload"] = dpart["payload"].decode(charset) result["parts"].append(dpart) else: result["payload"] = msg.get_payload(decode=True) charset = msg.get_content_charset() if charset: result["payload"] = result["payload"].decode(charset) return result
def send_summary(self, user_id, from_address, subject, notifications): if not notifications: return log.debug( "Sending summary of notifications [%s] to user %s", ", ".join([n._id for n in notifications]), user_id ) text = ["Digest of %s" % subject] for n in notifications: text.append("From: %s" % n.from_address) text.append("Subject: %s" % (n.subject or "(no subject)")) text.append("Message-ID: %s" % n._id) text.append("") text.append(h.text.truncate(n.text or "-no text-", 128)) text.append(n.footer()) text = "\n".join(text) allura.tasks.mail_tasks.sendmail.post( destinations=[str(user_id)], fromaddr=from_address, reply_to=from_address, subject=subject, message_id=h.gen_message_id(), text=text, )
def test_gen_messageid_with_id_set(): oid = ObjectId() assert re.match(r'*****@*****.**' % str(oid), h.gen_message_id(oid))
def test_gen_messageid(): assert re.match(r'[0-9a-zA-Z]*[email protected]', h.gen_message_id())