sonar_rexchange 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,22 @@
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
+
21
+ ## PROJECT::SPECIFIC
22
+ sonar_rexchange.gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Trampoline Systems Ltd
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/RAKEFILE ADDED
@@ -0,0 +1,50 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "sonar_rexchange"
8
+ gem.summary = %Q{Modified rexchange gem}
9
+ gem.description = %Q{The sonar_rexchange gem imports and fixes bugs in the rexchange gem.}
10
+ gem.email = "hello@empire42.com"
11
+ gem.homepage = "http://github.com/trampoline/sonar-rexchange"
12
+ gem.authors = ["Peter MacRobert", "Daniel Kwiecinski", "Craig McMillan"]
13
+ end
14
+ Jeweler::GemcutterTasks.new
15
+ rescue LoadError
16
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
17
+ end
18
+
19
+ require 'rake/testtask'
20
+ Rake::TestTask.new(:test) do |test|
21
+ test.libs << 'lib' << 'test'
22
+ test.pattern = 'test/**/test_*.rb'
23
+ test.verbose = true
24
+ end
25
+
26
+ begin
27
+ require 'rcov/rcovtask'
28
+ Rcov::RcovTask.new do |test|
29
+ test.libs << 'test'
30
+ test.pattern = 'test/**/test_*.rb'
31
+ test.verbose = true
32
+ end
33
+ rescue LoadError
34
+ task :rcov do
35
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
36
+ end
37
+ end
38
+
39
+ task :test => :check_dependencies
40
+ task :default => :test
41
+
42
+ require 'rake/rdoctask'
43
+ Rake::RDocTask.new do |rdoc|
44
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
45
+
46
+ rdoc.rdoc_dir = 'rdoc'
47
+ rdoc.title = "sonar_rexchange #{version}"
48
+ rdoc.rdoc_files.include('README*')
49
+ rdoc.rdoc_files.include('lib/**/*.rb')
50
+ end
data/README ADDED
@@ -0,0 +1,89 @@
1
+ = RExchange
2
+
3
+ == Please note
4
+ sonar-rexchange is a modified version of the rexchange gem.
5
+
6
+ RExchange is a pure ruby wrapper for the Microsoft Exchange Server WebDAV API
7
+
8
+ == Things you should know
9
+
10
+ * Requires Ruby 1.8.4 (or later, for the extended WebDAV support in the net/http library)
11
+ * RExchange is cross-platform compatible, being written in pure Ruby
12
+ * Kiwi fruits are packed with vitamins
13
+
14
+ == Why should you use RExchange
15
+
16
+ * It makes interacting with Exchange simple
17
+ * It was written for a real application, and does real work reliably day in and day out
18
+
19
+ == Example
20
+
21
+ # We pass our uri (pointing directly to a mailbox), a username, and a password to RExchange::open
22
+ # to create a RExchange::Session. Notice that we escape the "\" in our username.
23
+ RExchange::open('https://example.com/exchange/admin/', 'mydomain\\admin', 'secret') do |mailbox|
24
+
25
+ # The block parameter ("mailbox" in this case) is actually the Session itself.
26
+ # You can refer to folders by chaining them as method calls. "inbox" in this case
27
+ # isn't a defined method for Session, but part of the DSL to refer to folder names.
28
+ # Each folder name returns a RExchange::Folder. The folder is Enumerable, allowing
29
+ # iteration over the items in the folder, depending on the DAV:content-class of
30
+ # the folder.
31
+ mailbox.inbox.each do |message|
32
+
33
+ # The "message" block parameter is a RExchange::Message object. You have access to
34
+ # several attributes of the message, including: href, from, to, message_id, date,
35
+ # importance, has_attachment? and body.
36
+ p message.subject
37
+
38
+ # The RExchange::Message#move_to method moves the message to another folder, in this
39
+ # case, an "archive" folder off of the inbox.
40
+ message.move_to mailbox.inbox.archive
41
+ end
42
+
43
+ # Folder names are "normalized", replacing dashes and spaces with underscores,
44
+ # squeezing out multiple underscores in a row, and downcasing the whole thing.
45
+ # So a folder name such as "My Very-long Folder Name" would look like:
46
+ mailbox.my_very_long_folder_name
47
+
48
+ # The Enumerable mixin gives us an Folder#entries method in case we just want to
49
+ # return an array.
50
+ messages = mailbox.inbox.entries
51
+
52
+ # Other methods, like Enumerable#map and Enumerable#sort_by let us manipulate
53
+ # the Folder in fun ways:
54
+ puts mailbox.contacts.sort_by { |contact| contact.first_name }.map do |contact|
55
+ <<-CONTACT
56
+ Name: #{contact.first_name} #{contact.last_name}
57
+ Email: #{contact.email}
58
+ Phone: #{contact.phone}
59
+ CONTACT
60
+ end
61
+
62
+ end
63
+
64
+ # We can access the Global Address Book by accessing the "/public" folder:
65
+ RExchange::open('https://example.com/public', 'mydomain\\somebody', 'secret') do |mailbox|
66
+
67
+ puts mailbox.company_contacts.each do |contact|
68
+ <<-CONTACT
69
+ Company: #{contact.company}
70
+ Name: #{contact.first_name} #{contact.last_name}
71
+ Email: #{contact.email}
72
+ Phone: #{contact.phone}
73
+ CONTACT
74
+ end
75
+
76
+ end
77
+
78
+ == Caveats
79
+
80
+ There are several features missing (simply because we didn't need them yet). Among them:
81
+
82
+ * The ability to delete messages or folders
83
+ * The ability to create folders
84
+ * There's no mechanism for sending new messages, or replying or forwarding existing ones
85
+ * There are a lot more message attributes we're not retrieving since they weren't useful to us, but they might be to you
86
+ * Exporting an email or entire folder tree to offline storage
87
+ * And much much more!
88
+
89
+ If you'd like to see any of these features, or have some ideas of your own you'd like to see implemented don't hesitate to let us know, and if it strikes our fancy maybe you'll get some free programming!
@@ -0,0 +1,52 @@
1
+ -- 0.3.4:
2
+ * Updated the README so it jives with the current version
3
+ * Removed Folder::join since it's only used by Folder#to_s, and just used a simpler
4
+ solution in Folder#to_s instead
5
+
6
+ -- 0.3.3:
7
+ * Folder objects now default to using the Message class for enumeration
8
+ if no class is associated with the content_type of the Folder.
9
+
10
+ -- 0.3.2:
11
+ * Quick hack to make Message#move_to work
12
+
13
+ -- 0.3.1:
14
+ * Disabled a warning about overwriting GenericItem::CONTENT_CLASS
15
+ * Added a "rescue" for Time::parse failures that reverts to the original element text
16
+
17
+ -- 0.3.0:
18
+ * Added support for Appointments
19
+ * Options hash for Session creation is gone. The new form is: "RExchange::Session.new(url, username, password)"
20
+ * Removed GenericItem#[], GenericItem#method_missing (attributes are explicit now)
21
+ * Removed Folder#get_messages, #get_contacts and #messages_in. Folders are now Enumerables using Enumerable#entries, #each, etc
22
+ to retrieve entries according to the folder's content-class. So a 'mailfolder' will retrieve Message instances,
23
+ a 'calendarfolder' will retrieve Appointment instances, and so on.
24
+
25
+ -- 0.2.0:
26
+ * Fixed a bug in Message#move_to when passing folders
27
+ * Added support for Contact browsing
28
+ * Added GenericItem to abstract Contacts and Messages
29
+ * Renamed Folder#messages to Folder#get_messages to avoid naming collisions
30
+ * Modified attributes in GenericItem that end with "date" to be Time::parse'd
31
+ * Added "r_exchange.rb" for Rails auto-require compatibility
32
+ * Now calling String#normalize on attribute names instead of just String#tr('-', '_')
33
+
34
+ -- 0.1.4:
35
+ * Moved Folder::normalize_folder_name to String#normalize, and added support for MixedCase sources.
36
+ * Fixed a bug where the normalized folder name was being used for the Folder#to_s result.
37
+ * Moved the meat of Folder#messages to Message::find
38
+ * Added Message#to_s
39
+ * Modified Folder#get_folders to return a hash instead of an array, providing both the original, and normalized names.
40
+
41
+ -- 0.1.3: Bugfixes
42
+ * Fixed several nasty bugs.
43
+
44
+ -- 0.1.2: Minor documentation/cleanup
45
+ * Removed a double-dash in the README that was apparently causing some viewing errors
46
+ * Changed Session initialize to a ternary condition
47
+ * Added RExchange::Message#has_attachments?
48
+ * Added documentation for most methods in RExchange::Message and RExchange::Folder
49
+
50
+ -- 0.1.1
51
+ * Fleshed out the README
52
+ * Copied the RAKEFILE from the Rools project
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2006 Samuel Smoot
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.7
data/lib/r_exchange.rb ADDED
@@ -0,0 +1,2 @@
1
+ # Provided for compatibility with RubyOnRails auto-require functionality.
2
+ require 'rexchange'
data/lib/rexchange.rb ADDED
@@ -0,0 +1,33 @@
1
+ require 'rexchange/session'
2
+ require 'rexchange/r_exception'
3
+
4
+ class String
5
+ def ends_with?(partial)
6
+ self[self.size - partial.size..self.size] == partial
7
+ end
8
+
9
+ def ensure_ends_with(partial)
10
+ self.ends_with?(partial) ? self : self + partial
11
+ end
12
+
13
+ def normalize
14
+ self.split(/(?=[A-Z][a-z]*)/).join('_').tr('- ', '_').squeeze('_').downcase
15
+ end
16
+ end
17
+
18
+ module RExchange
19
+ # Use STDOUT or another stream if you'd like to capture the HTTP debug output
20
+ DEBUG_STREAM = $log
21
+
22
+ PR_HTTPMAIL_READ = 'read'
23
+ NS_HTTPMAIL = 'urn:schemas:httpmail'
24
+
25
+ # A shortcut to RExchange::Session#new
26
+ def self.open(dav_uri, owa_uri, username = nil, password = nil)
27
+ session = RExchange::Session.new(dav_uri, owa_uri, username, password)
28
+
29
+ yield session if block_given?
30
+ return session
31
+ end
32
+
33
+ end
@@ -0,0 +1,38 @@
1
+ require 'rexchange/generic_item'
2
+
3
+ module RExchange
4
+ class Appointment < GenericItem
5
+
6
+ set_folder_type 'calendar'
7
+
8
+ attribute_mappings :all_day_event => 'urn:schemas:calendar:alldayevent',
9
+ :busy_status => 'urn:schemas:calendar:busystatus',
10
+ :contact => 'urn:schemas:calendar:contact',
11
+ :contact_url => 'urn:schemas:calendar:contacturl',
12
+ :created_on => 'urn:schemas:calendar:created',
13
+ :description_url => 'urn:schemas:calendar:descriptionurl',
14
+ :end_at => 'urn:schemas:calendar:dtend',
15
+ :created_at => 'urn:schemas:calendar:dtstamp',
16
+ :start_at => 'urn:schemas:calendar:dtstart',
17
+ :duration => 'urn:schemas:calendar:duration',
18
+ :expires_on => 'urn:schemas:calendar:exdate',
19
+ :expiry_rule => 'urn:schemas:calendar:exrule',
20
+ :has_attachment? => 'urn:schemas:httpmail:hasattachment',
21
+ :html => 'urn:schemas:httpmail:htmldescription',
22
+ :modified_on => 'urn:schemas:calendar:lastmodified',
23
+ :location => 'urn:schemas:calendar:location',
24
+ :location_url => 'urn:schemas:calendar:locationurl',
25
+ :meeting_status => 'urn:schemas:calendar:meetingstatus',
26
+ :normalized_subject => 'urn:schemas:httpmail:normalizedsubject',
27
+ :priority => 'urn:schemas:httpmail:priority',
28
+ :recurres_on => 'urn:schemas:calendar:rdate',
29
+ :reminder_offset => 'urn:schemas:calendar:reminderoffset',
30
+ :reply_time => 'urn:schemas:calendar:replytime',
31
+ :sequence => 'urn:schemas:calendar:sequence',
32
+ :subject => 'urn:schemas:httpmail:subject',
33
+ :body => 'urn:schemas:httpmail:textdescription',
34
+ :timezone => 'urn:schemas:calendar:timezone',
35
+ :uid => 'urn:schemas:calendar:uid'
36
+
37
+ end
38
+ end
@@ -0,0 +1,29 @@
1
+ require 'rexchange/generic_item'
2
+
3
+ module RExchange
4
+ class Contact < GenericItem
5
+
6
+ set_folder_type 'contact'
7
+ set_content_class 'person'
8
+
9
+ attribute_mappings :first_name => 'urn:schemas:contacts:givenName',
10
+ :middle_name => 'urn:schemas:contacts:middlename',
11
+ :last_name => 'urn:schemas:contacts:sn',
12
+ :title => 'urn:schemas:contacts:title',
13
+ :created_at => 'DAV:creationdate',
14
+ :address => 'urn:schemas:contacts:mailingstreet',
15
+ :city => 'urn:schemas:contacts:mailingcity',
16
+ :state => 'urn:schemas:contacts:st',
17
+ :zip_code => 'urn:schemas:contacts:mailingpostalcode',
18
+ :country => 'urn:schemas:contacts:co',
19
+ :phone => 'urn:schemas:contacts:homePhone',
20
+ :business_phone => 'urn:schemas:contacts:telephoneNumber',
21
+ :fax => 'urn:schemas:contacts:facsimiletelephonenumber',
22
+ :mobile => 'urn:schemas:contacts:mobile',
23
+ :email => 'urn:schemas:contacts:email1',
24
+ :website => 'urn:schemas:contacts:businesshomepage',
25
+ :company => 'urn:schemas:contacts:o',
26
+ :notes => 'urn:schemas:httpmail:textdescription'
27
+
28
+ end
29
+ end
@@ -0,0 +1,38 @@
1
+ require 'uri'
2
+
3
+ module RExchange
4
+
5
+ # Credentials are passed around between Folders to emulate a stateful
6
+ # connection with the RExchange::Session
7
+ class Credentials
8
+ attr_reader :user, :password, :dav_uri, :owa_uri
9
+ attr_accessor :auth_cookie
10
+
11
+ # You must pass a dav_uri, owa_uri, a username and a password
12
+ def initialize(dav_uri = nil, owa_uri = nil, username = nil, password = nil)
13
+ owa_uri = nil if owa_uri && owa_uri.blank?
14
+ @dav_uri = URI.parse(dav_uri) if dav_uri
15
+ @owa_uri = URI.parse(owa_uri) if owa_uri
16
+ @dav_use_ssl = @dav_uri.scheme ? (@dav_uri.scheme.downcase == 'https') : false
17
+ @user = username || (@dav_uri && @dav_uri.userinfo ? @dav_uri.userinfo.split(':')[0] : nil) || (@owa_uri && @owa_uri.userinfo ? @owa_uri.userinfo.split(':')[0] : nil)
18
+ @password = password || (@dav_uri && @dav_uri.userinfo ? @dav_uri.userinfo.split(':')[1] : nil) || (@owa_uri && @owa_uri.userinfo ? @owa_uri.userinfo.split(':')[1] : nil)
19
+ @dav_port = (@dav_uri.port || @dav_uri.default_port) if @dav_uri
20
+ @owa_port = (@owa_uri.port || @owa_uri.default_port) if @owa_uri
21
+ if block_given?
22
+ yield self
23
+ else
24
+ return self
25
+ end
26
+ end
27
+
28
+ def dav_use_ssl?
29
+ @dav_use_ssl
30
+ end
31
+
32
+ def is_owa?
33
+ @owa_uri && @owa_uri.scheme && @owa_uri.host
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,30 @@
1
+ require 'rexchange/exchange_request'
2
+
3
+ module RExchange
4
+ # Used to move entities to different locations in the accessable mailbox.
5
+ class DavDeleteRequest < ExchangeRequest
6
+ METHOD = 'DELETE'
7
+ REQUEST_HAS_BODY = false
8
+ RESPONSE_HAS_BODY = false
9
+
10
+ def self.execute(credentials, source, &b)
11
+ begin
12
+ options = {
13
+ :path => source
14
+ }
15
+
16
+ response = super credentials, options
17
+ yield response if b
18
+ response
19
+ rescue RException => e
20
+ raise e
21
+ rescue Exception => e
22
+ raise RException.new(options[:request], response, e)
23
+ end
24
+ end
25
+
26
+
27
+
28
+ end
29
+
30
+ end