viewpoint 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. data/COPYING.txt +674 -0
  2. data/README +60 -0
  3. data/Rakefile +39 -0
  4. data/examples/cal2ical.rb +29 -0
  5. data/examples/ews_fusefs.rb +364 -0
  6. data/examples/webclient/.gems +2 -0
  7. data/examples/webclient/README +1 -0
  8. data/examples/webclient/config.ru +26 -0
  9. data/examples/webclient/ews_access_handler.rb +42 -0
  10. data/examples/webclient/public/css/mail.css +33 -0
  11. data/examples/webclient/public/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  12. data/examples/webclient/public/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  13. data/examples/webclient/public/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  14. data/examples/webclient/public/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  15. data/examples/webclient/public/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  16. data/examples/webclient/public/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  17. data/examples/webclient/public/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  18. data/examples/webclient/public/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  19. data/examples/webclient/public/css/smoothness/images/ui-icons_222222_256x240.png +0 -0
  20. data/examples/webclient/public/css/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  21. data/examples/webclient/public/css/smoothness/images/ui-icons_454545_256x240.png +0 -0
  22. data/examples/webclient/public/css/smoothness/images/ui-icons_888888_256x240.png +0 -0
  23. data/examples/webclient/public/css/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  24. data/examples/webclient/public/css/smoothness/jquery-ui-1.7.2.custom.css +406 -0
  25. data/examples/webclient/public/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  26. data/examples/webclient/public/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  27. data/examples/webclient/public/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png +0 -0
  28. data/examples/webclient/public/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  29. data/examples/webclient/public/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  30. data/examples/webclient/public/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  31. data/examples/webclient/public/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  32. data/examples/webclient/public/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  33. data/examples/webclient/public/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  34. data/examples/webclient/public/css/ui-lightness/images/ui-icons_222222_256x240.png +0 -0
  35. data/examples/webclient/public/css/ui-lightness/images/ui-icons_228ef1_256x240.png +0 -0
  36. data/examples/webclient/public/css/ui-lightness/images/ui-icons_ef8c08_256x240.png +0 -0
  37. data/examples/webclient/public/css/ui-lightness/images/ui-icons_ffd27a_256x240.png +0 -0
  38. data/examples/webclient/public/css/ui-lightness/images/ui-icons_ffffff_256x240.png +0 -0
  39. data/examples/webclient/public/css/ui-lightness/jquery-ui-1.7.2.custom.css +406 -0
  40. data/examples/webclient/public/js/jquery-1.3.2.min.js +19 -0
  41. data/examples/webclient/public/js/jquery-ui-1.7.2.custom.min.js +145 -0
  42. data/examples/webclient/viewpoint_web.rb +295 -0
  43. data/examples/webclient/views/_footer.haml +1 -0
  44. data/examples/webclient/views/_header.haml +11 -0
  45. data/examples/webclient/views/_tagline.haml +1 -0
  46. data/examples/webclient/views/index.haml +20 -0
  47. data/examples/webclient/views/layout.haml +42 -0
  48. data/examples/webclient/views/mail.haml +45 -0
  49. data/examples/webclient/views/welcome.haml +0 -0
  50. data/lib/calendar.rb +121 -0
  51. data/lib/calendar_item.rb +110 -0
  52. data/lib/event.rb +0 -0
  53. data/lib/exchange_headers.rb +50 -0
  54. data/lib/exchwebserv.rb +228 -0
  55. data/lib/folder.rb +269 -0
  56. data/lib/item.rb +56 -0
  57. data/lib/mail.rb +118 -0
  58. data/lib/message.rb +134 -0
  59. data/lib/soap/viewpoint.conf +3 -0
  60. data/lib/task.rb +0 -0
  61. data/lib/tasks.rb +0 -0
  62. data/lib/viewpoint.rb +2 -0
  63. data/lib/wsdl/defaultMappingRegistry.rb +10680 -0
  64. data/lib/wsdl/exchangeServiceBinding.rb +349 -0
  65. data/lib/wsdl/exchangeServiceTypes.rb +11013 -0
  66. data/preamble +19 -0
  67. data/test/spec/authtest_spec.rb +12 -0
  68. data/test/spec/findfolders_spec.rb +14 -0
  69. data/test/test_client.rb +12 -0
  70. data/test/testrestrict.rb +75 -0
  71. metadata +130 -0
