I have a Grails-application that sends emails. The mailserver has no SMTP-authentication, so it requires "POP before SMTP", which means that I need to authenticate against the POP-account before sending through SMTP. Most often it works, but then once in a while, the mailserver is not picked up from the properties, and it tries to connect to "localhost" instead. Here is the properties:
Properties props = new Properties();
props.setProperty("mail.store.protocol", "pop3")
props.setProperty("mail.pop3.host", "mail.xxxxx.com")
props.setProperty("mail.pop3.port", "110")
props.setProperty("mail.smtp.host", "mail.xxxxx.com")
props.setProperty("mail.smtp.port", "25")
props.setProperty("mail.smtp.sendpartial", "true")
props.setProperty("mail.pop3.socketFactory.port", "110")
props.setProperty("mail.pop3.socketFactory.class","javax.net.SocketFactory")
props.setProperty("mail.pop3.socketFactory.fallback", "false")
Transport t = null
def store
try {
URLName url = new URLName("pop3", "mail.xxxxxxx.com", 110,
"INBOX", "username", "password");
Session session = Session.getDefaultInstance(props, null)
store = session.getStore(url)
store.connect("mail.xxxxxx.com", "username", "password")
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress("xxxx#xxxxxx.com"));
message.setRecipients(Message.RecipientType.TO,
InternetAddress.parse(toAddress, true));
message.setSubject(mailTitle);
message.setContent(messageBody, "text/html");
message.setSentDate(new Date());
t = session.getTransport("smtp")
t.connect()
t.send(message)
return true
}
catch (AddressException e) {
e.printStackTrace()
return false
}
catch (MessagingException e) {
e.printStackTrace()
return false
}
finally {
store?.close()
}
More often than not, this works. But when a Quartz Job is doing the sending, the mailserver entry from the Properties is not honored and it uses "localhost" instead and then fails to send the emails.
I can connect with Telnet and send with the attributes mentioned.
Could it be a timeout issue? According to what I've read in docs, the timeouts are "infinite" as default, so that "should" not be the problem.
Could it be a performance issue? I've created a "dummy app", which - more or less - does the same, but does not issue the sending from a Grails service, but directly from a controller. That one works all the time, but that app is always idleing.
I'm not using the Mail Plugin for Grails, since I couldn't see that it could handle the "pop before smtp"-paradigm.
Thanks in advance.
Solution: It looks as though the solution is to put the "mail.smtp.localhost"-value to the same value as for "mail.smtp.host". Not one single mail has failed since I put that property in. I don't think it was an obvious property to set and an ignorance from my point of view, nevertheless, I hope this will help someone else in the future.
So, my properties are as follows:
Properties props = new Properties();
props.setProperty("mail.store.protocol", "pop3")
props.setProperty("mail.pop3.host", "mail.xxxxxxx.com")
props.setProperty("mail.pop3.port", "110")
props.setProperty("mail.smtp.localhost", "mail.xxxxxxx.com")
props.setProperty("mail.smtp.host", "mail.xxxxxxx.com")
props.setProperty("mail.smtp.port", "25")
props.setProperty("mail.smtp.sendpartial", "true")
props.setProperty("mail.pop3.socketFactory.port", "110")
props.setProperty("mail.pop3.socketFactory.class","javax.net.SocketFactory")
props.setProperty("mail.pop3.socketFactory.fallback", "false")
Setting the value of "mail.smtp.host" to the property "mail.smtp.localhost", not only made the errors go away, the entire routine became a lot faster.
Thank you for your efforts!
props.setProperty("mail.transport.protocol", "smtp");
props.setProperty("mail.smtp.port", "25");
props.setProperty("mail.host", "a.b.com");
props.setProperty("mail.smtp.timeout", "10000");
Above works
Related
I have Developed ASP.net Core Web API & Web APP Which are in the same solution but different projects.
in the API I have some validations/checking as you may call.
e.g: if user email already exists, the API returns 'Email alreday in use' like this
bool EmailExists = dbContext.Users.Any(u => u.Email == user.Email);
if (EmailExists)
{
return new JsonResult("Email Address already taken!, Try a differen Email");
}
and so on. in some cases I may need to check multiple columns one a time, (eg: UserName, Email, TellNum)
This is an example of calling the API in the MVC
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(_baseAPIUrl);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage Res = await client.PostAsJsonAsync("Users", user);
if (Res.IsSuccessStatusCode)
{
//in here I want check the `Res` and if it contains the returned messages, I want to display them by assigning it to `TempData[infoMsg]`
// else some something(register user)
}
// Check the returned JsonResult messages here if statusCode is ultered eg: BadRequest
}
My Question is how can I display these types of response messages in razor view in the MVC(Web App). in PostMan its workin, returning the response messages in body.
I did a lot of research about this but couldn't come to conclusion. I also cantacted some Devs I know(not .NET) and they said use JavaScript to call your API, which means I have to change almost everything I have done so far.
I aslo tried ultereing the statuCode to something like BadRequest in the API(if Email exists) in which case it will be checked outside the if (Res.IsSuccessStatusCode) of the Httpclient.
any help or direction is highly appreciated.
You should return a http error and a body containing some data about it eg field and message to your mvc controller. That could be a 422 error or whatever you like really since it's effectively internal and just coming back to the mvc controller.
The controller can then add any such error to modelstate and you can use the razor model "client" validation mechanism to show the error associated with a field.
This is therefore using the same mechanism used for attribute validation in the controller where you'd do
if (!ModelState.IsValid)
This is air code but will hopefully give you the idea.
[HttpPost]
public ActionResult PostUpdate(User u)
{
// call service and await response
var response = await httpClient.PostAsJsonAsync(posturi, u);
var returnContent = await response.Content.ReadAsAsync<ReturnContent>();
if (response.Result != HttpStatusCode.OK)
{
ModelState.AddModelError(returnContent.FieldName,returnContent.Error);
return Page();
}
// etc
You will want a more sophisticated checking on errors of course and check you get the body you're expecting.
Maybe you just hard code the field and error message if there's only one possibility. Maybe work with an array of fields and errors if there could be numerous validation fails.
I am working with Spring websocket implementation. For sending a message to clients, there are two ways:
1) Using #SendToUser annotation
2) Using convertAndSendToUser method of SimpMessagingTemplate
#SendToUser takes a boolean parameter called broadcast which if set to false publishes the message to the current session. Is there a way I can have this behaviour in SimpMessagingTemplate.
If we take a look to the SendToMethodReturnValueHandler source code, we'll see:
if (broadcast) {
this.messagingTemplate.convertAndSendToUser(user, destination, returnValue);
}
else {
this.messagingTemplate.convertAndSendToUser(user, destination, returnValue, createHeaders(sessionId));
}
So, what you need for your use-case just use that overloaded convertAndSendToUser and provide a Map with `sessionId:
messagingTemplate.convertAndSendToUser(user, destination, payload,
Collections.singletonMap(SimpMessageHeaderAccessor.SESSION_ID_HEADER, sessionId))
Spring doesn't have a clear document, I tried many different way, only below code works for me.
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create();
accessor.setHeader(SimpMessageHeaderAccessor.SESSION_ID_HEADER, sessionId);
messagingTemplate.convertAndSendToUser(sessionId, destination, payload, accessor.getMessageHeaders());
The answer above did not work for me. It turns out that with Spring 4.1.4 something slightly different is required.
The way that seems the cleanest to me looks like the following:
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create();
headerAccessor.setSessionId(cmd.getSessionId());
headerAccessor.setLeaveMutable(true);
MessageHeaders messageHeaders = headerAccessor.getMessageHeaders();
messagingTemplate.convertAndSendToUser(cmd.getPrincipal().getName(),
"/queue/responses", ret, messageHeaders);
The other way which worked was to explicitly add a "nativeHeaders" value to the Map sent to SimpMessagingTemplate.convertAndSendToUser(). However, this way seems to depend too much on implementation details:
Map<String, Object> headers = new HashMap<>();
headers.put("nativeHeaders", new HashMap<String, Object>());
headers.put(SimpMessageHeaderAccessor.SESSION_ID_HEADER, cmd.getSessionId());
messagingTemplate.convertAndSendToUser(cmd.getPrincipal().getName(),
"/queue/responses", ret, headers);
The "offending code" which made setting the "simpSessionId" header and nothing else in a Map not work was in SimpMessagingTemplate.processHeaders() and MessageHeaderAccessor.getAccessor(MessageHeaders, Class requiredType).
The simplest way send to User by SimpMessagingTemplate
#Autowired
private SimpMessagingTemplate messagingTemplate;
#MessageMapping("/getHello")
public void sendReply( MessageHeaders messageHeaders, #Payload String message, #Header(name = "simpSessionId") String sessionId){
messagingTemplate.convertAndSendToUser(sessionId, "/queue/hello", "Hello "+ message, messageHeaders);
}
I have a link on a grid in my AdminUsers view
grid.Column(header: "", format: (item) => (condition ? Html.ActionLink("Impersonate", "Impersonate", "Admin", new { id = item.username }, null) : Html.Label("Impersonate"), style: "webgrid-column-link"),
In the controller, I have
public ActionResult Impersonate(string id)
{
string result = ORCA.utilities.users.setImpersonation(id);
if(result == "nocommonfields")
return RedirectToAction("AdminUsers", "Admin");
else
return RedirectToAction("terms_of_use", "Forms");
}
How can send an error message to display when I return to the AdminUsers page?
You may use TempData
if(result == "nocommonfields")
{
TempData["ErrorMessage"]="This is the message";
return RedirectToAction("AdminUsers", "Admin");
}
and in your AdminUsers action, you can read it
public ActionResult AdminUsers()
{
var errMsg=TempData["ErrorMessage"] as string;
//check errMsg value do whatever you want now as needed
}
Remember, TempData has very short-life span. Session is the backup storage behind temp data.
Alternatively, You may also consider sending a flag in your querystring and read it in your next action method and decide what error message to show.
The TempData controller property can be used to achieve this kind of functionality. Its main drawback in my opinion is that it uses the session storage in to store its contents. This means that you'll have extra work getting it to function on a web farm, or that you need to turn on sessions in the first place.
The good thing about TempData is that is exactly does what you want. Its a string based dictionary and you can put anything in it and by default get it out only once. So before calling RedirectToAction() you set your message. On the next request you check for messages and display them. By retrieving the messages they are automatically deleted at the end of the request.
As an alternative you could use cookies for transporting the message between the two requests. Essentially you could either roll your own solution, or implement a custom ITempDataProvider which transports the contents of TempData via cookies. Note that you need to properly secure cookies. MachineKey.Protect() can help you if you are rolling your own.
I was facing the same problem you did and created a solution for it called FlashMessage. Perhaps this could save you some work. It's available on NuGet as well. Usage is simple: you simply queue a message before you call RedirectToAction() as follows:
if(result == "nocommonfields")
{
FlashMessage.Warning("Your error message");
return RedirectToAction("AdminUsers", "Admin");
}
In your view you include the following statement to render any previously queued messages:
#Html.RenderFlashMessages()
There is an older thread that seems to be the only relevant discussion I have been able to find.
I am trying to implement Kerberos with Javamail (over IMAP) and I have gotten my self thoroughly confused on exactly what is to be done with mail.imap.sasl.mechanisms. Assume I give the value "GSS-API" but am kind of lost where to go from there. I notice that Javamail has an class IMAPSaslAuthernticator. It seems to me that this is what is needed but I can find precious little documentation on where or how to use it.
Any ideas?
NOTE: I wanted to post more code for my question, but according the site directions, full posts are only for answers. So, I have edited the code I originally posted question.
Below is the real meat. For now, once I pass this point I get the Message[] from the server and print the size to console.
SSL/TLS security is required so it is enabled below. In this example certificates are managed by a trusted keystore in Java.
private Folder folder;
private Session session;
private Store store;
public boolean connectToKerberosMail() {
if (folder != null && folder.isOpen()) {
return true;
}
Properties properties = new Properties();
properties.setProperty("mail.debug", "true");
properties.put("mail.imaps.connectiontimeout",600000);
properties.put("mail.imaps.timeout",601000);
properties.put("mail.imaps.fetchsize", 65000);
properties.put("mail.imaps.starttls.enable", "true");
properties.put("mail.imaps.starttls.required", "false");
properties.put("mail.imaps.sasl.enable","true");
properties.put("mail.imaps.sasl.mechanisms","GSSAPI");
properties.put("mail.imaps.sasl.authorizationid",<user>);
properties.put("mail.imaps.sasl.realm",<realm>);
System.setProperty( "sun.security.krb5.debug", "true");
System.setProperty( "java.security.krb5.realm",<realm>);
System.setProperty( "java.security.krb5.kdc", <ip-address>);
System.setProperty( "java.security.auth.login.config", "jaas.conf");
System.setProperty( "javax.security.auth.useSubjectCredsOnly", "false");
try {
session = Session.getInstance(properties);
} catch (Exception e) {
session = null;
return false;
}
session.setDebug(true);
URLName url = new URLName("imaps", <host>, <port>, "", <user>, <pass>);
store = new IMAPSSLStore(session, url);
try {
store.connect();
} catch (Exception e) {
e.printStackTrace();
store = null;
session = null;
return false;
}
return openFolder();
}
My jaas.conf file is as follows (the ticket cache was acquired from kinit):
com.sun.security.jgss.initiate {
com.sun.security.auth.module.Krb5LoginModule required
principal="<principal>"
ticketCache="<cache-path>"
doNotPrompt="true"
useTicketCache="true"
debug="true";
};
com.sun.security.jgss.accept {
com.sun.security.auth.module.Krb5LoginModule required
principal="<principal>"
ticketCache="<cache-path>"
doNotPrompt="true"
useTicketCache="true"
debug="true";
};
I recently posted here the output but noticed that some of my properties where designated "imap" instead of "imaps". So I am doing more testing before posintg output incase it changes.
In the mean time is what I have above correct? From what I understand I have to enable imap for the imap connection, startTLS for the TLS/SSL, and sasl for kerberos. But maybe something is overriding the another?
While not 100% the way there yet i made some discoveries. LOGIN was happening with the protocol in the NamedURL was "imap". I changed it to "imaps".
However, it look like javamail takes the protocol and host uses them to contruct the principal. protocol/host#realm? so I was applying to principal imaps/host#REALM which didnt exist so failed on a non-matching pricipals error.
So, we added this new principal to the servers and got past this.
But authentication is still failing. In the kerberos log I was approved and sent a ticket for accessing the mail. But I do not see it in my ticket cache (using klist) only the first ticket for accessing kerberos (I got from using kinit).
It seems that I say this in every response. I don't know how to get the word out....
You almost certainly want to change Session.getDefaultInstance() to Session.getInstance(), although that's probably not the source of your problems.
Anyway, what does the protocol trace show when you run your program? (emailSession.setDebug(true);)
I don't know enough about Kerberos, and especially how Kerberos works as a SASL mechanism, but aren't you going to have to specify some sort of password? Or can it get the appropriate Kerberos ticket without asking you to prove who you are?
I'm working on an MVC3 application and I'm using Elmah to handle my error logging. What I want in my application is to carry the Elmah Id onto the custom error page as I will provide a link which allows a user to specifically report it in the event that it is a repeat error (in their opinion).
Now, I've read similar questions on here and they suggest adding the following code (or similar) to the Global.asax.cs file:
void ErrorLog_Logged(object sender, ErrorLoggedEventArgs args)
{
string sessionId = Session.SessionID;
Session["ElmahId_" + sessionId] = args.Entry.Id;
}
This is what I'm using at the moment, with the SessionID allowing for added flexibility in making the Session stored object unique. However, this may still cause issues if more than one error occurs at (virtually) the same time.
Instead, I decided to work on my own HandleErrorAttribute that looks something like this:
public class ElmahHandleErrorAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException("filterContext");
if (filterContext.IsChildAction && (!filterContext.ExceptionHandled
&& filterContext.HttpContext.IsCustomErrorEnabled))
{
Elmah.ErrorSignal.FromCurrentContext().Raise(filterContext.Exception);
// get error id here
string errorId = null;
string areaName = (String)filterContext.RouteData.Values["area"];
string controllerName = (String)filterContext.RouteData.Values["controller"];
string actionName = (String)filterContext.RouteData.Values["action"];
var model = new ErrorDetail
{
Area = areaName,
Controller = controllerName,
Action = actionName,
ErrorId = errorId,
Exception = filterContext.Exception
};
ViewResult result = new ViewResult
{
ViewName = "Error",,
ViewData = new ViewDataDictionary<ErrorDetail>(model),
TempData = filterContext.Controller.TempData
};
filterContext.Result = result;
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
}
}
where ErrorDetail is a custom model which just has the public properties that are being set here as strings. This data can then be shown in the model for admin's at a quick glance and the errorId can be used to create the 'Report Error' link.
So my question is does anyone know of a way of getting the Id after the line
Elmah.ErrorSignal.FromCurrentContext().Raise(filterContext.Exception)
without using the Logged event in the global.asax.cs?
Any thoughts are much appreciated.
After reading Dupin's comments it seems logical that it isn't quite possible. I tried digging around the Elmah source code and came up with a couple of alternatives that might be worth sharing.
The obvious alternative is stick with my original option of using the Logged event:
void ErrorLog_Logged(object sender, ErrorLoggedEventArgs args)
{
string sessionId = Session.SessionID;
Session["ElmahId_" + sessionId] = args.Entry.Id;
}
For a more direct solution it is possible to manually log the error with the following:
string errorId = Elmah.ErrorLog.GetDefault(HttpContext.Current)
.Log(new Elmah.Error(filterContext.Exception));
However, using this approach won't hit your filters or mail module and so on.
After doing a bit of thinking and a little more searching, I came up with a new compromise. Still using the logged event but I've found a way to create a new unique key that can be passed to the view, by adding my own data to the exception.
string loggingKey = "ElmahId_" + Guid.NewGuid().ToString();
filterContext.Exception.Data.Add("LoggingKey", loggingKey);
This way I can pass the exception in my view model, which has this key value in the Data collection. The logged event would be changed to something like this:
void ErrorLog_Logged(object sender, ErrorLoggedEventArgs args)
{
string key = args.Entry.Error.Exception.Data["LoggingKey"].ToString();
Session[key] = args.Entry.Id;
}
Then in the view I get the key from the model to then pull the Id from the Session collection.
Maybe not very helpful but I suspect you can't get the error id at that point and you will need to use the logged event.
When you call
Elmah.ErrorSignal.FromCurrentContext().Raise(filterContext.Exception)
You're just raising the error. Depending on how you've configured ELMAH you might be logging the error or you might just send an email or a tweet.
There's no direct link between a raised error and an Id. That will only come with logging which, if you're feeling funny, you could be doing in multiple places and so creating multiple ids.
http://code.google.com/p/elmah/issues/detail?id=148#c3 is an identical request and a proposed patch on the Elmah project site
The solution above only works only if there is a Session object (website scenario). We needed it to work in an Azure WorkerRole, or a console / desktop app type setup. This solution will also work for web and save some session memory. There isn't a perfect solution, but one that worked for us to be able to log the error and retrieve the stored ID AND fire off an email is to:
Store the error using ErrorLog.Log(error) (see: Using ELMAH in a console application)
Raise the error skipping the logging (SQL or otherwise)
For the second part, we used the implementation of ElmahExtension given here: https://stackoverflow.com/a/2473580/476400
and REMOVED the following lines adding the logging:
(ErrorLog as IHttpModule).Init(httpApplication);
errorFilter.HookFiltering(ErrorLog); //removed!
The entire call from our client code looks like this:
ErrorLog errorLog = ErrorLog.GetDefault(null);
errorLog.ApplicationName = "YourAppName";
Error error = new Error(ex);
string errorResult = errorLog.Log(error);
Guid errorId = new Guid(errorResult);
ex.LogToElmah(); //this is just going to send the email
You might want to call that extention method something else, like RaiseToElmahNoStorage(), or something to indicate it is skipping the storage component.