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.
- data/COPYING.txt +674 -0
- data/README +60 -0
- data/Rakefile +39 -0
- data/examples/cal2ical.rb +29 -0
- data/examples/ews_fusefs.rb +364 -0
- data/examples/webclient/.gems +2 -0
- data/examples/webclient/README +1 -0
- data/examples/webclient/config.ru +26 -0
- data/examples/webclient/ews_access_handler.rb +42 -0
- data/examples/webclient/public/css/mail.css +33 -0
- data/examples/webclient/public/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/examples/webclient/public/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/examples/webclient/public/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/examples/webclient/public/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/examples/webclient/public/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/examples/webclient/public/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/examples/webclient/public/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/examples/webclient/public/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/examples/webclient/public/css/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/examples/webclient/public/css/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/examples/webclient/public/css/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/examples/webclient/public/css/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/examples/webclient/public/css/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/examples/webclient/public/css/smoothness/jquery-ui-1.7.2.custom.css +406 -0
- data/examples/webclient/public/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
- data/examples/webclient/public/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
- data/examples/webclient/public/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png +0 -0
- data/examples/webclient/public/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- data/examples/webclient/public/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- data/examples/webclient/public/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/examples/webclient/public/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
- data/examples/webclient/public/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
- data/examples/webclient/public/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
- data/examples/webclient/public/css/ui-lightness/images/ui-icons_222222_256x240.png +0 -0
- data/examples/webclient/public/css/ui-lightness/images/ui-icons_228ef1_256x240.png +0 -0
- data/examples/webclient/public/css/ui-lightness/images/ui-icons_ef8c08_256x240.png +0 -0
- data/examples/webclient/public/css/ui-lightness/images/ui-icons_ffd27a_256x240.png +0 -0
- data/examples/webclient/public/css/ui-lightness/images/ui-icons_ffffff_256x240.png +0 -0
- data/examples/webclient/public/css/ui-lightness/jquery-ui-1.7.2.custom.css +406 -0
- data/examples/webclient/public/js/jquery-1.3.2.min.js +19 -0
- data/examples/webclient/public/js/jquery-ui-1.7.2.custom.min.js +145 -0
- data/examples/webclient/viewpoint_web.rb +295 -0
- data/examples/webclient/views/_footer.haml +1 -0
- data/examples/webclient/views/_header.haml +11 -0
- data/examples/webclient/views/_tagline.haml +1 -0
- data/examples/webclient/views/index.haml +20 -0
- data/examples/webclient/views/layout.haml +42 -0
- data/examples/webclient/views/mail.haml +45 -0
- data/examples/webclient/views/welcome.haml +0 -0
- data/lib/calendar.rb +121 -0
- data/lib/calendar_item.rb +110 -0
- data/lib/event.rb +0 -0
- data/lib/exchange_headers.rb +50 -0
- data/lib/exchwebserv.rb +228 -0
- data/lib/folder.rb +269 -0
- data/lib/item.rb +56 -0
- data/lib/mail.rb +118 -0
- data/lib/message.rb +134 -0
- data/lib/soap/viewpoint.conf +3 -0
- data/lib/task.rb +0 -0
- data/lib/tasks.rb +0 -0
- data/lib/viewpoint.rb +2 -0
- data/lib/wsdl/defaultMappingRegistry.rb +10680 -0
- data/lib/wsdl/exchangeServiceBinding.rb +349 -0
- data/lib/wsdl/exchangeServiceTypes.rb +11013 -0
- data/preamble +19 -0
- data/test/spec/authtest_spec.rb +12 -0
- data/test/spec/findfolders_spec.rb +14 -0
- data/test/test_client.rb +12 -0
- data/test/testrestrict.rb +75 -0
- metadata +130 -0
data/lib/folder.rb
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
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
|
+
require 'viewpoint'
|
|
22
|
+
|
|
23
|
+
# This class is inherited by all folder subtypes such as Mail, Calendar,
|
|
24
|
+
# Tasks and Search. It will serve as the brain for all of the methods that
|
|
25
|
+
# each of these folder types have in common.
|
|
26
|
+
class Viewpoint::Folder
|
|
27
|
+
include Viewpoint
|
|
28
|
+
|
|
29
|
+
attr_accessor :folder_id, :parent_id, :display_name
|
|
30
|
+
def initialize(folder)
|
|
31
|
+
@folder_id = folder.folderId.xmlattr_Id
|
|
32
|
+
@parent_id = folder.parentFolderId.xmlattr_Id unless folder.parentFolderId == nil
|
|
33
|
+
@display_name = folder.displayName
|
|
34
|
+
@sync_state = nil
|
|
35
|
+
@subscription_id = nil
|
|
36
|
+
@watermark = nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Subscribe to folder and save the subscription_id into the database. Subsequent calls
|
|
40
|
+
# to check_subscription will use that id.
|
|
41
|
+
# Returns true if the operation is successful, false otherwise
|
|
42
|
+
def subscribe
|
|
43
|
+
subscribe = SubscribeType.new
|
|
44
|
+
pull = PullSubscriptionRequestType.new
|
|
45
|
+
|
|
46
|
+
# Set-up folder Id
|
|
47
|
+
dist_fid = DistinguishedFolderIdType.new
|
|
48
|
+
dist_fid.xmlattr_Id = DistinguishedFolderIdNameType::Calendar
|
|
49
|
+
f_ids = NonEmptyArrayOfBaseFolderIdsType.new()
|
|
50
|
+
f_ids.distinguishedFolderId = dist_fid
|
|
51
|
+
pull.folderIds = f_ids
|
|
52
|
+
|
|
53
|
+
# Set-up event types
|
|
54
|
+
event_types = NonEmptyArrayOfNotificationEventTypesType.new
|
|
55
|
+
#event_types.push(NotificationEventTypeType::NewMailEvent)
|
|
56
|
+
event_types.push(NotificationEventTypeType::CreatedEvent)
|
|
57
|
+
event_types.push(NotificationEventTypeType::DeletedEvent)
|
|
58
|
+
#event_types.push(NotificationEventTypeType::ModifiedEvent)
|
|
59
|
+
event_types.push(NotificationEventTypeType::MovedEvent)
|
|
60
|
+
pull.eventTypes = event_types
|
|
61
|
+
|
|
62
|
+
pull.timeout = 10
|
|
63
|
+
|
|
64
|
+
subscribe.pullSubscriptionRequest = pull
|
|
65
|
+
|
|
66
|
+
resp = ExchWebServ.instance.ews.subscribe(subscribe).responseMessages.subscribeResponseMessage[0]
|
|
67
|
+
@subscription_id = resp.subscriptionId
|
|
68
|
+
@watermark = resp.watermark
|
|
69
|
+
|
|
70
|
+
return (resp.responseCode == "NoError")? true: false
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# Fetch events with the subscription_id. If one does not exists or is expired,
|
|
75
|
+
# call subscribe.
|
|
76
|
+
def check_subscription
|
|
77
|
+
begin
|
|
78
|
+
if( @subscription_id == nil or @watermark == nil) then
|
|
79
|
+
self.subscribe
|
|
80
|
+
end
|
|
81
|
+
ge = GetEventsType.new(@subscription_id, @watermark)
|
|
82
|
+
|
|
83
|
+
resp = ExchWebServ.instance.ews.getEvents(ge).responseMessages.getEventsResponseMessage[0]
|
|
84
|
+
|
|
85
|
+
#TODO: Add more event processing
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
notifications = resp.notification
|
|
89
|
+
|
|
90
|
+
# Process Notifications
|
|
91
|
+
if( notifications.createdEvent != nil)
|
|
92
|
+
notifications.createdEvent.each do |note|
|
|
93
|
+
@watermark = note.watermark
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
if( notifications.deletedEvent != nil)
|
|
98
|
+
notifications.deletedEvent.each do |note|
|
|
99
|
+
@watermark = note.watermark
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
if( notifications.movedEvent != nil)
|
|
104
|
+
notifications.movedEvent.each do |note|
|
|
105
|
+
@watermark = note.watermark
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
if( notifications.statusEvent != nil)
|
|
110
|
+
notifications.statusEvent.each do |note|
|
|
111
|
+
@watermark = note.watermark
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
return resp
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# This is a proxy for the SyncFolderItems operation detailed here: http://msdn.microsoft.com/en-us/library/aa563967.aspx
|
|
122
|
+
# It returns a SyncFolderItemsResponseType object
|
|
123
|
+
def sync_folder(max_items = 256)
|
|
124
|
+
begin
|
|
125
|
+
# ItemResponseShapeType
|
|
126
|
+
itemshapeT = ItemResponseShapeType.new(DefaultShapeNamesType::IdOnly, false)
|
|
127
|
+
|
|
128
|
+
# TargetFolderIdType
|
|
129
|
+
tfolderidT = TargetFolderIdType.new
|
|
130
|
+
folderidT = FolderIdType.new
|
|
131
|
+
folderidT.xmlattr_Id = self.folder_id
|
|
132
|
+
tfolderidT.folderId = folderidT
|
|
133
|
+
|
|
134
|
+
# SyncFolderItemsType
|
|
135
|
+
syncitemsT = SyncFolderItemsType.new(itemshapeT,tfolderidT,self.sync_state,nil,max_items)
|
|
136
|
+
|
|
137
|
+
# Call Sync => returns SyncFolderItemsResponseType
|
|
138
|
+
resp = ExchWebServ.instance.ews.syncFolderItems(syncitemsT).responseMessages.syncFolderItemsResponseMessage[0]
|
|
139
|
+
@sync_state = resp.syncState
|
|
140
|
+
return resp
|
|
141
|
+
rescue
|
|
142
|
+
puts "** Something happened during synchronizing folder => #{self.display_name}"
|
|
143
|
+
raise
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# Wrapper around EWS's FindItem method: http://msdn.microsoft.com/en-us/library/aa566107.aspx
|
|
149
|
+
# The specifics of this method should be set up in the child classes and then this method should
|
|
150
|
+
# be called with a parameter of FindItemType: http://msdn.microsoft.com/en-us/library/aa566370.aspx
|
|
151
|
+
# This allows for a central place of error handling. See Calendar::get_events for an example
|
|
152
|
+
# of how this works.
|
|
153
|
+
def find_items(find_item_t)
|
|
154
|
+
# FindItemResponseMessageType: http://msdn.microsoft.com/en-us/library/aa566424.aspx
|
|
155
|
+
resp = ExchWebServ.instance.ews.findItem(find_item_t).responseMessages.findItemResponseMessage[0]
|
|
156
|
+
|
|
157
|
+
#TODO: Error handling
|
|
158
|
+
if resp.xmlattr_ResponseClass == "Success"
|
|
159
|
+
return resp
|
|
160
|
+
else
|
|
161
|
+
return nil
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
# Fetch item by item_id
|
|
167
|
+
# Params: item_id: ItemIdType
|
|
168
|
+
# item_type: String (message, calendarItem)
|
|
169
|
+
# all_props: boolean; default is to get just the Default props
|
|
170
|
+
# Returns: a Item type of some sort ( depends on 'item_type' parameter )
|
|
171
|
+
def get_item(item_id, item_type = nil, all_props = false)
|
|
172
|
+
get_item = GetItemType.new
|
|
173
|
+
|
|
174
|
+
if all_props
|
|
175
|
+
shape_name = DefaultShapeNamesType::AllProperties
|
|
176
|
+
else
|
|
177
|
+
shape_name = DefaultShapeNamesType::Default
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
item_shape = ItemResponseShapeType.new( shape_name, false, BodyTypeResponseType::Text )
|
|
181
|
+
|
|
182
|
+
#additional_props = NonEmptyArrayOfPathsToElementType.new
|
|
183
|
+
#prop_a = PathToUnindexedFieldType.new
|
|
184
|
+
#prop_b = PathToUnindexedFieldType.new
|
|
185
|
+
#prop_c = PathToUnindexedFieldType.new
|
|
186
|
+
#prop_d = PathToUnindexedFieldType.new
|
|
187
|
+
#prop_a.xmlattr_FieldURI = UnindexedFieldURIType::CalendarMeetingTimeZone
|
|
188
|
+
#prop_b.xmlattr_FieldURI = UnindexedFieldURIType.new("calendar:EndTimeZone")
|
|
189
|
+
#prop_c.xmlattr_FieldURI = UnindexedFieldURIType::ItemInternetMessageHeaders
|
|
190
|
+
#prop_d.xmlattr_FieldURI = UnindexedFieldURIType::MessageFrom
|
|
191
|
+
#additional_props.upath << prop_a
|
|
192
|
+
#additional_props.upath << prop_b
|
|
193
|
+
#additional_props.upath << prop_c
|
|
194
|
+
#additional_props << prop_d
|
|
195
|
+
#item_shape.additionalProperties = additional_props
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
item_id_t = ItemIdType.new
|
|
200
|
+
item_id_t.xmlattr_Id = item_id
|
|
201
|
+
item_ids = NonEmptyArrayOfBaseItemIdsType.new([item_id_t])
|
|
202
|
+
|
|
203
|
+
get_item.itemShape = item_shape
|
|
204
|
+
get_item.itemIds= item_ids
|
|
205
|
+
|
|
206
|
+
# ItemInfoResponseMessageType: http://msdn.microsoft.com/en-us/library/aa565417.aspx
|
|
207
|
+
resp = ExchWebServ.instance.ews.getItem(get_item).responseMessages.getItemResponseMessage[0]
|
|
208
|
+
|
|
209
|
+
#TODO: Error handling
|
|
210
|
+
if resp.xmlattr_ResponseClass == "Success"
|
|
211
|
+
item = resp.items.send(item_type)
|
|
212
|
+
if( item.length > 1 )
|
|
213
|
+
puts "ERROR: Item Id should only resolve to a single item!"
|
|
214
|
+
end
|
|
215
|
+
return item[0]
|
|
216
|
+
else
|
|
217
|
+
return nil
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
# More info: http://msdn.microsoft.com/en-us/library/aa565781.aspx
|
|
223
|
+
def move_item(item_id, new_folder)
|
|
224
|
+
return false unless new_folder.kind_of?(Viewpoint::Folder)
|
|
225
|
+
|
|
226
|
+
folder_id_t = FolderIdType.new
|
|
227
|
+
folder_id_t.xmlattr_Id = new_folder.folder_id
|
|
228
|
+
target_folder_t = TargetFolderIdType.new(folder_id_t)
|
|
229
|
+
|
|
230
|
+
item_ids = NonEmptyArrayOfBaseItemIdsType.new
|
|
231
|
+
item_t = ItemIdType.new
|
|
232
|
+
item_t.xmlattr_Id = item_id
|
|
233
|
+
item_ids.itemId << item_t
|
|
234
|
+
|
|
235
|
+
move_t = MoveItemType.new(target_folder_t, item_ids)
|
|
236
|
+
resp = ExchWebServ.instance.ews.moveItem(move_t).responseMessages.moveItemResponseMessage.first
|
|
237
|
+
#TODO: Error handling
|
|
238
|
+
if resp.xmlattr_ResponseClass == "Success"
|
|
239
|
+
return true
|
|
240
|
+
else
|
|
241
|
+
return false
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Delete item by item_id
|
|
246
|
+
# More info: http://msdn.microsoft.com/en-us/library/aa580484.aspx
|
|
247
|
+
def delete_item(item_id, delete_type=DisposalType::HardDelete)
|
|
248
|
+
item_ids = NonEmptyArrayOfBaseItemIdsType.new
|
|
249
|
+
item = ItemIdType.new
|
|
250
|
+
item.xmlattr_Id = item_id
|
|
251
|
+
item_ids.itemId << item
|
|
252
|
+
|
|
253
|
+
delete = DeleteItemType.new(item_ids)
|
|
254
|
+
delete.xmlattr_DeleteType = delete_type
|
|
255
|
+
resp = ExchWebServ.instance.ews.deleteItem(delete).responseMessages.deleteItemResponseMessage.first
|
|
256
|
+
|
|
257
|
+
#TODO: Error handling
|
|
258
|
+
if resp.xmlattr_ResponseClass == "Success"
|
|
259
|
+
return true
|
|
260
|
+
else
|
|
261
|
+
return false
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Move item to Deleted Items folder instead of deleting it
|
|
266
|
+
def recycle_item(item_id)
|
|
267
|
+
delete_item(item_id, DisposalType::MoveToDeletedItems)
|
|
268
|
+
end
|
|
269
|
+
end
|
data/lib/item.rb
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
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 'viewpoint'
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# This class is inherited by all Item subtypes such as Message, Event,
|
|
24
|
+
# and Task. It will serve as the brain for all of the methods that
|
|
25
|
+
# each of these Item types have in common.
|
|
26
|
+
class Viewpoint::Item
|
|
27
|
+
include Viewpoint
|
|
28
|
+
|
|
29
|
+
attr_accessor :item_id
|
|
30
|
+
|
|
31
|
+
# Initialize an Exchange Web Services item
|
|
32
|
+
def initialize(ews_item, parent_folder)
|
|
33
|
+
@item_id = ews_item.itemId.xmlattr_Id
|
|
34
|
+
@parent_folder = parent_folder
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Takes an object of type Viewpoint::Folder as an argument
|
|
38
|
+
# Returns a boolean value, true if the move ocurred, false otherwise.
|
|
39
|
+
def move_to!(new_folder)
|
|
40
|
+
retval = @parent_folder.move_item(@item_id, new_folder)
|
|
41
|
+
@parent_folder = new_folder if retval
|
|
42
|
+
|
|
43
|
+
return retval
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# Returns a boolean value, true if the delete ocurred, false otherwise.
|
|
48
|
+
def delete!
|
|
49
|
+
@parent_folder.delete_item(@item_id)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Returns a boolean value, true if the recycle ocurred, false otherwise.
|
|
53
|
+
def recycle!
|
|
54
|
+
@parent_folder.recycle_item(@item_id)
|
|
55
|
+
end
|
|
56
|
+
end
|
data/lib/mail.rb
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
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 'wsdl/exchangeServiceBinding'
|
|
23
|
+
# --- Folder Types ---
|
|
24
|
+
require 'folder'
|
|
25
|
+
# --- Item Types ---
|
|
26
|
+
require 'message'
|
|
27
|
+
|
|
28
|
+
require 'viewpoint'
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Viewpoint::MailFolder < Viewpoint::Folder
|
|
32
|
+
include Viewpoint
|
|
33
|
+
|
|
34
|
+
# initialize with an item of MailFolderType
|
|
35
|
+
def initialize(folder)
|
|
36
|
+
super(folder)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def get_todays_messages
|
|
40
|
+
get_messages( DateTime.parse(Date.today.to_s).to_s )
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Get messages between a certain time period. Defaults to "today"
|
|
44
|
+
# Input: DateTime of start, DateTime of end
|
|
45
|
+
# This method will return an Array of Message
|
|
46
|
+
def get_messages(since = (DateTime.parse(Date.today.to_s)) )
|
|
47
|
+
# Set-up FindItemType
|
|
48
|
+
find_item_t = FindItemType.new
|
|
49
|
+
find_item_t.xmlattr_Traversal = ItemQueryTraversalType::Shallow
|
|
50
|
+
item_shape = ItemResponseShapeType.new(DefaultShapeNamesType::IdOnly, false)
|
|
51
|
+
|
|
52
|
+
additional_props = NonEmptyArrayOfPathsToElementType.new
|
|
53
|
+
prop_a = PathToUnindexedFieldType.new
|
|
54
|
+
prop_b = PathToUnindexedFieldType.new
|
|
55
|
+
prop_c = PathToUnindexedFieldType.new
|
|
56
|
+
prop_d = PathToUnindexedFieldType.new
|
|
57
|
+
prop_e = PathToUnindexedFieldType.new
|
|
58
|
+
prop_f = PathToUnindexedFieldType.new
|
|
59
|
+
prop_a.xmlattr_FieldURI = UnindexedFieldURIType::ItemSubject
|
|
60
|
+
prop_b.xmlattr_FieldURI = UnindexedFieldURIType::ItemDateTimeReceived
|
|
61
|
+
prop_c.xmlattr_FieldURI = UnindexedFieldURIType::MessageSender
|
|
62
|
+
prop_d.xmlattr_FieldURI = UnindexedFieldURIType::MessageFrom
|
|
63
|
+
prop_e.xmlattr_FieldURI = UnindexedFieldURIType::MessageIsRead
|
|
64
|
+
prop_f.xmlattr_FieldURI = UnindexedFieldURIType::MessageInternetMessageId
|
|
65
|
+
additional_props.upath << prop_a
|
|
66
|
+
additional_props.upath << prop_b
|
|
67
|
+
additional_props.upath << prop_c
|
|
68
|
+
additional_props.upath << prop_d
|
|
69
|
+
additional_props.upath << prop_e
|
|
70
|
+
additional_props.upath << prop_f
|
|
71
|
+
item_shape.additionalProperties = additional_props
|
|
72
|
+
find_item_t.itemShape = item_shape
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# Set-up folder Ids to search in
|
|
76
|
+
folder_ids = NonEmptyArrayOfBaseFolderIdsType.new()
|
|
77
|
+
dist_folder = DistinguishedFolderIdType.new
|
|
78
|
+
dist_folder.xmlattr_Id = DistinguishedFolderIdNameType.new(@display_name.downcase)
|
|
79
|
+
folder_ids.distinguishedFolderId = dist_folder
|
|
80
|
+
find_item_t.parentFolderIds = folder_ids
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# Set-up date-based restriction.
|
|
84
|
+
restriction = RestrictionType.new
|
|
85
|
+
search_type = IsGreaterThanOrEqualToType.new
|
|
86
|
+
fielduri = PathToUnindexedFieldType.new
|
|
87
|
+
fielduri.xmlattr_FieldURI = UnindexedFieldURIType::ItemDateTimeReceived
|
|
88
|
+
const = FieldURIOrConstantType.new
|
|
89
|
+
c = ConstantValueType.new
|
|
90
|
+
c.xmlattr_Value = (DateTime.now - 2).new_offset(0).to_s
|
|
91
|
+
const.constant = c
|
|
92
|
+
search_type.path = fielduri
|
|
93
|
+
search_type.fieldURIOrConstant = const
|
|
94
|
+
restriction.isGreaterThanOrEqualTo = search_type
|
|
95
|
+
|
|
96
|
+
find_item_t.restriction = restriction
|
|
97
|
+
|
|
98
|
+
resp = find_items(find_item_t)
|
|
99
|
+
|
|
100
|
+
if resp != nil
|
|
101
|
+
messages = []
|
|
102
|
+
resp.rootFolder.items.message.each do |msg|
|
|
103
|
+
messages << Message.new(msg, self)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
return messages
|
|
107
|
+
else
|
|
108
|
+
return resp
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
# See docs for Folder::get_item
|
|
115
|
+
def get_item(item_id)
|
|
116
|
+
msg = super(item_id, "message", true)
|
|
117
|
+
end
|
|
118
|
+
end
|
data/lib/message.rb
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
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 'item'
|
|
22
|
+
require 'viewpoint'
|
|
23
|
+
|
|
24
|
+
class Viewpoint::Message < Viewpoint::Item
|
|
25
|
+
include Viewpoint
|
|
26
|
+
|
|
27
|
+
attr_reader :subject, :parent_folder, :sender, :date_time_recieved
|
|
28
|
+
attr_reader :ews_item if $DEBUG
|
|
29
|
+
|
|
30
|
+
# Initialize an Exchange Web Services item of MessageType
|
|
31
|
+
def initialize(ews_item, parent_folder)
|
|
32
|
+
# keep this now for debuging
|
|
33
|
+
@ews_item = ews_item if $DEBUG
|
|
34
|
+
@subject = ews_item.subject # String
|
|
35
|
+
@sender = ews_item.sender.mailbox.name if ews_item.sender # String
|
|
36
|
+
@recipients =
|
|
37
|
+
@is_read = ews_item.isRead # boolean
|
|
38
|
+
@date_time_recieved = ews_item.dateTimeReceived # DateTime
|
|
39
|
+
|
|
40
|
+
# This is where the entire message object gets loaded if it
|
|
41
|
+
# is requested. Think of it like IMAP downloading the
|
|
42
|
+
# body when the message is viewed.
|
|
43
|
+
@message = nil
|
|
44
|
+
|
|
45
|
+
super(ews_item, parent_folder)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def body
|
|
49
|
+
get_message if @message == nil
|
|
50
|
+
return @message.body
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def recipients
|
|
54
|
+
get_message if @message == nil
|
|
55
|
+
to_recipients = ''
|
|
56
|
+
@message.toRecipients.each do |r|
|
|
57
|
+
to_recipients += ', ' if to_recipients != ''
|
|
58
|
+
to_recipients += "#{r.name} <#{r.emailAddress}>"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
return to_recipients
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def cc_recipients
|
|
65
|
+
get_message if @message == nil
|
|
66
|
+
if( @message.ccRecipients != nil)
|
|
67
|
+
cc = ''
|
|
68
|
+
@message.ccRecipients.each do |r|
|
|
69
|
+
cc += ', ' if cc != ''
|
|
70
|
+
cc += "#{r.name} <#{r.emailAddress}>"
|
|
71
|
+
end
|
|
72
|
+
return cc
|
|
73
|
+
end
|
|
74
|
+
return nil
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def bcc_recipients
|
|
78
|
+
get_message if @message == nil
|
|
79
|
+
if( @message.bccRecipients != nil)
|
|
80
|
+
bcc = ''
|
|
81
|
+
@message.bccRecipients.each do |r|
|
|
82
|
+
bcc += ', ' if bcc != ''
|
|
83
|
+
bcc += "#{r.name} <#{r.emailAddress}>"
|
|
84
|
+
end
|
|
85
|
+
return bcc
|
|
86
|
+
end
|
|
87
|
+
return nil
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def get_message
|
|
91
|
+
@message = @parent_folder.get_item(@item_id)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Output the message in RFC 822 format: http://www.ietf.org/rfc/rfc0822.txt
|
|
95
|
+
def to_rfc822
|
|
96
|
+
get_message if @message == nil
|
|
97
|
+
rfc822 = []
|
|
98
|
+
rfc822.push 'Return-Path' => @message.sender.mailbox.emailAddress
|
|
99
|
+
rfc822.push 'Delivered-To' => @message.receivedBy.mailbox.emailAddress
|
|
100
|
+
@message.internetMessageHeaders.each do |hdr|
|
|
101
|
+
# TODO: Quick Fix... needs more research
|
|
102
|
+
if( hdr.xmlattr_HeaderName == 'Content-Type' && hdr =~ /tnef/ )
|
|
103
|
+
rfc822.push hdr.xmlattr_HeaderName => 'multipart/alternative'
|
|
104
|
+
else
|
|
105
|
+
rfc822.push hdr.xmlattr_HeaderName => hdr
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# From
|
|
110
|
+
rfc822.push 'From' => "#{@message.sender.mailbox.name} <#{@message.sender.mailbox.emailAddress}>"
|
|
111
|
+
|
|
112
|
+
# To
|
|
113
|
+
rfc822.push 'To' => self.recipients
|
|
114
|
+
|
|
115
|
+
# CC
|
|
116
|
+
if( ( cc = self.cc_recipients) != nil)
|
|
117
|
+
rfc822.push 'Cc' => cc
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# BCC
|
|
121
|
+
if( ( bcc = self.bcc_recipients) != nil)
|
|
122
|
+
rfc822.push 'Bcc' => bcc
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
rfc822_msg = ""
|
|
126
|
+
rfc822.each do |line|
|
|
127
|
+
line.each_pair do |key,val|
|
|
128
|
+
rfc822_msg += "#{key}: #{val}\n"
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
rfc822_msg += "\n\n#{@message.body}"
|
|
133
|
+
end
|
|
134
|
+
end
|