viewpoint 0.0.5 → 0.1.0
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/README +84 -45
- data/Rakefile +7 -5
- data/TODO +6 -0
- data/VERSION +1 -1
- data/lib/exceptions/exceptions.rb +34 -0
- data/lib/extensions/string.rb +37 -0
- data/lib/model/calendar_folder.rb +45 -0
- data/lib/model/calendar_item.rb +145 -0
- data/lib/model/contact.rb +58 -0
- data/lib/model/contacts_folder.rb +35 -0
- data/lib/model/distribution_list.rb +26 -0
- data/lib/model/event.rb +118 -0
- data/lib/model/folder.rb +36 -0
- data/lib/model/generic_folder.rb +302 -0
- data/lib/model/item.rb +126 -0
- data/lib/model/mailbox_user.rb +107 -0
- data/lib/model/meeting_cancellation.rb +28 -0
- data/lib/{viewpoint/errors.rb → model/meeting_message.rb} +10 -14
- data/lib/model/meeting_request.rb +26 -0
- data/lib/model/meeting_response.rb +26 -0
- data/lib/model/message.rb +73 -0
- data/lib/model/model.rb +161 -0
- data/lib/model/search_folder.rb +36 -0
- data/lib/model/task.rb +62 -0
- data/lib/model/task_list.rb +19 -0
- data/lib/model/tasks_folder.rb +33 -0
- data/lib/soap/handsoap/builder.rb +22 -0
- data/lib/soap/handsoap/builders/ews_build_helpers.rb +317 -0
- data/lib/soap/handsoap/builders/ews_builder.rb +86 -0
- data/lib/soap/handsoap/ews_service.rb +646 -0
- data/lib/soap/handsoap/parser.rb +106 -0
- data/lib/soap/handsoap/parsers/ews_parser.rb +165 -0
- data/lib/soap/soap_provider.rb +75 -0
- data/lib/viewpoint.rb +98 -3
- data/preamble +1 -1
- data/test/spec/basic_functions.spec +51 -0
- data/test/spec/folder_subscriptions.spec +35 -0
- data/test/spec/folder_synchronization.spec +28 -0
- data/utils/ewsWSDL2rb.rb +29 -0
- metadata +101 -43
- data/lib/exchwebserv.rb +0 -6
- data/lib/soap/viewpoint.conf +0 -3
- data/lib/viewpoint/calendar.rb +0 -128
- data/lib/viewpoint/calendar_item.rb +0 -118
- data/lib/viewpoint/event.rb +0 -0
- data/lib/viewpoint/exchange_headers.rb +0 -49
- data/lib/viewpoint/exchwebserv.rb +0 -236
- data/lib/viewpoint/folder.rb +0 -358
- data/lib/viewpoint/item.rb +0 -101
- data/lib/viewpoint/mail.rb +0 -117
- data/lib/viewpoint/message.rb +0 -132
- data/lib/viewpoint/task.rb +0 -0
- data/lib/viewpoint/tasks.rb +0 -0
- data/lib/wsdl/defaultMappingRegistry.rb +0 -10680
- data/lib/wsdl/exchangeServiceBinding.rb +0 -349
- data/lib/wsdl/exchangeServiceTypes.rb +0 -11013
- data/test/spec/basic_features_spec.rb +0 -37
- data/test/test_client.rb +0 -13
- 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
|
data/lib/viewpoint/event.rb
DELETED
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
|
data/lib/viewpoint/folder.rb
DELETED
@@ -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
|