viewpoint 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/README +84 -45
  2. data/Rakefile +7 -5
  3. data/TODO +6 -0
  4. data/VERSION +1 -1
  5. data/lib/exceptions/exceptions.rb +34 -0
  6. data/lib/extensions/string.rb +37 -0
  7. data/lib/model/calendar_folder.rb +45 -0
  8. data/lib/model/calendar_item.rb +145 -0
  9. data/lib/model/contact.rb +58 -0
  10. data/lib/model/contacts_folder.rb +35 -0
  11. data/lib/model/distribution_list.rb +26 -0
  12. data/lib/model/event.rb +118 -0
  13. data/lib/model/folder.rb +36 -0
  14. data/lib/model/generic_folder.rb +302 -0
  15. data/lib/model/item.rb +126 -0
  16. data/lib/model/mailbox_user.rb +107 -0
  17. data/lib/model/meeting_cancellation.rb +28 -0
  18. data/lib/{viewpoint/errors.rb → model/meeting_message.rb} +10 -14
  19. data/lib/model/meeting_request.rb +26 -0
  20. data/lib/model/meeting_response.rb +26 -0
  21. data/lib/model/message.rb +73 -0
  22. data/lib/model/model.rb +161 -0
  23. data/lib/model/search_folder.rb +36 -0
  24. data/lib/model/task.rb +62 -0
  25. data/lib/model/task_list.rb +19 -0
  26. data/lib/model/tasks_folder.rb +33 -0
  27. data/lib/soap/handsoap/builder.rb +22 -0
  28. data/lib/soap/handsoap/builders/ews_build_helpers.rb +317 -0
  29. data/lib/soap/handsoap/builders/ews_builder.rb +86 -0
  30. data/lib/soap/handsoap/ews_service.rb +646 -0
  31. data/lib/soap/handsoap/parser.rb +106 -0
  32. data/lib/soap/handsoap/parsers/ews_parser.rb +165 -0
  33. data/lib/soap/soap_provider.rb +75 -0
  34. data/lib/viewpoint.rb +98 -3
  35. data/preamble +1 -1
  36. data/test/spec/basic_functions.spec +51 -0
  37. data/test/spec/folder_subscriptions.spec +35 -0
  38. data/test/spec/folder_synchronization.spec +28 -0
  39. data/utils/ewsWSDL2rb.rb +29 -0
  40. metadata +101 -43
  41. data/lib/exchwebserv.rb +0 -6
  42. data/lib/soap/viewpoint.conf +0 -3
  43. data/lib/viewpoint/calendar.rb +0 -128
  44. data/lib/viewpoint/calendar_item.rb +0 -118
  45. data/lib/viewpoint/event.rb +0 -0
  46. data/lib/viewpoint/exchange_headers.rb +0 -49
  47. data/lib/viewpoint/exchwebserv.rb +0 -236
  48. data/lib/viewpoint/folder.rb +0 -358
  49. data/lib/viewpoint/item.rb +0 -101
  50. data/lib/viewpoint/mail.rb +0 -117
  51. data/lib/viewpoint/message.rb +0 -132
  52. data/lib/viewpoint/task.rb +0 -0
  53. data/lib/viewpoint/tasks.rb +0 -0
  54. data/lib/wsdl/defaultMappingRegistry.rb +0 -10680
  55. data/lib/wsdl/exchangeServiceBinding.rb +0 -349
  56. data/lib/wsdl/exchangeServiceTypes.rb +0 -11013
  57. data/test/spec/basic_features_spec.rb +0 -37
  58. data/test/test_client.rb +0 -13
  59. data/test/testrestrict.rb +0 -75
