Return Email Message from Web Api - email

I'm not sure if this is possible, but I need to return an email message from a web api controller. Essentially, it would then allow the user the open the file (an eml or msg), make some changes and then send it to the relevant person.
Code wise I have a service that returns a MailMessage.
I have a controller that returns a pdf, using the file's byte array as it's content, but a mail message doesn't seem the easiest thing to convert.
Is this possible? I would rather not write the message to disk first if I can help it, but I could if this is the only solution.

I have just faced the same problem, and I resolved it like this:
var stream = new MemoryStream(); // this has to contain your file content
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(stream.GetBuffer())
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("message/rfc822");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "test.eml"
};
If you want to know how to generate emails and do not send the but save them to disk instead, you can check the solution proposed here: How to save MailMessage object to disk as *.eml or *.msg file

Related

Intercept and edit multipart form-data POST request body in Browser

I've got a site that accepts file uploads which are sent as multipart/form-data within a POST request. To verify that the upload, which shows the filename afterwards, is secured against XSS I want to upload a file which contains HTML Tags in the filename.
This is actually harder than I expected. I can't create a file containing < on my filesystem (Windows). Also, I don't know a way to change the filename of the file input element inside the DOM before the upload (which is what I would do with normal/hidden inputs). So I thought about editing the POST body before it's uploaded, but I don't know how. Popular extensions (I recall Tamper Data, Tamper Dev) only let me change headers. I guess this is due to the plugin system of Chrome, which is the Browser I use.
So, what's the simplest way of manipulating the POST requests body? I could craft the entire request using cUrl, but I also need state, lots of additional parameters and session data etc. which gets quite complex... A simple way within the Browser would ne nice.
So, while this is not a perfect solution, it is at least a way to recreate and manipulate the form submit using FormData and fetch. It is not as generic as I'd like it to be, but it works in that case. Just use this code in the devtools to submit the form with the altered filename:
let formElement = document.querySelector('#idForm'); // get the form element
let oldForm = new FormData(formElement);
let newForm = new FormData;
// copy the FormData entry by entry
for (var pair of oldForm.entries()) {
console.log(pair[0]+': '+pair[1]);
if(typeof(pair[1]) == 'object' && pair[1].name) {
// alter the filename if it's a file
newForm.append(pair[0],pair[1],'yourNewFilename.txt');
} else {
newForm.append(pair[0],pair[1]);
}
}
// Log the new FormData
for (var pair of newForm.entries()) {
console.log(pair[0]+': ');
console.log(pair[1]);
}
// Submit it
fetch(formElement.action, {
method: formElement.method,
body: newForm
});
I'd still appreciate other approaches.

MVC5 Mailkit send email with incorrect Email address

