I'm currently writing an Exchange 2007 Transport Agent to replace some headers in all outgoing mails from a particular sender. I managed to replace the 'From' SMTP header successfully, but rewriting the 'Return-Path' header does not seem to work.
To make this all happen, I have written a custom SmtpReceiveAgent and subscribe to the OnEndOfData event like this:
private static void MyAgent_OnEndOfData(ReceiveMessageEventSource source, EndOfDataEventArgs e)
{
try
{
var address = e.MailItem.Message.From.SmtpAddress;
if (address.ToLower().EndsWith("[internal email domain]"))
{
// replace the From: header - WORKING FINE!
e.MailItem.Message.From = new EmailRecipient("[displayname]",
"[email address]");
// replace the Return-Path: header - NOT WORKING!
var headerList = e.MailItem.Message.RootPart.Headers;
var header = (AddressHeader)headerList.FindFirst("Return-Path");
var newheader = new AddressHeader("Return-Path") { Value = "[email address" };
headerList.ReplaceChild(newheader, header);
}
}
catch (Exception ex)
{
// do something useful here
}
}
Per the RFCs, the Return-Path header is supposed to be set by the recipient's SMTP server. If a Return-Path header exists in the email, it is to be removed, and reset by the recipient's server.
Maybe what you are seeing, is the correct implementation of the RFCs.
I'm not sure, but it sounds like you might want to be changing the "reply-to" header and not "return-path". "return-path" is meant to be set by the server.
Related
I've started using the GMail API and it's working fine on my local machine; it will open the Google permissions page and I can select my account. It then stores the return json token and only asks again if this token is removed.
When I publish to the server, the OAUTH page is never displayed and the application appears to timeout with a 'Thread was being aborted' exception.
My code;
try
{
using (var stream = new FileStream(HttpContext.Current.Server.MapPath("~/credentials/client_id.json"), FileMode.Open, FileAccess.Read))
{
string credPath = HttpContext.Current.Server.MapPath("~/credentials/gmail_readonly_token.json");
_credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
db.writeLog("INFO", "Gmail Credentials Saved","Credential file saved to: " + credPath);
}
// Create Gmail API service.
service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = _credential,
});
}
catch (Exception e)
{
db.writeLog("Error", "Failure when creating Gmail class", e.Message, null, _username, null);
}
Is there something I need to change within the 'client_id.json' (formally client_secret.json) file? The only thing I have altered is the redirect_uris line.
Any other suggestions would be welcome, the only other question I could find that is similar is here but there is no answer.
Thanks,
Danny.
The first one worked because you followed the intended use case, which is client-side. But, to implement authorization on the server, follow the Implementing Server-Side AUthorization guide.
m having an issue with Office 365 EWS (its only Office 365, Exchange 2010 and 2013 work fine). I can create my pull subscription without error but when I go to use it by calling
getEvents()
I receive an error:
ErrorNoRespondingCASInDestinationSite
The following error occured while retrieving events for exchange
resource: - Exchange Web Services are not currently
available for this request because none of the Client Access Servers
in the destination site could process the request.
Here is some code snippets
Using autodiscover and setting up credentials
this.exchangeService.Credentials = new NetworkCredential(this.Username, this.Password);
try {
this.exchangeService.AutodiscoverUrl(this.Username, RedirectionCallback);
}
catch(Exception ex)
{
Logger.WriteToEventLog(EventLogEntryType.Warning, 104, "ExchangeDataAccess, AutodiscoverURL error: " + ex.Message);
}
if (exchangeService.Url == null)
{
this.ExchangeServerURL = GetOffice365EWSUrl(this.Username);
this.exchangeService.Url = new Uri(this.ExchangeServerURL);
this.exchangeService.CookieContainer = new CookieContainer();
}
Afterwhich we Login and find our exchange user that we will perform all operations under
ServicePointManager.ServerCertificateValidationCallback = (sender1, certificate, chain, errors) => true;
string username = this.Username;
if (this.authenticateContext.GetExchangeServerVersion().Contains("365"))
{
username = this.Username.Remove(this.Username.IndexOf("#"));
}
NameResolutionCollection resolveNameResult = this.exchangeService.ResolveName(username, ResolveNameSearchLocation.ContactsThenDirectory, true);
if (resolveNameResult.Count() > 0)
{
roomEmailAddress = resolveNameResult[0].Mailbox.Address;
if (!string.IsNullOrEmpty(roomEmailAddress))
{
this.ExchangeUserEmailAddress = roomEmailAddress;
logMsg.AppendLine("Logged into Exchange with " + roomEmailAddress + " successfully, RetrieveRoomsList is next");
}
}
We then get a SubscribeResponse and save it to a list
subscribeResponse = this.exchangeDataAccess.ExchangeSubscribe(syncPoint.ThirdPartyId, syncPoint.Watermark, true);
We pass the above object into a wrapper method to get all Events from the EWS
Dictionary<PullSubscription, IEnumerable<ItemEvent>> mailboxEvents = null;
GetEventsResults eventsResults = subscription.GetEvents();
if (eventsResults == null || eventsResults.ItemEvents.Count() == 0) {
return mailboxEvents;
}
mailboxEvents = new Dictionary<PullSubscription, IEnumerable<ItemEvent>>();
mailboxEvents.Add(subscription, eventsResults.ItemEvents);
return mailboxEvents;
The line that calls subscription.GetEvents() is where the exception indicated at the top is returned.
There is another layer of complexity added on because our Exchange user has a domain name of #FOOlab.onmicrosoft.com where as all of the rooms being managed have a domain name of #LAB.FOO.COM
According to the customer this is ADFS authentication, however I really don't know much about it.
I can say however that this code base did work (got events) and then something seemed to change and the error started popping up. Originally I thought the customer changed something but we have tested this against another Office 365 (without ADFS) and saw the same error, so now I don't know what to think.
The links below can explain it far better then I can, but what I have done so far which has resolved my issue is to surround the GetEvents
with the add and removal of the header data X-AnchorMailbox
MSDN Link1 Link2
public Dictionary<PullSubscription, IEnumerable<ItemEvent>> GetEvents(SyncPoint syncpoint)
{
Dictionary<PullSubscription, IEnumerable<ItemEvent>> mailboxEvents = null;
if (this.authenticateContext.GetExchangeServerVersion().Contains("365"))
{
try
{
//this is to maintain affinity (see here https://msdn.microsoft.com/en-us/library/office/dn458789(v=exchg.150).aspx)
//it was added to fix an error: The following error occured while retrieving events for exchange resource: <room address> - Exchange Web Services are not currently available for this request because none of the Client Access Servers in the destination site could process the request.
//according to docs it is only when getting notifications that its important
if (this.exchangeService.HttpHeaders.Any(m => m.Key.Equals("X-AnchorMailbox")))
{
this.exchangeService.HttpHeaders.Remove("X-AnchorMailbox");
}
this.exchangeService.HttpHeaders.Add("X-AnchorMailbox", syncpoint.ThirdPartyId); //this is the email address of the mailbox being queried
}
catch { }
}
GetEventsResults eventsResults = syncpoint.pullSubscription.GetEvents();
if (eventsResults == null || eventsResults.ItemEvents.Count() == 0)
{
return mailboxEvents;
}
mailboxEvents = new Dictionary<PullSubscription, IEnumerable<ItemEvent>>();
mailboxEvents.Add(syncpoint.pullSubscription, eventsResults.ItemEvents);
try
{
this.exchangeService.HttpHeaders.Remove("X-AnchorMailbox");
} catch { }
return mailboxEvents;
}
I'm using HttpClient from WP8 and do a Post request. I know the call may take long time as I'm actually simulating slow network scenarios. Therefore I set the HttpClient.Timeout accordingly to 5 minutes.
However, I get a Timeout at around 60s. I believe the Timeout is not working.
I believe there is an issue with this for WP as stated in this question:
HttpClient Portable returns 404 notfound on WP8.
They use a workaround but that does not applies to my scenario. I do actually want to wait for long time.
My questions:
1) Is it a bug/issue of HttpClient for WP8 or I'm not setting it properly?
2) Do you think of a workaround still using HttpClient?
I've read that maybe HttpWebRequest is an option. However, I believe HttpClient should be ideal for this 'simple' scenario.
My code is simple:
private static async Task<HttpResponseMessage> PostAsync(Uri serverUri, HttpContent httpContent)
{
var client = new HttpClient();
client.Timeout = TimeSpan.FromMinutes(5);
return await client.PostAsync(serverUri, httpContent).ConfigureAwait(false);
}
The server receives the request and while is processing it, the client aborts.
UPDATE: The HttpResponseMessage returned by HttpClient.PostAsyn is this "{StatusCode: 404, ReasonPhrase: '', Version: 0.0, Content: System.Net.Http.StreamContent, Headers: { Content-Length: 0 }}". As I said, the server is found and is receiving the data and processing it.
After some search and some tests I've came to the conclusion that the problem is Windows Phone itself and that it has a 60 seconds timeout (irrespective of the HttpClient) and that cannot be changed to my knowledge. See http://social.msdn.microsoft.com/Forums/en-US/faf00a04-8a2e-4a64-b1c1-74c52cf685d3/httpwebrequest-60-seconds-timeout.
As I'm programming the server as well, I will try the advice by Darin Rousseau in the link provided above, specifically to send an OK and then do some more processing.
UPDATE: The problem seems to be the Windows Phone emulator as stated here:
http://social.msdn.microsoft.com/forums/wpapps/en-us/6c114ae9-4dc1-4e1f-afb2-a6b9004bf0c6/httpclient-doesnt-work-on-windows-phone?forum=wpdevelop. In my experience the tcp connection times-out if it doesn't hear anything for 60s.
Therefore my solution is to use the Http header characters as a way of keep alive. The first line Http header response always starts with HTTP/1.0. So I send the characters one by one with a delay <60s between them. Of course, if the response gets ready, everything that is left is sent right away. This buys some time, for instance if using a delay of 50s per 9 character we get about 450s.
This is a project for my degree so I wouldn't recommend it for production.
By the way, I also tried with other characters instead the sub string of the header, for instance space character, but that results in a http protocol violation.
This is the main part of the code:
private const string Header1 = #"HTTP/1.0 ";
private int _keepAliveCounter = 0;
private readonly object _sendingLock = new object();
private bool _keepAliveDone = true;
private void StartKeepAlive()
{
Task.Run(() => KeepAlive());
}
/// <summary>
/// Keeps the connection alive sending the first characters of the http response with an interval.
/// This is a hack for Windows Phone 8 that need reponses within 60s interval.
/// </summary>
private void KeepAlive()
{
try
{
_keepAliveDone = false;
_keepAliveCounter = 0;
while (!_keepAliveDone && _keepAliveCounter < Header1.Length)
{
Task.Delay(TimeSpan.FromSeconds(50)).Wait();
lock (_sendingLock)
{
if (!_keepAliveDone)
{
var sw = new StreamWriter(OutputStream);
sw.Write(Header1[_keepAliveCounter]);
Console.Out.WriteLine("Wrote keep alive char '{0}'", Header1[_keepAliveCounter]);
_keepAliveCounter++;
sw.Flush();
}
}
}
_keepAliveCounter = 0;
_keepAliveDone = true;
}
catch (Exception e)
{
// log the exception
Console.Out.WriteLine("Error while sending keepalive: " + e.Message);
}
}
Then, the actual processing happens in a different thread.
Comments and critics are appreciated.
It is possible that you are hitting the timeout of the network stream. You can change this by doing,
var handler = new WebRequestHandler();
handler.ReadWriteTimeout= 5 * 60 * 1000;
var client = new HttpClient(handler);
client.Timeout = TimeSpan.FromMinutes(5);
return await client.PostAsync(serverUri, httpContent).ConfigureAwait(false);
The default on the desktop OS is already 5mins. However, it is possible that on Windows Phone it has been reduced by default.
Does MT support SMTP SendMail, or am I stuck with MFMailComposeViewController? Right now, I have it working (MFMailComposeViewController), but when I add an attachment, the mail is not received by the receipient.
I was wondering if SMTP would be more reliable and handle attachments.
Yes, it is supported its under System.Net.Mail but it is not recommended to be used because there is no way to get the user credentials from the system unless you ask for them on your application but i dont know if its against the EULA of apple.
i have successfully sent email with attachments from the iphone using the following code hope this helps :)
MFMailComposeViewController _mail;
mailButton.TouchUpInside += (o, e) =>
{
byte[] data = File.ReadAllBytes("photo.png");
NSData datas = NSData.FromArray(data);
if (MFMailComposeViewController.CanSendMail)
{
_mail = new MFMailComposeViewController ();
_mail.SetMessageBody ("This is the body of the email", false);
_mail.AddAttachmentData(datas, "image/png", "photo.png");
_mail.Finished += delegate(object sender, MFComposeResultEventArgs e1)
{
if (e1.Result == MFMailComposeResult.Sent)
{
UIAlertView alert = new UIAlertView ("Mail Alert", "Mail Sent", null, "Success", null);
alert.Show ();
//you should handle other values that could be returned in e.Result and also in e.Error
}
e1.Controller.DismissModalViewControllerAnimated (true);
};
this.PresentModalViewController (_mail, true);
} else {
//handle not being able to send mail
}
};
Also here is the link to the test solution, its based on mike bluestein's example http://dl.dropbox.com/u/2058130/MailDemo.zip and it works for me :)
hope this helps
Alex
Whether it supports it or not, you shouldn't use it.
You have no way of getting the user's SMTP connection settings, so you cannot send mail as the user.
You cannot assume that the user's connection can connect to your server.
Currently I'm sending E-Mail messages by SPUtility.SendMail. I'd like to embed images into my message so i can give it a little bit style (div backgrounds, logo images etc.).
Is this possible?
P.S. I can't give direct URL addresses to image SRCs because the resources are located in a site which belongs to a private network which requires authentication for accessing to the files.
Edit:
I did some research before asking here, ofcourse the first thing i encountered was the System.Net.Mail (do you know that there is a whole web site devoted to it). But the Sharepoint Deployment team in my client's company has some strict rules about custom coding. They have coding guide lines and everything. I'm trying to stick with the SP SDK as hard as i can.
The most straighforward way for me has been through using System.Net.Mail, since you can inline your own content.
Here's a sample usage
using (MailMessage msg = new MailMessage("fromaddress", "toaddress"))
{
msg.Subject = "subject";
msg.Body = "content";
msg.IsBodyHtml = true;
SmtpClient smtp = new SmtpClient("smtp server name");
smtp.Send(msg);
}
Same concept applies to using SPUtility.SendMail (aside from the fact that you'll need a reference to your SPWeb:
From http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.utilities.sputility.sendemail.aspx
try
{
SPWeb thisWeb = SPControl.GetContextWeb(Context);
string toField = "someone#microsoft.com";
string subject = "Test Message";
string body = "Message sent from SharePoint";
bool success = SPUtility.SendEmail(thisWeb,true, false, toField, subject, body);
}
catch (Exception ex)
{
// handle exception
}
The second boolean parameter in SendMail is false to disable HTML encoding, so you can use your <img > and <div > tags in the message body.