@@ -1,118 +0,0 @@
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 'item'
24
-
25
- class Viewpoint::CalendarItem < Viewpoint::Item
26
- include Viewpoint
27
-
28
- attr_reader :subject, :organizer, :location, :start, :end, :cal_item_type
29
-
30
- # Initialize an Exchange Web Services item
31
- def initialize(ews_item, parent_folder)
32
- raise InvalidEWSItemError if ews_item.nil?
33
-
34
- @subject = ews_item.subject # String
35
- @location = ews_item.location if ews_item.location
36
- @cal_item_type = ews_item.calendarItemType
37
- @organizer = ews_item.organizer.mailbox.name if ews_item.organizer and ews_item.organizer.mailbox # String
38
- @start = ews_item.start if ews_item.start # DateTime
39
- @end = ews_item.v_end if ews_item.v_end # DateTime
40
- @busy_status = ews_item.legacyFreeBusyStatus if ews_item.legacyFreeBusyStatus # String
41
- @date_time_recieved = ews_item.dateTimeReceived if ews_item.dateTimeReceived # DateTime
42
-
43
- # This is where the event object gets loaded if it
44
- # is requested. Think of it like IMAP downloading the
45
- # body when the message is viewed.
46
- @message = nil
47
-
48
- super(ews_item, parent_folder)
49
- end
50
-
51
-
52
- def body
53
- get_calitem if @message == nil
54
- @message.body
55
- end
56
-
57
- def sender
58
- @organizer
59
- end
60
-
61
-
62
- # Convert item to iCal format: http://www.ietf.org/rfc/rfc2445.txt
63
- # Returns Icalendar::Event object
64
- def to_ical_event
65
- get_calitem if @message == nil
66
- iev = Icalendar::Event.new
67
- iev.uid = @message.uID
68
- unless(@message.organizer == nil)
69
- iev.organizer = "CN=\"#{@message.organizer.mailbox.name}\"" if @message.organizer.mailbox.name
70
- iev.organizer += ":MAILTO:#{@message.organizer.mailbox.emailAddress}" if @message.organizer.mailbox.emailAddress
71
- end
72
- # TODO: Handle EWS Timezones better. TZ_HASH in viewpoint.rb is the start of this
73
- require 'time'
74
- #tzoffset = @message.timeZone.sub(/^\(GMT([^\)]+)\).*$/,'\1')
75
- dtstart = Time.at(@message.start.strftime('%s').to_i)
76
- dtend = Time.at(@message.v_end.strftime('%s').to_i)
77
- dtstamp = Time.at(@message.dateTimeStamp.strftime('%s').to_i)
78
- last_modified = Time.at(@message.lastModifiedTime.strftime('%s').to_i)
79
- timestr = "%Y%m%dT%H%M%S"
80
- iev.dtstart = dtstart.strftime(timestr)
81
- iev.dtend = dtend.strftime(timestr)
82
- iev.tzid = dtstart.strftime('%Z')
83
- iev.dtstamp = dtstamp.strftime(timestr)
84
- iev.last_modified = last_modified.strftime(timestr)
85
- iev.location = @message.location if @message.location
86
- iev.klass = @message.sensitivity if @message.sensitivity
87
- iev.summary = @message.subject if @message.subject
88
- iev.description = @message.body if @message.body
89
- iev.transp = @message.legacyFreeBusyStatus if @message.legacyFreeBusyStatus
90
- iev.duration = @message.duration if @message.duration
91
- iev.sequence = @message.appointmentSequenceNumber if @message.appointmentSequenceNumber
92
- iev.status = @message.myResponseType if @message.myResponseType
93
- #iev.attach @message.attachments if @message.hasAttachments
94
- @message.requiredAttendees.each do |pers|
95
- output = "ROLE=REQ-PARTICIPANT;CN=\"#{pers.mailbox.name}\"" if pers.mailbox.respond_to?(:name)
96
- output += ":MAILTO:#{pers.mailbox.emailAddress}" if pers.mailbox.respond_to?(:emailAddress)
97
- iev.attendees << output if output
98
- end if @message.requiredAttendees
99
- @message.optionalAttendees.each do |pers|
100
- output = "ROLE=OPT-PARTICIPANT;CN=\"#{pers.mailbox.name}\"" if pers.mailbox.respond_to?(:name)
101
- output += ":MAILTO:#{pers.mailbox.emailAddress}" if pers.mailbox.respond_to?(:emailAddress)
102
- iev.attendees << output if output
103
- end if @message.optionalAttendees
104
-
105
- #iev.categories = @message.categories if @message.categories
106
- #iev.resources = @message.resources if @message.resources
107
- return iev
108
- end
109
-
110
-
111
- # These methods are marked 'private' because they return EWS Types and I am trying to
112
- # hide those because they are not elegant and a bit too tedious for the public interface.
113
- private
114
-
115
- def get_calitem
116
- @message = @parent_folder.get_item(@item_id)
117
- end
118
- end
File without changes
@@ -1,49 +0,0 @@
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
-
24
-
25
- # Some functionality of EWS depends on setting the "RequestServerVersion"
26
- # element with a specified "Version" attribute. That is all this class
27
- # is doing.
28
- # It is used during the initial connection process by setting the
29
- # headerhandler in the serivce binding.
30
- # *<tt>@exchange.headerhandler << ExchangeHeaders.new</tt>
31
- class Viewpoint::ExchangeHeaders < SOAP::Header::SimpleHandler
32
- NAMESPACE = 'http://schemas.microsoft.com/exchange/services/2006/types'
33
-
34
- def initialize
35
- @qname = XSD::QName.new(NAMESPACE,"RequestServerVersion")
36
- super(@qname)
37
- end
38
-
39
- def on_simple_outbound
40
- {
41
- "xmlattr_Version" => "Exchange2007_SP1"
42
- }
43
- end
44
-
45
- def on_outbound
46
- h = on_simple_outbound
47
- h ? SOAP::SOAPElement.from_obj(h, nil) : nil
48
- end
49
- end
@@ -1,236 +0,0 @@
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 'singleton'
23
- require 'wsdl/exchangeServiceBinding'
24
- # --- Custom libs ---
25
- require 'exchange_headers'
26
- # --- Folder Types ---
27
- require 'folder'
28
- require 'calendar'
29
- require 'mail'
30
-
31
- # This class will act as the controller for a client connection to the
32
- # Exchange web service.
33
- class Viewpoint::ExchWebServ
34
- include Viewpoint
35
- include Singleton
36
-
37
- attr_reader :ews, :user, :authenticated, :email
38
-
39
- def initialize
40
- @authenticated = false
41
- @user = nil
42
- @pass = nil
43
- @email = nil
44
- @ews_endpoint = nil
45
-
46
- # Connection to Exchange web services.
47
- # You can get fetch this from an accessor later.
48
- @ews = nil
49
-
50
- # Stores folders returned from 'find_folders'
51
- @folders = {}
52
-
53
- # Do initial authentication
54
- #do_auth
55
- end
56
-
57
-
58
- # Finds all folders and returns a hash of FolderTypes
59
- def find_folders
60
- ff = FindFolderType.new()
61
- ff.xmlattr_Traversal = FolderQueryTraversalType::Deep
62
- ff.folderShape = FolderResponseShapeType.new( DefaultShapeNamesType::AllProperties )
63
- fid = NonEmptyArrayOfBaseFolderIdsType.new()
64
- fidt = DistinguishedFolderIdType.new
65
- fidt.xmlattr_Id = DistinguishedFolderIdNameType::Root
66
- fid.distinguishedFolderId = fidt
67
- ff.parentFolderIds = fid
68
-
69
- # FindFolderResponseType
70
- resp = @ews.findFolder(ff)
71
-
72
- # ArrayOfResponseMessagesType
73
- msgs = resp.responseMessages
74
-
75
- # Array of FindFolderResponseMessageType
76
- msgs.findFolderResponseMessage.each do |elem|
77
- # Mail Folders
78
- mail_folders = {}
79
- elem.rootFolder.folders.folder.each do |folder|
80
- if( folder.folderClass != nil)
81
- mail_folders[folder.displayName] = MailFolder.new(folder)
82
- end
83
- end
84
- @folders['MailFolder'] = mail_folders
85
-
86
- # CalendarFolderType
87
- cal_folders = {}
88
- elem.rootFolder.folders.calendarFolder.each do |folder|
89
- cal_folders[folder.displayName] = CalendarFolder.new(folder)
90
- end
91
- @folders['CalendarFolder'] = cal_folders
92
-
93
- #elem.rootFolder.folders.contactsFolder.each do |folder|
94
- #end
95
- #elem.rootFolder.folders.searchFolder.each do |folder|
96
- #end
97
- #elem.rootFolder.folders.tasksFolder.each do |folder|
98
- #end
99
- end
100
- return @folders
101
- end
102
-
103
- # Return folder
104
- # The default is to return a folder that is a subclass of Folder from the
105
- # SqliteDB, but if fetch_from_ews is set to true it will go out and return
106
- # the FolderType object from EWS.
107
- # Parameters:
108
- # folder_ids: NonEmptyArrayOfBaseFolderIdsType or String if fetch_from_ews is not set
109
- # fetch_from_ews: boolean
110
- # folder_shape: DefaultShapeNamesType
111
- def get_folder(folder_ids, fetch_from_ews = false, folder_shape = DefaultShapeNamesType::AllProperties)
112
- unless fetch_from_ews
113
- return @folders['MailFolder'][folder_ids] ||
114
- @folders['CalendarFolder'][folder_ids]
115
- end
116
-
117
- folder_shape = FolderResponseShapeType.new( folder_shape )
118
- get_folder = GetFolderType.new(folder_shape, folder_ids)
119
-
120
- resp = @ews.getFolder(get_folder).responseMessages.getFolderResponseMessage[0].folders.folder[0]
121
- end
122
-
123
- # Parameters:
124
- # display_name: String
125
- # fetch_from_ews: boolean
126
- # folder_shape: DefaultShapeNamesType
127
- def get_folder_by_name(display_name, fetch_from_ews = true, folder_shape = DefaultShapeNamesType::AllProperties)
128
- #folder_ids = NonEmptyArrayOfBaseFolderIdsType.new()
129
- dist_name = DistinguishedFolderIdType.new
130
- dist_name.xmlattr_Id = DistinguishedFolderIdNameType.new(display_name.downcase)
131
- #folder_ids.distinguishedFolderId = dist_name
132
- folder_ids = NonEmptyArrayOfBaseFolderIdsType.new(nil, [dist_name])
133
-
134
- get_folder(folder_ids, true, folder_shape)
135
- end
136
-
137
- # Parameters:
138
- # folder_id: String
139
- # change_key: String
140
- # folder_shape: DefaultShapeNamesType
141
- def get_folder_by_id(folder_id, change_key = nil, fetch_from_ews = true, folder_shape = DefaultShapeNamesType::AllProperties)
142
- folder_ids = NonEmptyArrayOfBaseFolderIdsType.new()
143
- folder_id_t = FolderIdType.new
144
- folder_id_t.xmlattr_Id = folder_id
145
- folder_id_t.xmlattr_ChangeKey = change_key unless change_key == nil
146
- folder_ids.folderId = folder_id
147
- folder_ids = NonEmptyArrayOfBaseFolderIdsType.new([folder_id_t], nil)
148
-
149
- get_folder(folder_ids, true, folder_shape)
150
- end
151
-
152
- def get_mail_folders
153
- return @folders['MailFolder']
154
- end
155
-
156
- def get_calendar_folders
157
- return @folders['CalendarFolder']
158
- end
159
-
160
- def authenticate(user = nil, pass = nil, endpoint = nil)
161
- unless @authenticated
162
- @user = user
163
- @pass = pass
164
- @ews_endpoint = endpoint
165
- do_auth unless @authenticated
166
- end
167
- @authenticated
168
- end
169
-
170
-
171
- # Resolve a Contact name.
172
- # More info: http://msdn.microsoft.com/en-us/library/aa563518.aspx
173
- def resolve_name(unresolved_entry, full_contact_data = false)
174
- rn_t = ResolveNamesType.new(nil,unresolved_entry)
175
- rn_t.xmlattr_ReturnFullContactData = full_contact_data
176
- ews.resolveNames(rn_t).responseMessages.resolveNamesResponseMessage.first
177
- end
178
-
179
- private
180
- def do_auth
181
- retry_count = 0
182
- begin
183
- if( File.exists?("#{ENV['HOME']}/.viewpointrc") )
184
- props = SOAP::Property.load(File.new("#{ENV['HOME']}/.viewpointrc"))
185
- @user = props['exchange.ews.user'] if @user == nil
186
- @pass = props['exchange.ews.pass'] if @pass == nil
187
- @ews_endpoint = props['exchange.ews.endpoint'] if @ews_endpoint == nil
188
- elsif( File.exists?("#{File.dirname(__FILE__)}/soap/viewpoint.conf") )
189
- props = SOAP::Property.load(File.new("#{File.dirname(__FILE__)}/soap/viewpoint.conf"))
190
- @user = props['exchange.ews.user'] if @user == nil
191
- @pass = props['exchange.ews.pass'] if @pass == nil
192
- @ews_endpoint = props['exchange.ews.endpoint'] if @ews_endpoint == nil
193
- elsif(@ews_endpoint == nil && @user == nil && @pass == nil)
194
- require 'highline/import'
195
- @ews_endpoint = ask("Exchange EWS Endpoint: ") { |q| q.echo = true }
196
- @user = ask("User: ") { |q| q.echo = true }
197
- @pass = ask("Pass: ") { |q| q.echo = "*"}
198
- else
199
- # Nothing to do
200
- end
201
-
202
- @ews = ExchangeServiceBinding.new(@ews_endpoint)
203
- @ews.options["protocol.http.auth.ntlm"] = [@ews_endpoint.sub(/\/[^\/]+$/,'/'),@user,@pass]
204
- @ews.headerhandler << ExchangeHeaders.new
205
-
206
- # Log SOAP request and response for debugging. Run ruby with the '-d' option.
207
- if($DEBUG) then
208
- @ews.wiredump_file_base = "viewpoint-soaplog"
209
- end
210
-
211
- # Do a ResolveNames operation to make sure that authentication works.
212
- # If you don't do an operation, you won't find out that bad credentials
213
- # were entered until later. The ResolveNames operation is completely
214
- # arbitrary and could be any EWS call.
215
- # http://msdn.microsoft.com/en-us/library/bb409286.aspx
216
- resp = resolve_name(@user + '@')
217
- # Set email while we're at it
218
- @email = resp.resolutionSet.resolution.first.mailbox.emailAddress
219
-
220
- rescue SOAP::HTTPStreamError
221
- puts "Bad Login! Try Again."
222
- if( retry_count < 2)
223
- retry_count += 1
224
- retry
225
- else
226
- puts "-----------------------------------------------------------"
227
- puts "Could not log into Exchange Web Services. Make sure your information is correct"
228
- puts "End Point: #{@ews_endpoint}"
229
- puts "User: #{@user}"
230
- puts "-----------------------------------------------------------"
231
- return
232
- end
233
- end
234
- @authenticated = true
235
- end
236
- end
@@ -1,358 +0,0 @@
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
-
22
- # This class is inherited by all folder subtypes such as Mail, Calendar,
23
- # Tasks and Search. It will serve as the brain for all of the methods that
24
- # each of these folder types have in common.
25
- class Viewpoint::Folder
26
- include Viewpoint
27
-
28
- attr_accessor :folder_id, :parent_id, :display_name
29
- def initialize(folder)
30
- @folder_id = folder.folderId.xmlattr_Id
31
- @parent_id = folder.parentFolderId.xmlattr_Id unless folder.parentFolderId == nil
32
- @display_name = folder.displayName
33
- @sync_state = nil
34
- @subscription_id = nil
35
- @watermark = nil
36
- end
37
-
38
- # Subscribe to folder and save the subscription_id into the database. Subsequent calls
39
- # to check_subscription will use that id.
40
- # Returns true if the operation is successful, false otherwise
41
- def subscribe
42
- # Refresh the subscription if already subscribed
43
- if( subscribed? )
44
- unsubscribe
45
- end
46
- subscribe = SubscribeType.new
47
- pull = PullSubscriptionRequestType.new
48
-
49
- # Set-up folder Id
50
- f_id_t = FolderIdType.new
51
- f_id_t.xmlattr_Id = @folder_id
52
- f_ids = NonEmptyArrayOfBaseFolderIdsType.new([f_id_t],nil)
53
- pull.folderIds = f_ids
54
-
55
- # Set-up event types
56
- event_types = NonEmptyArrayOfNotificationEventTypesType.new
57
- event_types.push(NotificationEventTypeType::CopiedEvent)
58
- event_types.push(NotificationEventTypeType::CreatedEvent)
59
- event_types.push(NotificationEventTypeType::DeletedEvent)
60
- event_types.push(NotificationEventTypeType::ModifiedEvent)
61
- event_types.push(NotificationEventTypeType::MovedEvent)
62
- event_types.push(NotificationEventTypeType::NewMailEvent)
63
- pull.eventTypes = event_types
64
-
65
- pull.timeout = 10
66
-
67
- subscribe.pullSubscriptionRequest = pull
68
-
69
- resp = ExchWebServ.instance.ews.subscribe(subscribe).responseMessages.subscribeResponseMessage.first
70
- @subscription_id = resp.subscriptionId
71
- @watermark = resp.watermark
72
-
73
- return (resp.responseCode == "NoError")? true: false
74
- end
75
-
76
- def subscribed?
77
- ( @subscription_id.nil? or @watermark.nil? )? false : true
78
- end
79
-
80
-
81
- # unsubscribe from folder
82
- # Returns true if unsubscription succeeds, false otherwise
83
- def unsubscribe
84
- begin
85
- if( @subscription_id == nil ) then
86
- return true
87
- end
88
-
89
- unsub_t = UnsubscribeType.new(@subscription_id)
90
- resp = ExchWebServ.instance.ews.unsubscribe(unsub_t).responseMessages.unsubscribeResponseMessage.first
91
-
92
- return (resp.responseCode == "NoError")? true: false
93
- end
94
- end
95
-
96
-
97
- # Fetch events with the subscription_id. If one does not exists or is expired,
98
- # call subscribe.
99
- # Returns a hash with the event type as the key and an Array of Hashes that
100
- # represent each event's data
101
- # An :error key is set if there is a problem
102
- def check_subscription
103
- begin
104
- if( @subscription_id == nil or @watermark == nil) then
105
- self.subscribe
106
- end
107
- ge = GetEventsType.new(@subscription_id, @watermark)
108
-
109
- resp = ExchWebServ.instance.ews.getEvents(ge).responseMessages.getEventsResponseMessage
110
-
111
- #TODO: Add more event processing
112
-
113
-
114
- notifications = resp.first.notification
115
-
116
- notif_hash = {}
117
- # Process Notifications
118
- if( notifications.copiedEvent != nil)
119
- notif_hash[:copiedEvent] = []
120
- notifications.copiedEvent.each do |note|
121
- @watermark = note.watermark
122
- item = {}
123
- item.store(:old_item_id,note.oldItemId.xmlattr_Id) if note.itemId
124
- item.store(:old_folder_id,note.oldParentFolderId.xmlattr_Id) if note.oldParentFolderId
125
- item.store(:item_id,note.itemId.xmlattr_Id) if note.itemId
126
- item.store(:folder_id,note.parentFolderId.xmlattr_Id) if note.parentFolderId
127
- notif_hash[:copiedEvent] << item
128
- end
129
- end
130
-
131
- if( notifications.createdEvent != nil)
132
- notif_hash[:createdEvent] = []
133
- notifications.createdEvent.each do |note|
134
- @watermark = note.watermark
135
- item = {}
136
- item.store(:item_id,note.itemId.xmlattr_Id) if note.itemId
137
- item.store(:folder_id,note.parentFolderId.xmlattr_Id) if note.parentFolderId
138
- notif_hash[:createdEvent] << item
139
- end
140
- end
141
-
142
- if( notifications.deletedEvent != nil)
143
- notif_hash[:deletedEvent] = []
144
- notifications.deletedEvent.each do |note|
145
- @watermark = note.watermark
146
- item = {}
147
- item.store(:item_id,note.itemId.xmlattr_Id) if note.itemId
148
- item.store(:folder_id,note.parentFolderId.xmlattr_Id) if note.parentFolderId
149
- notif_hash[:deletedEvent] << item
150
- end
151
- end
152
-
153
- if( notifications.modifiedEvent != nil)
154
- notif_hash[:modifiedEvent] = []
155
- notifications.modifiedEvent.each do |note|
156
- @watermark = note.watermark
157
- item = {}
158
- item.store(:item_id,note.itemId.xmlattr_Id) if note.itemId
159
- item.store(:folder_id,note.folderId.xmlattr_Id) if note.folderId
160
- item.store(:parent_folder_id,note.parentFolderId.xmlattr_Id) if note.parentFolderId
161
- item.store(:unreadCount,note.unreadCount)
162
- notif_hash[:modifiedEvent] << item
163
- end
164
- end
165
-
166
- if( notifications.movedEvent != nil)
167
- notif_hash[:movedEvent] = []
168
- notifications.movedEvent.each do |note|
169
- @watermark = note.watermark
170
- item = {}
171
- item.store(:old_item_id,note.oldItemId.xmlattr_Id) if note.oldItemId
172
- item.store(:old_folder_id,note.oldParentFolderId.xmlattr_Id) if note.oldParentFolderId
173
- item.store(:item_id,note.itemId.xmlattr_Id) if note.itemId
174
- item.store(:folder_id,note.parentFolderId.xmlattr_Id) if note.parentFolderId
175
- notif_hash[:movedEvent] << item
176
- end
177
- end
178
-
179
- if( notifications.newMailEvent!= nil)
180
- notif_hash[:newMailEvent] = []
181
- notifications.newMailEvent.each do |note|
182
- @watermark = note.watermark
183
- item = {}
184
- item.store(:item_id,note.itemId.xmlattr_Id) if note.itemId
185
- item.store(:folder_id,note.parentFolderId.xmlattr_Id) if note.parentFolderId
186
- notif_hash[:newMailEvent] << item
187
- end
188
- end
189
-
190
- if( notifications.statusEvent != nil)
191
- notifications.statusEvent.each do |note|
192
- @watermark = note.watermark
193
- end
194
- end
195
-
196
- rescue NoMethodError
197
- # This ocurrs if something bad happened to the subscription.
198
- # This is handled by resubscribing to the subscription and returning
199
- # an error key.
200
- self.subscribe
201
- return {:error => "Something bad happened to your subscription. You have been re-subscribed" }
202
- end
203
-
204
- #return resp
205
- notif_hash[:resp] = resp
206
- return notif_hash
207
- end
208
-
209
-
210
- # This is a proxy for the SyncFolderItems operation detailed here: http://msdn.microsoft.com/en-us/library/aa563967.aspx
211
- # It returns a SyncFolderItemsResponseType object
212
- def sync_folder(max_items = 256)
213
- begin
214
- # ItemResponseShapeType
215
- itemshapeT = ItemResponseShapeType.new(DefaultShapeNamesType::IdOnly, false)
216
-
217
- # TargetFolderIdType
218
- tfolderidT = TargetFolderIdType.new
219
- folderidT = FolderIdType.new
220
- folderidT.xmlattr_Id = self.folder_id
221
- tfolderidT.folderId = folderidT
222
-
223
- # SyncFolderItemsType
224
- syncitemsT = SyncFolderItemsType.new(itemshapeT,tfolderidT,self.sync_state,nil,max_items)
225
-
226
- # Call Sync => returns SyncFolderItemsResponseType
227
- resp = ExchWebServ.instance.ews.syncFolderItems(syncitemsT).responseMessages.syncFolderItemsResponseMessage[0]
228
- @sync_state = resp.syncState
229
- return resp
230
- rescue
231
- puts "** Something happened during synchronizing folder => #{self.display_name}"
232
- raise
233
- end
234
- end
235
-
236
-
237
- # Wrapper around EWS's FindItem method: http://msdn.microsoft.com/en-us/library/aa566107.aspx
238
- # The specifics of this method should be set up in the child classes and then this method should
239
- # be called with a parameter of FindItemType: http://msdn.microsoft.com/en-us/library/aa566370.aspx
240
- # This allows for a central place of error handling. See Calendar::get_events for an example
241
- # of how this works.
242
- def find_items(find_item_t)
243
- # FindItemResponseMessageType: http://msdn.microsoft.com/en-us/library/aa566424.aspx
244
- resp = ExchWebServ.instance.ews.findItem(find_item_t).responseMessages.findItemResponseMessage[0]
245
-
246
- #TODO: Error handling
247
- if resp.xmlattr_ResponseClass == "Success"
248
- return resp
249
- else
250
- return nil
251
- end
252
- end
253
-
254
-
255
- # Fetch item by item_id
256
- # Params: item_id: ItemIdType
257
- # item_type: String (message, calendarItem)
258
- # all_props: boolean; default is to get just the Default props
259
- # Returns: a Item type of some sort ( depends on 'item_type' parameter )
260
- def get_item(item_id, item_type = nil, all_props = false)
261
- get_item = GetItemType.new
262
-
263
- if all_props
264
- shape_name = DefaultShapeNamesType::AllProperties
265
- else
266
- shape_name = DefaultShapeNamesType::Default
267
- end
268
-
269
- item_shape = ItemResponseShapeType.new( shape_name, false, BodyTypeResponseType::Text )
270
-
271
- #additional_props = NonEmptyArrayOfPathsToElementType.new
272
- #prop_a = PathToUnindexedFieldType.new
273
- #prop_b = PathToUnindexedFieldType.new
274
- #prop_c = PathToUnindexedFieldType.new
275
- #prop_d = PathToUnindexedFieldType.new
276
- #prop_a.xmlattr_FieldURI = UnindexedFieldURIType::CalendarMeetingTimeZone
277
- #prop_b.xmlattr_FieldURI = UnindexedFieldURIType.new("calendar:EndTimeZone")
278
- #prop_c.xmlattr_FieldURI = UnindexedFieldURIType::ItemInternetMessageHeaders
279
- #prop_d.xmlattr_FieldURI = UnindexedFieldURIType::MessageFrom
280
- #additional_props.upath << prop_a
281
- #additional_props.upath << prop_b
282
- #additional_props.upath << prop_c
283
- #additional_props << prop_d
284
- #item_shape.additionalProperties = additional_props
285
-
286
-
287
-
288
- item_id_t = ItemIdType.new
289
- item_id_t.xmlattr_Id = item_id
290
- item_ids = NonEmptyArrayOfBaseItemIdsType.new([item_id_t])
291
-
292
- get_item.itemShape = item_shape
293
- get_item.itemIds= item_ids
294
-
295
- # ItemInfoResponseMessageType: http://msdn.microsoft.com/en-us/library/aa565417.aspx
296
- resp = ExchWebServ.instance.ews.getItem(get_item).responseMessages.getItemResponseMessage[0]
297
-
298
- #TODO: Error handling
299
- if resp.xmlattr_ResponseClass == "Success"
300
- item = resp.items.send(item_type)
301
- if( item.length > 1 )
302
- puts "ERROR: Item Id should only resolve to a single item!"
303
- end
304
- return item[0]
305
- else
306
- return nil
307
- end
308
- end
309
-
310
-
311
- # More info: http://msdn.microsoft.com/en-us/library/aa565781.aspx
312
- def move_item(item_id, new_folder)
313
- return false unless new_folder.kind_of?(Viewpoint::Folder)
314
-
315
- folder_id_t = FolderIdType.new
316
- folder_id_t.xmlattr_Id = new_folder.folder_id
317
- target_folder_t = TargetFolderIdType.new(folder_id_t)
318
-
319
- item_ids = NonEmptyArrayOfBaseItemIdsType.new
320
- item_t = ItemIdType.new
321
- item_t.xmlattr_Id = item_id
322
- item_ids.itemId << item_t
323
-
324
- move_t = MoveItemType.new(target_folder_t, item_ids)
325
- resp = ExchWebServ.instance.ews.moveItem(move_t).responseMessages.moveItemResponseMessage.first
326
- #TODO: Error handling
327
- if resp.xmlattr_ResponseClass == "Success"
328
- return true
329
- else
330
- return false
331
- end
332
- end
333
-
334
- # Delete item by item_id
335
- # More info: http://msdn.microsoft.com/en-us/library/aa580484.aspx
336
- def delete_item(item_id, delete_type=DisposalType::HardDelete)
337
- item_ids = NonEmptyArrayOfBaseItemIdsType.new
338
- item = ItemIdType.new
339
- item.xmlattr_Id = item_id
340
- item_ids.itemId << item
341
-
342
- delete = DeleteItemType.new(item_ids)
343
- delete.xmlattr_DeleteType = delete_type
344
- resp = ExchWebServ.instance.ews.deleteItem(delete).responseMessages.deleteItemResponseMessage.first
345
-
346
- #TODO: Error handling
347
- if resp.xmlattr_ResponseClass == "Success"
348
- return true
349
- else
350
- return false
351
- end
352
- end
353
-
354
- # Move item to Deleted Items folder instead of deleting it
355
- def recycle_item(item_id)
356
- delete_item(item_id, DisposalType::MoveToDeletedItems)
357
- end
358
- end