I am trying to send emails using my MVC5 application. To do this, I have installed Mailkit v 1.22.0 through NuGet package manager. And this is how my code looks like:
var FromAddress = "no-reply#email.com";
var FromAddressTitle = "My Org";
var connection = ConfigurationManager.ConnectionStrings["SmtpServer"].ConnectionString;
var Email = new MimeMessage();
Email.From.Add(new MailboxAddress(FromAddressTitle, FromAddress));
var AddressArray = value.SentTo.Split(';');
foreach (var item in AddressArray)
{
Email.To.Add(new MailboxAddress(item));
}
Email.Subject = value.Subject;
Email.Body = new TextPart("html")
{
Text = value.Content
};
using (var client = new SmtpClient())
{
client.Connect(connection);
client.Send(Email);
}
return "Email Successfully Sent";
which works fine except if a wrong recipient Email address has been entered, the application does not detect if the Email was actually sent or not (client.Send(Email) returns void). Is there a way to know if it really ended up getting sent to the recipient or not? If it is not possible with Mailkit, is there any other NuGet package that can do this?
The reason that SmtpClient.Send() returns void is that the SMTP protocol does not specify whether the message gets delivered successfully. All it can do us tell the client that the messages was accepted by the server or not (in which case MailKit will throw an exception).
If you need to know whether the message was successfully delivered, you will need to check for bounce messages sent to you which could take minutes or even hours.
The first thing you'll have to do, however, is subclass SmtpClient and override the GetEnvelopeId and GetDeliveryStatusNotifications methods.
Then, when you receive a bounce message, the top-level MIME part will typically be a multipart/report (represented by a MultipartReport object when using MimeKit). This multipart/report will then contain a message/delivery-status MIME part (and possibly others), which will have a list of header-like fields that specify the details about the delivery status for 1 or more recipients.
MimeKit will parse a lot of this for you (e.g. it has a MessageDeliveryStatus class which contains a StatusGroups property that you will want to use. However, what MimeKit does not do is parse the individual field values (but they shouldn't be that difficult for you to do, typically a few Split(';')'s should be enough iirc for some quick & dirty parsing).
You will want to read the spec for this at https://www.rfc-editor.org/rfc/rfc3464
The MimeKit docs linked above specify which sections to look closely at (I think 2.2 and 2.3).
I would recommend looking specifically at the Original-Recipient and Action fields.
original-recipient-field =
"Original-Recipient" ":" address-type ";" generic-address
generic-address = *text
action-field = "Action" ":" action-value
action-value =
"failed" / "delayed" / "delivered" / "relayed" / "expanded"
You will also need the Original-Envelope-Id field to figure out which message is being reported on:
original-envelope-id-field =
"Original-Envelope-Id" ":" envelope-id
envelope-id = *text
The envelope-id text will be the same string returned by your GetEnvelopeId implementation in the SmtpClient class.

Sending email to multiple recipients from UWP store app

I have a simple goal, to open up an email (in Outlook 2016) with the To field configured for multiple recipients from a Windows 10 UWP app.
I tried 3 approaches
1) The recommended way, as demod in the UWP samples, using the EmailMessage
var emailMessage = new Windows.ApplicationModel.Email.EmailMessage();
emailMessage.Body = "";
foreach (Person p in SelectedPeople)
{
if (string.IsNullOrEmpty(p.Email) == false)
{
var emailRecipient = new Windows.ApplicationModel.Email.EmailRecipient(p.Email);
emailMessage.To.Add(emailRecipient);
}
}
await Windows.ApplicationModel.Email.EmailManager.ShowComposeNewEmailAsync(emailMessage);
This results in an email window with the recipients seperate by commas which then do not resolve. Setting the option to allow comma seperators seemed like an answer, but it tuens out that doesn't work unless there is a space to seperate too?
2) Build a mailto:user1#work.com;user2#work.com URI and launch it.
var uri = new Uri("mailto:user1#work.com;user2#work.com");
var success = await Windows.System.Launcher.LaunchUriAsync(uri);
However, attempting to create URI with multiple recipeitns throws an exception that the URI hostname is invalid
3) Same as above but using the scheme mailto:?To=user1#work.com;user2#work.com
This is parsed correctly as a URI but on launch Outlook shows an empty recipient list. By way of testing, using CC= does show the recipients in the CC field
So, now I am stuck wondering how I can send an email to multiple recipients for a store app?
Late to the party, but someone linked this from another question..
Have you tied mailto:user1#work.com%3buser2#work.com
seems to work for me

phpmailer attach pdf from dynamic url

I'm sending an email using phpmailer. I have web service to generate pdf. This pdf is not uploading or downloading to anywhere.
PDF url is like
http://mywebsite/webservices/report/sales_invoice.php?company=development&sale_id=2
I need to attach this dynamic pdf url to my email.
My email sending service url is like
http://mywebsite/webservices/mailservices/sales_email.php
Below is the code which i am using to attach the pdf.
$pdf_url = "../report/sales_invoice.php?company=development&sale_id=2";
$mail->AddAttachment($pdf_url);
Sending message is working but pdf doesn't attached. It gives below message.
Could not access file: ../report/sales_invoice.php?company=development&sale_id=2
I need some help
To have the answer right here:
As phpmailer would not auto-fetch the remote content, you need to do it yourself.
So you go:
// we can use file_get_contents to fetch binary data from a remote location
$url = 'http://mywebsite/webservices/report/sales_invoice.php?company=development&sale_id=2';
$binary_content = file_get_contents($url);
// You should perform a check to see if the content
// was actually fetched. Use the === (strict) operator to
// check $binary_content for false.
if ($binary_content === false) {
throw new Exception("Could not fetch remote content from: '$url'");
}
// $mail must have been created
$mail->AddStringAttachment($binary_content, "sales_invoice.pdf", $encoding = 'base64', $type = 'application/pdf');
// continue building your mail object...
Some other things to watch out for:
Depending on the server response time, your script might run into timing issues. Also, the fetched data might be pretty large and could cause php to exceed its memory allocation.

Help with a Windows Service/Scheduled Task that must use a web browser and file dialogs

