From 385204820aa33a3713d991a866afd8520903a96b Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 28 Jan 2015 17:27:52 +0530 Subject: [PATCH 1/4] [fix] [smtp] strip no-width-break and no-width-space characters from email addresses --- frappe/__init__.py | 5 +++-- frappe/utils/__init__.py | 6 +++++- frappe/utils/data.py | 4 ++++ frappe/utils/email_lib/email_body.py | 21 +++++++++++---------- frappe/utils/email_lib/smtp.py | 3 ++- 5 files changed, 25 insertions(+), 14 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index a585a5d241..edebc735ab 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -490,10 +490,11 @@ def setup_module_map(): _cache.set_value("module_app", local.module_app) def get_file_items(path, raise_not_found=False, ignore_empty_lines=True): + import frappe.utils + content = read_file(path, raise_not_found=raise_not_found) if content: - # \ufeff is no-width-break, \u200b is no-width-space - content = content.replace("\ufeff", "").replace("\u200b", "").strip() + content = frappe.utils.strip(content) return [p.strip() for p in content.splitlines() if (not ignore_empty_lines) or (p.strip() and not p.startswith("#"))] else: diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 372d3bd4ba..7987f4614c 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -64,7 +64,11 @@ def extract_email_id(email): def validate_email_add(email_str): """Validates the email string""" email = extract_email_id(email_str) - return re.match("[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", email.lower()) + match = re.match("[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", email.lower()) + if not match: + return False + + return match.group(0)==email.lower() def random_string(length): """generate a random string""" diff --git a/frappe/utils/data.py b/frappe/utils/data.py index 952c6bf900..dd98847551 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -598,3 +598,7 @@ def unique(seq): seen = set() seen_add = seen.add return [ x for x in seq if not (x in seen or seen_add(x)) ] + +def strip(val, chars=None): + # \ufeff is no-width-break, \u200b is no-width-space + return (val or "").replace("\ufeff", "").replace("\u200b", "").strip(chars) diff --git a/frappe/utils/email_lib/email_body.py b/frappe/utils/email_lib/email_body.py index 9951623da0..a2f242b216 100644 --- a/frappe/utils/email_lib/email_body.py +++ b/frappe/utils/email_lib/email_body.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import msgprint, throw, _ -from frappe.utils import scrub_urls, get_url +from frappe.utils import scrub_urls, get_url, strip from frappe.utils.pdf import get_pdf import email.utils from markdown2 import markdown @@ -41,7 +41,7 @@ class EMail: recipients = recipients.split(',') # remove null - recipients = filter(None, (r.strip() for r in recipients)) + recipients = filter(None, (strip(r) for r in recipients)) self.sender = sender self.reply_to = reply_to or sender @@ -175,23 +175,24 @@ class EMail: msgprint(_("Alternatively, you can also specify 'auto_email_id' in site_config.json")) raise frappe.ValidationError, msg - self.sender = _validate(self.sender) - self.reply_to = _validate(self.reply_to) + self.sender = _validate(strip(self.sender)) + self.reply_to = _validate(strip(self.reply_to) or self.sender) + + self.recipients = [strip(r) for r in self.recipients] + self.cc = [strip(r) for r in self.cc] for e in self.recipients + (self.cc or []): - _validate(e.strip()) + _validate(e) def make(self): """build into msg_root""" - self.msg_root['Subject'] = self.subject.encode("utf-8") + self.msg_root['Subject'] = strip(self.subject).encode("utf-8") self.msg_root['From'] = self.sender.encode("utf-8") - self.msg_root['To'] = ', '.join([r.strip() for r in self.recipients]).encode("utf-8") + self.msg_root['To'] = ', '.join(self.recipients).encode("utf-8") self.msg_root['Date'] = email.utils.formatdate() - if not self.reply_to: - self.reply_to = self.sender self.msg_root['Reply-To'] = self.reply_to.encode("utf-8") if self.cc: - self.msg_root['CC'] = ', '.join([r.strip() for r in self.cc]).encode("utf-8") + self.msg_root['CC'] = ', '.join(self.cc).encode("utf-8") # add frappe site header self.msg_root.add_header(b'X-Frappe-Site', get_url().encode('utf-8')) diff --git a/frappe/utils/email_lib/smtp.py b/frappe/utils/email_lib/smtp.py index 6c04e0cb5f..543fd67702 100644 --- a/frappe/utils/email_lib/smtp.py +++ b/frappe/utils/email_lib/smtp.py @@ -23,7 +23,8 @@ def send(email, as_bulk=False): email.reply_to = email.sender email.sender = smtpserver.login - smtpserver.sess.sendmail(email.sender, email.recipients + (email.cc or []), + smtpserver.sess.sendmail(email.sender.encode("utf-8"), + [e.encode("utf-8") for e in (email.recipients + (email.cc or []))], email.as_string()) except smtplib.SMTPSenderRefused: From c8f6fa5aea9f0f7d48759e8679e015e737b435bb Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Tue, 3 Feb 2015 11:58:25 +0530 Subject: [PATCH 2/4] add bench helper --- frappe/utils/bench_helper.py | 40 ++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 frappe/utils/bench_helper.py diff --git a/frappe/utils/bench_helper.py b/frappe/utils/bench_helper.py new file mode 100644 index 0000000000..6c499dcbab --- /dev/null +++ b/frappe/utils/bench_helper.py @@ -0,0 +1,40 @@ +import click +import frappe +import importlib + +def main(): + click.Group(commands=get_app_groups())() + +def get_cli_options(): + pass + +def get_app_groups(): + ret = {} + for app in get_apps(): + app_group = get_app_group(app) + if app_group: + ret[app] = app_group + return ret + +def get_app_group(app): + app_commands = get_app_commands(app) + if app_commands: + return click.Group(name=app, commands=app_commands) + +def get_app_commands(app): + try: + app_command_module = importlib.import_module(app + '.commands') + except ImportError: + return [] + + ret = {} + for command in getattr(app_command_module, 'commands', []): + ret[command.name] = command + return ret + +def get_apps(): + return frappe.get_all_apps(with_internal_apps=False, sites_path='.') + +if __name__ == "__main__": + main() + From af8e98a4301d3208cbe912c87ae74322be4ff16e Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 3 Feb 2015 14:16:59 +0530 Subject: [PATCH 3/4] [fix] set random filename if not found, when extracting image from html --- frappe/utils/file_manager.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/frappe/utils/file_manager.py b/frappe/utils/file_manager.py index 4b296fcb14..3c8b74c8fc 100644 --- a/frappe/utils/file_manager.py +++ b/frappe/utils/file_manager.py @@ -6,7 +6,7 @@ import frappe import os, base64, re import hashlib import mimetypes -from frappe.utils import get_site_path, get_hook_method, get_files_path +from frappe.utils import get_site_path, get_hook_method, get_files_path, random_string from frappe import _ from frappe import conf from copy import copy @@ -89,8 +89,16 @@ def extract_images_from_html(doc, fieldname): def _save_file(match): data = match.group(1) + data = data.split("data:")[1] headers, content = data.split(",") - filename = headers.split("filename=")[-1] + + if "filename=" in headers: + filename = headers.split("filename=")[-1] + else: + mtype = headers.split(";")[0] + extn = mimetypes.guess_extension(mtype) + filename = random_string(7) + extn + # TODO fix this file_url = save_file(filename, content, doc.doctype, doc.name, decode=True).get("file_url") if not frappe.flags.has_dataurl: @@ -99,7 +107,7 @@ def extract_images_from_html(doc, fieldname): return ' Date: Thu, 5 Feb 2015 14:53:53 +0600 Subject: [PATCH 4/4] bumped to version 4.11.0 --- frappe/__version__.py | 2 +- frappe/hooks.py | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/__version__.py b/frappe/__version__.py index cf44e2fd56..bdb8bda6ae 100644 --- a/frappe/__version__.py +++ b/frappe/__version__.py @@ -1 +1 @@ -__version__ = "4.10.2" +__version__ = "4.11.0" diff --git a/frappe/hooks.py b/frappe/hooks.py index 8a285d33d9..8c08c03991 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -3,7 +3,7 @@ app_title = "Frappe Framework" app_publisher = "Web Notes Technologies Pvt. Ltd." app_description = "Full Stack Web Application Framework in Python" app_icon = "assets/frappe/images/frappe.svg" -app_version = "4.10.2" +app_version = "4.11.0" app_color = "#3498db" app_email = "support@frappe.io" diff --git a/setup.py b/setup.py index 077226c996..98ef872fcb 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, find_packages import os -version = "4.10.2" +version = "4.11.0" with open("requirements.txt", "r") as f: install_requires = f.readlines()