Some time ago we faced with the following problem: when send email via CSOM using Utility.SendEmail method we got the following error:
The e-mail message cannot be sent. Make sure the e-mail has a valid recipient.
When Sharepoint processes request from Utility.SendEmail method on server side it uses SPUtility.SendEmail_Client internal method:
1: internal static void SendEmail_Client(EmailProperties properties)
2: {
3: SPWeb web = SPContext.Current.Web;
4: if (web == null)
5: {
6: throw new SPException(SPResource.GetString("ContextWebNotFound",
7: new object[0]));
8: }
9: if (properties.To.Count == 0)
10: {
11: throw SPUtility.GetInvalidRecipientsException(web.LanguageCulture);
12: }
13: web.CheckPermissions(SPBasePermissions.CreateAlerts);
14: if (SPAdministrationWebApplication.Local.OutboundMailServiceInstance == null ||
15: string.IsNullOrEmpty(SPAdministrationWebApplication.Local.
16: OutboundMailServiceInstance.Server.Address))
17: {
18: throw new ConfigurationErrorsException();
19: }
20: Encoding encoding = Encoding.UTF8;
21: try
22: {
23: encoding = Encoding.GetEncoding(web.Site.WebApplication.
24: OutboundMailCodePage);
25: }
26: catch (ArgumentException)
27: {
28: encoding = Encoding.UTF8;
29: }
30: using (MailMessage mm = new MailMessage())
31: {
32: SPUtility.ResolveAddressesForEmail(web, properties.To, delegate(MailAddress a)
33: {
34: mm.To.Add(a);
35: });
36: SPUtility.ResolveAddressesForEmail(web, properties.CC, delegate(MailAddress a)
37: {
38: mm.CC.Add(a);
39: });
40: SPUtility.ResolveAddressesForEmail(web, properties.BCC, delegate(MailAddress a)
41: {
42: mm.Bcc.Add(a);
43: });
44: if (mm.To.Count == 0 && mm.CC.Count == 0 && mm.Bcc.Count == 0)
45: {
46: throw SPUtility.GetInvalidRecipientsException(web.LanguageCulture);
47: }
48: try
49: {
50: SPPrincipalInfo sPPrincipalInfo = SPUtility.ResolvePrincipal(web, properties.From,
51: SPPrincipalType.All, SPPrincipalSource.All, null, false);
52: if (sPPrincipalInfo != null && !string.IsNullOrEmpty(sPPrincipalInfo.Email))
53: {
54: mm.From = new MailAddress(sPPrincipalInfo.Email,
55: (sPPrincipalInfo.DisplayName != null) ?
56: sPPrincipalInfo.DisplayName : "", Encoding.UTF8);
57: }
58: }
59: catch (FormatException)
60: {
61: }
62: if (mm.From == null)
63: {
64: mm.From = new MailAddress(web.Site.WebApplication.OutboundMailSenderAddress,
65: web.Title, Encoding.UTF8);
66: }
67: mm.Subject = properties.Subject;
68: mm.IsBodyHtml = true;
69: mm.BodyEncoding = encoding;
70: mm.SubjectEncoding = encoding;
71: mm.Body = properties.Body;
72: foreach (KeyValuePair<string, string> current in properties.AdditionalHeaders)
73: {
74: mm.Headers[current.Key] = current.Value;
75: }
76: mm.Headers["SharePointSiteId"] = web.Site.ID.ToString("B");
77: SmtpClient smtpClient = new SmtpClient(SPAdministrationWebApplication.Local.
78: OutboundMailServiceInstance.Server.Address);
79: smtpClient.Send(mm);
80: }
81: }
As you can see it throws GetInvalidRecipientsException in 2 cases:
- when To recipients are not specified (line 11)
- when another internal method SPUtility.ResolveAddressesForEmail method doesn’t calls provided delegates for To, Cc and Bcc recipients and as result appropriate recipients lists are empty (line 46)
In our case To recipient was specified for sure, so the only reason was problems in SPUtility.ResolveAddressesForEmail method: because of some reason it didn’t resolve specified recipient. Let’s see the code of this method:
1: private static void ResolveAddressesForEmail(SPWeb web, IEnumerable<string> addresses,
2: SPUtility.AddressReader func)
3: {
4: if (addresses == null)
5: {
6: return;
7: }
8: foreach (string current in addresses)
9: {
10: if (!string.IsNullOrEmpty(current))
11: {
12: SPPrincipalInfo sPPrincipalInfo = SPUtility.ResolvePrincipal(web, current,
13: SPPrincipalType.All, SPPrincipalSource.All, null, false);
14: if (sPPrincipalInfo == null)
15: {
16: ULS.SendTraceTag(3146118u, ULSCat.msoulscat_WSS_General, ULSTraceLevel.Medium,
17: "ResolveAddressesForEmail : No user is resolved from the input '{0}', ignored.",
18: new object[]
19: {
20: current
21: });
22: }
23: else
24: {
25: if (sPPrincipalInfo.PrincipalId <= 0)
26: {
27: ULS.SendTraceTag(3146119u, ULSCat.msoulscat_WSS_General, ULSTraceLevel.Medium,
28: "ResolveAddressesForEmail : Resolved input '{0}' is not registered as the site " +
29: "collection user, ignored. Login '{1}', Email '{2}'", new object[]
30: {
31: current,
32: sPPrincipalInfo.LoginName,
33: sPPrincipalInfo.Email
34: });
35: }
36: else
37: {
38: if (!string.IsNullOrEmpty(sPPrincipalInfo.Email))
39: {
40: try
41: {
42: func(new MailAddress(sPPrincipalInfo.Email, (sPPrincipalInfo.DisplayName != null) ?
43: sPPrincipalInfo.DisplayName : "", Encoding.UTF8));
44: continue;
45: }
46: catch (FormatException)
47: {
48: continue;
49: }
50: }
51: if (string.IsNullOrEmpty(sPPrincipalInfo.Email) && sPPrincipalInfo.IsSharePointGroup &&
52: web.DoesUserHavePermissions(SPBasePermissions.BrowseUserInfo))
53: {
54: SPGroup byNameNoThrow = web.SiteGroups.GetByNameNoThrow(sPPrincipalInfo.LoginName);
55: if (byNameNoThrow != null)
56: {
57: foreach (SPUser sPUser in byNameNoThrow.Users)
58: {
59: if (!string.IsNullOrEmpty(sPUser.Email))
60: {
61: try
62: {
63: func(new MailAddress(sPUser.Email, (sPUser.Name != null) ?
64: sPUser.Name : "", Encoding.UTF8));
65: }
66: catch (FormatException)
67: {
68: }
69: }
70: }
71: }
72: }
73: }
74: }
75: }
76: }
77: }
As you can see it internally uses SPUtility.ResolvePrincipal method which resolve principals based on provided parameters. I tried to call this method for recipient which we specified for Utility.SendEmail and it returned not-null principal:
1: var p = SPUtility.ResolvePrincipal(web, "domain\\username", SPPrincipalType.All,
2: SPPrincipalSource.All, null, false);
But PrincipalId property of returned object was negative (-1). As result SPUtility.ResolveAddressesForEmail method didn’t use this principal and added the following line to the log (lines 25-35):
ResolveAddressesForEmail : Resolved input 'domain\user' is not registered as the site collection user, ignored.
The problem was in the format which we used for specifying email recipient: we used “domain\user” format, while the correct way is to use claims format “i:0#.w|domain\user”. After changing the code emails start working.
Well done! Save my day/week. :D
ReplyDeleteIt solved my big problem, thanks.
ReplyDeleteExcellent post!! Thank you.
ReplyDeleteHi,
ReplyDeleteIn Sendmail it give principal null.
Any idea. Please help.
Regards,
Rohit