ews-api 0.1.0.a
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +24 -0
- data/LICENSE +20 -0
- data/README.rdoc +38 -0
- data/Rakefile +88 -0
- data/VERSION +1 -0
- data/ews-api.gemspec +96 -0
- data/lib/ews-api.rb +16 -0
- data/lib/ews/attachment.rb +7 -0
- data/lib/ews/error.rb +14 -0
- data/lib/ews/folder.rb +44 -0
- data/lib/ews/message.rb +22 -0
- data/lib/ews/model.rb +37 -0
- data/lib/ews/parser.rb +166 -0
- data/lib/ews/service.rb +476 -0
- data/spec/ews/attachment_spec.rb +29 -0
- data/spec/ews/folder_spec.rb +85 -0
- data/spec/ews/message_spec.rb +28 -0
- data/spec/ews/model_spec.rb +31 -0
- data/spec/ews/parser_spec.rb +119 -0
- data/spec/ews/service_spec.rb +14 -0
- data/spec/fixtures/find_folder.xml +25 -0
- data/spec/fixtures/find_item.xml +20 -0
- data/spec/fixtures/find_item_all_properties.xml +120 -0
- data/spec/fixtures/get_attachment.xml +77 -0
- data/spec/fixtures/get_folder.xml +16 -0
- data/spec/fixtures/get_item_all_properties.xml +80 -0
- data/spec/fixtures/get_item_default.xml +46 -0
- data/spec/fixtures/get_item_id_only.xml +13 -0
- data/spec/fixtures/get_item_no_attachments.xml +75 -0
- data/spec/fixtures/get_item_with_error.xml +11 -0
- data/spec/integration.rb +153 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +61 -0
- metadata +145 -0
data/.document
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
## MAC OS
|
2
|
+
.DS_Store
|
3
|
+
|
4
|
+
## TEXTMATE
|
5
|
+
*.tmproj
|
6
|
+
tmtags
|
7
|
+
|
8
|
+
## EMACS
|
9
|
+
*~
|
10
|
+
\#*
|
11
|
+
.\#*
|
12
|
+
|
13
|
+
## VIM
|
14
|
+
*.swp
|
15
|
+
|
16
|
+
## PROJECT::GENERAL
|
17
|
+
coverage
|
18
|
+
rdoc
|
19
|
+
pkg
|
20
|
+
doc
|
21
|
+
.yardoc
|
22
|
+
|
23
|
+
## PROJECT::SPECIFIC
|
24
|
+
spec/test-config.yml
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Jeremy Burks
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
= ews-api
|
2
|
+
|
3
|
+
Exchange Web Services API.
|
4
|
+
|
5
|
+
== Configuration
|
6
|
+
|
7
|
+
Set the endpoint
|
8
|
+
|
9
|
+
EWS::Service.endpoint :uri => 'https://example.com/ews/exchange.asmx',
|
10
|
+
:version => 1
|
11
|
+
|
12
|
+
Set the credentials if the service requires autentication. NTLM is known to work.
|
13
|
+
|
14
|
+
EWS::Service.set_auth 'testuser', 'xxxxxx'
|
15
|
+
|
16
|
+
== Testing
|
17
|
+
|
18
|
+
Typically it isn't a good idea for tests to depend on external resources.
|
19
|
+
This project is in its early days and I am new to EWS. So as to make it
|
20
|
+
easier to implement the service the tests depend on connecting to EWS.
|
21
|
+
|
22
|
+
If +spec/test-config+ exists it will be loaded and the +EWS::Service+ will
|
23
|
+
be configured.
|
24
|
+
|
25
|
+
The config file is ignored via +.gitignore+.
|
26
|
+
|
27
|
+
=== Example +spec/test-config.yml+
|
28
|
+
|
29
|
+
# Example spec/test-config.yml
|
30
|
+
endpoint:
|
31
|
+
:uri: 'https://localhost/ews/exchange.asmx'
|
32
|
+
:version: 1
|
33
|
+
username: testuser
|
34
|
+
password: xxxxxx
|
35
|
+
|
36
|
+
== Copyright
|
37
|
+
|
38
|
+
Copyright (c) 2009 Jeremy Burks. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "ews-api"
|
8
|
+
gem.summary = 'Exchange Web Services API'
|
9
|
+
gem.description = "Exchange Web Services API. It doesn't use soap4r."
|
10
|
+
gem.email = "jeremy.burks@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/jrun/ews-api"
|
12
|
+
gem.authors = ["jrun"]
|
13
|
+
gem.add_dependency 'httpclient'
|
14
|
+
gem.add_dependency 'rubyntlm'
|
15
|
+
gem.add_dependency 'handsoap', '1.1.4'
|
16
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
17
|
+
gem.add_development_dependency "yard", ">= 0"
|
18
|
+
|
19
|
+
desc "Install development dependencies."
|
20
|
+
task :setup do
|
21
|
+
gems = ::Gem::SourceIndex.from_installed_gems
|
22
|
+
gem.dependencies.each do |dep|
|
23
|
+
if gems.find_name(dep.name, dep.version_requirements).empty?
|
24
|
+
puts "Installing dependency: #{dep}"
|
25
|
+
system %Q|gem install #{dep.name} -v "#{dep.version_requirements}" --development|
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "Build and reinstall the gem locally."
|
31
|
+
task :reinstall => :build do
|
32
|
+
version = File.read('VERSION')
|
33
|
+
if (system("gem list #{gem.name} -l") || "") =~ /#{gem.name}-#{version}/
|
34
|
+
system "gem uninstall #{gem.name}"
|
35
|
+
end
|
36
|
+
system "gem install --no-rdoc --no-ri -l pkg/#{gem.name}-#{version}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
Jeweler::GemcutterTasks.new
|
41
|
+
rescue LoadError
|
42
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
43
|
+
end
|
44
|
+
|
45
|
+
require 'spec/rake/spectask'
|
46
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
47
|
+
spec.libs << 'lib' << 'spec'
|
48
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
49
|
+
end
|
50
|
+
|
51
|
+
desc 'Run tests against a real EWS server'
|
52
|
+
Spec::Rake::SpecTask.new(:integration) do |spec|
|
53
|
+
spec.libs << 'lib' << 'spec'
|
54
|
+
spec.spec_files = FileList['spec/integration.rb']
|
55
|
+
end
|
56
|
+
|
57
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
58
|
+
spec.libs << 'lib' << 'spec'
|
59
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
60
|
+
spec.rcov = true
|
61
|
+
end
|
62
|
+
|
63
|
+
task :spec => :check_dependencies
|
64
|
+
task :default => :spec
|
65
|
+
task :build => [:spec, :yard]
|
66
|
+
|
67
|
+
begin
|
68
|
+
require 'yard'
|
69
|
+
YARD::Rake::YardocTask.new
|
70
|
+
rescue LoadError
|
71
|
+
task :yardoc do
|
72
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
begin
|
77
|
+
require 'grancher/task'
|
78
|
+
Grancher::Task.new do |g|
|
79
|
+
g.branch = 'gh-pages'
|
80
|
+
g.push_to = 'origin'
|
81
|
+
g.directory 'doc'
|
82
|
+
end
|
83
|
+
rescue LoadError
|
84
|
+
task :publish do
|
85
|
+
abort "grancher is not available. Run 'rake setup' to install all development dependencies."
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0.a
|
data/ews-api.gemspec
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{ews-api}
|
8
|
+
s.version = "0.1.0.a"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["jrun"]
|
12
|
+
s.date = %q{2009-12-17}
|
13
|
+
s.description = %q{Exchange Web Services API. It doesn't use soap4r.}
|
14
|
+
s.email = %q{jeremy.burks@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"ews-api.gemspec",
|
27
|
+
"lib/ews-api.rb",
|
28
|
+
"lib/ews/attachment.rb",
|
29
|
+
"lib/ews/error.rb",
|
30
|
+
"lib/ews/folder.rb",
|
31
|
+
"lib/ews/message.rb",
|
32
|
+
"lib/ews/model.rb",
|
33
|
+
"lib/ews/parser.rb",
|
34
|
+
"lib/ews/service.rb",
|
35
|
+
"spec/ews/attachment_spec.rb",
|
36
|
+
"spec/ews/folder_spec.rb",
|
37
|
+
"spec/ews/message_spec.rb",
|
38
|
+
"spec/ews/model_spec.rb",
|
39
|
+
"spec/ews/parser_spec.rb",
|
40
|
+
"spec/ews/service_spec.rb",
|
41
|
+
"spec/fixtures/find_folder.xml",
|
42
|
+
"spec/fixtures/find_item.xml",
|
43
|
+
"spec/fixtures/find_item_all_properties.xml",
|
44
|
+
"spec/fixtures/get_attachment.xml",
|
45
|
+
"spec/fixtures/get_folder.xml",
|
46
|
+
"spec/fixtures/get_item_all_properties.xml",
|
47
|
+
"spec/fixtures/get_item_default.xml",
|
48
|
+
"spec/fixtures/get_item_id_only.xml",
|
49
|
+
"spec/fixtures/get_item_no_attachments.xml",
|
50
|
+
"spec/fixtures/get_item_with_error.xml",
|
51
|
+
"spec/integration.rb",
|
52
|
+
"spec/spec.opts",
|
53
|
+
"spec/spec_helper.rb"
|
54
|
+
]
|
55
|
+
s.homepage = %q{http://github.com/jrun/ews-api}
|
56
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
57
|
+
s.require_paths = ["lib"]
|
58
|
+
s.rubygems_version = %q{1.3.5}
|
59
|
+
s.summary = %q{Exchange Web Services API}
|
60
|
+
s.test_files = [
|
61
|
+
"spec/spec_helper.rb",
|
62
|
+
"spec/integration.rb",
|
63
|
+
"spec/ews/parser_spec.rb",
|
64
|
+
"spec/ews/message_spec.rb",
|
65
|
+
"spec/ews/attachment_spec.rb",
|
66
|
+
"spec/ews/folder_spec.rb",
|
67
|
+
"spec/ews/service_spec.rb",
|
68
|
+
"spec/ews/model_spec.rb"
|
69
|
+
]
|
70
|
+
|
71
|
+
if s.respond_to? :specification_version then
|
72
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
73
|
+
s.specification_version = 3
|
74
|
+
|
75
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
76
|
+
s.add_runtime_dependency(%q<httpclient>, [">= 0"])
|
77
|
+
s.add_runtime_dependency(%q<rubyntlm>, [">= 0"])
|
78
|
+
s.add_runtime_dependency(%q<handsoap>, ["= 1.1.4"])
|
79
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
80
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
81
|
+
else
|
82
|
+
s.add_dependency(%q<httpclient>, [">= 0"])
|
83
|
+
s.add_dependency(%q<rubyntlm>, [">= 0"])
|
84
|
+
s.add_dependency(%q<handsoap>, ["= 1.1.4"])
|
85
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
86
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
87
|
+
end
|
88
|
+
else
|
89
|
+
s.add_dependency(%q<httpclient>, [">= 0"])
|
90
|
+
s.add_dependency(%q<rubyntlm>, [">= 0"])
|
91
|
+
s.add_dependency(%q<handsoap>, ["= 1.1.4"])
|
92
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
93
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
data/lib/ews-api.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'net/ntlm'
|
2
|
+
require 'handsoap'
|
3
|
+
|
4
|
+
require 'ews/error'
|
5
|
+
require 'ews/model'
|
6
|
+
require 'ews/attachment'
|
7
|
+
require 'ews/message'
|
8
|
+
require 'ews/folder'
|
9
|
+
require 'ews/parser'
|
10
|
+
require 'ews/service'
|
11
|
+
|
12
|
+
module EWS
|
13
|
+
def self.inbox
|
14
|
+
Service.get_folder(:inbox, :base_shape => :AllProperties)
|
15
|
+
end
|
16
|
+
end
|
data/lib/ews/error.rb
ADDED
data/lib/ews/folder.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module EWS
|
2
|
+
|
3
|
+
class Folder < Model
|
4
|
+
def id
|
5
|
+
attrs[:folder_id][:id]
|
6
|
+
end
|
7
|
+
|
8
|
+
def change_key
|
9
|
+
attrs[:folder_id][:change_key]
|
10
|
+
end
|
11
|
+
|
12
|
+
def name
|
13
|
+
attrs[:display_name]
|
14
|
+
end
|
15
|
+
|
16
|
+
def each_message
|
17
|
+
items.each {|message| yield message }
|
18
|
+
end
|
19
|
+
|
20
|
+
def folders
|
21
|
+
@folders ||= find_folders.inject({}) do |folders, folder|
|
22
|
+
folders[folder.name] = folder
|
23
|
+
folders
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def items
|
28
|
+
@items ||= find_folder_items
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def find_folder_items
|
33
|
+
# NOTE: This assumes Service#find_item only returns
|
34
|
+
# Messages. That is true now but will change as more
|
35
|
+
# of the parser is implemented.
|
36
|
+
service.find_item(self.name, :base_shape => :AllProperties)
|
37
|
+
end
|
38
|
+
|
39
|
+
def find_folders
|
40
|
+
service.find_folder(self.name)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
data/lib/ews/message.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module EWS
|
2
|
+
|
3
|
+
class Message < Model
|
4
|
+
def id
|
5
|
+
@id ||= attrs[:item_id][:id]
|
6
|
+
end
|
7
|
+
|
8
|
+
def change_key
|
9
|
+
@change_key ||= attrs[:item_id][:change_key]
|
10
|
+
end
|
11
|
+
|
12
|
+
def shallow?
|
13
|
+
self.body.nil? || self.header.nil?
|
14
|
+
end
|
15
|
+
|
16
|
+
def move_to!(folder_id)
|
17
|
+
# TODO: support DistinguishedFolderId?
|
18
|
+
service.move_item! folder_id, [self.id]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/lib/ews/model.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module EWS
|
2
|
+
|
3
|
+
class Model
|
4
|
+
def initialize(attrs = {})
|
5
|
+
@attrs = attrs.dup
|
6
|
+
end
|
7
|
+
|
8
|
+
def shallow?
|
9
|
+
raise NotImplementedError, "Each model must determine when it is shallow."
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
attr_reader :attrs
|
14
|
+
|
15
|
+
def service
|
16
|
+
EWS::Service
|
17
|
+
end
|
18
|
+
|
19
|
+
public
|
20
|
+
def method_missing(meth, *args)
|
21
|
+
method_name = meth.to_s
|
22
|
+
if method_name.end_with?('=')
|
23
|
+
attr = method_name.chomp('=').to_sym
|
24
|
+
@attrs[attr] = args.first
|
25
|
+
elsif method_name.end_with?('?')
|
26
|
+
attr = method_name.chomp('?').to_sym
|
27
|
+
@attrs[attr] == true
|
28
|
+
elsif @attrs.has_key?(meth)
|
29
|
+
@attrs[meth]
|
30
|
+
else
|
31
|
+
super meth, *args
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
data/lib/ews/parser.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
module EWS
|
2
|
+
|
3
|
+
class Parser
|
4
|
+
def parse_find_folder(doc)
|
5
|
+
doc.xpath('//t:Folders/child::*').map do |node|
|
6
|
+
parse_exchange_folder node.xpath('.') # force NodeSelection
|
7
|
+
end.compact
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse_get_folder(doc)
|
11
|
+
parse_exchange_folder doc.xpath('//m:Folders/child::*[1]')
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse_find_item(doc)
|
15
|
+
doc.xpath('//t:Items/child::*').map do |node|
|
16
|
+
parse_exchange_item node.xpath('.') # force NodeSelection
|
17
|
+
end.compact
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse_get_item(doc)
|
21
|
+
parse_exchange_item doc.xpath('//m:Items/child::*[1]')
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_get_attachment(doc)
|
25
|
+
parse_attachment doc.xpath('//m:Attachments/child::*[1]')
|
26
|
+
end
|
27
|
+
|
28
|
+
# Checks the ResponseMessage for errors.
|
29
|
+
#
|
30
|
+
# @see http://msdn.microsoft.com/en-us/library/aa494164%28EXCHG.80%29.aspx
|
31
|
+
# Exhange 2007 Valid Response Messages
|
32
|
+
def parse_response_message(doc)
|
33
|
+
error_node = doc.xpath('//m:ResponseMessages/child::*[@ResponseClass="Error"]')
|
34
|
+
unless error_node.empty?
|
35
|
+
error_msg = error_node.xpath('m:MessageText/text()').to_s
|
36
|
+
response_code = error_node.xpath('m:ResponseCode/text()').to_s
|
37
|
+
raise EWS::ResponseError.new(error_msg, response_code)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
def parse_exchange_folder(folder_node)
|
43
|
+
case folder_node.node_name
|
44
|
+
when 'Folder'
|
45
|
+
parse_folder folder_node
|
46
|
+
when 'CalendarFolder'
|
47
|
+
when 'ContactsFolder'
|
48
|
+
when 'SearchFolder'
|
49
|
+
when 'TasksFolder'
|
50
|
+
else
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def parse_folder(folder_node)
|
56
|
+
attrs = {
|
57
|
+
:folder_id => parse_id(folder_node.xpath('t:FolderId')),
|
58
|
+
:display_name => folder_node.xpath('t:DisplayName/text()').to_s,
|
59
|
+
:total_count => folder_node.xpath('t:TotalCount/text()').to_i,
|
60
|
+
:child_folder_count => folder_node.xpath('t:ChildFolderCount/text()').to_i,
|
61
|
+
:unread_count => folder_node.xpath('t:UnreadCount/text()').to_i
|
62
|
+
}
|
63
|
+
Folder.new attrs
|
64
|
+
end
|
65
|
+
|
66
|
+
def parse_exchange_item(item_node)
|
67
|
+
case item_node.node_name
|
68
|
+
when 'Item'
|
69
|
+
when 'Message'
|
70
|
+
parse_message item_node
|
71
|
+
when 'CalendarItem'
|
72
|
+
when 'Contact'
|
73
|
+
when 'Task'
|
74
|
+
when 'MeetingMessage'
|
75
|
+
when 'MeetingRequest'
|
76
|
+
when 'MeetingResponse'
|
77
|
+
when 'MeetingCancellation'
|
78
|
+
else
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def parse_message(message_node)
|
84
|
+
attrs = {
|
85
|
+
:item_id => parse_id(message_node.xpath('t:ItemId')),
|
86
|
+
:parent_folder_id => parse_id(message_node.xpath('t:ParentFolderId')),
|
87
|
+
:subject => message_node.xpath('t:Subject/text()').to_s,
|
88
|
+
:body => message_node.xpath('t:Body/text()').to_s,
|
89
|
+
:body_type => message_node.xpath('t:Body/@BodyType').to_s
|
90
|
+
}
|
91
|
+
|
92
|
+
nodeset = message_node.xpath('t:HasAttachments')
|
93
|
+
attrs[:has_attachments] = if not nodeset.empty?
|
94
|
+
parse_bool(nodeset)
|
95
|
+
end
|
96
|
+
|
97
|
+
nodeset = message_node.xpath('t:Attachments')
|
98
|
+
attrs[:attachments] = if not nodeset.empty?
|
99
|
+
nodeset.xpath('t:ItemAttachment|t:FileAttachment').map do |node|
|
100
|
+
parse_attachment node
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
nodeset = message_node.xpath('t:InternetMessageHeaders')
|
105
|
+
attrs[:header] = if not nodeset.empty?
|
106
|
+
parse_header nodeset
|
107
|
+
end
|
108
|
+
|
109
|
+
Message.new attrs
|
110
|
+
end
|
111
|
+
|
112
|
+
EXCHANGE_ITEM_XPATH = ['t:Item',
|
113
|
+
't:Message',
|
114
|
+
't:CalendarItem',
|
115
|
+
't:Contact',
|
116
|
+
't:Task',
|
117
|
+
't:MeetingMessage',
|
118
|
+
't:MeetingRequest',
|
119
|
+
't:MeetingResponse',
|
120
|
+
't:MeetingCancellation'].join('|').freeze
|
121
|
+
|
122
|
+
def parse_attachment(attachment_node)
|
123
|
+
attrs = {
|
124
|
+
:attachment_id => attachment_node.xpath('t:AttachmentId/@Id').to_s,
|
125
|
+
:name => attachment_node.xpath('t:Name/text()').to_s,
|
126
|
+
:content_type => attachment_node.xpath('t:ContentType/text()').to_s,
|
127
|
+
:content_id => attachment_node.xpath('t:ContentId/text()').to_s,
|
128
|
+
:content_location => attachment_node.xpath('t:ContentLocation/text()').to_s
|
129
|
+
}
|
130
|
+
|
131
|
+
case attachment_node.node_name
|
132
|
+
when 'ItemAttachment'
|
133
|
+
attrs[:item] = parse_exchange_item attachment_node.xpath(EXCHANGE_ITEM_XPATH)
|
134
|
+
when 'FileAttachment'
|
135
|
+
end
|
136
|
+
|
137
|
+
Attachment.new attrs
|
138
|
+
end
|
139
|
+
|
140
|
+
def parse_header(header_node)
|
141
|
+
header_node.xpath('t:InternetMessageHeader').inject({}) do |header, node|
|
142
|
+
name = node.xpath('@HeaderName').to_s.downcase
|
143
|
+
header[name] = [] unless header.has_key?(name)
|
144
|
+
header[name] << node.xpath('text()').to_s
|
145
|
+
header
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def parse_id(id_node)
|
150
|
+
return nil if id_node.empty?
|
151
|
+
{ :id => id_node.xpath('@Id').to_s,
|
152
|
+
:change_key => id_node.xpath('@ChangeKey').to_s }
|
153
|
+
end
|
154
|
+
|
155
|
+
def parse_bool(val)
|
156
|
+
case val.to_s.downcase
|
157
|
+
when 'true'
|
158
|
+
true
|
159
|
+
when 'false'
|
160
|
+
false
|
161
|
+
else
|
162
|
+
nil
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|