1: /*
2: Copyright (c) 2010 <a href="http://www.gutgames.com">James Craig</a>
3:
4: Permission is hereby granted, free of charge, to any person obtaining a copy
5: of this software and associated documentation files (the "Software"), to deal
6: in the Software without restriction, including without limitation the rights
7: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8: copies of the Software, and to permit persons to whom the Software is
9: furnished to do so, subject to the following conditions:
10:
11: The above copyright notice and this permission notice shall be included in
12: all copies or substantial portions of the Software.
13:
14: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20: THE SOFTWARE.*/
21:
22: #region Usings
23: using System;
24: using System.Collections.Generic;
25: using System.Net;
26: using System.Net.Mail;
27: using System.Text;
28: #endregion
29:
30:
31: namespace Utilities.Exchange
32: {
33: /// <summary>
34: /// Utility class for creating and sending an appointment
35: /// </summary>
36: public class Appointment
37: {
38: #region Constructor
39: /// <summary>
40: /// Constructor
41: /// </summary>
42: public Appointment()
43: {
44: AttendeeList = new MailAddressCollection();
45: Attachments = new List<Attachment>();
46: }
47: #endregion
48:
49: #region Public Functions
50:
51: /// <summary>
52: /// Adds an appointment to a user's calendar
53: /// </summary>
54: public virtual void AddAppointment()
55: {
56: string XMLNSInfo = "xmlns:g=\"DAV:\" "
57: + "xmlns:e=\"http://schemas.microsoft.com/exchange/\" "
58: + "xmlns:mapi=\"http://schemas.microsoft.com/mapi/\" "
59: + "xmlns:mapit=\"http://schemas.microsoft.com/mapi/proptag/\" "
60: + "xmlns:x=\"xml:\" xmlns:cal=\"urn:schemas:calendar:\" "
61: + "xmlns:dt=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\" "
62: + "xmlns:header=\"urn:schemas:mailheader:\" "
63: + "xmlns:mail=\"urn:schemas:httpmail:\"";
64:
65: string CalendarInfo = "<cal:location>" + Location + "</cal:location>"// + Location + "</cal:location>"
66: + "<cal:dtstart dt:dt=\"dateTime.tz\">" + StartDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.000Z") + "</cal:dtstart>"// + StartDate.ToUniversalTime().ToString("yyyyMMddTHHmmssZ") + "</cal:dtstart>"
67: + "<cal:dtend dt:dt=\"dateTime.tz\">" + EndDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.000Z") + "</cal:dtend>"// + EndDate.ToUniversalTime().ToString("yyyyMMddTHHmmssZ") + "</cal:dtend>"
68: + "<cal:instancetype dt:dt=\"int\">0</cal:instancetype>"
69: + "<cal:busystatus>BUSY</cal:busystatus>"
70: + "<cal:meetingstatus>CONFIRMED</cal:meetingstatus>"
71: + "<cal:alldayevent dt:dt=\"boolean\">0</cal:alldayevent>"
72: + "<cal:responserequested dt:dt=\"boolean\">1</cal:responserequested>"
73: + "<cal:reminderoffset dt:dt=\"int\">900</cal:reminderoffset>"
74: + "<cal:uid>" + MeetingGUID.ToString("B") + "</cal:uid>";
75:
76: string HeaderInfo = "<header:to>" + AttendeeList.ToString() + "</header:to>";
77:
78: string MailInfo = "<mail:subject>" + Subject + "</mail:subject>"
79: + "<mail:htmldescription>" + Summary + "</mail:htmldescription>";
80:
81: string AppointmentRequest = "<?xml version=\"1.0\"?>"
82: + "<g:propertyupdate " + XMLNSInfo + ">"
83: + "<g:set><g:prop>"
84: + "<g:contentclass>urn:content-classes:appointment</g:contentclass>"
85: + "<e:outlookmessageclass>IPM.Appointment</e:outlookmessageclass>"
86: + MailInfo
87: + CalendarInfo
88: + HeaderInfo
89: + "<mapi:finvited dt:dt=\"boolean\">1</mapi:finvited>"
90: + "</g:prop></g:set>"
91: + "</g:propertyupdate>";
92:
93: System.Net.HttpWebRequest PROPPATCHRequest = (System.Net.HttpWebRequest)HttpWebRequest.Create(ServerName + "/exchange/" + Directory + "/Calendar/" + MeetingGUID.ToString() + ".eml");
94:
95: System.Net.CredentialCache MyCredentialCache = new System.Net.CredentialCache();
96: if (!string.IsNullOrEmpty(UserName) && !string.IsNullOrEmpty(Password))
97: {
98: MyCredentialCache.Add(new System.Uri(ServerName + "/exchange/" + Directory + "/Calendar/" + MeetingGUID.ToString() + ".eml"),
99: "NTLM",
100: new System.Net.NetworkCredential(UserName, Password));
101: }
102: else
103: {
104: MyCredentialCache.Add(new System.Uri(ServerName + "/exchange/" + Directory + "/Calendar/" + MeetingGUID.ToString() + ".eml"),
105: "Negotiate",
106: (System.Net.NetworkCredential)CredentialCache.DefaultCredentials);
107: }
108:
109: PROPPATCHRequest.Credentials = MyCredentialCache;
110: PROPPATCHRequest.Method = "PROPPATCH";
111: byte[] bytes = Encoding.UTF8.GetBytes((string)AppointmentRequest);
112: PROPPATCHRequest.ContentLength = bytes.Length;
113: using (System.IO.Stream PROPPATCHRequestStream = PROPPATCHRequest.GetRequestStream())
114: {
115: PROPPATCHRequestStream.Write(bytes, 0, bytes.Length);
116: PROPPATCHRequestStream.Close();
117: PROPPATCHRequest.ContentType = "text/xml";
118: System.Net.WebResponse PROPPATCHResponse = (System.Net.HttpWebResponse)PROPPATCHRequest.GetResponse();
119: PROPPATCHResponse.Close();
120: }
121: }
122:
123: /// <summary>
124: /// Emails an appointment to the specified users
125: /// </summary>
126: public virtual void EmailAppointment()
127: {
128: using (MailMessage Mail = new MailMessage())
129: {
130: System.Net.Mime.ContentType TextType = new System.Net.Mime.ContentType("text/plain");
131: using (AlternateView TextView = AlternateView.CreateAlternateViewFromString(GetText(), TextType))
132: {
133: System.Net.Mime.ContentType HTMLType = new System.Net.Mime.ContentType("text/html");
134: using (AlternateView HTMLView = AlternateView.CreateAlternateViewFromString(GetHTML(false), HTMLType))
135: {
136: System.Net.Mime.ContentType CalendarType = new System.Net.Mime.ContentType("text/calendar");
137: CalendarType.Parameters.Add("method", "REQUEST");
138: CalendarType.Parameters.Add("name", "meeting.ics");
139: using (AlternateView CalendarView = AlternateView.CreateAlternateViewFromString(GetCalendar(false), CalendarType))
140: {
141: CalendarView.TransferEncoding = System.Net.Mime.TransferEncoding.SevenBit;
142:
143: Mail.AlternateViews.Add(TextView);
144: Mail.AlternateViews.Add(HTMLView);
145: Mail.AlternateViews.Add(CalendarView);
146:
147: Mail.From = new MailAddress(OrganizerEmail);
148:
149: foreach (MailAddress attendee in AttendeeList)
150: {
151: Mail.To.Add(attendee);
152: }
153:
154:
155: Mail.Subject = Subject;
156:
157: foreach (Attachment Attachment in Attachments)
158: {
159: Mail.Attachments.Add(Attachment);
160: }
161:
162:
163: SmtpClient Server = new SmtpClient(ServerName, Port);
164: if (!string.IsNullOrEmpty(UserName) && !string.IsNullOrEmpty(Password))
165: {
166: Server.Credentials = new System.Net.NetworkCredential(UserName, Password);
167: }
168: if (AttendeeList.Count > 0)
169: {
170: Server.Send(Mail);
171: }
172: }
173: }
174: }
175: }
176: }
177:
178: /// <summary>
179: /// Sends a cancellation to the people specified
180: /// </summary>
181: public virtual void SendCancelEmails()
182: {
183: using (MailMessage Mail = new MailMessage())
184: {
185: System.Net.Mime.ContentType TextType = new System.Net.Mime.ContentType("text/plain");
186: using (AlternateView TextView = AlternateView.CreateAlternateViewFromString(GetText(), TextType))
187: {
188: System.Net.Mime.ContentType HTMLType = new System.Net.Mime.ContentType("text/html");
189: using (AlternateView HTMLView = AlternateView.CreateAlternateViewFromString(GetHTML(true), HTMLType))
190: {
191: System.Net.Mime.ContentType CalendarType = new System.Net.Mime.ContentType("text/calendar");
192: CalendarType.Parameters.Add("method", "CANCEL");
193: CalendarType.Parameters.Add("name", "meeting.ics");
194: using (AlternateView CalendarView = AlternateView.CreateAlternateViewFromString(GetCalendar(true), CalendarType))
195: {
196: CalendarView.TransferEncoding = System.Net.Mime.TransferEncoding.SevenBit;
197:
198: Mail.AlternateViews.Add(TextView);
199: Mail.AlternateViews.Add(HTMLView);
200: Mail.AlternateViews.Add(CalendarView);
201:
202: Mail.From = new MailAddress(OrganizerEmail);
203: foreach (MailAddress attendee in AttendeeList)
204: {
205: Mail.To.Add(attendee);
206: }
207: Mail.Subject = Subject + " - Cancelled";
208: foreach (Attachment Attachment in Attachments)
209: {
210: Mail.Attachments.Add(Attachment);
211: }
212: SmtpClient Server = new SmtpClient(ServerName, Port);
213: if (!string.IsNullOrEmpty(UserName) && !string.IsNullOrEmpty(Password))
214: {
215: Server.Credentials = new System.Net.NetworkCredential(UserName, Password);
216: }
217: if (AttendeeList.Count > 0)
218: {
219: Server.Send(Mail);
220: }
221: }
222: }
223: }
224: }
225: }
226:
227: /// <summary>
228: /// Cancels an appointment on someone's calendar
229: /// </summary>
230: public virtual void CancelAppointment()
231: {
232: System.Net.HttpWebRequest PROPPATCHRequest = (System.Net.HttpWebRequest)HttpWebRequest.Create(ServerName + "/exchange/" + Directory + "/Calendar/" + MeetingGUID.ToString() + ".eml");
233:
234: System.Net.CredentialCache MyCredentialCache = new System.Net.CredentialCache();
235: if (!string.IsNullOrEmpty(UserName) && !string.IsNullOrEmpty(Password))
236: {
237: MyCredentialCache.Add(new System.Uri(ServerName + "/exchange/" + Directory + "/Calendar/" + MeetingGUID.ToString() + ".eml"),
238: "NTLM",
239: new System.Net.NetworkCredential(UserName, Password));
240: }
241: else
242: {
243: MyCredentialCache.Add(new System.Uri(ServerName + "/exchange/" + Directory + "/Calendar/" + MeetingGUID.ToString() + ".eml"),
244: "Negotiate",
245: (System.Net.NetworkCredential)CredentialCache.DefaultCredentials);
246: }
247:
248: PROPPATCHRequest.Credentials = MyCredentialCache;
249: PROPPATCHRequest.Method = "DELETE";
250: System.Net.WebResponse PROPPATCHResponse = (System.Net.HttpWebResponse)PROPPATCHRequest.GetResponse();
251: PROPPATCHResponse.Close();
252: }
253:
254: #endregion
255:
256: #region Private Functions
257:
258: /// <summary>
259: /// Returns the text version of the appointment
260: /// </summary>
261: /// <returns>A text version of the appointment</returns>
262: private string GetText()
263: {
264: string Body = "Type:Single Meeting\n" +
265: "Organizer:" + OrganizerName + "\n" +
266: "Start Time:" + StartDate.ToLongDateString() + " " + StartDate.ToLongTimeString() + "\n" +
267: "End Time:" + EndDate.ToLongDateString() + " " + EndDate.ToLongTimeString() + "\n" +
268: "Time Zone:" + System.TimeZone.CurrentTimeZone.StandardName + "\n" +
269: "Location: " + Location + "\n\n" +
270: "*~*~*~*~*~*~*~*~*~*\n\n" +
271: Summary;
272: return Body;
273: }
274:
275: /// <summary>
276: /// Gets an HTML version of the appointment
277: /// </summary>
278: /// <param name="Canceled">If true it returns a cancellation, false it returns a request</param>
279: /// <returns>An HTML version of the appointment</returns>
280: private string GetHTML(bool Canceled)
281: {
282: string bodyHTML = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\r\n<HTML>\r\n<HEAD>\r\n<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=utf-8\">\r\n<META NAME=\"Generator\" CONTENT=\"MS Exchange Server version 6.5.7652.24\">\r\n<TITLE>{0}</TITLE>\r\n</HEAD>\r\n<BODY>\r\n<!-- Converted from text/plain format -->\r\n<P><FONT SIZE=2>Type:Single Meeting<BR>\r\nOrganizer:{1}<BR>\r\nStart Time:{2}<BR>\r\nEnd Time:{3}<BR>\r\nTime Zone:{4}<BR>\r\nLocation:{5}<BR>\r\n<BR>\r\n*~*~*~*~*~*~*~*~*~*<BR>\r\n<BR>\r\n{6}<BR>\r\n</FONT>\r\n</P>\r\n\r\n</BODY>\r\n</HTML>";
283: string TempSummary = Summary;
284: if (Canceled)
285: {
286: TempSummary += " - Canceled";
287: }
288: return string.Format(bodyHTML,
289: TempSummary,
290: OrganizerName,
291: StartDate.ToLongDateString() + " " + StartDate.ToLongTimeString(),
292: EndDate.ToLongDateString() + " " + EndDate.ToLongTimeString(),
293: System.TimeZone.CurrentTimeZone.StandardName,
294: Location,
295: TempSummary);
296: }
297:
298: /// <summary>
299: /// Gets an iCalendar version of the appointment
300: /// </summary>
301: /// <param name="Canceled">If true, it returns a cancellation version.
302: /// If false, it returns a request version.</param>
303: /// <returns>An iCalendar version of the appointment</returns>
304: private string GetCalendar(bool Canceled)
305: {
306: string DateFormatUsing = "yyyyMMddTHHmmssZ";
307: string Method;
308: if (!Canceled)
309: Method = "REQUEST";
310: else
311: Method = "CANCEL";
312: string bodyCalendar = "BEGIN:VCALENDAR\r\nMETHOD:{10}\r\nPRODID:Microsoft CDO for Microsoft Exchange\r\nVERSION:2.0\r\nBEGIN:VTIMEZONE\r\nTZID:(GMT-06.00) Central Time (US & Canada)\r\nX-MICROSOFT-CDO-TZID:11\r\nBEGIN:STANDARD\r\nDTSTART:16010101T020000\r\nTZOFFSETFROM:-0500\r\nTZOFFSETTO:-0600\r\nRRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=11;BYDAY=1SU\r\nEND:STANDARD\r\nBEGIN:DAYLIGHT\r\nDTSTART:16010101T020000\r\nTZOFFSETFROM:-0600\r\nTZOFFSETTO:-0500\r\nRRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=2SU\r\nEND:DAYLIGHT\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nDTSTAMP:{8}\r\nDTSTART:{0}\r\nSUMMARY:{7}\r\nUID:{5}\r\nATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=\"{9}\":MAILTO:{9}\r\nACTION;RSVP=TRUE;CN=\"{4}\":MAILTO:{4}\r\nORGANIZER;CN=\"{3}\":mailto:{4}\r\nLOCATION:{2}\r\nDTEND:{1}\r\nDESCRIPTION:{7}\\N\r\nSEQUENCE:1\r\nPRIORITY:5\r\nCLASS:\r\nCREATED:{8}\r\nLAST-MODIFIED:{8}\r\nSTATUS:CONFIRMED\r\nTRANSP:OPAQUE\r\nX-MICROSOFT-CDO-BUSYSTATUS:BUSY\r\nX-MICROSOFT-CDO-INSTTYPE:0\r\nX-MICROSOFT-CDO-INTENDEDSTATUS:BUSY\r\nX-MICROSOFT-CDO-ALLDAYEVENT:FALSE\r\nX-MICROSOFT-CDO-IMPORTANCE:1\r\nX-MICROSOFT-CDO-OWNERAPPTID:-1\r\nX-MICROSOFT-CDO-ATTENDEE-CRITICAL-CHANGE:{8}\r\nX-MICROSOFT-CDO-OWNER-CRITICAL-CHANGE:{8}\r\nBEGIN:VALARM\r\nACTION:DISPLAY\r\nDESCRIPTION:REMINDER\r\nTRIGGER;RELATED=START:-PT00H15M00S\r\nEND:VALARM\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
313: bodyCalendar = string.Format(bodyCalendar,
314: StartDate.ToUniversalTime().ToString(DateFormatUsing),
315: EndDate.ToUniversalTime().ToString(DateFormatUsing),
316: Location,
317: OrganizerName,
318: OrganizerEmail,
319: MeetingGUID.ToString("B"),
320: Summary,
321: Subject,
322: DateTime.Now.ToUniversalTime().ToString(DateFormatUsing),
323: AttendeeList.ToString(),
324: Method);
325: return bodyCalendar;
326: }
327:
328: #endregion
329:
330: #region Variables
331: public DateTime StartDate;
332: public DateTime EndDate;
333: public string Subject;
334: public string Summary;
335: public string Location;
336: public MailAddressCollection AttendeeList;
337: public string OrganizerName;
338: public string OrganizerEmail;
339:
340: public List<Attachment> Attachments;
341:
342: public Guid MeetingGUID;
343:
344: public string Directory;
345: public string ServerName;
346: public string UserName;
347: public string Password;
348: public int Port;
349:
350: #endregion
351: }
352: }