What I'm Trying To Do
I'm trying to create a solution of any kind that will run nightly on a Windows server, authenticate to a website, check a web page on the site for new links indicating a new version of a zip file, use new links (if present) to download a zip file, unzip the downloaded file to an existing folder on the server, use the unzipped contents (sql scripts, etc.) to build an instance of a database, and log everything that happens to a text file.
Forms App: The Part That Sorta Works
I created a Windows Forms app that uses a couple of WebBrowser controls, a couple of threads, and a few timers to do all that except the running nightly. It works great as a Form when I'm logged in and run it, but I need to get it (or something like it) to run on it's own like a Service or scheduled task.
My Service Attempt
So, I created a Windows Service that ticks every hour and, if the System.DateTime.Now.Hour >= 22, attempts to launch the Windows Forms app to do it's thing. When the Service attempts to launch the Form, this error occurs:
ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded apartment.
which I researched and tried to resolve by either placing the [STAThread] attribute on the Main method of the Service's Program class or using some code like this in a few places including the Form constructor:
webBrowseThread = new Thread(new ThreadStart(InitializeComponent));
webBrowseThread.SetApartmentState(ApartmentState.STA);
webBrowseThread.Start();
I couldn't get either approach to work. In the latter approach, the controls on the Form (which would get initialized inside IntializeComponent) don't get initialized and I get null reference exceptions.
My Scheduled Task Attempt
So, I tried creating a nightly scheduled task using my own credentials to run the Form locally on my dev machine (just testing). It gets farther than the Service did, but gets hung up at the File Download Dialog.
Related Note: To send the key sequences to get through the File Download and File Save As dialogs, my Form actually runs a couple of vbscript files that use WScript.Shell.SendKeys. Ok, that's embarassing to admit, but I tried a few different things including SendMessage in Win32 API and referencing IWshRuntimeLibrary to use SendKeys inside my C# code. When I was researching how to get through the dialogs, the Win32 API seemed to be the recommended way to go, but I couldn't figure it out. The vbscript files was the only thing I could get to work, but I'm worried now that this may be the reason why a scheduled task won't work.
Regarding My Choice of WebBrowser Control
I have read about the System.WebClient class as an alternative to the WebBrowser control, but at a glance, it doesn't look like it has what I need to get this done. For example, I needed (or I think I needed) the WebBrowser's DocumentCompleted and FileDownload events to handle the delays in pages loading, files downloading, etc. Is there more to WebClient that I'm not seeing? Is there another class besides WebBrowser that is more Service-friendly and would do the trick?
In Summary
Geez, this is long. Sorry! It would help to even have a high level recommendation for a better way to do what I'm trying to do, because nothing I've tried has worked.
Update 10/22/09
Well, I think I'm closer, but I'm stuck again. I should end up with a decent-sized zip file with several files in it, but the zip file resulting from my code is empty. Here's my code:
// build post request
string targetHref = "http://wwwcf.nlm.nih.gov/umlslicense/kss/login.cfm";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(targetHref);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
// encoding to use
Encoding enc = Encoding.GetEncoding(1252);
// build post string containing authentication information and add to post request
string poststring = "returnUrl=" + fixCharacters(targetDownloadFileUrl);
poststring += getUsernameAndPasswordString();
poststring += "&login2.x=0&login2.y=0";
// convert to required byte array
byte[] postBytes = enc.GetBytes(poststring);
request.ContentLength = postBytes.Length;
// write post to request
Stream postStream = request.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Close();
// get response as stream
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
// writes stream to zip file
FileStream writeStream = new FileStream(fullZipFileName, FileMode.Create, FileAccess.Write);
ReadWriteStream(responseStream, writeStream);
response.Close();
responseStream.Close();
The code for ReadWriteStream looks like this.
private void ReadWriteStream(Stream readStream, Stream writeStream)
{
// taken verbatum from http://www.developerfusion.com/code/4669/save-a-stream-to-a-file/
int Length = 256;
Byte[] buffer = new Byte[Length];
int bytesRead = readStream.Read(buffer, 0, Length);
// write the required bytes
while (bytesRead > 0)
{
writeStream.Write(buffer, 0, bytesRead);
bytesRead = readStream.Read(buffer, 0, Length);
}
readStream.Close();
writeStream.Close();
}
The building of the post string is taken from my previous forms app that works. I compared the resulting values in poststring for both sets of code (my working forms app and this one) and they're identical.
I'm not even sure how to troubleshoot this further. Anyone see anything obvious as to why this isn't working?
Conclusion 10/23/09
I finally have this working. A couple of important hurdles I had to get over. I had some problems with the ReadWriteStream method code that I got online. I don't know why, but it wasn't working for me. A guy named JB in Claudio Lassala's Virtual Brown Bag meeting helped me to come up with this code which worked much better for my purposes:
private void WriteResponseStreamToFile(Stream responseStreamToRead, string zipFileFullName)
{
// responseStreamToRead will contain a zip file, write it to a file in
// the target location at zipFileFullName
FileStream fileStreamToWrite = new FileStream(zipFileFullName, FileMode.Create);
int readByte = responseStreamToRead.ReadByte();
while (readByte != -1)
{
fileStreamToWrite.WriteByte((byte)readByte);
readByte = responseStreamToRead.ReadByte();
}
fileStreamToWrite.Flush();
fileStreamToWrite.Close();
}
As Will suggested below, I did have trouble with the authentication. The following code is what worked to get around that issue. A few comments inserted addressing key issues I ran into.
string targetHref = "http://wwwcf.nlm.nih.gov/umlslicense/kss/login.cfm";
HttpWebRequest firstRequest = (HttpWebRequest)WebRequest.Create(targetHref);
firstRequest.AllowAutoRedirect = false; // this is critical, without this, NLM redirects and the whole thing breaks
// firstRequest.Proxy = new WebProxy("127.0.0.1", 8888); // not needed for production, but this helped in order to debug the http traffic using Fiddler
firstRequest.Method = "POST";
firstRequest.ContentType = "application/x-www-form-urlencoded";
// build post string containing authentication information and add to post request
StringBuilder poststring = new StringBuilder("returnUrl=" + fixCharacters(targetDownloadFileUrl));
poststring.Append(getUsernameAndPasswordString());
poststring.Append("&login2.x=0&login2.y=0");
// convert to required byte array
byte[] postBytes = Encoding.UTF8.GetBytes(poststring.ToString());
firstRequest.ContentLength = postBytes.Length;
// write post to request
Stream postStream = firstRequest.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length); // Fiddler shows that post and response happen on this line
postStream.Close();
// get response as stream
HttpWebResponse firstResponse = (HttpWebResponse)firstRequest.GetResponse();
// create new request for new location and cookies
HttpWebRequest secondRequest = (HttpWebRequest)WebRequest.Create(firstResponse.GetResponseHeader("location"));
secondRequest.AllowAutoRedirect = false;
secondRequest.Headers.Add(HttpRequestHeader.Cookie, firstResponse.GetResponseHeader("Set-Cookie"));
// get response to second request
HttpWebResponse secondResponse = (HttpWebResponse)secondRequest.GetResponse();
// write stream to zip file
Stream responseStreamToRead = secondResponse.GetResponseStream();
WriteResponseStreamToFile(responseStreamToRead, fullZipFileName);
responseStreamToRead.Close();
sl.logScriptActivity("Downloading update.");
firstResponse.Close();
I want to underscore that setting AllowAutoRedirect to false on the first HttpWebRequest instance was critical to the whole thing working. Fiddler showed two additional requests that occurred when this was not set, and it broke the rest of the script.
You're trying to use UI controls to do something in a windows service. This will never work.
What you need to do is just use the WebRequest and WebResponse classes to download the contents of the webpage.
var request = WebRequest.Create("http://www.google.com");
var response = request.GetResponse();
var stream = response.GetResponseStream();
You can dump the contents of the stream, parse the text looking for updates, and then construct a new request for the URL of the file you want to download. That response stream will then have the file, which you can dump on the filesystem and etc etc.
Before you wonder, GetResponse will block until the response returns, and the stream will block as data is being received, so you don't need to worry about events firing when everything has been downloaded.
You definitely need to re-think your approach (as you've already begun to do) to eliminate the Forms-based application approach. The service you're describing needs to operate with no UI at all.
I'm not familiar with the details of System.WebClient, but since it
provides common methods for sending
data to and receiving data from a
resource identified by a URI,
it will probably be your answer.
At first glance, WebClient.DownloadFile(...) or WebClient.DownloadFileAsync(...) will do what you need.
The only thing I can add is that once you have scraped your screen and have the fully qualified name of the file you want to download, you could pass it along to the Windows/DOS command 'get' which will fetch files via HTTP. You can also script a command-line FTP client if desired. It's been a long time since I tried something like this in Windows, but I think you're almost there. Once you have fetched the correct file, building a batch file to do everything else should be pretty easy. If you are more comfortable with Unix, google "unix services for windows" just keep an eye on the services they start running (DHCP, etc). There are some nice utilities which will let your treat dos as a unix-like shell (ls -l, grep, etc) Finally, you could try another language like Perl or Python but I don't think that's the kind of advice you were looking for. :)