@@ -0,0 +1 @@
1
+ %h3 The Outlook Webmail Alternative
@@ -0,0 +1,20 @@
1
+ -#############################################################################
2
+ -# Copyright © 2009 Dan Wanek <dan.wanek@gmail.com>
3
+ -#
4
+ -#
5
+ -# This file is part of Viewpoint.
6
+ -#
7
+ -# Viewpoint is free software: you can redistribute it and/or
8
+ -# modify it under the terms of the GNU General Public License as published
9
+ -# by the Free Software Foundation, either version 3 of the License, or (at
10
+ -# your option) any later version.
11
+ -#
12
+ -# Viewpoint is distributed in the hope that it will be useful,
13
+ -# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
+ -# Public License for more details.
16
+ -#
17
+ -# You should have received a copy of the GNU General Public License along
18
+ -# with Viewpoint. If not, see <http://www.gnu.org/licenses/>.
19
+ -#############################################################################
20
+ %h2 Viewpoint Web Client
@@ -0,0 +1,42 @@
1
+ -#############################################################################
2
+ -# Copyright © 2009 Dan Wanek <dwanek@nd.gov>
3
+ -#
4
+ -#
5
+ -# This file is part of CommentMonster.
6
+ -#
7
+ -# CommentMonster is free software: you can redistribute it and/or
8
+ -# modify it under the terms of the GNU General Public License as published
9
+ -# by the Free Software Foundation, either version 3 of the License, or (at
10
+ -# your option) any later version.
11
+ -#
12
+ -# CommentMonster is distributed in the hope that it will be useful,
13
+ -# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
+ -# Public License for more details.
16
+ -#
17
+ -# You should have received a copy of the GNU General Public License along
18
+ -# with CommentMonster. If not, see <http://www.gnu.org/licenses/>.
19
+ -#############################################################################
20
+ !!! XML
21
+ !!!
22
+ %html
23
+ %head
24
+ %link{:rel => "stylesheet", :href => "css/ui-lightness/jquery-ui-1.7.2.custom.css", :type => "text/css"}
25
+ %link{:rel => "stylesheet", :href => "css/mail.css", :type => "text/css"}
26
+ %script{:type => "text/javascript", :src => "js/jquery-1.3.2.min.js"}
27
+ %script{:type => "text/javascript", :src => "js/jquery-ui-1.7.2.custom.min.js"}
28
+ :javascript
29
+ $(function(){
30
+ $('#tabs').tabs();
31
+ });
32
+
33
+ %body
34
+ #page
35
+ #header
36
+ = partial :_header
37
+ #tagline
38
+ = partial :_tagline
39
+ #content
40
+ = yield
41
+ #footer
42
+ = partial :_footer
@@ -0,0 +1,45 @@
1
+ -#############################################################################
2
+ -# Copyright © 2009 Dan Wanek <dan.wanek@gmail.com>
3
+ -#
4
+ -#
5
+ -# This file is part of Viewpoint.
6
+ -#
7
+ -# Viewpoint is free software: you can redistribute it and/or
8
+ -# modify it under the terms of the GNU General Public License as published
9
+ -# by the Free Software Foundation, either version 3 of the License, or (at
10
+ -# your option) any later version.
11
+ -#
12
+ -# Viewpoint is distributed in the hope that it will be useful,
13
+ -# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
+ -# Public License for more details.
16
+ -#
17
+ -# You should have received a copy of the GNU General Public License along
18
+ -# with Viewpoint. If not, see <http://www.gnu.org/licenses/>.
19
+ -#############################################################################
20
+ %html
21
+ %head
22
+ %link{:rel => "stylesheet", :href => "css/ui-lightness/jquery-ui-1.7.2.custom.css", :type => "text/css"}
23
+ %link{:rel => "stylesheet", :href => "css/mail.css", :type => "text/css"}
24
+ %script{:type => "text/javascript", :src => "js/jquery-1.3.2.min.js"}
25
+ %script{:type => "text/javascript", :src => "js/jquery-ui-1.7.2.custom.min.js"}
26
+ :javascript
27
+ $(function(){
28
+ $(".msg_header:even").addClass("even_msg");
29
+ $(".msg_header:odd").addClass("odd_msg");
30
+ $(".msg_header").click(function(e) {
31
+ //var txt = $(this).children(".msg_id").attr('id');
32
+ var txt = $(this).children('input').attr('value');
33
+ $("#msg_body").load("/msg", { msg_id: txt } );
34
+ });
35
+ });
36
+
37
+ %body
38
+ %div#msg_body
39
+ - msgs.each do |msg|
40
+ %div.msg_header
41
+ %input{:type => 'hidden', :id => 'msg_id', :value => msg.item_id}
42
+ %div.sender= msg.sender
43
+ %div.subject= msg.subject
44
+ %div.date= msg.date_time_recieved.strftime('%b %y')
45
+ %div.msg_body
File without changes
@@ -0,0 +1,121 @@
1
+ #############################################################################
2
+ # Copyright © 2009 Dan Wanek <dan.wanek@gmail.com>
3
+ #
4
+ #
5
+ # This file is part of Viewpoint.
6
+ #
7
+ # Viewpoint is free software: you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or (at
10
+ # your option) any later version.
11
+ #
12
+ # Viewpoint is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
+ # Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License along
18
+ # with Viewpoint. If not, see <http://www.gnu.org/licenses/>.
19
+ #############################################################################
20
+ $:.unshift(File.dirname(__FILE__))
21
+ require 'rubygems'
22
+ require 'icalendar'
23
+ require 'wsdl/exchangeServiceBinding'
24
+ require 'viewpoint'
25
+ # --- Folder Types ---
26
+ require 'folder'
27
+ # --- Item Types ---
28
+ require 'calendar_item'
29
+ #require 'meeting_request'
30
+
31
+
32
+
33
+ class Viewpoint::CalendarFolder < Viewpoint::Folder
34
+ include Viewpoint
35
+
36
+ # initialize with an item of CalendarFolderType
37
+ def initialize(folder)
38
+ # Call initialize in the
39
+ super(folder)
40
+ end
41
+
42
+ def get_todays_events
43
+ get_events(DateTime.parse(Date.today.to_s), DateTime.parse(Date.today.next.to_s))
44
+ end
45
+
46
+ def get_weeks_events
47
+ start_date = Date.today
48
+ end_date = (start_date + ( 6 - start_date.wday))
49
+
50
+ get_events(DateTime.parse(start_date.to_s), DateTime.parse(end_date.to_s))
51
+ end
52
+
53
+ # Get events between a certain time period. Defaults to "today"
54
+ # Input: DateTime of start, DateTime of end
55
+ # You can get a year of events by passing 'nil, nil', but you should really consider using synchronization then.
56
+ # This method will return an Array of CalendarItem
57
+ def get_events(start_time = (DateTime.parse(Date.today.to_s)), end_time = (DateTime.parse(Date.today.next.to_s)))
58
+ find_item_t = FindItemType.new
59
+ find_item_t.xmlattr_Traversal = ItemQueryTraversalType::Shallow
60
+ item_shape = ItemResponseShapeType.new(DefaultShapeNamesType::Default, false)
61
+
62
+ find_item_t.itemShape = item_shape
63
+
64
+ folder_ids = NonEmptyArrayOfBaseFolderIdsType.new()
65
+ dist_folder = DistinguishedFolderIdType.new
66
+ dist_folder.xmlattr_Id = DistinguishedFolderIdNameType.new(@display_name.downcase)
67
+ folder_ids.distinguishedFolderId = dist_folder
68
+ find_item_t.parentFolderIds = folder_ids
69
+
70
+ # If we pass nil, force times to be:
71
+ # start: 1 years ago
72
+ # end: 1 years from now
73
+ cal_span = CalendarViewType.new
74
+ if ( start_time == nil or end_time == nil)
75
+ cal_span.xmlattr_StartDate = DateTime.parse((Date.today - 365).to_s).to_s
76
+ cal_span.xmlattr_EndDate = DateTime.parse((Date.today + 365).to_s).to_s
77
+ find_item_t.calendarView = cal_span
78
+ else
79
+ cal_span.xmlattr_StartDate = start_time.to_s
80
+ cal_span.xmlattr_EndDate = end_time.to_s
81
+ find_item_t.calendarView = cal_span
82
+ end
83
+
84
+ resp = find_items(find_item_t)
85
+ if resp != nil
86
+ cal_items = []
87
+
88
+ resp.rootFolder.items.calendarItem.each do |cali|
89
+ cal_items << CalendarItem.new(cali, self)
90
+ end
91
+ # TODO: Handle the following types of Calendar data
92
+ # meetingMessage
93
+ # meetingRequest
94
+ # meetingResponse
95
+ # meetingCancellation
96
+
97
+ return cal_items
98
+ else
99
+ return resp # return nil
100
+ end
101
+ end
102
+
103
+
104
+ # Returns an Icalendar::Calendar object
105
+ # Input: DateTime of start, DateTime of end
106
+ def to_ical(dtstart = nil, dtend = nil)
107
+ ical = Icalendar::Calendar.new
108
+ events = get_events(dtstart, dtend)
109
+ events.each do |ev|
110
+ ical.add_event(ev.to_ical_event)
111
+ end
112
+ return ical
113
+ end
114
+
115
+
116
+ # See docs for Folder::get_item
117
+ def get_item(item_id)
118
+ cali = super(item_id, "calendarItem", true)
119
+ end
120
+ end
121
+
@@ -0,0 +1,110 @@
1
+ #############################################################################
2
+ # Copyright © 2009 Dan Wanek <dan.wanek@gmail.com>
3
+ #
4
+ #
5
+ # This file is part of Viewpoint.
6
+ #
7
+ # Viewpoint is free software: you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or (at
10
+ # your option) any later version.
11
+ #
12
+ # Viewpoint is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
+ # Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License along
18
+ # with Viewpoint. If not, see <http://www.gnu.org/licenses/>.
19
+ #############################################################################
20
+ $:.unshift(File.dirname(__FILE__))
21
+ require 'rubygems'
22
+ require 'icalendar'
23
+ require 'viewpoint'
24
+ require 'item'
25
+
26
+ class Viewpoint::CalendarItem < Viewpoint::Item
27
+ include Viewpoint
28
+
29
+ attr_reader :subject, :parent_folder, :sender
30
+ attr_reader :ews_item if $DEBUG
31
+
32
+ # Initialize an Exchange Web Services item
33
+ def initialize(ews_item, parent_folder)
34
+ # keep this now for debuging
35
+ @ews_item = ews_item if $DEBUG
36
+ @subject = ews_item.subject # String
37
+ @location = ews_item.location if ews_item.location
38
+ @cal_item_type = ews_item.calendarItemType
39
+ @organizer = ews_item.organizer.mailbox.name if ews_item.organizer and ews_item.organizer.mailbox # String
40
+ @start = ews_item.start if ews_item.start # DateTime
41
+ @end = ews_item.v_end if ews_item.v_end # DateTime
42
+ @busy_status = ews_item.legacyFreeBusyStatus if ews_item.legacyFreeBusyStatus # String
43
+ @date_time_recieved = ews_item.dateTimeReceived if ews_item.dateTimeReceived # DateTime
44
+
45
+ # This is where the event object gets loaded if it
46
+ # is requested. Think of it like IMAP downloading the
47
+ # body when the message is viewed.
48
+ @message = nil
49
+
50
+ super(ews_item, parent_folder)
51
+ end
52
+
53
+
54
+ # Convert item to iCal format: http://www.ietf.org/rfc/rfc2445.txt
55
+ # Returns Icalendar::Event object
56
+ def to_ical_event
57
+ get_calitem if @message == nil
58
+ iev = Icalendar::Event.new
59
+ iev.uid = @message.uID
60
+ unless(@message.organizer == nil)
61
+ iev.organizer = "CN=\"#{@message.organizer.mailbox.name}\"" if @message.organizer.mailbox.name
62
+ iev.organizer += ":MAILTO:#{@message.organizer.mailbox.emailAddress}" if @message.organizer.mailbox.emailAddress
63
+ end
64
+ # TODO: Handle EWS Timezones better. TZ_HASH in viewpoint.rb is the start of this
65
+ require 'time'
66
+ #tzoffset = @message.timeZone.sub(/^\(GMT([^\)]+)\).*$/,'\1')
67
+ dtstart = Time.at(@message.start.strftime('%s').to_i)
68
+ dtend = Time.at(@message.v_end.strftime('%s').to_i)
69
+ dtstamp = Time.at(@message.dateTimeStamp.strftime('%s').to_i)
70
+ last_modified = Time.at(@message.lastModifiedTime.strftime('%s').to_i)
71
+ timestr = "%Y%m%dT%H%M%S"
72
+ iev.dtstart = dtstart.strftime(timestr)
73
+ iev.dtend = dtend.strftime(timestr)
74
+ iev.tzid = dtstart.strftime('%Z')
75
+ iev.dtstamp = dtstamp.strftime(timestr)
76
+ iev.last_modified = last_modified.strftime(timestr)
77
+ iev.location = @message.location if @message.location
78
+ iev.klass = @message.sensitivity if @message.sensitivity
79
+ iev.summary = @message.subject if @message.subject
80
+ iev.description = @message.body if @message.body
81
+ iev.transp = @message.legacyFreeBusyStatus if @message.legacyFreeBusyStatus
82
+ iev.duration = @message.duration if @message.duration
83
+ iev.sequence = @message.appointmentSequenceNumber if @message.appointmentSequenceNumber
84
+ iev.status = @message.myResponseType if @message.myResponseType
85
+ #iev.attach @message.attachments if @message.hasAttachments
86
+ @message.requiredAttendees.each do |pers|
87
+ output = "ROLE=REQ-PARTICIPANT;CN=\"#{pers.mailbox.name}\"" if pers.mailbox.respond_to?(:name)
88
+ output += ":MAILTO:#{pers.mailbox.emailAddress}" if pers.mailbox.respond_to?(:emailAddress)
89
+ iev.attendees << output if output
90
+ end if @message.requiredAttendees
91
+ @message.optionalAttendees.each do |pers|
92
+ output = "ROLE=OPT-PARTICIPANT;CN=\"#{pers.mailbox.name}\"" if pers.mailbox.respond_to?(:name)
93
+ output += ":MAILTO:#{pers.mailbox.emailAddress}" if pers.mailbox.respond_to?(:emailAddress)
94
+ iev.attendees << output if output
95
+ end if @message.optionalAttendees
96
+
97
+ #iev.categories = @message.categories if @message.categories
98
+ #iev.resources = @message.resources if @message.resources
99
+ return iev
100
+ end
101
+
102
+
103
+ # These methods are marked 'private' because they return EWS Types and I am trying to
104
+ # hide those because they are not elegant and a bit too tedious for the public interface.
105
+ private
106
+
107
+ def get_calitem
108
+ @message = @parent_folder.get_item(@item_id)
109
+ end
110
+ end
File without changes
@@ -0,0 +1,50 @@
1
+ #############################################################################
2
+ # Copyright © 2009 Dan Wanek <dan.wanek@gmail.com>
3
+ #
4
+ #
5
+ # This file is part of Viewpoint.
6
+ #
7
+ # Viewpoint is free software: you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or (at
10
+ # your option) any later version.
11
+ #
12
+ # Viewpoint is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
+ # Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License along
18
+ # with Viewpoint. If not, see <http://www.gnu.org/licenses/>.
19
+ #############################################################################
20
+ require 'rubygems'
21
+ gem 'soap4r'
22
+ require 'soap/header/simplehandler'
23
+ require 'viewpoint'
24
+
25
+
26
+ # Some functionality of EWS depends on setting the "RequestServerVersion"
27
+ # element with a specified "Version" attribute. That is all this class
28
+ # is doing.
29
+ # It is used during the initial connection process by setting the
30
+ # headerhandler in the serivce binding.
31
+ # *<tt>@exchange.headerhandler << ExchangeHeaders.new</tt>
32
+ class Viewpoint::ExchangeHeaders < SOAP::Header::SimpleHandler
33
+ NAMESPACE = 'http://schemas.microsoft.com/exchange/services/2006/types'
34
+
35
+ def initialize
36
+ @qname = XSD::QName.new(NAMESPACE,"RequestServerVersion")
37
+ super(@qname)
38
+ end
39
+
40
+ def on_simple_outbound
41
+ {
42
+ "xmlattr_Version" => "Exchange2007_SP1"
43
+ }
44
+ end
45
+
46
+ def on_outbound
47
+ h = on_simple_outbound
48
+ h ? SOAP::SOAPElement.from_obj(h, nil) : nil
49
+ end
50
+ end
@@ -0,0 +1,228 @@
1
+ #############################################################################
2
+ # Copyright © 2009 Dan Wanek <dan.wanek@gmail.com>
3
+ #
4
+ #
5
+ # This file is part of Viewpoint.
6
+ #
7
+ # Viewpoint is free software: you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or (at
10
+ # your option) any later version.
11
+ #
12
+ # Viewpoint is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
+ # Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License along
18
+ # with Viewpoint. If not, see <http://www.gnu.org/licenses/>.
19
+ #############################################################################
20
+ $:.unshift(File.dirname(__FILE__))
21
+ require 'rubygems'
22
+ #require 'highline/import'
23
+ require 'singleton'
24
+ require 'wsdl/exchangeServiceBinding'
25
+ # --- Custom libs ---
26
+ require 'exchange_headers'
27
+ # --- Folder Types ---
28
+ require 'folder'
29
+ require 'calendar'
30
+ require 'mail'
31
+
32
+ require 'viewpoint'
33
+
34
+ # This class will act as the controller for a client connection to the
35
+ # Exchange web service.
36
+ class Viewpoint::ExchWebServ
37
+ include Viewpoint
38
+ include Singleton
39
+
40
+ attr_reader :ews, :user, :authenticated
41
+
42
+ def initialize
43
+ @authenticated = false
44
+ @user = nil
45
+ @pass = nil
46
+ @ews_endpoint = nil
47
+
48
+ # Connection to Exchange web services.
49
+ # You can get fetch this from an accessor later.
50
+ @ews = nil
51
+
52
+ # Stores folders returned from 'find_folders'
53
+ @folders = {}
54
+
55
+ # Do initial authentication
56
+ #do_auth
57
+ end
58
+
59
+
60
+ # Finds all folders and returns a hash of FolderTypes
61
+ def find_folders
62
+ ff = FindFolderType.new()
63
+ ff.xmlattr_Traversal = FolderQueryTraversalType::Deep
64
+ ff.folderShape = FolderResponseShapeType.new( DefaultShapeNamesType::AllProperties )
65
+ fid = NonEmptyArrayOfBaseFolderIdsType.new()
66
+ fidt = DistinguishedFolderIdType.new
67
+ fidt.xmlattr_Id = DistinguishedFolderIdNameType::Root
68
+ fid.distinguishedFolderId = fidt
69
+ ff.parentFolderIds = fid
70
+
71
+ # FindFolderResponseType
72
+ resp = @ews.findFolder(ff)
73
+
74
+ # ArrayOfResponseMessagesType
75
+ msgs = resp.responseMessages
76
+
77
+ # Array of FindFolderResponseMessageType
78
+ msgs.findFolderResponseMessage.each do |elem|
79
+ # Mail Folders
80
+ mail_folders = {}
81
+ elem.rootFolder.folders.folder.each do |folder|
82
+ if( folder.folderClass != nil)
83
+ mail_folders[folder.displayName] = MailFolder.new(folder)
84
+ end
85
+ end
86
+ @folders['MailFolder'] = mail_folders
87
+
88
+ # CalendarFolderType
89
+ cal_folders = {}
90
+ elem.rootFolder.folders.calendarFolder.each do |folder|
91
+ cal_folders[folder.displayName] = CalendarFolder.new(folder)
92
+ end
93
+ @folders['CalendarFolder'] = cal_folders
94
+
95
+ #elem.rootFolder.folders.contactsFolder.each do |folder|
96
+ #end
97
+ #elem.rootFolder.folders.searchFolder.each do |folder|
98
+ #end
99
+ #elem.rootFolder.folders.tasksFolder.each do |folder|
100
+ #end
101
+ end
102
+ return @folders
103
+ end
104
+
105
+ # Return folder
106
+ # The default is to return a folder that is a subclass of Folder from the
107
+ # SqliteDB, but if fetch_from_ews is set to true it will go out and return
108
+ # the FolderType object from EWS.
109
+ # Parameters:
110
+ # folder_ids: NonEmptyArrayOfBaseFolderIdsType or String if fetch_from_ews is not set
111
+ # fetch_from_ews: boolean
112
+ # folder_shape: DefaultShapeNamesType
113
+ def get_folder(folder_ids, fetch_from_ews = false, folder_shape = DefaultShapeNamesType::AllProperties)
114
+ unless fetch_from_ews
115
+ return @folders['MailFolder'][folder_ids] ||
116
+ @folders['CalendarFolder'][folder_ids]
117
+ end
118
+
119
+ folder_shape = FolderResponseShapeType.new( folder_shape )
120
+ get_folder = GetFolderType.new(folder_shape, folder_ids)
121
+
122
+ resp = @ews.getFolder(get_folder).responseMessages.getFolderResponseMessage[0].folders.folder[0]
123
+ end
124
+
125
+ # Parameters:
126
+ # display_name: String
127
+ # fetch_from_ews: boolean
128
+ # folder_shape: DefaultShapeNamesType
129
+ def get_folder_by_name(display_name, fetch_from_ews = true, folder_shape = DefaultShapeNamesType::AllProperties)
130
+ #folder_ids = NonEmptyArrayOfBaseFolderIdsType.new()
131
+ dist_name = DistinguishedFolderIdType.new
132
+ dist_name.xmlattr_Id = DistinguishedFolderIdNameType.new(display_name.downcase)
133
+ #folder_ids.distinguishedFolderId = dist_name
134
+ folder_ids = NonEmptyArrayOfBaseFolderIdsType.new(nil, [dist_name])
135
+
136
+ get_folder(folder_ids, true, folder_shape)
137
+ end
138
+
139
+ # Parameters:
140
+ # folder_id: String
141
+ # change_key: String
142
+ # folder_shape: DefaultShapeNamesType
143
+ def get_folder_by_id(folder_id, change_key = nil, fetch_from_ews = true, folder_shape = DefaultShapeNamesType::AllProperties)
144
+ folder_ids = NonEmptyArrayOfBaseFolderIdsType.new()
145
+ folder_id_t = FolderIdType.new
146
+ folder_id_t.xmlattr_Id = folder_id
147
+ folder_id_t.xmlattr_ChangeKey = change_key unless change_key == nil
148
+ folder_ids.folderId = folder_id
149
+ folder_ids = NonEmptyArrayOfBaseFolderIdsType.new([folder_id_t], nil)
150
+
151
+ get_folder(folder_ids, true, folder_shape)
152
+ end
153
+
154
+ def get_mail_folders
155
+ return @folders['MailFolder']
156
+ end
157
+
158
+ def get_calendar_folders
159
+ return @folders['CalendarFolder']
160
+ end
161
+
162
+ def authenticate(user = nil, pass = nil, endpoint = nil)
163
+ unless @authenticated
164
+ @user = user
165
+ @pass = pass
166
+ @ews_endpoint = endpoint
167
+ do_auth unless @authenticated
168
+ end
169
+ @authenticated
170
+ end
171
+
172
+ private
173
+ def do_auth
174
+ retry_count = 0
175
+ begin
176
+ if( File.exists?("#{ENV['HOME']}/.viewpointrc") )
177
+ props = SOAP::Property.load(File.new("#{ENV['HOME']}/.viewpointrc"))
178
+ @user = props['exchange.ews.user'] if @user == nil
179
+ @pass = props['exchange.ews.pass'] if @pass == nil
180
+ @ews_endpoint = props['exchange.ews.endpoint'] if @ews_endpoint == nil
181
+ elsif( File.exists?("#{File.dirname(__FILE__)}/soap/viewpoint.conf") )
182
+ props = SOAP::Property.load(File.new("#{File.dirname(__FILE__)}/soap/viewpoint.conf"))
183
+ @user = props['exchange.ews.user'] if @user == nil
184
+ @pass = props['exchange.ews.pass'] if @pass == nil
185
+ @ews_endpoint = props['exchange.ews.endpoint'] if @ews_endpoint == nil
186
+ elsif(@ews_endpoint == nil && @user == nil && @pass == nil)
187
+ @ews_endpoint = ask("Exchange EWS Endpoint: ") { |q| q.echo = true }
188
+ @user = ask("User: ") { |q| q.echo = true }
189
+ @pass = ask("Pass: ") { |q| q.echo = "*"}
190
+ else
191
+ # Nothing to do
192
+ end
193
+
194
+ @ews = ExchangeServiceBinding.new(@ews_endpoint)
195
+ @ews.options["protocol.http.auth.ntlm"] = [@ews_endpoint.sub(/\/[^\/]+$/,'/'),@user,@pass]
196
+ @ews.headerhandler << ExchangeHeaders.new
197
+
198
+ # Log SOAP request and response for debugging. Run ruby with the '-d' option.
199
+ if($DEBUG) then
200
+ @ews.wiredump_file_base = "viewpoint-soaplog"
201
+ end
202
+
203
+ # Do a ResolveNames operation to make sure that authentication works.
204
+ # If you don't do an operation, you won't find out that bad credentials
205
+ # were entered until later. The ResolveNames operation is completely
206
+ # arbitrary and could be any EWS call.
207
+ # http://msdn.microsoft.com/en-us/library/bb409286.aspx
208
+ rnt = ResolveNamesType.new(nil,@user)
209
+ rnt.xmlattr_ReturnFullContactData = false
210
+ ews.resolveNames(rnt)
211
+
212
+ rescue SOAP::HTTPStreamError
213
+ puts "Bad Login! Try Again."
214
+ if( retry_count < 2)
215
+ retry_count += 1
216
+ retry
217
+ else
218
+ puts "-----------------------------------------------------------"
219
+ puts "Could not log into Exchange Web Services. Make sure your information is correct"
220
+ puts "End Point: #{@ews_endpoint}"
221
+ puts "User: #{@user}"
222
+ puts "-----------------------------------------------------------"
223
+ return
224
+ end
225
+ end
226
+ @authenticated = true
227
+ end
228
+ end