ruby-gmail 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data.tar.gz.sig CHANGED
Binary file
data/History.txt CHANGED
@@ -1,8 +1,21 @@
1
- === 0.0.1 / 2009-11-18
1
+ === 0.0.4 / 2009-11-30
2
2
 
3
- * 1 major enhancement
3
+ * 4 minor enhancement
4
4
 
5
- * Birthday!
5
+ * Added label creation (thanks to Justin Perkins / http://github.com/justinperkins)
6
+ * Made the gem login automatically when first needed
7
+ * Added an optional block on the Gmail.new object that will login and logout for you.
8
+ * Added several search options (thanks to Mikkel Malmberg / http://github.com/mikker)
9
+
10
+ === 0.0.3 / 2009-11-19
11
+
12
+ * 1 bugfix
13
+
14
+ * Fixed MIME::Message#content= for messages without an encoding
15
+
16
+ * 1 minor enhancement
17
+
18
+ * Added Gmail#new_message
6
19
 
7
20
  === 0.0.2 / 2009-11-18
8
21
 
@@ -10,9 +23,9 @@
10
23
 
11
24
  * Made all of the examples in the README possible.
12
25
 
13
- === 0.0.3 / 2009-11-19
26
+ === 0.0.1 / 2009-11-18
14
27
 
15
- * 1 minor enhancement
28
+ * 1 major enhancement
29
+
30
+ * Birthday!
16
31
 
17
- * Fixed MIME::Message#content= for messages without an encoding
18
- * Added Gmail#new_message
data/Manifest.txt CHANGED
@@ -10,4 +10,4 @@ lib/mime/message.rb
10
10
  lib/smtp_tls.rb
11
11
  Manifest.txt
12
12
  Rakefile
13
- README.txt
13
+ README.markdown
data/README.markdown ADDED
@@ -0,0 +1,106 @@
1
+ # ruby-gmail
2
+
3
+ * Homepage: [http://dcparker.github.com/ruby-gmail/](http://dcparker.github.com/ruby-gmail/)
4
+ * Code: [http://github.com/dcparker/ruby-gmail](http://github.com/dcparker/ruby-gmail)
5
+ * Gem: [http://gemcutter.org/gems/ruby-gmail](http://gemcutter.org/gems/ruby-gmail)
6
+
7
+ ## Author(s)
8
+
9
+ * Daniel Parker of BehindLogic.com
10
+
11
+ Extra thanks for specific feature contributions from:
12
+
13
+ * [Justin Perkins](http://github.com/justinperkins)
14
+ * [Mikkel Malmberg](http://github.com/mikker)
15
+
16
+
17
+ ## Description
18
+
19
+ A Rubyesque interface to Gmail, with all the tools you'll need. Search, read and send multipart emails; archive, mark as read/unread, delete emails; and manage labels.
20
+
21
+ ## Features
22
+
23
+ * Search emails
24
+ * Read emails (handles attachments)
25
+ * Emails: Label, archive, delete, mark as read/unread/spam
26
+ * Create and delete labels
27
+ * Create and send multipart email messages in plaintext and/or html, with inline images and attachments
28
+ * Utilizes Gmail's IMAP & SMTP, MIME-type detection and parses and generates MIME properly.
29
+
30
+ ## Problems:
31
+
32
+ * May not correctly read malformed MIME messages. This could possibly be corrected by having IMAP parse the MIME structure.
33
+ * Cannot grab the plain or html message without also grabbing attachments. It might be nice to lazy-[down]load attachments.
34
+
35
+ ## Example Code:
36
+
37
+ require 'gmail'
38
+ gmail = Gmail.new(username, password) do |g|
39
+ read_count = g.inbox.count(:read) # => .count take the same arguments as .emails
40
+ unread = g.inbox.emails(:unread)
41
+ unread[0].archive!
42
+ unread[1].delete!
43
+ unread[2].move_to('FunStuff') # => labels and removes from inbox
44
+ unread[3].message # => a MIME::Message, parsed from the email body
45
+ unread[3].mark(:read)
46
+ unread[3].message.attachments.length
47
+ unread[4].label('FunStuff') # => Just adds the label 'FunStuff'
48
+ unread[4].message.save_attachments_to('path/to/save/into')
49
+ unread[5].message.attachments[0].save_to_file('path/to/save/into')
50
+ unread[6].mark(:spam)
51
+ end
52
+
53
+ # Optionally use a block like above to have the gem automatically login and logout,
54
+ # or just use it without a block after creating the object like below, and it will
55
+ # automatically logout at_exit. The block method is recommended in order to limit
56
+ # your signed-in session.
57
+
58
+ older = gmail.inbox.emails(:after => '2009-03-04', :before => '2009-03-15')
59
+ todays_date = Time.parse(Time.now.strftime('%Y-%m-%d'))
60
+ yesterday = gmail.inbox.emails(:after => (todays_date - 24*60*60), :before => todays_date)
61
+ todays_unread = gmail.inbox.emails(:unread, :after => todays_date)
62
+
63
+ new_email = MIME::Message.generate
64
+ new_email.to "email@example.com"
65
+ new_email.subject "Having fun in Puerto Rico!"
66
+ plain, html = new_email.generate_multipart('text/plain', 'text/html')
67
+ plain.content = "Text of plain message."
68
+ html.content = "<p>Text of <em>html</em> message.</p>"
69
+ new_email.attach_file('some_image.dmg')
70
+ gmail.send_email(new_email)
71
+
72
+ ## Requirements
73
+
74
+ * ruby
75
+ * net/smtp
76
+ * net/imap
77
+ * shared-mime-info rubygem
78
+
79
+ ## Install
80
+
81
+ sudo gem install ruby-gmail -s http://gemcutter.org
82
+
83
+ ## License
84
+
85
+ (The MIT License)
86
+
87
+ Copyright (c) 2009 BehindLogic
88
+
89
+ Permission is hereby granted, free of charge, to any person obtaining
90
+ a copy of this software and associated documentation files (the
91
+ 'Software'), to deal in the Software without restriction, including
92
+ without limitation the rights to use, copy, modify, merge, publish,
93
+ distribute, sublicense, and/or sell copies of the Software, and to
94
+ permit persons to whom the Software is furnished to do so, subject to
95
+ the following conditions:
96
+
97
+ The above copyright notice and this permission notice shall be
98
+ included in all copies or substantial portions of the Software.
99
+
100
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
101
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
102
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
103
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
104
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
105
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
106
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile CHANGED
@@ -6,6 +6,7 @@ require 'hoe'
6
6
  Hoe.spec 'ruby-gmail' do
7
7
  developer 'Daniel Parker', 'gems@behindlogic.com'
8
8
  extra_deps << ['shared-mime-info', '>= 0']
9
+ self.readme_file = 'README.markdown'
9
10
  self.url = "http://dcparker.github.com/ruby-gmail"
10
- self.post_install_message = "If you *really* like this gem, consider donating toward time for me to make it even better: http://pledgie.com/campaigns/7087"
11
+ self.post_install_message = "\n\033[34mIf ruby-gmail saves you TWO hours of work, want to compensate me for, like, a half-hour?\nSupport me in making new and better gems:\033[0m \033[31;4mhttp://pledgie.com/campaigns/7087\033[0m\n\n"
11
12
  end
data/lib/gmail.rb CHANGED
@@ -3,12 +3,13 @@ require 'net/smtp'
3
3
  require 'smtp_tls'
4
4
 
5
5
  class Gmail
6
- VERSION = '0.0.3'
6
+ VERSION = '0.0.4'
7
7
 
8
- attr_reader :imap
8
+ class NoLabel < RuntimeError; end
9
9
 
10
10
  def initialize(username, password)
11
11
  # This is to hide the username and password, not like it REALLY needs hiding, but ... you know.
12
+ # Could be helpful when demoing the gem in irb, these bits won't show up that way.
12
13
  meta = class << self
13
14
  class << self
14
15
  attr_accessor :username, :password
@@ -18,8 +19,11 @@ class Gmail
18
19
  meta.username = username =~ /@/ ? username : username + '@gmail.com'
19
20
  meta.password = password
20
21
  @imap = Net::IMAP.new('imap.gmail.com',993,true)
21
- @connected = true if @imap.login(username, password)
22
- at_exit { logout if @connected }
22
+ if block_given?
23
+ @connected = true if @imap.login(username, password)
24
+ yield self
25
+ logout
26
+ end
23
27
  end
24
28
 
25
29
  # Accessors for IMAP things
@@ -31,27 +35,41 @@ class Gmail
31
35
  def inbox
32
36
  mailbox('inbox')
33
37
  end
38
+ # Accessor for @imap, but ensures that it's logged in first.
39
+ def imap
40
+ if !@connected
41
+ meta = class << self; self end
42
+ @connected = true if @imap.login(meta.username, meta.password)
43
+ at_exit { logout if @connected } # Set up auto-logout for later.
44
+ end
45
+ @imap
46
+ end
34
47
  # Log out of gmail
35
48
  def logout
36
49
  @connected = false if @imap.logout
37
50
  end
38
51
 
52
+ def create_label(name)
53
+ imap.create(name)
54
+ end
55
+
39
56
  def in_mailbox(mailbox, &block)
40
57
  raise ArgumentError, "Must provide a code block" unless block_given?
41
58
  mailbox_stack << mailbox
42
59
  unless @selected == mailbox.name
43
- @imap.select(mailbox.name)
60
+ imap.select(mailbox.name)
44
61
  @selected = mailbox.name
45
62
  end
46
63
  value = block.arity == 1 ? block.call(mailbox) : block.call
47
64
  mailbox_stack.pop
48
65
  # Select previously selected mailbox if there is one
49
66
  if mailbox_stack.last
50
- @imap.select(mailbox_stack.last.name)
67
+ imap.select(mailbox_stack.last.name)
51
68
  @selected = mailbox.name
52
69
  end
53
70
  return value
54
71
  end
72
+ alias :in_label :in_mailbox
55
73
 
56
74
  def open_smtp(&block)
57
75
  raise ArgumentError, "This method is to be used with a block." unless block_given?
data/lib/gmail/mailbox.rb CHANGED
@@ -1,3 +1,11 @@
1
+ require 'date'
2
+ require 'time'
3
+ class Object
4
+ def to_imap_date
5
+ Date.parse(to_s).strftime("%d-%B-%Y")
6
+ end
7
+ end
8
+
1
9
  class Gmail
2
10
  class Mailbox
3
11
  attr_reader :name
@@ -17,18 +25,45 @@ class Gmail
17
25
 
18
26
  # Method: emails
19
27
  # Args: [ :all | :unread | :read ]
20
- def emails(key = :all)
21
- aliases = {
22
- :all => ['ALL'],
23
- :unread => ['UNSEEN'],
24
- :read => ['SEEN']
25
- }
28
+ # Opts: {:since => Date.new}
29
+ def emails(key_or_opts = :all, opts={})
30
+ if key_or_opts.is_a?(Hash) && opts.empty?
31
+ search = ['ALL']
32
+ opts = key_or_opts
33
+ elsif key_or_opts.is_a?(Symbol) && opts.is_a?(Hash)
34
+ aliases = {
35
+ :all => ['ALL'],
36
+ :unread => ['UNSEEN'],
37
+ :read => ['SEEN']
38
+ }
39
+ search = aliases[key_or_opts]
40
+ elsif key_or_opts.is_a?(Array) && opts.empty?
41
+ search = key_or_opts
42
+ else
43
+ raise ArgumentError, "Couldn't make sense of arguments to #emails - should be an optional hash of options preceded by an optional read-status bit; OR simply an array of parameters to pass directly to the IMAP uid_search call."
44
+ end
45
+ if !opts.empty?
46
+ # Support for several search macros
47
+ # :before => Date, :on => Date, :since => Date, :from => String, :to => String
48
+ search.concat ['SINCE', opts[:after].to_imap_date] if opts[:after]
49
+ search.concat ['BEFORE', opts[:before].to_imap_date] if opts[:before]
50
+ search.concat ['ON', opts[:on].to_imap_date] if opts[:on]
51
+ search.concat ['FROM', opts[:from]] if opts[:from]
52
+ search.concat ['TO', opts[:to]] if opts[:to]
53
+ end
54
+
26
55
  # puts "Gathering #{(aliases[key] || key).inspect} messages for mailbox '#{name}'..."
27
56
  @gmail.in_mailbox(self) do
28
- @gmail.imap.uid_search(aliases[key] || key).collect { |uid| messages[uid] ||= Message.new(@gmail, self, uid) }
57
+ @gmail.imap.uid_search(search).collect { |uid| messages[uid] ||= Message.new(@gmail, self, uid) }
29
58
  end
30
59
  end
31
60
 
61
+ # This is a convenience method that really probably shouldn't need to exist, but it does make code more readable
62
+ # if seriously all you want is the count of messages.
63
+ def count(*args)
64
+ emails(*args).length
65
+ end
66
+
32
67
  def messages
33
68
  @messages ||= {}
34
69
  end
data/lib/gmail/message.rb CHANGED
@@ -64,7 +64,22 @@ class Gmail
64
64
  end
65
65
  def label(name)
66
66
  @gmail.in_mailbox(@mailbox) do
67
- @gmail.imap.uid_copy(uid, name)
67
+ begin
68
+ @gmail.imap.uid_copy(uid, name)
69
+ rescue Net::IMAP::NoResponseError
70
+ raise Gmail::NoLabel, "No label `#{name}' exists!"
71
+ end
72
+ end
73
+ end
74
+ def label!(name)
75
+ @gmail.in_mailbox(@mailbox) do
76
+ begin
77
+ @gmail.imap.uid_copy(uid, name)
78
+ rescue Net::IMAP::NoResponseError
79
+ # need to create the label first
80
+ @gmail.create_label(name)
81
+ retry
82
+ end
68
83
  end
69
84
  end
70
85
  # We're not sure of any 'labels' except the 'mailbox' we're in at the moment.
data/lib/mime/entity.rb CHANGED
@@ -89,11 +89,12 @@ module MIME
89
89
  end
90
90
  end
91
91
  def attachment?
92
- headers['content-disposition'] =~ /^attachment(?=;|$)/
92
+ headers['content-disposition'] =~ /^attachment(?=;|$)/ || headers['content-disposition'] =~ /^form-data;.* filename=[\"\']?[^\"\']+[\"\']?/
93
93
  end
94
- def attachment_filename
94
+ alias :file? :attachment?
95
+ def part_filename
95
96
  # Content-Disposition: attachment; filename="summary.txt"
96
- if headers['content-disposition'] =~ /^attachment; filename=[\"\']?([^\"\']+)/
97
+ if headers['content-disposition'] =~ /; filename=[\"\']?([^\"\']+)/
97
98
  $1
98
99
  end
99
100
  end
@@ -120,8 +121,8 @@ module MIME
120
121
 
121
122
  def save_to_file(path=nil)
122
123
  filename = path if path && !File.exists?(path) # If path doesn't exist, assume it's a filename
123
- filename ||= path + '/' + attachment_filename if path && attachment? # If path does exist, and we're saving an attachment, use the attachment filename
124
- filename ||= (attachment? ? attachment_filename : path) # If there is no path and we're saving an attachment, use the attachment filename; otherwise use path (whether it is present or not)
124
+ filename ||= path + '/' + part_filename if path && attachment? # If path does exist, and we're saving an attachment, use the attachment filename
125
+ filename ||= (attachment? ? part_filename : path) # If there is no path and we're saving an attachment, use the attachment filename; otherwise use path (whether it is present or not)
125
126
  filename ||= '.' # No path supplied, and not saving an attachment. We'll just save it in the current directory.
126
127
  if File.directory?(filename)
127
128
  i = 0
data/lib/mime/message.rb CHANGED
@@ -28,6 +28,15 @@ module MIME
28
28
  find_parts(:content_disposition => 'attachment')
29
29
  end
30
30
 
31
+ def text
32
+ part = find_part(:content_type => 'text/plain')
33
+ part.content if part
34
+ end
35
+ def html
36
+ part = find_part(:content_type => 'text/html')
37
+ part.content if part
38
+ end
39
+
31
40
  def save_attachments_to(path=nil)
32
41
  attachments.each {|a| a.save_to_file(path) }
33
42
  end
data/lib/smtp_tls.rb CHANGED
@@ -15,13 +15,13 @@ Net::SMTP.class_eval do
15
15
  start_method = use_tls ? :do_tls_start : :do_start
16
16
  if block_given?
17
17
  begin
18
- send start_method, helo, user, secret, authtype
18
+ send(start_method, helo, user, secret, authtype)
19
19
  return yield(self)
20
20
  ensure
21
21
  do_finish
22
22
  end
23
23
  else
24
- send start_method helo, user, secret, authtype
24
+ send(start_method, helo, user, secret, authtype)
25
25
  return self
26
26
  end
27
27
  end
@@ -31,9 +31,9 @@ Net::SMTP.class_eval do
31
31
  def do_tls_start(helodomain, user, secret, authtype)
32
32
  raise IOError, 'SMTP session already started' if @started
33
33
  if RUBY_VERSION == '1.8.6'
34
- check_auth_args user, secret, authtype if user or secret
34
+ check_auth_args(user, secret, authtype) if user or secret
35
35
  else
36
- check_auth_args user, secret if user or secret
36
+ check_auth_args(user, secret) if user or secret
37
37
  end
38
38
 
39
39
  sock = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-gmail
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Parker
@@ -30,7 +30,7 @@ cert_chain:
30
30
  teST6sOe8lUhZQ==
31
31
  -----END CERTIFICATE-----
32
32
 
33
- date: 2009-11-20 00:00:00 -05:00
33
+ date: 2009-12-10 00:00:00 -05:00
34
34
  default_executable:
35
35
  dependencies:
36
36
  - !ruby/object:Gem::Dependency
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: 2.3.3
55
55
  version:
56
- description: A Rubyesque interface to Gmail. Connect to Gmail via IMAP and manipulate emails and labels. Send email with your Gmail account via SMTP. Includes full support for parsing and generating MIME messages.
56
+ description: A Rubyesque interface to Gmail, with all the tools you'll need. Search, read and send multipart emails; archive, mark as read/unread, delete emails; and manage labels.
57
57
  email:
58
58
  - gems@behindlogic.com
59
59
  executables: []
@@ -63,7 +63,6 @@ extensions: []
63
63
  extra_rdoc_files:
64
64
  - History.txt
65
65
  - Manifest.txt
66
- - README.txt
67
66
  files:
68
67
  - .autotest
69
68
  - History.txt
@@ -77,15 +76,17 @@ files:
77
76
  - lib/smtp_tls.rb
78
77
  - Manifest.txt
79
78
  - Rakefile
80
- - README.txt
79
+ - README.markdown
81
80
  has_rdoc: true
82
81
  homepage: http://dcparker.github.com/ruby-gmail
83
82
  licenses: []
84
83
 
85
- post_install_message: "If you *really* like this gem, consider donating toward time for me to make it even better: http://pledgie.com/campaigns/7087"
84
+ post_install_message: "\n\
85
+ \e[34mIf ruby-gmail saves you TWO hours of work, want to compensate me for, like, a half-hour?\n\
86
+ Support me in making new and better gems:\e[0m \e[31;4mhttp://pledgie.com/campaigns/7087\e[0m\n\n"
86
87
  rdoc_options:
87
88
  - --main
88
- - README.txt
89
+ - README.markdown
89
90
  require_paths:
90
91
  - lib
91
92
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -106,6 +107,6 @@ rubyforge_project: ruby-gmail
106
107
  rubygems_version: 1.3.5
107
108
  signing_key:
108
109
  specification_version: 3
109
- summary: A Rubyesque interface to Gmail
110
+ summary: A Rubyesque interface to Gmail, with all the tools you'll need
110
111
  test_files: []
111
112
 
metadata.gz.sig CHANGED
Binary file
data/README.txt DELETED
@@ -1,85 +0,0 @@
1
- = ruby-gmail
2
-
3
- * Homepage: http://dcparker.github.com/ruby-gmail/
4
- * Code: http://github.com/dcparker/ruby-gmail
5
-
6
-
7
- == DESCRIPTION:
8
-
9
- A Rubyesque interface to Gmail. Connect to Gmail via IMAP and manipulate emails and labels. Send email with your Gmail account via SMTP. Includes full support for parsing and generating MIME messages.
10
-
11
- == FEATURES:
12
-
13
- * Read emails via IMAP
14
- * Full MIME parsing ability, with understanding of attachments
15
- * Create, rename, and delete labels
16
- * Label, archive, delete, mark as read/unread/spam
17
- * Send emails via SMTP
18
- * Full ability to generate MIME messages including inline images and attachments
19
-
20
- == PROBLEMS:
21
-
22
- * May not correctly read malformed MIME messages. This could possibly be corrected by having IMAP parse the MIME structure.
23
- * Cannot grab the plain or html message without also grabbing attachments. It might be nice to lazy-[down]load attachments.
24
-
25
- == SYNOPSIS:
26
-
27
- require 'gmail'
28
- gmail = Gmail.new(username, password)
29
- gmail.inbox.count # => {:read => 41, :unread => 2}
30
- unread = gmail.inbox.emails(:unread)
31
- unread[0].archive!
32
- unread[1].delete!
33
- unread[2].move_to('FunStuff') # => Labels 'FunStuff' and removes from inbox
34
- unread[3].message # => a MIME::Message, parsed from the email body
35
- unread[3].mark(:read)
36
- unread[3].message.attachments.length
37
- unread[4].label('FunStuff') # => Just adds the label 'FunStuff'
38
- unread[4].message.save_attachments_to('path/to/save/into')
39
- unread[5].message.attachments[0].save_to_file('path/to/save/into')
40
- unread[6].mark(:spam)
41
-
42
- new_email = MIME::Message.generate
43
- new_email.to "email@example.com"
44
- new_email.subject "Having fun in Puerto Rico!"
45
- plain, html = new_email.generate_multipart('text/plain', 'text/html')
46
- plain.content = "Text of plain message."
47
- html.content = "<p>Text of <em>html</em> message.</p>"
48
- new_email.attach_file('some_image.dmg')
49
- gmail.send_email(new_email)
50
-
51
- == REQUIREMENTS:
52
-
53
- * ruby
54
- * net/smtp
55
- * net/imap
56
- * gem shared-mime-info
57
-
58
- == INSTALL:
59
-
60
- * [sudo] gem install ruby-gmail -s http://gemcutter.org
61
-
62
- == LICENSE:
63
-
64
- (The MIT License)
65
-
66
- Copyright (c) 2009 BehindLogic.com
67
-
68
- Permission is hereby granted, free of charge, to any person obtaining
69
- a copy of this software and associated documentation files (the
70
- 'Software'), to deal in the Software without restriction, including
71
- without limitation the rights to use, copy, modify, merge, publish,
72
- distribute, sublicense, and/or sell copies of the Software, and to
73
- permit persons to whom the Software is furnished to do so, subject to
74
- the following conditions:
75
-
76
- The above copyright notice and this permission notice shall be
77
- included in all copies or substantial portions of the Software.
78
-
79
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
80
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
81
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
82
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
83
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
84
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
85
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.