Split string with response from gmail - c#

After I retrieve messages from mail box I want to separate message body from subject, date and other information. But I can't find wright algorithm. Here is my code:
// create an instance of TcpClient
TcpClient tcpclient = new TcpClient();
// HOST NAME POP SERVER and gmail uses port number 995 for POP
tcpclient.Connect("pop.gmail.com", 995);
// This is Secure Stream // opened the connection between client and POP Server
System.Net.Security.SslStream sslstream = new SslStream(tcpclient.GetStream());
// authenticate as client
sslstream.AuthenticateAsClient("pop.gmail.com");
//bool flag = sslstream.IsAuthenticated; // check flag
// Asssigned the writer to stream
System.IO.StreamWriter sw = new StreamWriter(sslstream);
// Assigned reader to stream
System.IO.StreamReader reader = new StreamReader(sslstream);
// refer POP rfc command, there very few around 6-9 command
sw.WriteLine("USER my_login");
// sent to server
sw.Flush();
sw.WriteLine("PASS my_pass");
sw.Flush();
// this will retrive your first email
sw.WriteLine("RETR 1");
sw.Flush();
string str = string.Empty;
string strTemp = string.Empty;
while ((strTemp = reader.ReadLine()) != null)
{
// find the . character in line
if (strTemp == ".")
{
break;
}
if (strTemp.IndexOf("-ERR") != -1)
{
break;
}
str += strTemp;
}
// close the connection
sw.WriteLine("Quit ");
sw.Flush();
richTextBox2.Text = str;
I have to extract:
The subject of message
The author
The date
The message body
Can anyone tell me how to do this?
String which I receive (str) contains the subject Test message and the body This is the text of test message. It looks like:
+OK Gpop ready for requests from 46.55.3.85 s42mb37199022eev+OK send PASS+OK Welcome.+OK message followsReturn-Path:
Received: from TMD-I31S3H51L29
(host-static-46-55-3-85.moldtelecom.md. [46.55.3.85]) by
mx.google.com with ESMTPSA id o5sm61119999eeg.8.2014.04.16.13.48.20
for (version=TLSv1
cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 16 Apr 2014
13:48:21 -0700 (PDT)Message-ID:
<534eec95.856b0e0a.55e1.6612#mx.google.com>MIME-Version: 1.0From:
mail_address#gmail.comTo: mail_address#gmail.comDate: Wed, 16 Apr 2014
13:48:21 -0700 (PDT)Subject: Test messageContent-Type: text/plain;
charset=us-asciiContent-Transfer-Encoding: quoted-printableThis is the
text of test message
Thank you very much!

What you first need to do is read rfc1939 to get an idea of the POP3 protocol. But immediately after reading that, you'll need to read the following list of RFCs... actually, screw it, I'm not going to paste the long list of them here, I'll just link you to the website of my MimeKit library which already has a fairly comprehensible list of them.
As your original code correctly did, it needs to keep reading from the socket until the termination sequence (".\r\n") is encountered, thus terminating the message stream.
The way you are doing it is really inefficient, but whatever, it'll (mostly) work except for the fact that you need to undo any/all byte-stuffing that is done by the POP3 server to munge lines beginning with a period ('.'). For more details, read the POP3 specification I linked above.
To parse the headers, you'll need to read rfc822. Suffice it to say, Olivier's approach will fall flat on its face, most likely the second it tries to 'split' any real-world messages... unless it gets extremely lucky.
As a hint, the message body is separated from the headers by a blank line.
Here's a few other problems you are likely to eventually run into:
Header values are supposed to be encoded if they contain non-ASCII text (see rfc2047 and rfc2231 for details).
Some header values in the wild are not properly encoded, and sometimes, even though they are not supposed to, include undeclared 8-bit text. Dealing with this is non-trivial. This also means that you cannot really use a StreamReader to read lines as you'll lose the original byte sequences.
If you actually want to do anything with the body of the message, you'll have to write a MIME parser.
I'd highly recommend using MimeKit and my other library, MailKit, for POP3 support.
Trust me, you are in for a world of pain trying to do this the way you are trying to do it.

String.Split is not powerful enough for this task. You wiil have to use Regex. The pattern that I suggest is:
^(?<name>\w+): (?<value>.*?)$
The meaning is:
^ Beginning of line (if you use the multiline option).
(?<name>pattern) Capturing group where the group name is "name".
\w+ A word.
.*? Any sequence of characters (for the value)
$ End of line
This code ...
MatchCollection matches =
Regex.Matches(text, #"^(?<name>\w+): (?<value>.*?)$", RegexOptions.Multiline);
foreach (Match match in matches) {
Console.WriteLine("{0} = {1}",
match.Groups["name"].Value,
match.Groups["value"].Value
);
}
... produces this output:
Received = from TMD-I31S3H51L29 (host-static-46-55-3-85.m ...
From = mail_address#gmail.com
To = mail_address#gmail.com
Date = Wed, 16 Apr 2014 13:48:21 -0700 (PDT)
Subject = Test message
The body seems to be start after the "Content-Transfer-Encoding:" line and goes to the end of the string. You can find the body like this:
Match body =
Regex.Match(text, #"^Content-Transfer-Encoding: .*?$", RegexOptions.Multiline);
if (body.Success) {
Console.WriteLine(text.Substring(body.Index + body.Length + 1));
}
In case the lines are separated by LineFeeds only the RegexOptions.Multiline might not works. Then you would have to replace the beginning and end of line symbols (^ and $) by \n in the regex expressions.

Related

C# AE.Net.Mail Random Carriage Returns in Subject

I have a problem with the email Subject. I always get an email when there is an error somewhere. So when I look at the email, everything seems fine. But when I watch the characteristics off it, I can see a few errors.
For example:
Subject: Text: Alarm Text about an error
For some reason a carriage return and a space at the start of the new line:
It’s still the same Subject again with a carriage return*
As you can see, for some reasons there are carriage returns (CRLF) which are completely random.
What I’ve tried so far is to check when this happens. Found out that it is Random. When I’m trying to read it out (C#), the Subject is technically right. But instead off a carriage return, there is just nothing. To clarify this: the Subject should be like this:
Subject: Text: Alarm Text about an error for some reason a carriage return and a space at the start of the new line: It’s still the same Subject with a carriage return*
But I get it in the Code like this:
Subject: Text: Alarm Text about an errorfor some reason a carriage return and a space at the start of the new line:It’s still the same Subject with a carriage return
As you can see there is just nothing and there should be a space. So far I’ve tried to change the CRLF to space. Which obviously did not work ‘cause there is no carriage return in the Code. Then I tried to change the encoding to base64. Reason for that was:
http://www.kodokmarton.eu/desktop-application-web-programming/41-web-programming/129-random-newline-and-spaces-inserted-in-emails
So then I tried it like this:
How do I encode and decode a base64 string?
So far nothing worked. This is how I read the mails out:
imap = new AE.Net.Mail.ImapClient(mailServer, login, password, AuthMethods.Login, port, ssl);
var msgs = imap.SearchMessages(SearchCondition.Subject("text Subject"));
for (int i = 0; i < msgs.Length; i++)
{
MailMessage msg = new MailMessage();
msg = msgs[i].Value;
string Subject = msg.Subject;
NOTE: I have to fix it in the Code. For some reasons I can not change the way I send Emails or other things. It has to be in the Code.
Thanks for any advice!

System.Environment.Newline intermittently not working

I have some code that sends an email.
The code is generally along the lines of :
Stringbuilder sb = new StringBuilder();
sb.Append("Field1:" + Field1.Text);
sb.Append(System.Environment.NewLine);
.
.
.
The string is built, then converted to a string and passed to a function that sends the email.
This works for most of the fields in the form. However, there are a couple of fields that never get a line break. It's really weird that the same code is behaving differently.
I've read there are numerous ways to add a newline in a stringBuilder, and I've also tried
sb.AppendLine()
But that didn't work either.
Suggestions?
Various mail clients will try to mess around with emails to try to make them look more like they think you want them to.
One common example is that sometimes plain text emails are artificially line broken in the sending process after a certain number of characters and a mail client might try to detect this and strip out those artificial line breaks. Most often it will do this by thinking that a single line break is a "fake" and that a double line break represents a new paragraph and should be preserved.
The best way around this is to send mail as HTML. Mail clients may have settings to turn off this undesired behaviour but there is no way to disable it in the mail itself as far as I am aware.
Use an HTML line break tag. Make sure the email is set to allow HTML.
Stringbuilder sb = new StringBuilder();
sb.Append("Field1:" + Field1.Text);
sb.AppendLine("<br />"); //use AppendLine so that source looks pretty. Use HTML break so that the rendered text has a new line
public static void SendMail(MailMessage mail)
{
mail.IsBodyHtml=true;
//send the email
}

Equals sign magically appears in message sending to pager

I am sending a mail to an email address which forwards messages to a pager. The message I am sending is
"Oth info; Thankyou testing now complete just be aware that 34a door
will open and shut when pager messages are sent with the CFSRES lt1".
but client receives it on his pager as
"Oth info; Thankyou testing now complete just be aware that 34a door
will op= en and shut when pager messages are sent with the CFSRES
lt1".
Does anyone know why the equals sign shows up in open changed to op= en. I know that special characters may some times be changed, like spaces can become %20, but open does not contain any special character, so as far as I know, nothing should happen to it.
This is a sign of Quoted-printable encoding
QP works by using the equals sign "=" as an escape character. It also limits line length to 76, as some software has limits on line length.
So you may try to split the message into multiple lines in an attempt to prevent escape characters being added by the forwarder.
I search for net and found the solution, this links solve my problem. Here is some userfull code, i need to provide the CreateAlternateViewFromString.
MailMessage emailmsg = new MailMessage("from#address.co.za", "to#address.co.za")
emailmsg.Subject = "Subject";
emailmsg.IsBodyHtml = false;
emailmsg.ReplyToList.Add("from#address.co.za");
emailmsg.BodyEncoding = System.Text.Encoding.UTF8;
emailmsg.HeadersEncoding = System.Text.Encoding.UTF8;
emailmsg.SubjectEncoding = System.Text.Encoding.UTF8;
emailmsg.Body = null;
var plainView = AlternateView.CreateAlternateViewFromString(EmailBody, emailmsg.BodyEncoding, "text/plain");
plainView.TransferEncoding = TransferEncoding.SevenBit;
emailmsg.AlternateViews.Add(plainView);
SmtpClient sSmtp = new SmtpClient();
sSmtp.Send(emailmsg);

Accented characters displayed as hex values in mail source file

I have to convert the content of a mail message to XML format but I am facing some encoding problems. Indeed, all my accented characters and some others are displayed in the message file with their hex value.
Ex :
é is displayed =E9,
ô is displayed =F4,
= is displayed =3D...
The mail is configured to be sent with iso-8859-1 coding and I can see these parameters in the file :
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
Notepad++ detects the file as "ANSI as UTF-8".
I need to convert it in C# (I am in a script task in an SSIS project) to be readable and I can not manage to do that.
I tried encoding it in UTF-8 in my StreamReader but it does nothing. Despite my readings on the topic, I still do not really understand the steps that lead to my problem and the means to solve it.
I point out that Outlook decodes the message well and the accented characters are displayed correctly.
Thanks in advance.
Ok I was looking on the wrong direction. The keyword here is "Quoted-Printable". This is where my issue comes from and this is what I really have to decode.
In order to do that, I followed the example posted by Martin Murphy in this thread :
C#: Class for decoding Quoted-Printable encoding?
The method described is :
public static string DecodeQuotedPrintables(string input)
{
var occurences = new Regex(#"=[0-9A-F]{2}", RegexOptions.Multiline);
var matches = occurences.Matches(input);
foreach (Match match in matches)
{
char hexChar= (char) Convert.ToInt32(match.Groups[0].Value.Substring(1), 16);
input =input.Replace(match.Groups[0].Value, hexChar.ToString());
}
return input.Replace("=\r\n", "");
}
To summarize, I open a StreamReader in UTF8 and place each read line in a string like that :
myString += line + "\r\n";
I open then my StreamWriter in UTF8 too and write the myString variable decoded in it :
myStreamWriter.WriteLine(DecodeQuotedPrintables(myString));

Creating iCal Files in c#

I'm looking for a good method of generating an iCalendar file (*.ics) in c# (asp.net). I've found a couple resources, but one thing that has been lacking is their support for quoted-printable fields - fields that have carriage returns and line feeds.
For example, if the description field isn't encoded properly, only the first line will display and possibly corrupting the rest of the information in the *.ics file.
I'm looking for existing classes that can generate *.ics files and/or a class that can generate quoted-printable fields.
I use DDay.Ical, its good stuff.
Has the ability to open up an ical file and get its data in a nice object model. It says beta, but it works great for us.
Edit Nov 2016
This library has been deprecated, but was picked up and re-released as iCal.NET by another dev.
Notes about the release: rianjs.net/2016/07/dday-ical-is-now-ical-net
Source on GitHub: github.com/rianjs/ical.net
The easiest way I've found of doing this is to markup your HTML using microformats.
If you're looking to generate iCalendar files then you could use the hCalendar microformat then include a link such as 'Add to Calendar' that points to:
http://feeds.technorati.com/events/[ your page's full URL including the http:// ]
The Technorati page then parses your page, extracts the hCalendar info and sends the iCalendar file to the client.
I wrote a shim function to handle this. It's mostly compliant--the only hangup is that the first line is 74 characters instead of 75 (the 74 is to handle the space on subsequent lines)...
Private Function RFC2445TextField(ByVal LongText As String) As String
LongText = LongText.Replace("\", "\\")
LongText = LongText.Replace(";", "\;")
LongText = LongText.Replace(",", "\,")
Dim sBuilder As New StringBuilder
Dim charArray() As Char = LongText.ToCharArray
For i = 1 To charArray.Length
sBuilder.Append(charArray(i - 1))
If i Mod 74 = 0 Then sBuilder.Append(vbCrLf & " ")
Next
Return sBuilder.ToString
End Function
I use this for the summary and description on our ICS feed. Just feed the line with the field already prepended (e.g. LongText = "SUMMARY:Event Title"). As long as you set caching decently long, it's not too expensive of an operation.
iCal (ical 2.0) and quoted-printable don't go together.
Quoted-printable is used a lot in vCal (vCal 1.0) to represent non-printable characters, e.g. line-breaks (=0D=0A). The default vCal encoding is 7-bit, so sometimes you need to use quoted-printable to represent non-ASCII characters (you can override the default encoding, but the other vCal-compliant communicating party is not required to understand it.)
In iCal, special characters are represented using escapes, e.g. '\n'. The default encoding is UTF-8, all iCal-compliant parties must support it and that makes quoted-printable completely unnecessary in iCal 2.0 (and vCard 3.0, for that matter).
You may need to back your customer/stakeholder to clarify the requirements. There seems to be confusion between vCal and iCal.
I'm missing an example with custom time zones. So here a snippet that show how you can set a time zone in the ics (and send it to the browser in asp.net).
//set a couple of variables for demo purposes
DateTime IcsDateStart = DateTime.Now.AddDays(2);
DateTime IcsDateEnd = IcsDateStart.AddMinutes(90);
string IcsSummary = "ASP.Net demo snippet";
string IcsLocation = "Amsterdam (Netherlands)";
string IcsDescription = #"This snippes show you how to create a calendar item file (.ics) in ASP.NET.\nMay it be useful for you.";
string IcsFileName = "MyCalendarFile";
//create a new stringbuilder instance
StringBuilder sb = new StringBuilder();
//begin the calendar item
sb.AppendLine("BEGIN:VCALENDAR");
sb.AppendLine("VERSION:2.0");
sb.AppendLine("PRODID:stackoverflow.com");
sb.AppendLine("CALSCALE:GREGORIAN");
sb.AppendLine("METHOD:PUBLISH");
//create a custom time zone if needed, TZID to be used in the event itself
sb.AppendLine("BEGIN:VTIMEZONE");
sb.AppendLine("TZID:Europe/Amsterdam");
sb.AppendLine("BEGIN:STANDARD");
sb.AppendLine("TZOFFSETTO:+0100");
sb.AppendLine("TZOFFSETFROM:+0100");
sb.AppendLine("END:STANDARD");
sb.AppendLine("END:VTIMEZONE");
//add the event
sb.AppendLine("BEGIN:VEVENT");
//with a time zone specified
sb.AppendLine("DTSTART;TZID=Europe/Amsterdam:" + IcsDateStart.ToString("yyyyMMddTHHmm00"));
sb.AppendLine("DTEND;TZID=Europe/Amsterdam:" + IcsDateEnd.ToString("yyyyMMddTHHmm00"));
//or without a time zone
//sb.AppendLine("DTSTART:" + IcsDateStart.ToString("yyyyMMddTHHmm00"));
//sb.AppendLine("DTEND:" + IcsDateEnd.ToString("yyyyMMddTHHmm00"));
//contents of the calendar item
sb.AppendLine("SUMMARY:" + IcsSummary + "");
sb.AppendLine("LOCATION:" + IcsLocation + "");
sb.AppendLine("DESCRIPTION:" + IcsDescription + "");
sb.AppendLine("PRIORITY:3");
sb.AppendLine("END:VEVENT");
//close calendar item
sb.AppendLine("END:VCALENDAR");
//create a string from the stringbuilder
string CalendarItemAsString = sb.ToString();
//send the ics file to the browser
Response.ClearHeaders();
Response.Clear();
Response.Buffer = true;
Response.ContentType = "text/calendar";
Response.AddHeader("content-length", CalendarItemAsString.Length.ToString());
Response.AddHeader("content-disposition", "attachment; filename=\"" + IcsFileName + ".ics\"");
Response.Write(CalendarItemAsString);
Response.Flush();
HttpContext.Current.ApplicationInstance.CompleteRequest();
Check out http://www.codeproject.com/KB/vb/vcalendar.aspx
It doesn't handle the quoted-printable fields like you asked, but the rest of the code is there and can be modified.
According to RFC-2445, the comment and description fields are TEXT. The rules for a test field are:
[1] A single line in a TEXT field is not to exceed 75 octets.
[2] Wrapping is achieved by inserting a CRLF followed by whitespace.
[3] There are several characters that must be encoded including \ (reverse slash) ; (semicolon) , (comma) and newline. Using a \ (reverse slash) as a delimiter gives \ \; \, \n
Example: The following is an example of the property with formatted
line breaks in the property value:
DESCRIPTION:Meeting to provide technical review for "Phoenix"
design.\n Happy Face Conference Room. Phoenix design team
MUST attend this meeting.\n RSVP to team leader.
iCal can be complicated, so I recommend using a library. DDay is a good free solution. Last I checked it didn't have full support for recurring events, but other than that it looks really nice. Definitely test the calendars with several clients.
i know it is too late, but it may help others. in my case i wrote following text file with .ics extension
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Calendly//EN
CALSCALE:GREGORIAN
METHOD:PUBLISH
BEGIN:VEVENT
DTSTAMP:20170509T164109Z
UID:your id-11273661
DTSTART:20170509T190000Z
DTEND:20170509T191500Z
CLASS:PRIVATE
DESCRIPTION:Event Name: 15 Minute Meeting\nDate & Time: 03:00pm - 03:15pm (
Eastern Time - US & Canada) on Tuesday\, May 9\, 2017\n\nBest Phone Number
To Reach You :: xxxxxxxxx\n\nany "link": https://wwww.yahoo.com\n\n
SUMMARY:15 Minute Meeting
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR
it worked for me.

Categories

Resources