[xwiki-devs] Mail Auth patch for 1.8
Hello, Here's the newest version. Basically it bridges XWiki.sendMessage to MailSenderPlugin. It uses Reflection, so no circulars, but it's not exactly pretty. Why it's needed - registration / validation / activation e-mails are still sent via obsolete Apache Commons SmtpClient, so they _do not_ support SMTP AUTH. This is a _major_ problem because due to spam and other abuse less and less hosting providers / network admins allow SMTP without authorization, and those who do are likely to have very insecure infrastructure. I honestly believe this is a MUST HAVE for a secure, spam-free configuration. Greetings, Lilianne Index: main/resources/XWiki/AdminGeneralSheet.xml =================================================================== --- main/resources/XWiki/AdminGeneralSheet.xml (revision 18064) +++ main/resources/XWiki/AdminGeneralSheet.xml (working copy) @@ -61,6 +61,6 @@ #set($params.language = ['multilingual', 'languages' , 'default_language', 'dateformat']) #set($params.editor = ['editor']) #set($params.admin = ['admin_email']) -#set($params.server = ['smtp_server']) +#set($params.server = ['smtp_server', 'smtp_server_username', 'smtp_server_password', 'javamail_extra_props']) #includeForm('XWiki.AdminFieldsDisplaySheet')</content> </xwikidoc> Index: main/java/com/xpn/xwiki/XWiki.java =================================================================== --- main/java/com/xpn/xwiki/XWiki.java (revision 17953) +++ main/java/com/xpn/xwiki/XWiki.java (working copy) @@ -161,6 +161,8 @@ import com.xpn.xwiki.web.XWikiURLFactoryService; import com.xpn.xwiki.web.XWikiURLFactoryServiceImpl; import com.xpn.xwiki.web.includeservletasstring.IncludeServletAsString; +import java.io.BufferedReader; +import java.io.StringReader; public class XWiki implements XWikiDocChangeNotificationInterface { @@ -2838,9 +2840,13 @@ needsUpdate |= bclass.addTextAreaField("meta", "HTTP Meta Info", 60, 8); needsUpdate |= bclass.addTextField("dateformat", "Date Format", 30); + // mail needsUpdate |= bclass.addBooleanField("use_email_verification", "Use eMail Verification", "yesno"); + needsUpdate |= bclass.addTextField("admin_email", "Admin eMail", 30); needsUpdate |= bclass.addTextField("smtp_server", "SMTP Server", 30); - needsUpdate |= bclass.addTextField("admin_email", "Admin eMail", 30); + needsUpdate |= bclass.addTextField("smtp_server_username", "SMTP Server username (optional)", 30); + needsUpdate |= bclass.addTextField("smtp_server_password", "SMTP Server password (optional)", 30); + needsUpdate |= bclass.addTextAreaField("javamail_extra_props", "Additional JavaMail properties", 60, 6); needsUpdate |= bclass.addTextAreaField("validation_email_content", "Validation eMail Content", 72, 10); needsUpdate |= bclass.addTextAreaField("confirmation_email_content", "Confirmation eMail Content", 72, 10); needsUpdate |= bclass.addTextAreaField("invitation_email_content", "Invitation eMail Content", 72, 10); @@ -3311,9 +3317,139 @@ * Plugin</a> */ @Deprecated - public void sendMessage(String sender, String[] recipient, String message, XWikiContext context) + public void sendMessage(String sender, String[] recipients, String message, XWikiContext context) throws XWikiException { + LOG.info("Entering sendMessage(...)..."); + + Object mailSender; + Class mailSenderClass; + Method mailSenderSendText; + + try + { + mailSender = getPluginApi("mailsender", context); + mailSenderClass = Class.forName("com.xpn.xwiki.plugin.mailsender.MailSenderPluginApi"); + + // public int sendTextMessage(String from, String to, String subject, String message) + mailSenderSendText = mailSenderClass.getMethod("sendTextMessage", + new Class[]{String.class, String.class, String.class, String.class}); + } + catch(Exception e) + { + String eMsg = "Problem getting MailSender via Reflection: " + e; + LOG.error(eMsg); + throw new XWikiException(XWikiException.MODULE_XWIKI_EMAIL, + XWikiException.ERROR_XWIKI_EMAIL_ERROR_SENDING_EMAIL, eMsg); + } + + LOG.trace("Message = \"" + message + "\""); + + String messageParsed[] = parseRawMessage(message); + String messageSubject = messageParsed[0]; + String messageBody = messageParsed[1]; + String messageRecipients = recipients[0]; + for( int i = 1; i < recipients.length; i++) + { + messageRecipients = messageRecipients + "," + recipients[i]; + } + + if( messageSubject == null ) + { + // TODO: provide some sensible default + messageSubject = "Message from XWiki"; + } + + LOG.trace("Subject = \"" + messageParsed[0] + "\""); + LOG.trace("Text = \"" + messageParsed[1] + "\""); + + try + { + mailSenderSendText.invoke(mailSender, sender, messageRecipients, messageSubject, messageBody); + } + catch(InvocationTargetException ite) + { + Throwable cause = ite.getCause(); + if( cause instanceof XWikiException ) + { + throw (XWikiException)cause; + } + else + { + throw new RuntimeException(cause); + } + } + catch(Exception e) + { + // probably either IllegalAccessException or IllegalArgumentException + // shouldn't happen unless there were an incompatible code change + throw new RuntimeException(e); + } + + LOG.info("Exiting sendMessage(...). It seems everything went ok."); + } + + /** + * + * @return [subject (can be null), text (never null)] + */ + public String[] parseRawMessage(String rawMessage) + { + String SUBJECT = "Subject: "; + + String messageSubject = null; + String messageText = ""; + + // sanity check + if( rawMessage == null ) + { + throw new IllegalArgumentException("rawMessage can't be null"); + } + else if( rawMessage.trim().equals("") ) + { + throw new IllegalArgumentException("rawMessage can't be empty"); + } + + try + { + StringReader sr = new StringReader(rawMessage); + BufferedReader br = new BufferedReader(sr); + String line; + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + line = br.readLine(); + if( line.startsWith(SUBJECT) ) + { + messageSubject = line.substring(SUBJECT.length()); + line = br.readLine(); + if( !line.trim().equals("") ) + { + pw.println(line); + } + } + + while( (line = br.readLine()) != null ) + { + pw.println(line); + } + + messageText = sw.toString(); + } + catch(IOException ioe) + { + // can't happen here + } + + return new String[]{messageSubject, messageText}; + } + + + @Deprecated + public void sendMessageOld(String sender, String[] recipient, String message, XWikiContext context) + throws XWikiException + { SMTPClient smtpc = null; try { String server = getXWikiPreference("smtp_server", context); Index: main/resources/ApplicationResources.properties =================================================================== --- main/resources/ApplicationResources.properties (revision 17953) +++ main/resources/ApplicationResources.properties (working copy) @@ -181,6 +181,9 @@ use_email_verification=Use email verification admin_email=Admin email smtp_server=Outgoing SMTP Server +smtp_server_username=SMTP Server Username (optional) +smtp_server_password=SMTP Server Password (optional) +javamail_extra_props=Additional JavaMail properties validation_email_content=Validation e-Mail Content confirmation_email_content=Confirmation e-Mail Content preferences=Preferences
Lilianne E. Blaze wrote:
Hello, Here's the newest version.
Basically it bridges XWiki.sendMessage to MailSenderPlugin.
It uses Reflection, so no circulars, but it's not exactly pretty.
Why it's needed - registration / validation / activation e-mails are still sent via obsolete Apache Commons SmtpClient, so they _do not_ support SMTP AUTH.
This is a _major_ problem because due to spam and other abuse less and less hosting providers / network admins allow SMTP without authorization, and those who do are likely to have very insecure infrastructure.
I honestly believe this is a MUST HAVE for a secure, spam-free configuration.
Greetings, Lilianne
Hi Lilianne, Thanks for this patch. I (and other developers) don't quite agree with its approach, but I guess we could include it for the moment, until we implement the mail component. It's a hack, but one not as bad as other hacks we have, and it does solve a problem. -- Sergiu Dumitriu http://purl.org/net/sergiu/
Sergiu Dumitriu wrote:
Lilianne E. Blaze wrote:
Hello, Here's the newest version.
Basically it bridges XWiki.sendMessage to MailSenderPlugin.
It uses Reflection, so no circulars, but it's not exactly pretty.
Why it's needed - registration / validation / activation e-mails are still sent via obsolete Apache Commons SmtpClient, so they _do not_ support SMTP AUTH.
This is a _major_ problem because due to spam and other abuse less and less hosting providers / network admins allow SMTP without authorization, and those who do are likely to have very insecure infrastructure.
I honestly believe this is a MUST HAVE for a secure, spam-free configuration.
Greetings, Lilianne
Hi Lilianne,
Thanks for this patch. I (and other developers) don't quite agree with its approach, but I guess we could include it for the moment, until we implement the mail component. It's a hack, but one not as bad as other hacks we have, and it does solve a problem.
I agree. Since none of us has time right now to transform the mailsender in a component, +1 to apply it to fix this until then. Jerome.
On Apr 2, 2009, at 1:30 PM, Jerome Velociter wrote:
Sergiu Dumitriu wrote:
Lilianne E. Blaze wrote:
Hello, Here's the newest version.
Basically it bridges XWiki.sendMessage to MailSenderPlugin.
It uses Reflection, so no circulars, but it's not exactly pretty.
Why it's needed - registration / validation / activation e-mails are still sent via obsolete Apache Commons SmtpClient, so they _do not_ support SMTP AUTH.
This is a _major_ problem because due to spam and other abuse less and less hosting providers / network admins allow SMTP without authorization, and those who do are likely to have very insecure infrastructure.
I honestly believe this is a MUST HAVE for a secure, spam-free configuration.
Greetings, Lilianne
Hi Lilianne,
Thanks for this patch. I (and other developers) don't quite agree with its approach, but I guess we could include it for the moment, until we implement the mail component. It's a hack, but one not as bad as other hacks we have, and it does solve a problem.
I agree.
Since none of us has time right now to transform the mailsender in a component, +1 to apply it to fix this until then.
-0 I don't agree with it but I won't block it because that code needs to be cleaned anyway. Note that if some other committers had done this code we would certainly have asked him to revert and fix the problem. Thanks -Vincent
+1 from this xwiki-user. I think this is a very important and necessary patch! I don't think it's fair to call this a "hack" when it is in fact a patch to existing code to allow for functionality whose absence is a total showstopper for many Xwiki installations. The fact that patching for such common-use-case functionality isn't "clean" signifies the need for refactoring, and future development of a grand unified mail plugin. Lilianne also authored the previously accepted authentication patch http://jira.xwiki.org/jira/browse/XPMAIL-10 , providing solutions to issues with modern SMTP use-case scenarios: http://n2.nabble.com/Email-system-in-xwiki-td2566794.html (Mar 31, 2009). Thanks for these most useful patches Lilianne E. Blaze! It would be great if these could be given the appropriately high level of priority and scheduled for release with 1.8.1 or 1.8.2 . Niels http://nielsmayer.com
FYI, one thing a future mail-rearchitecture might consider supporting: A better way of querying whether SMTP is working and the registration email address is valid prior to creating the user being registered. It would probably be a good idea to add the user and the user-document only after successful SMTP delivery of the registration message. Or cleanup the created user/document in a catch() on SMTP failure in com.xpn.xwiki.XWiki.sendMessage(). Note bugs in current feedback and UI-flow on registration email errors: http://jira.xwiki.org/jira/browse/XWIKI-3492 Two problems that arise when following options enabed:
- Administration->Registration->Use email verification == yes - Administration->Registration->Check Active fields for user authentication==yes
(1) /xwiki/bin/register/XWiki/Register allows registration of a user with an empty email field.
After submission, a "dead" user is created with the name given by the user. That account will of course never be automatically validated given that no email went out. The user gets to figure out what's wrong by reading the stacktrace resulting from hitting "submit" with an empty email field:
A problem occured while trying to process your request. Please contact the webmaster if this happens again.
Detailed information:
Error number 10006 in 10: Could not send mail to server smtp port 25 error code 553 (553 5.0.0 <>... User address required ) com.xpn.xwiki.XWikiException: Error number 10006 in 10: Could not send mail to server smtp port 25 error code 553 (553 5.0.0 <>... User address required ) at com.xpn.xwiki.XWiki.sendMessage(XWiki.java:3362) at com.xpn.xwiki.XWiki.sendMessage(XWiki.java:3392) at com.xpn.xwiki.XWiki.sendValidationEmail(XWiki.java:3306) at com.xpn.xwiki.XWiki.sendValidationEmail(XWiki.java:3271) at com.xpn.xwiki.XWiki.createUser(XWiki.java:3225) at com.xpn.xwiki.web.RegisterAction.action(RegisterAction.java:41) at ...
(2) /xwiki/bin/register/XWiki/Register allows registration of a user with a bad or bogus email name, resulting in another backtrace:
A problem occured while trying to process your request. Please contact the webmaster if this happens again.
Detailed information:
Error number 10006 in 10: Could not send mail to server smtp port 25 error code 550 (550 5.1.1 ... User unknown ) com.xpn.xwiki.XWikiException: Error number 10006 in 10: Could not send mail to server smtp port 25 error code 550 (550 5.1.1 ... User unknown ) at com.xpn.xwiki.XWiki.sendMessage(XWiki.java:3362) at com.xpn.xwiki.XWiki.sendMessage(XWiki.java:3392) at com.xpn.xwiki.XWiki.sendValidationEmail(XWiki.java:3306) at com.xpn.xwiki.XWiki.sendValidationEmail(XWiki.java:3271) at com.xpn.xwiki.XWiki.createUser(XWiki.java:3225) at com.xpn.xwiki.web.RegisterAction.action(RegisterAction.java:41) at ...
In both cases, the creation of a XWiki.username document and associated XWiki.XWikiAllGroup entry shoudn't occur until after successful send of the registration email. Also, some common validation rules for email fields should be applied to the registration page's email field when "Use email verification==yes" && "Check Active fields for user authentication==yes".
-- Niels http://nielsmayer.com
Niels Mayer wrote:
FYI, one thing a future mail-rearchitecture might consider supporting: A better way of querying whether SMTP is working and the registration email address is valid prior to creating the user being registered. It would probably be a good idea to add the user and the user-document only after successful SMTP delivery of the registration message. Or cleanup the created user/document in a catch() on SMTP failure in com.xpn.xwiki.XWiki.sendMessage().
Good idea, but note that many counter-spam features will make it unreliable. Many systems when presented with an address like [email protected], where xxx is an user who does not exist, will simply say it's accepted and trash it immediately. What can and should be done is to 1) check if an address looks real according to http://tools.ietf.org/html/rfc5321 , 2) check if the target domain exists. There's no reliable way of checking if an user exists (I believe SMTP has such a feature, but it's usually turned off to not make spammers' lives easier). The only reliable way of dealing with real-looking but invalid addresses is to give an user (for example) 24-72 hours for activation, then delete them (not necessarily after the exact number of hours - a cleaning thread running once per day at whatever time is considered low-traffic will do just fine). Of course the number should be configurable - one admin would set it to 2h which is a sensible minimum (long re-send delay + gray list = up to a little above one hour until the user gets it), another would want one week to give the user a chance to contact them and allow for manual activation if needed for some reason. For checking addresses see http://commons.apache.org/validator/api-1.3.1/org/apache/commons/validator/E... Greetings, Lilianne
Lilianne E. Blaze wrote:
Hello, Here's the newest version.
Basically it bridges XWiki.sendMessage to MailSenderPlugin.
It uses Reflection, so no circulars, but it's not exactly pretty.
The patch cannot be applied yet, since it introduces a regression I'd like to fix first. The old sendMessage allowed indeed to set a Subject inside the content (as a mail header), which is extracted (when found) and passed to the plugin's sendTextMessage method. The problem is that ALL kinds of headers were allowed inside the text content, not just the subject, and with this patch these headers are discarded. -- Sergiu Dumitriu http://purl.org/net/sergiu/
Sergiu Dumitriu wrote:
Lilianne E. Blaze wrote:
Hello, Here's the newest version.
Basically it bridges XWiki.sendMessage to MailSenderPlugin.
It uses Reflection, so no circulars, but it's not exactly pretty.
The patch cannot be applied yet, since it introduces a regression I'd like to fix first.
The old sendMessage allowed indeed to set a Subject inside the content (as a mail header), which is extracted (when found) and passed to the plugin's sendTextMessage method.
The old sendMessage passed headers+body as-is to SMTPClient.
The problem is that ALL kinds of headers were allowed inside the text content, not just the subject, and with this patch these headers are discarded.
Ok. Here's what I'm thinking - remove subject extraction from the patch, instead add a new method to MailSender plugin, like sendRawMessage(String from, String to, String rawData), which acts more like old SMTPClient, and add header extraction/handling logic there. Would that be ok? Greetings, Lilianne
Lilianne E. Blaze wrote:
Sergiu Dumitriu wrote:
Lilianne E. Blaze wrote:
Hello, Here's the newest version.
Basically it bridges XWiki.sendMessage to MailSenderPlugin.
It uses Reflection, so no circulars, but it's not exactly pretty. The patch cannot be applied yet, since it introduces a regression I'd like to fix first.
The old sendMessage allowed indeed to set a Subject inside the content (as a mail header), which is extracted (when found) and passed to the plugin's sendTextMessage method.
The old sendMessage passed headers+body as-is to SMTPClient.
The problem is that ALL kinds of headers were allowed inside the text content, not just the subject, and with this patch these headers are discarded.
Ok.
Here's what I'm thinking - remove subject extraction from the patch, instead add a new method to MailSender plugin, like sendRawMessage(String from, String to, String rawData), which acts more like old SMTPClient, and add header extraction/handling logic there.
Would that be ok?
Yes, that would work. What the parser needs to do is find try to determine if the content starts with headers (keyword: some value), and while a completely blank line isn't found (and for failsafe, while the line still matches this pattern) add each header to the contructed Mail object. -- Sergiu Dumitriu http://purl.org/net/sergiu/
Sergiu Dumitriu wrote:
Lilianne E. Blaze wrote:
Sergiu Dumitriu wrote:
Lilianne E. Blaze wrote:
Hello, Here's the newest version.
Basically it bridges XWiki.sendMessage to MailSenderPlugin.
It uses Reflection, so no circulars, but it's not exactly pretty. The patch cannot be applied yet, since it introduces a regression I'd like to fix first.
The old sendMessage allowed indeed to set a Subject inside the content (as a mail header), which is extracted (when found) and passed to the plugin's sendTextMessage method. The old sendMessage passed headers+body as-is to SMTPClient.
The problem is that ALL kinds of headers were allowed inside the text content, not just the subject, and with this patch these headers are discarded. Ok.
Here's what I'm thinking - remove subject extraction from the patch, instead add a new method to MailSender plugin, like sendRawMessage(String from, String to, String rawData), which acts more like old SMTPClient, and add header extraction/handling logic there.
Would that be ok?
Yes, that would work.
I know it would, I was asking would that be acceptable ;/
What the parser needs to do is find try to determine if the content starts with headers (keyword: some value), and while a completely blank line isn't found (and for failsafe, while the line still matches this pattern) add each header to the contructed Mail object.
Even better. I believe there's a way in JavaMail to create a message from such a raw text, so it should be even easier and shorter. I'll have to check. I'm kind of busy now, but I'll try to have it ready in 2-3 days. Greetings, Lilianne
Sergiu Dumitriu wrote:
Lilianne E. Blaze wrote:
Sergiu Dumitriu wrote:
Lilianne E. Blaze wrote:
Hello, Here's the newest version.
Basically it bridges XWiki.sendMessage to MailSenderPlugin.
It uses Reflection, so no circulars, but it's not exactly pretty. The patch cannot be applied yet, since it introduces a regression I'd like to fix first.
The old sendMessage allowed indeed to set a Subject inside the content (as a mail header), which is extracted (when found) and passed to the plugin's sendTextMessage method. The old sendMessage passed headers+body as-is to SMTPClient.
The problem is that ALL kinds of headers were allowed inside the text content, not just the subject, and with this patch these headers are discarded. Ok.
Here's what I'm thinking - remove subject extraction from the patch, instead add a new method to MailSender plugin, like sendRawMessage(String from, String to, String rawData), which acts more like old SMTPClient, and add header extraction/handling logic there.
Would that be ok?
Yes, that would work.
What the parser needs to do is find try to determine if the content starts with headers (keyword: some value), and while a completely blank line isn't found (and for failsafe, while the line still matches this pattern) add each header to the contructed Mail object.
Done for 1.9m1 core and 1.7 mailsender, attaching. I had no time to test it extensively, but it works in a couple basic scenarios (like adding Priority header). Also logging is debug-type, you'll probably want to change all/most to trace. Note it is still not 100% compatible, in old version you could in theory compose a multipart message, or even multipart with attachments, line-by-line from the template fields, but as the old type was direct text-to-smtp, and the new one isn't (temporary XWiki-Mail objects), it's impossible to write something like that without rewriting it from scratch. This patch should take care of all except the most unusual cases. Also you need to change the line: #set($params.server = ['smtp_server']) To #set($params.server = ['smtp_server', 'smtp_server_username', 'smtp_server_password', 'javamail_extra_props']) In XWiki.AdminGeneralSheet Greetings, Lilianne Index: main/java/com/xpn/xwiki/XWiki.java =================================================================== --- main/java/com/xpn/xwiki/XWiki.java (revision 18814) +++ main/java/com/xpn/xwiki/XWiki.java (working copy) @@ -30,7 +30,6 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; @@ -69,8 +68,6 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.commons.net.smtp.SMTPClient; -import org.apache.commons.net.smtp.SMTPReply; import org.apache.ecs.Filter; import org.apache.ecs.filter.CharacterFilter; import org.apache.ecs.xhtml.textarea; @@ -2877,9 +2874,13 @@ needsUpdate |= bclass.addTextAreaField("meta", "HTTP Meta Info", 60, 8); needsUpdate |= bclass.addTextField("dateformat", "Date Format", 30); + // mail needsUpdate |= bclass.addBooleanField("use_email_verification", "Use eMail Verification", "yesno"); - needsUpdate |= bclass.addTextField("smtp_server", "SMTP Server", 30); needsUpdate |= bclass.addTextField("admin_email", "Admin eMail", 30); + needsUpdate |= bclass.addTextField("smtp_server", "SMTP Server", 30); + needsUpdate |= bclass.addTextField("smtp_server_username", "SMTP Server username (optional)", 30); + needsUpdate |= bclass.addTextField("smtp_server_password", "SMTP Server password (optional)", 30); + needsUpdate |= bclass.addTextAreaField("javamail_extra_props", "Additional JavaMail properties", 60, 6); needsUpdate |= bclass.addTextAreaField("validation_email_content", "Validation eMail Content", 72, 10); needsUpdate |= bclass.addTextAreaField("confirmation_email_content", "Confirmation eMail Content", 72, 10); needsUpdate |= bclass.addTextAreaField("invitation_email_content", "Invitation eMail Content", 72, 10); @@ -3353,74 +3354,66 @@ * @deprecated replaced by the <a href="http://code.xwiki.org/xwiki/bin/view/Plugins/MailSenderPlugin">Mail Sender * Plugin</a> */ + // DEVNOTE: this is the main sendMessage, other variants delegate to this one @Deprecated - public void sendMessage(String sender, String[] recipient, String message, XWikiContext context) + public void sendMessage(String sender, String[] recipients, String rawMessage, XWikiContext context) throws XWikiException { - SMTPClient smtpc = null; - try { - String server = getXWikiPreference("smtp_server", context); - String port = getXWikiPreference("smtp_port", context); - String login = getXWikiPreference("smtp_login", context); + LOG.info("Entering sendMessage(...)..."); - if (context.get("debugMail") != null) { - StringBuffer msg = new StringBuffer(message); - msg.append("\n Recipient: "); - msg.append(recipient); - recipient = ((String) context.get("debugMail")).split(","); - message = msg.toString(); - } + Object mailSender; + Class mailSenderClass; + Method mailSenderSendRaw; - if ((server == null) || server.equals("")) { - server = "127.0.0.1"; - } - if ((port == null) || (port.equals(""))) { - port = "25"; - } - if ((login == null) || login.equals("")) { - login = InetAddress.getLocalHost().getHostName(); - } + try + { + mailSender = getPluginApi("mailsender", context); + mailSenderClass = Class.forName("com.xpn.xwiki.plugin.mailsender.MailSenderPluginApi"); - smtpc = new SMTPClient(); - smtpc.connect(server, Integer.parseInt(port)); - int reply = smtpc.getReplyCode(); - if (!SMTPReply.isPositiveCompletion(reply)) { - Object[] args = {server, port, new Integer(reply), smtpc.getReplyString()}; - throw new XWikiException(XWikiException.MODULE_XWIKI_EMAIL, - XWikiException.ERROR_XWIKI_EMAIL_CONNECT_FAILED, - "Could not connect to server {0} port {1} error code {2} ({3})", null, args); - } + // public int sendRawMessage(String from, String to, String rawMessage) + mailSenderSendRaw = mailSenderClass.getMethod("sendRawMessage", + new Class[]{String.class, String.class, String.class}); + } + catch(Exception e) + { + String eMsg = "Problem getting MailSender via Reflection: " + e; + LOG.error(eMsg); + throw new XWikiException(XWikiException.MODULE_XWIKI_EMAIL, + XWikiException.ERROR_XWIKI_EMAIL_ERROR_SENDING_EMAIL, eMsg); + } - if (smtpc.login(login) == false) { - reply = smtpc.getReplyCode(); - Object[] args = {server, port, new Integer(reply), smtpc.getReplyString()}; - throw new XWikiException(XWikiException.MODULE_XWIKI_EMAIL, - XWikiException.ERROR_XWIKI_EMAIL_LOGIN_FAILED, - "Could not login to mail server {0} port {1} error code {2} ({3})", null, args); - } + LOG.trace("Message = \"" + rawMessage + "\""); - if (smtpc.sendSimpleMessage(sender, recipient, message) == false) { - reply = smtpc.getReplyCode(); - Object[] args = {server, port, new Integer(reply), smtpc.getReplyString()}; - throw new XWikiException(XWikiException.MODULE_XWIKI_EMAIL, - XWikiException.ERROR_XWIKI_EMAIL_SEND_FAILED, - "Could not send mail to server {0} port {1} error code {2} ({3})", null, args); - } + String messageRecipients = recipients[0]; + for( int i = 1; i < recipients.length; i++) + { + messageRecipients = messageRecipients + "," + recipients[i]; + } - } catch (IOException e) { - Object[] args = {sender, recipient}; - throw new XWikiException(XWikiException.MODULE_XWIKI_EMAIL, - XWikiException.ERROR_XWIKI_EMAIL_ERROR_SENDING_EMAIL, "Exception while sending email from {0} to {1}", - e, args); - } finally { - if ((smtpc != null) && (smtpc.isConnected())) { - try { - smtpc.disconnect(); - } catch (IOException e) { - e.printStackTrace(); - } - } + try + { + mailSenderSendRaw.invoke(mailSender, sender, messageRecipients, rawMessage); } + catch(InvocationTargetException ite) + { + Throwable cause = ite.getCause(); + if( cause instanceof XWikiException ) + { + throw (XWikiException)cause; + } + else + { + throw new RuntimeException(cause); + } + } + catch(Exception e) + { + // probably either IllegalAccessException or IllegalArgumentException + // shouldn't happen unless there were an incompatible code change + throw new RuntimeException(e); + } + + LOG.info("Exiting sendMessage(...). It seems everything went ok."); } /** Index: main/resources/ApplicationResources.properties =================================================================== --- main/resources/ApplicationResources.properties (revision 18814) +++ main/resources/ApplicationResources.properties (working copy) @@ -181,6 +181,9 @@ use_email_verification=Use email verification admin_email=Admin email smtp_server=Outgoing SMTP Server +smtp_server_username=SMTP Server Username (optional) +smtp_server_password=SMTP Server Password (optional) +javamail_extra_props=Additional JavaMail properties validation_email_content=Validation e-Mail Content confirmation_email_content=Confirmation e-Mail Content preferences=Preferences Index: main/java/com/xpn/xwiki/plugin/mailsender/MailSender.java =================================================================== --- main/java/com/xpn/xwiki/plugin/mailsender/MailSender.java (revision 18814) +++ main/java/com/xpn/xwiki/plugin/mailsender/MailSender.java (working copy) @@ -121,6 +121,16 @@ List<Attachment> attachments); /** + * Sends a raw message. + * + * @param from + * @param to + * @param rawMessage + * @return + */ + int sendRawMessage(String from, String to, String rawMessage); + + /** * Uses an XWiki document to build the message subject and context, based on variables stored in the * VelocityContext. Sends the email. * Index: main/java/com/xpn/xwiki/plugin/mailsender/MailSenderPluginApi.java =================================================================== --- main/java/com/xpn/xwiki/plugin/mailsender/MailSenderPluginApi.java (revision 18814) +++ main/java/com/xpn/xwiki/plugin/mailsender/MailSenderPluginApi.java (working copy) @@ -29,6 +29,11 @@ import com.xpn.xwiki.api.Attachment; import com.xpn.xwiki.api.XWiki; import com.xpn.xwiki.plugin.PluginApi; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringReader; +import java.io.StringWriter; /** * Plugin that brings powerful mailing capabilities. This is the wrapper accessible from in-document scripts. @@ -109,6 +114,93 @@ return sendMail(email); } + public int sendRawMessage(String from, String to, String rawMessage) + { + LOG.error("sendRawMessage(...)..."); + LOG.error("from = " + from); + LOG.error("to = " + to); + LOG.error("rawMessage = >>>" + rawMessage + "<<<"); + + Mail email = new Mail(); + email.setFrom(from); + email.setTo(to); + + // TODO: do we want a default here? + //email.setSubject("XWiki message"); + + parseRawMessage(email, rawMessage); + return sendMail(email); + } + + protected void parseRawMessage(Mail toMail, String rawMessage) + { + String SUBJECT = "Subject"; + String COLON_SPACE = ": "; + + // sanity check + if( rawMessage == null ) + { + throw new IllegalArgumentException("rawMessage can't be null"); + } + else if( rawMessage.trim().equals("") ) + { + throw new IllegalArgumentException("rawMessage can't be empty"); + } + + try + { + StringReader sr = new StringReader(rawMessage); + BufferedReader br = new BufferedReader(sr); + String line; + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + // skip whitespace at beginning, just in case + // (there should be no empty lines here) + do + { + line = br.readLine(); + } + while( line.trim().equals("") ); + + int pos; + while( (pos = line.indexOf(COLON_SPACE)) != -1 ) + { + String header = line.substring(0, pos); + String value = line.substring(pos + COLON_SPACE.length()); + if( header.equals(SUBJECT) ) + { + toMail.setSubject(value); + } + else + { + toMail.setHeader(header, value); + } + + line = br.readLine(); + } + + // skip whitespace. there should be zero or one empty line here + while( line.trim().equals("") ) + { + line = br.readLine(); + } + + do + { + pw.print(line + "\r\n"); + } + while( (line = br.readLine()) != null ); + + toMail.setTextPart(sw.toString()); + } + catch(IOException ioe) + { + // can't happen here + } + } + /** * {@inheritDoc} *
participants (5)
-
Jerome Velociter -
Lilianne E. Blaze -
Niels Mayer -
Sergiu Dumitriu -
Vincent Massol